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'
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'
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'
}
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) {
}
}
}
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>
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>
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:
Location and Map Kit Training Video:
https://developer.huawei.com/consumer/en/training/course/video/201575277450653242
Top comments (0)