如何在Android 5(棒棒糖)上禁止锁屏通知但仍允许通知区域显示?

18

在升级到Android 5.0 Lollipop之后,它开始在锁屏界面自动显示正在进行的通知。

有时用户不想看到所有这些通知,因此他们会询问开发人员如何让通知在状态栏中保留但在锁屏界面中隐藏。

我找到的唯一方法是强制用户使用屏幕锁(例如手势或PIN),并编程使用setVisibility()设置为VISIBILITY_SECRET。但并非所有用户都想使用屏幕锁。

是否有任何标志(或标志组合)可以告诉通知:不要在锁屏界面上可见,但在通知区域中可见?


这听起来应该可以在隐私选项下进行配置。尽管我记得几年前iPhone也做过类似的事情,但这是一个奇怪的设计决策。 - G_V
不行。甚至在全局设置应用程序的通知设置中也没有。用户可以完全锁定特定应用程序的通知,但不能仅锁定锁屏通知。作为用户,希望能看到后者! - Wolfram Rittmeyer
很遗憾,目前这是不可能的。安卓似乎做出了一个奇怪的决定来防止这种情况发生;一些应用程序(如llama、SignalCheck和Battery Status)在通知栏中非常有用,但并不需要出现在锁屏界面上。已经提出了更改此行为的请求;如果您给它点个赞,也许谷歌将来会进行调整:https://code.google.com/p/android/issues/detail?id=80061 - mike47
你好Tomas,我正在寻找与你所询问的类似的功能,你有找到实现它的方法吗? - user2028
4个回答

14

使用可见性和优先级

正如这个答案所述,您可以使用VISIBILITY_SECRET来在用户有安全密钥保护(不仅仅是滑动或没有密钥保护)且已禁止显示敏感通知时,在锁定屏幕上禁止通知。

为了覆盖其余情况,您可以通过将通知的优先级设置为PRIORITY_MIN来在出现键盘保护时从锁定屏幕和状态栏中以编程方式隐藏通知,然后在键盘保护消失时重置优先级。

缺点

  • 使用Android 5模拟器,似乎会导致通知在锁屏上非常短暂地出现,然后消失。
  • 自Android O起,当用户没有安全锁屏(例如仅滑动)时,通知优先级被弃用,因此不再起作用。

示例

final BroadcastReceiver notificationUpdateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        NotificationManager notificationManager =
            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);

        NotificationCompat.Builder builder =
            new NotificationCompat.Builder(context, YOUR_NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.drawable.your_icon)
                .setVisibility(NotificationCompat.VISIBILITY_SECRET);
        
        KeyguardManager keyguardManager =
            (KeyguardManager)context.getSystemService(Context.KEYGUARD_SERVICE);

        if (keyguardManager.isKeyguardLocked())
            builder.setPriority(NotificationCompat.PRIORITY_MIN);
        
        notificationManager.notify(YOUR_NOTIFICATION_ID, builder.build());
    }
};

//For when the screen might have been locked
context.registerReceiver(notificationUpdateReceiver,
    new IntentFilter(Intent.ACTION_SCREEN_OFF));

//Just in case the screen didn't get a chance to finish turning off but still locked
context.registerReceiver(notificationUpdateReceiver,
    new IntentFilter(Intent.ACTION_SCREEN_ON));

//For when the user unlocks the device
context.registerReceiver(notificationUpdateReceiver,
    new IntentFilter(Intent.ACTION_USER_PRESENT));

//For when the user changes users
context.registerReceiver(notificationUpdateReceiver,
    new IntentFilter(Intent.ACTION_USER_BACKGROUND));
context.registerReceiver(notificationUpdateReceiver,
    new IntentFilter(Intent.ACTION_USER_FOREGROUND));

似乎在Android文档中缺少这一点关于setLockScreenVisibility()的说明:“仅由系统和通知排序器可修改。” - Jeffrey Blattman
@JeffreyBlattman,你无法更改频道的可见性,但仍然可以在通知本身上更改它。我刚在Android 10上测试了一下,并确认它仍然有效。 - Sam
没错,是我的错误。我也测试了通知,它可以正常工作。 - Jeffrey Blattman

7
似乎VISIBILITY_SECRET是最简洁的方法。根据文档:一个通知可以被设置为VISIBILITY_SECRET,这将抑制其图标和滚动消息,直到用户绕过锁屏。根据源代码(SystemUI AOSP项目中的NotificationData),VISIBILITY_SECRET是唯一的方法。
boolean shouldFilterOut(StatusBarNotification sbn) {
    if (!(mEnvironment.isDeviceProvisioned() ||
            showNotificationEvenIfUnprovisioned(sbn))) {
        return true;
    }

    if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) {
        return true;
    }

    if (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET &&
            mEnvironment.shouldHideSensitiveContents(sbn.getUserId())) {
        return true;
    }
    return false;
}

似乎还有一种被过滤掉的通知类型是在存在摘要的组中的子通知。因此,除非您有多个正当理由需要使用摘要,否则当前最好的方法就是使用VISIBILITY_SECRET。


1
问题在于,如果用户没有启用PIN码或锁定模式,则此方法不适用,并且它会隐藏通知区域中的图标。这不能满足OP(或我,或许多其他人)的需求。 - mike47

4
你可以将通知的优先级设置为PRIORITY_MIN。这样可以在锁屏界面上隐藏通知。它还会隐藏状态栏中的图标(不确定你是否希望这样),但通知本身仍然可在通知区域中看到。

3
不,那不是我的意图。人们希望在状态栏中看到通知,而不是在锁屏界面上。 - Tomáš Hubálek
我已经寻找这个答案好几天了。如果可以的话,我会给它+2分。谢谢! - Nicu Surdu
很遗憾,这在 Android O 开发者预览版 1 中不再起作用。 - Sam

0
我为我的持续通知创建了一个名为“LockscreenIntentReceiver”的锁屏意图接收器,它看起来像这样:


    private class LockscreenIntentReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try { 
            String action = intent.getAction();
            if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                Log.d(TAG, "LockscreenIntentReceiver: ACTION_SCREEN_OFF");
                disableNotification();
            } else if (action.equals(Intent.ACTION_USER_PRESENT)){
                Log.d(TAG, "LockscreenIntentReceiver: ACTION_USER_PRESENT");
                // NOTE: Swipe unlocks don't have an official Intent/API in android for detection yet,
                // and if we set ungoing control without a delay, it will get negated before it's created
                // when pressing the lock/unlock button too fast consequently.
                Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (NotificationService.this.isNotificationAllowed()) {
                            enableNotification((Context)NotificationService.this);
                        }
                    }
                }, 800);
            }
        } catch (Exception e) {
            Log.e(TAG, "LockscreenIntentReceiver exception: " + e.toString());
        }
    }
}

这段代码主要是在用户锁定手机时基本上移除正在进行的通知(移除会非常短暂地可见)。一旦用户解锁手机,正在进行的通知将在延迟时间(这里是 800 毫秒)后恢复。 enableNotification() 是一个方法,它将创建通知,并调用 startForeground()。目前已验证可在 Android 7.1.1 上运行。

您只需记得相应地注册和取消注册接收器即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接