我正在开发一个需要用户能够对通过相机拍摄的照片进行地理标记的应用程序。该应用程序旨在鼓励用户对树进行地理标记,以鼓励造林并减少全球变暖。

有可能实现此功能,如何实现?

注意:这是我的第一个主要项目。

最佳答案

创建具有位置感的舒适用户体验是一项艰巨的任务。这些是您必须解决的一些问题:


用户可能已全局禁用位置。
该应用可能没有使用位置的权限。
您想询问用户许可和/或启用位置。
您不想一遍又一遍地打扰用户。
寻求许可意味着暂停您的活动,而转向需要许可的系统活动。有类似但不同的机制要求启用该位置。
当用户响应时,您的活动将恢复,您需要重写一个特殊的回调方法来接收结果。
仅在需要时尝试获取位置是不好的。可能需要一些时间。相反,您必须首先要求Android将位置更新推送给您。
用户可能会在使用应用程序时撤销位置权限,您必须不断地重新检查权限,以应对突发异常情况,并在位置权限返回时重复请求以接收位置更新。


以下是我的项目中的一些片段,可以帮助您入门:

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/

10-12 02:36