DEV Community

Cover image for Integration of Huawei Location kit in Navigation Glove IoT application - Part 7
HMS Community
HMS Community

Posted on

Integration of Huawei Location kit in Navigation Glove IoT application - Part 7

Introduction

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

Beginner: Integration of Huawei Account kit in Navigation Glove IoT application Using Kotlin - Part 1

Beginner: Integration of Huawei Map kit in Navigation Glove IoT application Using Kotlin - Part 2

Beginner: Integration of Huawei Site kit in Navigation Glove IoT application Using Kotlin - Part 3

Beginner: Integration of Huawei Direction API in Navigation Glove IoT application Using Kotlin - Part 4

Beginner: Connecting to Smart Gloves Using Bluetooth in Navigation Glove IoT application Using Kotlin - Part 5

Beginner: Integration of Huawei Analytics kit and Crash service in Navigation Glove IoT application Using Kotlin - Part 6

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/' }
Enter fullscreen mode Exit fullscreen mode
  1. Add the classpath inside the dependency section of the project level build.gradle file.
classpath 'com.huawei.agconnect:agcp:1.5.2.300'
Enter fullscreen mode Exit fullscreen mode
  1. Add the plugin in the app-level build.gradle file.
apply plugin: 'com.huawei.agconnect'
Enter fullscreen mode Exit fullscreen mode
  1. Add the below library in the app-level build.gradle file dependencies section.
implementation 'com.huawei.hms:location:5.1.0.301'
Enter fullscreen mode Exit fullscreen mode
  1. 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"/>
Enter fullscreen mode Exit fullscreen mode
  1. 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" />
Enter fullscreen mode Exit fullscreen mode
  1. 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)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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")
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Step 4: initialize the fusedLocationProvideClient in onCreate(()

fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
Enter fullscreen mode Exit fullscreen mode

Step 5: Initialize the SettingClient in onCreate()

settingsClient = LocationServices.getSettingsClient(this)
Enter fullscreen mode Exit fullscreen mode

Step 6: Initialize the LocationRequest in onCreate()

mLocationRequest = LocationRequest().apply {
interval = 1000
needAddress = true
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
Enter fullscreen mode Exit fullscreen mode

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")
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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}")
    }
}
Enter fullscreen mode Exit fullscreen mode

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}")
    }
}
Enter fullscreen mode Exit fullscreen mode

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}"
        )
    }
}
Enter fullscreen mode Exit fullscreen mode

Result

Image description
Image description

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

Top comments (0)