Встраиваем геолокацию от Huawei в Android приложение

    image


    В предыдущих статьях мы создавали аккаунт разработчика для использования Huawei Mobile Services и подготавливали проект к их использованию. И использовали аналитику от Huawei вместо аналога от Google. В этой статье мы будем встраивать определение геолокации от Huawei.


    Вот полный список статей из цикла:


    1. Создаём аккаунт разработчика, подключаем зависимости, подготавливаем код к внедрению. тык
    2. Встраиваем Huawei Analytics. тык
    3. Используем геолокацию от Huawei. ← вы тут
    4. 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)
            // Add this permission too after API=28 if you want to receive location in background
            // if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
            //     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> {
            val permissions = mutableListOf(Manifest.permission.ACCESS_FINE_LOCATION)
            // Add this permission too after API=28 if you want to receive location in background
            // if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
            //     permissions += Manifest.permission.ACCESS_BACKGROUND_LOCATION
            // }
            return permissionsHelper.requestPermission(*permissions.toTypedArray())
        }
    
        @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)
                }
            }
        }
    }

    В итоге реализации отличаются только импортами)


    Аналогично с аналитикой, в 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'
    }

    И не забудьте добавить разрешение на доступ к местоположению в фоне если в приложении планируется такой вариант использования сервиса! Если такое разрешение уже есть в файле AndroidManifest.xml — то можете этот пункт пропустить. Если нет — добавьте его:


    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example">
    
        <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    </manifest>

    Подводные камни


    Надо иметь в виду, что геолокация от Huawei будет работать при следующих условиях:


    1. У вас установлены Huawei Mobile Services на девайсе.
    2. Им выданы нужные разрешения.

    Дальше — встраиваем карты


    С геолокацией мы разобрались, в следующей статье покажем как встроить карты от Huawei в приложение, которое уже использует аналог от Google.


    Весь код, который есть в этом цикле статей вы можете посмотреть в репозитории на GitHub. Вот ссылка: https://github.com/MobileUpLLC/huawei_and_google_services.

    Средняя зарплата в IT

    110 450 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 7 043 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 4

      0
      Юзер согласился на определение местоположения в фоне, а не только во время использования.

      Оч интересно, а это почему так?

        0

        Хм, проверил сейчас, работает и без этого. В статье поправлю, спасибо)

        0
        КПДВ — чудо. Лучшая в своем роде.
        Автору КПДВ — почитайте про цвета — Как выбрать красивые цвета для вашей инфографики. И про композицию стоит почитать.
        • НЛО прилетело и опубликовало эту надпись здесь

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое