DEV Community

HMS Community
HMS Community

Posted on

Expert: Courier App MVVM Jetpack (HMS Location and Map Kit) in Android using Kotlin- Part-5

Overview

In this article, I will create a Courier android application using Kotlin in which I will integrate HMS Core kits such as HMS Account, Push, CloudDB, AuthService, Push Kit Uplink Message, Location and Map Kit.

We have integrated HMS Account and AuthService Kit in part-, Push Notification Using HMS Push Kit in Part-2, Cloud DB Kit in Part-3 and Huawei Client Push integration in Part4 of this series. Kindly go through the link below-

Part-1 https://forums.developer.huawei.com/forumPortal/en/topic/0202841957497640128
Part-2 https://forums.developer.huawei.com/forumPortal/en/topic/0201847982965230092
part-3 https://forums.developer.huawei.com/forumPortal/en/topic/0201854022878900124?fid=0101187876626530001

App will make use of android MVVM clean architecture using Jetpack components such as DataBinding, AndroidViewModel, Observer, LiveData and much more.

In this article, we are going to implement DataBinding using Observable pattern.

Map Kit Introduction

Map Kit covers map data of more than 200 countries and regions, and supports over 70 languages. User can easily integrate map-based functions into your apps using SDK. It optimizes and enriches the map detail display capability. Map Kit supports gestures including zoom, rotation, moving and tilt gestures to ensure smooth interaction experience.

Location Kit introduction

Location Kit combines the GPS, Wi-Fi and base station location functionalities in your app to build up global positioning capabilities, allows to provide flexible location-based services targeted at users around globally. Currently, it provides three main capabilities: fused location, activity identification and geo-fence. You can call one or more of these capabilities as required.

Prerequisite

Huawei Phone EMUI 3.0 or later.
Non-Huawei phones Android 4.4 or later (API level 19 or higher).
HMS Core APK 4.0.0.300 or later
Android Studio
AppGallery Account
App Gallery Integration process

Sign In and Create or Choose a project on AppGallery Connect portal.

Navigate to Project settings and download the configuration file.

Navigate to General Information, and then provide Data Storage location.

App Development

Add Required Dependencies:
Launch Android studio and create a new project. Once the project is ready.
Navigate to the Gradle scripts folder and open build.gradle (project: app).


 ext.kotlin_version = "1.4.21"
    repositories {
        google()
        jcenter()
        maven {url 'https://developer.huawei.com/repo/'}
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.0.1"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.huawei.agconnect:agcp:1.4.2.300'
Enter fullscreen mode Exit fullscreen mode

Add following dependency for HMS Loaction and Map Kits

 // Huawei Map
    implementation 'com.huawei.hms:maps:6.2.0.301'
// Huawei Location Kit
    implementation 'com.huawei.hms:location:6.2.0.300'
Enter fullscreen mode Exit fullscreen mode

Navigate to the Gradle scripts folder and open build.gradle (module: app).

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.huawei.agconnect'
apply plugin: 'kotlin-kapt'


android {
    compileSdkVersion 31
    buildToolsVersion "29.0.3"

    buildFeatures {
        dataBinding = true
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }

    defaultConfig {
        applicationId "com.hms.corrierapp"
        minSdkVersion 27
        targetSdkVersion 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    implementation "android.arch.lifecycle:extensions:1.1.1"

    implementation 'com.google.android.material:material:1.2.0'

    implementation 'com.squareup.retrofit2:retrofit:2.6.2'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    implementation 'com.squareup.retrofit2:converter-scalars:2.5.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'
    implementation(
            [group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.4.1'],
            [group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.4.1'],
            [group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.4.1'],
    )

    //HMS Kits
    implementation 'com.huawei.agconnect:agconnect-core:1.5.0.300'
    implementation 'com.huawei.hms:hwid:5.3.0.302'

    implementation 'com.huawei.hms:push:4.0.3.301'

    implementation 'com.huawei.agconnect:agconnect-cloud-database:1.5.0.300'
    implementation "com.huawei.agconnect:agconnect-auth-huawei:1.6.0.300"
    implementation 'com.huawei.agconnect:agconnect-auth:1.5.0.300'

    // Huawei Map
    implementation 'com.huawei.hms:maps:6.2.0.301'
// Huawei Location Kit
    implementation 'com.huawei.hms:location:6.2.0.300'

}
Enter fullscreen mode Exit fullscreen mode

Code Implementation
Created following package model, push, viewmodel.
Model: In your primary folder, create a new package and name it model.

MapActivity.kt:

package com.hms.corrierapp.map

import android.Manifest
import android.content.IntentSender
import android.content.pm.PackageManager
import android.location.Location
import android.os.*
import android.util.Log
import android.widget.Chronometer
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import com.hms.corrierapp.R
import com.huawei.hmf.tasks.Task
import com.huawei.hms.common.ApiException
import com.huawei.hms.common.ResolvableApiException
import com.huawei.hms.location.*
import com.huawei.hms.maps.*
import com.huawei.hms.maps.model.*
import java.io.File
import java.text.DecimalFormat


class MapActivity : AppCompatActivity(), OnMapReadyCallback {

    private lateinit var mMapView: MapView
    private var mHwMap: HuaweiMap? = null
    private var mPolylineOptions: PolylineOptions? = null
    private var mListener: LocationSource.OnLocationChangedListener? = null

    private var mMarkerStart: Marker? = null
    private var mMarkerEnd: Marker? = null
    private var mLocationRequest: LocationRequest? = null
    private var mTvStart: TextView? = null
    private var mTvDistance: TextView? = null
    private var mTime: Chronometer? = null
    private var fusedLocationProviderClient: FusedLocationProviderClient? = null
    private val mPath: PathBean = PathBean()
    private var mSeconds: Long = 0
    private val mHandler = Handler(Looper.getMainLooper())
    private val mDecimalFormat = DecimalFormat("0.00")
    private var mIsRunning = false
    private val mTimeRunnable: Runnable = object : Runnable {
        override fun run() {
            mTime!!.text = formatSeconds()
            mHandler.postDelayed(this, 1000)
        }
    }
    private var mLocationCallback: LocationCallback? = null

    // GPS Data
    private var mGpsDataThread: HandlerThread? = null
    private var mGpsDataHandler: Handler? = null
    private var mGpsDataFile: File? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_map)
        checkPermission()
        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
        // check location settings
        checkLocationSettings()
        // init MapView
        mMapView = findViewById(R.id.hw_mapview)
        var mapViewBundle: Bundle? = null
        if (savedInstanceState != null) {
            mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY)
        }
        mMapView.onCreate(mapViewBundle)
        mMapView.getMapAsync(this)
        mTvDistance = findViewById(R.id.tv_distance)
        mTime = findViewById(R.id.cm_time)
        mTvStart = findViewById(R.id.tv_start)
        mTvStart!!.setOnClickListener({ processStartClick() })
        // Initializing Map Kit Polyline
        mPolylineOptions = PolylineOptions()
        mPolylineOptions!!.color(resources.getColor(R.color.colorAccent))
        mPolylineOptions!!.width(5f)
        // Recording GPS Data
        mGpsDataFile = File(getExternalFilesDir(null), "GpsData.txt")

    }

    private fun checkPermission() {
        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 {
            // Dynamically apply for permissions required for SDK > 28. Add the android.permission.ACCESS_BACKGROUND_LOCATION permission.
            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,
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                val strings = arrayOf(
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION
                )
                ActivityCompat.requestPermissions(this, strings, 2)
            }
        }
    }

    override fun onMapReady(huaweiMap: HuaweiMap?) {
        Log.d(TAG, "onMapReady: ")
        mHwMap = huaweiMap
        mHwMap?.isMyLocationEnabled = true
        mHwMap?.uiSettings?.isZoomControlsEnabled = false
        // Add Location Source
        mHwMap?.setLocationSource(object : LocationSource {
            override fun activate(onLocationChangedListener: LocationSource.OnLocationChangedListener?) {
                mListener = onLocationChangedListener
            }

            override fun deactivate() {}
        })
        // Obtains the current position and updates the map camera.
        try {
            val lastLocation: Task<Location> = fusedLocationProviderClient!!.lastLocation
            lastLocation.addOnSuccessListener { location ->
                if (location ==null) return@addOnSuccessListener
                    mListener!!.onLocationChanged(location)
                if (mListener != null) {
                    mListener!!.onLocationChanged(location)
                    val cameraUpdate: CameraUpdate = CameraUpdateFactory.newLatLngZoom(
                        LatLng(location.latitude, location.longitude), 15f
                    )
                    mHwMap!!.animateCamera(cameraUpdate)
                }
            }.addOnFailureListener {
                Log.d(TAG, "onMapReady: Obtains the current position failure")
            }
        } catch (e: Exception) {

        }
    }

    companion object {
        private const val MAPVIEW_BUNDLE_KEY = "MapViewBundleKey"
        private const val TAG = "MapActivity"
    }

    override fun onStart() {
        super.onStart()
        mMapView.onStart()
    }

    override fun onStop() {
        super.onStop()
        mMapView.onStop()
    }

    override fun onDestroy() {
        removeLocationUpdatesWithCallback()
        super.onDestroy()
        mHandler.removeCallbacksAndMessages(null)
        mMapView.onDestroy()
    }

    override fun onPause() {
        mMapView.onPause()
        super.onPause()
        mGpsDataThread!!.quitSafely()
        try {
            mGpsDataThread!!.join()
            mGpsDataThread = null
            mGpsDataHandler = null
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
    }

    override fun onResume() {
        super.onResume()
        mMapView.onResume()
        mGpsDataThread = HandlerThread("DotThread")
        mGpsDataThread!!.start()
        mGpsDataHandler = Handler(mGpsDataThread!!.looper)
    }

    override fun onLowMemory() {
        super.onLowMemory()
        mMapView.onLowMemory()
    }

    private fun checkLocationSettings() {
        val builder: LocationSettingsRequest.Builder = LocationSettingsRequest.Builder()
        val locationSettingsRequest: LocationSettingsRequest = builder.build()
        val settingsClient: SettingsClient = LocationServices.getSettingsClient(this)
        settingsClient.checkLocationSettings(locationSettingsRequest)
            .addOnSuccessListener { requestLocationUpdate() }.addOnFailureListener { e ->
                when ((e as ApiException).statusCode) {
                    LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
                        val rae: ResolvableApiException = e as ResolvableApiException
                        rae.startResolutionForResult(this@MapActivity, 0)
                    } catch (sie: IntentSender.SendIntentException) {
                    }
                }
            }
    }

    private fun requestLocationUpdate() {
        mLocationRequest = LocationRequest()
        mLocationRequest!!.interval = 5000
        mLocationRequest!!.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        mLocationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                super.onLocationResult(locationResult)
                writeGpsData2Sdcard(locationResult.lastLocation)
                if (mIsRunning) {
                    processLocationChange(locationResult.lastLocation)
                }
            }

            override fun onLocationAvailability(locationAvailability: LocationAvailability?) {
                super.onLocationAvailability(locationAvailability)
            }
        }
        fusedLocationProviderClient
            ?.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
            ?.addOnSuccessListener { Log.i(TAG, "request location updates success") }
            ?.addOnFailureListener { e ->
                Log.e(TAG, "request location updates failed, error: " + e.message)
            }
    }

    // Removed when the location update is no longer required.
    private fun removeLocationUpdatesWithCallback() {
        try {
            val voidTask: Task<Void> =
                fusedLocationProviderClient!!.removeLocationUpdates(mLocationCallback)
            voidTask.addOnSuccessListener { }.addOnFailureListener { }
        } catch (e: Exception) {
            Log.e(TAG, "removeLocationUpdatesWithCallback Exception : $e")
        }
    }

    private fun processStartClick() {
        if (mIsRunning) {
            mIsRunning = false
            mPath.endTime = (System.currentTimeMillis())
            mTvStart!!.text = "Start"
            mHandler.removeCallbacks(mTimeRunnable)
            if (mPath.mPathLinePoints!!.size > 0) {
                mPath.endPoint = (mPath.mPathLinePoints!!.get(mPath.mPathLinePoints!!.size - 1))
                if (null != mMarkerStart && null != mMarkerEnd) {
                    mMarkerStart!!.remove()
                    mMarkerEnd!!.remove()
                }
                val startPointOptions: MarkerOptions = MarkerOptions()
                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_location_marker))
                    .position(mPath.mStartPoint)
                startPointOptions.title("Start Point")
                startPointOptions.snippet("Start Point")
                mMarkerStart = mHwMap!!.addMarker(startPointOptions)
                val endPointOptions: MarkerOptions = MarkerOptions()
                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_location_marker))
                    .position(mPath.mEndPoint)
                endPointOptions.title("End Point")
                endPointOptions.snippet("End Point")
                mMarkerEnd = mHwMap!!.addMarker(endPointOptions)
            }
        } else {
            mIsRunning = true
            mPath.reset()
            mPath.startTime = System.currentTimeMillis()
            mHandler.post(mTimeRunnable)
            mTvStart!!.text = "Stop"
        }
    }

    private fun processLocationChange(location: android.location.Location) {
        val latLng = LatLng(location.latitude, location.longitude)
        if (mPath.mStartPoint == null) {
            mPath.mStartPoint = latLng
        }
        mPath.addPoint(latLng)
        val distance: Float = mPath.updateDistance()
        val sportMile = distance / 1000.0
        if (mSeconds > 0) {
            val distribution = mSeconds.toDouble() / 60.0 / sportMile
            mPath.setDistribution(distribution)
            mTvDistance!!.text = mDecimalFormat.format(sportMile)
        } else {
            mPath.setDistribution(0.0)
            mTvDistance!!.text = "0.00"
        }
        mPolylineOptions!!.add(latLng)
        mHwMap!!.addPolyline(mPolylineOptions)

        if (mListener != null) {
            mListener!!.onLocationChanged(location)
            val cameraUpdate: CameraUpdate = CameraUpdateFactory.newLatLngZoom(
                LatLng(location.latitude, location.longitude), 15f
            )
            mHwMap!!.animateCamera(cameraUpdate)
        }
    }

    fun formatSeconds(): String {
        val hh = if (mSeconds / 3600 > 9) mSeconds / 3600 else mSeconds / 3600
        val mm =
            if (mSeconds % 3600 / 60 > 9) mSeconds % 3600 / 60 else mSeconds % 3600 / 60
        mSeconds++
        return "$hh:$mm"
    }

    private fun writeGpsData2Sdcard(location: Location) {
        Log.d(
            TAG,
            "write latitude and longitude, latitude: " + location.latitude + ", longitude: " + location.longitude
        )
        mGpsDataHandler!!.post(
            GpsDataSaver(
                mGpsDataFile, """
     ${location.latitude}, ${location.longitude}

     """.trimIndent()
            )
        )
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        }
    }


}
Enter fullscreen mode Exit fullscreen mode

activity_map.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:map="http://schemas.android.com/apk/res-auto"
    android:id="@+id/sport_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.huawei.hms.maps.MapView
        android:id="@+id/hw_mapview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        map:mapType="normal"
        map:uiCompass="true"
        map:uiZoomControls="true" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginStart="15dp"
        android:layout_marginTop="60dp"
        android:layout_marginEnd="15dp"
        android:background="@color/white"
        android:orientation="horizontal">

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="20dp"
            android:layout_marginTop="20dp"
            android:layout_weight="1"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_distance"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:gravity="center_horizontal"
                android:maxLength="8"
                android:text="0.00"
                android:textColor="#000000"
                android:textSize="25sp"
                android:textStyle="bold" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="6dp"
                android:layout_marginBottom="2.5dp"
                android:gravity="center_horizontal"
                android:text="km"
                android:textColor="#88000000"
                android:textSize="18sp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="20dp"
            android:layout_marginTop="20dp"
            android:layout_weight="2"
            android:orientation="vertical">

            <Chronometer
                android:id="@+id/cm_time"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="20dp"
                android:ellipsize="end"
                android:format="00:00"
                android:gravity="center"
                android:textColor="#000000"
                android:textSize="22sp"
                android:textStyle="bold" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="6dp"
                android:layout_marginBottom="2.5dp"
                android:gravity="center_horizontal"
                android:text="Total time"
                android:textColor="#88000000"
                android:textSize="18sp" />
        </LinearLayout>

    </LinearLayout>

    <TextView
        android:id="@+id/tv_start"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="30dp"
        android:gravity="center"
        android:text="Track Courier Location"
        android:textColor="@color/colorPrimary"
        android:textSize="21sp"
        android:textStyle="bold" />

</RelativeLayout>
Enter fullscreen mode Exit fullscreen mode

activity_delivery_status.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white">

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:padding="16dp">


                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:padding="5dp"
                    android:text="@string/delivery"
                    android:textAlignment="center"
                    android:textColor="@color/colorPrimaryDark"
                    android:textSize="34sp"
                    android:textStyle="bold" />

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:orientation="horizontal">

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="100dp"
                        android:layout_weight="0.1"
                        android:gravity="center"
                        android:orientation="vertical">

                        <ImageView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:background="@drawable/ic_location_marker" />

                        <View
                            android:layout_width="5dp"
                            android:layout_height="match_parent"
                            android:background="@android:color/black" />

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="100dp"
                        android:layout_weight="0.9"
                        android:gravity="center_vertical"
                        android:orientation="vertical">

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="Booking"
                            android:textAlignment="center"
                            android:textColor="@color/gray"
                            android:textSize="20sp"
                            android:textStyle="bold" />

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:padding="5dp"
                            android:text="Done"
                            android:textAlignment="center"
                            android:textColor="@color/green"
                            android:textSize="18sp"
                            android:textStyle="bold" />


                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:padding="5dp"
                            android:text="Payment Pending"
                            android:textAlignment="center"
                            android:textColor="@color/red"
                            android:textSize="18sp"
                            android:textStyle="bold" />

                    </LinearLayout>

                </LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:orientation="horizontal">

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="100dp"
                        android:layout_weight="0.1"
                        android:gravity="center"
                        android:orientation="vertical">

                        <ImageView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:background="@drawable/ic_location_marker" />

                        <View
                            android:layout_width="5dp"
                            android:layout_height="match_parent"
                            android:background="@android:color/black" />

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="100dp"
                        android:layout_weight="0.9"
                        android:gravity="center_vertical"
                        android:orientation="vertical">

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="PickUp"
                            android:textAlignment="center"
                            android:textColor="@color/gray"
                            android:textSize="20sp"
                            android:textStyle="bold" />

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:padding="5dp"
                            android:text="Done"
                            android:textAlignment="center"
                            android:textColor="@color/green"
                            android:textSize="18sp"
                            android:textStyle="bold" />


                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:padding="5dp"
                            android:text="Shipping"
                            android:textAlignment="center"
                            android:textColor="@color/gray"
                            android:textSize="18sp"
                            android:textStyle="bold" />

                    </LinearLayout>

                </LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:orientation="horizontal">

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="100dp"
                        android:layout_weight="0.1"
                        android:gravity="center"
                        android:orientation="vertical">

                        <ImageView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:background="@drawable/ic_location_marker" />

                        <View
                            android:layout_width="5dp"
                            android:layout_height="match_parent"
                            android:background="@android:color/black" />

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="100dp"
                        android:layout_weight="0.9"
                        android:gravity="center_vertical"
                        android:orientation="vertical">

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="Delivery"
                            android:textAlignment="center"
                            android:textColor="@color/gray"
                            android:textSize="20sp"
                            android:textStyle="bold" />

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:padding="5dp"
                            android:text="On It's Way"
                            android:textAlignment="center"
                            android:textColor="@color/gray"
                            android:textSize="18sp"
                            android:textStyle="bold" />


                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:padding="5dp"
                            android:text="Tracking ID: 12231223"
                            android:textAlignment="center"
                            android:textColor="@color/gray"
                            android:textSize="18sp"
                            android:textStyle="bold" />

                    </LinearLayout>

                </LinearLayout>


                <Button
                    android:id="@+id/btn_done"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="20dp"
                    android:layout_marginBottom="5dp"
                    android:background="@color/colorPrimaryDark"
                    android:text="See On Map"
                    android:textColor="@color/white"
                    android:textStyle="bold" />
            </LinearLayout>

        </ScrollView>

    </RelativeLayout>
</layout>
Enter fullscreen mode Exit fullscreen mode

App Build Result

Tips and Tricks

Set minSDK version to 24 or later, otherwise you will get AndriodManifest merge issue.

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

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

Make sure all the dependencies are added properly.

Conclusion

In this article, we have learned how to integrate HMS Account, Push, CloudDB, AuthService, Push Kit Uplink Message, Location and Map Kit in Android application. After completely read this article user can easily implement Location and Map Kit in the Courier android application using Kotlin.

Thanks for reading this article. Be sure to like and comment to this article, if you found it helpful. It means a lot to me.

References

HMS Docs:

https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/introduction-0000001050048870

https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-sdk-brief-introduction-0000001061991343

https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-introduction-0000001121930588

Location and Map Kit Training Video:

https://developer.huawei.com/consumer/en/training/course/video/201575277450653242

Top comments (0)