본문 바로가기
안드로이드 코틀린

[Kotlin][Android] ActivityResultContract를 활용한 권한 획득 방법

by teamnova 2021. 8. 6.
728x90

이번 포스팅에서는 코틀린을 사용한 권한 요청 기능 사용법에 대해 정리하겠습니다.

코드는 아래의 스틱코드 포스팅을 참고해 주세요.

 

https://stickode.com/detail.html?no=2309 

 

스틱코드

 

stickode.com

 

먼저 앱 수준 gradle 파일에 아래의 의존성들을 추가해줍니다.

 implementation 'androidx.activity:activity-ktx:1.2.0-rc01'
 implementation 'androidx.fragment:fragment-ktx:1.3.0-rc01'

 

둘 중 하나라도 빠뜨리게 되면 Can only use lower 16 bits for requestCode 런타임 에러가 나오게 됩니다.

그 다음 메인 액티비티의 XML 소스입니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="선택해 주세요"
        android:textSize="20sp"
        app:layout_constraintVertical_bias="0.4"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/launch_button_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="성인"
        app:layout_constraintTop_toBottomOf="@id/text_view"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/launch_button_right"/>

    <Button
        android:id="@+id/launch_button_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="어린이"
        app:layout_constraintTop_toBottomOf="@id/text_view"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/launch_button_left"/>

    <Button
        android:id="@+id/request_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="권한 요청"
        app:layout_constraintTop_toBottomOf="@id/launch_button_left"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 

다음으로 메인 액티비티 코틀린 소스입니다.

import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity()
{
    object Contracts : ActivityResultContract<Int, User?>()
    {
        override fun createIntent(context: Context, input: Int): Intent
        {
            val intent = Intent(context, SecondActivity::class.java)
            intent.putExtra("select_id", input)
            return intent
        }

        override fun parseResult(resultCode: Int, intent: Intent?): User?
        {
            if (resultCode == Activity.RESULT_OK)
            {
                return intent?.getParcelableExtra("result")
            }
            return null
        }
    }

    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val simpleLauncher =
            registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
                if (result.resultCode == Activity.RESULT_OK)
                {
                    val intent = result.data
                    val user = intent?.getParcelableExtra<User>("result")
                        ?: return@registerForActivityResult
                    setResultText(user)
                }
            }

        val customLauncher = registerForActivityResult(Contracts) { result ->
            result ?: return@registerForActivityResult
            setResultText(result)
        }

        val requestPermission =
            registerForActivityResult(ActivityResultContracts.RequestPermission()) {
                if (it)
                {
                    Toast.makeText(this, "권한이 허용되었습니다", Toast.LENGTH_SHORT).show()
                }
                else
                {
                    Toast.makeText(this, "권한이 거부되었습니다", Toast.LENGTH_SHORT).show()
                }
            }

        launch_button_left.setOnClickListener {
            val intent = Intent(this, SecondActivity::class.java)
            intent.putExtra("select_id", User.Attribute.ADULT.id)
            simpleLauncher.launch(intent)
        }

        launch_button_right.setOnClickListener {
            customLauncher.launch(User.Attribute.CHILD.id)
        }

        request_button.setOnClickListener {
            requestPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION)
        }
    }

    private fun setResultText(user: User)
    {
        val attr = User.Attribute.values()[user.attr].value
        text_view.text = ("이름 : ${user.name}\n구분 : $attr")
    }

}

 

그리고 User.kt 파일을 만들겠습니다. 이 파일은 권한을 요청한 사람이 성인인지 어린이인지에 따라 다른 문자를 보여줄 때 사용합니다.

import android.os.Parcelable
import kotlinx.android.parcel.Parcelize

@Parcelize
data class User(val name: String, val attr: Int) : Parcelable
{
    enum class Attribute(val id: Int, val res: Int, val value: String)
    {
        ADULT(0, R.id.adult, "성인"),
        CHILD(1, R.id.child, "어린이")
    }
}

 

다음은 메인 액티비티에서 '권한 요청' 버튼을 누르면 이동하는 화면을 만들겠습니다. 먼저 XML 코드입니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SecondActivity">

    <RadioGroup
        android:id="@+id/which_group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.4">

        <RadioButton
            android:id="@+id/adult"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="false"
            android:text="성인" />

        <RadioButton
            android:id="@+id/child"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="false"
            android:text="어린이" />
    </RadioGroup>

    <EditText
        android:id="@+id/edit_text"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:hint="이름"
        android:inputType="text"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/which_group"
        tools:ignore="Autofill" />

    <Button
        android:id="@+id/ok_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="OK"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/edit_text" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

다음은 SecondActivity의 코틀린 코드입니다.

import android.app.Activity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_second.*

class SecondActivity : AppCompatActivity()
{
    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)

        val selectedId = intent.getIntExtra("select_id", 0)
        val resId = User.Attribute.values()[selectedId].res
        which_group.check(resId)

        ok_button.setOnClickListener {
            val user = User(edit_text.text.toString(), selectedId)
            intent.putExtra("result", user)
            setResult(Activity.RESULT_OK, intent)
            finish()
        }
    }
}

 

 

마지막으로 위치 권한을 요청할 것이기 때문에 매니페스트에 아래 문장을 추가해줍니다.

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

 

모든 코드를 입력하고 앱을 빌드하면 아래와 같은 화면을 볼 수 있습니다.