java - Android 中的地理标记

标签 java android kotlin kotlin-coroutines

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

是否可以实现此功能以及如何实现?

注意:这是我的第一个重大项目。

最佳答案

通过位置创建舒适的用户体验是一项艰巨的任务。以下是您必须解决的一些问题:

  1. 用户可能已停用全局位置功能。<​​/li>
  2. 该应用可能没有使用位置的权限。
  3. 您想要请求用户许可和/或启用位置。
  4. 您不想一遍又一遍地询问来打扰用户。
  5. 请求许可意味着暂停您的 Activity 以支持请求许可的系统 Activity 。有一个类似但不同的机制来请求启用位置。
  6. 您的 Activity 将在用户响应时恢复,您需要重写接收结果的特殊回调方法。
  7. 在需要时尝试获取位置并不好。这可能需要时间。相反,您必须从一开始就要求 Android 向您推送位置更新。
  8. 用户在使用应用时可能会撤销位置权限,您必须不断重新检查权限,应对突发异常,并在位置权限恢复时重复请求接收位置更新。

以下是我的项目中的一些 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/

相关文章:

java - 从 panel2 中的按钮添加到 panel1 中的文本字段

java - Google map 维基百科图层

java - 在 Java 8 中,reduce() 方法如何处理并行流?

android - 生成 ionic SDK

androidx.core :core-ktx:1. 0.0 小部件包丢失

java - 如何在java中获取没有命名空间的XML节点名称?

android - 需要使用闹钟管理器定期重启安卓设备

java - Hilt 和 WorkManager 错误 : java. lang.NoSuchMethodException :<init> [class android. content.Context,类 androidx.work.WorkerParameters]

inheritance - Kotlin:如何继承数据类中的属性

mysql - Spring Hibernate 无法在初始化数据库时添加外键约束