Android推送通知許可權判斷及跳轉到許可權設置界面(完善兼容8.0)

有時候產品要求增加一個推送通知的開關(有些還要求具體到哪些通知,比如廣告類? 比如重大熱點等?)。

我們首先想到的肯定就是再推送回調介面裡面判斷開啟的狀態,進而進行過濾!沒錯,如果對於關閉通知肯定沒問題。但是對於開啟通知有個問題?就是即使你開啟了這個狀態值,但是如果系統關閉了該應用的通知許可權,那麼你開啟了其實也沒有用對吧?

所以正常的邏輯是:

1. 如果關閉,則不用判斷許可權,直接關閉就行

2. 如果開啟,首先判斷是否有通知許可權,如果有則走關閉邏輯就行;如果沒有,則把開關狀態重置回來,然後申請通知許可權 - 通知許可權開啟後再次開啟就沒有問題了

a> 直接給判斷許可權的工具類:

public class NotificationsUtils {
private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";

@TargetApi(Build.VERSION_CODES.KITKAT)
public static boolean isNotificationEnabled(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
///< 8.0手機以上
if (((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).getImportance() == NotificationManager.IMPORTANCE_NONE) {
return false;
}
}

AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;

Class appOpsClass = null;
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 = (Integer) opPostNotificationValue.get(Integer.class);
return ((Integer) checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg) == AppOpsManager.MODE_ALLOWED);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}

b> 跳轉到通知設置的界面(自己做了下完善和測試)

/**
* 通知許可權申請
* @param context
*/
public static void requestNotify(Context context) {
/**
* 跳到通知欄設置界面
* @param context
*/
Intent localIntent = new Intent();
///< 直接跳轉到應用通知設置的代碼
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
localIntent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
localIntent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
}
else if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
localIntent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
localIntent.putExtra("app_package", context.getPackageName());
localIntent.putExtra("app_uid", context.getApplicationInfo().uid);
} else if (android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
localIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
localIntent.addCategory(Intent.CATEGORY_DEFAULT);
localIntent.setData(Uri.parse("package:" + context.getPackageName()));
} else {
///< 4.4以下沒有從app跳轉到應用通知設置頁面的Action,可考慮跳轉到應用詳情頁面,
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 9) {
localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
localIntent.setData(Uri.fromParts("package", context.getPackageName(), null));
} else if (Build.VERSION.SDK_INT <= 8) {
localIntent.setAction(Intent.ACTION_VIEW);
localIntent.setClassName("com.android.settings", "com.android.setting.InstalledAppDetails");
localIntent.putExtra("com.android.settings.ApplicationPkgName", context.getPackageName());
}
}
context.startActivity(localIntent);
}

啊哈!!目前就是醬紫,這個應該有官方的吧,我找找看...有相關類的說明,但是具體的案例沒有。不過有具體的一些個解釋:

比如:Settings | Android Developers

再比如判斷的方式:

ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED
Intent that is broadcast when a NotificationChannel is blocked
(when NotificationChannel.getImportance() is IMPORTANCE_NONE) or
unblocked (when NotificationChannel.getImportance() is anything other than IMPORTANCE_NONE).

意思就是說:當一個NotificationChannel 被阻塞時,這個getImportance()的值就是IMPORTANCE_NONE,其他情況則不是。所以8.0也就可以根據這個狀態來做判斷.8.0以下的話採用的是反射的方式 - 這個具體的大家可以研究一下。小白暫時不研究了,先做個記錄和簡單學習。

最後使用邏輯:Switch控制項(selector_thumb, selector_track就是slector,裡面就是一些個shape文件...)

<Switch
android:id="@+id/ass_pushSt"
android:layout_width_="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:switchMinWidth="40dp"
android:text=""
android:textOff=" "
android:textOn=" "
android:thumb="@drawable/selector_thumb"
android:track="@drawable/selector_track"
app:layout_constraintEnd_toEndOf="parent" />

pushSt.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (b){
if (!NotificationsUtils.isNotificationEnabled(MineSystemSettingActivity.this)){
runOnUiThread(new Runnable() {
@Override
public void run() {
pushSt.setChecked(false);
}
});
PermissionTool.requestNotify(MineSystemSettingActivity.this);
return;
}
}
UserInfoControlPresenter.setPushStatus(!b);
}
});

剩下的你可以測測了喲...小寶貝...

推薦閱讀:

TAG:Android | 通知(Notification) | 設置 |