1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 package com.android.settings.applications.specialaccess.notificationaccess; 15 16 import android.content.ComponentName; 17 import android.content.Context; 18 import android.content.pm.VersionedPackage; 19 import android.service.notification.NotificationListenerFilter; 20 21 import androidx.preference.CheckBoxPreference; 22 import androidx.preference.Preference; 23 import androidx.preference.PreferenceScreen; 24 25 import com.android.settings.applications.AppStateBaseBridge; 26 import com.android.settings.core.BasePreferenceController; 27 import com.android.settings.notification.NotificationBackend; 28 import com.android.settings.widget.AppCheckBoxPreference; 29 import com.android.settingslib.applications.AppUtils; 30 import com.android.settingslib.applications.ApplicationsState; 31 import com.android.settingslib.applications.ApplicationsState.AppEntry; 32 import com.android.settingslib.applications.ApplicationsState.AppFilter; 33 import com.android.settingslib.core.lifecycle.Lifecycle; 34 import com.android.settingslib.core.lifecycle.LifecycleObserver; 35 36 import java.util.ArrayList; 37 import java.util.Set; 38 import java.util.TreeSet; 39 40 41 public class BridgedAppsPreferenceController extends BasePreferenceController implements 42 LifecycleObserver, ApplicationsState.Callbacks, 43 AppStateBaseBridge.Callback { 44 45 private ApplicationsState mApplicationsState; 46 private ApplicationsState.Session mSession; 47 private AppFilter mFilter; 48 private PreferenceScreen mScreen; 49 50 private ComponentName mCn; 51 private int mUserId; 52 private NotificationBackend mNm; 53 private NotificationListenerFilter mNlf; 54 BridgedAppsPreferenceController(Context context, String key)55 public BridgedAppsPreferenceController(Context context, String key) { 56 super(context, key); 57 } 58 setAppState(ApplicationsState appState)59 public BridgedAppsPreferenceController setAppState(ApplicationsState appState) { 60 mApplicationsState = appState; 61 return this; 62 } 63 setCn(ComponentName cn)64 public BridgedAppsPreferenceController setCn(ComponentName cn) { 65 mCn = cn; 66 return this; 67 } 68 setUserId(int userId)69 public BridgedAppsPreferenceController setUserId(int userId) { 70 mUserId = userId; 71 return this; 72 } 73 setNm(NotificationBackend nm)74 public BridgedAppsPreferenceController setNm(NotificationBackend nm) { 75 mNm = nm; 76 return this; 77 } 78 setFilter(AppFilter filter)79 public BridgedAppsPreferenceController setFilter(AppFilter filter) { 80 mFilter = filter; 81 return this; 82 } 83 setSession(Lifecycle lifecycle)84 public BridgedAppsPreferenceController setSession(Lifecycle lifecycle) { 85 mSession = mApplicationsState.newSession(this, lifecycle); 86 return this; 87 } 88 89 @Override displayPreference(PreferenceScreen screen)90 public void displayPreference(PreferenceScreen screen) { 91 mScreen = screen; 92 } 93 94 @Override getAvailabilityStatus()95 public int getAvailabilityStatus() { 96 return AVAILABLE; 97 } 98 99 100 @Override onExtraInfoUpdated()101 public void onExtraInfoUpdated() { 102 rebuild(); 103 } 104 105 @Override onRunningStateChanged(boolean running)106 public void onRunningStateChanged(boolean running) { 107 108 } 109 110 @Override onPackageListChanged()111 public void onPackageListChanged() { 112 rebuild(); 113 } 114 115 @Override onRebuildComplete(ArrayList<AppEntry> apps)116 public void onRebuildComplete(ArrayList<AppEntry> apps) { 117 if (apps == null) { 118 return; 119 } 120 mNlf = mNm.getListenerFilter(mCn, mUserId); 121 122 // Create apps key set for removing useless preferences 123 final Set<String> appsKeySet = new TreeSet<>(); 124 // Add or update preferences 125 final int N = apps.size(); 126 for (int i = 0; i < N; i++) { 127 final AppEntry entry = apps.get(i); 128 final String prefKey = entry.info.packageName + "|" + entry.info.uid; 129 appsKeySet.add(prefKey); 130 AppCheckBoxPreference preference = mScreen.findPreference(prefKey); 131 if (preference == null) { 132 preference = new AppCheckBoxPreference(mScreen.getContext()); 133 preference.setIcon(AppUtils.getIcon(mContext, entry)); 134 preference.setTitle(entry.label); 135 preference.setKey(prefKey); 136 mScreen.addPreference(preference); 137 } 138 preference.setOrder(i); 139 preference.setChecked(mNlf.isPackageAllowed( 140 new VersionedPackage(entry.info.packageName, entry.info.uid))); 141 preference.setOnPreferenceChangeListener(this::onPreferenceChange); 142 } 143 144 // Remove preferences that are no longer existing in the updated list of apps 145 removeUselessPrefs(appsKeySet); 146 } 147 148 @Override onPackageIconChanged()149 public void onPackageIconChanged() { 150 rebuild(); 151 } 152 153 @Override onPackageSizeChanged(String packageName)154 public void onPackageSizeChanged(String packageName) { 155 156 } 157 158 @Override onAllSizesComputed()159 public void onAllSizesComputed() { 160 } 161 162 @Override onLauncherInfoChanged()163 public void onLauncherInfoChanged() { 164 } 165 166 @Override onLoadEntriesCompleted()167 public void onLoadEntriesCompleted() { 168 rebuild(); 169 } 170 onPreferenceChange(Preference preference, Object newValue)171 public boolean onPreferenceChange(Preference preference, Object newValue) { 172 if (preference instanceof CheckBoxPreference) { 173 String packageName = preference.getKey().substring(0, preference.getKey().indexOf("|")); 174 int uid = Integer.parseInt(preference.getKey().substring( 175 preference.getKey().indexOf("|") + 1)); 176 boolean allowlisted = newValue == Boolean.TRUE; 177 mNlf = mNm.getListenerFilter(mCn, mUserId); 178 if (allowlisted) { 179 mNlf.removePackage(new VersionedPackage(packageName, uid)); 180 } else { 181 mNlf.addPackage(new VersionedPackage(packageName, uid)); 182 } 183 mNm.setListenerFilter(mCn, mUserId, mNlf); 184 return true; 185 } 186 return false; 187 } 188 rebuild()189 public void rebuild() { 190 final ArrayList<AppEntry> apps = mSession.rebuild(mFilter, 191 ApplicationsState.ALPHA_COMPARATOR); 192 if (apps != null) { 193 onRebuildComplete(apps); 194 } 195 } 196 removeUselessPrefs(final Set<String> appsKeySet)197 private void removeUselessPrefs(final Set<String> appsKeySet) { 198 final int prefCount = mScreen.getPreferenceCount(); 199 String prefKey; 200 if (prefCount > 0) { 201 for (int i = prefCount - 1; i >= 0; i--) { 202 Preference pref = mScreen.getPreference(i); 203 prefKey = pref.getKey(); 204 if (!appsKeySet.contains(prefKey)) { 205 mScreen.removePreference(pref); 206 } 207 } 208 } 209 } 210 } 211