1 /* 2 * Copyright (C) 2014 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; 18 19 import android.app.NotificationChannel; 20 import android.app.NotificationChannelGroup; 21 import android.content.Context; 22 import android.os.AsyncTask; 23 import android.os.Bundle; 24 import android.support.v14.preference.SwitchPreference; 25 import android.support.v7.preference.Preference; 26 import android.support.v7.preference.PreferenceCategory; 27 import android.support.v7.preference.PreferenceGroup; 28 import android.support.v7.preference.PreferenceScreen; 29 import android.text.TextUtils; 30 import android.util.Log; 31 32 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 33 import com.android.internal.widget.LockPatternUtils; 34 import com.android.settings.R; 35 import com.android.settings.widget.MasterCheckBoxPreference; 36 import com.android.settingslib.RestrictedSwitchPreference; 37 import com.android.settingslib.core.AbstractPreferenceController; 38 39 import java.util.ArrayList; 40 import java.util.Collections; 41 import java.util.Comparator; 42 import java.util.List; 43 44 /** These settings are per app, so should not be returned in global search results. */ 45 public class AppNotificationSettings extends NotificationSettingsBase { 46 private static final String TAG = "AppNotificationSettings"; 47 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 48 49 private static String KEY_GENERAL_CATEGORY = "categories"; 50 private static String KEY_ADVANCED_CATEGORY = "app_advanced"; 51 private static String KEY_BADGE = "badge"; 52 private static String KEY_APP_LINK = "app_link"; 53 54 private List<NotificationChannelGroup> mChannelGroupList; 55 56 @Override 57 public int getMetricsCategory() { 58 return MetricsEvent.NOTIFICATION_APP_NOTIFICATION; 59 } 60 61 @Override 62 public void onCreate(Bundle savedInstanceState) { 63 super.onCreate(savedInstanceState); 64 final PreferenceScreen screen = getPreferenceScreen(); 65 if (mShowLegacyChannelConfig && screen != null) { 66 // if showing legacy settings, pull advanced settings out of the advanced category 67 Preference badge = findPreference(KEY_BADGE); 68 Preference appLink = findPreference(KEY_APP_LINK); 69 removePreference(KEY_ADVANCED_CATEGORY); 70 if (badge != null) { 71 screen.addPreference(badge); 72 73 } 74 if (appLink != null) { 75 screen.addPreference(appLink); 76 } 77 } 78 } 79 80 @Override 81 public void onResume() { 82 super.onResume(); 83 84 if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null) { 85 Log.w(TAG, "Missing package or uid or packageinfo"); 86 finish(); 87 return; 88 } 89 90 if (!mShowLegacyChannelConfig) { 91 // Load channel settings 92 new AsyncTask<Void, Void, Void>() { 93 @Override 94 protected Void doInBackground(Void... unused) { 95 mChannelGroupList = mBackend.getGroups(mPkg, mUid).getList(); 96 Collections.sort(mChannelGroupList, mChannelGroupComparator); 97 return null; 98 } 99 100 @Override 101 protected void onPostExecute(Void unused) { 102 if (getHost() == null) { 103 return; 104 } 105 populateList(); 106 } 107 }.execute(); 108 } 109 110 for (NotificationPreferenceController controller : mControllers) { 111 controller.onResume(mAppRow, mChannel, mChannelGroup, mSuspendedAppsAdmin); 112 controller.displayPreference(getPreferenceScreen()); 113 } 114 updatePreferenceStates(); 115 } 116 117 @Override 118 protected String getLogTag() { 119 return TAG; 120 } 121 122 @Override 123 protected int getPreferenceScreenResId() { 124 return R.xml.app_notification_settings; 125 } 126 127 @Override 128 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 129 mControllers = new ArrayList<>(); 130 mControllers.add(new HeaderPreferenceController(context, this)); 131 mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend)); 132 mControllers.add(new BadgePreferenceController(context, mBackend)); 133 mControllers.add(new AllowSoundPreferenceController( 134 context, mImportanceListener, mBackend)); 135 mControllers.add(new ImportancePreferenceController( 136 context, mImportanceListener, mBackend)); 137 mControllers.add(new SoundPreferenceController(context, this, 138 mImportanceListener, mBackend)); 139 mControllers.add(new LightsPreferenceController(context, mBackend)); 140 mControllers.add(new VibrationPreferenceController(context, mBackend)); 141 mControllers.add(new VisibilityPreferenceController(context, new LockPatternUtils(context), 142 mBackend)); 143 mControllers.add(new DndPreferenceController(context, mBackend)); 144 mControllers.add(new AppLinkPreferenceController(context)); 145 mControllers.add(new DescriptionPreferenceController(context)); 146 mControllers.add(new NotificationsOffPreferenceController(context)); 147 mControllers.add(new DeletedChannelsPreferenceController(context, mBackend)); 148 return new ArrayList<>(mControllers); 149 } 150 151 private void populateList() { 152 if (!mDynamicPreferences.isEmpty()) { 153 for (Preference p : mDynamicPreferences) { 154 getPreferenceScreen().removePreference(p); 155 } 156 mDynamicPreferences.clear(); 157 } 158 if (mChannelGroupList.isEmpty()) { 159 PreferenceCategory groupCategory = new PreferenceCategory(getPrefContext()); 160 groupCategory.setTitle(R.string.notification_channels); 161 groupCategory.setKey(KEY_GENERAL_CATEGORY); 162 getPreferenceScreen().addPreference(groupCategory); 163 mDynamicPreferences.add(groupCategory); 164 165 Preference empty = new Preference(getPrefContext()); 166 empty.setTitle(R.string.no_channels); 167 empty.setEnabled(false); 168 groupCategory.addPreference(empty); 169 } else { 170 populateGroupList(); 171 mImportanceListener.onImportanceChanged(); 172 } 173 } 174 175 private void populateGroupList() { 176 for (NotificationChannelGroup group : mChannelGroupList) { 177 PreferenceCategory groupCategory = new PreferenceCategory(getPrefContext()); 178 groupCategory.setOrderingAsAdded(true); 179 getPreferenceScreen().addPreference(groupCategory); 180 mDynamicPreferences.add(groupCategory); 181 if (group.getId() == null) { 182 if (mChannelGroupList.size() > 1) { 183 groupCategory.setTitle(R.string.notification_channels_other); 184 } 185 groupCategory.setKey(KEY_GENERAL_CATEGORY); 186 } else { 187 groupCategory.setTitle(group.getName()); 188 groupCategory.setKey(group.getId()); 189 populateGroupToggle(groupCategory, group); 190 } 191 if (!group.isBlocked()) { 192 final List<NotificationChannel> channels = group.getChannels(); 193 Collections.sort(channels, mChannelComparator); 194 int N = channels.size(); 195 for (int i = 0; i < N; i++) { 196 final NotificationChannel channel = channels.get(i); 197 populateSingleChannelPrefs(groupCategory, channel, group.isBlocked()); 198 } 199 } 200 } 201 } 202 203 protected void populateGroupToggle(final PreferenceGroup parent, 204 NotificationChannelGroup group) { 205 RestrictedSwitchPreference preference = new RestrictedSwitchPreference(getPrefContext()); 206 preference.setTitle(R.string.notification_switch_label); 207 preference.setEnabled(mSuspendedAppsAdmin == null 208 && isChannelGroupBlockable(group)); 209 preference.setChecked(!group.isBlocked()); 210 preference.setOnPreferenceClickListener(preference1 -> { 211 final boolean allowGroup = ((SwitchPreference) preference1).isChecked(); 212 group.setBlocked(!allowGroup); 213 mBackend.updateChannelGroup(mAppRow.pkg, mAppRow.uid, group); 214 215 onGroupBlockStateChanged(group); 216 return true; 217 }); 218 219 parent.addPreference(preference); 220 } 221 222 private Comparator<NotificationChannelGroup> mChannelGroupComparator = 223 new Comparator<NotificationChannelGroup>() { 224 225 @Override 226 public int compare(NotificationChannelGroup left, NotificationChannelGroup right) { 227 // Non-grouped channels (in placeholder group with a null id) come last 228 if (left.getId() == null && right.getId() != null) { 229 return 1; 230 } else if (right.getId() == null && left.getId() != null) { 231 return -1; 232 } 233 return left.getId().compareTo(right.getId()); 234 } 235 }; 236 237 protected void onGroupBlockStateChanged(NotificationChannelGroup group) { 238 if (group == null) { 239 return; 240 } 241 PreferenceGroup groupGroup = ( 242 PreferenceGroup) getPreferenceScreen().findPreference(group.getId()); 243 244 if (groupGroup != null) { 245 if (group.isBlocked()) { 246 List<Preference> toRemove = new ArrayList<>(); 247 int childCount = groupGroup.getPreferenceCount(); 248 for (int i = 0; i < childCount; i++) { 249 Preference pref = groupGroup.getPreference(i); 250 if (pref instanceof MasterCheckBoxPreference) { 251 toRemove.add(pref); 252 } 253 } 254 for (Preference pref : toRemove) { 255 groupGroup.removePreference(pref); 256 } 257 } else { 258 final List<NotificationChannel> channels = group.getChannels(); 259 Collections.sort(channels, mChannelComparator); 260 int N = channels.size(); 261 for (int i = 0; i < N; i++) { 262 final NotificationChannel channel = channels.get(i); 263 populateSingleChannelPrefs(groupGroup, channel, group.isBlocked()); 264 } 265 } 266 } 267 } 268 269 } 270