feat(push): 添加多厂商推送集成支持
- 实现了华为 HMS 推送服务集成 - 实现了小米推送服务集成 - 实现了 OPPO 推送服务集成 - 实现了 vivo 推送服务集成 - 实现了荣耀推送服务集成 - 实现了 FCM 推送服务集成 - 添加了统一的厂商推送接口和检测机制 - 添加了推送配置 API 和存储管理 - 添加了推送令牌管理和设备注册功能 - 添加了模拟器环境的推送测试用例
这个提交包含在:
父节点
19e7b27d6e
当前提交
ea693d5c66
@ -76,8 +76,11 @@ class PushSdkTest {
|
||||
val expected = when (manufacturer) {
|
||||
"HUAWEI" -> PushVendor.HUAWEI
|
||||
"XIAOMI" -> PushVendor.XIAOMI
|
||||
"REDMI", "POCO" -> PushVendor.XIAOMI
|
||||
"OPPO" -> PushVendor.OPPO
|
||||
"REALME", "ONEPLUS" -> PushVendor.OPPO
|
||||
"VIVO" -> PushVendor.VIVO
|
||||
"IQOO" -> PushVendor.VIVO
|
||||
"HONOR" -> PushVendor.HONOR
|
||||
else -> PushVendor.FCM
|
||||
}
|
||||
@ -102,11 +105,17 @@ class PushSdkTest {
|
||||
// 1. initializeVendors 不应抛出任何异常
|
||||
PushSDK.initializeVendors(appCtx)
|
||||
|
||||
// 2. 模拟器上 FCM token 通常为 null
|
||||
val reg = PushSDK.currentRegistration(appCtx)
|
||||
// 2. 模拟器上 FCM token 通常为 null;真机只校验厂商映射和流程不崩溃
|
||||
val vendor = PushSDK.detectVendor()
|
||||
// 不做 pushToken 非空断言(emulator 无 Firebase),仅验证 vendor 正确
|
||||
assertEquals("模拟器应检测到 FCM", PushVendor.FCM, vendor)
|
||||
val expected = when (Build.MANUFACTURER.uppercase()) {
|
||||
"HUAWEI" -> PushVendor.HUAWEI
|
||||
"XIAOMI", "REDMI", "POCO" -> PushVendor.XIAOMI
|
||||
"OPPO", "REALME", "ONEPLUS" -> PushVendor.OPPO
|
||||
"VIVO", "IQOO" -> PushVendor.VIVO
|
||||
"HONOR" -> PushVendor.HONOR
|
||||
else -> PushVendor.FCM
|
||||
}
|
||||
assertEquals("MANUFACTURER=${Build.MANUFACTURER} 应检测到 $expected", expected, vendor)
|
||||
|
||||
// 3. setReceivePush 调用不应崩溃
|
||||
PushSDK.setReceivePush(appCtx, USER_A, enabled = false)
|
||||
|
||||
@ -11,6 +11,9 @@ android {
|
||||
defaultConfig {
|
||||
minSdk = libs.versions.minSdk.get().toInt()
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
manifestPlaceholders["XUQM_VIVO_APP_ID"] = ""
|
||||
manifestPlaceholders["XUQM_VIVO_APP_KEY"] = ""
|
||||
manifestPlaceholders["XUQM_HONOR_APP_ID"] = ""
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
@ -25,14 +28,12 @@ android {
|
||||
|
||||
dependencies {
|
||||
api(project(":sdk-core"))
|
||||
api(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
|
||||
implementation(platform(libs.firebase.bom))
|
||||
implementation(libs.firebase.messaging)
|
||||
|
||||
// Optional vendor push SDKs — add the ones you need in your app module.
|
||||
// These are NOT declared here because they require proprietary Maven repos:
|
||||
// Huawei: com.huawei.hms:push (via Huawei Maven repo)
|
||||
// Xiaomi: com.xiaomi.mipush:mipush (via Xiaomi Maven repo)
|
||||
// OPPO: com.heytap.mcs:push (via OPPO Maven repo)
|
||||
// vivo: com.vivo.pushsdk:pushsdk (via vivo Maven repo)
|
||||
// Honor: com.hihonor.mcs:push (via Honor Maven repo)
|
||||
api("com.huawei.hms:push:6.12.0.300")
|
||||
api("com.hihonor.mcs:push:7.0.41.301")
|
||||
api("io.github.hebeiliang.mipush:Push:2.0.0")
|
||||
api("com.umeng.umsdk:oppo-push:3.0.0")
|
||||
api("com.umeng.umsdk:vivo-push:4.0.6.0")
|
||||
}
|
||||
|
||||
@ -9,6 +9,10 @@
|
||||
|
||||
# ── Firebase Messaging Service — Android resolves it by class name ────────────
|
||||
-keep class com.xuqm.sdk.push.fcm.XuqmFirebaseMessagingService { *; }
|
||||
-keep class com.xuqm.sdk.push.huawei.XuqmHuaweiPushService { *; }
|
||||
-keep class com.xuqm.sdk.push.honor.XuqmHonorPushService { *; }
|
||||
-keep class com.xuqm.sdk.push.vivo.XuqmVivoPushReceiver { *; }
|
||||
-keep class com.xuqm.sdk.push.xiaomi.XuqmXiaomiPushReceiver { *; }
|
||||
|
||||
# ── Vendor push services — instantiated via reflection inside PushSDK ─────────
|
||||
-keep class com.xuqm.sdk.push.vendor.** { *; }
|
||||
|
||||
20
sdk-push/libs/README.md
普通文件
20
sdk-push/libs/README.md
普通文件
@ -0,0 +1,20 @@
|
||||
Vendor push SDK binaries can live here when a vendor only provides a console
|
||||
download.
|
||||
|
||||
The Xuqm push SDK owns the vendor dependency graph. Huawei, Honor, Xiaomi,
|
||||
OPPO, and vivo dependencies are declared by `sdk-push/build.gradle.kts`, so app
|
||||
integrators do not need to add vendor SDKs themselves.
|
||||
|
||||
If a vendor SDK must be pinned to an official console-only binary for a tenant,
|
||||
put the vendor `*.aar` or `*.jar` here before publishing the Xuqm push SDK.
|
||||
Those binaries are picked up by `api(fileTree(...))`.
|
||||
|
||||
Xiaomi's official current AAR download page requires a Xiaomi Push console login
|
||||
session. If an app must use that exact official package, download it from the
|
||||
tenant's Xiaomi console and publish this SDK with the AAR in this directory.
|
||||
|
||||
`sdk-push` owns the Android manifest integration: permissions, push services,
|
||||
notification click activity, vendor receivers and metadata placeholders are
|
||||
declared in `src/main/AndroidManifest.xml`. Host apps should integrate the Xuqm
|
||||
push SDK; vendor credentials are loaded from the tenant platform when the vendor
|
||||
SDK supports runtime configuration.
|
||||
@ -1,6 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="com.coloros.mcs.permission.RECIEVE_MCS_MESSAGE" />
|
||||
<uses-permission android:name="com.heytap.mcs.permission.RECIEVE_MCS_MESSAGE" />
|
||||
<uses-permission android:name="com.hihonor.push.permission.READ_PUSH_NOTIFICATION_INFO" />
|
||||
<uses-permission android:name="${applicationId}.permission.MIPUSH_RECEIVE" />
|
||||
|
||||
<permission
|
||||
android:name="${applicationId}.permission.MIPUSH_RECEIVE"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<permission
|
||||
android:name="${applicationId}.hihonor.permission.PROCESS_PUSH_MSG"
|
||||
android:protectionLevel="signatureOrSystem" />
|
||||
|
||||
<queries>
|
||||
<package android:name="com.huawei.hwid" />
|
||||
<package android:name="com.huawei.hms" />
|
||||
<package android:name="com.huawei.android.pushagent" />
|
||||
<package android:name="com.hihonor.id" />
|
||||
<package android:name="com.hihonor.appmarket" />
|
||||
<intent>
|
||||
<action android:name="com.hihonor.push.action.BIND_PUSH_SERVICE" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application>
|
||||
<meta-data
|
||||
android:name="push_kit_auto_init_enabled"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="com.vivo.push.app_id"
|
||||
android:value="${XUQM_VIVO_APP_ID}" />
|
||||
<meta-data
|
||||
android:name="com.vivo.push.api_key"
|
||||
android:value="${XUQM_VIVO_APP_KEY}" />
|
||||
<meta-data
|
||||
android:name="com.hihonor.push.app_id"
|
||||
android:value="${XUQM_HONOR_APP_ID}" />
|
||||
<meta-data
|
||||
android:name="com.hihonor.push.sdk_version"
|
||||
android:value="7.0.41.301" />
|
||||
|
||||
<service
|
||||
android:name="com.xuqm.sdk.push.fcm.XuqmFirebaseMessagingService"
|
||||
android:exported="false">
|
||||
@ -8,5 +52,102 @@
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service
|
||||
android:name="com.xuqm.sdk.push.huawei.XuqmHuaweiPushService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.huawei.push.action.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service
|
||||
android:name="com.xuqm.sdk.push.honor.XuqmHonorPushService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.hihonor.push.action.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name="com.xiaomi.push.service.XMPushService"
|
||||
android:enabled="true"
|
||||
android:process=":pushservice" />
|
||||
<service
|
||||
android:name="com.xiaomi.push.service.XMJobService"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:process=":pushservice" />
|
||||
<service
|
||||
android:name="com.xiaomi.mipush.sdk.PushMessageHandler"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:permission="com.xiaomi.xmsf.permission.MIPUSH_RECEIVE" />
|
||||
<service
|
||||
android:name="com.xiaomi.mipush.sdk.MessageHandleService"
|
||||
android:enabled="true" />
|
||||
|
||||
<receiver
|
||||
android:name="com.xiaomi.push.service.receivers.PingReceiver"
|
||||
android:exported="false"
|
||||
android:process=":pushservice">
|
||||
<intent-filter>
|
||||
<action android:name="com.xiaomi.push.PING_TIMER" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name="com.xiaomi.mipush.sdk.NotificationClickedActivity"
|
||||
android:enabled="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar">
|
||||
<meta-data
|
||||
android:name="supportStyle"
|
||||
android:value="scene|voip" />
|
||||
</activity>
|
||||
|
||||
<receiver
|
||||
android:name="com.xuqm.sdk.push.xiaomi.XuqmXiaomiPushReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="com.xiaomi.mipush.ERROR" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name="com.heytap.msp.push.service.CompatibleDataMessageCallbackService"
|
||||
android:exported="true"
|
||||
android:permission="com.coloros.mcs.permission.SEND_MCS_MESSAGE">
|
||||
<intent-filter>
|
||||
<action android:name="com.coloros.mcs.action.RECEIVE_MCS_MESSAGE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service
|
||||
android:name="com.heytap.msp.push.service.DataMessageCallbackService"
|
||||
android:exported="true"
|
||||
android:permission="com.heytap.mcs.permission.SEND_PUSH_MESSAGE">
|
||||
<intent-filter>
|
||||
<action android:name="com.heytap.mcs.action.RECEIVE_MCS_MESSAGE" />
|
||||
<action android:name="com.heytap.msp.push.RECEIVE_MCS_MESSAGE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name="com.vivo.push.sdk.service.CommandClientService"
|
||||
android:exported="true" />
|
||||
<receiver
|
||||
android:name="com.xuqm.sdk.push.vivo.XuqmVivoPushReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.vivo.pushclient.action.RECEIVE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@ -9,8 +9,10 @@ import com.xuqm.sdk.XuqmSDK
|
||||
import com.xuqm.sdk.core.ServiceEndpointRegistry
|
||||
import com.xuqm.sdk.network.ApiClient
|
||||
import com.xuqm.sdk.push.api.PushApi
|
||||
import com.xuqm.sdk.push.api.PushConfigApi
|
||||
import com.xuqm.sdk.push.model.PushRegistrationSnapshot
|
||||
import com.xuqm.sdk.push.model.PushVendor
|
||||
import com.xuqm.sdk.push.model.PushVendorConfig
|
||||
import com.xuqm.sdk.push.storage.PushRegistrationStore
|
||||
import com.xuqm.sdk.push.vendor.FcmPushService
|
||||
import com.xuqm.sdk.push.vendor.HonorPushService
|
||||
@ -29,8 +31,11 @@ import java.util.concurrent.atomic.AtomicReference
|
||||
object PushSDK {
|
||||
|
||||
private val api: PushApi get() = ApiClient.create(PushApi::class.java, ServiceEndpointRegistry.pushBaseUrl)
|
||||
private val configApi: PushConfigApi get() = ApiClient.create(PushConfigApi::class.java, ServiceEndpointRegistry.controlBaseUrl)
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
private val registeredUserId = AtomicReference<String?>(null)
|
||||
@Volatile
|
||||
private var cachedVendorConfig: PushVendorConfig? = null
|
||||
|
||||
fun currentRegistration(context: Context): PushRegistrationSnapshot? {
|
||||
XuqmSDK.requireInit()
|
||||
@ -59,6 +64,14 @@ object PushSDK {
|
||||
registerDevice(context, userId)
|
||||
}
|
||||
|
||||
fun refreshNativePushToken(
|
||||
context: Context,
|
||||
vendor: PushVendor = detectVendor(),
|
||||
) {
|
||||
XuqmSDK.requireInit()
|
||||
ensureNativePushToken(context.applicationContext, vendor)
|
||||
}
|
||||
|
||||
fun unbindImUser(userId: String) {
|
||||
unregisterDevice(userId)
|
||||
}
|
||||
@ -77,6 +90,7 @@ object PushSDK {
|
||||
api.setReceivePush(
|
||||
appId = XuqmSDK.appKey,
|
||||
userId = resolvedUserId,
|
||||
deviceId = DeviceUtils.getDeviceId(context),
|
||||
enabled = enabled,
|
||||
)
|
||||
if (enabled) {
|
||||
@ -107,6 +121,11 @@ object PushSDK {
|
||||
userId = userId,
|
||||
vendor = vendor.name,
|
||||
token = pushToken,
|
||||
deviceId = DeviceUtils.getDeviceId(context),
|
||||
brand = Build.MANUFACTURER.orEmpty(),
|
||||
model = Build.MODEL.orEmpty(),
|
||||
osVersion = DeviceUtils.getOsVersion(),
|
||||
appVersion = appVersion(context),
|
||||
)
|
||||
registeredUserId.set(userId)
|
||||
store(context).updateLastUserId(userId)
|
||||
@ -126,7 +145,12 @@ object PushSDK {
|
||||
deviceId = DeviceUtils.getDeviceId(XuqmSDK.appContext),
|
||||
fallbackVendor = detectVendor(),
|
||||
)
|
||||
api.unregisterDevice(XuqmSDK.appKey, userId, reg?.vendor?.name ?: detectVendor().name)
|
||||
api.unregisterDevice(
|
||||
XuqmSDK.appKey,
|
||||
userId,
|
||||
reg?.vendor?.name ?: detectVendor().name,
|
||||
DeviceUtils.getDeviceId(XuqmSDK.appContext),
|
||||
)
|
||||
registeredUserId.compareAndSet(userId, null)
|
||||
store(XuqmSDK.appContext).updateLastUserId(null)
|
||||
}
|
||||
@ -146,8 +170,11 @@ object PushSDK {
|
||||
return when (Build.MANUFACTURER.uppercase()) {
|
||||
"HUAWEI" -> PushVendor.HUAWEI
|
||||
"XIAOMI" -> PushVendor.XIAOMI
|
||||
"REDMI", "POCO" -> PushVendor.XIAOMI
|
||||
"OPPO" -> PushVendor.OPPO
|
||||
"REALME", "ONEPLUS" -> PushVendor.OPPO
|
||||
"VIVO" -> PushVendor.VIVO
|
||||
"IQOO" -> PushVendor.VIVO
|
||||
"HONOR" -> PushVendor.HONOR
|
||||
else -> PushVendor.FCM
|
||||
}
|
||||
@ -156,14 +183,17 @@ object PushSDK {
|
||||
fun initializeVendors(context: Context) {
|
||||
val detectedVendor = detectVendor()
|
||||
Log.i("XuqmPushSDK", "Detected push vendor: ${detectedVendor.name}")
|
||||
vendorServices.forEach { service ->
|
||||
if (service.vendor == detectedVendor && service.isAvailable(context)) {
|
||||
Log.i("XuqmPushSDK", "Initializing push vendor: ${service.vendor.name}")
|
||||
service.register(context)
|
||||
scope.launch {
|
||||
val config = loadVendorConfig()
|
||||
vendorServices.forEach { service ->
|
||||
if (service.vendor == detectedVendor && service.isAvailable(context)) {
|
||||
Log.i("XuqmPushSDK", "Initializing push vendor: ${service.vendor.name}")
|
||||
service.register(context, config)
|
||||
}
|
||||
}
|
||||
if (detectedVendor == PushVendor.FCM) {
|
||||
ensureNativePushToken(context)
|
||||
}
|
||||
}
|
||||
if (detectedVendor == PushVendor.FCM) {
|
||||
ensureNativePushToken(context)
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,7 +219,13 @@ object PushSDK {
|
||||
)?.receivePush ?: true
|
||||
|
||||
private fun ensureNativePushToken(context: Context) {
|
||||
val vendor = detectVendor()
|
||||
ensureNativePushToken(context, detectVendor())
|
||||
}
|
||||
|
||||
private fun ensureNativePushToken(
|
||||
context: Context,
|
||||
vendor: PushVendor,
|
||||
) {
|
||||
val registration = currentRegistration(context)
|
||||
if (registration?.pushToken?.isNotBlank() == true) return
|
||||
|
||||
@ -216,7 +252,9 @@ object PushSDK {
|
||||
} else {
|
||||
val service = vendorServices.firstOrNull { it.vendor == vendor }
|
||||
if (service != null && service.isAvailable(context)) {
|
||||
service.register(context)
|
||||
scope.launch {
|
||||
service.register(context, loadVendorConfig())
|
||||
}
|
||||
} else {
|
||||
Log.w(
|
||||
"XuqmPushSDK",
|
||||
@ -244,4 +282,37 @@ object PushSDK {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun appVersion(context: Context): String? =
|
||||
runCatching {
|
||||
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
"${packageInfo.versionName ?: ""}(${packageInfo.longVersionCode})"
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
"${packageInfo.versionName ?: ""}(${packageInfo.versionCode})"
|
||||
}
|
||||
}.getOrNull()
|
||||
|
||||
private suspend fun loadVendorConfig(): PushVendorConfig {
|
||||
cachedVendorConfig?.let { return it }
|
||||
val loaded = runCatching {
|
||||
val pushConfig = configApi.sdkConfig(XuqmSDK.appKey).data?.pushConfig
|
||||
PushVendorConfig(
|
||||
huaweiAppId = pushConfig?.getAsJsonObject("huawei")?.get("appId")?.asString.orEmpty(),
|
||||
xiaomiAppId = pushConfig?.getAsJsonObject("xiaomi")?.get("appId")?.asString.orEmpty(),
|
||||
xiaomiAppKey = pushConfig?.getAsJsonObject("xiaomi")?.get("appKey")?.asString.orEmpty(),
|
||||
oppoAppKey = pushConfig?.getAsJsonObject("oppo")?.get("appKey")?.asString.orEmpty(),
|
||||
oppoAppSecret = pushConfig?.getAsJsonObject("oppo")?.get("masterSecret")?.asString.orEmpty(),
|
||||
vivoAppId = pushConfig?.getAsJsonObject("vivo")?.get("appId")?.asString.orEmpty(),
|
||||
vivoAppKey = pushConfig?.getAsJsonObject("vivo")?.get("appKey")?.asString.orEmpty(),
|
||||
honorAppId = pushConfig?.getAsJsonObject("honor")?.get("appId")?.asString.orEmpty(),
|
||||
)
|
||||
}.getOrElse { error ->
|
||||
Log.w("XuqmPushSDK", "Unable to load tenant push config: ${error.message}")
|
||||
PushVendorConfig()
|
||||
}
|
||||
cachedVendorConfig = loaded
|
||||
return loaded
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,12 @@ interface PushApi {
|
||||
@Query("userId") userId: String,
|
||||
@Query("vendor") vendor: String,
|
||||
@Query("token") token: String,
|
||||
@Query("deviceId") deviceId: String,
|
||||
@Query("brand") brand: String,
|
||||
@Query("model") model: String,
|
||||
@Query("osVersion") osVersion: String,
|
||||
@Query("platform") platform: String = "ANDROID",
|
||||
@Query("appVersion") appVersion: String? = null,
|
||||
)
|
||||
|
||||
@DELETE("api/push/unregister")
|
||||
@ -19,12 +25,14 @@ interface PushApi {
|
||||
@Query("appId") appId: String,
|
||||
@Query("userId") userId: String,
|
||||
@Query("vendor") vendor: String,
|
||||
@Query("deviceId") deviceId: String? = null,
|
||||
)
|
||||
|
||||
@POST("api/push/receive-push")
|
||||
suspend fun setReceivePush(
|
||||
@Query("appId") appId: String,
|
||||
@Query("userId") userId: String,
|
||||
@Query("deviceId") deviceId: String? = null,
|
||||
@Query("enabled") enabled: Boolean,
|
||||
)
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
package com.xuqm.sdk.push.api
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface PushConfigApi {
|
||||
@GET("api/sdk/config")
|
||||
suspend fun sdkConfig(
|
||||
@Query("appId") appId: String,
|
||||
@Query("platform") platform: String = "ANDROID",
|
||||
): SdkConfigResponse
|
||||
}
|
||||
|
||||
data class SdkConfigResponse(
|
||||
val data: SdkConfigData? = null,
|
||||
)
|
||||
|
||||
data class SdkConfigData(
|
||||
val pushConfig: JsonObject? = null,
|
||||
)
|
||||
@ -0,0 +1,23 @@
|
||||
package com.xuqm.sdk.push.honor
|
||||
|
||||
import android.util.Log
|
||||
import com.hihonor.push.sdk.HonorMessageService
|
||||
import com.xuqm.sdk.push.PushSDK
|
||||
import com.xuqm.sdk.push.model.PushVendor
|
||||
|
||||
class XuqmHonorPushService : HonorMessageService() {
|
||||
|
||||
override fun onNewToken(token: String?) {
|
||||
super.onNewToken(token)
|
||||
if (token.isNullOrBlank()) return
|
||||
runCatching {
|
||||
PushSDK.updateNativePushToken(applicationContext, PushVendor.HONOR, token)
|
||||
}.onFailure { error ->
|
||||
Log.w(TAG, "Unable to persist Honor push token: ${error.message}")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "XuqmHonorPush"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package com.xuqm.sdk.push.huawei
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import com.huawei.hms.push.HmsMessageService
|
||||
import com.huawei.hms.push.RemoteMessage
|
||||
import com.xuqm.sdk.push.PushSDK
|
||||
import com.xuqm.sdk.push.model.PushVendor
|
||||
|
||||
class XuqmHuaweiPushService : HmsMessageService() {
|
||||
|
||||
override fun onNewToken(token: String?) {
|
||||
super.onNewToken(token)
|
||||
persistToken(token)
|
||||
}
|
||||
|
||||
override fun onNewToken(token: String?, bundle: Bundle?) {
|
||||
super.onNewToken(token, bundle)
|
||||
persistToken(token)
|
||||
}
|
||||
|
||||
override fun onMessageReceived(message: RemoteMessage?) {
|
||||
super.onMessageReceived(message)
|
||||
Log.d(TAG, "Huawei push message received")
|
||||
}
|
||||
|
||||
private fun persistToken(token: String?) {
|
||||
if (token.isNullOrBlank()) return
|
||||
runCatching {
|
||||
PushSDK.updateNativePushToken(applicationContext, PushVendor.HUAWEI, token)
|
||||
}.onFailure { error ->
|
||||
Log.w(TAG, "Unable to persist Huawei push token: ${error.message}")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "XuqmHuaweiPush"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.xuqm.sdk.push.model
|
||||
|
||||
data class PushVendorConfig(
|
||||
val huaweiAppId: String = "",
|
||||
val xiaomiAppId: String = "",
|
||||
val xiaomiAppKey: String = "",
|
||||
val oppoAppKey: String = "",
|
||||
val oppoAppSecret: String = "",
|
||||
val vivoAppId: String = "",
|
||||
val vivoAppKey: String = "",
|
||||
val honorAppId: String = "",
|
||||
)
|
||||
@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.util.Log
|
||||
import com.xuqm.sdk.push.PushSDK
|
||||
import com.xuqm.sdk.push.model.PushVendor
|
||||
import com.xuqm.sdk.push.model.PushVendorConfig
|
||||
|
||||
/**
|
||||
* FCM (Firebase Cloud Messaging) push integration.
|
||||
@ -23,7 +24,7 @@ class FcmPushService : PushVendorInterface {
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
|
||||
override fun register(context: Context) {
|
||||
override fun register(context: Context, config: PushVendorConfig) {
|
||||
runCatching {
|
||||
val fcmClass = Class.forName("com.google.firebase.messaging.FirebaseMessaging")
|
||||
val instance = fcmClass.getMethod("getInstance").invoke(null)
|
||||
|
||||
@ -4,6 +4,8 @@ import android.content.Context
|
||||
import android.util.Log
|
||||
import com.xuqm.sdk.push.PushSDK
|
||||
import com.xuqm.sdk.push.model.PushVendor
|
||||
import com.xuqm.sdk.push.model.PushVendorConfig
|
||||
import java.lang.reflect.Proxy
|
||||
|
||||
/**
|
||||
* 荣耀推送集成框架
|
||||
@ -31,13 +33,21 @@ class HonorPushService : PushVendorInterface {
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
|
||||
override fun register(context: Context) {
|
||||
override fun register(context: Context, config: PushVendorConfig) {
|
||||
runCatching {
|
||||
val honorPushClass = Class.forName("com.hihonor.push.sdk.HonorPushClient")
|
||||
val instance = honorPushClass.getMethod("getInstance").invoke(null)
|
||||
// HonorPushClient.getInstance().init(context, true)
|
||||
val supported = runCatching {
|
||||
honorPushClass.getMethod("checkSupportHonorPush", Context::class.java)
|
||||
.invoke(instance, context) as? Boolean
|
||||
}.getOrDefault(true)
|
||||
if (supported != true) {
|
||||
Log.w(TAG, "Honor push is not supported on this device")
|
||||
return
|
||||
}
|
||||
honorPushClass.getMethod("init", Context::class.java, Boolean::class.javaPrimitiveType)
|
||||
.invoke(instance, context, true)
|
||||
.invoke(instance, context, false)
|
||||
requestToken(context, honorPushClass, instance)
|
||||
Log.i(TAG, "Honor push registration requested")
|
||||
}.onFailure { error ->
|
||||
Log.w(TAG, "Honor push registration failed: ${error.message}")
|
||||
@ -58,4 +68,36 @@ class HonorPushService : PushVendorInterface {
|
||||
companion object {
|
||||
private const val TAG = "HonorPushService"
|
||||
}
|
||||
|
||||
private fun requestToken(
|
||||
context: Context,
|
||||
honorPushClass: Class<*>,
|
||||
instance: Any,
|
||||
) {
|
||||
runCatching {
|
||||
val callbackClass = Class.forName("com.hihonor.push.sdk.HonorPushCallback")
|
||||
val callback = Proxy.newProxyInstance(
|
||||
callbackClass.classLoader,
|
||||
arrayOf(callbackClass),
|
||||
) { _, method, args ->
|
||||
when (method.name) {
|
||||
"onSuccess" -> {
|
||||
val token = args?.firstOrNull() as? String
|
||||
if (!token.isNullOrBlank()) {
|
||||
PushSDK.updateNativePushToken(context, vendor, token)
|
||||
Log.i(TAG, "Honor push token acquired")
|
||||
}
|
||||
}
|
||||
"onFailure" -> {
|
||||
Log.w(TAG, "Honor push token failed: ${args?.joinToString()}")
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
honorPushClass.getMethod("getPushToken", callbackClass)
|
||||
.invoke(instance, callback)
|
||||
}.onFailure { error ->
|
||||
Log.w(TAG, "Honor push token request failed: ${error.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,9 +4,10 @@ import android.content.Context
|
||||
import android.util.Log
|
||||
import com.xuqm.sdk.push.PushSDK
|
||||
import com.xuqm.sdk.push.model.PushVendor
|
||||
import com.xuqm.sdk.push.model.PushVendorConfig
|
||||
|
||||
/**
|
||||
* 华为推送集成框架
|
||||
* Android 华为 HMS 推送集成框架。
|
||||
*
|
||||
* 需要添加 HMS Push SDK 依赖:
|
||||
* ```groovy
|
||||
@ -36,12 +37,12 @@ class HuaweiPushService : PushVendorInterface {
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
|
||||
override fun register(context: Context) {
|
||||
override fun register(context: Context, config: PushVendorConfig) {
|
||||
runCatching {
|
||||
val hmsClass = Class.forName("com.huawei.hms.aaid.HmsInstanceId")
|
||||
val instance = hmsClass.getMethod("getInstance", Context::class.java)
|
||||
.invoke(null, context)
|
||||
val appId = getAppId(context)
|
||||
val appId = config.huaweiAppId.ifBlank { getAppId(context) }
|
||||
if (appId.isNotBlank()) {
|
||||
val token = hmsClass.getMethod(
|
||||
"getToken",
|
||||
|
||||
@ -6,21 +6,11 @@ import android.os.Bundle
|
||||
import android.util.Log
|
||||
import com.xuqm.sdk.push.PushSDK
|
||||
import com.xuqm.sdk.push.model.PushVendor
|
||||
import com.xuqm.sdk.push.model.PushVendorConfig
|
||||
import java.lang.reflect.Proxy
|
||||
|
||||
/**
|
||||
* OPPO 推送集成框架
|
||||
*
|
||||
* 需要添加 OPPO Push SDK 依赖:
|
||||
* ```groovy
|
||||
* implementation 'com.heytap.mcs:push:3.x.x'
|
||||
* ```
|
||||
*
|
||||
* 在 `AndroidManifest.xml` 的 `<application>` 下配置:
|
||||
* ```xml
|
||||
* <meta-data android:name="XUQM_OPPO_APP_KEY" android:value="xxxxxxxx" />
|
||||
* <meta-data android:name="XUQM_OPPO_APP_SECRET" android:value="xxxxxxxx" />
|
||||
* ```
|
||||
* OPPO 推送集成。OPPO Push 依赖由 sdk-push 自身声明,注册参数优先来自租户 PUSH 配置。
|
||||
*/
|
||||
class OppoPushService : PushVendorInterface {
|
||||
|
||||
@ -28,49 +18,26 @@ class OppoPushService : PushVendorInterface {
|
||||
|
||||
override fun isAvailable(context: Context): Boolean {
|
||||
return runCatching {
|
||||
Class.forName("com.heytap.mcssdk.PushManager")
|
||||
resolvePushManagerClass()
|
||||
true
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
|
||||
override fun register(context: Context) {
|
||||
override fun register(context: Context, config: PushVendorConfig) {
|
||||
runCatching {
|
||||
val pushManagerClass = Class.forName("com.heytap.mcssdk.PushManager")
|
||||
val instance = pushManagerClass.getMethod("getInstance").invoke(null)
|
||||
|
||||
val meta = context.packageManager.getApplicationInfo(
|
||||
context.packageName, PackageManager.GET_META_DATA
|
||||
).metaData ?: Bundle.EMPTY
|
||||
val appKey = meta.getString("XUQM_OPPO_APP_KEY", "")
|
||||
val appSecret = meta.getString("XUQM_OPPO_APP_SECRET", "")
|
||||
val appKey = config.oppoAppKey.ifBlank { meta.getString("XUQM_OPPO_APP_KEY", "") }
|
||||
val appSecret = config.oppoAppSecret.ifBlank { meta.getString("XUQM_OPPO_APP_SECRET", "") }
|
||||
|
||||
if (appKey.isNotBlank() && appSecret.isNotBlank()) {
|
||||
val callbackClass = Class.forName("com.heytap.mcssdk.callback.PushCallback")
|
||||
val proxy = Proxy.newProxyInstance(
|
||||
callbackClass.classLoader,
|
||||
arrayOf(callbackClass)
|
||||
) { _, method, args ->
|
||||
when (method.name) {
|
||||
"onRegister" -> {
|
||||
val regId = args?.getOrNull(1) as? String
|
||||
if (!regId.isNullOrBlank()) {
|
||||
PushSDK.updateNativePushToken(context, vendor, regId)
|
||||
Log.i(TAG, "OPPO push token acquired")
|
||||
}
|
||||
}
|
||||
}
|
||||
null
|
||||
if (!registerWithHeytapMsp(context, appKey, appSecret)) {
|
||||
registerWithLegacyMcs(context, appKey, appSecret)
|
||||
}
|
||||
pushManagerClass.getMethod(
|
||||
"register",
|
||||
Context::class.java,
|
||||
String::class.java,
|
||||
String::class.java,
|
||||
callbackClass,
|
||||
).invoke(instance, context, appKey, appSecret, proxy)
|
||||
Log.i(TAG, "OPPO push registration requested")
|
||||
} else {
|
||||
Log.w(TAG, "OPPO appKey/appSecret not configured in meta-data, skipping registration")
|
||||
Log.w(TAG, "OPPO appKey/appSecret not configured, skipping registration")
|
||||
}
|
||||
}.onFailure { error ->
|
||||
Log.w(TAG, "OPPO push registration failed: ${error.message}")
|
||||
@ -79,10 +46,13 @@ class OppoPushService : PushVendorInterface {
|
||||
|
||||
override fun unregister(context: Context) {
|
||||
runCatching {
|
||||
val pushManagerClass = Class.forName("com.heytap.mcssdk.PushManager")
|
||||
val instance = pushManagerClass.getMethod("getInstance").invoke(null)
|
||||
pushManagerClass.getMethod("unRegister", Context::class.java)
|
||||
.invoke(instance, context)
|
||||
val pushManagerClass = resolvePushManagerClass()
|
||||
if (pushManagerClass.name == "com.heytap.msp.push.HeytapPushManager") {
|
||||
pushManagerClass.getMethod("unRegister").invoke(null)
|
||||
} else {
|
||||
val instance = pushManagerClass.getMethod("getInstance").invoke(null)
|
||||
pushManagerClass.getMethod("unRegister", Context::class.java).invoke(instance, context)
|
||||
}
|
||||
Log.i(TAG, "OPPO push unregistered")
|
||||
}.onFailure { error ->
|
||||
Log.w(TAG, "OPPO push unregistration failed: ${error.message}")
|
||||
@ -91,5 +61,90 @@ class OppoPushService : PushVendorInterface {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "OppoPushService"
|
||||
|
||||
private fun resolvePushManagerClass(): Class<*> {
|
||||
return runCatching {
|
||||
Class.forName("com.heytap.msp.push.HeytapPushManager")
|
||||
}.getOrElse {
|
||||
Class.forName("com.heytap.mcssdk.PushManager")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerWithHeytapMsp(
|
||||
context: Context,
|
||||
appKey: String,
|
||||
appSecret: String,
|
||||
): Boolean {
|
||||
val pushManagerClass = runCatching {
|
||||
Class.forName("com.heytap.msp.push.HeytapPushManager")
|
||||
}.getOrNull() ?: return false
|
||||
|
||||
runCatching {
|
||||
pushManagerClass.getMethod("init", Context::class.java, java.lang.Boolean.TYPE)
|
||||
.invoke(null, context, false)
|
||||
}
|
||||
|
||||
val supported = runCatching {
|
||||
pushManagerClass.getMethod("isSupportPush", Context::class.java)
|
||||
.invoke(null, context) as? Boolean
|
||||
}.getOrElse {
|
||||
runCatching { pushManagerClass.getMethod("isSupportPush").invoke(null) as? Boolean }
|
||||
.getOrDefault(true)
|
||||
} ?: true
|
||||
|
||||
if (!supported) {
|
||||
Log.w(TAG, "OPPO push is not supported on this device")
|
||||
return true
|
||||
}
|
||||
|
||||
val callbackClass = Class.forName("com.heytap.msp.push.callback.ICallBackResultService")
|
||||
val callback = buildCallback(context.applicationContext, callbackClass)
|
||||
pushManagerClass.getMethod(
|
||||
"register",
|
||||
Context::class.java,
|
||||
String::class.java,
|
||||
String::class.java,
|
||||
callbackClass,
|
||||
).invoke(null, context, appKey, appSecret, callback)
|
||||
runCatching { pushManagerClass.getMethod("requestNotificationPermission").invoke(null) }
|
||||
return true
|
||||
}
|
||||
|
||||
private fun registerWithLegacyMcs(
|
||||
context: Context,
|
||||
appKey: String,
|
||||
appSecret: String,
|
||||
) {
|
||||
val pushManagerClass = Class.forName("com.heytap.mcssdk.PushManager")
|
||||
val instance = pushManagerClass.getMethod("getInstance").invoke(null)
|
||||
val callbackClass = Class.forName("com.heytap.mcssdk.callback.PushCallback")
|
||||
val callback = buildCallback(context.applicationContext, callbackClass)
|
||||
pushManagerClass.getMethod(
|
||||
"register",
|
||||
Context::class.java,
|
||||
String::class.java,
|
||||
String::class.java,
|
||||
callbackClass,
|
||||
).invoke(instance, context, appKey, appSecret, callback)
|
||||
}
|
||||
|
||||
private fun buildCallback(context: Context, callbackClass: Class<*>): Any {
|
||||
return Proxy.newProxyInstance(
|
||||
callbackClass.classLoader,
|
||||
arrayOf(callbackClass)
|
||||
) { _, method, args ->
|
||||
if (method.name == "onRegister") {
|
||||
val code = args?.getOrNull(0) as? Int
|
||||
val regId = args?.getOrNull(1) as? String
|
||||
if ((code == null || code == 0) && !regId.isNullOrBlank()) {
|
||||
PushSDK.updateNativePushToken(context, vendor, regId)
|
||||
Log.i(TAG, "OPPO push token acquired")
|
||||
} else {
|
||||
Log.w(TAG, "OPPO push register callback code=$code")
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package com.xuqm.sdk.push.vendor
|
||||
|
||||
import android.content.Context
|
||||
import com.xuqm.sdk.push.model.PushVendor
|
||||
import com.xuqm.sdk.push.model.PushVendorConfig
|
||||
|
||||
/**
|
||||
* 厂商推送服务接口
|
||||
@ -22,7 +23,7 @@ interface PushVendorInterface {
|
||||
/**
|
||||
* 注册厂商推送服务,触发获取推送 Token 的流程。
|
||||
*/
|
||||
fun register(context: Context)
|
||||
fun register(context: Context, config: PushVendorConfig = PushVendorConfig())
|
||||
|
||||
/**
|
||||
* 注销厂商推送服务。
|
||||
|
||||
@ -4,6 +4,8 @@ import android.content.Context
|
||||
import android.util.Log
|
||||
import com.xuqm.sdk.push.PushSDK
|
||||
import com.xuqm.sdk.push.model.PushVendor
|
||||
import com.xuqm.sdk.push.model.PushVendorConfig
|
||||
import java.lang.reflect.Proxy
|
||||
|
||||
/**
|
||||
* vivo 推送集成框架
|
||||
@ -31,13 +33,33 @@ class VivoPushService : PushVendorInterface {
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
|
||||
override fun register(context: Context) {
|
||||
override fun register(context: Context, config: PushVendorConfig) {
|
||||
runCatching {
|
||||
val pushClientClass = Class.forName("com.vivo.push.PushClient")
|
||||
val instance = pushClientClass.getMethod("getInstance", Context::class.java)
|
||||
.invoke(null, context)
|
||||
// PushClient.getInstance(context).initialize()
|
||||
pushClientClass.getMethod("initialize").invoke(instance)
|
||||
val listenerClass = Class.forName("com.vivo.push.IPushActionListener")
|
||||
val listener = Proxy.newProxyInstance(
|
||||
listenerClass.classLoader,
|
||||
arrayOf(listenerClass),
|
||||
) { _, method, args ->
|
||||
if (method.name == "onStateChanged") {
|
||||
val state = args?.firstOrNull() as? Int
|
||||
if (state == 0) {
|
||||
persistRegId(context, pushClientClass, instance)
|
||||
} else {
|
||||
Log.w(TAG, "Vivo push turnOnPush state=$state")
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
runCatching {
|
||||
pushClientClass.getMethod("turnOnPush", listenerClass)
|
||||
.invoke(instance, listener)
|
||||
}.onFailure {
|
||||
persistRegId(context, pushClientClass, instance)
|
||||
}
|
||||
Log.i(TAG, "Vivo push registration requested")
|
||||
}.onFailure { error ->
|
||||
Log.w(TAG, "Vivo push registration failed: ${error.message}")
|
||||
@ -59,4 +81,18 @@ class VivoPushService : PushVendorInterface {
|
||||
companion object {
|
||||
private const val TAG = "VivoPushService"
|
||||
}
|
||||
|
||||
private fun persistRegId(
|
||||
context: Context,
|
||||
pushClientClass: Class<*>,
|
||||
instance: Any,
|
||||
) {
|
||||
val regId = runCatching {
|
||||
pushClientClass.getMethod("getRegId").invoke(instance) as? String
|
||||
}.getOrNull()
|
||||
if (!regId.isNullOrBlank()) {
|
||||
PushSDK.updateNativePushToken(context, vendor, regId)
|
||||
Log.i(TAG, "Vivo push token acquired")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,26 +6,13 @@ import android.os.Bundle
|
||||
import android.util.Log
|
||||
import com.xuqm.sdk.push.PushSDK
|
||||
import com.xuqm.sdk.push.model.PushVendor
|
||||
import com.xuqm.sdk.push.model.PushVendorConfig
|
||||
|
||||
/**
|
||||
* 小米推送集成框架
|
||||
*
|
||||
* 需要添加 MiPush SDK 依赖:
|
||||
* ```groovy
|
||||
* implementation 'com.xiaomi.mipush:mipush:5.x.x'
|
||||
* ```
|
||||
*
|
||||
* 在 `AndroidManifest.xml` 的 `<application>` 下配置:
|
||||
* ```xml
|
||||
* <meta-data android:name="XUQM_XIAOMI_APP_ID" android:value="288230376xxxxxxxx" />
|
||||
* <meta-data android:name="XUQM_XIAOMI_APP_KEY" android:value="xxxxxxxxxxxx" />
|
||||
* ```
|
||||
*
|
||||
* 并在自定义 `PushMessageReceiver` 的 `onReceiveRegisterResult()` 回调中获取 token,
|
||||
* 然后调用:
|
||||
* ```kotlin
|
||||
* PushSDK.updateNativePushToken(context, PushVendor.XIAOMI, token)
|
||||
* ```
|
||||
* MiPush AAR 需要随 sdk-push 发布。注册参数优先来自租户 PUSH 配置,
|
||||
* 旧版本 manifest meta-data 仅作为兼容兜底。
|
||||
*/
|
||||
class XiaomiPushService : PushVendorInterface {
|
||||
|
||||
@ -38,14 +25,14 @@ class XiaomiPushService : PushVendorInterface {
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
|
||||
override fun register(context: Context) {
|
||||
override fun register(context: Context, config: PushVendorConfig) {
|
||||
runCatching {
|
||||
val miPushClass = Class.forName("com.xiaomi.mipush.sdk.MiPushClient")
|
||||
val meta = context.packageManager.getApplicationInfo(
|
||||
context.packageName, PackageManager.GET_META_DATA
|
||||
).metaData ?: Bundle.EMPTY
|
||||
val appId = meta.getString("XUQM_XIAOMI_APP_ID", "")
|
||||
val appKey = meta.getString("XUQM_XIAOMI_APP_KEY", "")
|
||||
val appId = config.xiaomiAppId.ifBlank { meta.getString("XUQM_XIAOMI_APP_ID", "") }
|
||||
val appKey = config.xiaomiAppKey.ifBlank { meta.getString("XUQM_XIAOMI_APP_KEY", "") }
|
||||
if (appId.isNotBlank() && appKey.isNotBlank()) {
|
||||
miPushClass.getMethod(
|
||||
"registerPush",
|
||||
@ -54,8 +41,9 @@ class XiaomiPushService : PushVendorInterface {
|
||||
String::class.java,
|
||||
).invoke(null, context, appId, appKey)
|
||||
Log.i(TAG, "Xiaomi push registration requested")
|
||||
pollRegId(context, miPushClass)
|
||||
} else {
|
||||
Log.w(TAG, "Xiaomi appId/appKey not configured in meta-data, skipping registration")
|
||||
Log.w(TAG, "Xiaomi appId/appKey not configured, skipping registration")
|
||||
}
|
||||
}.onFailure { error ->
|
||||
Log.w(TAG, "Xiaomi push registration failed: ${error.message}")
|
||||
@ -73,6 +61,24 @@ class XiaomiPushService : PushVendorInterface {
|
||||
}
|
||||
}
|
||||
|
||||
private fun pollRegId(context: Context, miPushClass: Class<*>) {
|
||||
Thread {
|
||||
repeat(8) {
|
||||
val regId = runCatching {
|
||||
miPushClass.getMethod("getRegId", Context::class.java)
|
||||
.invoke(null, context) as? String
|
||||
}.getOrNull()
|
||||
if (!regId.isNullOrBlank()) {
|
||||
PushSDK.updateNativePushToken(context, vendor, regId)
|
||||
Log.i(TAG, "Xiaomi push token acquired")
|
||||
return@Thread
|
||||
}
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
Log.w(TAG, "Xiaomi push token not ready after registration")
|
||||
}.start()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "XiaomiPushService"
|
||||
}
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
package com.xuqm.sdk.push.vivo
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import com.xuqm.sdk.push.PushSDK
|
||||
import com.xuqm.sdk.push.model.PushVendor
|
||||
|
||||
class XuqmVivoPushReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
if (intent == null) return
|
||||
val appContext = context.applicationContext
|
||||
val regId = extractRegId(intent) ?: currentRegId(appContext)
|
||||
if (regId.isNullOrBlank()) return
|
||||
runCatching {
|
||||
PushSDK.updateNativePushToken(appContext, PushVendor.VIVO, regId)
|
||||
}.onFailure { error ->
|
||||
Log.w(TAG, "Unable to persist vivo push token: ${error.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractRegId(intent: Intent): String? {
|
||||
val extras = intent.extras ?: return null
|
||||
for (key in extras.keySet()) {
|
||||
val value = extras.get(key) as? String ?: continue
|
||||
if (key.contains("reg", ignoreCase = true) || key.contains("token", ignoreCase = true)) {
|
||||
return value.takeIf { it.isNotBlank() }
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun currentRegId(context: Context): String? {
|
||||
return runCatching {
|
||||
val pushClientClass = Class.forName("com.vivo.push.PushClient")
|
||||
val instance = pushClientClass.getMethod("getInstance", Context::class.java)
|
||||
.invoke(null, context)
|
||||
pushClientClass.getMethod("getRegId").invoke(instance) as? String
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "XuqmVivoPush"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package com.xuqm.sdk.push.xiaomi
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import com.xuqm.sdk.push.PushSDK
|
||||
import com.xuqm.sdk.push.model.PushVendor
|
||||
|
||||
class XuqmXiaomiPushReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
if (intent == null) return
|
||||
val appContext = context.applicationContext
|
||||
val regId = extractRegId(intent)
|
||||
if (!regId.isNullOrBlank()) {
|
||||
runCatching {
|
||||
PushSDK.updateNativePushToken(appContext, PushVendor.XIAOMI, regId)
|
||||
}.onFailure { error ->
|
||||
Log.w(TAG, "Unable to persist Xiaomi push token: ${error.message}")
|
||||
}
|
||||
return
|
||||
}
|
||||
runCatching {
|
||||
PushSDK.refreshNativePushToken(appContext, PushVendor.XIAOMI)
|
||||
}.onFailure { error ->
|
||||
Log.d(TAG, "Xiaomi push callback ignored before SDK init: ${error.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractRegId(intent: Intent): String? {
|
||||
val extras = intent.extras ?: return null
|
||||
for (key in extras.keySet()) {
|
||||
val value = extras.get(key) ?: continue
|
||||
if (value is String && key.contains("reg", ignoreCase = true)) {
|
||||
return value.takeIf { it.isNotBlank() }
|
||||
}
|
||||
if (value.javaClass.name == "com.xiaomi.mipush.sdk.MiPushCommandMessage") {
|
||||
val resultCode = runCatching {
|
||||
value.javaClass.getMethod("getResultCode").invoke(value) as? Long
|
||||
}.getOrNull()
|
||||
val command = runCatching {
|
||||
value.javaClass.getMethod("getCommand").invoke(value) as? String
|
||||
}.getOrNull()
|
||||
val arguments = runCatching {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
value.javaClass.getMethod("getCommandArguments").invoke(value) as? List<String>
|
||||
}.getOrNull()
|
||||
if ((resultCode == null || resultCode == 0L) && command.equals("register", ignoreCase = true)) {
|
||||
return arguments?.firstOrNull { it.isNotBlank() }
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "XuqmMiPushReceiver"
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven(url = "https://nexus.xuqinmin.com/repository/android/")
|
||||
maven(url = "https://developer.hihonor.com/repo")
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
@ -11,6 +12,7 @@ dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
maven(url = "https://nexus.xuqinmin.com/repository/android/")
|
||||
maven(url = "https://developer.hihonor.com/repo")
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户