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.settingslib.applications.ApplicationsState; 29 import com.android.settingslib.applications.ApplicationsState.AppEntry; 30 import com.android.settingslib.applications.ApplicationsState.AppFilter; 31 import com.android.settingslib.core.lifecycle.Lifecycle; 32 import com.android.settingslib.core.lifecycle.LifecycleObserver; 33 34 import java.util.ArrayList; 35 import java.util.Set; 36 import java.util.TreeSet; 37 38 39 public class BridgedAppsPreferenceController extends BasePreferenceController implements 40 LifecycleObserver, ApplicationsState.Callbacks, 41 AppStateBaseBridge.Callback { 42 43 private ApplicationsState mApplicationsState; 44 private ApplicationsState.Session mSession; 45 private AppFilter mFilter; 46 private PreferenceScreen mScreen; 47 48 private ComponentName mCn; 49 private int mUserId; 50 private NotificationBackend mNm; 51 private NotificationListenerFilter mNlf; 52 BridgedAppsPreferenceController(Context context, String key)53 public BridgedAppsPreferenceController(Context context, String key) { 54 super(context, key); 55 } 56 setAppState(ApplicationsState appState)57 public BridgedAppsPreferenceController setAppState(ApplicationsState appState) { 58 mApplicationsState = appState; 59 return this; 60 } 61 setCn(ComponentName cn)62 public BridgedAppsPreferenceController setCn(ComponentName cn) { 63 mCn = cn; 64 return this; 65 } 66 setUserId(int userId)67 public BridgedAppsPreferenceController setUserId(int userId) { 68 mUserId = userId; 69 return this; 70 } 71 setNm(NotificationBackend nm)72 public BridgedAppsPreferenceController setNm(NotificationBackend nm) { 73 mNm = nm; 74 return this; 75 } 76 setFilter(AppFilter filter)77 public BridgedAppsPreferenceController setFilter(AppFilter filter) { 78 mFilter = filter; 79 return this; 80 } 81 setSession(Lifecycle lifecycle)82 public BridgedAppsPreferenceController setSession(Lifecycle lifecycle) { 83 mSession = mApplicationsState.newSession(this, lifecycle); 84 return this; 85 } 86 87 @Override displayPreference(PreferenceScreen screen)88 public void displayPreference(PreferenceScreen screen) { 89 mScreen = screen; 90 } 91 92 @Override getAvailabilityStatus()93 public int getAvailabilityStatus() { 94 return AVAILABLE; 95 } 96 97 98 @Override onExtraInfoUpdated()99 public void onExtraInfoUpdated() { 100 rebuild(); 101 } 102 103 @Override onRunningStateChanged(boolean running)104 public void onRunningStateChanged(boolean running) { 105 106 } 107 108 @Override onPackageListChanged()109 public void onPackageListChanged() { 110 rebuild(); 111 } 112 113 @Override onRebuildComplete(ArrayList<AppEntry> apps)114 public void onRebuildComplete(ArrayList<AppEntry> apps) { 115 if (apps == null) { 116 return; 117 } 118 mNlf = mNm.getListenerFilter(mCn, mUserId); 119 120 // Create apps key set for removing useless preferences 121 final Set<String> appsKeySet = new TreeSet<>(); 122 // Add or update preferences 123 final int N = apps.size(); 124 for (int i = 0; i < N; i++) { 125 final AppEntry entry = apps.get(i); 126 final String prefKey = entry.info.packageName + "|" + entry.info.uid; 127 appsKeySet.add(prefKey); 128 CheckBoxPreference preference = mScreen.findPreference(prefKey); 129 if (preference == null) { 130 preference = new CheckBoxPreference(mScreen.getContext()); 131 preference.setIcon(entry.icon); 132 preference.setTitle(entry.label); 133 preference.setKey(prefKey); 134 mScreen.addPreference(preference); 135 } 136 preference.setOrder(i); 137 preference.setChecked(mNlf.isPackageAllowed( 138 new VersionedPackage(entry.info.packageName, entry.info.uid))); 139 preference.setOnPreferenceChangeListener(this::onPreferenceChange); 140 } 141 142 // Remove preferences that are no longer existing in the updated list of apps 143 removeUselessPrefs(appsKeySet); 144 } 145 146 @Override onPackageIconChanged()147 public void onPackageIconChanged() { 148 rebuild(); 149 } 150 151 @Override onPackageSizeChanged(String packageName)152 public void onPackageSizeChanged(String packageName) { 153 154 } 155 156 @Override onAllSizesComputed()157 public void onAllSizesComputed() { 158 } 159 160 @Override onLauncherInfoChanged()161 public void onLauncherInfoChanged() { 162 } 163 164 @Override onLoadEntriesCompleted()165 public void onLoadEntriesCompleted() { 166 rebuild(); 167 } 168 onPreferenceChange(Preference preference, Object newValue)169 public boolean onPreferenceChange(Preference preference, Object newValue) { 170 if (preference instanceof CheckBoxPreference) { 171 String packageName = preference.getKey().substring(0, preference.getKey().indexOf("|")); 172 int uid = Integer.parseInt(preference.getKey().substring( 173 preference.getKey().indexOf("|") + 1)); 174 boolean allowlisted = newValue == Boolean.TRUE; 175 mNlf = mNm.getListenerFilter(mCn, mUserId); 176 if (allowlisted) { 177 mNlf.removePackage(new VersionedPackage(packageName, uid)); 178 } else { 179 mNlf.addPackage(new VersionedPackage(packageName, uid)); 180 } 181 mNm.setListenerFilter(mCn, mUserId, mNlf); 182 return true; 183 } 184 return false; 185 } 186 rebuild()187 public void rebuild() { 188 final ArrayList<AppEntry> apps = mSession.rebuild(mFilter, 189 ApplicationsState.ALPHA_COMPARATOR); 190 if (apps != null) { 191 onRebuildComplete(apps); 192 } 193 } 194 removeUselessPrefs(final Set<String> appsKeySet)195 private void removeUselessPrefs(final Set<String> appsKeySet) { 196 final int prefCount = mScreen.getPreferenceCount(); 197 String prefKey; 198 if (prefCount > 0) { 199 for (int i = prefCount - 1; i >= 0; i--) { 200 Preference pref = mScreen.getPreference(i); 201 prefKey = pref.getKey(); 202 if (!appsKeySet.contains(prefKey)) { 203 mScreen.removePreference(pref); 204 } 205 } 206 } 207 } 208 } 209