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

mohaxSpb
MobileUp
Published in
4 min readOct 14, 2020

В предыдущих статьях мы создавали аккаунт разработчика для использования 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) // или ошибку кидаем какую-то
}
}
}
}

Используем разные реализации определения геолокации

Если вышеописанное верно для вашего случая, то как и в случае с аналитикой нам понадобятся две разные реализации FusedLocationClient — FusedLocationClientImpl:

  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

--

--