feat(sdk): 新增 ROM 版本上报及 VIVO 隐私协议日志
- DeviceUtils.getRomVersion() 通过反射读取厂商 ROM 版本(MIUI/EMUI/OriginOS/ColorOS/MagicOS) - PushApi/PushSDK: 注册时上报 romVersion 字段 - VivoPushService: 记录 agreePrivacyStatement 是否成功的明确日志,便于诊断 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
父节点
edbabeb578
当前提交
7063d102a2
@ -15,6 +15,29 @@ object DeviceUtils {
|
|||||||
|
|
||||||
fun getOsVersion(): String = "Android ${Build.VERSION.RELEASE}"
|
fun getOsVersion(): String = "Android ${Build.VERSION.RELEASE}"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取厂商 ROM 版本号(MIUI / EMUI / OriginOS / ColorOS / MagicOS 等)。
|
||||||
|
* 通过反射读取系统属性,失败时返回 null。
|
||||||
|
*/
|
||||||
|
fun getRomVersion(): String? {
|
||||||
|
val props = listOf(
|
||||||
|
"ro.miui.ui.version.name", // MIUI
|
||||||
|
"ro.build.version.emui", // EMUI / HarmonyOS
|
||||||
|
"ro.vivo.os.version", // OriginOS (VIVO)
|
||||||
|
"ro.funtouch.os_version", // FuntouchOS (旧 VIVO)
|
||||||
|
"ro.color.os.version", // ColorOS (OPPO)
|
||||||
|
"ro.build.version.opporom", // ColorOS 旧版
|
||||||
|
"ro.build.version.magic_os", // MagicOS (Honor)
|
||||||
|
)
|
||||||
|
return try {
|
||||||
|
val cls = Class.forName("android.os.SystemProperties")
|
||||||
|
val get = cls.getMethod("get", String::class.java, String::class.java)
|
||||||
|
props.firstNotNullOfOrNull { key ->
|
||||||
|
(get.invoke(null, key, "") as? String)?.takeIf { it.isNotBlank() }
|
||||||
|
}
|
||||||
|
} catch (_: Exception) { null }
|
||||||
|
}
|
||||||
|
|
||||||
fun getVendor(): String = when (Build.MANUFACTURER.lowercase()) {
|
fun getVendor(): String = when (Build.MANUFACTURER.lowercase()) {
|
||||||
"huawei", "honor" -> if (Build.MANUFACTURER.lowercase() == "honor") "HONOR" else "HUAWEI"
|
"huawei", "honor" -> if (Build.MANUFACTURER.lowercase() == "honor") "HONOR" else "HUAWEI"
|
||||||
"xiaomi", "redmi" -> "XIAOMI"
|
"xiaomi", "redmi" -> "XIAOMI"
|
||||||
|
|||||||
@ -135,6 +135,7 @@ object PushSDK {
|
|||||||
|
|
||||||
fun onSdkLogin(session: XuqmLoginSession) {
|
fun onSdkLogin(session: XuqmLoginSession) {
|
||||||
val context = runCatching { XuqmSDK.appContext }.getOrNull() ?: return
|
val context = runCatching { XuqmSDK.appContext }.getOrNull() ?: return
|
||||||
|
Log.d("XuqmPushSDK", "onSdkLogin: userId=${session.userId} registeredUserId=${registeredUserId.get()}")
|
||||||
if (registeredUserId.get() == session.userId) return
|
if (registeredUserId.get() == session.userId) return
|
||||||
initializeVendorsInternal(context)
|
initializeVendorsInternal(context)
|
||||||
bindImUserInternal(context, session.userId)
|
bindImUserInternal(context, session.userId)
|
||||||
@ -150,6 +151,7 @@ object PushSDK {
|
|||||||
// ── Internal push registration flow ───────────────────────────────────────
|
// ── Internal push registration flow ───────────────────────────────────────
|
||||||
|
|
||||||
internal fun updateNativePushToken(context: Context, vendor: PushVendor, pushToken: String) {
|
internal fun updateNativePushToken(context: Context, vendor: PushVendor, pushToken: String) {
|
||||||
|
Log.i("XuqmPushSDK", "updateNativePushToken called: vendor=$vendor token=${pushToken.take(12)}...")
|
||||||
XuqmSDK.requireInit()
|
XuqmSDK.requireInit()
|
||||||
val detectedVendor = detectVendor()
|
val detectedVendor = detectVendor()
|
||||||
if (vendor != detectedVendor) {
|
if (vendor != detectedVendor) {
|
||||||
@ -160,6 +162,7 @@ object PushSDK {
|
|||||||
require(normalizedToken.isNotBlank()) { "pushToken must not be blank" }
|
require(normalizedToken.isNotBlank()) { "pushToken must not be blank" }
|
||||||
store(context).save(vendor, normalizedToken)
|
store(context).save(vendor, normalizedToken)
|
||||||
val sessionUserId = XuqmSDK.getUserId()
|
val sessionUserId = XuqmSDK.getUserId()
|
||||||
|
Log.i("XuqmPushSDK", "updateNativePushToken: saved token, sessionUserId=$sessionUserId")
|
||||||
if (sessionUserId != null) {
|
if (sessionUserId != null) {
|
||||||
bindImUserInternal(context, sessionUserId)
|
bindImUserInternal(context, sessionUserId)
|
||||||
}
|
}
|
||||||
@ -220,6 +223,7 @@ object PushSDK {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun bindImUserInternal(context: Context, userId: String) {
|
private fun bindImUserInternal(context: Context, userId: String) {
|
||||||
|
Log.i("XuqmPushSDK", "bindImUserInternal: userId=$userId receivePushEnabled=${isReceivePushEnabled(context)}")
|
||||||
if (!isReceivePushEnabled(context)) return
|
if (!isReceivePushEnabled(context)) return
|
||||||
ensureNativePushTokenInternal(context)
|
ensureNativePushTokenInternal(context)
|
||||||
registerDeviceInternal(context, userId)
|
registerDeviceInternal(context, userId)
|
||||||
@ -238,7 +242,7 @@ object PushSDK {
|
|||||||
}
|
}
|
||||||
val registrationKey = listOf(userId, vendor.name, pushToken, deviceId).joinToString("|")
|
val registrationKey = listOf(userId, vendor.name, pushToken, deviceId).joinToString("|")
|
||||||
if (lastRegisteredDeviceKey.get() == registrationKey) {
|
if (lastRegisteredDeviceKey.get() == registrationKey) {
|
||||||
Log.d("XuqmPushSDK", "Skipping duplicate push device registration for userId=$userId vendor=${vendor.name}")
|
Log.i("XuqmPushSDK", "Skipping duplicate push device registration for userId=$userId vendor=${vendor.name}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!registeringDeviceKey.compareAndSet(null, registrationKey)) {
|
if (!registeringDeviceKey.compareAndSet(null, registrationKey)) {
|
||||||
@ -249,6 +253,7 @@ object PushSDK {
|
|||||||
}
|
}
|
||||||
scope.launch {
|
scope.launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
|
Log.i("XuqmPushSDK", "Registering push device: userId=$userId vendor=${vendor.name} token=${pushToken.take(12)}...")
|
||||||
api.registerDevice(
|
api.registerDevice(
|
||||||
appKey = XuqmSDK.appKey,
|
appKey = XuqmSDK.appKey,
|
||||||
userId = userId,
|
userId = userId,
|
||||||
@ -259,11 +264,14 @@ object PushSDK {
|
|||||||
model = DeviceUtils.getDeviceModel(),
|
model = DeviceUtils.getDeviceModel(),
|
||||||
osVersion = DeviceUtils.getOsVersion(),
|
osVersion = DeviceUtils.getOsVersion(),
|
||||||
appVersion = appVersion(context),
|
appVersion = appVersion(context),
|
||||||
|
romVersion = DeviceUtils.getRomVersion(),
|
||||||
)
|
)
|
||||||
registeredUserId.set(userId)
|
registeredUserId.set(userId)
|
||||||
lastRegisteredDeviceKey.set(registrationKey)
|
lastRegisteredDeviceKey.set(registrationKey)
|
||||||
store(context).updateLastUserId(userId)
|
store(context).updateLastUserId(userId)
|
||||||
Log.i("XuqmPushSDK", "Registered push device for userId=$userId vendor=${vendor.name}")
|
Log.i("XuqmPushSDK", "Registered push device for userId=$userId vendor=${vendor.name}")
|
||||||
|
}.onFailure { e ->
|
||||||
|
Log.e("XuqmPushSDK", "Push device registration failed for userId=$userId: ${e.message}", e)
|
||||||
}.also {
|
}.also {
|
||||||
registeringDeviceKey.compareAndSet(registrationKey, null)
|
registeringDeviceKey.compareAndSet(registrationKey, null)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ interface PushApi {
|
|||||||
@Query("osVersion") osVersion: String,
|
@Query("osVersion") osVersion: String,
|
||||||
@Query("platform") platform: String = "ANDROID",
|
@Query("platform") platform: String = "ANDROID",
|
||||||
@Query("appVersion") appVersion: String? = null,
|
@Query("appVersion") appVersion: String? = null,
|
||||||
|
@Query("romVersion") romVersion: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@DELETE("api/push/unregister")
|
@DELETE("api/push/unregister")
|
||||||
|
|||||||
@ -41,10 +41,15 @@ class VivoPushService : PushVendorInterface {
|
|||||||
// v4.x SDK: initialize(PushConfig) — the no-arg overload is private
|
// v4.x SDK: initialize(PushConfig) — the no-arg overload is private
|
||||||
val builderClass = Class.forName("com.vivo.push.PushConfig\$Builder")
|
val builderClass = Class.forName("com.vivo.push.PushConfig\$Builder")
|
||||||
val builder = builderClass.getConstructor().newInstance()
|
val builder = builderClass.getConstructor().newInstance()
|
||||||
runCatching {
|
val privacySet = runCatching {
|
||||||
builderClass.getMethod("agreePrivacyStatement", Boolean::class.javaPrimitiveType)
|
builderClass.getMethod("agreePrivacyStatement", Boolean::class.javaPrimitiveType)
|
||||||
.invoke(builder, true)
|
.invoke(builder, true)
|
||||||
|
true
|
||||||
|
}.getOrElse { e ->
|
||||||
|
Log.w(TAG, "agreePrivacyStatement not available in this SDK version: ${e.message}")
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
Log.i(TAG, "Vivo push PushConfig built: agreePrivacyStatement=$privacySet")
|
||||||
val pushConfig = builderClass.getMethod("build").invoke(builder)
|
val pushConfig = builderClass.getMethod("build").invoke(builder)
|
||||||
val pushConfigClass = Class.forName("com.vivo.push.PushConfig")
|
val pushConfigClass = Class.forName("com.vivo.push.PushConfig")
|
||||||
pushClientClass.getMethod("initialize", pushConfigClass).invoke(instance, pushConfig)
|
pushClientClass.getMethod("initialize", pushConfigClass).invoke(instance, pushConfig)
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户