[Huawei Developers] Freemind R:
More information like this, you can visit HUAWEI Developer Forum​

Original link: https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0202351086095380160&fid=0101187876626530001

Location is essential for many apps and developers need services for getting location data. Huawei Location Kit helps developers in enabling their apps to get quick and accurate user locations and expand global positioning capabilities by using GPS, Wi-Fi, and base station locations.

First of all, we have to get a Huawei Developer Account and create a project from Huawei App Gallery Console but since this article is about to Location Kit, we will not cover these topics.

You can refer developer account documentation for creating account, and follow this codelab for creating a project at App Gallery Console.

After enabling Location Kit from Manage API section at console, we need to add Location Kit dependency.

implementation 'com.huawei.hms:location:5.0.0.301'

After sync project, we are ready to code.

We will cover necessary methods one by one then collect them in a single class for the sake of maintainable and clean code.

First we have to add permissions to our app

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

If app needs to continuously locate the device when it runs in the background in Android Q, apply for the ACCESS_BACKGROUND_LOCATION

After applying for permissions in the AndroidManifest file, apply for the permissions dynamically in the code (according to requirements for dangerous permissions in Android 6.0).

if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
Log.i(TAG, "sdk < 28 Q");
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
String[] strings =
{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
ActivityCompat.requestPermissions(this, strings, 1);
}
} else {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this,
"android.permission.ACCESS_BACKGROUND_LOCATION") != PackageManager.PERMISSION_GRANTED) {
String[] strings = {android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
"android.permission.ACCESS_BACKGROUND_LOCATION"};
ActivityCompat.requestPermissions(this, strings, 2);
}
}

The location function of Huawei Location Kit depends on the device location settings. If location disabled we can not obtain the device location so it is better to check location setting before obtaining the location.

Check Location Settings

private val mSettingsClient: SettingsClient by lazy {
LocationServices.getSettingsClient(context)
}

SettingsClient instance allow us to call checkLocationSettings method for obtain to device location settings.

fun checkLocationSettingsAndShowPopup(
activity: Activity,
onSuccess: ((locationSettingsResponse: LocationSettingsResponse?) -> Unit)? = null
) {
val builder = LocationSettingsRequest.Builder()
val locationRequest = LocationRequest()
builder.addLocationRequest(locationRequest)
val locationSettingsRequest = builder.build()
mSettingsClient.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener {
// Initiate location requests when the location settings meet the requirements.
LogUtils.d("LocationManager -> Device location is open")
// Notify
onSuccess?.invoke(it)
}
.addOnFailureListener { e ->
// Device location settings do not meet the requirements.
val statusCode = (e as ApiException).statusCode
LogUtils.d("LocationManager -> Device location is close with status code $statusCode, User will get system dialog for enable location")
when (statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
val rae = e as ResolvableApiException
// Call startResolutionForResult to display a pop-up asking the user to enable related permission.
activity.startIntentSenderForResult(
rae.resolution.intentSender,
REQUEST_CHECK_SETTINGS, null, 0, 0, 0, null
)
} catch (sie: IntentSender.SendIntentException) {
LogUtils.d("LocationKit -> Error while showing system pop-up with error message:" + sie.message)
}
}
}
}

If the location settings do not meet the requirements, we are showing a system dialog for request location permission and catch result in related Activity or Fragment

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CHECK_SETTINGS) {
when (resultCode) {
Activity.RESULT_OK -> {
LogUtils.d("User confirm to access location")
}
Activity.RESULT_CANCELED -> {
LogUtils.d("User denied to access location")
}
}
}
}

After successfully enable device location, we can use location related APIs

private val mFusedLocationProviderClient: FusedLocationProviderClient by lazy {
LocationServices.getFusedLocationProviderClient(context)
}

We are creating FusedLocationProviderClient instance first. We will use it to call location-related APIs like location availability, last known location, register and unregister location updates.

Location Availability

fun getLocationAvailability(
onSuccess: ((locationAvailability: LocationAvailability?) -> Unit)? = null,
onFail: ((exception: Exception) -> Unit)? = null
) {
val locationAvailabilityTask: Task<LocationAvailability> = mFusedLocationProviderClient.locationAvailability
locationAvailabilityTask.addOnSuccessListener { locationAvailability ->
if (locationAvailability != null) {
LogUtils.d("Location Kit -> getLocationAvailability onSuccess:${locationAvailability.isLocationAvailable}")
}
// Notify
onSuccess?.invoke(locationAvailability)
}
.addOnFailureListener { e ->
LogUtils.d("getLocationAvailability onFailure:" + e.message)
// Notify
onFail?.invoke(e)
}
}

For obtaining location availability from Huawei Location Services, we call locationAvailability method of FusedLocationProviderClient instance and listen success and failure callbacks.

Last Known Location

fun getLastKnownLocation(
onSuccess: ((lastKnownLocation: Location?) -> Unit)? = null,
onFail: ((exception: Exception) -> Unit)? = null
) {
val task: Task<Location> = mFusedLocationProviderClient.lastLocation
task.addOnSuccessListener { lastKnowLocation ->
if (lastKnowLocation == null) {
LogUtils.d("LocationKit -> Last Known Location is empty")
} else {
val currentLatLng = LatLng(
lastKnowLocation.latitude,
lastKnowLocation.longitude
)
LogUtils.d("LocationKit -> Last Known Location: $currentLatLng")
}
// Notify
onSuccess?.invoke(lastKnowLocation)
}.addOnFailureListener { exception ->
LogUtils.d("LocationKit -> Failed to get Last Known Location with exception: ${exception.localizedMessage}")
// Notify
onFail?.invoke(exception)
}
}

Just like location availability, we just call lastLocation method of FusedLocationProviderClient instance and listen success and failure callbacks.

If the obtained device location is null, the possible cause is that the system cache has been cleared. In this case, we need to call the requestLocationUpdates method to obtain the device location and update the cache, and then call the lastLocation method to obtain the device location.

Location Updates

private fun createLocationCallback(
onSuccess: ((location: Location?) -> Unit)? = null,
onFail: ((locationAvailability: LocationAvailability?) -> Unit)? = null
): LocationCallback {
return object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
val currentLatLng = LatLng(
locationResult.lastLocation.latitude,
locationResult.lastLocation.longitude
)
LogUtils.d("LocationKit -> currentLatLng: $currentLatLng")
// Notify
onSuccess?.invoke(locationResult.lastLocation)
}
override fun onLocationAvailability(locationAvailability: LocationAvailability) {
val flag = locationAvailability.isLocationAvailable
LogUtils.i("LocationKit isLocationAvailable:$flag")
// Notify
onFail?.invoke(locationAvailability)
}
}
}

For request location updates, we have to create a LocationCallback. This callback will be triggered every time when device get new location.

Also we have to specify location request conditions. In order to do that we create a LocationRequest.

val mLocationRequest = LocationRequest()
// Set the location update interval (in milliseconds).
mLocationRequest.interval = 10000
// Set the weight.
mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY

If we want to obtain location once only we can set numUpdates.

// Get location update only once
mLocationRequest.numUpdates = 1

Finally we can start listening location updates by calling requestLocationUpdates method of FusedLocationProviderClient.

fun registerLocationUpdates(
interval: Long = 10000,
onSuccess: ((location: Location?) -> Unit)? = null,
onFail: ((locationAvailability: LocationAvailability?) -> Unit)? = null
) {
val mLocationRequest = LocationRequest()
// Set the location update interval (in milliseconds).
mLocationRequest.interval = interval
// Set the weight.
mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
// Create location callback
if (mLocationCallback == null)
mLocationCallback = createLocationCallback(onSuccess, onFail)
// Request location update
mFusedLocationProviderClient.requestLocationUpdates(
mLocationRequest,
mLocationCallback,
Looper.getMainLooper()
)
.addOnSuccessListener {
// Requesting location updates is started successfully.
LogUtils.d("LocationKit -> Start Location Updates Successfully")
}
.addOnFailureListener { exception ->
// Failed to start location updates.
LogUtils.d("LocationKit -> Start Location Updates Failed with exception: ${exception.localizedMessage}")
}
}

If app does not need to receive location updates anymore, stop the location update function to reduce power consumption. To do this, call the removeLocationUpdates method, and pass the LocationCallback.

fun unregisterLocationUpdates() {
mLocationCallback?.let { locationCallback ->
mFusedLocationProviderClient.removeLocationUpdates(locationCallback)
.addOnSuccessListener {
// Requesting location updates is stopped successfully.
LogUtils.d("LocationKit -> Stop Listening Location Successfully")
}
.addOnFailureListener {
// Failed to stop requesting location updates.
LogUtils.d("LocationKit -> Stop Listening Location Failed with exception: ${it.localizedMessage}")
}
}
}

LocationCallback must be same callback that we used at requestLocationUpdates method.

We have covered necessary location related methods, now lets collect them in a single manager class. In this way we can easily separate logic from view and class will be more maintainable

You can see whole class from this gist

In Activity or Fragment, we can easily use this manager class.

val locationManager = HuaweiLocationManager(this)

locationManager.registerLocationUpdates(onSuccess = {
viewModel.updateCurrentLocation(it)
})

We can see location updates in logcat

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/026/840/403/5190090000026840403.20200916072040.47130898942924167392104893657980:50510923021055:2800:3410E22A5D1973EFB80EA351685C441F743927628596AB8CC40E6FA9F2B27106.jpg"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/026/840/403/5190090000026840403.20200916072040.47130898942924167392104893657980:50510923021055:2800:3410E22A5D1973EFB80EA351685C441F743927628596AB8CC40E6FA9F2B27106.jpg[/IMG]

Some important key points

Currently, HUAWEI Location Kit can only be used on Huawei phones.

The location permission must be granted to HMS Core (APK). In Android Q, Location must be set to Always for HMS Core (APK).

Min API level should be 19 and EMUI Version should be 5.0 or later

References

https://developer.huawei.com/consumer/en/hms/huawei-locationkit

https://github.com/HMS-Core/hms-location-demo-android-studio

https://github.com/huaweicodelabs/LocationKit