我该如何检查我的应用程序是否被允许发布通知?

55
用户可以在Android设置中关闭个别应用的通知。是否有一种类似于isNotificationsAllowed()的方法,让我可以检查我的应用是否被允许发送通知?另外,我如何打开Android设置界面,引导用户打开通知开关?

这可能会对您有所帮助: https://dev59.com/DIDba4cB1Zd3GeqPFIJL - TheTanic
你可以在这里查看我的回答 https://dev59.com/-FoT5IYBdhLWcg3wxxok#57769424 同样的解决方案适用。 - ljtomev
8个回答

107

编辑 - 新回答:

看起来谷歌已经添加了正确的API调用: NotificationManagerCompat.from(context).areNotificationsEnabled()


旧回答:

对于任何查看此问题的人,请注意,NotificationListenerService与“显示通知”不同。这两者是不同的事情!如果一个应用程序可以访问NotificationListenerService,并不意味着其通知被显示,反之亦然。为了检查用户是否已阻止您的应用程序的通知,可以使用反射:

public class NotificationsUtils {

private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";

public static boolean isNotificationEnabled() {

    AppOpsManager mAppOps = (AppOpsManager) GlobalContext.getContext().getSystemService(Context.APP_OPS_SERVICE);

    ApplicationInfo appInfo = GlobalContext.getContext().getApplicationInfo();

    String pkg = GlobalContext.getContext().getApplicationContext().getPackageName();

    int uid = appInfo.uid;

    Class appOpsClass = null; /* Context.APP_OPS_MANAGER */

    try {

        appOpsClass = Class.forName(AppOpsManager.class.getName());

        Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class);

        Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
        int value = (int)opPostNotificationValue.get(Integer.class);

        return ((int)checkOpNoThrowMethod.invoke(mAppOps,value, uid, pkg) == AppOpsManager.MODE_ALLOWED);

    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return false;
}
}

来源: https://code.google.com/p/android/issues/detail?id=38482


AppOpsManager是在API级别19(Android 4.4)中添加的,因此只能用于API级别19+。 - nibarius
@nibarius 是的,你说得对。但是:从4.4开始,用户可以在应用程序级别上关闭通知。因此,在4.4以下,可以安全地假定通知已启用(除非在篡改的ROM上运行)。 - Kasra
@Kasra 我刚刚在一台运行Android 4.2.2的三星Galaxy Grand上进行了测试,我也可以在应用程序级别上关闭通知。这个SO问题也说从4.1开始可以在应用程序级别上完成:https://dev59.com/QGgu5IYBdhLWcg3wAigi - nibarius
1
@nibarius 不好意思,4.1-4.4 这个版本有点模糊。请看我的更新答案。如果你查看 NotificationManagerCompat.from(context).areNotificationsEnabled() 的源代码,你会发现它对每个 Android 版本使用不同的逻辑(它也在内部使用 AppOpsManager)。无论如何,这应该是最可靠的方法,因为它是由 Android 团队制作的。 - Kasra
嗨@kasra,这个方法在Android 8以下的版本运行良好。但是在最新的Android 8(Android-奥利奥)中,该方法总是返回true。 - shyam.y

65
NotificationManagerCompat.from(context).areNotificationsEnabled()

看起来这是一个可行的方案。


我通过手动更改设置中的通知权限进行了测试,结果很好,布尔值在两种情况下都正确返回。这是一个很棒的一行解决方案。而且正好赶上了...我在这个答案发布后几个小时内正在寻找解决方案...所以时间很好 :) 谢谢! - Gene Bo
@gnB 不错!很高兴能帮到你 ;) - asamoylenko
NotificationManagerCompat.From(context).AreNotificationsEnabled(); - Gerry

11
这里是关于这个问题的更完整的答案。
fun areNotificationsEnabled(context: Context, channelId: String = "fcm_fallback_notification_channel"): Boolean {
    // check if global notification switch is ON or OFF
    if (NotificationManagerCompat.from(context).areNotificationsEnabled())
        // if its ON then we need to check for individual channels in OREO
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            val channel = manager.getNotificationChannel(channelId)
            return channel?.importance != NotificationManager.IMPORTANCE_NONE
        } else {
            // if this less then OREO it means that notifications are enabled
            true
        }
    // if this is less then OREO it means that notifications are disabled
    return false
}

0

2023-03-18:Android 13(SDK 33 TIRAMISU)引入了一个新的运行时权限(android.permission.POST_NOTIFICATIONS),用于应用程序发布通知(在 Android 13 之前默认允许)。

如果您的应用程序的目标 SDK 低于 33 并在 Android 13 设备上运行,则用户第一次打开您的应用程序时,系统将自动请求此权限,然后由用户决定是否允许您的应用程序发布通知。

如果您的应用程序的目标是 SDK 33 并在 Android 13 设备上运行,则默认情况下不允许发布通知,直到您明确请求并得到用户许可为止。

请注意,用户可以允许您的应用程序发布通知,但同时禁用特定的通知渠道,因此仅测试权限是否已授予是不够的,还需要测试通知渠道是否已启用。

下面是一个检查通知是否被允许的示例帮助函数(使用 Kotlin 编写):

fun isPermissionGranted(context: Context, permission: String): Boolean =
    ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED

fun isNotificationAllowed(context: Context, channelId: String? = null): Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        if (!isPermissionGranted(Manifest.permission.POST_NOTIFICATIONS)) return false
    }
    if (!NotificationManagerCompat.from(context).areNotificationsEnabled()) return false

    if (channelId == null) return true
    runCatching {
        val mgr = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val channel = mgr.getNotificationChannel(channelId)
        return channel.importance != NotificationManager.IMPORTANCE_NONE
    }
    return false
}

0
public class MainActivity extends AppCompatActivity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

//Main Code Below-
//NotificationManagerCompat.from(context).areNotificationsEnabled()
       if(NotificationManagerCompat.from(this).areNotificationsEnabled())
       {
           Toast.makeText(this, "Notification is on for this Application", Toast.LENGTH_SHORT).show();
       }
       else
       {
           Toast.makeText(this, "Notification is off for this Application", Toast.LENGTH_SHORT).show();
       }
   }

}

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

0
if(NotificationManagerCompat.from(context).areNotificationsEnabled())
{
 //Do your Work
}
else
{
     //Ask for permission
}

0

这是针对NotificationListenerService的,它与显示通知不同!!! - Kasra

-1

试试这个:

if (Settings.Secure.getString(getActivity().getContentResolver(), "enabled_notification_listeners").contains(getActivity().getPackageName())) {
    // Notification access service already enabled
    Toast.makeText(getActivity(),"notification enabled",Toast.LENGTH_LONG).show();
} else {
    Intent intent = new Intent();
    intent.setClassName("com.android.settings", "com.android.settings.Settings$AppNotificationSettingsActivity");
    intent.putExtra("app_package", getPackageName());
    intent.putExtra("app_uid", getApplicationInfo().uid);
    startActivity(intent);
}

这是针对NotificationListenerService的,它与显示通知不同!!! - Kasra

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