[Huawei Developers] HMS Community:
Introduction​

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

Beginner: Integration of Huawei Account kit in Paralysis Glove IoT application - Part 1

In this article, we will learn about Paralysis Glove application and also we will learn integration of the Huawei Push kit in Paralysis Glove IoT application. Here it will cover how exactly push notification works, and benefits of push notification and also we can integrate Huawei Push kit in the smart gloves application. And also we will learn how to test the Push notification from the Postman.

Content​

What is Push Notification?

Advantages of Push notification

Type of notification

Huawei Push notification

Message types.

Integration of push kit

Testing push kit from Postman

In the current mobile world 90% of the applications integrated the push notification. Every application integrate the push notification to engage the users. And also to make better marketing. Every ecommerce application shared the details about offers, discounts, price drops for particular product, the delivery status through the push notification.

First let us understand what push notification is.

Push notification are basically alerts to users from mobile application. Users receives in real-time. Developers can send notification at any time and any day.

Advantages of Push Notification.

User retention: In the world of mobile apps, user retention is a key indicator of how well your app is performing in the market. This metric lets you to see how many users who have downloaded your app and used it once come back to use the app again.

Increase engagement: The power of Huawei push notification is to make user engage by sending some cool notification to users. Notification provides the interaction with application, by this user can spend more time with the application.

Push Notification types

Reminder Notification: Its reminder notification for example recharge reminder, meeting time reminder, appointment reminder etc.

Alert Notification: this type of notification alerts to user when something happens in application which depends upon user. Example when someone sends message, comment on pic.

Promotional notification: these are the promotional notification when application offers something to user example discount, sales date, some weekend sales etc.

Purchas notification: These are valuable notifications and have to do with purchases users make within your app. It can contain information like order confirmation, order status, order updates, tracking, and receipts.

Survey notification: these are feedback or survey notification when application wants to get the feedback or survey at that moment these kind of notification will be used.

Huawei Push Kit is a messaging service provided for you to establish a cloud-to-device messaging channel. By integrating Push Kit, you can send messages to your apps on user devices in real time. This helps you to maintain closer ties with users and increases user awareness of and engagement with your apps.

Push Kit consists of two parts:

Message push from the cloud to the device: enables you to send data and messages to your apps on user devices.

Message display on devices: provides various display styles, such as the notification panel, home screen banner, and lock screen on user devices.

Huawei has 2 types of Messages.

1. Notification Message

2. Data Message

Notification Message: A notification message is directly sent by Push Kit and displayed in the notification panel on the user device, not requiring your app to be running in the background. The user can tap the notification message to trigger the corresponding action such as opening your app or opening a web page. You can tailor the display styles and reminder modes to fit user needs, which will greatly improve the daily active users (DAU) of your app. The common application scenarios of the notification message include subscription, travel reminder, and account status.

Batch message: a message sent by an app in batches to users who will obtain the same content, which can improve user experience and stimulate user interaction with the app.

Personalized message: a message generated based on a unified message template and sent by an app to an audience. The unified message template contains placeholders, which will be replaced based on the settings and preferences of specific users.

Point-to-point message: a message sent by an app to a user when the user takes a specific action.

Instant message: An instant message is a point-to-point or group chatting message (or private message) between users.

Data Message: Data messages are processed by your app on user devices. After a device receives a message containing data or instructions from the cloud, the device passes the message to the app instead of directly displaying it. The app then parses the message and triggers the required action (for example, going to a web page or an app page). For such a message, you can also customize display styles for higher efficiency.

Push Kit cannot guarantee a high data message delivery rate, because it may be affected by Android system restrictions and whether the app is running in the background. The common application scenarios of the data message include the VoIP call, voice broadcast, and interaction with friends.

Prerequisite​

AppGallery Account

Android Studio 3.X

SDK Platform 19 or later

Gradle 4.6 or later

HMS Core (APK) 4.0.0.300 or later

Huawei Phone EMUI 5.0 or later

Non-Huawei Phone Android 5.1 or later

Service integration on AppGallery​

1. We need to register as a developer account in AppGallery Connect.

2. Create an app by referring to Creating a Project and Creating an App in the Project.

3. Set the data storage location based on the current location.

4. Enabling Push Kit Service on AppGallery.

5. Generating a Signing Certificate Fingerprint.

6. Configuring the Signing Certificate Fingerprint.

7. Get your agconnect-services.json file to the app root directory.

Client development

1. Create android project in android studio IDE.

2. Add the maven URL inside the repositories of buildscript and allprojects respectively (project level build.gradle file).

maven { url 'https://developer.huawei.com/repo/' }

3. Add the classpath inside the dependency section of the project level build.gradle file.

classpath 'com.huawei.agconnect:agcp:1.5.2.300'

4. Add the plugin in the app-level build.gradle file.

apply plugin: 'com.huawei.agconnect'

5. Add the below library in the app-level build.gradle file dependencies section.

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

6. Add all the below permission in the AndroidManifest.xml.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<manifest ...>
...
<application ...>
<service android:name=".push.PushNotificationHmsMessageService" android:exported="false">
<intent-filter>
<action android:name="com.huawei.push.action.MESSAGING_EVENT"/>
</intent-filter>
</service>
</application>
...
</manifest>

7. Sync the project.

Getting Push token in android.

private fun getPushToken() {
object : Thread() {
override fun run() {
try {
// Obtain the app ID from the agconnect-services.json file.
val appId = "your APP_ID"

// Set tokenScope to HCM.
val tokenScope = "HCM"
val token = HmsInstanceId.getInstance(this@MainActivity).getToken(appId, tokenScope)
Log.i(TAG, "get token:$token")

// Check whether the token is null.
if (!TextUtils.isEmpty(token)) {
sendRegTokenToServer(token)
}
} catch (e: ApiException) {
Log.e(TAG, "get token failed, $e")
}
}
}.start()
}
private fun sendRegTokenToServer(token: String?) {
Log.i(TAG, "sending token to server. token:$token")
}

Whenever new token is generated onNewToken() will be called and you need to send the latest token to backend or server.

// When new token is generated below method gives the new token
override fun onNewToken(token: String?, bundle: Bundle?) {
// Obtain a push token.
Log.i(TAG, "have received refresh token:$token")

// Check whether the token is null.
if (!token.isNullOrEmpty()) {
refreshedTokenToServer(token)
}
}

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

When push notification is triggered from AGC console or Postman below method will be called. You need to generate notification when onMessageReceived() called.

//When push notification is triggered from AGC console or Postman below method will be called
override fun onMessageReceived(message: RemoteMessage?) {
Log.i(TAG, "onMessageReceived is called")

// Check whether the message is empty.
if (message == null)
{
Log.e(TAG, "Received message entity is null!")
return
}

// Obtain the message content.
Log.i(TAG, """getData: ${message.data}
getFrom: ${message.from}
getTo: ${message.to}
getMessageId: ${message.messageId}
getSentTime: ${message.sentTime}
getDataMap: ${message.dataOfMap}
getMessageType: ${message.messageType}
getTtl: ${message.ttl}
getToken: ${message.token}""".trimIndent())

val judgeWhetherIn10s = false
// If the message is not processed within 10 seconds, create a job to process it.
if (judgeWhetherIn10s) {
startWorkManagerJob(message)
} else {
// Process the message within 10 seconds.
processWithin10s(message)
}
}
private fun startWorkManagerJob(message: RemoteMessage?) {
Log.d(TAG, "Start new Job processing.")
}
private fun processWithin10s(message: RemoteMessage?) {
Log.d(TAG, "Processing now.")
}

Now let’s learn the coding part.

Step 1: Create Notification.

NotificationUtils.kt

package com.huawei.paralysisglove.push

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.media.RingtoneManager
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import kotlin.random.Random
import android.provider.Settings
import com.huawei.paralysisglove.R
import com.huawei.paralysisglove.ui.activities.SplashActivity

class NotificationUtil(private val context: Context) {

fun showNotification(title: String, message: String) {
val intent = Intent(context, SplashActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(
context, 0, intent,
PendingIntent.FLAG_ONE_SHOT
)

val channelId = context.getString(R.string.default_notification_channel_id)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(context, channelId)
.setColor(ContextCompat.getColor(context, android.R.color.holo_red_dark))
.setSmallIcon(R.drawable.glove)
.setContentTitle(title)
.setContentText(message)
.setAutoCancel(true)
.setStyle(
NotificationCompat.BigTextStyle()
.bigText(message)
)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent)

val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
channelId,
"Default Channel",
NotificationManager.IMPORTANCE_HIGH
)
notificationManager.createNotificationChannel(channel)
}

notificationManager.notify(Random.nextInt(), notificationBuilder.build())
}

fun isTimeAutomatic(context: Context): Boolean {
return Settings.Global.getInt(
context.contentResolver,
Settings.Global.AUTO_TIME,
0
) == 1;
}
}

Step 2: Create Worker manager to process data and notification in background. If received data need to be processed and if it takes more than 10 seconds, then create work manager else directly show the notification.

SchedulerWorker.kt

package com.huawei.paralysisglove.push

import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters

class ScheduledWorker(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {

override fun doWork(): Result {
Log.d(TAG, "Work START")
// Get Notification Data
val title = inputData.getString(NOTIFICATION_TITLE)
val message = inputData.getString(NOTIFICATION_MESSAGE)

// Show Notification
NotificationUtil(applicationContext).showNotification(title!!, message!!)

// TODO Do your other Background Processing

Log.d(TAG, "Work DONE")
// Return result
return Result.success()
}

companion object {
private const val TAG = "ScheduledWorker"
const val NOTIFICATION_TITLE = "notification_title"
const val NOTIFICATION_MESSAGE = "notification_message"
}
}

Step 3: Create broadcast receiver.

NotificationBroadcastReceiver.kt

package com.huawei.paralysisglove.push
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.huawei.paralysisglove.push.ScheduledWorker.Companion.NOTIFICATION_MESSAGE
import com.huawei.paralysisglove.push.ScheduledWorker.Companion.NOTIFICATION_TITLE

class NotificationBroadcastReceiver : BroadcastReceiver() {

override fun onReceive(context: Context?, intent: Intent?) {
intent?.let {
val title = it.getStringExtra(NOTIFICATION_TITLE)
val message = it.getStringExtra(NOTIFICATION_MESSAGE)

// Create Notification Data
val notificationData = Data.Builder()
.putString(NOTIFICATION_TITLE, title)
.putString(NOTIFICATION_MESSAGE, message)
.build()

// Init Worker
val work = OneTimeWorkRequest.Builder(ScheduledWorker::class.java)
.setInputData(notificationData)
.build()

// Start Worker
WorkManager.getInstance().beginWith(work).enqueue()

Log.d(javaClass.name, "WorkManager is Enqueued.")
}
}
}

Step 4: Add NotificationBroadcastReceiver in the AndroidManifest.xml

<receiver android:name=".push.NotificationBroadcastReceiver" />

Step 5: Create Huawei PushNotificationHmsMessageService.kt

package com.huawei.paralysisglove.push

import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import com.huawei.hms.push.HmsMessageService
import com.huawei.hms.push.RemoteMessage
import android.text.TextUtils
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.google.gson.Gson
import com.huawei.hms.common.ApiException

import com.huawei.hms.aaid.HmsInstanceId

import com.huawei.agconnect.config.AGConnectServicesConfig
import com.huawei.paralysisglove.push.ScheduledWorker.Companion.NOTIFICATION_MESSAGE
import com.huawei.paralysisglove.push.ScheduledWorker.Companion.NOTIFICATION_TITLE
import com.huawei.paralysisglove.push.model.PushModel
import org.json.JSONException
import java.text.SimpleDateFormat
import java.util.*

class PushNotificationHmsMessageService : HmsMessageService() {
override fun onMessageReceived(message: RemoteMessage?) {
Log.i(TAG, "onMessageReceived is called")
if (message == null) {
Log.e(TAG, "Received message entity is null!")
return
}
Log.i(
TAG, """get Data: ${message.data} getFrom: ${message.from}
getTo: ${message.to}
getMessageId: ${message.messageId}
getSendTime: ${message.sentTime}
getDataMap: ${message.dataOfMap}
getMessageType: ${message.messageType}
getTtl: ${message.ttl}
getToken: ${message.token}"""
)

message.data.isNotEmpty().let { it ->
if (it) {
Log.d(TAG, "Message data payload: ${message.data}")
try {
val pushModel: PushModel = Gson().fromJson(message.data, PushModel::class.java)
//val jsonData = JSONObject(message.data)
// Get Message details
val title = pushModel.title
val content = pushModel.message

// Check whether notification is scheduled or not
val isScheduled = pushModel.isScheduled
isScheduled.let {
if (it) {
// Check that 'Automatic Date and Time' settings are turned ON.
// If it's not turned on, Return
val notificationUtil = NotificationUtil(this)
if (!notificationUtil.isTimeAutomatic(applicationContext)) {
Log.d(TAG, "`Automatic Date and Time` is not enabled")
return
}
// This is Scheduled Notification, Schedule it
val scheduledTime = pushModel.scheduledTime
scheduleAlarm(scheduledTime, title, content)
} else {
// This is not scheduled notification, show it now
// Create Notification Data
var body =
"You have reached from " + pushModel.data.fromLocation + " to " + pushModel.data.toLocation
val notificationData = Data.Builder()
.putString(NOTIFICATION_TITLE, title)
.putString(NOTIFICATION_MESSAGE, body)
.build()

// Init Worker
val work = OneTimeWorkRequest.Builder(ScheduledWorker::class.java)
.setInputData(notificationData)
.build()

// Start Worker
WorkManager.getInstance(this).beginWith(work).enqueue()

Log.d(javaClass.name, "WorkManager is Enqueued.")
}
}
} catch (e: JSONException) {
e.printStackTrace()
}
} else {
val notificationData = Data.Builder()
.putString(NOTIFICATION_TITLE, message.notification.title)
.putString(NOTIFICATION_MESSAGE, message.notification.body)
.build()

// Init Worker
val work = OneTimeWorkRequest.Builder(ScheduledWorker::class.java)
.setInputData(notificationData)
.build()

// Start Worker
WorkManager.getInstance(this).beginWith(work).enqueue()
}
}
}

private fun scheduleAlarm(
scheduledTimeString: String?,
title: String?,
message: String?
) {
val alarmMgr = applicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val alarmIntent =
Intent(applicationContext, NotificationBroadcastReceiver::class.java).let { intent ->
intent.putExtra(NOTIFICATION_TITLE, title)
intent.putExtra(NOTIFICATION_MESSAGE, message)
PendingIntent.getBroadcast(applicationContext, 0, intent, 0)
}

// Parse Schedule time
val scheduledTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
.parse(scheduledTimeString!!)

scheduledTime?.let {
// With set(), it'll set non repeating one time alarm.
alarmMgr.set(
AlarmManager.RTC_WAKEUP,
it.time,
alarmIntent
)
}
}

override fun onNewToken(token: String) {
Log.i(TAG, "received refresh token:$token")
if (!TextUtils.isEmpty(token)) {
refreshedTokenToServer(token)
}
}

// If the version of the Push SDK you integrated is 5.0.4.302 or later, you also need to override the method.
override fun onNewToken(token: String, bundle: Bundle?) {
Log.i(TAG, "have received refresh token $token")
if (!TextUtils.isEmpty(token)) {
refreshedTokenToServer(token)
}
}

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

private fun deleteToken() {
// Create a thread.
object : Thread() {
override fun run() {
try {
// Obtain the app ID from the agconnect-service.json file.
val appId =
AGConnectServicesConfig.fromContext(this@PushNotificationHmsMessageService)
.getString("client/app_id")

// Set tokenScope to HCM.
val tokenScope = "HCM"
// Delete the token.
HmsInstanceId.getInstance(this@PushNotificationHmsMessageService)
.deleteToken(appId, tokenScope)
Log.i(TAG, "token deleted successfully")
} catch (e: ApiException) {
Log.e(TAG, "deleteToken failed.$e")
}
}
}.start()
}

/**
* Persist token to third-party servers.
*
* Modify this method to associate the user's FCM registration token with any server-side account
* maintained by your application.
*
* @param token The new token.
*/
private fun sendRegistrationToServer(token: String?) {
// TODO: Implement this method to send token to your app server.
Log.d(TAG, "sendRegistrationTokenToServer($token)")
}

companion object {
private const val TAG = "PushNotificationHmsMessageService"
}

}

Testing on Postman

Notification Message

Step 1: Download below json file.

{
"info": {
"_postman_id": "6ee69330-4edb-4b0c-b235-4247daa3e2ea",
"name": "PushKitTest",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Send notification",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "Bearer CgB6e3x9aAsJsxGgxgasZWXyu63iI9H/bwuVZFKbJfRjcCjP0LCJYpep4UAkZj33jknaHBRLWTRnTxHKcMfzOxBj",
"type": "string"
}
]
},
"method": "POST",
"header": [
{
"key": "Content-Type",
"name": "Content-Type",
"value": "application/json",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"validate_only\": false,\n \"message\": \n {\n \"notification\":\n {\n \"title\": \"Huawei Push Kit\",\n \"body\": \"Hello, I'm a Push Kit.\"\n },\n \"android\":\n {\n \"notification\":\n {\n \"click_action\":\n {\n \"type\": 3\n }\n }\n },\n \"token\": [\"ANPpqLe8aTPl9tsB2Iu_820GmUQ-prH-dzPV-orbkm9yym2bitxd3emgxglrkVJW8hbTkusjHCu2_RW-AdnqqSbHevBQYwhjqOBzCdcu_Zrqfzuowi7Sxp4c2r_ztTORVQ\"]\n }\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "https://push-api.cloud.huawei.com/v1/103346107/messages:send",
"protocol": "https",
"host": [
"push-api",
"cloud",
"huawei",
"com"
],
"path": [
"v1",
"103346107",
"messages:send"
]
}
},
"response": []
},
{
"name": "Send data message",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "Bearer CgB6e3x9aAsJsxGgxgasZWXyu63iI9H/bwuVZFKbJfRjcCjP0LCJYpep4UAkZj33jknaHBRLWTRnTxHKcMfzOxBj",
"type": "string"
}
]
},
"method": "POST",
"header": [
{
"key": "Content-Type",
"name": "Content-Type",
"type": "text",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"validate_only\": false,\n \"message\": \n {\n \"data\": \"{\\\"title\\\":\\\"Huawei Push Kit\\\",\\\"text\\\":\\\"Hi, I'm a Push Kit.\\\",\\\"channel_id\\\":\\\"channel_1\\\"}\",\n \"token\": [\"ANPpqLe8aTPl9tsB2Iu_820GmUQ-prH-dzPV-orbkm9yym2bitxd3emgxglrkVJW8hbTkusjHCu2_RW-AdnqqSbHevBQYwhjqOBzCdcu_Zrqfzuowi7Sxp4c2r_ztTORVQ\"]\n }\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "https://push-api.cloud.huawei.com/v1/103346107/messages:send",
"protocol": "https",
"host": [
"push-api",
"cloud",
"huawei",
"com"
],
"path": [
"v1",
"103346107",
"messages:send"
]
}
},
"response": []
},
{
"name": "Get access token",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"name": "Content-Type",
"type": "text",
"value": "application/x-www-form-urlencoded"
}
],
"body": {
"mode": "urlencoded",
"urlencoded": [
{
"key": "grant_type",
"value": "client_credentials",
"type": "text"
},
{
"key": "client_id",
"value": "103346107",
"type": "text"
},
{
"key": "client_secret",
"value": "1e35055832e028a9b07251ad1f9192b2ffbf4551cd71bfbb005875b046f0b981",
"type": "text"
}
]
},
"url": {
"raw": "https://oauth-login.cloud.huawei.com/oauth2/v2/token",
"protocol": "https",
"host": [
"oauth-login",
"cloud",
"huawei",
"com"
],
"path": [
"oauth2",
"v2",
"token"
]
}
},
"response": []
}
]
}

Step 2: Open Postman. Import the file. (Postman > file > import) After you import, we can send 3 requests.

Step 3: Replace the app_id and app_secret values in the body with the App ID and App secret values on the application home page of the Huawei Console. Replace the fields in the image below with the values in your own project.

Step 4: Get push token in MainActivity.kt

private fun getToken() {
object : Thread() {
override fun run() {
try {
val appId = AGConnectOptionsBuilder().build(this@MainActivity)
.getString("client/app_id")
val token = HmsInstanceId.getInstance(this@MainActivity)
.getToken(appId, "HCM")
Log.i("PUSH", "getToken() token: $token")
} catch (e: ApiException) {
Log.e("PUSH", "getToken() failure: ${e.message}")
}
}
}.start()
}

Step 5: Open the Get access token file and click the Send button to get access token. The access token is valid for 3600 seconds and you need to renew it late.

Step 6: Open the Send notification and Send data message file and replace the access_token part in Authorization with the access token we got in before step. Don't forget to delete this characters \ in the access token.

After authorization token replaces, let us send the notification now.

Send Notification

Body:

{
"validate_only": false,
"message":
{
"notification":
{
"title": "Push Ki Demo ",
"body": "Enter body here."
},
"android":
{
"notification":
{
"click_action":
{
"type": 3
}
}
},
"token": ["Add your push token here"]
}
}

Data Message

Body:

{
"validate_only": false,
"message":
{
"data": "{\"title\":\"Huawei Push Kit\",\"text\":\"Hi, I'm a Push Kit.\",\"channel_id\":\"channel_1\"}",
"token": ["Add your push token here",
"Add your push token here"]
}
}

Result​

​

Tips and Tricks​

1. Make sure you are already registered as a Huawei developer.

2. Set min SDK version to 19 or later, otherwise you will get AndriodManifest to merge issue.

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

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

5. Make sure all the dependencies are added properly.

Conclusion​

In this article, we have learnt the integration of the Huawei Push Kit in Paralysis Gloves mobile application using Android Studio and Kotlin. And also understood about Huawei Push kit, types of messages, notification type, How to test push notification from AppGallery console.

Reference​

Push Kit - Official document

Push Kit - Code lab

Push Kit - Training Video