Offline push
When the app isn't in the foreground, new messages and incoming calls reach users through system push. On Android, push is added with the im-push module, which supports FCM and the Huawei, Honor, Xiaomi, OPPO, vivo and Meizu vendor channels. For users' do-not-disturb settings, see Alerts and reports.
Before you start, configure push credentials for each channel of your app in the tenant console.
| You want to | See |
|---|---|
| Initialize push | Initialization |
| Add FCM (outside mainland China) | FCM |
| Add Chinese phone vendors' channels | Chinese vendors |
| Request notification permission, set up notification channels | Notification permission and channels |
| Open the right conversation when a notification is tapped | Registration and taps |
| Clear notifications, update the badge | Clearing notifications and badges |
Initialization
import com.deeprespond.im.*
import com.deeprespond.im.push.*
val options = DRClientOptions(
appKey = "org#app",
apiUrl = "https://api.example.com",
push = DRPushOptions(
credentials = mapOf(PushChannel.FCM to "控制台凭据编号"),
),
)- The values in
credentialsare credential IDs from the console, not vendor server secrets. - Initialize after the user agrees to your privacy policy; push registers in the main process.
- After initialization, get the push controller with
DRPush.of(im). It'snullwithout thepushoption or withoutim-push.
FCM
Your app sets up its own Firebase project, adds google-services.json and initializes Firebase. The SDK ships its own FCM receiving service, DRFirebaseMessagingService, so nothing else is needed.
If your app already has its own FCM service: an app can only have one active FCM receiving service, so remove the SDK's service in AndroidManifest.xml and keep yours:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<service android:name="com.deeprespond.im.push.DRFirebaseMessagingService"
tools:node="remove" />
<service android:name=".AppMessagingService" android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>Then hand messages over to the SDK from your service:
import com.deeprespond.im.push.DRPush
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
class AppMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(message: RemoteMessage) {
if (!DRPush.handleRemoteMessage(this, message)) {
// 处理 App 自己的其他推送。
}
}
override fun onNewToken(token: String) {
DRPush.handleNewToken(this, token)
}
override fun onDeletedMessages() {
DRPush.handleDeletedMessages(this)
}
}Newer Firebase versions have onRegistered / onUnregistered; forward them to handleRegistered(context, installationId) and handleUnregistered(context). Registering by Firebase installation ID (FID) also needs support on both the server and your app; without it the SDK still registers by push token. Changing only the client code doesn't mean FID registration works.
Chinese vendors
| Channel | Artifact | VendorPush constructor | App configuration |
|---|---|---|---|
| Huawei | im-push-huawei | HuaweiPush() | HMS / AGConnect configuration and the Huawei repository |
| Honor | im-push-honor | HonorPush() | com.hihonor.push.app_id in the manifest and the Honor repository |
| Xiaomi | im-push-xiaomi | XiaomiPush(appId, appKey) | Add the real vendor SDK to the app |
| OPPO | im-push-oppo | OppoPush(appKey, appSecret) | Add the real vendor SDK to the app |
| vivo | im-push-vivo | VivoPush() | com.vivo.push.app_id and com.vivo.push.api_key in the manifest, and the vivo repository |
| Meizu | im-push-meizu | MeizuPush(appId, appKey) | Vendor app configuration and the real dependency |
- These classes are in
com.deeprespond.im.push.<vendor>. Pass the instances toDRPushOptions.vendors, and give each channel's credential ID incredentials. - The constructor arguments are client-side configuration the vendor requires; they're not the server credentials in the console.
- The Maven repositories for Huawei, Honor and vivo are
https://developer.huawei.com/repo/,https://developer.hihonor.com/repoandhttps://repos.vivo.com.cn/maven/repository/external-lib/; you can restrict them by dependency group. - Download the Xiaomi and OPPO SDKs from the vendors as AARs and put them in your app's
libsor a private repository. Thevendor-stubsin the source are empty stand-ins for compiling only and can't be used at runtime.
The vendor modules currently pass compile and simulated tests. Real push channels, notification categories, taps, badges and battery saving still need to be verified on real devices with vendor accounts.
Notification permission and channels
Notification permission: from Android 13, your app requests the notification permission at a suitable moment; even then, users may get no alerts if they turn off a notification channel.
controller.permission()returnsGRANTED,DENIEDorNOT_DETERMINED.- Recommended approach: register
permissionContract()withregisterForActivityResultwhen the Activity is created, andlaunch(Unit)when the user taps; the result then survives screen rotation. You can also callrequestPermission(activity)from a coroutine. - If you requested the permission yourself, call
markPermissionRequested()to tell the SDK; after a denial,shouldShowRationale(activity)tells you whether you can ask again.
Notification channels:
DRPushOptions.androidChannelsdefaults to three channels:dr_message(messages),dr_call(calls) anddr_silent(silent). The channel configuration in the console must match the app.- Each channel (
AndroidChannel) hasid,name,kind,descriptionandsound;kindisMESSAGE,CALLorSILENT. DRPushOptionscan also setsmallIcon,accentColor,languageandbadge.- Once a channel exists, its sound and other settings belong to the user and the system; initializing again doesn't override the user's choices.
controller.channelStatesobserves whether each channel is on, andopenNotificationSettings(activity, channelId?)opens the system notification settings;isHibernationExempt/openHibernationSettingscheck and open the app hibernation settings.
Registration and taps
import com.deeprespond.im.push.DRPush
val controller = DRPush.of(im)
controller?.register()
val registration = controller?.currentRegistration()
val launch = controller?.takeLaunchOpen()
println(registration)
println(launch)Registration: register() registers this device with the server, and registrations subscribes to the registration results; setEnabled turns notifications on or off for this device, and language changes the notification language.
Notification taps:
- Call
DRPush.handleIntent(intent)inonCreateandonNewIntentof the launch Activity. Each tap is handled only once. - Taps while the app is running arrive through
opened. It supports only one collector, so collect it in one central place in your app. - When a tap launches the app, take that tap with
takeLaunchOpen()(it's gone after the first call). - Wait for the SDK to restore the sign-in, then navigate to the conversation according to
PushOpened:- When
forCurrentUserisfalse, the notification belongs to a different account that was signed in before; only open the home screen, and never jump into the current account's conversation. trustedbeingtruemeans the tap was on a notification the SDK itself created.- When the target is unknown or doesn't match, go back to the home screen.
- When
Clearing notifications and badges
clearDeliveredNotifications()clears displayed notifications (except incoming-call notifications).cancelNotification(tag)cancels your app's own notifications by tag; tags starting withdr:are reserved for the SDK.- Badges:
BadgeMode.AUTOupdates from the unread count; withMANUAL, callsetBadge(count)yourself, andcountnullclears it. Whether badges show depends on the phone's system. The badge is display only and doesn't change the read position on the server.
For the full native API, see DRPushController; for call-related permissions, see Audio and video calls.
