我正在开发一个应用程序,该应用程序要求用户能够对通过相机拍摄的照片进行地理标记。该应用程序旨在鼓励用户地理标记树,以鼓励植树造林并减少全局变暖。
是否可以实现此功能以及如何实现?
注意:这是我的第一个重大项目。
最佳答案
通过位置创建舒适的用户体验是一项艰巨的任务。以下是您必须解决的一些问题:
- 用户可能已停用全局位置功能。</li>
- 该应用可能没有使用位置的权限。
- 您想要请求用户许可和/或启用位置。
- 您不想一遍又一遍地询问来打扰用户。
- 请求许可意味着暂停您的 Activity 以支持请求许可的系统 Activity 。有一个类似但不同的机制来请求启用位置。
- 您的 Activity 将在用户响应时恢复,您需要重写接收结果的特殊回调方法。
- 在需要时尝试获取位置并不好。这可能需要时间。相反,您必须从一开始就要求 Android 向您推送位置更新。
- 用户在使用应用时可能会撤销位置权限,您必须不断重新检查权限,应对突发异常,并在位置权限恢复时重复请求接收位置更新。
以下是我的项目中的一些 fragment ,可以帮助您入门:
private val Context.fusedLocationProviderClient get() = getFusedLocationProviderClient(this)
private suspend fun FusedLocationProviderClient.tryFetchLastLocation(): Location? =
lastLocation.await()
?.also { info { "Got response from getLastLocation()" } }
?: run { warn { "getLastLocation() returned null" }; null }
suspend fun Context.canUseLocationFg() =
appHasLocationPermission() &&
locationSettingsException(locationRequestFg, locationRequestBg) == null
import android.Manifest.permission.ACCESS_FINE_LOCATION
import androidx.core.content.PermissionChecker.PERMISSION_GRANTED
fun Context.appHasLocationPermission() =
checkSelfPermission(this, ACCESS_FINE_LOCATION) == PERMISSION_GRANTED
suspend fun Context.locationSettingsException(
vararg locationRequests: LocationRequest
): ApiException? = try {
getSettingsClient(this)
.checkLocationSettings(LocationSettingsRequest.Builder()
.addAllLocationRequests(locationRequests.asList()).build())
.await()
null
} catch (e: ApiException) { e }
const val CODE_REQUEST_FINE_LOCATION = 13
const val CODE_RESOLVE_API_EXCEPTION = 14
suspend fun Fragment.checkAndCorrectPermissionsAndSettings() {
with(context!!) {
if (!appHasLocationPermission()) {
warn { "FG: our app has no permission to access fine location" }
delay(WAIT_MILLISECONDS_BEFORE_ASKING)
if (!appHasLocationPermission()) {
startIntentRequestLocationPermission()
return
}
}
if (locationSettingsException(locationRequestFg, locationRequestBg) == null) return
warn { "FG: ResolvableApiException for location request (probably location disabled)" }
if (!mainPrefs.shouldAskToEnableLocation) return
delay(WAIT_MILLISECONDS_BEFORE_ASKING)
locationSettingsException(locationRequestFg, locationRequestBg)
?.let { it as? ResolvableApiException }
?.also { startIntentResolveException(it) }
}
}
fun Fragment.startIntentRequestLocationPermission() =
requestPermissions(arrayOf(ACCESS_FINE_LOCATION), CODE_REQUEST_FINE_LOCATION)
fun Fragment.startIntentResolveException(e: ResolvableApiException) =
startIntentSenderForResult(e.resolution.intentSender, CODE_RESOLVE_API_EXCEPTION, null, 0, 0, 0, null)
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
if (requestCode != CODE_REQUEST_FINE_LOCATION) return
permissions.zip(grantResults.asList())
.find { (perm, _) -> perm == ACCESS_FINE_LOCATION }
?.also { (_, result) ->
if (result == PermissionChecker.PERMISSION_GRANTED) {
info { "User has granted us the fine location permission" }
} else {
warn { "User hasn't granted us the fine location permission (grant result: ${grantResults[0]})" }
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode != CODE_RESOLVE_API_EXCEPTION) return
if (resultCode == Activity.RESULT_OK) {
info { "ResolvableApiException is now resolved" }
} else {
warn { "ResolvableApiException resolution failed with code $resultCode" }
activity!!.mainPrefs.applyUpdate { setShouldAskToEnableLocation(false) }
}
}
val locationRequestFg = LocationRequest().apply {
interval = 1000
fastestInterval = 10
priority = PRIORITY_BALANCED_POWER_ACCURACY
}
suspend fun Context.receiveLocationUpdatesFg(locationState: LocationState) {
fusedLocationProviderClient.apply {
tryFetchLastLocation()?.also {
info { "lastLocation: ${it.description}" }
locationState.location = it
}
LocationCallbackFg.locationState = locationState
requestLocationUpdates(locationRequestFg, LocationCallbackFg, null).await()
info(CC_PRIVATE) { "FG: started receiving location updates" }
}
}
object LocationCallbackFg : LocationCallback() {
var locationState: LocationState? = null
override fun onLocationResult(result: LocationResult) {
val lastLocation = result.lastLocation
info { "FG: received location ${lastLocation.description}" }
locationState?.apply { location = lastLocation }
?: warn { "LocationCallbackFg received an event while not in use" }
}
}
该代码依赖于 Task.await()
,它位于 Kotlin 的库 kotlinx-coroutines-play-services
中。
关于java - Android 中的地理标记,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59086342/