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