Ever wanted to send push notifications to your Android app? Yes? Well then, there's a good chance you've used a delivery service like Google Cloud Messaging or Firebase Cloud Messaging. Unfortunately, these services won't work on Huawei devices.
Luckily, Huawei has implemented its own notification delivery framework as part of HMS. If you're targeting Huawei devices with your app and need to deliver push notifications, this guide is for you. We'll be talking about receiving notifications through HMS on Android.
Preparation
First up, make sure you have a Huawei Developer Account. This process can take a couple days, and you'll need one to use this SDK, so be sure to start that as soon as possible. You can sign up at https://developer.huawei.com.
Next, you'll want to obtain the SHA-256 representation of your app's signing key. If you don't have a signing key yet, be sure to create one before continuing. To obtain your signing key's SHA-256, you'll need to use Keytool which is part of the JDK installation. Keytool is a command-line program. If you're on Windows, open CMD. If you're on Linux, open Terminal.
On Windows, you'll need to "cd" into the directory containing the Keytool executable. For example, if you have JDK 1.8 v231 installed, Keytool will be located at the following path:
Code:
C:\Program Files\Java\jdk1.8.0_231\bin\
Once you find the directory, "cd" into it:
Code:
C: #Make sure you're in the right drive
cd C:\Program Files\Java\jdk1.8.0_231\bin\
Next, you need to find the location of your keystore. Using Android's debug keystore as an example, where the Android SDK is hosted on the "E:" drive in Windows, the path will be as follows:
Code:
E:\AndroidSDK\.android\debug.keystore
(Keytool also supports JKS-format keystores.)
Now you're ready to run the command. On Windows, it'll look something like this:
Code:
keytool -list -v -keystore E:\AndroidSDK\.android\debug.keystore
On Linux, the command should be similar, just using UNIX-style paths instead.
Enter the keystore password, and the key name (if applicable), and you'll be presented with something similar to the following:
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Make note of the SHA256 field.
SDK Setup
Now we're ready to add the Push Kit SDK to your Android Studio project. Go to your Huawei Developer Console and click the HUAWEI AppGallery tile. Agree to the terms of use if prompted.
Click the "My projects" tile here. If you haven't already added your project to the AppGallery, add it now. You'll be asked for a project name. Make it something descriptive so you know what it's for.
Now, you should be on a screen that looks something like the following:
Click the "Add app" button. Here, you'll need to provide some details about your app, like its name and package name.
Once you click OK, some SDK setup instructions will be displayed. Follow them to get everything added to your project. You'll also need to add the following to the "dependencies" section of your app-level build.gradle file:
Code:
implementation 'com.huawei.hms:push:4.0.4.301'
If you ever need to come back to these instructions, you can always click the "Add SDK" button after "App information" on the "Project setting" page.
Now you should be back on the "Project setting" page. Find the "SHA-256 certificate fingerprint" field under "App information," click the "+" button, and paste your SHA-256.
Go back to the Manage APIs tab on the "Project setting" page. Scroll down until you find "Push Kit" and make sure it's enabled.
Next, expand the "Growing" category in the sidebar and select "Push Kit."
You should see something similar to the following:
Click the "Enable now" button. After a few seconds, you should see this popup:
Next, go to the "Settings" tab. You should then be prompted to select a data storage location. Choose the one that's best for you.
Now, if you're using obfuscation in your app, you'll need to whitelist a few things for HMS to work properly.
For ProGuard:
Code:
-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.hianalytics.android.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}
For AndResGuard:
Code:
"R.string.hms*",
"R.string.agc*",
"R.string.connect_server_fail_prompt_toast",
"R.string.getting_message_fail_prompt_toast",
"R.string.no_available_network_prompt_toast",
"R.string.third_app_*",
"R.string.upsdk_*",
"R.layout.hms*",
"R.layout.upsdk_*",
"R.drawable.upsdk*",
"R.color.upsdk*",
"R.dimen.upsdk*",
"R.style.upsdk*
Next up, you'll need to create a class extending HmsMessageService:
Code:
class MyMessageService : HmsMessageService() {
//...
}
And add it to your AndroidManifest.XML:
XML:
<service
android:name=".MyMessageService"
android:exported="false">
<intent-filter>
<action android:name="com.huawei.push.action.MESSAGING_EVENT" />
</intent-filter>
</service>
That's it! The Push Kit SDK should now be available in your project.
Basic Usage
To use the Push Kit SDK, you'll need to generate a token for each device using your app. The SDK can take care of this for you. Simply add the following to your AndroidManifest.xml:
XML:
<application>
<!--.....-->
<meta-data
android:name="push_kit_auto_init_enabled"
android:value="true" />
</application>
Then, inside MyMessageService, you just need to override the following method:
Code:
class MyMessageService : HmsMessageService() {
//Make sure whatever happens here takes fewer than 10 seconds.
//Otherwise, you'll run into Android's Service restrictions
//and you'll need to create a Job.
override fun onNewToken(token: String?) {
if (!token.isNullOrBlank()) {
//Send the token to your server
}
}
}
(For more advanced behavior, like manually applying for tokens, see Huawei's documentation.)
To receive messages, simply override the onMessageReceived() method:
Code:
class MyMessageService : HmsMessageService() {
//...
//A new message has been received
override fun onMessageReceived(message: RemoteMessage?) {
if (message == null) {
//The message is null. Nothing to handle.
return
}
//There is a lot of potential data contained in a message.
val collapseKey = message.collapseKey //String
val data = message.data //String
val from = message.from //String
val to = message.to //String
val messageId = message.messageId //String
val sentTime = message.sentTime //Long
val dataOfMap = message.dataOfMap //MutableMap<String, String>
val messageType = message.messageType //String
val ttl = message.ttl //Int
val token = message.token //String
//You can also get a notification template for the message
val notification = message.notification //RemoteMessage$Notification
val imageUrl = notification.imageUrl //Uri
val title = notification.title //String
val titleLocalizationKey = notification.titleLocalizationKey //String
val titleLocalizationArgs = notification.titleLocalizationArgs //Array<String>
val body = notification.body //String
val bodyLocalizationKey = notification.bodyLocalizationKey //String
val bodyLocalizationArgs = notification.bodyLocalizationArgs //Array<String>
val icon = notification.icon //String
val sound = notification.sound //String
val tag = notification.tag //String
val color = notification.color //String
val clickAction = notification.clickAction //String
val channelId = notification.channelId //String
val link = notification.link //Uri
val notifyId = notification.notifyId //Int
//Just like with onNewToken(), this method needs to complete
//within 10 seconds.
}
}
Conclusion
That's all you need to receive push notifications on Android through HMS!
To learn how you can set up message sending, and for more advanced methods of notification handling, be sure to check out Huawei's official documentation.
Related
Huawei's Nearby SDK is sort of like Android Beam on steroids. Apps implementing it can use it for local file transfer, but that's not all. Nearby also enables apps to do realtime local communication, which is useful for things like locak multiplayer gaming. Finally, it also supports messaging, in the form of "beacons" that an implementing app can use to retrieve relevant localized information.
If any of this seems like it could be useful for you, read on, cause we're going to implement it.
Preparation
First up, make sure you have a Huawei Developer Account. This process can take a couple days, and you'll need one to use this SDK, so be sure to start that as soon as possible. You can sign up at https://developer.huawei.com.
Next, you'll want to obtain the SHA-256 representation of your app's signing key. If you don't have a signing key yet, be sure to create one before continuing. To obtain your signing key's SHA-256, you'll need to use Keytool which is part of the JDK installation. Keytool is a command-line program. If you're on Windows, open CMD. If you're on Linux, open Terminal.
On Windows, you'll need to "cd" into the directory containing the Keytool executable. For example, if you have JDK 1.8 v231 installed, Keytool will be located at the following path:
Code:
C:\Program Files\Java\jdk1.8.0_231\bin\
Once you find the directory, "cd" into it:
Code:
C: #Make sure you're in the right drive
cd C:\Program Files\Java\jdk1.8.0_231\bin\
Next, you need to find the location of your keystore. Using Android's debug keystore as an example, where the Android SDK is hosted on the "E:" drive in Windows, the path will be as follows:
Code:
E:\AndroidSDK\.android\debug.keystore
(Keytool also supports JKS-format keystores.)
Now you're ready to run the command. On Windows, it'll look something like this:
Code:
keytool -list -v -keystore E:\AndroidSDK\.android\debug.keystore
On Linux, the command should be similar, just using UNIX-style paths instead.
Enter the keystore password, and the key name (if applicable), and you'll be presented with something similar to the following:
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Make note of the SHA256 field.
SDK Setup
Now we're ready to add the Nearby SDK to your Android Studio project. Go to your Huawei Developer Console and click the HUAWEI AppGallery tile. Agree to the terms of use if prompted.
Click the "My projects" tile here. If you haven't already added your project to the AppGallery, add it now. You'll be asked for a project name. Make it something descriptive so you know what it's for.
Now, you should be on a screen that looks something like the following:
Click the "Add app" button. Here, you'll need to provide some details about your app, like its name and package name.
Once you click OK, some SDK setup instructions will be displayed. Follow them to get everything added to your project. You'll also need to add the following to the "dependencies" section of your app-level build.gradle file:
Code:
implementation 'com.huawei.hms:nearby:4.0.4.300'
If you ever need to come back to these instructions, you can always click the "Add SDK" button after "App information" on the "Project setting" page.
Now you should be back on the "Project setting" page. Find the "SHA-256 certificate fingerprint" field under "App information," click the "+" button, and paste your SHA-256.
Now, go to the Manage APIs tab on the "Project setting" page. Scroll down until you find "Nearby Service" and make sure it's enabled.
Now, if you're using obfuscation in your app, you'll need to whitelist a few things for HMS to work properly.
For ProGuard:
Code:
-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.hianalytics.android.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}
For AndResGuard:
Code:
"R.string.hms*",
"R.string.agc*",
"R.string.connect_server_fail_prompt_toast",
"R.string.getting_message_fail_prompt_toast",
"R.string.no_available_network_prompt_toast",
"R.string.third_app_*",
"R.string.upsdk_*",
"R.layout.hms*",
"R.layout.upsdk_*",
"R.drawable.upsdk*",
"R.color.upsdk*",
"R.dimen.upsdk*",
"R.style.upsdk*
That's it! The Nearby SDK should now be available in your project.
Basic Usage
There are currently three ways to use the Nearby SDK: Nearby Connection, Nearby Message, and Beacon Management.
Nearby Connection
Nearby Connection is the API that allows you to locally transmit and receive data to and from another device. The first thing you'll need to do to implement this is declare the use of quite a few permissions. These should go in your AndroidManifest.xml.
XML:
<!-- Required for Nearby Discovery and Nearby Transfer -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!-- If you don't care about devices running Android 10 or later, this can be replaced with ACCESS_COARSE_LOCATION -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Required for FILE payloads -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Some of these permissions, like "ACCESS_FINE_LOCATION" are "dangerous"-level permissions, so make sure you request access to them on Android Marshmallow and above.
Now, there are three usage "modes" for this API: Mesh, Star, and P2P.
Mesh allows all involved devices to send and receive data to and from all other involved devices. This comes at the cost of bandwidth speed, though, so this should only be used for things with low data throughput, like game-state sharing.
Star is similar to Mesh, but with a twist: the bandwidth is higher. What's the trade-off? Well, one device has to act as a "hub" of sorts. That hub can send to and receive from all other involved devices, but the rest can only communicate to the hub itself. This is useful for things like mass video and file sharing.
Finally, there's P2P. This is a simple 1-on-1 connection, where one device sends a file to another. This method has the highest bandwidth, but obviously the strictest constraints.
Below is an example of how to broadcast the ability to connect using the Star policy. This would be used on potential clients for the "hub" to connect to.
Code:
//Begin broadcasting the ability to receive connections.
fun startBroadcasting() {
val policy = Policy.POLICY_STAR
val option = BroadcastOption.Builder()
.setPolicy(policy)
.build()
val dataCallback = object : DataCallback() {
override fun onReceived(p0: String?, p1: Data?) {
//We'll get to the implementation of this later.
}
override fun onTransferUpdate(p0: String?, p1: TransferStateUpdate?) {
}
}
val connectionCallback = object : ConnectCallback() {
override fun onDisconnected(endpointId: String) {
//The connection has been terminated
}
override fun onEstablish(endpointId: String, connectInfo: ConnectInfo) {
//A connection has been established.
//This is where you either accept or reject the connection.
//Both parties need to accept the connection. You can either
//present a confirmation to the user or silently accept.
//Similarly to Bluetooth pairing, you can display an auth code
//on both devices. Obtain the auth code:
val authCode = connectInfo.authCode
//Silently accept:
Nearby.getDiscoveryEngine(context)
.acceptConnect(endpointId, dataCallback)
}
override fun onResult(endpointId: String, connectResult: ConnectResult) {
//Handle the result of a connection request.
when (connectResult.status.statusCode) {
StatusCode.STATUS_SUCCESS -> {
//The connection was accepted.
//We can start exchanging data.
}
StatusCode.STATUS_CONNECT_REJECTED -> {
//The connection was rejected.
//Notify the user.
}
else -> {
//This shouldn't happen...
}
}
}
}
Nearby.getDiscoveryEngine(context)
//"NAME" should be something descriptive (what is this device?)
//"SERVICE_ID" should be your app's package name
.startBroadcasting("NAME", "SERVICE_ID", connectionCallback, option)
.addOnSuccessListener {
//Broadcasting successfully started
}
.addOnFailureListener {
//Broadcasting failed to start
}
}
//This device is no longer accepting connections
fun stopBroadcasting() {
Nearby.getDiscoveryEngine(context)
.stopBroadcasting()
}
Next, a device needs to scan for potential connections. The following code shows how to do that.
Code:
//Start scanning for available connections.
fun startScanning() {
val policy = Policy.POLICY_STAR
val option = ScanOption.Builder()
.setPolicy(policy)
.build()
val scanEndpointCallback = object : ScanEndpointCallback() {
override fun onFound(endpointId: String?, endpointInfo: ScanEndpointInfo?) {
//A device has been found. Use this opportunity to either automatically
//connect to it, or add it to a list for the user to select from.
}
override fun onLost(endpointId: String?) {
//A device has gone out of range, been turned off, etc.
//It's no longer available to connect to, so if you're
//presenting a list, make sure to remove it.
}
}
Nearby.getDiscoveryEngine(context)
.startScan("SERVICE_ID", scanEndpointCallback, option)
.addOnSuccessListener {
//Scanning started successfully
}
.addOnFailureListener {
//Scanning couldn't start
}
}
//Stop scanning for new devices
fun stopScanning() {
Nearby.getDiscoveryEngine(context)
.stopScan()
}
Finally, once the scan is complete and the user (or your code) has selected a device, you'll need to initiate the connection:
Code:
//Connect to a device. This should be called
//from the scanner.
//endpointId comes from the onFound() method of
//the ScanEndpointCallback
fun startConnection(endpointId: String) {
val dataCallback = object : DataCallback() {
override fun onReceived(endpointId: String, data: Data) {
//We'll get to the implementation of this later.
}
override fun onTransferUpdate(endpointId: String, update: TransferStateUpdate) {
}
}
val connectionCallback = object : ConnectCallback() {
override fun onDisconnected(endpointId: String) {
//The connection has been terminated
}
override fun onEstablish(endpointId: String, connectInfo: ConnectInfo) {
//A connection has been established.
//This is where you either accept or reject the connection.
//Both parties need to accept the connection. You can either
//present a confirmation to the user or silently accept.
//Similarly to Bluetooth pairing, you can display an auth code
//on both devices. Obtain the auth code:
val authCode = connectInfo.authCode
//Silently accept:
Nearby.getDiscoveryEngine(context)
.acceptConnect(endpointId, dataCallback)
}
override fun onResult(endpointId: String, connectResult: ConnectResult) {
//Handle the result of a connection request.
when (connectResult.status.statusCode) {
StatusCode.STATUS_SUCCESS -> {
//The connection was accepted.
//We can start exchanging data.
}
StatusCode.STATUS_CONNECT_REJECTED -> {
//The connection was rejected.
//Notify the user.
}
else -> {
//This shouldn't happen...
}
}
}
}
Nearby.getDiscoveryEngine(context)
.requestConnect("NAME", endpointId, connectionCallback)
.addOnSuccessListener {
//Request was sent successfully
}
.addOnFailureListener {
//Request failed to send
}
}
//End a connection
fun stopConnection(endpointId: String) {
Nearby.getDiscoveryEngine(context)
.disconnect(endpointId)
}
Finally, once everything is connected, it's time to start transfering data. There are currently three forms of data transfer: bytes, files, and streams.
Bytes
Nearby Connection allows you to send small packets of data in the form of byte arrays. This could be useful for if you only need to send some simple data, like a chat message, or a game-state update. The size limit is 32KB.
To send a byte array, use the following code:
Code:
//Send some data to a client in the form
//of a byte array.
fun sendByteData(endpointId: String, data: ByteArray) {
Nearby.getTransferEngine(context)
.sendData(endpointId, Data.fromBytes(data))
}
Files
If you have a file you want to send (e.g. a video or music file), use this method.
Sending a file is similar to sending a byte array:
Code:
//Send some data to a client in the form
//of a File.
//Files received with this method are stored
//in the receiving device's Download folder.
fun sendFileData(endpointId: String, file: File) {
try {
Nearby.getTransferEngine(context)
.sendData(endpointId, Data.fromFile(file))
} catch (e: FileNotFoundException) {
//Handle accordingly
}
}
//Send some data to a client in the form
//of a File using ParcelFileDescriptor
//Files received with this method are stored
//in the receiving device's Download folder.
fun sendFileData(endpointId: String, file: ParcelFileDescriptor) {
try {
Nearby.getTransferEngine(context)
.sendData(endpointId, Data.fromFile(file))
} catch (e: FileNotFoundException) {
//Handle accordingly
}
}
Streams
If it's easier for you to send your data in the form of a stream,
you can also do that.
Yet again, the process is very similar to the previous two methods.
Code:
//Send some data to a client in the form
//of a stream.
fun sendStreamData(endpointId: String, stream: InputStream) {
Nearby.getTransferEngine(context)
.sendData(endpointId, Data.fromStream(stream))
}
//Send soe data to a client in the form
//of a stream using ParcelFileDescriptor.
fun sendStreamData(endpointId: String, stream: ParcelFileDescriptor) {
Nearby.getTransferEngine(context)
.sendData(endpointId, Data.fromStream(stream))
}
_______
If you want to cancel a transfer, it's also fairly simple:
Code:
//Cancel the transmission of data.
//The dataId can be obtained from the
//Data instance being sent.
fun cancelTransmission(dataId: Long) {
Nearby.getTransferEngine(context)
.cancelDataTransfer(dataId)
}
Now that you know how to send data, it's time to go over receiving it. In the code examples above for broadcasting and connecting, there's an unimplemented DataCallback. Well, it's time to implement it. Below is an example of how you might do that.
Code:
//An example implementation of a DataCallback
class DataReceiver : DataCallback() {
//A method to hold received data until we can
//properly retrieve files and streams.
private val receivedData = HashMap<Long, Data>()
override fun onReceived(endpointId: String, data: Data) {
//There's some new data.
when (data.type) {
Data.Type.BYTES -> {
//The data received is in the byte array format.
//Retrieve it as such, and handle accordingly.
//This is the only format where it's safe to retrieve the data here.
val bytes = data.asBytes()
//However, in this implementation, we're going to temporarily store
//the data reference and retrieve it the same way as we do files
//and streams.
receivedData[data.id] = data
}
Data.Type.FILE, Data.Type.STREAM -> {
//Temporarily store the data reference until the transfer is complete.
receivedData[data.id] = data
}
}
}
override fun onTransferUpdate(endpointId: String, update: TransferStateUpdate) {
when (update.status) {
TransferStateUpdate.Status.TRANSFER_STATE_SUCCESS -> {
//The transfer is complete. Retrieve data and handle it.
val data = receivedData[update.dataId]
when (data?.type) {
Data.Type.BYTES -> {
val bytes = data.asBytes()
}
Data.Type.FILE -> {
val file = data.asFile()
}
Data.Type.STREAM -> {
val stream = data.asStream()
}
}
}
TransferStateUpdate.Status.TRANSFER_STATE_CANCELED -> {
//The transfer was canceled
}
TransferStateUpdate.Status.TRANSFER_STATE_FAILURE -> {
//The transfer failed
}
TransferStateUpdate.Status.TRANSFER_STATE_IN_PROGRESS -> {
//The transfer is still in progress. You can use this event to display and
//update a progress indicator.
val progressPercent = (update.bytesTransferred.toFloat() / update.totalBytes.toFloat() * 100).toInt()
}
}
}
}
Nearby Message
Next up is the Nearby Message API. This is a way to publish and subscribe to messages generated by other devices or by dedicated "beacons."
The first thing to do is declare permissions. In order to properly use this API, the following permissions must be requested and granted:
XML:
<uses-permission android:name="android.permission.INTERNET " />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Now, it's time to actually use it. First let's talk about publishing messages. Publishing and unpublishing is fairly simple:
Code:
//Publish a message for other devices to see.
//You can publish up to 50 messages within a
//5 second timespan.
//You can also specify a type string and/or
//a namespace string.
fun publishMessage(msg: String) {
Nearby.getMessageEngine(context)
.put(Message(msg.toByteArray()))
}
//Unpublish a message. The Message class
//takes care of checking message equality
//for you, so you only need to save the
//string itself.
//Make sure to call this in your component's
//onDestroy() method.
fun unpublishMessage(msg: String) {
Nearby.getMessageEngine(context)
.unput(Message(msg.toByteArray()))
}
Subscribing to messages is a bit more complex (obviously). There are two ways to subscribe: in the foreground, and in the background.
The following code is an example of how to subscribe in the foreground:
Code:
val handler = object : MessageHandler() {
override fun onFound(message: Message) {
//A new Message has been found.
}
override fun onLost(message: Message) {
//An existing message was lost.
}
//The following methods can be used to estimate the distance from a beacon
//or publisher, based on signal strength.
override fun onBleSignalChanged(message: Message, bleSignal: BleSignal) {
val strength = bleSignal.rssi
}
override fun onDistanceChanged(message: Message, distance: Distance) {
val dist = distance.meters
val precision = distance.precision
}
}
//Subscribe to new messages in the foreground.
//This should be run in a foreground Service or an Activity.
fun subscribeToMessagesForeground() {
val options = GetOption.Builder()
.setPicker(MessagePicker.Builder()
//MessagePicker.Builder has various options
//for filtering messages.
.build())
.setPolicy(com.huawei.hms.nearby.message.Policy.Builder()
//Policy.Builder has various options
//for filtering messages.
.build())
.build()
Nearby.getMessageEngine(context)
.get(handler, options)
}
Here's an example for subscribing in the background:
Code:
val intent = PendingIntent.getService(context, 0, Intent(context, MessageReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
//Subscribe to new messages in the background.
//To receive these messages, you'll need to set up
//an IntentService to handle a PendingIntent.
fun subscribeToMessagesBackground() {
val options = GetOption.Builder()
.setPicker(MessagePicker.Builder()
//MessagePicker.Builder has various options
//for filtering messages.
.build())
.setPolicy(com.huawei.hms.nearby.message.Policy.Builder()
//Policy.Builder has various options
//for filtering messages.
.build())
.build()
Nearby.getMessageEngine(context)
.get(intent, options)
}
//Usubscribe from new background messages.
//Make sure to call this on app exit.
fun unsubscribeFromMessagesBackground() {
Nearby.getMessageEngine(context)
.unget(intent)
}
class MessageReceiver : JobIntentService() {
override fun onHandleWork(intent: Intent) {
//Pass the data to the messaging API and let it
//call the appropriate callback methods.
Nearby.getMessageEngine(this)
.handleIntent(intent, object : MessageHandler() {
override fun onFound(message: Message) {
//A new Message has been found.
}
override fun onLost(message: Message) {
//An existing message was lost.
}
//The following methods can be used to estimate the distance from a beacon
//or publisher, based on signal strength.
override fun onBleSignalChanged(message: Message, bleSignal: BleSignal) {
val strength = bleSignal.rssi
}
override fun onDistanceChanged(message: Message, distance: Distance) {
val dist = distance.meters
val precision = distance.precision
}
})
}
}
Beacon Management
To set up beacons, please refer to Huawei's documentation.
Conclusion
And that's it! Be sure to check out Huawei's full documentation for more details.
Introduction
You can use App Messaging of AppGallery Connect to subscribe and send relevant messages to target active users of your app to encourage them to use key app functions. For example, you can send in-app messages to encourage users to subscribe certain products, provide tips on passing a game level or recommend activities of a restaurant.
App Messaging allows you to customize our messages look and the way they will be sent, and define events for triggering message sending to your users at the right moment.
AG Connect supports three types of messages as follows:
1. Pop-up message
2. Image message
3. Banner message
Development Overview
You need to install Unity software and I assume that you have prior knowledge about the unity and C#.
Hardware Requirements
A computer (desktop or laptop) running Windows 10.
A Huawei phone (with the USB cable), which is used for debugging.
Software Requirements
Java JDK installation package.
Unity software installed.
Visual Studio/Code installed.
HMS Core (APK) 4.X or later.
Follows the steps.
1. Create Unity Project.
Open unity Hub.
Click NEW, select 3D, Project Name and Location.
Click CREATE, as follows:
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
2. Click Asset Store, search Huawei HMS Core App Services and click Import, as follows.
3. Once import is successful, verify directory in Assets> Huawei HMS Core App Services path, as follows.
4. Choose Edit > Project Settings > Player and edit the required options in Publishing Settings, as follows.
5. Generate a SHA-256 certificate fingerprint.
To generating SHA-256 certificate fingerprint use below command.
Code:
keytool -list -v -keystore D:\Unity\projects_unity\file_name.keystore -alias alias_name
6. Download agconnect-services.json and copy and paste to Assets > Plugins > Android, as follows.
7. Choose Project Settings > Player and update package name.
8. Open LauncherTemplate.gradle and add below line.
Code:
apply plugin: 'com.huawei.agconnect'
implementation 'com.huawei.hms:hianalytics:5.1.0.301'
implementation 'com.huawei.agconnect:agconnect-core:1.4.2.301'
9. Open AndroidManifest file and add below permissions.
Code:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
10. Open "baseProjectTemplate.gradle" and add lines, as follows.
Code:
classpath'com.huawei.agconnect:agcp:1.4.1.300'
maven {url 'https://developer.huawei.com/repo/'}
11. Open "mainTemplate.gradle" and add lines, as follows.
Code:
implementation 'com.huawei.agconnect:agconnect-core:1.4.2.301'
implementation 'com.huawei.hms:hianalytics:5.1.0.301'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.huawei.agconnect:agconnect-appmessaging:1.4.1.300'
12. Enable debug mode use in cmd prompt [optional].
Code:
adb shell setprop debug.huawei.hms.analytics.app package_name
13. Configuring project in AGC
14. Create Scripts folder and create a class.
APPMessaging.cs
C#:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HuaweiService;
using HuaweiService.appmessage;
using HuaweiService.analytic;
using HuaweiService.push;
using Exception = HuaweiService.Exception;
public class APPMessaging : MonoBehaviour
{
private HiAnalyticsInstance instance;
private AGConnectAppMessaging appMessaging;
private void Start()
{
instance = HiAnalytics.getInstance(new Context());
appMessaging = AGConnectAppMessaging.getInstance();
appMessaging.setFetchMessageEnable(true);
appMessaging.setDisplayEnable(true);
instance.setAnalyticsEnabled(true);
getAAID();
}
private void getAAID(){
// Task result = instance.getAAID();
Task id = HmsInstanceId.getInstance(new Context()).getAAID();
id.addOnSuccessListener(new HmsSuccessListener<AAIDResult>((aaidResult) =>
{
string aaId = aaidResult.getId();
Debug.Log("AAID==>> "+aaId);
})).addOnFailureListener(new HmsFailureListener((e) =>
{
Debug.Log("AAID==>> Failed");
}));
}
public delegate void SuccessCallBack<T>(T o);
public class HmsSuccessListener<T>:OnSuccessListener{
public SuccessCallBack<T> CallBack;
public HmsSuccessListener(SuccessCallBack<T> c){
CallBack = c;
}
public void onSuccess(T arg0)
{
if(CallBack != null)
{
CallBack.Invoke(arg0);
}
}
public override void onSuccess(AndroidJavaObject arg0){
if(CallBack !=null)
{
Type type = typeof(T);
IHmsBase ret = (IHmsBase)Activator.CreateInstance(type);
ret.obj = arg0;
CallBack.Invoke((T)ret);
}
}
}
public delegate void SuccessCallBack(AndroidJavaObject object);
public delegate void FailureCallBack(Exception e);
public class HmsFailureListener:OnFailureListener{
public FailureCallBack CallBack;
public HmsFailureListener(FailureCallBack c){
CallBack = c;
}
public override void onFailure(Exception arg0){
if(CallBack !=null){
CallBack.Invoke(arg0);
}
}
}
}
AndroidManifest.xml
Code:
<?xml version="1.0" encoding="utf-8"?>
<!-- GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.player"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application>
<activity android:name="com.unity3d.player.UnityPlayerActivity"
android:theme="@style/UnityThemeSelector">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
</application>
</manifest>
15. Follow the steps, as shown in image:
a. Create empty GameObject.
b. Assign script APPMessaging(as per you script name) to empty GameObject.
16. To build apk and run in device, choose File > Build Settings > Build for apk or Build and Run for run on connected device.
Result
Sign in to AppGallery Connect and click My projects.
Find and click your project.
Navigate to Grow > App Messaging and click New.
Select Name and Description.
Click Test in Operation drop-down.
Enter text in Test user AAID and click Save.
Tips and Tricks
Always use the latest version of the library.
Add agconnect-services.json file without fail.
Add SHA-256 fingerprint without fail.
Make sure dependencies added in build files.
Make sure you have enable debug mode.
Make sure the image url is correct and aspect ratio for Portrait is 3:2 (300x200) and Landscape aspect ratio is 1:1 or 3:2 (100 x100 or 300x200).
Conclusion
In this article, we have learnt to integrate Huawei AppMessaging service into unity game development. AppMessaging provides services sending promotional messages and new updates and also we can target users to promote some products.
Thanks for reading the article, please do likes, comments and your queries or suggestions.
References
App Messaging - click here
Unity App Messaging Integration Manual - click here
Original source
Introduction
In this article, we can learn the integration of user address in apps by Huawei Identity Kit. The Identity Kit provides an easy interface to add or edit or delete user details and enables the users to grant permission for apps to access their addresses through a single click on the screen.
This kit is mainly used in e-commerce, food delivery and logistics apps to deliver the products in an easy and safe way to users.
Services
Address Management: Users can enter and edit address information.
Address Access: Increases productivity by allowing user to access address information with the required permission from users.
Address Selection: Users can select addresses quickly and reliably.
Advantages
Easy access: Only one interface to integrate address selection service.
Extensive coverage: More than 170 countries and regions are covered.
Privacy protection: Strict compliance with European General Data Protection Regulation (GDPR) regulations and compliant use of address data.
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 installed.
4. Minimum API Level 21 is required.
5. Required EMUI 9.0.0 and later version devices.
How to integrate HMS Dependencies
1. First register as Huawei developer and complete identity verification in Huawei developers website, refer to register a Huawei ID.
2. Create a project in android studio, refer Creating an Android Studio Project.
3. Generate a SHA-256 certificate fingerprint.
4. 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.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Note: Project Name depends on the user created name.
5. Create an App in AppGallery Connect.
6. Download the agconnect-services.json file from App information, copy and paste in android Project under app directory, as follows.
7. Enter SHA-256 certificate fingerprint and click tick icon, as follows.
Note: Above steps from Step 1 to 7 is common for all Huawei Kits.
8. Add the below maven URL in build.gradle(Project) file under the repositories of buildscript, dependencies and allprojects, refer Add Configuration.
Code:
maven { url 'http://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.4.1.300'
9. Add the below plugin and dependencies in build.gradle(Module) file.
Code:
apply plugin: 'com.huawei.agconnect'
// Huawei AGC
implementation 'com.huawei.agconnect:agconnect-core:1.5.0.300'
// Identity Kit.
implementation 'com.huawei.hms:identity:4.0.4.300'
10. Now Sync the gradle.
11. Add the required permission to the AndroidManifest.xml file.
Code:
<uses-permission android:name="android.permission.INTERNET"/>
Let us move to development
I have created a project on Android studio with empty activity let's start coding.
In the MainActivity.kt we can create the business logic.
Code:
class MainActivity : AppCompatActivity() {
private val GET_ADDRESS = 1000
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
query_user_address.setOnClickListener {
if(isNetworkAvailable([email protected])){
getUserAddress()
}
else {
Toast.makeText(this, "Please check your internet connection...", Toast.LENGTH_SHORT).show()
}
}
}
// To parse user address selection, returning the selected user address and displaying the selected user address in text view.
@SuppressLint("SetTextI18n")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
Toast.makeText(this, "onActivityResult requestCode $requestCode resultCode $resultCode", Toast.LENGTH_SHORT).show()
when (requestCode) {
GET_ADDRESS -> when(resultCode) {
RESULT_OK -> {
val userAddress = UserAddress.parseIntent(data)
if(userAddress != null){
val sb = StringBuilder()
sb.apply {
append("name: ${userAddress.name} ,")
append("city: ${userAddress.administrativeArea} ,")
append("area: ${userAddress.locality} ,")
append("address: ${userAddress.addressLine1} ${userAddress.addressLine2} ,")
append("phone: ${userAddress.phoneNumber} ,")
}
Toast.makeText(this, "user address is $sb", Toast.LENGTH_SHORT).show()
user_address.text = sb.toString()
}else {
user_address.text = "Failed to get user address."
}
}
RESULT_CANCELED -> {
}
else -> Toast.makeText(this, "Result is wrong, result code is $resultCode", Toast.LENGTH_SHORT).show()
}
else -> {
}
}
}
// To query the user addresses and open the user address selection page.
private fun getUserAddress() {
val req = UserAddressRequest()
val task = com.huawei.hms.identity.Address.getAddressClient(this).getUserAddress(req)
task.addOnSuccessListener { result ->
Toast.makeText(this, "onSuccess result code: ${result.returnCode}", Toast.LENGTH_SHORT).show()
try{
startActivityForResult(result)
}
catch (e: IntentSender.SendIntentException){
e.printStackTrace()
}
}.addOnFailureListener { e ->
Toast.makeText(this, "on Failed result code: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
private fun startActivityForResult(result: GetUserAddressResult){
val status = result.status
if(result.returnCode == 0 && status.hasResolution()){
Toast.makeText(this, "The result had resolution", Toast.LENGTH_SHORT).show()
status.startResolutionForResult(this, GET_ADDRESS)
}
else {
Toast.makeText(this, "the response is wrong, the return code is ${result.returnCode}", Toast.LENGTH_SHORT).show()
}
}
fun isNetworkAvailable(context: Context?): Boolean {
if(context != null){
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val aNetworkInfo = connectivityManager.activeNetworkInfo
aNetworkInfo?.let{
return aNetworkInfo.isAvailable
}
}
return false
}
}
In the activity_main.xml we can create the UI screen.
Code:
<?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"
android:padding="10dp"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="MissingConstraints"
android:orientation="vertical">
<TextView
android:id="@+id/user_address"
android:layout_width="match_parent"
android:layout_height="35dp"
android:hint="show shipping address"
android:textAllCaps="false"
android:textSize="15sp"
android:text="Show User Address"/>
<Button
android:id="@+id/query_user_address"
android:layout_width="match_parent"
android:layout_marginTop="10sp"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:textSize="15sp"
android:text="Get Huawei User Address"/>
<TextView
android:id="@+id/demo_introduce"
android:layout_width="match_parent"
android:textSize="15sp"
android:layout_height="320dp"
android:layout_marginLeft="0dp"
android:layout_marginTop="50dp"
android:layout_marginRight="0dp"
android:layout_marginBottom="0dp"
android:text="@string/demo_introduction" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Demo
Tips and Tricks
1. Make sure you are already registered as Huawei developer.
2. Set minSDK version to 21 or later.
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
6. The Identity Kit functions can be used only after signin with registered Huawei ID.
7. A maximum of 10 user addresses are allowed.
Conclusion
In this article, we have learnt integration of user address feature in apps by Huawei Identity Kit. It allows the user to login with Huawei ID and can access the easy interface to add or edit or delete user details. It helps to deliver the online booking products by e-commerce, food delivery and logistics apps in an easy and safe way to users.
I hope you have read this article. If you found it is helpful, please provide likes and comments.
Reference
Identity Kit
Original Source
How many addresses we can add to the device?
Basavaraj.navi said:
How many addresses we can add to the device?
Click to expand...
Click to collapse
max 10 address we can store in device
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
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 or directly from the AppGallery Push Kit Console.
AppGallery Connect
Find the Push Kit message service in AppGallery connect dashboard.
Choose My Projects > Grow > Push Kit, and click Enable now.
Follow the steps to send the notification message to device from AppGallery Connect, Sending a Notification Message.
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
1. First register as Huawei developer and complete identity verification in Huawei developers website, refer to register a Huawei ID.
2. Create a project in android studio, refer Creating an Android Studio Project.
3. Generate a SHA-256 certificate fingerprint.
4. 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.
Note: Project Name depends on the user created name.
5. Create an App in AppGallery Connect.
6. Download the agconnect-services.json file from App information, copy and paste in android Project under app directory, as follows.
7. Enter SHA-256 certificate fingerprint and click Save button, as follows.
Note: Above steps from Step 1 to 7 is common for all Huawei Kits.
8. Click Manage APIs tab and enable Push Kit.
9. Add the below maven URL in build.gradle(Project) file under the repositories of buildscript, dependencies and allprojects, refer Add Configuration.
Code:
maven { url 'http://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
10. Add the below plugin and dependencies in build.gradle(Module) file.
Java:
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'
11. Now Sync the gradle.
12. Add the required permission to the AndroidManifest.xml file.
XML:
// 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.
Java:
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.
Code:
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([email protected]).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:
<?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.
Code:
<?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
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
Push Kit – Training Video
Precise messaging is an important way for mobile apps to retain users and is usually achieved by segmenting users into different groups according to their preferences and then adopting different messaging policies for each user segment. However, if you want to push messages to users based on their precise locations, in-depth customization is usually required since most available third-party messaging services cannot narrow the target audience down to a specific business area or a small area. With geofences, this issue can be effectively resolved. A geofence is a set of virtual boundaries that define a given area on a map. When a user's device enters or leaves the geofence, or stays in the geofence for a specific amount of time, messages and notifications can be automatically sent to an app on the user's device. Geofence and messaging capabilities can work together to precisely send messages to target audiences in a specified area.
For example, suppose that a travel app wants to promote its ticket booking service in Paris. To do so, the app can create geofences for popular scenic spots in Paris. When a target user arrives at a scenic spot during a specified time range, the app will send a promotion message such as "You have received a coupon for the XXX. Tap here to claim the coupon." to the user, increasing their willingness to buy a ticket.
ImplementationYou can carry out precise messaging to specified target audiences by using the geofence capability HMS Core Location Kit in conjunction with the message pushing capability of HMS Core Push Kit. By creating a geofence for a specified area, the app can detect the user's status, for example, when they enter, leave, or stay in this area. Once the messaging condition is met, the app on the user's device will receive a push message in real time. The push message can be sent to and displayed on the user's device even when the app is not running in the background, achieving a delivery rate as high as 99%.
Demo1. Install the demo app on the test device.
2. Start the demo app, tap Add Geofence on the GeoFence screen, and set relevant parameters to create a geofence.
3. Wait for the geofence to be triggered.
4. Check the received message.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Development Procedure1. Configure the Maven repository address for the SDK.
(The procedure for configuring the Maven repository address in Android Studio is different for Gradle plugin versions earlier than 7.0, Gradle plugin 7.0, and Gradle plugin 7.1 or later versions. Here, the procedure for Gradle plugin 7.1 is used as an example.)
a) Go to buildscript > dependencies and add AppGallery Connect plugin configurations.
Code:
buildscript {
dependencies {
...
// Add the AppGallery Connect plugin configuration. You are advised to use the latest plugin version.
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
}
}
b) Open the project-level settings.gradle file and configure the Maven repository address for the SDK.
Code:
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
// Configure the Maven repository address for the SDK.
maven { url 'https://developer.huawei.com/repo/' }
}
}
dependencyResolutionManagement {
...
repositories {
google()
mavenCentral()
// Configure the Maven repository address for the SDK.
maven { url 'https://developer.huawei.com/repo/' }
}
}
2. Add build dependencies in the dependencies block.
Code:
// Configure the app-level build.gradle file.
dependencies {
implementation 'com.huawei.hms:location: 6.4.0.300'
implementation 'com.huawei.hms:push: 6.3.0.304'
}
3. Declare system permissions in the AndroidManifest.xml file.
The Location SDK incorporates the GNSS, Wi-Fi, and base station location functions into an app to build up precise global positioning capabilities. Therefore, it requires the network, precise location, and coarse location permissions to function correctly. If the app needs to continuously obtain user locations when running in the background, the ACCESS_BACKGROUND_LOCATION permission also needs to be declared in the AndroidManifest.xml file.
Code:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARES_LOCATION" />
Note: The ACCESS_FINE_LOCATION, WRITE_EXTERNAL_STORAGE, and READ_EXTERNAL_STORAGE permissions are dangerous system permissions, so they must be dynamically applied for. If the app does not have the permissions, Location Kit will be unable to provide services for the app.
Key CodeCode file: com.huawei.hmssample2.geofence\GeoFenceActivity.java
If you want to integrate the geofence service and implement message pushing in your app, you only need to add relevant code in GeoFenceActivity.java to your app project.
1. Configure geofences.
a) Create geofences and geofence groups as needed, and set relevant parameters, such as the geofence radius and triggering time.
Code:
if (checkStyle(geofences, data.uniqueId) == false) {
LocationLog.d("GeoFenceActivity", "not unique ID!");
LocationLog.i("GeoFenceActivity", "addGeofence failed!");
return;
}
geoBuild.setRoundArea(data.latitude, data.longitude, data.radius);
geoBuild.setUniqueId(data.uniqueId);
geoBuild.setConversions(data.conversions);
geoBuild.setValidContinueTime(data.validContinueTime);
geoBuild.setDwellDelayTime(data.dwellDelayTime);
geoBuild.setNotificationInterval(data.notificationInterval);
geofences.add(geoBuild.build());
LocationLog.i("GeoFenceActivity", "addGeofence success!");
b) Register a broadcast using the intent.
Code:
GeofenceRequest.Builder geofenceRequest = new GeofenceRequest.Builder();
geofenceRequest.createGeofenceList(GeoFenceData.returnList());
if (trigger.getText() != null) {
int trigGer = Integer.parseInt(trigger.getText().toString());
geofenceRequest.setInitConversions(trigGer);
LocationLog.d(TAG, "trigger is " + trigGer);
} else {
geofenceRequest.setInitConversions(5);
LocationLog.d(TAG, "default trigger is 5");
}
final PendingIntent pendingIntent = getPendingIntent();
try {
geofenceService.createGeofenceList(geofenceRequest.build(), pendingIntent)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(Task<Void> task) {
if (task.isSuccessful()) {
LocationLog.i(TAG, "add geofence success! ");
setList(pendingIntent, GeoFenceData.getRequestCode(), GeoFenceData.returnList());
GeoFenceData.createNewList();
} else {
// Get the status code for the error and log it using a user-friendly message.
LocationLog.w(TAG, "add geofence failed : " + task.getException().getMessage());
}
}
});
} catch (Exception e) {
LocationLog.i(TAG, "add geofence error:" + e.getMessage());
}
private PendingIntent getPendingIntent() {
Intent intent = new Intent(this, GeoFenceBroadcastReceiver.class);
intent.setAction(GeoFenceBroadcastReceiver.ACTION_PROCESS_LOCATION);
Log.d(TAG, "new request");
GeoFenceData.newRequest();
return PendingIntent.getBroadcast(this, GeoFenceData.getRequestCode(), intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
2. Trigger message pushing. Send a push message when onReceive of GeoFenceBroadcastReceiver detects that the geofence is triggered successfully. The message will be displayed in the notification panel on the device.
Code:
GeofenceData geofenceData = GeofenceData.getDataFromIntent(intent);
if (geofenceData != null) {
int errorCode = geofenceData.getErrorCode();
int conversion = geofenceData.getConversion();
ArrayList<Geofence> list = (ArrayList<Geofence>) geofenceData.getConvertingGeofenceList();
Location myLocation = geofenceData.getConvertingLocation();
boolean status = geofenceData.isSuccess();
sb.append("errorcode: " + errorCode + next);
sb.append("conversion: " + conversion + next);
if (list != null) {
for (int i = 0; i < list.size(); i++) {
sb.append("geoFence id :" + list.get(i).getUniqueId() + next);
}
}
if (myLocation != null) {
sb.append("location is :" + myLocation.getLongitude() + " " + myLocation.getLatitude() + next);
}
sb.append("is successful :" + status);
LocationLog.i(TAG, sb.toString());
Toast.makeText(context, "" + sb.toString(), Toast.LENGTH_LONG).show();
//
new PushSendUtils().netSendMsg(sb.toString());
}
Note: The geofence created using the sample code will trigger two callbacks for conversion types 1 and 4. One is triggered when a user enters the geofence and the other when a user stays in the geofence. If Trigger is set to 7 in the code, callbacks will be configured for all scenarios, including entering, staying, and leaving the geofence.
Once you have performed the preceding steps, you will have enabled geofence-based message pushing for your app and can send messages to target audiences in specific areas, achieving precise marketing.
ReferencesLocation Kit official website
Location Kit development documentation