From fc43885f1ef5bbfd66adea75ae9568b6d847bb75 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Thu, 25 Jun 2026 18:03:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(push):=20=E9=80=9A=E7=9F=A5=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E4=BA=8B=E4=BB=B6=E9=80=9A=E8=BF=87=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E7=BB=99=20App=EF=BC=8C=E4=B8=8D=E5=9C=A8=20SDK=20=E5=86=85?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E4=B8=9A=E5=8A=A1=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 PushNotificationClickEvent(vendor/title/content/params/skipContent) - PushSDK.setNotificationClickListener 供 App 注册通知点击回调 - PushSDK.dispatchNotificationClick(internal):触发回调 + 启动宿主 Activity 携带原始 extras(EXTRA_PUSH_PARAMS/EXTRA_PUSH_SKIP_CONTENT/EXTRA_PUSH_VENDOR), 冷启动场景由 App 自行从 Intent extras 解析 - XuqmVivoPushReceiver 仅负责把 UPSNotificationMessage 转为 event 后分发, 不做任何业务层解析(URL 跳转逻辑由 App 实现) - 删除 PushDeepLinkHandler(职责越界) Co-Authored-By: Claude Sonnet 4.6 --- .../com/xuqm/sdk/push/PushDeepLinkHandler.kt | 20 ---------- .../main/java/com/xuqm/sdk/push/PushSDK.kt | 40 +++++++++++++++++++ .../push/model/PushNotificationClickEvent.kt | 9 +++++ .../sdk/push/vivo/XuqmVivoPushReceiver.kt | 17 ++++---- 4 files changed, 58 insertions(+), 28 deletions(-) delete mode 100644 sdk-push/src/main/java/com/xuqm/sdk/push/PushDeepLinkHandler.kt create mode 100644 sdk-push/src/main/java/com/xuqm/sdk/push/model/PushNotificationClickEvent.kt diff --git a/sdk-push/src/main/java/com/xuqm/sdk/push/PushDeepLinkHandler.kt b/sdk-push/src/main/java/com/xuqm/sdk/push/PushDeepLinkHandler.kt deleted file mode 100644 index 2a71391..0000000 --- a/sdk-push/src/main/java/com/xuqm/sdk/push/PushDeepLinkHandler.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.xuqm.sdk.push - -import android.content.Context -import android.content.Intent -import android.util.Log - -object PushDeepLinkHandler { - - const val EXTRA_PUSH_URL = "xuqm_push_url" - private const val TAG = "PushDeepLinkHandler" - - fun open(context: Context, url: String) { - val pm = context.packageManager - val intent = pm.getLaunchIntentForPackage(context.packageName) ?: return - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP - intent.putExtra(EXTRA_PUSH_URL, url) - Log.d(TAG, "Opening app with push URL: $url") - context.startActivity(intent) - } -} diff --git a/sdk-push/src/main/java/com/xuqm/sdk/push/PushSDK.kt b/sdk-push/src/main/java/com/xuqm/sdk/push/PushSDK.kt index 47fd483..470d04a 100644 --- a/sdk-push/src/main/java/com/xuqm/sdk/push/PushSDK.kt +++ b/sdk-push/src/main/java/com/xuqm/sdk/push/PushSDK.kt @@ -25,6 +25,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch +import com.xuqm.sdk.push.model.PushNotificationClickEvent import java.util.concurrent.atomic.AtomicReference object PushSDK { @@ -37,9 +38,27 @@ object PushSDK { private val lastRegisteredDeviceKey = AtomicReference(null) @Volatile private var cachedVendorConfig: PushVendorConfig? = null @Volatile private var cachedConfigAt: Long = 0L + @Volatile private var notificationClickListener: ((PushNotificationClickEvent) -> Unit)? = null + + /** 推送通知点击 Intent extras 的键:原始 params JSON */ + const val EXTRA_PUSH_PARAMS = "xuqm_push_params" + /** 推送通知点击 Intent extras 的键:skipContent */ + const val EXTRA_PUSH_SKIP_CONTENT = "xuqm_push_skip_content" + /** 推送通知点击 Intent extras 的键:厂商名 */ + const val EXTRA_PUSH_VENDOR = "xuqm_push_vendor" // ── Public API (spec-aligned) ────────────────────────────────────────────── + /** + * 注册推送通知点击回调。 + * App 在运行时会通过此回调收到通知点击事件; + * 若 App 未运行(冷启动),通知数据通过 Intent extras 传递到启动 Activity。 + * Intent extras 键:[EXTRA_PUSH_PARAMS]、[EXTRA_PUSH_SKIP_CONTENT]、[EXTRA_PUSH_VENDOR]。 + */ + fun setNotificationClickListener(listener: ((PushNotificationClickEvent) -> Unit)?) { + notificationClickListener = listener + } + /** * 设置离线推送开关。 * 关闭后用户不会收到离线推送,但在线消息仍正常接收。 @@ -91,6 +110,27 @@ object PushSDK { fun notificationChannelIdFor(context: Context, routeType: String): String? = PushNotificationChannelManager.channelIdFor(context.applicationContext, routeType) + // ── Internal notification click dispatch ────────────────────────────────── + + /** + * 由各厂商 Receiver 在通知被点击时调用。 + * 优先触发已注册的 [notificationClickListener](App 在前台/后台均有效); + * 同时启动宿主 App,携带原始 params extras 供冷启动场景下读取。 + */ + internal fun dispatchNotificationClick(context: Context, event: PushNotificationClickEvent) { + Log.d("XuqmPushSDK", "Notification clicked: vendor=${event.vendor}, params=${event.params}") + notificationClickListener?.invoke(event) + val pm = context.packageManager + val intent = pm.getLaunchIntentForPackage(context.packageName) ?: return + intent.flags = android.content.Intent.FLAG_ACTIVITY_NEW_TASK or android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP + intent.putExtra(EXTRA_PUSH_VENDOR, event.vendor.name) + intent.putExtra(EXTRA_PUSH_SKIP_CONTENT, event.skipContent) + if (event.params.isNotEmpty()) { + intent.putExtra(EXTRA_PUSH_PARAMS, HashMap(event.params)) + } + context.startActivity(intent) + } + // ── Internal hooks called by XuqmSDK via reflection ─────────────────────── fun onSdkLogin(session: XuqmLoginSession) { diff --git a/sdk-push/src/main/java/com/xuqm/sdk/push/model/PushNotificationClickEvent.kt b/sdk-push/src/main/java/com/xuqm/sdk/push/model/PushNotificationClickEvent.kt new file mode 100644 index 0000000..287c916 --- /dev/null +++ b/sdk-push/src/main/java/com/xuqm/sdk/push/model/PushNotificationClickEvent.kt @@ -0,0 +1,9 @@ +package com.xuqm.sdk.push.model + +data class PushNotificationClickEvent( + val vendor: PushVendor, + val title: String?, + val content: String?, + val params: Map, + val skipContent: String?, +) diff --git a/sdk-push/src/main/java/com/xuqm/sdk/push/vivo/XuqmVivoPushReceiver.kt b/sdk-push/src/main/java/com/xuqm/sdk/push/vivo/XuqmVivoPushReceiver.kt index 1107bc3..f6cee17 100644 --- a/sdk-push/src/main/java/com/xuqm/sdk/push/vivo/XuqmVivoPushReceiver.kt +++ b/sdk-push/src/main/java/com/xuqm/sdk/push/vivo/XuqmVivoPushReceiver.kt @@ -1,13 +1,12 @@ package com.xuqm.sdk.push.vivo import android.content.Context -import android.content.Intent import android.util.Log import com.vivo.push.model.UnvarnishedMessage import com.vivo.push.model.UPSNotificationMessage import com.vivo.push.sdk.OpenClientPushMessageReceiver -import com.xuqm.sdk.push.PushDeepLinkHandler import com.xuqm.sdk.push.PushSDK +import com.xuqm.sdk.push.model.PushNotificationClickEvent import com.xuqm.sdk.push.model.PushVendor class XuqmVivoPushReceiver : OpenClientPushMessageReceiver() { @@ -27,12 +26,14 @@ class XuqmVivoPushReceiver : OpenClientPushMessageReceiver() { } override fun onNotificationMessageClicked(context: Context, message: UPSNotificationMessage) { - val url = message.params?.get("url")?.takeIf { it.isNotBlank() } - ?: message.skipContent?.takeIf { it.isNotBlank() } - Log.d(TAG, "Vivo notification clicked: ${message.title}, url=$url") - if (url != null) { - PushDeepLinkHandler.open(context, url) - } + val event = PushNotificationClickEvent( + vendor = PushVendor.VIVO, + title = message.title, + content = message.content, + params = message.params?.toMap() ?: emptyMap(), + skipContent = message.skipContent, + ) + PushSDK.dispatchNotificationClick(context, event) } companion object {