• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.packageinstaller.permission.ui.television;
17 
18 import android.app.ActionBar;
19 import android.app.AlertDialog;
20 import android.app.Fragment;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.content.DialogInterface.OnClickListener;
24 import android.content.Intent;
25 import android.graphics.drawable.Drawable;
26 import android.os.Bundle;
27 import android.support.v14.preference.SwitchPreference;
28 import android.support.v4.util.ArrayMap;
29 import android.support.v7.preference.Preference;
30 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
31 import android.support.v7.preference.Preference.OnPreferenceClickListener;
32 import android.support.v7.preference.PreferenceScreen;
33 import android.util.ArraySet;
34 import android.view.Menu;
35 import android.view.MenuInflater;
36 import android.view.MenuItem;
37 import android.view.View;
38 import android.widget.TextView;
39 
40 import com.android.packageinstaller.DeviceUtils;
41 import com.android.packageinstaller.R;
42 import com.android.packageinstaller.permission.model.AppPermissionGroup;
43 import com.android.packageinstaller.permission.model.PermissionApps;
44 import com.android.packageinstaller.permission.model.PermissionApps.Callback;
45 import com.android.packageinstaller.permission.model.PermissionApps.PermissionApp;
46 import com.android.packageinstaller.permission.ui.ReviewPermissionsActivity;
47 import com.android.packageinstaller.permission.utils.LocationUtils;
48 import com.android.packageinstaller.permission.utils.SafetyNetLogger;
49 import com.android.packageinstaller.permission.utils.Utils;
50 
51 import java.util.ArrayList;
52 import java.util.List;
53 
54 public final class PermissionAppsFragment extends SettingsWithHeader implements Callback,
55         OnPreferenceChangeListener {
56 
57     private static final int MENU_SHOW_SYSTEM = Menu.FIRST;
58     private static final int MENU_HIDE_SYSTEM = Menu.FIRST + 1;
59     private static final String KEY_SHOW_SYSTEM_PREFS = "_showSystem";
60 
newInstance(String permissionName)61     public static PermissionAppsFragment newInstance(String permissionName) {
62         return setPermissionName(new PermissionAppsFragment(), permissionName);
63     }
64 
setPermissionName(T fragment, String permissionName)65     private static <T extends Fragment> T setPermissionName(T fragment, String permissionName) {
66         Bundle arguments = new Bundle();
67         arguments.putString(Intent.EXTRA_PERMISSION_NAME, permissionName);
68         fragment.setArguments(arguments);
69         return fragment;
70     }
71 
72     private PermissionApps mPermissionApps;
73 
74     private PreferenceScreen mExtraScreen;
75 
76     private ArrayMap<String, AppPermissionGroup> mToggledGroups;
77     private ArraySet<String> mLauncherPkgs;
78     private boolean mHasConfirmedRevoke;
79 
80     private boolean mShowSystem;
81     private boolean mHasSystemApps;
82     private MenuItem mShowSystemMenu;
83     private MenuItem mHideSystemMenu;
84 
85     private Callback mOnPermissionsLoadedListener;
86 
87     @Override
onCreate(Bundle savedInstanceState)88     public void onCreate(Bundle savedInstanceState) {
89         super.onCreate(savedInstanceState);
90         setLoading(true /* loading */, false /* animate */);
91         setHasOptionsMenu(true);
92         final ActionBar ab = getActivity().getActionBar();
93         if (ab != null) {
94             ab.setDisplayHomeAsUpEnabled(true);
95         }
96         mLauncherPkgs = Utils.getLauncherPackages(getContext());
97 
98         String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
99         mPermissionApps = new PermissionApps(getActivity(), groupName, this);
100         mPermissionApps.refresh(true);
101     }
102 
103     @Override
onResume()104     public void onResume() {
105         super.onResume();
106         mPermissionApps.refresh(true);
107     }
108 
109     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)110     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
111         if (mHasSystemApps) {
112             mShowSystemMenu = menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE,
113                     R.string.menu_show_system);
114             mHideSystemMenu = menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE,
115                     R.string.menu_hide_system);
116             updateMenu();
117         }
118     }
119 
120     @Override
onOptionsItemSelected(MenuItem item)121     public boolean onOptionsItemSelected(MenuItem item) {
122         switch (item.getItemId()) {
123             case android.R.id.home:
124                 getActivity().finish();
125                 return true;
126             case MENU_SHOW_SYSTEM:
127             case MENU_HIDE_SYSTEM:
128                 mShowSystem = item.getItemId() == MENU_SHOW_SYSTEM;
129                 if (mPermissionApps.getApps() != null) {
130                     onPermissionsLoaded(mPermissionApps);
131                 }
132                 updateMenu();
133                 break;
134         }
135         return super.onOptionsItemSelected(item);
136     }
137 
updateMenu()138     private void updateMenu() {
139         mShowSystemMenu.setVisible(!mShowSystem);
140         mHideSystemMenu.setVisible(mShowSystem);
141     }
142 
143     @Override
onSetEmptyText(TextView textView)144     protected void onSetEmptyText(TextView textView) {
145         textView.setText(R.string.no_apps);
146     }
147 
148     @Override
onViewCreated(View view, Bundle savedInstanceState)149     public void onViewCreated(View view, Bundle savedInstanceState) {
150         super.onViewCreated(view, savedInstanceState);
151         bindUi(this, mPermissionApps);
152     }
153 
bindUi(SettingsWithHeader fragment, PermissionApps permissionApps)154     private static void bindUi(SettingsWithHeader fragment, PermissionApps permissionApps) {
155         final Drawable icon = permissionApps.getIcon();
156         final CharSequence label = permissionApps.getLabel();
157 
158         fragment.setHeader(null, null, null,
159                 fragment.getString(R.string.permission_apps_decor_title, label));
160     }
161 
setOnPermissionsLoadedListener(Callback callback)162     private void setOnPermissionsLoadedListener(Callback callback) {
163         mOnPermissionsLoadedListener = callback;
164     }
165 
166     @Override
onPermissionsLoaded(PermissionApps permissionApps)167     public void onPermissionsLoaded(PermissionApps permissionApps) {
168         Context context = getPreferenceManager().getContext();
169 
170         if (context == null) {
171             return;
172         }
173 
174         boolean isTelevision = DeviceUtils.isTelevision(context);
175         PreferenceScreen screen = getPreferenceScreen();
176 
177         ArraySet<String> preferencesToRemove = new ArraySet<>();
178         for (int i = 0, n = screen.getPreferenceCount(); i < n; i++) {
179             preferencesToRemove.add(screen.getPreference(i).getKey());
180         }
181         if (mExtraScreen != null) {
182             for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) {
183                 preferencesToRemove.add(mExtraScreen.getPreference(i).getKey());
184             }
185         }
186 
187         mHasSystemApps = false;
188         boolean menuOptionsInvalided = false;
189 
190         for (PermissionApp app : permissionApps.getApps()) {
191             if (!Utils.shouldShowPermission(app)) {
192                 continue;
193             }
194 
195             String key = app.getKey();
196             preferencesToRemove.remove(key);
197             Preference existingPref = screen.findPreference(key);
198             if (existingPref == null && mExtraScreen != null) {
199                 existingPref = mExtraScreen.findPreference(key);
200             }
201 
202             boolean isSystemApp = Utils.isSystem(app, mLauncherPkgs);
203 
204             if (isSystemApp && !menuOptionsInvalided) {
205                 mHasSystemApps = true;
206                 getActivity().invalidateOptionsMenu();
207                 menuOptionsInvalided = true;
208             }
209 
210             if (isSystemApp && !isTelevision && !mShowSystem) {
211                 if (existingPref != null) {
212                     screen.removePreference(existingPref);
213                 }
214                 continue;
215             }
216 
217             if (existingPref != null) {
218                 // If existing preference - only update its state.
219                 if (app.isPolicyFixed()) {
220                     existingPref.setSummary(getString(
221                             R.string.permission_summary_enforced_by_policy));
222                 }
223                 existingPref.setPersistent(false);
224                 existingPref.setEnabled(!app.isPolicyFixed());
225                 if (existingPref instanceof SwitchPreference) {
226                     ((SwitchPreference) existingPref)
227                             .setChecked(app.areRuntimePermissionsGranted());
228                 }
229                 continue;
230             }
231 
232             SwitchPreference pref = new SwitchPreference(context);
233             pref.setOnPreferenceChangeListener(this);
234             pref.setKey(app.getKey());
235             pref.setIcon(app.getIcon());
236             pref.setTitle(app.getLabel());
237             if (app.isPolicyFixed()) {
238                 pref.setSummary(getString(R.string.permission_summary_enforced_by_policy));
239             }
240             pref.setPersistent(false);
241             pref.setEnabled(!app.isPolicyFixed());
242             pref.setChecked(app.areRuntimePermissionsGranted());
243 
244             if (isSystemApp && isTelevision) {
245                 if (mExtraScreen == null) {
246                     mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
247                 }
248                 mExtraScreen.addPreference(pref);
249             } else {
250                 screen.addPreference(pref);
251             }
252         }
253 
254         if (mExtraScreen != null) {
255             preferencesToRemove.remove(KEY_SHOW_SYSTEM_PREFS);
256             Preference pref = screen.findPreference(KEY_SHOW_SYSTEM_PREFS);
257 
258             if (pref == null) {
259                 pref = new Preference(context);
260                 pref.setKey(KEY_SHOW_SYSTEM_PREFS);
261                 pref.setIcon(Utils.applyTint(context, R.drawable.ic_toc,
262                         android.R.attr.colorControlNormal));
263                 pref.setTitle(R.string.preference_show_system_apps);
264                 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
265                     @Override
266                     public boolean onPreferenceClick(Preference preference) {
267                         SystemAppsFragment frag = new SystemAppsFragment();
268                         setPermissionName(frag, getArguments().getString(
269                                 Intent.EXTRA_PERMISSION_NAME));
270                         frag.setTargetFragment(PermissionAppsFragment.this, 0);
271                         getFragmentManager().beginTransaction()
272                             .replace(android.R.id.content, frag)
273                             .addToBackStack("SystemApps")
274                             .commit();
275                         return true;
276                     }
277                 });
278                 screen.addPreference(pref);
279             }
280 
281             int grantedCount = 0;
282             for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) {
283                 if (((SwitchPreference) mExtraScreen.getPreference(i)).isChecked()) {
284                     grantedCount++;
285                 }
286             }
287             pref.setSummary(getString(R.string.app_permissions_group_summary,
288                     grantedCount, mExtraScreen.getPreferenceCount()));
289         }
290 
291         for (String key : preferencesToRemove) {
292             Preference pref = screen.findPreference(key);
293             if (pref != null) {
294                 screen.removePreference(pref);
295             } else if (mExtraScreen != null) {
296                 pref = mExtraScreen.findPreference(key);
297                 if (pref != null) {
298                     mExtraScreen.removePreference(pref);
299                 }
300             }
301         }
302 
303         setLoading(false /* loading */, true /* animate */);
304 
305         if (mOnPermissionsLoadedListener != null) {
306             mOnPermissionsLoadedListener.onPermissionsLoaded(permissionApps);
307         }
308     }
309 
310     @Override
onPreferenceChange(final Preference preference, Object newValue)311     public boolean onPreferenceChange(final Preference preference, Object newValue) {
312         String pkg = preference.getKey();
313         final PermissionApp app = mPermissionApps.getApp(pkg);
314 
315         if (app == null) {
316             return false;
317         }
318 
319         if (LocationUtils.isLocationGroupAndProvider(mPermissionApps.getGroupName(),
320                 app.getPackageName())) {
321             LocationUtils.showLocationDialog(getContext(), app.getLabel());
322             return false;
323         }
324 
325         addToggledGroup(app.getPackageName(), app.getPermissionGroup());
326 
327         if (app.isReviewRequired()) {
328             Intent intent = new Intent(getActivity(), ReviewPermissionsActivity.class);
329             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, app.getPackageName());
330             startActivity(intent);
331             return false;
332         }
333 
334         if (newValue == Boolean.TRUE) {
335             app.grantRuntimePermissions();
336         } else {
337             final boolean grantedByDefault = app.hasGrantedByDefaultPermissions();
338             if (grantedByDefault || (!app.doesSupportRuntimePermissions()
339                     && !mHasConfirmedRevoke)) {
340                 new AlertDialog.Builder(getContext())
341                         .setMessage(grantedByDefault ? R.string.system_warning
342                                 : R.string.old_sdk_deny_warning)
343                         .setNegativeButton(R.string.cancel, null)
344                         .setPositiveButton(R.string.grant_dialog_button_deny_anyway,
345                                 new OnClickListener() {
346                             @Override
347                             public void onClick(DialogInterface dialog, int which) {
348                                 ((SwitchPreference) preference).setChecked(false);
349                                 app.revokeRuntimePermissions();
350                                 if (!grantedByDefault) {
351                                     mHasConfirmedRevoke = true;
352                                 }
353                             }
354                         })
355                         .show();
356                 return false;
357             } else {
358                 app.revokeRuntimePermissions();
359             }
360         }
361         return true;
362     }
363 
364     @Override
onPause()365     public void onPause() {
366         super.onPause();
367         logToggledGroups();
368     }
369 
addToggledGroup(String packageName, AppPermissionGroup group)370     private void addToggledGroup(String packageName, AppPermissionGroup group) {
371         if (mToggledGroups == null) {
372             mToggledGroups = new ArrayMap<>();
373         }
374         // Double toggle is back to initial state.
375         if (mToggledGroups.containsKey(packageName)) {
376             mToggledGroups.remove(packageName);
377         } else {
378             mToggledGroups.put(packageName, group);
379         }
380     }
381 
logToggledGroups()382     private void logToggledGroups() {
383         if (mToggledGroups != null) {
384             final int groupCount = mToggledGroups.size();
385             for (int i = 0; i < groupCount; i++) {
386                 String packageName = mToggledGroups.keyAt(i);
387                 List<AppPermissionGroup> groups = new ArrayList<>();
388                 groups.add(mToggledGroups.valueAt(i));
389                 SafetyNetLogger.logPermissionsToggled(packageName, groups);
390             }
391             mToggledGroups = null;
392         }
393     }
394 
395     public static class SystemAppsFragment extends SettingsWithHeader implements Callback {
396         PermissionAppsFragment mOuterFragment;
397 
398         @Override
onCreate(Bundle savedInstanceState)399         public void onCreate(Bundle savedInstanceState) {
400             mOuterFragment = (PermissionAppsFragment) getTargetFragment();
401             setLoading(true /* loading */, false /* animate */);
402             super.onCreate(savedInstanceState);
403         }
404 
405         @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)406         public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
407             if (mOuterFragment.mExtraScreen != null) {
408                 setPreferenceScreen();
409             } else {
410                 mOuterFragment.setOnPermissionsLoadedListener(this);
411             }
412         }
413 
414         @Override
onViewCreated(View view, Bundle savedInstanceState)415         public void onViewCreated(View view, Bundle savedInstanceState) {
416             super.onViewCreated(view, savedInstanceState);
417             String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
418             PermissionApps permissionApps = new PermissionApps(getActivity(), groupName, null);
419             bindUi(this, permissionApps);
420         }
421 
422         @Override
onResume()423         public void onResume() {
424             super.onResume();
425             mOuterFragment.mPermissionApps.refresh(true);
426         }
427 
428         @Override
onDestroy()429         public void onDestroy() {
430             super.onDestroy();
431             mOuterFragment.setOnPermissionsLoadedListener(null);
432         }
433 
434 
bindUi(SettingsWithHeader fragment, PermissionApps permissionApps)435         private static void bindUi(SettingsWithHeader fragment, PermissionApps permissionApps) {
436             final CharSequence label = permissionApps.getLabel();
437             fragment.setHeader(null, null, null,
438                     fragment.getString(R.string.system_apps_decor_title, label));
439         }
440 
441         @Override
onPermissionsLoaded(PermissionApps permissionApps)442         public void onPermissionsLoaded(PermissionApps permissionApps) {
443             setPreferenceScreen();
444         }
445 
setPreferenceScreen()446         private void setPreferenceScreen() {
447             setPreferenceScreen(mOuterFragment.mExtraScreen);
448             setLoading(false /* loading */, true /* animate */);
449         }
450     }
451 }
452