1 /* 2 * Copyright (C) 2024 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.modes; 18 19 import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID; 20 21 import android.content.Context; 22 import android.os.Bundle; 23 import android.util.ArraySet; 24 25 import androidx.annotation.NonNull; 26 import androidx.annotation.VisibleForTesting; 27 import androidx.core.text.BidiFormatter; 28 import androidx.fragment.app.Fragment; 29 import androidx.preference.Preference; 30 31 import com.android.settings.core.SubSettingLauncher; 32 import com.android.settings.notification.NotificationBackend; 33 import com.android.settingslib.applications.ApplicationsState; 34 35 import java.util.ArrayList; 36 import java.util.HashMap; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Set; 40 41 /** 42 * Preference with a link and summary about what apps can break through the mode 43 */ 44 class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceController { 45 46 private static final String TAG = "ZenModeAppsLinkPreferenceController"; 47 48 private final ZenModeSummaryHelper mSummaryHelper; 49 private ApplicationsState.Session mAppSession; 50 private NotificationBackend mNotificationBackend = new NotificationBackend(); 51 private ZenMode mZenMode; 52 private Preference mPreference; 53 ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host, ApplicationsState applicationsState, ZenModesBackend backend)54 ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host, 55 ApplicationsState applicationsState, ZenModesBackend backend) { 56 super(context, key, backend); 57 mSummaryHelper = new ZenModeSummaryHelper(mContext, mBackend); 58 if (applicationsState != null && host != null) { 59 mAppSession = applicationsState.newSession(mAppSessionCallbacks, host.getLifecycle()); 60 } 61 } 62 63 @Override updateState(Preference preference, @NonNull ZenMode zenMode)64 public void updateState(Preference preference, @NonNull ZenMode zenMode) { 65 Bundle bundle = new Bundle(); 66 bundle.putString(MODE_ID, zenMode.getId()); 67 // TODO(b/332937635): Update metrics category 68 preference.setIntent(new SubSettingLauncher(mContext) 69 .setDestination(ZenModeAppsFragment.class.getName()) 70 .setSourceMetricsCategory(0) 71 .setArguments(bundle) 72 .toIntent()); 73 mZenMode = zenMode; 74 mPreference = preference; 75 triggerUpdateAppsBypassingDndSummaryText(); 76 } 77 triggerUpdateAppsBypassingDndSummaryText()78 private void triggerUpdateAppsBypassingDndSummaryText() { 79 if (mAppSession == null) { 80 return; 81 } 82 83 ApplicationsState.AppFilter filter = android.multiuser.Flags.enablePrivateSpaceFeatures() 84 && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace() 85 ? ApplicationsState.FILTER_ENABLED_NOT_QUIET 86 : ApplicationsState.FILTER_ALL_ENABLED; 87 // We initiate a rebuild in the background here. Once the rebuild is completed, 88 // the onRebuildComplete() callback will be invoked, which will trigger the summary text 89 // to be initialized. 90 mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR, false); 91 } 92 updateAppsBypassingDndSummaryText(List<ApplicationsState.AppEntry> apps)93 private void updateAppsBypassingDndSummaryText(List<ApplicationsState.AppEntry> apps) { 94 Set<String> appNames = getAppsBypassingDnd(apps); 95 mPreference.setSummary(mSummaryHelper.getAppsSummary(mZenMode, appNames)); 96 } 97 98 @VisibleForTesting getAppsBypassingDnd(@onNull List<ApplicationsState.AppEntry> apps)99 ArraySet<String> getAppsBypassingDnd(@NonNull List<ApplicationsState.AppEntry> apps) { 100 ArraySet<String> appsBypassingDnd = new ArraySet<>(); 101 102 Map<String, String> pkgLabelMap = new HashMap<String, String>(); 103 for (ApplicationsState.AppEntry entry : apps) { 104 if (entry.info != null) { 105 pkgLabelMap.put(entry.info.packageName, entry.label); 106 } 107 } 108 for (String pkg : mNotificationBackend.getPackagesBypassingDnd(mContext.getUserId(), 109 /* includeConversationChannels= */ false)) { 110 // Settings may hide some packages from the user, so if they're not present here 111 // we skip displaying them, even if they bypass dnd. 112 if (pkgLabelMap.get(pkg) == null) { 113 continue; 114 } 115 appsBypassingDnd.add(BidiFormatter.getInstance().unicodeWrap(pkgLabelMap.get(pkg))); 116 } 117 return appsBypassingDnd; 118 } 119 120 @VisibleForTesting final ApplicationsState.Callbacks mAppSessionCallbacks = 121 new ApplicationsState.Callbacks() { 122 123 @Override 124 public void onRunningStateChanged(boolean running) { } 125 126 @Override 127 public void onPackageListChanged() { 128 triggerUpdateAppsBypassingDndSummaryText(); 129 } 130 131 @Override 132 public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) { 133 updateAppsBypassingDndSummaryText(apps); 134 } 135 136 @Override 137 public void onPackageIconChanged() { } 138 139 @Override 140 public void onPackageSizeChanged(String packageName) { } 141 142 @Override 143 public void onAllSizesComputed() { } 144 145 @Override 146 public void onLauncherInfoChanged() { } 147 148 @Override 149 public void onLoadEntriesCompleted() { 150 triggerUpdateAppsBypassingDndSummaryText(); 151 } 152 }; 153 } 154