1 /*
2  * Copyright (C) 2017 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.tv.settings.device.apps.specialaccess;
18 
19 import android.app.NotificationManager;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.pm.PackageItemInfo;
23 import android.content.pm.PackageManager;
24 import android.content.pm.ServiceInfo;
25 import android.os.Bundle;
26 import android.os.UserHandle;
27 import android.provider.Settings;
28 import android.service.notification.NotificationListenerService;
29 import android.util.IconDrawableFactory;
30 import android.util.Log;
31 
32 import androidx.annotation.Keep;
33 import androidx.preference.Preference;
34 import androidx.preference.PreferenceScreen;
35 import androidx.preference.SwitchPreference;
36 
37 import com.android.internal.logging.nano.MetricsProto;
38 import com.android.settingslib.applications.ServiceListing;
39 import com.android.tv.settings.R;
40 import com.android.tv.settings.SettingsPreferenceFragment;
41 
42 import java.util.List;
43 
44 /**
45  * Settings screen for managing notification listener permissions
46  */
47 @Keep
48 public class NotificationAccess extends SettingsPreferenceFragment {
49     private static final String TAG = "NotificationAccess";
50 
51     private static final String HEADER_KEY = "header";
52 
53     private NotificationManager mNotificationManager;
54     private PackageManager mPackageManager;
55     private ServiceListing mServiceListing;
56     private IconDrawableFactory mIconDrawableFactory;
57 
58     @Override
getMetricsCategory()59     public int getMetricsCategory() {
60         return MetricsProto.MetricsEvent.NOTIFICATION_ACCESS;
61     }
62 
63     @Override
onAttach(Context context)64     public void onAttach(Context context) {
65         super.onAttach(context);
66         mPackageManager = context.getPackageManager();
67         mNotificationManager = context.getSystemService(NotificationManager.class);
68         mIconDrawableFactory = IconDrawableFactory.newInstance(context);
69     }
70 
71     @Override
onCreate(Bundle savedInstanceState)72     public void onCreate(Bundle savedInstanceState) {
73         super.onCreate(savedInstanceState);
74         mServiceListing = new ServiceListing.Builder(getContext())
75                 .setTag(TAG)
76                 .setSetting(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS)
77                 .setIntentAction(NotificationListenerService.SERVICE_INTERFACE)
78                 .setPermission(android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE)
79                 .setNoun("notification listener")
80                 .build();
81         mServiceListing.addCallback(this::updateList);
82     }
83 
84     @Override
onResume()85     public void onResume() {
86         super.onResume();
87         mServiceListing.reload();
88         mServiceListing.setListening(true);
89     }
90 
91     @Override
onPause()92     public void onPause() {
93         super.onPause();
94         mServiceListing.setListening(false);
95     }
96 
97     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)98     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
99         setPreferencesFromResource(R.xml.notification_access, null);
100     }
101 
updateList(List<ServiceInfo> services)102     private void updateList(List<ServiceInfo> services) {
103         final PreferenceScreen screen = getPreferenceScreen();
104         final Preference header = screen.findPreference(HEADER_KEY);
105         screen.removeAll();
106         if (header != null) {
107             screen.addPreference(header);
108         }
109         services.sort(new PackageItemInfo.DisplayNameComparator(mPackageManager));
110         for (ServiceInfo service : services) {
111             final ComponentName cn = new ComponentName(service.packageName, service.name);
112             CharSequence title = null;
113             try {
114                 title = mPackageManager.getApplicationInfo(
115                         service.packageName, 0).loadLabel(mPackageManager);
116             } catch (PackageManager.NameNotFoundException e) {
117                 // unlikely, as we are iterating over live services.
118                 Log.w(TAG, "can't find package name", e);
119             }
120             final String summary = service.loadLabel(mPackageManager).toString();
121             final SwitchPreference pref = new SwitchPreference(getPreferenceManager().getContext());
122             pref.setPersistent(false);
123             pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo,
124                     UserHandle.getUserId(service.applicationInfo.uid)));
125             if (title != null && !title.equals(summary)) {
126                 pref.setTitle(title);
127                 pref.setSummary(summary);
128             } else {
129                 pref.setTitle(summary);
130             }
131             pref.setKey(cn.flattenToString());
132             pref.setChecked(mNotificationManager.isNotificationListenerAccessGranted(cn));
133             pref.setOnPreferenceChangeListener((preference, newValue) -> {
134                 final boolean enable = (boolean) newValue;
135                 mNotificationManager.setNotificationListenerAccessGranted(cn, enable);
136                 return true;
137             });
138             screen.addPreference(pref);
139         }
140         if (services.isEmpty()) {
141             final Preference preference = new Preference(getPreferenceManager().getContext());
142             preference.setTitle(R.string.no_notification_listeners);
143         }
144     }
145 
146 }
147