1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.applications.specialaccess.notificationaccess; 18 19 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; 20 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS; 21 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING; 22 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT; 23 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.pm.ServiceInfo; 27 import android.os.Build; 28 import android.service.notification.NotificationListenerFilter; 29 import android.service.notification.NotificationListenerService; 30 import android.text.TextUtils; 31 32 import androidx.preference.CheckBoxPreference; 33 import androidx.preference.Preference; 34 35 import com.android.settings.core.BasePreferenceController; 36 import com.android.settings.core.PreferenceControllerMixin; 37 import com.android.settings.notification.NotificationBackend; 38 39 public abstract class TypeFilterPreferenceController extends BasePreferenceController implements 40 PreferenceControllerMixin, Preference.OnPreferenceChangeListener { 41 42 private static final String TAG = "TypeFilterPrefCntlr"; 43 private static final String FLAG_SEPARATOR = "\\|"; 44 45 private ComponentName mCn; 46 private int mUserId; 47 private NotificationBackend mNm; 48 private NotificationListenerFilter mNlf; 49 private ServiceInfo mSi; 50 private int mTargetSdk; 51 TypeFilterPreferenceController(Context context, String key)52 public TypeFilterPreferenceController(Context context, String key) { 53 super(context, key); 54 } 55 setCn(ComponentName cn)56 public TypeFilterPreferenceController setCn(ComponentName cn) { 57 mCn = cn; 58 return this; 59 } 60 setUserId(int userId)61 public TypeFilterPreferenceController setUserId(int userId) { 62 mUserId = userId; 63 return this; 64 } 65 setNm(NotificationBackend nm)66 public TypeFilterPreferenceController setNm(NotificationBackend nm) { 67 mNm = nm; 68 return this; 69 } 70 setServiceInfo(ServiceInfo si)71 public TypeFilterPreferenceController setServiceInfo(ServiceInfo si) { 72 mSi = si; 73 return this; 74 } 75 setTargetSdk(int targetSdk)76 public TypeFilterPreferenceController setTargetSdk(int targetSdk) { 77 mTargetSdk = targetSdk; 78 return this; 79 } 80 getType()81 abstract protected int getType(); 82 83 @Override getAvailabilityStatus()84 public int getAvailabilityStatus() { 85 if (mNm.isNotificationListenerAccessGranted(mCn)) { 86 if (mTargetSdk > Build.VERSION_CODES.S) { 87 return AVAILABLE; 88 } 89 90 mNlf = mNm.getListenerFilter(mCn, mUserId); 91 if (!mNlf.areAllTypesAllowed() || !mNlf.getDisallowedPackages().isEmpty()) { 92 return AVAILABLE; 93 } 94 } 95 return DISABLED_DEPENDENT_SETTING; 96 } 97 hasFlag(int value, int flag)98 private boolean hasFlag(int value, int flag) { 99 return (value & flag) != 0; 100 } 101 102 @Override onPreferenceChange(Preference preference, Object newValue)103 public boolean onPreferenceChange(Preference preference, Object newValue) { 104 // retrieve latest in case the package filter has changed 105 mNlf = mNm.getListenerFilter(mCn, mUserId); 106 107 boolean enabled = (boolean) newValue; 108 109 int newFilter = mNlf.getTypes(); 110 if (enabled) { 111 newFilter |= getType(); 112 } else { 113 newFilter &= ~getType(); 114 } 115 mNlf.setTypes(newFilter); 116 mNm.setListenerFilter(mCn, mUserId, mNlf); 117 return true; 118 } 119 120 @Override updateState(Preference pref)121 public void updateState(Preference pref) { 122 mNlf = mNm.getListenerFilter(mCn, mUserId); 123 124 CheckBoxPreference check = (CheckBoxPreference) pref; 125 check.setChecked(hasFlag(mNlf.getTypes(), getType())); 126 127 boolean disableRequestedByApp = false; 128 if (mSi != null) { 129 if (mSi.metaData != null && mSi.metaData.containsKey( 130 NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES)) { 131 String typeList = mSi.metaData.get( 132 NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES).toString(); 133 if (typeList != null) { 134 int types = 0; 135 String[] typeStrings = typeList.split(FLAG_SEPARATOR); 136 for (int i = 0; i < typeStrings.length; i++) { 137 final String typeString = typeStrings[i]; 138 if (TextUtils.isEmpty(typeString)) { 139 continue; 140 } 141 if (typeString.equalsIgnoreCase("ONGOING")) { 142 types |= FLAG_FILTER_TYPE_ONGOING; 143 } else if (typeString.equalsIgnoreCase("CONVERSATIONS")) { 144 types |= FLAG_FILTER_TYPE_CONVERSATIONS; 145 } else if (typeString.equalsIgnoreCase("SILENT")) { 146 types |= FLAG_FILTER_TYPE_SILENT; 147 } else if (typeString.equalsIgnoreCase("ALERTING")) { 148 types |= FLAG_FILTER_TYPE_ALERTING; 149 } else { 150 try { 151 types |= Integer.parseInt(typeString); 152 } catch (NumberFormatException e) { 153 // skip 154 } 155 } 156 } 157 if (hasFlag(types, getType())) { 158 disableRequestedByApp = true; 159 } 160 } 161 } 162 } 163 // Apps can prevent a category from being turned on, but not turned off 164 boolean disabledByApp = disableRequestedByApp && !check.isChecked(); 165 pref.setEnabled(getAvailabilityStatus() == AVAILABLE && !disabledByApp); 166 } 167 }