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