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