iT邦幫忙

2021 iThome 鐵人賽

DAY 23
0
Mobile Development

Android 口罩地圖入門實戰 30 天 (使用 Kotlin 程式語言)系列 第 23

Day 23:獲取位置權限

本篇文章同步發表在 HKT 線上教室 部落格,線上影音教學課程已上架至 UdemyYoutube 頻道。另外,想追蹤更多相關技術資訊,歡迎到 臉書粉絲專頁 按讚追蹤喔~

程式碼範例

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

在之前的介紹當中,我們主要的重心是放在,如何在滾動式列表上呈現藥局資訊。而在接下來的重心,我們將會放在,如何在 Google Map 地圖上,呈現藥局地點與藥局相關資訊。將會依序的跟大家介紹:

  1. 如何獲取裝置位置權限
  2. 如何檢查 GPS 狀態
  3. 如何獲取裝置經緯度座標位置
  4. 如何在 Google Map 上顯示藥局相關資訊

所以今天要先來跟大家介紹第一個部分,如何獲取使用者位置權限,除了要在 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()
        }
    }
}

輸出結果

第一次呈現詢問畫面

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

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

參考資料

HKT 線上教室
https://tw-hkt.blogspot.com/

Freepik
https://www.freepik.com/

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


那今天【iThome 鐵人賽】就介紹到這邊囉~

順帶一提,KT 線上教室,臉書粉絲團,會不定期發佈相關資訊,不想錯過最新資訊,不要忘記來按讚,追蹤喔!也歡迎大家將這篇文章分享給更多人喔。

我們明天再見囉!!!掰掰~


上一篇
Day 22:Spinner 下拉選單結合縣市鄉鎮小工具
下一篇
Day 24:檢查GPS狀態
系列文
Android 口罩地圖入門實戰 30 天 (使用 Kotlin 程式語言)30

尚未有邦友留言

立即登入留言