1 /*
2  * Copyright (C) 2015 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.zen;
18 
19 import android.annotation.Nullable;
20 import android.app.NotificationManager;
21 import android.app.settings.SettingsEnums;
22 import android.content.Context;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.PackageItemInfo;
25 import android.content.pm.PackageManager;
26 import android.os.Bundle;
27 import android.util.ArraySet;
28 import android.view.View;
29 
30 import androidx.preference.PreferenceScreen;
31 
32 import com.android.settings.R;
33 import com.android.settings.applications.AppInfoBase;
34 import com.android.settings.applications.specialaccess.zenaccess.ZenAccessController;
35 import com.android.settings.applications.specialaccess.zenaccess.ZenAccessDetails;
36 import com.android.settings.applications.specialaccess.zenaccess.ZenAccessSettingObserverMixin;
37 import com.android.settings.search.BaseSearchIndexProvider;
38 import com.android.settings.widget.EmptyTextSettings;
39 import com.android.settingslib.search.SearchIndexable;
40 import com.android.settingslib.widget.apppreference.AppPreference;
41 
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.List;
45 import java.util.Set;
46 
47 @SearchIndexable
48 public class ZenAccessSettings extends EmptyTextSettings implements
49         ZenAccessSettingObserverMixin.Listener {
50     private final String TAG = "ZenAccessSettings";
51 
52     private Context mContext;
53     private PackageManager mPkgMan;
54     private NotificationManager mNoMan;
55 
56     @Override
getMetricsCategory()57     public int getMetricsCategory() {
58         return SettingsEnums.NOTIFICATION_ZEN_MODE_ACCESS;
59     }
60 
61     @Override
onCreate(Bundle icicle)62     public void onCreate(Bundle icicle) {
63         super.onCreate(icicle);
64 
65         mContext = getActivity();
66         mPkgMan = mContext.getPackageManager();
67         mNoMan = mContext.getSystemService(NotificationManager.class);
68         getSettingsLifecycle().addObserver(
69                 new ZenAccessSettingObserverMixin(getContext(), this /* listener */));
70     }
71 
72     @Override
onViewCreated(View view, @Nullable Bundle savedInstanceState)73     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
74         super.onViewCreated(view, savedInstanceState);
75         setEmptyText(R.string.zen_access_empty_text);
76     }
77 
78     @Override
getPreferenceScreenResId()79     protected int getPreferenceScreenResId() {
80         return R.xml.zen_access_settings;
81     }
82 
83     @Override
onResume()84     public void onResume() {
85         super.onResume();
86         reloadList();
87     }
88 
89     @Override
onZenAccessPolicyChanged()90     public void onZenAccessPolicyChanged() {
91         reloadList();
92     }
93 
reloadList()94     private void reloadList() {
95         final PreferenceScreen screen = getPreferenceScreen();
96         screen.removeAll();
97         final ArrayList<ApplicationInfo> apps = new ArrayList<>();
98         final Set<String> requesting =
99                 ZenAccessController.getPackagesRequestingNotificationPolicyAccess();
100         if (!requesting.isEmpty()) {
101             final List<ApplicationInfo> installed = mPkgMan.getInstalledApplications(0);
102             if (installed != null) {
103                 for (ApplicationInfo app : installed) {
104                     if (requesting.contains(app.packageName)) {
105                         apps.add(app);
106                     }
107                 }
108             }
109         }
110         ArraySet<String> autoApproved = new ArraySet<>();
111         autoApproved.addAll(mNoMan.getEnabledNotificationListenerPackages());
112         Collections.sort(apps, new PackageItemInfo.DisplayNameComparator(mPkgMan));
113         for (ApplicationInfo app : apps) {
114             final String pkg = app.packageName;
115             final CharSequence label = app.loadLabel(mPkgMan);
116             final AppPreference pref = new AppPreference(getPrefContext());
117             pref.setKey(pkg);
118             pref.setIcon(app.loadIcon(mPkgMan));
119             pref.setTitle(label);
120             if (autoApproved.contains(pkg)) {
121                 //Auto approved, user cannot do anything. Hard code summary and disable preference.
122                 pref.setEnabled(false);
123                 pref.setSummary(getString(R.string.zen_access_disabled_package_warning));
124             } else {
125                 // Not auto approved, update summary according to notification backend.
126                 pref.setSummary(getPreferenceSummary(pkg));
127             }
128             pref.setOnPreferenceClickListener(preference -> {
129                 AppInfoBase.startAppInfoFragment(
130                         ZenAccessDetails.class  /* fragment */,
131                         R.string.manage_zen_access_title /* titleRes */,
132                         pkg,
133                         app.uid,
134                         this /* source */,
135                         -1 /* requestCode */,
136                         getMetricsCategory() /* sourceMetricsCategory */);
137                 return true;
138             });
139 
140             screen.addPreference(pref);
141         }
142     }
143 
144     /**
145      * @return the summary for the current state of whether the app associated with the given
146      * {@param packageName} is allowed to enter picture-in-picture.
147      */
getPreferenceSummary(String packageName)148     private int getPreferenceSummary(String packageName) {
149         final boolean enabled = ZenAccessController.hasAccess(getContext(), packageName);
150         return enabled ? R.string.app_permission_summary_allowed
151                 : R.string.app_permission_summary_not_allowed;
152     }
153 
154     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
155             new BaseSearchIndexProvider(R.xml.zen_access_settings);
156 }
157