2024년 5월 27일 월요일

[Android Studio 팁] powershell7 터미널 설정

증상:

Android Studio 터미널

Powershell7 을 윈도에 설치하였지만 Android Studio 터미널은 내장 powershell (5.1 버전) 이다.

해결:

File -> Settings

Tools -> Terminal -> Application Settings -> Shell path


e.g.) C:\Program Files\PowerShell\7\pwsh.exe

Android Studio 터미널


2024년 5월 21일 화요일

KSP? KAPT? annotationProcessor?

증상:

  • Room tutorial 수행 중 kapt? ksp? 의존성을 추가하는 부분이 있었고 적용하였더니 빌드 에러가 발생했다
    • 어떤 함수를 찾지 못하는 문제
      • java.lang.NoSuchMethodError: 'void org.jetbrains.kotlin.incremental.IncrementalCompilationContext.<init>(org.jetbrains.kotlin.incremental.storage.FileToPathConverter, org.jetbrains.kotlin.incremental.storage.FileToPathConverter, boolean, org.jetbrains.kotlin.incremental.CompilationTransaction, org.jetbrains.kotlin.build.report.ICReporter, boolean, boolean, boolean, int, kotlin.jvm.internal.DefaultConstructorMarker)'
    • Impl 클래스가 없다고 하는 문제
      • Android room persistent: AppDatabase_Impl does not exist
  • 그래서 KSP 가 뭔지 구글링 하다 보니 kapt 에서 ksp 로 mirgration 하는 문서가 있었다 (kapt 는 ksp 의 전신이며 ksp 가 kapt 보다 빌드 속도가 많이 개선되었다고 함)
  • kapt 는 또 뭔가 해서 찾아보았더니 그전에 사용되던 annotationProcessor 가 바뀐거란다 (참고: https://3edc.tistory.com/65)
  • 원하는 설명은 찾기 어렵고 후신의 플러그인들만 검색되는 상태


해결법:



2024년 5월 20일 월요일

[Kotlin] 권한 처리

권한 처리 방법 정리


예시 화면



권한 처리 과정


권한 처리를 위하여 AndroidManifest.xml 에 작성할 부분과 코드 상 작성할 부분을 구분하여 정리

권한 요청은 두 가지 방법
  1. requestPermission() 함수를 이용하는 방식
  2. ActivityResultApi 를 사용하는 방식

전체 샘플 코드

  • Github
    • 주요코드 위치: android/PermissionSample/app/src/main/java/com/example/permissionsample/CheckPermission.kt


목차

  1. AndroidManifest.xml 에 필요 권한 작성
    1. uses-permission 설명
    2. uses-feature 설명
  2. [코드] 권한 확인하기
  3. [코드] 권한 요청하기 (2가지 방식)
    1. requestPermissions() & onRequestPermissionsResult() 방식
    2. ActivityResultApi 방식


1. AndroidManifest.xml 에 필요 권한 작성


예제) Camera 와 audio 녹음 권한 필요시

<!-- A camera with burst capability is required to use this application -->
<uses-feature android:name="android.hardware.camera.any" />

<!-- Permission declarations -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

안드로이드 권한 목록

1.1. uses-permission 설명


설명


앱이 필요로 하는 권한들 선언


예제) AndroidManifest.xml


<!-- Permission declarations -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />



1.2. uses-feature 설명


설명


마켓플레이스 (Google Play 스토어) 에 필터링 적용


예제) AndroidManifest.xml

<!-- A camera with burst capability is required to use this application -->
<uses-feature android:name="android.hardware.camera.any" />



2. [코드] 권한 확인하기

https://developer.android.com/training/permissions/requesting#already-granted


예제) 권한 목록과 모든 권한 확인


import android.Manifest
import androidx.core.content.ContextCompat

...

private val PERMISSIONS_REQUIRED = arrayOf(
        Manifest.permission.CAMERA,
        Manifest.permission.RECORD_AUDIO)

...

fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {
    ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}

코드 설명


shouldShowRequestPermissionRationale

예)


ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA)


설명

사용자 UI, 예를 들어 권한이 필요한 이유를 설명하는 화면를 보여줘야할 필요가 있다고 안드로이드 운영체제가 판단하는지 여부를 확인하는 함수

문제는 언제 true/false 값을 주는지 앱 개발자 측면에서 명확하지는 않다.

다만 안드로이드 공식 예제를 통해 처리 시점을 가늠해 본다



3. [코드] 권한 요청


권한 요청하는 방식은 2가지

3.1. requestPermissions() & onRequestPermissionsResult() 방식





requestPermissions() -> override fun onRequestPermissionsResult()


3.2. ActivityResultApi 방식 (공식 문서 추천)


requestPermissions 와 차이점
  • 주요 차이점: request code 필요 없음
  • 코드 스타일: 권한 핸들러를 따로 독립적으로 구현하여 Activity flow 와 섞임 방지

단일 권한


예제)

class MainActivity : AppCompatActivity() {
    private val requestPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
        // returns boolean representind whether the 
        // permission is granted or not
        if (isGranted) {
            Log.i("DEBUG", "permission granted")
        } else {
            Log.i("DEBUG", "permission denied")
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById<Button>(R.id.button).setOnClickListener {
            // call launch with string containing permission to be 
            // requested to show the system permission dialog
            requestPermission.launch(Manifest.permission.CAMERA)
        }
    }
}

복수 권한


예제)

class MainActivity : AppCompatActivity() {
    private val requestPermission = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->

        permissions.forEach { actionMap ->
            when (actionMap.key) {
                Manifest.permission.CAMERA -> {
                    if (actionMap.value) {
                        
                        Log.i("DEBUG", "permission granted")
                    } else {

                        Log.i("DEBUG", "permission denied")
                    }
                }
            }
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById<Button>(R.id.button).setOnClickListener {

            requestMultiplePermissions.launch(
                arrayOf(
                    Manifest.permission.CAMERA,
                    Manifest.permission.ACCESS_FINE_LOCATION
                )
            )
        }
    }
}