聲明:該方案只對API19及以上版本有效
一、目標需求
最近項目中在完善推送功能,需要進入APP時檢測一下是否開啟了推送權限,如果沒有開啟彈窗提醒,當用戶點擊彈窗時直接跳轉到APP的通知設置界面,就像下面這種:

二、需求實現(xiàn)
1、檢測是否開啟通知權限
接到需求時一臉懵,不知道咋實現(xiàn),先是一番搜索,搜索后得知可以通過NotificationManagerCompat 中的 areNotificationsEnabled()來判斷是否開啟通知權限。
查閱官方文檔可知 NotificationManagerCompat 在 android.support.v4.app包中,是API 22.1.0 中加入的。而 areNotificationsEnabled()則是在 API 24.1.0之后加入的。
注意:
areNotificationsEnabled 只對 API 19 及以上版本有效,低于API 19 會一直返回true
2、跳轉到通知設置界面
假設沒有開啟通知權限,點擊之后就需要跳轉到 APP的通知設置界面,對應的Action是:Settings.ACTION_APP_NOTIFICATION_SETTINGS, 這個Action是 API 26 后增加的。APP的通知設置界面如下圖:

如果在部分手機中無法精確的跳轉到 APP對應的通知設置界面,那么我們就考慮直接跳轉到 APP信息界面,對應的Action是:Settings.ACTION_APPLICATION_DETAILS_SETTINGS。APP信息界面如下圖:

3、代碼實現(xiàn):
不多說了,代碼其實很簡單,注釋也很明了,直接上代碼:
(1)、java版(使用了DataBinding-數據綁定)
/**
* 作者:CnPeng
* 時間:2018/7/11
* 功用:檢測在設置中是否開啟了APP的推送
* 其他:
*
* 參考鏈接:
* https://stackoverflow.com/questions/32366649/any-way-to-link-to-the-android-notification-settings-for-my-app
* https://blog.csdn.net/ysy950803/article/details/71910806
* https://juejin.im/post/5a2508656fb9a0450407b638
*/
public class CheckNotifyActivity extends AppCompatActivity {
ActivityCheckNotifyBinding mBinding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_check_notify);
initClickListener();
}
@Override
protected void onResume() {
super.onResume();
checkNotifySetting();
}
/**
* 作者:CnPeng
* 時間:2018/7/12 上午8:02
* 功用:初始化點擊事件
* 說明:
*/
private void initClickListener() {
//CnPeng 2018/7/12 上午7:08 跳轉到通知設置界面
mBinding.tvMsg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
// 根據isOpened結果,判斷是否需要提醒用戶跳轉AppInfo頁面,去打開App通知權限
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
//這種方案適用于 API 26, 即8.0(含8.0)以上可以用
intent.putExtra(EXTRA_APP_PACKAGE, getPackageName());
intent.putExtra(EXTRA_CHANNEL_ID, getApplicationInfo().uid);
//這種方案適用于 API21——25,即 5.0——7.1 之間的版本可以使用
intent.putExtra("app_package", getPackageName());
intent.putExtra("app_uid", getApplicationInfo().uid);
// 小米6 -MIUI9.6-8.0.0系統(tǒng),是個特例,通知設置界面只能控制"允許使用通知圓點"——然而這個玩意并沒有卵用,我想對雷布斯說:I'm not ok!!!
// if ("MI 6".equals(Build.MODEL)) {
// intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
// Uri uri = Uri.fromParts("package", getPackageName(), null);
// intent.setData(uri);
// // intent.setAction("com.android.settings/.SubSettings");
// }
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
// 出現(xiàn)異常則跳轉到應用設置界面:錘子堅果3——OC105 API25
Intent intent = new Intent();
//下面這種方案是直接跳轉到當前應用的設置界面。
//https://blog.csdn.net/ysy950803/article/details/71910806
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
}
});
}
/**
* 作者:CnPeng
* 時間:2018/7/12 上午9:02
* 功用:檢查是否已經開啟了通知權限
* 說明:
*/
private void checkNotifySetting() {
NotificationManagerCompat manager = NotificationManagerCompat.from(this);
// areNotificationsEnabled方法的有效性官方只最低支持到API 19,低于19的仍可調用此方法不過只會返回true,即默認為用戶已經開啟了通知。
boolean isOpened = manager.areNotificationsEnabled();
if (isOpened) {
mBinding.tvMsg.setText("通知權限已經被打開" +
"\n手機型號:" + android.os.Build.MODEL +
"\nSDK版本:" + android.os.Build.VERSION.SDK +
"\n系統(tǒng)版本:" + android.os.Build.VERSION.RELEASE +
"\n軟件包名:" + getPackageName());
} else {
mBinding.tvMsg.setText("還沒有開啟通知權限,點擊去開啟");
}
}
}
(2)、kotlin版
/**
* 作者:CnPeng
* 時間:2018/7/12
* 功用:檢查通知推送是否已經被打開
* 其他:
*/
public class PushCheckActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_push_check)
initClickListener()
}
override fun onResume() {
super.onResume()
checkPushSwitchStatus()
}
/**
* 作者:CnPeng
* 時間:2018/7/12 下午3:43
* 功用:檢查通知推送的開關狀態(tài)
* 說明:
*/
private fun checkPushSwitchStatus() {
val notificationManager: NotificationManagerCompat = NotificationManagerCompat.from(this);
val isOpend = notificationManager.areNotificationsEnabled()
if (isOpend) {
tv_msg.text = "通知權限已經被打開" +
"\n手機型號:${android.os.Build.MODEL}" +
"\nSDK版本:${android.os.Build.VERSION.SDK_INT}" +
"\n系統(tǒng)版本:${android.os.Build.VERSION.RELEASE}" +
"\n軟件包名:${getPackageName()}"
} else {
tv_msg.text = "通知權限沒有被開啟,點擊去開啟"
}
}
private fun initClickListener() {
tv_msg.setOnClickListener {
val intent: Intent = Intent()
try {
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
//8.0及以后版本使用這兩個extra. >=API 26
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
intent.putExtra(Settings.EXTRA_CHANNEL_ID, applicationInfo.uid)
//5.0-7.1 使用這兩個extra. <= API 25, >=API 21
intent.putExtra("app_package", packageName)
intent.putExtra("app_uid", applicationInfo.uid)
startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
//其他低版本或者異常情況,走該節(jié)點。進入APP設置界面
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
intent.putExtra("package", packageName)
//val uri = Uri.fromParts("package", packageName, null)
//intent.data = uri
startActivity(intent)
}
}
}
}
4、踩坑記錄
A: com.android.support包的版本
因為 NotificationManagerCompat 是 22.1.0才有的,其中的 areNotificaitonEnabled() 是 24.1.0 才有的,Settings.ACTION_APP_NOTIFICATION_SETTINGS 是 26 才有的,所以,為了保證這些內容在不同版本中生效,最好在 gradle文件中 support 的版本升級到最新。如:
implementation 'com.android.support:appcompat-v7:27.1.1'
B: 部分國產手機中沒有APP通知設置頁面
在部分國產手機系統(tǒng)中,Settings.ACTION_APPLICATION_DETAILS_SETTINGS對應的Activity是不存在的,比如:錘子堅果3——OC105 API25。
所以,在堅果3手機上,最終會走我們代碼中的 catch 節(jié)點,然后進入到 應用信息界面。
下面兩張圖分別是 錘子堅果3 手機的截圖。第一張是 設置--通知中心的界面,點擊之后只是一個開關的開啟和關閉,并沒有再進入詳細的通知設置界面。第二張是 應用管理--應用程序管理--應用信息界面, 點擊其中的 允許推送通知時也只是開關的開啟和關閉。


C: 部分國產手機 APP通知設置界面中沒有開啟和關閉的操作
部分國產手機中 Settings.ACTION_APPLICATION_DETAILS_SETTINGS對應的Activity并不是我們期望的通知設置界面。比如,小米6。小米6中 Settings.ACTION_APPLICATION_DETAILS_SETTINGS對應的通知設置界面如下:

這完全不是我們需要的界面啊。。。里面并沒有我們想要的開關啊。而且,在小米6中 Settings.ACTION_APPLICATION_DETAILS_SETTINGS 對應的應用信息界面中,點擊其中的 通知管理 之后跳轉的也是上面圖中的樣子。
但是,如果我們手動的從 設置--通知和狀態(tài)欄--通知管理 進入我們應用的通知設置界面時,就可以正常的看到 允許通知的開關,如下圖:

對于小米6手機的這個情況,分析了一陣子之后還是沒找到解決辦法。本來想著通過log確認一下上圖中的界面到底是哪個Activity,但非常郁悶的是Log中只得到了
com.android.settings/.SubSettings這么一個地址,之前沒見過這個地址,然后繼續(xù)搜索。在看完 https://www.cnblogs.com/Lefter/archive/2013/04/27/3048010.html 和 https://blog.csdn.net/hfreeman2008/article/details/52778992 之后,明白了 .SubSettings 是干啥的了。也大致推斷出為啥在小米6上得不到我們想要的界面了——他們在定制系統(tǒng)時更改了通知設置界面對應的Fragment?。。?!
此時,真想對雷布斯說一句:I'm not ok!!!!
三、附錄
1、測試結果說明
| 手機型號 | 系統(tǒng)版本 | 測試結果 |
|---|---|---|
| Vivo X9s | 7.1.2 | 正常跳轉到通知設置界面 |
| 榮耀10 | 8.1.0 | 正常跳轉到通知設置界面 |
| 紅米note4x | 7.0 | 正常跳轉到通知設置界面 |
| Oppo R7 plus | 5.0 | 正常跳轉到通知設置界面 |
| ZTE BA910 | 5.1 | 正常跳轉到通知設置界面 |
| Oppo R15 | 8.1.0 | 正常跳轉到通知設置界面 |
| 三星蓋樂世On5 | 7.1.1 | 正常跳轉到通知設置界面 |
| 360Vizza | 7.1.1 | 正常跳轉到通知設置界面 |
| 魅族Mx3 | 4.4 | 進入APP設置界面 |
| 華為榮耀4X | 4.4 | 進入APP設置界面 |
| 錘子堅果3 | 7.1.2 | 進入APP設置界面 |
| 小米6 | 8.0.0 | 進入的頁面中沒有通知開關?。。?! |
2、參考鏈接
(1)通知設置的參考鏈接
- https://stackoverflow.com/questions/32366649/any-way-to-link-to-the-android-notification-settings-for-my-app
- https://blog.csdn.net/ysy950803/article/details/71910806
- https://juejin.im/post/5a2508656fb9a0450407b638
(2)SubSettings 和 Settings 的參考鏈接
- https://www.cnblogs.com/Lefter/archive/2013/04/27/3048010.html
- https://blog.csdn.net/hfreeman2008/article/details/52778992
3、文中代碼的GitHub地址
文中代碼分別對應下列倉庫中的:b_34_checkNotify、b_34_pushcheck
Java版:https://github.com/CnPeng/CnPengAndroid.git
Kotlin版:https://github.com/CnPeng/CnPengKotlin.git
本文到此結束,謝謝觀看!
如有不足,敬請指正!