1 /* 2 * Copyright (C) 2017 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.notification.app; 18 19 import static android.app.NotificationManager.IMPORTANCE_NONE; 20 import static android.os.UserHandle.USER_SYSTEM; 21 22 import android.annotation.Nullable; 23 import android.app.NotificationChannel; 24 import android.app.NotificationChannelGroup; 25 import android.app.NotificationManager; 26 import android.content.Context; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.ShortcutInfo; 30 import android.graphics.drawable.Drawable; 31 import android.os.UserManager; 32 import android.provider.Settings; 33 import android.util.Log; 34 35 import androidx.preference.Preference; 36 37 import com.android.settings.notification.NotificationBackend; 38 import com.android.settingslib.RestrictedLockUtils; 39 import com.android.settingslib.core.AbstractPreferenceController; 40 41 import java.util.Comparator; 42 import java.util.List; 43 import java.util.Objects; 44 45 /** 46 * Parent class for preferences appearing on notification setting pages at the app, 47 * notification channel group, or notification channel level. 48 */ 49 public abstract class NotificationPreferenceController extends AbstractPreferenceController { 50 private static final String TAG = "ChannelPrefContr"; 51 @Nullable 52 protected NotificationChannel mChannel; 53 @Nullable 54 protected NotificationChannelGroup mChannelGroup; 55 protected RestrictedLockUtils.EnforcedAdmin mAdmin; 56 protected NotificationBackend.AppRow mAppRow; 57 protected final NotificationManager mNm; 58 protected final NotificationBackend mBackend; 59 protected final Context mContext; 60 protected final UserManager mUm; 61 protected final PackageManager mPm; 62 protected Preference mPreference; 63 @Nullable 64 protected Drawable mConversationDrawable; 65 @Nullable 66 protected ShortcutInfo mConversationInfo; 67 protected List<String> mPreferenceFilter; 68 69 boolean overrideCanBlock; 70 boolean overrideCanConfigure; 71 boolean overrideCanBlockValue; 72 boolean overrideCanConfigureValue; 73 NotificationPreferenceController(Context context, NotificationBackend backend)74 public NotificationPreferenceController(Context context, NotificationBackend backend) { 75 super(context); 76 mContext = context; 77 mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 78 mBackend = backend; 79 mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 80 mPm = mContext.getPackageManager(); 81 } 82 83 /** 84 * Returns true if field's parent object is not blocked. 85 */ 86 @Override isAvailable()87 public boolean isAvailable() { 88 if (mAppRow == null) { 89 return false; 90 } 91 if (mAppRow.banned) { 92 return false; 93 } 94 if (mChannelGroup != null) { 95 if (mChannelGroup.isBlocked()) { 96 return false; 97 } 98 } 99 if (mChannel != null) { 100 if (mPreferenceFilter != null && !isIncludedInFilter()) { 101 return false; 102 } 103 return mChannel.getImportance() != IMPORTANCE_NONE; 104 } 105 return true; 106 } 107 onResume(NotificationBackend.AppRow appRow, @Nullable NotificationChannel channel, @Nullable NotificationChannelGroup group, Drawable conversationDrawable, ShortcutInfo conversationInfo, RestrictedLockUtils.EnforcedAdmin admin, List<String> preferenceFilter)108 protected void onResume(NotificationBackend.AppRow appRow, 109 @Nullable NotificationChannel channel, @Nullable NotificationChannelGroup group, 110 Drawable conversationDrawable, 111 ShortcutInfo conversationInfo, 112 RestrictedLockUtils.EnforcedAdmin admin, 113 List<String> preferenceFilter) { 114 mAppRow = appRow; 115 mChannel = channel; 116 mChannelGroup = group; 117 mAdmin = admin; 118 mConversationDrawable = conversationDrawable; 119 mConversationInfo = conversationInfo; 120 mPreferenceFilter = preferenceFilter; 121 } 122 isIncludedInFilter()123 abstract boolean isIncludedInFilter(); 124 checkCanBeVisible(int minImportanceVisible)125 protected boolean checkCanBeVisible(int minImportanceVisible) { 126 if (mChannel == null) { 127 Log.w(TAG, "No channel"); 128 return false; 129 } 130 131 int importance = mChannel.getImportance(); 132 if (importance == NotificationManager.IMPORTANCE_UNSPECIFIED) { 133 return true; 134 } 135 return importance >= minImportanceVisible; 136 } 137 saveChannel()138 protected void saveChannel() { 139 if (mChannel != null && mAppRow != null) { 140 mBackend.updateChannel(mAppRow.pkg, mAppRow.uid, mChannel); 141 } 142 } 143 isChannelBlockable()144 protected boolean isChannelBlockable() { 145 return isChannelBlockable(mChannel); 146 } 147 isChannelBlockable(NotificationChannel channel)148 protected boolean isChannelBlockable(NotificationChannel channel) { 149 if (overrideCanBlock) { 150 return overrideCanBlockValue; 151 } 152 if (overrideCanConfigure) { 153 return overrideCanConfigureValue; 154 } 155 if (channel != null && mAppRow != null) { 156 boolean locked = mAppRow.lockedImportance; 157 if (locked) { 158 return channel.isBlockable() || channel.getImportance() == IMPORTANCE_NONE; 159 } 160 161 return channel.isBlockable() || !mAppRow.systemApp 162 || channel.getImportance() == IMPORTANCE_NONE; 163 } 164 return false; 165 } 166 isAppBlockable()167 protected boolean isAppBlockable() { 168 if (overrideCanBlock) { 169 return overrideCanBlockValue; 170 } 171 if (overrideCanConfigure) { 172 return overrideCanConfigureValue; 173 } 174 if (mAppRow != null) { 175 boolean systemBlockable = !mAppRow.systemApp || (mAppRow.systemApp && mAppRow.banned); 176 return systemBlockable && !mAppRow.lockedImportance; 177 } 178 return true; 179 } 180 isChannelConfigurable(NotificationChannel channel)181 protected boolean isChannelConfigurable(NotificationChannel channel) { 182 if (overrideCanConfigure) { 183 return overrideCanConfigureValue; 184 } 185 if (channel != null && mAppRow != null) { 186 boolean locked = mAppRow.lockedImportance; 187 return !locked || channel.isBlockable(); 188 } 189 return false; 190 } 191 isChannelGroupBlockable()192 protected boolean isChannelGroupBlockable() { 193 return isChannelGroupBlockable(mChannelGroup); 194 } 195 isChannelGroupBlockable(NotificationChannelGroup group)196 protected boolean isChannelGroupBlockable(NotificationChannelGroup group) { 197 if (overrideCanBlock) { 198 return overrideCanBlockValue; 199 } 200 if (overrideCanConfigure) { 201 return overrideCanConfigureValue; 202 } 203 if (group != null && mAppRow != null) { 204 if (!mAppRow.systemApp && !mAppRow.lockedImportance) { 205 return true; 206 } 207 208 return group.isBlocked(); 209 } 210 return false; 211 } 212 hasValidGroup()213 protected boolean hasValidGroup() { 214 return mChannelGroup != null; 215 } 216 isDefaultChannel()217 protected final boolean isDefaultChannel() { 218 if (mChannel == null) { 219 return false; 220 } 221 return Objects.equals(NotificationChannel.DEFAULT_CHANNEL_ID, mChannel.getId()); 222 } 223 setOverrideCanBlock(boolean canBlock)224 protected final void setOverrideCanBlock(boolean canBlock) { 225 overrideCanBlock = true; 226 overrideCanBlockValue = canBlock; 227 } 228 setOverrideCanConfigure(boolean canConfigure)229 protected final void setOverrideCanConfigure(boolean canConfigure) { 230 overrideCanConfigure = true; 231 overrideCanConfigureValue = canConfigure; 232 } 233 234 public static final Comparator<NotificationChannelGroup> CHANNEL_GROUP_COMPARATOR = 235 new Comparator<NotificationChannelGroup>() { 236 @Override 237 public int compare(NotificationChannelGroup left, NotificationChannelGroup right) { 238 // Non-grouped channels (in placeholder group with a null id) come last 239 if (left.getId() == null && right.getId() != null) { 240 return 1; 241 } else if (right.getId() == null && left.getId() != null) { 242 return -1; 243 } 244 return left.getId().compareTo(right.getId()); 245 } 246 }; 247 248 public static final Comparator<NotificationChannel> CHANNEL_COMPARATOR = (left, right) -> { 249 if (left.isDeleted() != right.isDeleted()) { 250 return Boolean.compare(left.isDeleted(), right.isDeleted()); 251 } else if (left.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) { 252 // Uncategorized/miscellaneous legacy channel goes last 253 return 1; 254 } else if (right.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) { 255 return -1; 256 } 257 258 return left.getId().compareTo(right.getId()); 259 }; 260 } 261