일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 클래스
- 2022 플러터 설치
- ANR이란
- 객체
- 안드로이드 유닛 테스트 예시
- 안드로이드 라이선스
- 큐 자바 코드
- 스택 자바 코드
- rxjava disposable
- 안드로이드 레트로핏 crud
- jvm 작동 원리
- 서비스 쓰레드 차이
- rxjava cold observable
- rxjava hot observable
- 서비스 vs 쓰레드
- 안드로이드 os 구조
- 안드로이드 레트로핏 사용법
- 안드로이드 라이선스 종류
- 스택 큐 차이
- jvm이란
- 자바 다형성
- 플러터 설치 2022
- ar vr 차이
- 안드로이드 유닛테스트란
- android ar 개발
- 안드로이드 유닛 테스트
- android retrofit login
- Rxjava Observable
- 멤버변수
- 2022 플러터 안드로이드 스튜디오
- Today
- Total
나만을 위한 블로그
[Android] 라이브러리 없이 권한 요청하는 법 본문
라이브러리를 사용할 수 없는 때가 오면 순수 로직으로 커버쳐야 할 때가 언젠가 발생할 수 있다. 라이브러리에 의존하기 쉬운 것 중 하나가 권한인데, 이 포스팅에선 라이브러리 없이 권한을 요청하고 그에 따라 텍스트뷰, 이미지뷰의 글자, 이미지를 각각 바꾸는 예제를 포스팅한다.
데이터 바인딩과 hilt, StateFlow를 사용해서 예제를 구성해 봤다. 예제에 너무 과한 스펙을 입히는 것 같긴 한데, 내 블로그에 쓰는 포스팅이니 내 맘대로 할란다. 나만을 위한 블로그니까
먼저 drawable 패키지에 allowed, not_allowed라는 이름의 아무 이미지나 추가한다. 대충 권한이 허용됐구나, 안 됐구나를 알 수 있는 간단한 드로어블이면 상관없다.
그리고 뷰모델을 간단하게 구성한다.
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
@HiltViewModel
class PermissionRequestViewModel @Inject constructor() : ViewModel() {
// 권한 상태를 나타내는 StateFlow
private var _permissionState = MutableStateFlow(false)
val permissionState: StateFlow<Boolean> = _permissionState
/**
* 권한 허용 여부를 T/F로 갖는 StateFlow를 업데이트하는 함수
*
* @param isAllowed 권한 허용 여부
*/
fun updatePermissionState(isAllowed: Boolean) {
_permissionState.value = isAllowed
}
}
hilt를 썼기 때문에 뷰모델에 써야 하는 어노테이션 쓰고, MutableStateFlow, StateFlow 프로퍼티를 각각 선언하고 액티비티에서 호출할 함수만 존재하는 간단한 뷰모델이다. 코드만 달랑 쓰자니 너무 허전해서 주석이라도 좀 추가해봤다.
아래는 액티비티의 전체 코드다.
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".permission.PermissionRequestActivity">
<ImageView
android:id="@+id/ivPermissionState"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@drawable/not_allowed"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="@+id/tvPermissionState"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@+id/ivPermissionState"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="권한 허용 안 됨!!"
android:textColor="@color/black"
android:textSize="30dp"/>
<Button
android:id="@+id/btnRequestPermission"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@+id/tvPermissionState"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="권한 요청"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
@AndroidEntryPoint
@SuppressLint("NewApi")
class PermissionRequestActivity :
BaseActivity<ActivityPermissionRequestBinding>(R.layout.activity_permission_request) {
private val permissionRequestViewModel: PermissionRequestViewModel by viewModels()
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private val permissions = arrayOf(
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_VIDEO,
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
checkCurrentPermissionState()
setupPermissionStateObserver()
setupPermissionRequestButton()
}
private fun checkCurrentPermissionState() {
val isPermissionGranted = permissions.all {
ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
}
permissionRequestViewModel.updatePermissionState(isPermissionGranted)
}
private fun setupPermissionStateObserver() = lifecycleScope.launch {
permissionRequestViewModel.permissionState.collect { isAllowed ->
if (isAllowed) {
updateUIForGrantedPermission()
} else {
updateUIForDeniedPermission()
}
}
}
private fun updateUIForGrantedPermission() {
binding.ivPermissionState.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.allowed))
binding.tvPermissionState.text = "권한 허용됨!!"
}
private fun updateUIForDeniedPermission() {
binding.ivPermissionState.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.not_allowed))
binding.tvPermissionState.text = "권한 허용 안 됨!!"
}
private fun setupPermissionRequestButton() = bind {
btnRequestPermission.setOnClickListener {
if (isPermissionGranted(this@PermissionRequestActivity)) {
permissionRequestViewModel.updatePermissionState(true)
} else {
requestPermissions(this@PermissionRequestActivity, 100)
}
}
}
private fun isPermissionGranted(activity: Activity) = permissions.all {
ContextCompat.checkSelfPermission(activity, it) == PackageManager.PERMISSION_GRANTED
}
private fun requestPermissions(activity: Activity, requestCode: Int) =
ActivityCompat.requestPermissions(activity, permissions, requestCode)
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
100 -> {
binding.tvPermissionState.text = "권한 허용됨!!"
permissionRequestViewModel.updatePermissionState(true)
}
}
}
}
마찬가지로 액티비티에도 hilt 관련 어노테이션을 붙이고, 권한을 요청하는 로직들을 작성했다.
이렇게 작성하고 실행하면 아래와 같이 작동한다.
앱을 실행하면 검은 배경의 흰 체크가 있는 아이콘과 "권한 허용 안 됨!!"이라는 글자가 보인다.
권한 요청 버튼을 누르면 권한 요청 팝업이 표시되면서, 허용을 누르면 흰 배경에 검은 체크가 있는 아이콘으로 바뀌고 글자도 "권한 허용됨!!"으로 바뀐다.
앱을 종료하고 재실행하더라도 앞서 권한을 허용했기 때문에 당연히 권한 허용 상태를 뜻하는 아이콘, 글자가 표시된다. 앱 데이터를 모두 지우고 재실행하면 다시 권한 허용 이전 상태로 되돌아가고, 다시 권한을 허용하면 위 플로우가 반복된다.
이렇게 간단한 권한 체크 예제를 만들어 봤다. 만약 hilt를 모른다면 hilt 어노테이션인 @AndroidEntryPoint, @HiltViewModel을 지우고 ViewModelProvider로 뷰모델 만들어 쓰면 되고, StateFlow는 LiveData로 바꿔 쓰면 된다. checkSelfPermission()으로 권한들이 허용 상태인지 파악한 다음 requestPermissions()로 요청하는 흐름만 기억하면 된다.
'Android' 카테고리의 다른 글
[Android] Android 14 변경사항 미리보기 (0) | 2023.09.04 |
---|---|
[Android] 웹뷰에서 인텐트 호출 시 net::ERR_UNKNOWN_URL_SCHEME 에러 해결 (0) | 2023.08.29 |
[Android] 안드로이드 13에서 Photo Picker 사용하는 법 (0) | 2023.08.27 |
[Android] StateFlow, SharedFlow Best Practices (0) | 2023.08.27 |
[Android] 샌드버드 SDK v4를 사용한 1:1 채팅 기능 구현하기 (0) | 2023.08.23 |