找咖啡專案的需求 :
昨天已經成功的實作出我們的第一個需求 - 點擊地圖標記顯示商家資訊,接著繼續做出第二的需求 - 顯示我的位置附近的商家資訊。
要顯示我的位置附近的商家,首先需要取得使用者裝置的位置,就從這邊下手吧 !
今天的實作範例 :
每種權限都具有以下特性:
文件上說,如果應用程式包含只會分享或接收位置資訊一次,或只在指定時間內分享位置資訊的功能,則必須取得前景位置資訊存取權才能執行該功能。
在 Android 10 以上,系統會強制要宣告前景服務類型 :
<!-- Recommended for Android 9 (API level 28) and lower. -->
<!-- Required for Android 10 (API level 29) and higher. -->
<service
android:name="MyNavigationService"
android:foregroundServiceType="location" ... >
<!-- Any inner elements would go here. -->
</service>
如果應用程式中會持續和其他地使用者分享位置或是使用 Geofencing API,我們就需要背景位置資訊存取權。
在 Android 10 (API 級別 29) 以上版本中,會被強制宣告 [ACCESS_BACKGROUND_LOCATION](https://developer.android.com/reference/android/Manifest.permission?hl=zh-tw#ACCESS_BACKGROUND_LOCATION)
權限 :
<manifest ... >
<!-- Required only when requesting background location access on
Android 10 (API level 29) and higher. -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>
但我們專案應該用不到,就提供給各位參考了!
先來取得使用者權限,Android 提供兩種位置存取權,選擇的權限會決定 API 傳回的位置精確度。
android.permisjsion.ACCESS_COARSE_LOCATION
: 允許 API 傳回裝置的大概位置。取得權限後,就能依據定位服務取得裝置的概略位置,這邊有官方文件可以看更多。android.permission.ACCESS_FINE_LOCATION
: 允許 API 透過可用的定位服務供應商 (包括全球定位系統 (GPS) 以及 Wi-Fi 和行動數據,盡可能精確判斷位置。文件有提供大概位置和精確位置的定義 :
以下為範例程式碼 :
val locationPermissionRequest = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
when {
permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
// Precise location access granted.
}
permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
// Only approximate location access granted.
} else -> {
// No location access granted.
}
}
}
// ...
// Before you perform the actual permission request, check whether your app
// already has the permissions, and whether your app needs to show a permission
// rationale dialog. For more details, see Request permissions.
locationPermissionRequest.launch(arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION))
接著來專案內實作,我們在onMapReady()
內向使用者取得權限 :
/**
* Flag indicating whether a requested permission has been denied after returning in
* [.onRequestPermissionsResult].
*/
private var permissionDenied = false
// Update the map configuration at runtime.
override fun onMapReady(googleMap: GoogleMap) {
// return early if the map was not initialised properly
map = googleMap
// create bounds that encompass every location we reference
val boundsBuilder = LatLngBounds.Builder()
// include all places we have markers for on the map
places.keys.map { place -> boundsBuilder.include(places.getValue(place)) }
val bounds = boundsBuilder.build()
with(map) {
// Set the map type
mapType = com.google.android.gms.maps.GoogleMap.MAP_TYPE_NORMAL
// Display traffic 、指南針、縮放按鈕
isTrafficEnabled = true
uiSettings.isCompassEnabled = true
uiSettings.isZoomControlsEnabled = true
// Override the default content description on the view, for accessibility mode.
// Ideally this string would be localised.
setContentDescription("Map with lots of markers.")
moveCamera(com.google.android.gms.maps.CameraUpdateFactory.newLatLngBounds(bounds, 50))
setOnMyLocationButtonClickListener(this@MainActivity)
setOnMyLocationClickListener(this@MainActivity)
}
// Add lots of markers to the googleMap.
addMarkersToMap()
// 取得位置權限
requestLocationPermissions()
}
// ...
/**
* 權限 callback
*/
private val locationPermissionRequest = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
when {
permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
// Precise location access granted.
enableMyLocation()
}
permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
// Only approximate location access granted.
enableMyLocation()
}
else -> {
// No location access granted.
permissionDenied = true
}
}
}
/**
* 取得位置權限
*/
private fun requestLocationPermissions() {
locationPermissionRequest.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
}
/**
* Enables the My Location layer if the fine location permission has been granted.
*/
@SuppressLint("MissingPermission")
private fun enableMyLocation() {
// 1. Check if permissions are granted, if so, enable the my location layer
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
map.isMyLocationEnabled = true
return
}
// 3. Otherwise, request permission
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
),
LOCATION_PERMISSION_REQUEST_CODE
)
}
override fun onMyLocationButtonClick(): Boolean {
Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT)
.show()
// Return false so that we don't consume the event and the default behavior still occurs
// (the camera animates to the user's current position).
return false
}
override fun onMyLocationClick(location: Location) {
Toast.makeText(this, "Current location:\n$location", Toast.LENGTH_LONG)
.show()
}
companion object {
/**
* Request code for location permission request.
*
* @see .onRequestPermissionsResult
*/
private const val LOCATION_PERMISSION_REQUEST_CODE = 1
}
執行後就會跳出詢問權限的對話框了~