[Gradle, Java, Kotlin, Разработка мобильных приложений, Разработка под Android] Встраиваем геолокацию от Huawei в Android приложение

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
14-Окт-2020 18:41


В предыдущих статьях мы создавали аккаунт разработчика для использования Huawei Mobile Services и подготавливали проект к их использованию. И использовали аналитику от Huawei вместо аналога от Google. В этой статье мы будем встраивать определение геолокации от Huawei.
Вот полный список статей из цикла:
  • Создаём аккаунт разработчика, подключаем зависимости, подготавливаем код к внедрению. тык
  • Встраиваем Huawei Analytics. тык
  • Используем геолокацию от Huawei. ← вы тут
  • Huawei maps. Используем вместо Google maps для AppGallery.

С геолокацией немного сложнее, т.к. надо нюансы учитывать. О них, конечно, тоже расскажем.
Как должен выглядеть код в уже готовом проекте
Исходить будем, опять таки, из того, что у вас геолокация для гугла сделана примерно так:
1) Для проверки разрешения пользователя на доступ к его местоположению использована библиотека RxPermissions примерно так:
class PermissionsHelper {
    private var rxPermissions: RxPermissions? = null
    /**
     * Вызываем в Activity#onCreate
     */
    fun attach(activity: FragmentActivity) {
        rxPermissions = RxPermissions(activity)
    }
    /**
     * Вызываем в Activity#onDestroy
     */
    fun detach() {
        rxPermissions = null
    }
    fun requestPermission(vararg permissionName: String): Single<Boolean> {
        return rxPermissions?.request(*permissionName)
            ?.firstOrError()
            ?: Single.error(
                IllegalStateException("PermissionHelper is not attached to Activity")
            )
    }
}

2) Создан свой класс для местоположения:
data class Location(
    val latitude: Double,
    val longitude: Double
) {
    companion object {
        val DEFAULT_LOCATION = Location(59.927752, 30.346944)
    }
}

3) Создана абстракция над поставщиком местоположения:
interface FusedLocationClient {
    fun checkPermissions(): Single<Boolean>
    fun getLastLocation(): Single<Location>
    fun requestLastLocation(): Single<Location>
}

4) И используется она примерно так:
class LocationGateway(
    private val fusedLocationClient: FusedLocationClient
) {
    fun requestLastLocation(): Single<Location> {
        return fusedLocationClient.checkPermissions()
            .flatMap { granted ->
                if (granted) {
                    fusedLocationClient.getLastLocation()
                        .onErrorResumeNext(fusedLocationClient.requestLastLocation())
                } else {
                    Single.just(Location.DEFAULT_LOCATION) // или ошибку кидаем какую-то
                }
            }
    }
}

Используем разные реализации определения геолокации
Если вышеописанное верно для вашего случая, то как и в случае с аналитикой нам понадобятся 2 разные реализации FusedLocationClientFusedLocationClientImpl:
1) В папке src/huawei/kotlin/com/example:
class FusedLocationClientImpl(
    private val permissionsHelper: PermissionsHelper,
    context: Context
) : FusedLocationClient {
    private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
    override fun checkPermissions(): Single<Boolean> {
        val permissions = mutableListOf(Manifest.permission.ACCESS_FINE_LOCATION)
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
            // for huawei we need this permission too after API=28
            permissions += Manifest.permission.ACCESS_BACKGROUND_LOCATION
        }
        return permissionsHelper.requestPermission(*permissions.toTypedArray())
    }
    override fun getLastLocation(): Single<Location> {
        return Single.create { singleEmitter ->
            fusedLocationClient.lastLocation
                .addOnFailureListener {
                    if (singleEmitter.isDisposed) return@addOnFailureListener
                    singleEmitter.onError(it)
                }
                .addOnSuccessListener { newLocation ->
                    if (singleEmitter.isDisposed) return@addOnSuccessListener
                    if (newLocation == null) {
                        singleEmitter.onError(UnknownLocationException())
                    } else {
                        singleEmitter.onSuccess(
                            Location(
                                newLocation.latitude,
                                newLocation.longitude
                            )
                        )
                    }
                }
        }
    }
    override fun requestLastLocation(): Single<Location> {
        return Single.create { singleEmitter ->
            val locationRequest = LocationRequest.create()
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setInterval(5000)
                .setSmallestDisplacement(5.5F)
                .setNumUpdates(1)
            val callback = object : LocationCallback() {
                override fun onLocationResult(result: LocationResult) {
                    if (singleEmitter.isDisposed) return
                    singleEmitter.onSuccess(
                        Location(
                            result.lastLocation.latitude,
                            result.lastLocation.longitude
                        )
                    )
                }
            }
            fusedLocationClient.requestLocationUpdates(locationRequest, callback, null)
            singleEmitter.setCancellable {
                fusedLocationClient.removeLocationUpdates(callback)
            }
        }
    }
}

2) В папке src/google/kotlin/com/example:
class FusedLocationClientImpl(
    private val permissionsHelper: PermissionsHelper,
    context: Context
) : FusedLocationClient {
    private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
    override fun checkPermissions(): Single<Boolean> {
        return permissionsHelper.requestPermission(Manifest.permission.ACCESS_FINE_LOCATION)
    }
    @SuppressLint("MissingPermission")
    override fun getLastLocation(): Single<Location> {
        return Single.create { singleEmitter ->
            fusedLocationClient.lastLocation
                .addOnFailureListener {
                    if (singleEmitter.isDisposed) return@addOnFailureListener
                    singleEmitter.onError(it)
                }
                .addOnSuccessListener { newLocation ->
                    if (singleEmitter.isDisposed) return@addOnSuccessListener
                    if (newLocation == null) {
                        singleEmitter.onError(UnknownLocationException())
                    } else {
                        singleEmitter.onSuccess(
                            Location(
                                newLocation.latitude,
                                newLocation.longitude
                            )
                        )
                    }
                }
        }
    }
    @SuppressLint("MissingPermission")
    override fun requestLastLocation(): Single<Location> {
        return Single.create { singleEmitter ->
            val locationRequest = LocationRequest.create()
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setInterval(5000)
                .setSmallestDisplacement(5.5F)
                .setNumUpdates(1)
            val callback = object : LocationCallback() {
                override fun onLocationResult(result: LocationResult) {
                    if (singleEmitter.isDisposed) return
                    singleEmitter.onSuccess(
                        Location(
                            result.lastLocation.latitude,
                            result.lastLocation.longitude
                        )
                    )
                }
            }
            fusedLocationClient.requestLocationUpdates(locationRequest, callback, null)
            singleEmitter.setCancellable {
                fusedLocationClient.removeLocationUpdates(callback)
            }
        }
    }
}

В итоге реализации отличаются 2 вещами: импортами и тем, что в случае Huawei надо запрашивать разрешение на запрос геолокации в фоне на API>28.
Аналогично с аналитикой, в DI биндим для типа FusedLocationClient экземпляр FusedLocationClientImpl. Для разных сборок будет взята та или иная реализация.
Ну и не забываем, конечно, зависимости в скрипте сборки прописать:
dependencies {
  huaweiImplementation 'com.huawei.agconnect:agconnect-core:1.3.1.300'
  huaweiImplementation 'com.huawei.hms:location:5.0.0.301'
  googleImplementation 'com.google.android.gms:play-services-location:17.0.0'
}

И не забудьте добавить разрешение на доступ к местоположению в фоне для Huawei сборки! Если такое разрешение уже есть в основном файле AndroidManifest.xml — то можете этот пункт пропустить. Если нет — то создайте ещё один файл манифеста в папке src/huawei/ с таким содержимым:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example">
    <!-- huawei location throws error without this permission -->
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>

Подводные камни
Надо иметь в виду, что геолокация от Huawei будет работать при следующих условиях:
  • У вас установлены Huawei Mobile Services на девайсе.
  • Им выданы нужные разрешения.
  • Юзер согласился на определение местоположения в фоне, а не только во время использования.

Дальше — встраиваем карты
С геолокацией мы разобрались, в следующей статье покажем как встроить карты от Huawei в приложение, которое уже использует аналог от Google.
Весь код, который есть в этом цикле статей вы можете посмотреть в репозитории на GitHub. Вот ссылка: https://github.com/MobileUpLLC/huawei_and_google_services.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_gradle, #_java, #_kotlin, #_razrabotka_mobilnyh_prilozhenij (Разработка мобильных приложений), #_razrabotka_pod_android (Разработка под Android), #_gradle, #_java, #_kotlin, #_razrabotka_mobilnyh_prilozhenij (Разработка мобильных приложений), #_razrabotka_pod_android (Разработка под Android), #_gradle, #_java, #_kotlin, #_razrabotka_mobilnyh_prilozhenij (
Разработка мобильных приложений
)
, #_razrabotka_pod_android (
Разработка под Android
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 14:58
Часовой пояс: UTC + 5