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