티스토리 뷰
1. 위치 권한 팝업 (2가지) (주의사항)
2. GPS 허용 팝업, 위치 얻기
Manifest.permission.ACCESS_FINE_LOCATION (해당 권한 팝업 허용시 ACCESS_COARSE_LOCATION 권한도 허용됨)
Manifest.permission.ACCESS_COARSE_LOCATION (팝업 허용시 ACCESS_FINE_LOCATION 권한은 허용이 안됨)
ContextCompat.checkSelfPermission()
ActivityResultContracts.RequestPermission()
ActivityResultContracts.RequestMultiplePermissions()
PackageManager.PERMISSION_GRANTED
checkSelfPermission
requestPermissions
FusedLocationProviderApi
FusedLocationProviderClient
1. 위치 권한 팝업 (2가지) (주의사항)
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:gravity="center"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_latitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24dp"
tools:text="111.12313"/>
<TextView
android:id="@+id/tv_longitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24dp"
tools:text="122.244231"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/bt_permission_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="권한 요청 첫 번째 방법"
android:textAllCaps="false" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/bt_permission_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="권한 요청 두 번째 방법"
android:textAllCaps="false" />
</LinearLayout>
MainActivity.kt
package com.example.gpsexample
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatButton
import androidx.core.content.ContextCompat
/**
* 1. ActivityResultLauncher.launch() 를 이용하여 위치 권한 요청
* 2. Activity.RequestPermissions() 를 이용하여 위치 권한 요청
*
* 주의 사항 : 첫 번째 방법 사용 시 onRequestPermissionsResult() 의 super() 먼저 호출되고
* registerForActivityResult 콜백이 실행된 후 다시
* onRequestPermissionsResult() super() 하위 코드가 실행된다.
* when() 으로 분기 처리 안 하면 위치 권한 응답 처리가 두 번 된다.
*
* 두 번째 방법 사용 시엔 onRequestPermissionsResult() 만 실행됨.
*
* 즉, 사용자가 시스템 권한 대화상자에 응답하면 onRequestPermissionsResult() 실행됨.
*/
class MainActivity : AppCompatActivity() {
companion object {
const val REQUEST_CODE_PERMISSIONS = 1001
}
private val permissions = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
private val tvLatitude: TextView by lazy { findViewById(R.id.tv_latitude) }
private val tvLongitude: TextView by lazy { findViewById(R.id.tv_longitude) }
private val btPermission1: AppCompatButton by lazy { findViewById(R.id.bt_permission_1) }
private val btPermission2: AppCompatButton by lazy { findViewById(R.id.bt_permission_2) }
private lateinit var activityResultLauncher: ActivityResultLauncher<Array<String>>
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
activityResultLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
if (it.all { permission -> permission.value == true }) {
} else {
Toast.makeText(this, "권한 거부", Toast.LENGTH_SHORT).show()
}
}
setOnClick()
}
/**
* 버튼 클릭 이벤트
*/
@RequiresApi(Build.VERSION_CODES.M)
private fun setOnClick() {
// 권한 요청 첫 번째 방법
btPermission1.setOnClickListener {
if (checkPermission(permissions)) {
activityResultLauncher.launch(permissions)
}
}
// 권한 요청 두 번째 방법
btPermission2.setOnClickListener {
if (!checkPermission(permissions)) {
requestPermissions(permissions, REQUEST_CODE_PERMISSIONS)
}
}
}
/**
* 권한 체크
*/
private fun checkPermission(permissions: Array<String>): Boolean {
return permissions.all {
ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
}
}
/**
* 권한 요청 결과
*/
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
REQUEST_CODE_PERMISSIONS -> {
if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
} else {
Toast.makeText(this, "권한 거부", Toast.LENGTH_SHORT).show()
}
}
}
}
}
Activity.RequestPermissions() 사용시 @RequiresApi(Build.VERSION_CODES.M) 필요
즉, ActivityResultLauncher 사용 추천
주의사항 : 둘 다 onRequestPermissionResult 호출됨. (when 분기처리 한 이유)
2. GPS 허용 팝업, 위치 얻기
LocationRequest
LocationSettingRequest
FusedLocationProviderClient
LocationCallback()
ActivityResultContracts.StartIntentSenderForResult()
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:gravity="center"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_latitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24dp"
tools:text="111.12313"/>
<TextView
android:id="@+id/tv_longitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24dp"
tools:text="122.244231"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/bt_permission_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="권한 요청 첫 번째 방법"
android:textAllCaps="false" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/bt_permission_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="권한 요청 두 번째 방법"
android:textAllCaps="false" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/bt_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="위치 얻기"
android:textAllCaps="false" />
</LinearLayout>
MainActivity.kt
package com.example.gpsexample
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.IntentSender
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.os.Looper
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatButton
import androidx.core.content.ContextCompat
import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.location.*
/**
* 1. ActivityResultLauncher.launch() 를 이용하여 위치 권한 요청
* 2. Activity.RequestPermissions() 를 이용하여 위치 권한 요청
*
* 주의 사항 : 첫 번째 방법 사용 시 onRequestPermissionsResult() 의 super() 먼저 호출되고
* registerForActivityResult 콜백이 실행된 후 다시
* onRequestPermissionsResult() super() 하위 코드가 실행된다.
* when() 으로 분기 처리 안 하면 위치 권한 응답 처리가 두 번 된다.
*
* 두 번째 방법 사용 시엔 onRequestPermissionsResult() 만 실행됨.
*
* 즉, 사용자가 시스템 권한 대화상자에 응답하면 onRequestPermissionsResult() 실행됨.
*/
class MainActivity : AppCompatActivity() {
companion object {
const val REQUEST_CODE_PERMISSIONS = 1001
}
private val permissions = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
private val tvLatitude: TextView by lazy { findViewById(R.id.tv_latitude) }
private val tvLongitude: TextView by lazy { findViewById(R.id.tv_longitude) }
private val btPermission1: AppCompatButton by lazy { findViewById(R.id.bt_permission_1) }
private val btPermission2: AppCompatButton by lazy { findViewById(R.id.bt_permission_2) }
private val btLocation: AppCompatButton by lazy { findViewById(R.id.bt_location) }
private lateinit var permissionResultLauncher: ActivityResultLauncher<Array<String>>
private lateinit var resolutionResultLauncher: ActivityResultLauncher<IntentSenderRequest>
private val locationRequest = LocationRequest.create().apply {
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
interval = 10000
fastestInterval = 5000
numUpdates = 10
}
private val builder = LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest)
.setAlwaysShow(true)
private lateinit var fusedLocationClient: FusedLocationProviderClient
private val locationCallback = object : LocationCallback() {
override fun onLocationResult(p0: LocationResult) {
super.onLocationResult(p0)
tvLatitude.text = p0.lastLocation.latitude.toString()
tvLongitude.text = p0.lastLocation.longitude.toString()
}
override fun onLocationAvailability(p0: LocationAvailability) {
super.onLocationAvailability(p0)
}
}
@SuppressLint("VisibleForTests")
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fusedLocationClient = FusedLocationProviderClient(this)
permissionResultLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
if (it.all { permission -> permission.value == true }) {
getLocation()
} else {
Toast.makeText(this, "권한 거부", Toast.LENGTH_SHORT).show()
}
}
resolutionResultLauncher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
if (it.resultCode == RESULT_OK) {
getLocation()
}
}
setOnClick()
}
override fun onPause() {
super.onPause()
fusedLocationClient.removeLocationUpdates(locationCallback)
}
/**
* 버튼 클릭 이벤트
*/
@RequiresApi(Build.VERSION_CODES.M)
private fun setOnClick() {
// 권한 요청 첫 번째 방법
btPermission1.setOnClickListener {
if (!checkPermission(permissions)) {
permissionResultLauncher.launch(permissions)
} else {
Toast.makeText(this, "이미 권한이 있습니다.", Toast.LENGTH_SHORT).show()
}
}
// 권한 요청 두 번째 방법
btPermission2.setOnClickListener {
if (!checkPermission(permissions)) {
requestPermissions(permissions, REQUEST_CODE_PERMISSIONS)
} else {
Toast.makeText(this, "이미 권한이 있습니다.", Toast.LENGTH_SHORT).show()
}
}
// 위치 얻기
btLocation.setOnClickListener {
getLocation()
}
}
/**
* 권한 체크
*/
private fun checkPermission(permissions: Array<String>): Boolean {
return permissions.all {
ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
}
}
/**
* 위치 얻기
*/
private fun getLocation() {
if (checkPermission(permissions)) {
LocationServices.getSettingsClient(this@MainActivity).checkLocationSettings(builder.build()).run {
addOnSuccessListener { response ->
// All location settings are satisfied. The client can initialize
// location requests here.
// ...
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
}
addOnFailureListener { exception ->
if (exception is ResolvableApiException) {
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
val intentSenderRequest = IntentSenderRequest.Builder(exception.resolution).build()
resolutionResultLauncher.launch(intentSenderRequest)
} catch (sendEx: IntentSender.SendIntentException) {
// Ignore the error.
}
}
}
}
} else {
permissionResultLauncher.launch(permissions)
}
}
/**
* 권한 요청 결과
*/
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
REQUEST_CODE_PERMISSIONS -> {
if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
} else {
Toast.makeText(this, "권한 거부", Toast.LENGTH_SHORT).show()
}
}
}
}
}
참고.
https://developer.android.com/training/location/change-location-settings?hl=ko (위치 요청 다이얼로그)
https://developer.android.com/training/location/request-updates?hl=ko#stop-updates
https://stackoverflow.com/questions/65158308/deprecated-onactivityresult-in-androidx (startResolutionForResult 응답 처리)
'Android > Kotlin' 카테고리의 다른 글
[Kotlin] Lifecycle, LifecycleOwner, LifecycleObserver (0) | 2021.06.05 |
---|---|
[Kotlin] registerForActivityResult (0) | 2021.05.27 |
[Kotlin] View (0) | 2021.05.25 |
[Kotlin] dp와 px (0) | 2021.05.25 |
[Kotlin] CoordinatorLayout (0) | 2021.05.24 |
- Total
- Today
- Yesterday
- Vue.js #Vue.js + javascript
- DataBinding
- TabLayout
- java
- 자바
- fragment
- Architecture Pattern
- MVVM
- activity
- 코틀린
- ViewModel
- JSONObject
- recyclerview
- coroutine
- ViewPager2
- Design Pattern
- View
- Kotlin
- handler
- 혀가 길지 않은 개발자
- Android
- 안드로이드 #코틀린 #Android #Kotlin
- CoordinatorLayout
- James Kim
- XML
- Livedata
- 안드로이드
- Intent
- JSONArray
- ArrayList
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |