[Huawei Developers] HMS Community:
Introduction​

If you are new to series of this article, follow the below article.

In this series of article, we will learn about Navigation Glove application and also we will learn about integration of the Huawei Location kit in Navigation Glove IoT application.

One of the major features of android framework is Location Kit. You can see, the Location Kit is widely used in lot of apps those provides services like food ordering, Taxi booking, health tracking, social networking and also finance and lot more. The Location Kit is part of Huawei Mobile service and in the same kit geofencing and activity recognition are included.

Most Android devices allowed to determine the current geo location. This can be done via a GPS (Global Positioning System) module, via cell tower triangulation and via wifi networks.

The Location SDK for Android combines the Global Navigation Satellite System (GNSS), Wi-Fi, and base station location functionalities into your app to build up global positioning capabilities, allowing you to provide flexible location-based services for global users.

Function of Location Kit

Fused Location

Activity Identification

Geofence

High-Precision Location

Indoor Location

Geocoding

In this article, we will learn only about the Fused Location. Most of the developer does not know about the location permission. Follow the details about when we need what kind of permission.

ACCESS_COARSE_LOCATION: approximate location permission, which provides location information accurate to the city block level.

ACCESS_FINE_LOCATION: precise location permission, which allows your app to obtain the user location which is more accurate than that obtained based on the ACCESS_COARSE_LOCATION permission.

ACCESS_BACKGROUND_LOCATION: background location permission, which allows your app to obtain the user location when it runs in the background in Android 10 (API level: 29). In Android 10 or later, this permission is a dangerous permission and needs to be dynamically applied for. In versions earlier than Android 10, your app can obtain the user location regardless of whether it runs in the foreground or background, as long as it is assigned the ACCESS_COARSE_LOCATION permission.

Supported Platforms​

Android

HarmonyOS (Java)

iOS (Objective C)

Rest API

Prerequisite​

AppGallery Account

Android Studio 3.X

SDK Platform 19 or later

Gradle 5.4.1 or later

HMS Core (APK) 4.0.0.300 or later

Huawei Phone EMUI 5.0 or later

Non-Huawei Phone Android 5.1 or later

Service integration on AppGallery

1. We need to register as a developer account in AppGallery Connect.

2. Create an app by referring to Creating a Project and Creating an App in the Project.

3. Set the data storage location based on the current location.

4. Enabling Location Kit Service on AppGallery.

5. Generating a Signing Certificate Fingerprint.

6. Configuring the Signing Certificate Fingerprint.

7. Get your agconnect-services.json file to the app root directory.

Client development

1. Create android project in android studio IDE.

2. Add the maven URL inside the repositories of buildscript and allprojects respectively (project level build.gradle file).

maven { url 'https://developer.huawei.com/repo/' }

3. Add the classpath inside the dependency section of the project level build.gradle file.

classpath 'com.huawei.agconnect:agcp:1.5.2.300'

4. Add the plugin in the app-level build.gradle file.

apply plugin: 'com.huawei.agconnect'

5. Add the below library in the app-level build.gradle file dependencies section.

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

6. Add all the below permission in the AndroidManifest.xml.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

7. If your app needs to locate continuously the device when it runs in the background in Android 10 or later, declare the ACCESS_BACKGROUND_LOCATION permission in the AndroidManifest.xml file. This step is optional.

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

8. Sync the project.

Now let’s learn the coding part.

Fused Location

Step 1: If you are running application Android 6 or later first check the permission.

private fun checkPermission() {
// check location permission
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
) {
val strings = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
ActivityCompat.requestPermissions(this, strings, 1)
}
} else {
if (ActivityCompat.checkSelfPermission(
this@HomeScreenActivity,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this@HomeScreenActivity,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this@HomeScreenActivity,
"android.permission.ACCESS_BACKGROUND_LOCATION"
) != PackageManager.PERMISSION_GRANTED
) {
val strings = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
"android.permission.ACCESS_BACKGROUND_LOCATION"
)
ActivityCompat.requestPermissions(this@HomeScreenActivity, strings, 2)
}
}
}

Step 2: Handle the permission result

override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == 1) {
if (grantResults.size > 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED
) {
Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSION successful")
requestLocationUpdatesWithCallback()
} else {
Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSSION failed")
}
}
if (requestCode == 2) {
if (grantResults.size > 2 && grantResults[2] == PackageManager.PERMISSION_GRANTED && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
Log.i(
TAG,
"onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION successful"
)
requestLocationUpdatesWithCallback()
} else {
Log.i(TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION failed")
}
}
}

Step 3: Declare the following variable like LocationCallBack, LocationRequest, SettingClient, FusedLocationProviderClient

private var mLocationCallback: LocationCallback? = null
private var mLocationRequest: LocationRequest? = null
private lateinit var settingsClient: SettingsClient
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient

Step 4: initialize the fusedLocationProvideClient in onCreate(()

fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)

Step 5: Initialize the SettingClient in onCreate()

settingsClient = LocationServices.getSettingsClient(this)

Step 6: Initialize the LocationRequest in onCreate()

mLocationRequest = LocationRequest().apply {
interval = 1000
needAddress = true
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}

Step 7: Initialize the LocationCallBak in the onCreate()

mLocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
if (locationResult != null) {
val locations: List<Location> = locationResult.locations
if (locations.isNotEmpty()) {
for (location in locations) {
Log.i(TAG,
"onLocationResult location[Longitude,Latitude,Accuracy]:${location.longitude} , ${location.latitude} , ${location.accuracy}"
)
}
strLocation = """ Latitude:${locationResult.lastLocation.latitude} Longitude${locationResult.lastLocation.longitude} """.trimIndent()
Log.d(TAG, strLocation)
Log.d(TAG, "Country:" + locationResult.lastHWLocation.county);
Log.d(TAG, "CountryCode:" + locationResult.lastHWLocation.countryCode);
Log.d(TAG, "State:" + locationResult.lastHWLocation.state);
Log.d(TAG, "Postal Code:" + locationResult.lastHWLocation.postalCode);
Log.d(TAG, "Latitude:" + locationResult.lastHWLocation.latitude);
Log.d(TAG, "Longitude:" + locationResult.lastHWLocation.longitude);
Toast.makeText(this@HomeScreenActivity, strLocation, Toast.LENGTH_LONG).show()
onLocationChanged(locationResult.lastLocation)
val latlng = LatLng(
locationResult.lastLocation.latitude,
locationResult.lastLocation.longitude
)
updateDatToSmartGlove(latlng)
}
}
}

override fun onLocationAvailability(locationAvailability: LocationAvailability?) {
locationAvailability?.let {
val flag: Boolean = locationAvailability.isLocationAvailable
Log.i(TAG, "onLocationAvailability isLocationAvailable:$flag")
}
}
}

Step 8: Get the last know location.

/**
* Obtain the last known location
*/
private fun getLastLocation() {
try {
val lastLocation =
fusedLocationProviderClient.lastLocation
lastLocation.addOnSuccessListener(OnSuccessListener { location ->
if (location == null) {
Log.i(TAG, "getLastLocation onSuccess location is null")
return@OnSuccessListener
}
Log.i(
TAG,
"getLastLocation onSuccess location[Longitude,Latitude]:${location.longitude},${location.latitude}"
)
Toast.makeText(this,"",Toast.LENGTH_SHORT).show()
return@OnSuccessListener
}).addOnFailureListener { e: Exception ->
// Guide users to install or upgrade the HMS Core when the HMS Core (APK) is not installed on Huawei mobile phones.
if (e is ResolvableApiException) {
val apiException = e
Log.e(
TAG,
"getLastLocation onFailure:" + apiException.statusCode
)
try {
apiException.startResolutionForResult(this@HomeScreenActivity, 2009)
} catch (ex: IntentSender.SendIntentException) {
Log.e(
TAG, "getLastLocation sendIntentException:${ex.message}"
)
}
} else {
Log.e(
TAG,
"getLastLocation onFailure:" + e.message
)
}

}
} catch (e: Exception) {
Log.e(TAG, "getLastLocation exception:${e.message}")
}
}

Step 9: If you need location update with every X seconds, then you need to request location update with callback.

private fun requestLocationUpdatesWithCallback() {
try {
val builder = LocationSettingsRequest.Builder()
builder.addLocationRequest(mLocationRequest)
val locationSettingsRequest = builder.build()
// check devices settings before request location updates.
//Before requesting location update, invoke checkLocationSettings to check device settings.
val locationSettingsResponseTask: Task<LocationSettingsResponse> =
settingsClient.checkLocationSettings(locationSettingsRequest)

locationSettingsResponseTask.addOnSuccessListener { locationSettingsResponse: LocationSettingsResponse? ->
Log.i(TAG, "check location settings success {$locationSettingsResponse}")
// request location updates
fusedLocationProviderClient.requestLocationUpdates(
mLocationRequest,
mLocationCallback,
Looper.getMainLooper()
)
.addOnSuccessListener {
Log.i(TAG, "requestLocationUpdatesWithCallback onSuccess")
}
.addOnFailureListener { e ->
Log.e(
TAG,
"requestLocationUpdatesWithCallback onFailure:${e.message}"
)
}
}
.addOnFailureListener { e: Exception ->
Log.e(TAG, "checkLocationSetting onFailure:${e.message}")
when ((e as ApiException).statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
val rae = e as ResolvableApiException
rae.startResolutionForResult(
this@HomeScreenActivity, 0
)
} catch (sie: IntentSender.SendIntentException) {
Log.e(TAG, "PendingIntent unable to execute request.")
}
}
}
} catch (e: Exception) {
Log.e(TAG, "requestLocationUpdatesWithCallback exception:${e.message}")
}
}

Step 10: Once location update is not required anymore, then you need to remove the location update with callback.

private fun removeLocationUpdatesWithCallback() {
try {
fusedLocationProviderClient.removeLocationUpdates(mLocationCallback)
.addOnSuccessListener {
Log.i(
TAG,
"removeLocationUpdatesWithCallback onSuccess"
)
}
.addOnFailureListener { e ->
Log.e(
TAG,
"removeLocationUpdatesWithCallback onFailure:${e.message}"
)
}
} catch (e: Exception) {
Log.e(
TAG,
"removeLocationUpdatesWithCallback exception:${e.message}"
)
}
}

Result​

Tips and Tricks​

1. Make sure you are already registered as a Huawei developer.

2. Set min SDK version to 19 or later, otherwise you will get AndriodManifest to merge issue.

3. Make sure you have added the agconnect-services.json file to the app folder.

4. Make sure you have added the SHA-256 fingerprint without fail.

5. Make sure all the dependencies are added properly.

6. Make sure all necessary permissions are added in the AndroidManifest.xml.

Conclusion​

In this article, we have learnt the integration of the Huawei Location Kit in Smart Gloves mobile application using Android Studio and Kotlin. In this article, we understood about Huawei Location, Functions of Huawei Location and FusedLocation, Last know location and also we have learnt location update with callback and also removing the location update with callback.

Reference​

Location Kit - Official document

Location Kit - Code lab

Location Kit - Training Video