| /* |
| * 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.permissioncontroller.permission.ui.television; |
| |
| import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; |
| |
| import android.app.ActionBar; |
| import android.app.AlertDialog; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.graphics.drawable.Drawable; |
| import android.os.Bundle; |
| import android.util.ArraySet; |
| import android.view.View; |
| import android.widget.TextView; |
| |
| import androidx.preference.Preference; |
| import androidx.preference.Preference.OnPreferenceClickListener; |
| import androidx.preference.PreferenceCategory; |
| import androidx.preference.PreferenceGroup; |
| import androidx.preference.PreferenceScreen; |
| |
| import com.android.permissioncontroller.R; |
| import com.android.permissioncontroller.permission.model.AppPermissionGroup; |
| import com.android.permissioncontroller.permission.model.legacy.PermissionApps; |
| import com.android.permissioncontroller.permission.model.legacy.PermissionApps.Callback; |
| import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp; |
| import com.android.permissioncontroller.permission.ui.ReviewPermissionsActivity; |
| import com.android.permissioncontroller.permission.utils.LocationUtils; |
| import com.android.permissioncontroller.permission.utils.SafetyNetLogger; |
| import com.android.permissioncontroller.permission.utils.Utils; |
| |
| public final class PermissionAppsFragment extends SettingsWithHeader implements Callback, |
| OnPreferenceClickListener { |
| |
| private static final String KEY_CATEGORY_ALLOWED = "_allowed"; |
| private static final String KEY_CATEGORY_DENIED = "_denied"; |
| private static final String KEY_NO_APPS_ALLOWED = "_noAppsAllowed"; |
| private static final String KEY_NO_APPS_DENIED = "_noAppsDenied"; |
| private static final String KEY_SHOW_SYSTEM_PREFS = "_showSystem"; |
| |
| public static PermissionAppsFragment newInstance(String permissionName) { |
| return setPermissionName(new PermissionAppsFragment(), permissionName); |
| } |
| |
| private static <T extends PermissionsFrameFragment> 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 mSystemAppsScreen; |
| |
| private ArraySet<AppPermissionGroup> mToggledGroups; |
| private boolean mHasConfirmedRevoke; |
| |
| private Callback mOnPermissionsLoadedListener; |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setLoading(true /* loading */, false /* animate */); |
| String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME); |
| mPermissionApps = new PermissionApps(getActivity(), groupName, this); |
| } |
| |
| @Override |
| public void onStart() { |
| super.onStart(); |
| |
| setHasOptionsMenu(true); |
| final ActionBar ab = getActivity().getActionBar(); |
| if (ab != null) { |
| ab.setDisplayHomeAsUpEnabled(true); |
| } |
| |
| mPermissionApps.refresh(true); |
| } |
| |
| @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(SettingsWithHeader fragment, PermissionApps permissionApps) { |
| final Drawable icon = permissionApps.getIcon(); |
| final CharSequence label = permissionApps.getLabel(); |
| |
| fragment.setHeader(null, null, null, |
| fragment.getString(R.string.permission_apps_decor_title, label)); |
| } |
| |
| private void setOnPermissionsLoadedListener(Callback callback) { |
| mOnPermissionsLoadedListener = callback; |
| } |
| |
| @Override |
| public void onPermissionsLoaded(PermissionApps permissionApps) { |
| Context context = getPreferenceManager().getContext(); |
| if (context == null) { |
| return; |
| } |
| |
| ArraySet<PermissionApp> mainScreenApps = new ArraySet<>(); |
| ArraySet<PermissionApp> systemScreenApps = new ArraySet<>(); |
| for (PermissionApp app : permissionApps.getApps()) { |
| if (!Utils.shouldShowPermission(getContext(), app.getPermissionGroup())) { |
| continue; |
| } |
| |
| boolean isSystemApp = !Utils.isGroupOrBgGroupUserSensitive(app.getPermissionGroup()); |
| if (isSystemApp) { |
| systemScreenApps.add(app); |
| } else { |
| mainScreenApps.add(app); |
| } |
| } |
| |
| PreferenceScreen mainScreen = getPreferenceScreen(); |
| if (!systemScreenApps.isEmpty()) { |
| if (mSystemAppsScreen == null) { |
| mSystemAppsScreen = getPreferenceManager().createPreferenceScreen(context); |
| } |
| } else { |
| mSystemAppsScreen = null; |
| } |
| |
| // Updating "main" screen categories |
| PreferenceCategory allowedCategory = findOrCreateCategory(context, mainScreen, |
| KEY_CATEGORY_ALLOWED, R.string.allowed_header); |
| PreferenceCategory deniedCategory = findOrCreateCategory(context, mainScreen, |
| KEY_CATEGORY_DENIED, R.string.denied_header); |
| updateCategories(context, allowedCategory, deniedCategory, mainScreenApps); |
| |
| Preference showSystemAppPref = mainScreen.findPreference(KEY_SHOW_SYSTEM_PREFS); |
| if (mSystemAppsScreen != null) { |
| // Updating "system" screen categories |
| allowedCategory = findOrCreateCategory(context, mSystemAppsScreen, KEY_CATEGORY_ALLOWED, |
| R.string.allowed_header); |
| deniedCategory = findOrCreateCategory(context, mSystemAppsScreen, KEY_CATEGORY_DENIED, |
| R.string.denied_header); |
| updateCategories(context, allowedCategory, deniedCategory, systemScreenApps); |
| |
| // Setting "Show system apps" button on the "main" screen. |
| if (showSystemAppPref == null) { |
| showSystemAppPref = new Preference(context); |
| showSystemAppPref.setKey(KEY_SHOW_SYSTEM_PREFS); |
| showSystemAppPref.setIcon(Utils.applyTint(context, R.drawable.ic_toc, |
| android.R.attr.colorControlNormal)); |
| showSystemAppPref.setTitle(R.string.preference_show_system_apps); |
| showSystemAppPref.setOnPreferenceClickListener(this); |
| |
| mainScreen.addPreference(showSystemAppPref); |
| } |
| } else if (showSystemAppPref != null) { |
| // There are not system apps, but there is a "Show system apps" button: remove the |
| // button. |
| mainScreen.removePreference(showSystemAppPref); |
| } |
| |
| setLoading(false /* loading */, true /* animate */); |
| |
| if (mOnPermissionsLoadedListener != null) { |
| mOnPermissionsLoadedListener.onPermissionsLoaded(permissionApps); |
| } |
| } |
| |
| private void updateCategories( |
| Context context, |
| PreferenceCategory allowedCategory, |
| PreferenceCategory deniedCategory, |
| ArraySet<PermissionApp> apps) { |
| ArraySet<String> toRemoveFromAllowed = getAllPreferencesKeys(allowedCategory); |
| ArraySet<String> toRemoveFromDenied = getAllPreferencesKeys(deniedCategory); |
| |
| boolean hasAllowed = false; |
| boolean hasDenied = false; |
| |
| for (int i = 0, n = apps.size(); i < n; i++) { |
| PermissionApp app = apps.valueAt(i); |
| String key = app.getKey(); |
| |
| // One-time granted permissions should appear in the "Denied" category |
| boolean isAllowed = |
| app.areRuntimePermissionsGranted() && !app.getPermissionGroup().isOneTime(); |
| |
| if (isAllowed) { |
| hasAllowed = true; |
| if (toRemoveFromAllowed.remove(key)) { |
| // Already have this app in the right category. |
| continue; |
| } |
| } else if (app.getPermissionGroup().isUserSet()) { |
| hasDenied = true; |
| if (toRemoveFromDenied.remove(key)) { |
| // Already have this app in the right category. |
| continue; |
| } |
| } else { |
| // Not granted and not user set, meaning that it was never requested, so we don't |
| // need to show it. |
| continue; |
| } |
| |
| PreferenceCategory rightCategory = isAllowed ? allowedCategory : deniedCategory; |
| PreferenceCategory otherCategory = isAllowed ? deniedCategory : allowedCategory; |
| |
| // Check if we have the app in the wrong category. |
| Preference preference = otherCategory.findPreference(key); |
| if (preference != null) { |
| // Remove from the wrong category, before adding to the right one. |
| otherCategory.removePreference(preference); |
| // We are leaving the key in toRemoveFrom* list, that's fine. |
| } else { |
| preference = new Preference(context); |
| preference.setOnPreferenceClickListener(this); |
| preference.setKey(app.getKey()); |
| preference.setIcon(app.getIcon()); |
| preference.setTitle(app.getLabel()); |
| if (app.isSystemFixed()) { |
| preference.setSummary( |
| getString(R.string.permission_summary_enabled_system_fixed)); |
| } else if (app.isPolicyFixed()) { |
| preference.setSummary( |
| getString(R.string.permission_summary_enforced_by_policy)); |
| } |
| preference.setPersistent(false); |
| preference.setEnabled(!app.isSystemFixed() && !app.isPolicyFixed()); |
| } |
| |
| // Add to the right category. |
| rightCategory.addPreference(preference); |
| } |
| |
| if (!hasAllowed) { |
| if (!toRemoveFromAllowed.remove(KEY_NO_APPS_ALLOWED)) { |
| // Did not have "No apps allowed" label, adding. |
| Preference preference = new Preference(context); |
| preference.setKey(KEY_NO_APPS_ALLOWED); |
| preference.setLayoutResource(R.layout.preference_permissions_no_apps); |
| preference.setTitle(R.string.no_apps_allowed); |
| preference.setEnabled(false); |
| |
| allowedCategory.addPreference(preference); |
| } |
| } |
| |
| if (!hasDenied) { |
| if (!toRemoveFromDenied.remove(KEY_NO_APPS_DENIED)) { |
| // Did not have "No apps denied" label, adding. |
| Preference preference = new Preference(context); |
| preference.setKey(KEY_NO_APPS_DENIED); |
| preference.setLayoutResource(R.layout.preference_permissions_no_apps); |
| preference.setTitle(R.string.no_apps_denied); |
| preference.setEnabled(false); |
| |
| deniedCategory.addPreference(preference); |
| } |
| } |
| |
| removePreferences(allowedCategory, toRemoveFromAllowed); |
| removePreferences(deniedCategory, toRemoveFromDenied); |
| } |
| |
| @Override |
| public boolean onPreferenceClick(final Preference preference) { |
| final String key = preference.getKey(); |
| |
| if (KEY_SHOW_SYSTEM_PREFS.equals(key)) { |
| SystemAppsFragment frag = new SystemAppsFragment(); |
| setPermissionName(frag, getArguments().getString( |
| Intent.EXTRA_PERMISSION_NAME)); |
| frag.setTargetFragment(this, 0); |
| getFragmentManager().beginTransaction() |
| .replace(android.R.id.content, frag) |
| .addToBackStack("SystemApps") |
| .commit(); |
| return true; |
| } |
| |
| final PermissionApp app = mPermissionApps.getApp(key); |
| |
| if (LocationUtils.isLocationGroupAndProvider(getContext(), mPermissionApps.getGroupName(), |
| app.getPackageName())) { |
| LocationUtils.showLocationDialog(getContext(), app.getLabel()); |
| return true; |
| } |
| |
| addToggledGroup(app.getPackageName(), app.getPermissionGroup()); |
| |
| if (app.isReviewRequired()) { |
| Intent intent = new Intent(getActivity(), ReviewPermissionsActivity.class); |
| intent.putExtra(Intent.EXTRA_PACKAGE_NAME, app.getPackageName()); |
| startActivity(intent); |
| return true; |
| } |
| |
| final AppPermissionFragment frag = new AppPermissionFragment(); |
| frag.setArguments(AppPermissionFragment.createArgs( |
| /* packageName= */ app.getPackageName(), |
| /* permName= */ null, |
| /* groupName= */ app.getPermissionGroup().getName(), |
| /* userHandle= */ app.getPermissionGroup().getUser(), |
| /* caller= */ null, |
| /* sessionId= */ INVALID_SESSION_ID, |
| /* grantCategory= */ null)); |
| frag.setTargetFragment(this, 0); |
| getFragmentManager().beginTransaction() |
| .replace(android.R.id.content, frag) |
| .addToBackStack(null) |
| .commit(); |
| |
| return true; |
| } |
| |
| @Override |
| public void onStop() { |
| super.onStop(); |
| logToggledGroups(); |
| } |
| |
| private PreferenceCategory findOrCreateCategory( |
| Context context, PreferenceScreen screen, String key, int titleResId) { |
| PreferenceCategory category = screen.findPreference(key); |
| if (category == null) { |
| category = new PreferenceCategory(context); |
| category.setKey(key); |
| category.setTitle(titleResId); |
| |
| screen.addPreference(category); |
| } |
| return category; |
| } |
| |
| private ArraySet<String> getAllPreferencesKeys(PreferenceGroup group) { |
| ArraySet<String> keys = new ArraySet<>(); |
| if (group != null) { |
| for (int i = 0, n = group.getPreferenceCount(); i < n; i++) { |
| keys.add(group.getPreference(i).getKey()); |
| } |
| } |
| return keys; |
| } |
| |
| private void removePreferences(PreferenceGroup group, ArraySet<String> keys) { |
| if (group == null || keys == null) { |
| return; |
| } |
| for (int i = 0, n = keys.size(); i < n; i++) { |
| Preference preference = group.findPreference(keys.valueAt(i)); |
| if (preference != null) { |
| group.removePreference(preference); |
| } |
| } |
| } |
| |
| private void showWarningIfNeeded(PermissionApp app) { |
| final boolean grantedByDefault = app.hasGrantedByDefaultPermissions(); |
| if (grantedByDefault || (!app.doesSupportRuntimePermissions() |
| && !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_anyway, |
| (dialog, which) -> { |
| app.revokeRuntimePermissions(); |
| if (!grantedByDefault) { |
| mHasConfirmedRevoke = true; |
| } |
| }) |
| .show(); |
| } else { |
| app.revokeRuntimePermissions(); |
| } |
| } |
| |
| private void addToggledGroup(String packageName, AppPermissionGroup group) { |
| if (mToggledGroups == null) { |
| mToggledGroups = new ArraySet<>(); |
| } |
| |
| mToggledGroups.add(group); |
| } |
| |
| private void logToggledGroups() { |
| if (mToggledGroups != null) { |
| SafetyNetLogger.logPermissionsToggled(mToggledGroups); |
| mToggledGroups = null; |
| } |
| } |
| |
| public static class SystemAppsFragment extends SettingsWithHeader 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.mSystemAppsScreen != 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, |
| (Callback) null); |
| bindUi(this, permissionApps); |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| mOuterFragment.mPermissionApps.refresh(true); |
| } |
| |
| @Override |
| public void onDestroy() { |
| super.onDestroy(); |
| mOuterFragment.setOnPermissionsLoadedListener(null); |
| } |
| |
| |
| private static void bindUi(SettingsWithHeader fragment, PermissionApps permissionApps) { |
| final CharSequence label = permissionApps.getLabel(); |
| fragment.setHeader(null, null, null, |
| fragment.getString(R.string.system_apps_decor_title, label)); |
| } |
| |
| @Override |
| public void onPermissionsLoaded(PermissionApps permissionApps) { |
| setPreferenceScreen(); |
| } |
| |
| private void setPreferenceScreen() { |
| setPreferenceScreen(mOuterFragment.mSystemAppsScreen); |
| setLoading(false /* loading */, true /* animate */); |
| } |
| } |
| } |