我找到了一个名为NCleaner的应用程序,该应用程序以某种方式隐藏了抬头通知,甚至包括正在进行的通知(由前台服务使用)。
我想知道这种事情是如何工作的。
关于如何控制其他应用程序的通知的信息并不多。仅出于当前应用程序,由于某种原因,我未能取消正在进行的通知,但成功取消了正常通知。
我发现this sample显示了如何监视通知。在搜索有关NotificationListenerService的文档之后,我还发现了如何取消其他应用程序的特定通知。
但是,它对于正在进行的通知不起作用。
为了测试正常的通知,我只是通过PC给自己发送电子邮件或在PushBullet应用中写了一些东西。
为了测试正在进行的通知,我已经安装并运行了我的业余时间应用程序(here),该应用程序从Android O开始就在应用程序的开头显示了正在进行的通知。实际上,它可以甚至隐藏操作系统的通知,USB连接和调试的通知(只需通过USB将设备连接到PC)...
这是我根据示例制作的用于取消其收到的所有通知的代码:
NotificationListenerExampleService.kt
class NotificationListenerExampleService : NotificationListenerService() {
override fun onNotificationPosted(sbn: StatusBarNotification) {
Log.d("AppLog", "sbn:" + sbn.toString())
cancelNotification(sbn.key)
}
override fun onListenerConnected() {
super.onListenerConnected()
Log.d("AppLog", "onListenerConnected")
}
override fun onNotificationRemoved(sbn: StatusBarNotification) {}
}
清单:
<application
android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service
android:name=".NotificationListenerExampleService" android:label="@string/service_label" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService"/>
</intent-filter>
</service>
</application>
MainActivity.kt
class MainActivity : AppCompatActivity() {
private val isNotificationServiceEnabled: Boolean
get() {
val pkgName = packageName
val flat = Settings.Secure.getString(contentResolver, "enabled_notification_listeners"")
if (!TextUtils.isEmpty(flat)) {
val names = flat.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (i in names.indices) {
val cn = ComponentName.unflattenFromString(names[i])
if (cn != null) {
if (TextUtils.equals(pkgName, cn.packageName)) {
return true
}
}
}
}
return false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (!isNotificationServiceEnabled) {
startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
}
}
}
答案 0 :(得分:2)
我使用此代码构建通知。它具有标题,文本,图标, contentAction 作为广播消息。我希望这足以回答您的问题)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notChannel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT)
notManager.createNotificationChannel(notChannel)
}
val builder = NotificationCompat.Builder(this@MainActivity, CHANNEL_ID)
val contentIntent = Intent(NOT_ACTION)
contentIntent.putExtra(EXTRA_NOT_ID, notificationId)
val contentAction = PendingIntent.getBroadcast(this@MainActivity, NOT_REQ_CODE, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentTitle("Notification $notificationId")
.setContentText("Awesome text for $notificationId notification")
.setContentIntent(contentAction)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setDefaults(NotificationCompat.DEFAULT_ALL)
NotificationManagerCompat
.from(this@MainActivity)
.notify(notificationId++, builder.build())
是的。。您可以从Notification
获取StatusBarNotification
,然后获取PendingIntent
进行操作并加以使用。
例如,我要触发通知的 contentAction
override fun onNotificationPosted(sbn: StatusBarNotification) {
super.onNotificationPosted(sbn)
Log.d(TAG, "Gotcha notification from ${sbn.packageName}")
if (sbn.packageName == TARGET_PACKAGE) {
val contentIntent = sbn.notification.contentIntent
contentIntent.send()
}
}
此代码将触发发送广播消息。但是它将忽略.setAutoCancel(true)
通知,因此您需要像这样手动处理
if (sbn.packageName == TARGET_PACKAGE) {
val contentIntent = sbn.notification.contentIntent
contentIntent.send()
if (sbn.notification.flags and Notification.FLAG_AUTO_CANCEL != 0) {
cancelNotification(sbn.key)
}
}
我想到的第一种方法是像这样使用NotificationListenerService.snoozeNotification
if (sbn.packageName == TARGET_PACKAGE) {
snoozeNotification(sbn.key, Long.MAX_VALUE - System.currentTimeMillis())
}
我有99%的把握确定NCLeaner使用此代码。因为它不支持Pre-O设备的此功能。
注意。您不能只使用Long.MAX_VALUE
,通知将被延后几秒钟,然后再次出现。在Pixel 2 8.1上进行了测试。
否,您不能。只有通知所有者应用可以修改它。
我还尝试通过隐藏方法和NotificationManager
和NotificationListenerService
的字段克服反射的限制。所有尝试的结果是:
由以下原因引起:java.lang.SecurityException:调用uid 10210给出了uid.10211拥有的包com.ns.notificationsender。
因此,您的应用必须是系统之一,否则root可以帮助成功。但是我没有这样的设备。