/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.packageinstaller.permission.ui.television; import android.app.ActionBar; import android.app.AlertDialog; import android.app.Fragment; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.v14.preference.SwitchPreference; import android.support.v4.util.ArrayMap; import android.support.v7.preference.Preference; import android.support.v7.preference.Preference.OnPreferenceChangeListener; import android.support.v7.preference.Preference.OnPreferenceClickListener; import android.support.v7.preference.PreferenceScreen; import android.util.ArraySet; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.android.packageinstaller.DeviceUtils; import com.android.packageinstaller.R; import com.android.packageinstaller.permission.model.AppPermissionGroup; import com.android.packageinstaller.permission.model.PermissionApps; import com.android.packageinstaller.permission.model.PermissionApps.Callback; import com.android.packageinstaller.permission.model.PermissionApps.PermissionApp; import com.android.packageinstaller.permission.ui.OverlayTouchActivity; import com.android.packageinstaller.permission.utils.LocationUtils; import com.android.packageinstaller.permission.utils.SafetyNetLogger; import com.android.packageinstaller.permission.utils.Utils; import java.util.ArrayList; import java.util.List; public final class PermissionAppsFragment extends PermissionsFrameFragment implements Callback, OnPreferenceChangeListener { private static final int MENU_SHOW_SYSTEM = Menu.FIRST; private static final int MENU_HIDE_SYSTEM = Menu.FIRST + 1; private static final String KEY_SHOW_SYSTEM_PREFS = "_showSystem"; public static PermissionAppsFragment newInstance(String permissionName) { return setPermissionName(new PermissionAppsFragment(), permissionName); } private static T setPermissionName(T fragment, String permissionName) { Bundle arguments = new Bundle(); arguments.putString(Intent.EXTRA_PERMISSION_NAME, permissionName); fragment.setArguments(arguments); return fragment; } private PermissionApps mPermissionApps; private PreferenceScreen mExtraScreen; private ArrayMap mToggledGroups; private ArraySet mLauncherPkgs; private boolean mHasConfirmedRevoke; private boolean mShowSystem; private MenuItem mShowSystemMenu; private MenuItem mHideSystemMenu; private Callback mOnPermissionsLoadedListener; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setLoading(true /* loading */, false /* animate */); setHasOptionsMenu(true); final ActionBar ab = getActivity().getActionBar(); if (ab != null) { ab.setDisplayHomeAsUpEnabled(true); } mLauncherPkgs = Utils.getLauncherPackages(getContext()); String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME); mPermissionApps = new PermissionApps(getActivity(), groupName, this); mPermissionApps.refresh(true); } @Override public void onResume() { super.onResume(); mPermissionApps.refresh(true); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { mShowSystemMenu = menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE, R.string.menu_show_system); mHideSystemMenu = menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE, R.string.menu_hide_system); updateMenu(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: getActivity().finish(); return true; case MENU_SHOW_SYSTEM: case MENU_HIDE_SYSTEM: mShowSystem = item.getItemId() == MENU_SHOW_SYSTEM; if (mPermissionApps.getApps() != null) { onPermissionsLoaded(mPermissionApps); } updateMenu(); break; } return super.onOptionsItemSelected(item); } private void updateMenu() { mShowSystemMenu.setVisible(!mShowSystem); mHideSystemMenu.setVisible(mShowSystem); } @Override protected void onSetEmptyText(TextView textView) { textView.setText(R.string.no_apps); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); bindUi(this, mPermissionApps); } private static void bindUi(Fragment fragment, PermissionApps permissionApps) { final Drawable icon = permissionApps.getIcon(); final CharSequence label = permissionApps.getLabel(); final ActionBar ab = fragment.getActivity().getActionBar(); if (ab != null) { ab.setTitle(fragment.getString(R.string.permission_title, label)); } final ViewGroup rootView = (ViewGroup) fragment.getView(); final ImageView iconView = (ImageView) rootView.findViewById(R.id.lb_icon); if (iconView != null) { // Set the icon as the background instead of the image because ImageView // doesn't properly scale vector drawables beyond their intrinsic size iconView.setBackground(icon); } final TextView titleView = (TextView) rootView.findViewById(R.id.lb_title); if (titleView != null) { titleView.setText(label); } final TextView breadcrumbView = (TextView) rootView.findViewById(R.id.lb_breadcrumb); if (breadcrumbView != null) { breadcrumbView.setText(R.string.app_permissions); } } private void setOnPermissionsLoadedListener(Callback callback) { mOnPermissionsLoadedListener = callback; } @Override public void onPermissionsLoaded(PermissionApps permissionApps) { Context context = getPreferenceManager().getContext(); if (context == null) { return; } boolean isTelevision = DeviceUtils.isTelevision(context); PreferenceScreen screen = getPreferenceScreen(); ArraySet preferencesToRemove = new ArraySet<>(); for (int i = 0, n = screen.getPreferenceCount(); i < n; i++) { preferencesToRemove.add(screen.getPreference(i).getKey()); } if (mExtraScreen != null) { for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) { preferencesToRemove.add(mExtraScreen.getPreference(i).getKey()); } } for (PermissionApp app : permissionApps.getApps()) { if (!Utils.shouldShowPermission(app)) { continue; } String key = app.getKey(); preferencesToRemove.remove(key); Preference existingPref = screen.findPreference(key); if (existingPref == null && mExtraScreen != null) { existingPref = mExtraScreen.findPreference(key); } boolean isSystemApp = Utils.isSystem(app, mLauncherPkgs); if (isSystemApp && !isTelevision && !mShowSystem) { if (existingPref != null) { screen.removePreference(existingPref); } continue; } if (existingPref != null) { // If existing preference - only update its state. if (app.isPolicyFixed()) { existingPref.setSummary(getString( R.string.permission_summary_enforced_by_policy)); } existingPref.setPersistent(false); existingPref.setEnabled(!app.isPolicyFixed()); if (existingPref instanceof SwitchPreference) { ((SwitchPreference) existingPref) .setChecked(app.areRuntimePermissionsGranted()); } continue; } SwitchPreference pref = new SwitchPreference(context); pref.setOnPreferenceChangeListener(this); pref.setKey(app.getKey()); pref.setIcon(app.getIcon()); pref.setTitle(app.getLabel()); if (app.isPolicyFixed()) { pref.setSummary(getString(R.string.permission_summary_enforced_by_policy)); } pref.setPersistent(false); pref.setEnabled(!app.isPolicyFixed()); pref.setChecked(app.areRuntimePermissionsGranted()); if (isSystemApp && isTelevision) { if (mExtraScreen == null) { mExtraScreen = getPreferenceManager().createPreferenceScreen(context); } mExtraScreen.addPreference(pref); } else { screen.addPreference(pref); } } if (mExtraScreen != null) { preferencesToRemove.remove(KEY_SHOW_SYSTEM_PREFS); Preference pref = screen.findPreference(KEY_SHOW_SYSTEM_PREFS); if (pref == null) { pref = new Preference(context); pref.setKey(KEY_SHOW_SYSTEM_PREFS); pref.setIcon(Utils.applyTint(context, R.drawable.ic_toc, android.R.attr.colorControlNormal)); pref.setTitle(R.string.preference_show_system_apps); pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { SystemAppsFragment frag = new SystemAppsFragment(); setPermissionName(frag, getArguments().getString(Intent.EXTRA_PERMISSION_NAME)); frag.setTargetFragment(PermissionAppsFragment.this, 0); getFragmentManager().beginTransaction() .replace(android.R.id.content, frag) .addToBackStack("SystemApps") .commit(); return true; } }); screen.addPreference(pref); } int grantedCount = 0; for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) { if (((SwitchPreference) mExtraScreen.getPreference(i)).isChecked()) { grantedCount++; } } pref.setSummary(getString(R.string.app_permissions_group_summary, grantedCount, mExtraScreen.getPreferenceCount())); } for (String key : preferencesToRemove) { Preference pref = screen.findPreference(key); if (pref != null) { screen.removePreference(pref); } else if (mExtraScreen != null) { pref = mExtraScreen.findPreference(key); if (pref != null) { mExtraScreen.removePreference(pref); } } } setLoading(false /* loading */, true /* animate */); if (mOnPermissionsLoadedListener != null) { mOnPermissionsLoadedListener.onPermissionsLoaded(permissionApps); } } @Override public boolean onPreferenceChange(final Preference preference, Object newValue) { String pkg = preference.getKey(); final PermissionApp app = mPermissionApps.getApp(pkg); if (app == null) { return false; } OverlayTouchActivity activity = (OverlayTouchActivity) getActivity(); if (activity.isObscuredTouch()) { activity.showOverlayDialog(); return false; } addToggledGroup(app.getPackageName(), app.getPermissionGroup()); if (LocationUtils.isLocationGroupAndProvider(mPermissionApps.getGroupName(), app.getPackageName())) { LocationUtils.showLocationDialog(getContext(), app.getLabel()); return false; } if (newValue == Boolean.TRUE) { app.grantRuntimePermissions(); } else { final boolean grantedByDefault = app.hasGrantedByDefaultPermissions(); if (grantedByDefault || (!app.hasRuntimePermissions() && !mHasConfirmedRevoke)) { new AlertDialog.Builder(getContext()) .setMessage(grantedByDefault ? R.string.system_warning : R.string.old_sdk_deny_warning) .setNegativeButton(R.string.cancel, null) .setPositiveButton(R.string.grant_dialog_button_deny, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ((SwitchPreference) preference).setChecked(false); app.revokeRuntimePermissions(); if (!grantedByDefault) { mHasConfirmedRevoke = true; } } }) .show(); return false; } else { app.revokeRuntimePermissions(); } } return true; } @Override public void onPause() { super.onPause(); logToggledGroups(); } private void addToggledGroup(String packageName, AppPermissionGroup group) { if (mToggledGroups == null) { mToggledGroups = new ArrayMap<>(); } // Double toggle is back to initial state. if (mToggledGroups.containsKey(packageName)) { mToggledGroups.remove(packageName); } else { mToggledGroups.put(packageName, group); } } private void logToggledGroups() { if (mToggledGroups != null) { final int groupCount = mToggledGroups.size(); for (int i = 0; i < groupCount; i++) { String packageName = mToggledGroups.keyAt(i); List groups = new ArrayList<>(); groups.add(mToggledGroups.valueAt(i)); SafetyNetLogger.logPermissionsToggled(packageName, groups); } mToggledGroups = null; } } public static class SystemAppsFragment extends PermissionsFrameFragment implements Callback { PermissionAppsFragment mOuterFragment; @Override public void onCreate(Bundle savedInstanceState) { mOuterFragment = (PermissionAppsFragment) getTargetFragment(); setLoading(true /* loading */, false /* animate */); super.onCreate(savedInstanceState); } @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { if (mOuterFragment.mExtraScreen != null) { setPreferenceScreen(); } else { mOuterFragment.setOnPermissionsLoadedListener(this); } } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME); PermissionApps permissionApps = new PermissionApps(getActivity(), groupName, null); bindUi(this, permissionApps); } @Override public void onPermissionsLoaded(PermissionApps permissionApps) { setPreferenceScreen(); mOuterFragment.setOnPermissionsLoadedListener(null); } private void setPreferenceScreen() { setPreferenceScreen(mOuterFragment.mExtraScreen); setLoading(false /* loading */, true /* animate */); } } }