【Notification】屏蔽特定应用的通知提示

须要默认屏蔽特定app的通知提示
设置app是否接收通知的界面
点击每一个条目进去的界面
这里写图片描写叙述这里写图片描写叙述

AppNotificationSettings extends SettingsPreferenceFragment
private SwitchPreference mBlock; //条目通过Preference设置
mBlock.setChecked(mAppRow.banned);

mBlock.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
    final boolean block = (Boolean) newValue;
    return mBackend.setNotificationsBanned(pkg, uid, block);
}
    });

    // Users cannot block notifications from system/signature packages
    //通过工具类推断app是系统包(如计算器)时移除设置通知设置的preference
    if (Utils.isSystemPackage(pm, info)) {
        getPreferenceScreen().removePreference(mBlock);
        mPriority.setDependency(null); // don't have it depend on a preference that's gone
    }

通过对Block的当前状态通过mAppRow设置
对preference状态的监听,通过mBackend实现
import com.android.settings.notification.NotificationAppList.AppRow;
import com.android.settings.notification.NotificationAppList.Backend;

查看NotificationAppList.java

    //对AppRow中的属性进行初始化
    public static AppRow loadAppRow(PackageManager pm, ApplicationInfo app,
            Backend backend) {
        final AppRow row = new AppRow();
        row.pkg = app.packageName;
        row.uid = app.uid;
        try {
            row.label = app.loadLabel(pm);
        } catch (Throwable t) {
            Log.e(TAG, "Error loading application label for " + row.pkg, t);
            row.label = row.pkg;
        }
        row.icon = app.loadIcon(pm);
        row.banned = backend.getNotificationsBanned(row.pkg, row.uid);// 是否禁止通知
        row.priority = backend.getHighPriority(row.pkg, row.uid);
        row.sensitive = backend.getSensitive(row.pkg, row.uid);
        return row;
}

这里写图片描写叙述
查看INotificationManager接口
find frameworks/ -name “INotification*”
frameworks/support/v4/java/android/support/v4/app/INotificationSideChannel.aidl
frameworks/base/core/java/com/mediatek/common/mom/INotificationListener.aidl
frameworks/base/core/java/android/app/INotificationManager.aidl
frameworks/base/core/java/android/service/notification/INotificationListener.aidl

.aidl文件(接口定义语言,用于进程间通讯)
frameworks/base/core/java/android/app/INotificationManager.aidl
实现的service路径为
frameworksaseservicesjavacomandroidserverNotificationManagerService.java

对接受Notification属性的获取

这里写图片描写叙述

这里写图片描写叙述

这里写图片描写叙述

mService的类型IAppOpsService
frameworks/base/core/java/com/android/internal/app/IAppOpsService.aidl
frameworks/base/services/core/java/com/android/server/AppOpsService.java

返回的值为MODE_IGNORED时,boolean areNotificationsEnabledForPackage(String pkg, int uid)会返回false
mService.checkOperation(op, uid, packageName) = MODE_ALLOWED时。则同意接收通知 ;

这里写图片描写叙述

对接受Notification的属性设置

@Override
        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
            checkCallerIsSystem();

            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
        }

这里写图片描写叙述
AppOpsManager的路径:frameworks/base/core/java/android/app/AppOpsManager.java
这里写图片描写叙述
public static final int OP_POST_NOTIFICATION = 11;
uid = app.uid
pkg = app.packageName;
mode = enabled?AppOpsManager.MODE_ALLOWED:AppOpsManager.MODE_IGNORED
这里写图片描写叙述

查看代码中哪些位置调用了setNotificationsEnabledForPackageImpl方法
这里写图片描写叙述
除此处还有两处都是对方法的重写与详细实现
这里写图片描写叙述
这里写图片描写叙述

详细解决步骤

当须要屏蔽全部应用通知没有例外时
在AppOpsManager中有关于app很多參数设置的默认值,比方图中第十二个就是默认对app的通知开启或关闭。AppOpsService中的checkOperation方法下就进行了推断。当op为空时。返回的时默认的MODE。

所以讲原本的MODE_ALLOWED改为MODE_IGNORED后。编译frameworks/base后push进手机重新启动就会发现全部app全部被屏蔽通知没有例外。

当有特定的app须要开启通知时,我们能够在checkOperation中进行改动。
这里写图片描写叙述

   @Override
    public int checkOperation(int code, int uid, String packageName) {
        verifyIncomingUid(uid);
        verifyIncomingOp(code);
        synchronized (this) {
            if (isOpRestricted(uid, code, packageName)) {
                return AppOpsManager.MODE_IGNORED;
            }
            Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
            if (op == null) {
                return AppOpsManager.opToDefaultMode(code);
            }
            return op.mode;
        }
}

在checkOperation中进行推断时须要推断code值。否则easy出现点击重新启动的状况。改动后代码例如以下。

public int checkOperation(int code, int uid, String packageName) {
        verifyIncomingUid(uid);
        verifyIncomingOp(code);
        synchronized (this) {
         //chenzilong add for ZELY-41 block app notifications 20160331 start
         if(code == AppOpsManager.OP_POST_NOTIFICATION){
             if ((packageName.equals("com.advan.advanstore")||packageName.equals("com.stkj.android.freeshare"))){
                 return AppOpsManager.MODE_ALLOWED;
             }else{
                 return AppOpsManager.MODE_IGNORED;
             }
         }
         // chenzilong add for ZELY-41 block app notifications 20160331 end

            if (isOpRestricted(uid, code, packageName)) {
                return AppOpsManager.MODE_IGNORED;
            }
            Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
            if (op == null) {
                return AppOpsManager.opToDefaultMode(code);
            }
            return op.mode;
        }

这里写图片描写叙述
最后的实现结果

原文地址:https://www.cnblogs.com/zsychanpin/p/7182093.html