|
|
|
@ -2,7 +2,6 @@ package com.example.pin
|
|
|
|
|
|
|
|
|
|
|
|
import android.content.Context
|
|
|
|
import android.content.Context
|
|
|
|
import android.content.Intent
|
|
|
|
import android.content.Intent
|
|
|
|
import android.content.pm.ResolveInfo
|
|
|
|
|
|
|
|
import android.net.Uri
|
|
|
|
import android.net.Uri
|
|
|
|
import android.os.Handler
|
|
|
|
import android.os.Handler
|
|
|
|
import android.os.Looper
|
|
|
|
import android.os.Looper
|
|
|
|
@ -13,6 +12,7 @@ import android.service.notification.StatusBarNotification
|
|
|
|
import android.text.TextUtils
|
|
|
|
import android.text.TextUtils
|
|
|
|
import com.example.logger.LogUtils
|
|
|
|
import com.example.logger.LogUtils
|
|
|
|
import java.lang.reflect.Method
|
|
|
|
import java.lang.reflect.Method
|
|
|
|
|
|
|
|
import java.lang.reflect.Modifier
|
|
|
|
|
|
|
|
|
|
|
|
object NotificationManger {
|
|
|
|
object NotificationManger {
|
|
|
|
private const val PREFIX_ANDROID = "android."
|
|
|
|
private const val PREFIX_ANDROID = "android."
|
|
|
|
@ -31,9 +31,13 @@ object NotificationManger {
|
|
|
|
private var isPolling = false
|
|
|
|
private var isPolling = false
|
|
|
|
private var pollInterval = DEFAULT_POLL_INTERVAL
|
|
|
|
private var pollInterval = DEFAULT_POLL_INTERVAL
|
|
|
|
private val processedNotifications = mutableSetOf<String>() // 记录已处理的通知 key
|
|
|
|
private val processedNotifications = mutableSetOf<String>() // 记录已处理的通知 key
|
|
|
|
private var applicationContext: Context? = null
|
|
|
|
private lateinit var applicationContext: Context
|
|
|
|
|
|
|
|
|
|
|
|
fun process(context: Context) {
|
|
|
|
fun initialized(context: Context) {
|
|
|
|
|
|
|
|
applicationContext = context.applicationContext
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun process(context: Context) {
|
|
|
|
// 检查通知监听器是否已启用
|
|
|
|
// 检查通知监听器是否已启用
|
|
|
|
if (!isNotificationListenerEnabled(context)) {
|
|
|
|
if (!isNotificationListenerEnabled(context)) {
|
|
|
|
LogUtils.info("NotificationManager: notification listener is not enabled")
|
|
|
|
LogUtils.info("NotificationManager: notification listener is not enabled")
|
|
|
|
@ -62,29 +66,32 @@ object NotificationManger {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 用过滤结果替换 notifications 列表
|
|
|
|
// 用过滤结果替换 notifications 列表
|
|
|
|
LogUtils.info("NotificationManager: found ${notifications.size} notifications")
|
|
|
|
LogUtils.info("NotificationManager: found ${notifications.size} notifications")
|
|
|
|
|
|
|
|
// notifications = notifications.asReversed()
|
|
|
|
for (notification in notifications) {
|
|
|
|
for (notification in notifications) {
|
|
|
|
processNotification(notification, instance, context)
|
|
|
|
processNotification(notification, instance, context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fun startPolling(context: Context, intervalMs: Long = DEFAULT_POLL_INTERVAL) {
|
|
|
|
fun startPolling(intervalMs: Long = DEFAULT_POLL_INTERVAL, duration: Long, l:(NotificationMessage)-> Unit) {
|
|
|
|
if (isPolling) {
|
|
|
|
if (isPolling) {
|
|
|
|
LogUtils.info("NotificationManager: polling already started")
|
|
|
|
LogUtils.info("NotificationManager: polling already started")
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.listener = l
|
|
|
|
|
|
|
|
val startTime = System.currentTimeMillis()
|
|
|
|
LogUtils.info("NotificationManager: start polling with interval ${intervalMs}ms")
|
|
|
|
LogUtils.info("NotificationManager: start polling with interval ${intervalMs}ms")
|
|
|
|
// 使用 ApplicationContext 避免内存泄漏
|
|
|
|
// 使用 ApplicationContext 避免内存泄漏
|
|
|
|
applicationContext = context.applicationContext
|
|
|
|
|
|
|
|
pollInterval = intervalMs
|
|
|
|
pollInterval = intervalMs
|
|
|
|
isPolling = true
|
|
|
|
isPolling = true
|
|
|
|
pollingHandler = Handler(Looper.getMainLooper())
|
|
|
|
pollingHandler = Handler(Looper.getMainLooper())
|
|
|
|
|
|
|
|
|
|
|
|
pollingRunnable = object : Runnable {
|
|
|
|
pollingRunnable = object : Runnable {
|
|
|
|
override fun run() {
|
|
|
|
override fun run() {
|
|
|
|
if (isPolling && applicationContext != null) {
|
|
|
|
if (isPolling) {
|
|
|
|
process(applicationContext!!)
|
|
|
|
process(applicationContext)
|
|
|
|
pollingHandler?.postDelayed(this, pollInterval)
|
|
|
|
if(System.currentTimeMillis() - startTime < duration) {
|
|
|
|
|
|
|
|
pollingHandler?.postDelayed(this, pollInterval)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -102,8 +109,8 @@ object NotificationManger {
|
|
|
|
pollingRunnable?.let { pollingHandler?.removeCallbacks(it) }
|
|
|
|
pollingRunnable?.let { pollingHandler?.removeCallbacks(it) }
|
|
|
|
pollingRunnable = null
|
|
|
|
pollingRunnable = null
|
|
|
|
pollingHandler = null
|
|
|
|
pollingHandler = null
|
|
|
|
applicationContext = null
|
|
|
|
|
|
|
|
processedNotifications.clear()
|
|
|
|
processedNotifications.clear()
|
|
|
|
|
|
|
|
listener = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun getServiceInstance(context: Context): Any? {
|
|
|
|
private fun getServiceInstance(context: Context): Any? {
|
|
|
|
@ -121,10 +128,25 @@ object NotificationManger {
|
|
|
|
|
|
|
|
|
|
|
|
// 优先尝试直接获取 MyService.instance(系统绑定的实例)
|
|
|
|
// 优先尝试直接获取 MyService.instance(系统绑定的实例)
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
val myServiceClass = Class.forName("com.galaxy.demo.MyService")
|
|
|
|
// 尝试从服务类的静态字段获取实例(备用方案)
|
|
|
|
val instanceField = myServiceClass.getDeclaredField("instance")
|
|
|
|
val serviceClass = findServiceClass(context)
|
|
|
|
instanceField.isAccessible = true
|
|
|
|
if (serviceClass == null) {
|
|
|
|
val instance = instanceField.get(null) as? NotificationListenerService
|
|
|
|
LogUtils.info("NotificationManager: service class not found")
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
var instance: NotificationListenerService? = null
|
|
|
|
|
|
|
|
val staticFields = serviceClass.declaredFields.filter { Modifier.isStatic(it.modifiers) }
|
|
|
|
|
|
|
|
LogUtils.info("${serviceClass.name} has files: ${staticFields.size}")
|
|
|
|
|
|
|
|
for(field in staticFields) {
|
|
|
|
|
|
|
|
LogUtils.info("field's Name: ${field.name}")
|
|
|
|
|
|
|
|
if(Modifier.isStatic(field.modifiers)) {
|
|
|
|
|
|
|
|
field.isAccessible = true
|
|
|
|
|
|
|
|
instance= field.get(null) as? NotificationListenerService
|
|
|
|
|
|
|
|
if(instance == null) {
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if (instance != null && isServiceValid(instance)) {
|
|
|
|
if (instance != null && isServiceValid(instance)) {
|
|
|
|
LogUtils.info("NotificationManager: got instance from MyService.instance")
|
|
|
|
LogUtils.info("NotificationManager: got instance from MyService.instance")
|
|
|
|
serviceInstance = instance
|
|
|
|
serviceInstance = instance
|
|
|
|
@ -134,24 +156,7 @@ object NotificationManger {
|
|
|
|
LogUtils.info("NotificationManager: failed to get MyService.instance: ${e.message}")
|
|
|
|
LogUtils.info("NotificationManager: failed to get MyService.instance: ${e.message}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试从服务类的静态字段获取实例(备用方案)
|
|
|
|
return null
|
|
|
|
val serviceClass = findServiceClass(context)
|
|
|
|
|
|
|
|
if (serviceClass == null) {
|
|
|
|
|
|
|
|
LogUtils.info("NotificationManager: service class not found")
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LogUtils.info("NotificationManager: found service class ${serviceClass.name}")
|
|
|
|
|
|
|
|
val instance = getInstance(serviceClass)
|
|
|
|
|
|
|
|
if (instance != null && instance is NotificationListenerService && isServiceValid(instance)) {
|
|
|
|
|
|
|
|
serviceInstance = instance
|
|
|
|
|
|
|
|
LogUtils.info("NotificationManager: service instance obtained successfully")
|
|
|
|
|
|
|
|
return instance
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
LogUtils.info("NotificationManager: failed to get valid service instance")
|
|
|
|
|
|
|
|
serviceInstance = null
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun isServiceValid(service: NotificationListenerService): Boolean {
|
|
|
|
private fun isServiceValid(service: NotificationListenerService): Boolean {
|
|
|
|
@ -221,27 +226,6 @@ object NotificationManger {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun getInstance(clazz: Class<*>): Any? {
|
|
|
|
|
|
|
|
return try {
|
|
|
|
|
|
|
|
val getInstanceMethod = clazz.getDeclaredMethod("getInstance")
|
|
|
|
|
|
|
|
getInstanceMethod.isAccessible = true
|
|
|
|
|
|
|
|
val instance = getInstanceMethod.invoke(null)
|
|
|
|
|
|
|
|
LogUtils.info("NotificationManager: got instance via getInstance() method")
|
|
|
|
|
|
|
|
instance
|
|
|
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
val instanceField = clazz.getDeclaredField("instance")
|
|
|
|
|
|
|
|
instanceField.isAccessible = true
|
|
|
|
|
|
|
|
val instance = instanceField.get(null)
|
|
|
|
|
|
|
|
LogUtils.info("NotificationManager: got instance via instance field")
|
|
|
|
|
|
|
|
instance
|
|
|
|
|
|
|
|
} catch (e2: Exception) {
|
|
|
|
|
|
|
|
LogUtils.error(e2, "NotificationManager: failed to get instance")
|
|
|
|
|
|
|
|
null
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun getNotifications(instance: Any): List<StatusBarNotification>? {
|
|
|
|
private fun getNotifications(instance: Any): List<StatusBarNotification>? {
|
|
|
|
val service = instance as? NotificationListenerService
|
|
|
|
val service = instance as? NotificationListenerService
|
|
|
|
if (service == null) {
|
|
|
|
if (service == null) {
|
|
|
|
@ -316,8 +300,6 @@ object NotificationManger {
|
|
|
|
|
|
|
|
|
|
|
|
LogUtils.info("NotificationManager: processed notification from ${notification.packageName}, content $content, key $notificationKey")
|
|
|
|
LogUtils.info("NotificationManager: processed notification from ${notification.packageName}, content $content, key $notificationKey")
|
|
|
|
listener?.invoke(msg)
|
|
|
|
listener?.invoke(msg)
|
|
|
|
} ?: run {
|
|
|
|
|
|
|
|
LogUtils.info("NotificationManager: notification extras is null, skip: ${notification.packageName}")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|