Expert: Courier App MVVM Jetpack (HMS Push Kit Uplink Message) in Android using Kotlin- Part-4
Published: 2022-04-22 11:26:20

**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 and Push Kit Uplink Message.

We have integrated HMS Account and AuthService Kit in part-1 and Push Notification Using HMS Push Kit in Part-2 and Cloud DB Kit in Part-3 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.

**Huawei Push Kit Introduction**

Push Kit is a messaging service provided by Huawei for developers. Push Kit establishes a messaging channel from the cloud to devices. By integrating HUAWEI Push Kit, developers can send messages to apps on users’ devices in real time. Push Kit helps developers rapidly to reach the target audience. Push notification can be sent to everyone or to a specific person individually. For send notification, user’s token is required. You can send messages to a device group or specific user. Push Kit has two different notification type. Text only style (Shows longer text messages) and big picture style (Shows large text and image messages).

**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 (module: app).
```
//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'
```
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'
```
**Code Implementation**
Created following package model, push, viewmodel.
Model: In your primary folder, create a new package and name it model.

ShippingCheckoutActivity:
```

package com.hms.corrierapp

import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.hms.corrierapp.databinding.ActivityAddressBinding
import com.hms.corrierapp.push.*
import com.huawei.agconnect.config.AGConnectServicesConfig
import com.huawei.hms.aaid.HmsInstanceId
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response


class ShippingCheckoutAddressActivity : AppCompatActivity() {

    private lateinit var pushToken: String
    private var accessToken: String? = null

    private lateinit var binding: ActivityAddressBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_address)

        getToken()
        getAccessToken()

        binding.btnCheckout.setOnClickListener {
            sendNotification(pushToken)
        }
    }

    private fun getToken() {
        object : Thread() {
            override fun run() {
                try {
                    val appId: String =
                        AGConnectServicesConfig.fromContext(this@ShippingCheckoutAddressActivity)
                            .getString("client/app_id")
                    pushToken =
                        HmsInstanceId.getInstance(this@ShippingCheckoutAddressActivity)
                            .getToken(appId, "HCM")

                    if (!TextUtils.isEmpty(pushToken)) {
                        Log.i("Push", "get token:$pushToken")
                    }
                } catch (e: Exception) {
                    Log.i("Push", "getToken failed, $e")
                }
            }
        }.start()
    }

    private fun getAccessToken() {
        AccessTokenClient.getClient().create(AccessTokenInterface::class.java)
            .createAccessToken(
                "client_credentials",
                "a3c3072ecb38e7c58f3ad8ee48ed5a8e31a2f6bcddf5c094a00329fdd77c8f50",
                "105919003"
            )
            .enqueue(object : Callback<AccessTokenModel> {
                override fun onFailure(call: Call<AccessTokenModel>, t: Throwable) {
                    Log.d("Push", "ERROR : " + t.message)
                }

                override fun onResponse(
                    call: Call<AccessTokenModel>,
                    response: Response<AccessTokenModel>
                ) {
                    if (response.isSuccessful) {
                        Log.d("Push", "Token " + response.body()?.access_token)
                        accessToken = response.body()?.access_token
                    }
                }
            })
    }

    private fun sendNotification(pushToken: String) {

        val notifMessageBody: NotificationMessageBody = NotificationMessageBody.Builder(
            "Your Courier is Booked", "We will notify you once your courier dispatch",
            arrayOf(pushToken)
        )
            .build()

        NotificationClient.getClient().create(NotificationInterface::class.java)
            .createNotification(
                "Bearer $accessToken",
                notifMessageBody
            )
            .enqueue(object : Callback<NotificationMessageModel> {
                override fun onFailure(call: Call<NotificationMessageModel>, t: Throwable) {
                    Log.d("Push", "ERROR :  " + t.message)
                }

                override fun onResponse(
                    call: Call<NotificationMessageModel>,
                    response: Response<NotificationMessageModel>
                ) {
                    if (response.isSuccessful) {
                        Log.d("Push", "Response " + response.body())
                        Toast.makeText(
                            this@ShippingCheckoutAddressActivity,
                            "Sent For Track",
                            Toast.LENGTH_SHORT
                        )
                            .show()
                    }
                }

            })
    }
}
```
activity_shipping_checkout_activity:

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

    <data>

        <variable
            name="addressViewmODEL"
            type="com.hms.corrierapp.viewmodel.AddressViewModel" />
    </data>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <LinearLayout
                android:id="@+id/profileLayout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"
                android:paddingTop="20dp">

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


                <com.google.android.material.textfield.TextInputLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="5dp"
                    android:paddingLeft="6dp"
                    android:paddingRight="6dp"
                    android:textColor="@color/colorPrimaryDark">

                    <EditText
                        android:id="@+id/fullNameEdt"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:hint="Full Name"
                        android:imeOptions="actionNext"
                        android:singleLine="true"
                        android:textSize="14sp" />
                </com.google.android.material.textfield.TextInputLayout>


                <com.google.android.material.textfield.TextInputLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingLeft="6dp"
                    android:paddingRight="6dp"
                    android:textColor="@color/colorPrimaryDark">

                    <EditText
                        android:id="@+id/mobEditText"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:hint="Mobile Number *"
                        android:imeOptions="actionNext"
                        android:inputType="number"
                        android:paddingLeft="6dp"
                        android:singleLine="true"
                        android:textSize="14sp" />

                </com.google.android.material.textfield.TextInputLayout>


                <com.google.android.material.textfield.TextInputLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingLeft="6dp"
                    android:paddingRight="6dp"
                    android:textColor="@color/colorPrimaryDark">

                    <EditText
                        android:id="@+id/cityEditText"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:hint="City *"
                        android:imeOptions="actionNext"
                        android:paddingLeft="6dp"
                        android:singleLine="true"
                        android:textSize="14sp" />
                </com.google.android.material.textfield.TextInputLayout>

                <com.google.android.material.textfield.TextInputLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingLeft="6dp"
                    android:paddingRight="6dp"
                    android:textColor="@color/colorPrimaryDark">

                    <EditText
                        android:id="@+id/areaEditText"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:hint="Locality, area or street *"
                        android:imeOptions="actionNext"
                        android:paddingLeft="6dp"
                        android:singleLine="true"
                        android:textSize="14sp" />
                </com.google.android.material.textfield.TextInputLayout>


                <com.google.android.material.textfield.TextInputLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingLeft="6dp"
                    android:paddingRight="6dp"
                    android:textColor="@color/colorPrimaryDark">

                    <EditText
                        android:id="@+id/buildingEditText"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:hint="Flat no., Building name *"
                        android:imeOptions="actionNext"
                        android:paddingLeft="6dp"
                        android:singleLine="true"
                        android:textSize="14sp" />
                </com.google.android.material.textfield.TextInputLayout>

                <RelativeLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content">

                    <com.google.android.material.textfield.TextInputLayout
                        android:id="@+id/relativeLayout2"
                        android:layout_width="150dp"
                        android:layout_height="wrap_content"
                        android:paddingLeft="6dp">

                        <EditText
                            android:id="@+id/pincodeEditText"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:hint="Pincode *"
                            android:imeOptions="actionNext"
                            android:inputType="number"
                            android:paddingLeft="6dp"
                            android:singleLine="true"
                            android:textSize="14sp" />

                    </com.google.android.material.textfield.TextInputLayout>

                    <com.google.android.material.textfield.TextInputLayout
                        android:layout_width="150dp"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="52dp"
                        android:layout_toRightOf="@+id/relativeLayout2">

                        <EditText
                            android:id="@+id/stateEditText"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:hint="State *"
                            android:imeOptions="actionNext"
                            android:singleLine="true"
                            android:textSize="14sp"

                            />

                    </com.google.android.material.textfield.TextInputLayout>
                </RelativeLayout>


                <com.google.android.material.textfield.TextInputLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingLeft="6dp"
                    android:paddingRight="6dp">

                    <EditText
                        android:id="@+id/landmarkEditText"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:hint="Landmark(Optional)"
                        android:imeOptions="actionDone"
                        android:paddingLeft="6dp"
                        android:singleLine="true"
                        android:textSize="14sp" />
                </com.google.android.material.textfield.TextInputLayout>


                <Button
                    android:id="@+id/btn_checkout"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="10dp"
                    android:backgroundTint="@color/colorPrimary"
                    android:text="Checkout"
                    android:textColor="@color/white"
                    android:textSize="17sp" />


            </LinearLayout>

        </RelativeLayout>
    </ScrollView>
</layout>
```
**App Build Result**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h7mu35m7iu92der9fwiy.gif)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n2ot4go0odjs7uq1djw8.gif)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wbqmh0z5mvedltpd67n6.gif)
   

**Tips and Tricks**

Identity Kit displays the HUAWEI ID registration or sign-in page first. The user can use the functions provided by Identity Kit only after signing in using a registered HUAWEI ID.

Push Kit supports cross-region messaging, but the messaging performance may be affected. To minimize cross-region messaging, it is recommended that you deploy servers in regions where users gather.

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

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

**Conclusion**

In this article, we have learned how to integrate Huawei ID and Push Kit in Android application. After completely read this article user can easily implement Huawei ID and Client Side Push Notification 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/service-introduction-0000001050040060

Push Kit Training Video:

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

Push Kit Code Lab:

https://developer.huawei.com/consumer/en/codelabsPortal/carddetails/HMSPushKit