【Android 入門開發實戰:口罩地圖】獲取位置權限

 

【Android 入門開發實戰:口罩地圖】線上免費講義課程目錄

獲取使用者位置權限,除了要在 AndroidManifest.xml 宣告外,因為被列屬為危險權限,從 Android 6 (SDK 23)以上,需要特別額外跟使用詢問獲取,否則會因權限不足,而造成 APP 閃退。詳細權限級別,可以查閱官網文件: Manifest.permission

獲取 APP 權限方法

獲取 APP 權限,主要分成這四個部分。

checkSelfPermission

檢查我們的APP,是否獲得權限

shouldShowRequestPermissionRationale

是否要顯示更多說明解釋為何此權限對話視窗。收到 true,代表需要跟使用者顯示更多說明解釋為何此權限對話視窗,收到 false,代表不需額外顯示給使用者說明。

使用情境:

  • 第一次詢問權限,這個值會收到 false。
  • 第一次詢問,用戶選擇「拒絕」,這個值會收到 true。則需要顯示客制對話視窗,在視窗中解釋為何需要此權限。
  • 用戶允許權限後或是選擇「拒絕且不在詢問」後,這個值會收到 false。

requestPermissions

要求用戶給我們APP使用權限

onRequestPermissionsResult

覆寫此方法,要求權限後,會收到用戶決定是否給權限的結果 CallBack

AndroidManifest.xml

獲取位置權限,需要先在 AndroidManifest.xml 裡,加入此宣告。位置權限有兩種,一個是概略位置存取權(ACCESS_COARSE_LOCATION)和另一個精確位置存取(ACCESS_FINE_LOCATION),擇一即可,我們選擇精確定位權限。

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

MapActivity.kt

我們先新開一個空白的 Activity 頁面,檔名為 MapActivity,並在 AndroidManifest.xml 裡將啟動載入Activity,更改為此 Activity。

<activity android:name=".MapActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

檢查有無權限

透過 checkSelfPermission 方法,檢查有無精準位置(ACCESS_FINE_LOCATION)權限,若沒有則詢問使用者要求獲取權限,若獲得 locationPermissionGranted 設定為 true。

private var locationPermissionGranted = false

...
...
...

private fun getLocationPermission() {
    //檢查權限
    if (ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED
    ) {
        //已獲取到權限
        Toast.makeText(this, "已獲取到位置權限,可以準備開始獲取經緯度", Toast.LENGTH_SHORT).show()
        locationPermissionGranted = true
        //todo checkGPSState()
    } else {
        //詢問要求獲取權限
        requestLocationPermission()
    }
}

詢問要求獲取權限

private fun requestLocationPermission() {
    if (ActivityCompat.shouldShowRequestPermissionRationale(
            this, Manifest.permission.ACCESS_FINE_LOCATION
        )
    ) {
        AlertDialog.Builder(this)
            .setMessage("此應用程式,需要位置權限才能正常使用")
            .setPositiveButton("確定") { _, _ ->
                ActivityCompat.requestPermissions(
                    this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                    REQUEST_LOCATION_PERMISSION
                )
            }
            .setNegativeButton("取消") { _, _ -> requestLocationPermission() }
            .show()
    } else {
        ActivityCompat.requestPermissions(
            this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_LOCATION_PERMISSION
        )
    }
}

處理權限 CallBack

override fun onRequestPermissionsResult(
    requestCode: Int, permissions: Array<out String>, grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)

    when (requestCode) {
        REQUEST_LOCATION_PERMISSION -> {
            if (grantResults.isNotEmpty()) {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //已獲取到權限
                    locationPermissionGranted = true
                    //todo checkGPSState()
                } else if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
                    if (!ActivityCompat.shouldShowRequestPermissionRationale(
                            this,
                            Manifest.permission.ACCESS_FINE_LOCATION
                        )
                    ) {
                        //權限被永久拒絕
                        Toast.makeText(this, "位置權限已被關閉,功能將會無法正常使用", Toast.LENGTH_SHORT).show()

                        AlertDialog.Builder(this)
                            .setTitle("開啟位置權限")
                            .setMessage("此應用程式,位置權限已被關閉,需開啟才能正常使用")
                            .setPositiveButton("確定") { _, _ ->
                                val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
                                startActivityForResult(intent, REQUEST_LOCATION_PERMISSION)
                            }
                            .setNegativeButton("取消") { _, _ -> requestLocationPermission() }
                            .show()
                    } else {
                        //權限被拒絕
                        Toast.makeText(this, "位置權限被拒絕,功能將會無法正常使用", Toast.LENGTH_SHORT).show()
                        requestLocationPermission()
                    }
                }
            }
        }
    }
}

處理設定回來 CallBack

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
        REQUEST_LOCATION_PERMISSION -> {
            getLocationPermission()
        }
    }
}

輸出結果

第一次呈現詢問畫面

若在第一次詢問,選擇「拒絕」,則會出現,我們自定義的對話視窗,可以在這個視窗跟用戶說明為何需要權限。

若是選擇「拒絕且不在詢問」,將不會在出現詢問視窗

參考資料

Build location-aware apps
https://developer.android.com/training/location

Request App Permissions
https://developer.android.com/training/permissions/requesting

permissions API reference page
https://developer.android.com/reference/android/Manifest.permission#ACCESS_FINE_LOCATION

Location Data
https://developers.google.com/maps/documentation/android-sdk/location

Select Current Place and Show Details on a Map
https://developers.google.com/maps/documentation/android-sdk/current-place-tutorial

程式碼範例

範例名稱:獲取位置權限
開發人員:HKT (侯光燦)
程式語言:Kotlin
開發環境:Android Studio 4.1.2 & Android 11 & Kotlin 1.4.30
授權範圍:使用時必須註明出處且不得為商業目的之使用
範例下載點:點我下載