Integration of Huawei Push Kit in Book Reading Android app (Kotlin) - Part 3
Tags: kotlin, huawei, pushkit, android
Published: 2022-02-14 10:54:43


**Introduction**
In this article, we can learn how to integrate the **Huawei Push Kit** in Book Reading app to send the push message notification to users phone from the **AppGallery Connect**. Push notifications offers a great way to increase your application’s user engagement and boost your retention rates by sending meaningful messages or by informing users about your application. These messages can be sent at any time and even if your app is not running at that time. So, I will provide the series of articles on this Book Reading App, in upcoming articles I will integrate other Huawei Kits.

**Push Kit**
**Huawei Push Kit** is a messaging service developed by Huawei for developers to send messages to apps on users’ device in real time. Push Kit supports two types of messages: **notification messages** and **data messages**. You can send notifications and data messages to your users from your server using the [Push Kit APIs](https://developer.huawei.com/consumer/en/doc/development/HMS-3-References/push-server-send) or directly from the [AppGallery Push Kit Console](https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/push-console).

**AppGallery Connect**
Find the **Push Kit** message service in AppGallery connect dashboard.
Choose **My Projects > Grow > Push Kit**, and click **Enable now**.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cn9y62qxgqox508ln2v2.jpg)

Follow the steps to send the notification message to device from AppGallery Connect, [Sending a Notification Message](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/msg-sending-android-0000001136294192).

**Requirements**
1. Any operating system (MacOS, Linux and Windows).
2. Must have a Huawei phone with HMS 4.0.0.300 or later.
3. Must have a laptop or desktop with Android Studio, Jdk 1.8, SDK platform 26 and Gradle 4.6 and above installed.
4. Minimum API Level 24 is required.
5. Required EMUI 9.0.0 and later version devices.

**How to integrate HMS Dependencies**
- First register as Huawei developer and complete identity verification in Huawei developers website, refer to register a [Huawei ID](https://developer.huawei.com/consumer/en/doc/start/registration-and-verification-0000001053628148).
- Create a project in android studio, refer [Creating an Android Studio Project](https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#1).
- Generate a **SHA-256 certificate fingerprint**.
- To generate **SHA-256 certificate fingerprint**. On right-upper corner of android project click **Gradle**, choose **Project Name > Tasks > android**, and then click **signingReport**, as follows.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wlthkjyfulhjyiopvh3s.JPG)

Note: Project Name depends on the user created name.
- [Create an App in AppGallery Connect](https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#0).
- Download the **agconnect-services.json** file from **App information**, copy and paste in android **Project **under **app **directory, as follows.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qr9lonqmkywtplhexuo6.jpg)
- Enter **SHA-256 certificate fingerprint** and click **Save **button, as follows.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4a8u31q19frl4v4zzj4f.jpg)

- Click **Manage APIs** tab and enable **Push Kit**.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2aafcs7xlu3iy7066xek.jpg)

- Add the below maven URL in **build.gradle(Project)** file under the repositories of **buildscript, dependencies** and **allprojects**, refer [Add Configuration](https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#6).
`maven { url 'http://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
`
- Add the below plugin and dependencies in **build.gradle(Module)** file.
`apply plugin: id 'com.huawei.agconnect'
// Huawei AGC
implementation 'com.huawei.agconnect:agconnect-core:1.6.0.300'
// Huawei Push Kit
implementation 'com.huawei.hms:push:6.3.0.302'
// PDF Viewer
implementation 'com.github.barteksc:android-pdf-viewer:2.8.2'`
- Now Sync the gradle.
- Add the required permission to the **AndroidManifest.xm**l file.
`// Push Kit
<uses-permission android:name="android.permission.INTERNET" />
<service
    android:name=".PushService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.huawei.push.action.MESSAGING_EVENT" />
    </intent-filter>
</service>`

**Let us move to development**
I have created a project on Android studio with empty activity let us start coding.
In the **WebViewActivity.kt** to find the web view of pdf document.
`class WebViewActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_web_view)

        webView.webViewClient = WebViewClient()
        webView.settings.setSupportZoom(true)
        webView.settings.javaScriptEnabled = true
         val url = getPdfUrl()        webView.loadUrl("https://docs.google.com/gview?embedded=true&url=$url")

    }

    companion object{
        fun getPdfUrl(): String {
            return "https://mindorks.s3.ap-south-1.amazonaws.com/courses/MindOrks_Android_Online_Professional_Course-Syllabus.pdf"
        }
    }

}`
Create **PushService.kt** class to send the push notification to device.
`class PushService : HmsMessageService() {

     // When an app calls the getToken method to apply for a token from the server,
     // if the server does not return the token during current method calling, the server can return the token through this method later.
     // This method callback must be completed in 10 seconds. Otherwise, you need to start a new Job for callback processing.
     // @param token token
    override fun onNewToken(token: String?) {
        Log.i(TAG, "received refresh token:$token")
        // send the token to your app server.
        if (!token.isNullOrEmpty()) {
            // This method callback must be completed in 10 seconds. Otherwise, you need to start a new Job for callback processing.
            refreshedTokenToServer(token)
        }
        val intent = Intent()
        intent.action = CODELABS_ACTION
        intent.putExtra("method", "onNewToken")
        intent.putExtra("msg", "onNewToken called, token: $token")
        sendBroadcast(intent)
    }

    private fun refreshedTokenToServer(token: String) {
        Log.i(TAG, "sending token to server. token:$token")
    }

     // This method is used to receive downstream data messages.
     // This method callback must be completed in 10 seconds. Otherwise, you need to start a new Job for callback processing.
     // @param message RemoteMessage
    override fun onMessageReceived(message: RemoteMessage?) {
        Log.i(TAG, "onMessageReceived is called")
        if (message == null) {
            Log.e(TAG, "Received message entity is null!")
            return
        }
        // getCollapseKey() Obtains the classification identifier (collapse key) of a message.
        // getData() Obtains valid content data of a message.
        // getMessageId() Obtains the ID of a message.
        // getMessageType() Obtains the type of a message.
        // getNotification() Obtains the notification data instance from a message.
        // getOriginalUrgency() Obtains the original priority of a message.
        // getSentTime() Obtains the time when a message is sent from the server.
        // getTo() Obtains the recipient of a message.
        Log.i(TAG, """getCollapseKey: ${message.collapseKey}
            getData: ${message.data}
            getFrom: ${message.from}
            getTo: ${message.to}
            getMessageId: ${message.messageId}
            getMessageType: ${message.messageType}
            getSendTime: ${message.sentTime}
            getTtl: ${message.ttl}
            getSendMode: ${message.sendMode}
            getReceiptMode: ${message.receiptMode}
            getOriginalUrgency: ${message.originalUrgency}
            getUrgency: ${message.urgency}
            getToken: ${message.token}""".trimIndent())
        // getBody() Obtains the displayed content of a message
        // getTitle() Obtains the title of a message
        // getTitleLocalizationKey() Obtains the key of the displayed title of a notification message
        // getTitleLocalizationArgs() Obtains variable parameters of the displayed title of a message
        // getBodyLocalizationKey() Obtains the key of the displayed content of a message
        // getBodyLocalizationArgs() Obtains variable parameters of the displayed content of a message
        // getIcon() Obtains icons from a message
        // getSound() Obtains the sound from a message
        // getTag() Obtains the tag from a message for message overwriting
        // getColor() Obtains the colors of icons in a message
        // getClickAction() Obtains actions triggered by message tapping
        // getChannelId() Obtains IDs of channels that support the display of messages
        // getImageUrl() Obtains the image URL from a message
        // getLink() Obtains the URL to be accessed from a message
        // getNotifyId() Obtains the unique ID of a message
        val notification = message.notification
        if (notification != null) {
            Log.i(TAG, """
                getTitle: ${notification.title}
                getTitleLocalizationKey: ${notification.titleLocalizationKey}
                getTitleLocalizationArgs: ${Arrays.toString(notification.titleLocalizationArgs)}
                getBody: ${notification.body}
                getBodyLocalizationKey: ${notification.bodyLocalizationKey}
                getBodyLocalizationArgs: ${Arrays.toString(notification.bodyLocalizationArgs)}
                getIcon: ${notification.icon}                
                getImageUrl: ${notification.imageUrl}
                getSound: ${notification.sound}
                getTag: ${notification.tag}
                getColor: ${notification.color}
                getClickAction: ${notification.clickAction}
                getIntentUri: ${notification.intentUri}
                getChannelId: ${notification.channelId}
                getLink: ${notification.link}
                getNotifyId: ${notification.notifyId}
                isDefaultLight: ${notification.isDefaultLight}
                isDefaultSound: ${notification.isDefaultSound}
                isDefaultVibrate: ${notification.isDefaultVibrate}
                getWhen: ${notification.`when`}
                getLightSettings: ${Arrays.toString(notification.lightSettings)}
                isLocalOnly: ${notification.isLocalOnly}
                getBadgeNumber: ${notification.badgeNumber}
                isAutoCancel: ${notification.isAutoCancel}
                getImportance: ${notification.importance}
                getTicker: ${notification.ticker}
                getVibrateConfig: ${notification.vibrateConfig}
                getVisibility: ${notification.visibility}""".trimIndent())
            showNotification(notification.title,notification.body)
        }
        val intent = Intent()
        intent.action = CODELABS_ACTION
        intent.putExtra("method", "onMessageReceived")
        intent.putExtra("msg", "onMessageReceived called, message id:" + message.messageId + ", payload data:" + message.data)
        sendBroadcast(intent)
        val judgeWhetherIn10s = false
        // If the messages are not processed in 10 seconds, the app needs to use WorkManager for processing.
        if (judgeWhetherIn10s) {
            startWorkManagerJob(message)
        } else {
            // Process message within 10s
            processWithin10s(message)
        }
    }

    private fun showNotification(title: String?, body: String?) {
        val intent = Intent(this, WebViewActivity::class.java)
        intent.putExtra("URL", "Provide link here")
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT)
        val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
        val notificationBuilder = NotificationCompat.Builder(this)
            .setSmallIcon(R.drawable.sym_def_app_icon)
            .setContentTitle(title)
            .setContentText(body)
            .setAutoCancel(true)
            .setSound(soundUri)
            .setContentIntent(pendingIntent)
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(0, notificationBuilder.build())
    }

    private fun startWorkManagerJob(message: RemoteMessage?) {
        Log.d(TAG, "Start new Job processing.")
    }

    private fun processWithin10s(message: RemoteMessage?) {
        Log.d(TAG, "Processing now.")
    }

    override fun onMessageSent(msgId: String?) {
        Log.i(TAG, "onMessageSent called, Message id:$msgId")
        val intent = Intent()
        intent.action = CODELABS_ACTION
        intent.putExtra("method", "onMessageSent")
        intent.putExtra("msg", "onMessageSent called, Message id:$msgId")
        sendBroadcast(intent)
    }

    override fun onSendError(msgId: String?, exception: Exception?) {
        Log.i(TAG, "onSendError called, message id:$msgId, ErrCode:${(exception as SendException).errorCode}, " +
              "description:${exception.message}")
        val intent = Intent()
        intent.action = CODELABS_ACTION
        intent.putExtra("method", "onSendError")
        intent.putExtra("msg", "onSendError called, message id:$msgId, ErrCode:${exception.errorCode}, " +
                        "description:${exception.message}")
        sendBroadcast(intent)
    }

    override fun onTokenError(e: Exception) {
        super.onTokenError(e)
    }

private fun getToken() {
    showLog("getToken:begin")
    object : Thread() {
        override fun run() {
            try {
                // read from agconnect-services.json
                val appId = "Your app id"
                val token = HmsInstanceId.getInstance(this@MainActivity).getToken(appId, "HCM")
                Log.i(TAG, "get token:$token")
                if (!TextUtils.isEmpty(token)) {
                    sendRegTokenToServer(token)
                }
                showLog("get token:$token")
            } catch (e: ApiException) {
                Log.e(TAG, "get token failed, $e")
                showLog("get token failed, $e")
            }
        }
    }.start()
}

fun showLog(log: String?) {
    runOnUiThread {
        val tvView = findViewById<View?>(R.id.tv_log)
        val svView = findViewById<View?>(R.id.sv_log)
        if (tvView is TextView) {
            tvView.text = log
        }
        if (svView is ScrollView) {
            svView.fullScroll(View.FOCUS_DOWN)
        }
    }
}

private fun sendRegTokenToServer(token: String?) {
    Log.i(TAG, "sending token to server. token:$token")
}

    companion object {
        private const val TAG: String = "PushDemoLog"
        private const val CODELABS_ACTION: String = "com.huawei.codelabpush.action"
    }

}`
In the **activity_web_view.xml** we can create the UI screen.
`<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".WebViewActivity">

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>`
In the **log_layout.xml** we can create the UI screen.
`<?xml version="1.0" encoding="utf-8"?>
<ScrollView android:id="@+id/sv_log"
    android:overScrollMode="never"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView
        android:id="@+id/tv_log"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</ScrollView>`

**Demo**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yphwuh0x66qu3ydr5t7m.jpg)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y6ilyyyxgx1dio3xx00j.jpg)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7tsm2tz1exiouhqxp1je.jpg)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5dkjbm6cwli1jgdtxv3i.jpg)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ydg8evbncymavve2m1zy.jpg)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c2dm963grj0a90uzhxog.jpg)

**Tips and Tricks**
1. Make sure you are already registered as Huawei developer.
2. Set minSDK version to 24 or later, otherwise you will get AndriodManifest merge issue.
3. Make sure you have added the **agconnect-services.json** file to app folder.
4. Make sure you have added SHA-256 fingerprint without fail.
5. Make sure all the dependencies are added properly.

**Conclusion**
In this article, we have learned how to integrate the Huawei Push Kit in Book Reading app to send the push message notification to users’ phone from the AppGallery Connect. Push notifications offers a great way to increase your application’s user engagement and boost your retention rates by sending meaningful messages or by informing users about your application. These messages can be sent at any time and even if your app is not running at that time.

I hope you have read this article. If you found it is helpful, please provide likes and comments.

**Reference**
Push Kit – [Document](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)