[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 2de44cce33 -s ours am: 196b23e356 -s ours am: 1dd4ab4fb9 -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/Permission/+/16588225
Change-Id: Icea785f06db89833026c396584f027d59afd9534
diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml
index ba03cbf..7a08681 100644
--- a/PermissionController/res/values/strings.xml
+++ b/PermissionController/res/values/strings.xml
@@ -136,6 +136,9 @@
<!-- Label when there are no unused apps [CHAR LIMIT=30] -->
<string name="no_unused_apps">No unused apps</string>
+ <!-- Label when there are zero unused apps [CHAR LIMIT=30] -->
+ <string name="zero_unused_apps">0 unused apps</string>
+
<!-- [CHAR LIMIT=30] Manage applications, label for option to disable app -->
<string name="app_disable_dlg_positive">Disable app</string>
diff --git a/PermissionController/res/values/themes.xml b/PermissionController/res/values/themes.xml
index 04265a3..16ff3c1 100644
--- a/PermissionController/res/values/themes.xml
+++ b/PermissionController/res/values/themes.xml
@@ -57,6 +57,10 @@
<item name="android:windowIsTranslucent">true</item>
</style>
+ <style name="GrantPermissions.Car">
+ <item name="carUiActivity">true</item>
+ </style>
+
<!-- Unused since R but exposed as overlayable. -->
<style name="Header.Settings"
parent="@android:style/Theme.DeviceDefault.Settings">
@@ -118,6 +122,10 @@
<item name="android:filterTouchesWhenObscured">true</item>
</style>
+ <style name="GrantPermissions.Car.FilterTouches">
+ <item name="android:filterTouchesWhenObscured">true</item>
+ </style>
+
<style name="RequestRole.FilterTouches">
<item name="android:filterTouchesWhenObscured">true</item>
</style>
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsages.java b/PermissionController/src/com/android/permissioncontroller/permission/model/PermissionUsages.java
similarity index 92%
rename from PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsages.java
rename to PermissionController/src/com/android/permissioncontroller/permission/model/PermissionUsages.java
index f83309f..b1dd404 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsages.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/PermissionUsages.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.permission.ui.handheld.dashboard;
+package com.android.permissioncontroller.permission.model;
import static android.Manifest.permission.CAMERA;
import static android.Manifest.permission.RECORD_AUDIO;
@@ -45,10 +45,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
-import com.android.permissioncontroller.permission.model.AppPermissionGroup;
-import com.android.permissioncontroller.permission.model.AppPermissionUsage;
import com.android.permissioncontroller.permission.model.AppPermissionUsage.Builder;
-import com.android.permissioncontroller.permission.model.Permission;
import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp;
import com.android.permissioncontroller.permission.model.legacy.PermissionGroup;
import com.android.permissioncontroller.permission.model.legacy.PermissionGroups;
@@ -92,14 +89,27 @@
private @Nullable PermissionsUsagesChangeCallback mCallback;
+ /**
+ * Callback for when the permission usages has loaded or changed.
+ */
public interface PermissionsUsagesChangeCallback {
+ /**
+ * Called when the permission usages have loaded or changed.
+ */
void onPermissionUsagesChanged();
}
+ /**
+ * Creates a new instance of {@link PermissionUsages}.
+ */
public PermissionUsages(@NonNull Context context) {
mContext = context;
}
+ /**
+ * Start the {@link Loader} to load the permission usages in the background. Loads without a uid
+ * filter.
+ */
public void load(@Nullable String filterPackageName,
@Nullable String[] filterPermissionGroups, long filterBeginTimeMillis,
long filterEndTimeMillis, int usageFlags, @NonNull LoaderManager loaderManager,
@@ -110,6 +120,10 @@
getNonPlatformPermissions, callback, sync);
}
+ /**
+ * Start the {@link Loader} to load the permission usages in the background. Loads only
+ * permissions for the specified {@code filterUid}.
+ */
public void load(int filterUid, @Nullable String filterPackageName,
@Nullable String[] filterPermissionGroups, long filterBeginTimeMillis,
long filterEndTimeMillis, int usageFlags, @NonNull LoaderManager loaderManager,
@@ -155,36 +169,20 @@
mCallback.onPermissionUsagesChanged();
}
+ /**
+ * Return the usages that have already been loaded.
+ */
public @NonNull List<AppPermissionUsage> getUsages() {
return mUsages;
}
+ /**
+ * Stop the {@link Loader} from loading the usages.
+ */
public void stopLoader(@NonNull LoaderManager loaderManager) {
loaderManager.destroyLoader(1);
}
- public static @Nullable AppPermissionUsage.GroupUsage loadLastGroupUsage(
- @NonNull Context context, @NonNull AppPermissionGroup group) {
- final ArraySet<String> opNames = new ArraySet<>();
- final List<Permission> permissions = group.getPermissions();
- final int permCount = permissions.size();
- for (int i = 0; i < permCount; i++) {
- final Permission permission = permissions.get(i);
- final String opName = permission.getAppOp();
- if (opName != null) {
- opNames.add(opName);
- }
- }
- final String[] opNamesArray = opNames.toArray(new String[opNames.size()]);
- final List<PackageOps> usageOps = context.getSystemService(AppOpsManager.class)
- .getOpsForPackage(group.getApp().applicationInfo.uid,
- group.getApp().packageName, opNamesArray);
- if (usageOps == null || usageOps.isEmpty()) {
- return null;
- }
- return new AppPermissionUsage.GroupUsage(group, usageOps.get(0), null);
- }
-
private static final class UsageLoader extends AsyncTaskLoader<List<AppPermissionUsage>> {
private final int mFilterUid;
private @Nullable String mFilterPackageName;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
index b96efef..812e4fe 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
@@ -127,6 +127,9 @@
@Override
public void onCreate(Bundle icicle) {
+ if (DeviceUtils.isAuto(this)) {
+ setTheme(R.style.GrantPermissions_Car_FilterTouches);
+ }
super.onCreate(icicle);
if (icicle == null) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
index bf48e28..f8594ac 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
@@ -256,10 +256,10 @@
if (DeviceUtils.isAuto(this)) {
if (allPermissions) {
androidXFragment = AutoAllAppPermissionsFragment.newInstance(packageName,
- userHandle);
+ userHandle, sessionId);
} else {
androidXFragment = AutoAppPermissionsFragment.newInstance(packageName,
- userHandle);
+ userHandle, sessionId, /* isSystemPermsScreen= */ true);
}
} else if (DeviceUtils.isWear(this)) {
androidXFragment = AppPermissionsFragmentWear.newInstance(packageName);
@@ -268,7 +268,7 @@
.AppPermissionsFragment.newInstance(packageName, userHandle);
} else {
Bundle args = AppPermissionGroupsFragment.createArgs(packageName, userHandle,
- sessionId, true);
+ sessionId, /* isSystemPermsScreen= */ true);
setNavGraph(args, R.id.app_permission_groups);
return;
}
@@ -296,7 +296,8 @@
return;
}
if (DeviceUtils.isAuto(this)) {
- androidXFragment = AutoPermissionAppsFragment.newInstance(permissionName);
+ androidXFragment =
+ AutoPermissionAppsFragment.newInstance(permissionName, sessionId);
} else if (DeviceUtils.isTelevision(this)) {
androidXFragment = com.android.permissioncontroller.permission.ui.television
.PermissionAppsFragment.newInstance(permissionName);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt
index c1d26dd..73aeb76 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt
@@ -26,6 +26,7 @@
import android.os.UserHandle
import android.util.Log
import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.preference.Preference
@@ -52,11 +53,9 @@
* A fragment displaying all applications that are unused as well as the option to remove them
* and to open them.
*/
-class UnusedAppsFragment<PF, UnusedAppPref> : PreferenceFragmentCompat()
+class UnusedAppsFragment<PF, UnusedAppPref> : Fragment()
where PF : PreferenceFragmentCompat, PF : UnusedAppsFragment.Parent<UnusedAppPref>,
- UnusedAppPref : Preference, UnusedAppPref : RemovablePref {
-
- private val INFO_MSG_CATEGORY = "info_msg_category"
+ UnusedAppPref : Preference, UnusedAppPref : RemovablePref {
private lateinit var viewModel: UnusedAppsViewModel
private lateinit var collator: Collator
@@ -64,6 +63,7 @@
private var isFirstLoad = false
companion object {
+ public const val INFO_MSG_CATEGORY = "info_msg_category"
private const val SHOW_LOAD_DELAY_MS = 200L
private const val INFO_MSG_KEY = "info_msg"
private const val ELEVATION_HIGH = 8f
@@ -91,10 +91,6 @@
}
}
- override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- // empty
- }
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val preferenceFragment: PF = requirePreferenceFragment()
@@ -153,14 +149,15 @@
*/
private fun createPreferenceScreen() {
val preferenceFragment: PF = requirePreferenceFragment()
- val preferenceScreen = preferenceManager.inflateFromResource(
+ val preferenceScreen = preferenceFragment.preferenceManager.inflateFromResource(
context,
R.xml.unused_app_categories,
/* rootPreferences= */ null)
preferenceFragment.preferenceScreen = preferenceScreen
val infoMsgCategory = preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)
- val footerPreference = preferenceFragment.createFooterPreference(context!!)
+ val footerPreference = preferenceFragment.createFooterPreference(
+ preferenceFragment.preferenceManager.context)
footerPreference.key = INFO_MSG_KEY
infoMsgCategory?.addPreference(footerPreference)
}
@@ -211,7 +208,8 @@
var pref = category.findPreference<UnusedAppPref>(key)
if (pref == null) {
pref = removedPrefs[key] ?: preferenceFragment.createUnusedAppPref(
- activity!!.application, pkgName, user, preferenceManager.context!!)
+ activity!!.application, pkgName, user,
+ preferenceFragment.preferenceManager.context)
pref.key = key
pref.title = KotlinUtils.getPackageLabel(activity!!.application, pkgName, user)
}
@@ -253,9 +251,7 @@
}
}
- val infoMsgCategory =
- preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
- infoMsgCategory.isVisible = !allCategoriesEmpty
+ preferenceFragment.setEmptyState(allCategoriesEmpty)
if (isFirstLoad) {
if (categorizedPackages[Months.SIX]!!.isNotEmpty() ||
@@ -377,5 +373,12 @@
user: UserHandle,
context: Context
): UnusedAppPref
+
+ /**
+ * Updates the state based on whether the content is empty.
+ *
+ * @param empty whether the content is empty
+ */
+ fun setEmptyState(empty: Boolean)
}
}
\ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java
index f065f1c..e509d8a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAllAppPermissionsFragment.java
@@ -16,6 +16,8 @@
package com.android.permissioncontroller.permission.ui.auto;
+import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -62,18 +64,19 @@
/** Creates an {@link AutoAllAppPermissionsFragment} with no filter. */
public static AutoAllAppPermissionsFragment newInstance(@NonNull String packageName,
- @NonNull UserHandle userHandle) {
- return newInstance(packageName, /* filterGroup= */ null, userHandle);
+ @NonNull UserHandle userHandle, long sessionId) {
+ return newInstance(packageName, /* filterGroup= */ null, userHandle, sessionId);
}
/** Creates an {@link AutoAllAppPermissionsFragment} with a specific filter group. */
public static AutoAllAppPermissionsFragment newInstance(@NonNull String packageName,
- @NonNull String filterGroup, @NonNull UserHandle userHandle) {
+ @NonNull String filterGroup, @NonNull UserHandle userHandle, long sessionId) {
AutoAllAppPermissionsFragment instance = new AutoAllAppPermissionsFragment();
Bundle arguments = new Bundle();
arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, filterGroup);
arguments.putParcelable(Intent.EXTRA_USER, userHandle);
+ arguments.putLong(EXTRA_SESSION_ID, sessionId);
instance.setArguments(arguments);
return instance;
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
index 3181db4..22b9c41 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
@@ -16,31 +16,35 @@
package com.android.permissioncontroller.permission.ui.auto;
+import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY;
import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED;
import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
+import android.text.BidiFormatter;
import android.util.Log;
import android.view.View;
import android.widget.RadioButton;
-import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.res.TypedArrayUtils;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
@@ -50,35 +54,26 @@
import com.android.car.ui.AlertDialogBuilder;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
-import com.android.permissioncontroller.permission.model.AppPermissionGroup;
-import com.android.permissioncontroller.permission.model.Permission;
import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler;
-import com.android.permissioncontroller.permission.utils.LocationUtils;
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel;
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModelFactory;
+import com.android.permissioncontroller.permission.utils.KotlinUtils;
import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor;
-import com.android.permissioncontroller.permission.utils.SafetyNetLogger;
-import com.android.permissioncontroller.permission.utils.Utils;
import com.android.settingslib.RestrictedLockUtils;
-import java.lang.annotation.Retention;
-import java.util.List;
+import java.util.Map;
+
+import kotlin.Pair;
/** Settings related to a particular permission for the given app. */
-public class AutoAppPermissionFragment extends AutoSettingsFrameFragment {
+public class AutoAppPermissionFragment extends AutoSettingsFrameFragment
+ implements AppPermissionViewModel.ConfirmDialogShowingFragment {
private static final String LOG_TAG = "AppPermissionFragment";
-
- @Retention(SOURCE)
- @IntDef(value = {CHANGE_FOREGROUND, CHANGE_BACKGROUND}, flag = true)
- @interface ChangeTarget {
- }
-
- static final int CHANGE_FOREGROUND = 1;
- static final int CHANGE_BACKGROUND = 2;
- static final int CHANGE_BOTH = CHANGE_FOREGROUND | CHANGE_BACKGROUND;
+ private static final long POST_DELAY_MS = 20;
@NonNull
- private AppPermissionGroup mGroup;
-
+ private TwoStatePreference mAllowPermissionPreference;
@NonNull
private TwoStatePreference mAlwaysPermissionPreference;
@NonNull
@@ -88,14 +83,19 @@
@NonNull
private AutoTwoTargetPreference mDetailsPreference;
- private boolean mHasConfirmedRevoke;
-
- /**
- * Listens for changes to the permission of the app the permission is currently getting
- * granted to. {@code null} when unregistered.
- */
- @Nullable
- private PackageManager.OnPermissionsChangedListener mPermissionChangeListener;
+ @NonNull
+ private AppPermissionViewModel mViewModel;
+ @NonNull
+ private String mPackageName;
+ @NonNull
+ private String mPermGroupName;
+ @NonNull
+ private UserHandle mUser;
+ @NonNull
+ private String mPackageLabel;
+ @NonNull
+ private String mPermGroupLabel;
+ private Drawable mPackageIcon;
/**
* Listens for changes to the app the permission is currently getting granted to. {@code null}
@@ -114,7 +114,8 @@
*/
@NonNull
public static AutoAppPermissionFragment newInstance(@NonNull String packageName,
- @NonNull String permName, @Nullable String groupName, @NonNull UserHandle userHandle) {
+ @NonNull String permName, @Nullable String groupName, @NonNull UserHandle userHandle,
+ @NonNull long sessionId) {
AutoAppPermissionFragment fragment = new AutoAppPermissionFragment();
Bundle arguments = new Bundle();
arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
@@ -124,75 +125,34 @@
arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName);
}
arguments.putParcelable(Intent.EXTRA_USER, userHandle);
+ arguments.putLong(EXTRA_SESSION_ID, sessionId);
fragment.setArguments(arguments);
return fragment;
}
@Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mHasConfirmedRevoke = false;
-
- mGroup = getAppPermissionGroup();
- if (mGroup == null) {
- requireActivity().setResult(Activity.RESULT_CANCELED);
- requireActivity().finish();
- return;
- }
-
- setHeaderLabel(
- requireContext().getString(R.string.app_permission_title, mGroup.getFullLabel()));
- }
-
- private void setResult(@GrantPermissionsViewHandler.Result int result) {
- Intent intent = new Intent()
- .putExtra(EXTRA_RESULT_PERMISSION_INTERACTED,
- requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME))
- .putExtra(EXTRA_RESULT_PERMISSION_RESULT, result);
- requireActivity().setResult(Activity.RESULT_OK, intent);
- }
-
- private AppPermissionGroup getAppPermissionGroup() {
- Activity activity = requireActivity();
- Context context = getPreferenceManager().getContext();
-
- String packageName = requireArguments().getString(Intent.EXTRA_PACKAGE_NAME);
- String groupName = requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME);
- if (groupName == null) {
- groupName = requireArguments().getString(Intent.EXTRA_PERMISSION_NAME);
- }
- PackageItemInfo groupInfo = Utils.getGroupInfo(groupName, context);
- List<PermissionInfo> groupPermInfos = Utils.getGroupPermissionInfos(groupName, context);
- if (groupInfo == null || groupPermInfos == null) {
- Log.i(LOG_TAG, "Illegal group: " + groupName);
- return null;
- }
- UserHandle userHandle = requireArguments().getParcelable(Intent.EXTRA_USER);
- if (userHandle == null) {
- Log.e(LOG_TAG, "User handle is null");
- return null;
- }
- PackageInfo packageInfo = AutoPermissionsUtils.getPackageInfo(activity, packageName,
- userHandle);
- if (packageInfo == null) {
- Log.i(LOG_TAG, "PackageInfo is null");
- return null;
- }
- AppPermissionGroup group = AppPermissionGroup.create(context, packageInfo, groupInfo,
- groupPermInfos, false);
-
- if (group == null || !Utils.shouldShowPermission(context, group)) {
- Log.i(LOG_TAG, "Illegal group: " + (group == null ? "null" : group.getName()));
- return null;
- }
-
- return group;
+ public void onCreatePreferences(Bundle bundle, String s) {
+ setPreferenceScreen(getPreferenceManager().createPreferenceScreen(requireContext()));
}
@Override
- public void onCreatePreferences(Bundle bundle, String s) {
- setPreferenceScreen(getPreferenceManager().createPreferenceScreen(requireContext()));
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mPackageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+ mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME);
+ if (mPermGroupName == null) {
+ mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
+ }
+ mUser = getArguments().getParcelable(Intent.EXTRA_USER);
+ mPackageLabel = BidiFormatter.getInstance().unicodeWrap(
+ KotlinUtils.INSTANCE.getPackageLabel(getActivity().getApplication(), mPackageName,
+ mUser));
+ mPermGroupLabel = KotlinUtils.INSTANCE.getPermGroupLabel(getContext(),
+ mPermGroupName).toString();
+ mPackageIcon = KotlinUtils.INSTANCE.getBadgedPackageIcon(getActivity().getApplication(),
+ mPackageName, mUser);
+ setHeaderLabel(
+ requireContext().getString(R.string.app_permission_title, mPermGroupLabel));
}
@Override
@@ -202,14 +162,18 @@
PreferenceScreen screen = getPreferenceScreen();
screen.addPreference(
AutoPermissionsUtils.createHeaderPreference(requireContext(),
- mGroup.getApp().applicationInfo));
+ mPackageIcon, mPackageName, mPackageLabel));
// Add permissions selector preferences.
PreferenceGroup permissionSelector = new PreferenceCategory(requireContext());
permissionSelector.setTitle(
- getString(R.string.app_permission_header, mGroup.getFullLabel()));
+ getString(R.string.app_permission_header, mPermGroupLabel));
screen.addPreference(permissionSelector);
+ mAllowPermissionPreference = new SelectedPermissionPreference(requireContext());
+ mAllowPermissionPreference.setTitle(R.string.app_permission_button_allow);
+ permissionSelector.addPreference(mAllowPermissionPreference);
+
mAlwaysPermissionPreference = new SelectedPermissionPreference(requireContext());
mAlwaysPermissionPreference.setTitle(R.string.app_permission_button_allow_always);
permissionSelector.addPreference(mAlwaysPermissionPreference);
@@ -223,6 +187,35 @@
mDenyPermissionPreference.setTitle(R.string.app_permission_button_deny);
permissionSelector.addPreference(mDenyPermissionPreference);
+ mAllowPermissionPreference.setOnPreferenceClickListener(v -> {
+ checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.ALLOW);
+ setResult(GrantPermissionsViewHandler.GRANTED_ALWAYS);
+ requestChange(AppPermissionViewModel.ChangeRequest.GRANT_FOREGROUND,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW);
+ return true;
+ });
+ mAlwaysPermissionPreference.setOnPreferenceClickListener(v -> {
+ checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.ALLOW_ALWAYS);
+ setResult(GrantPermissionsViewHandler.GRANTED_ALWAYS);
+ requestChange(AppPermissionViewModel.ChangeRequest.GRANT_BOTH,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS);
+ return true;
+ });
+ mForegroundOnlyPermissionPreference.setOnPreferenceClickListener(v -> {
+ checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND);
+ setResult(GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY);
+ requestChange(AppPermissionViewModel.ChangeRequest.GRANT_FOREGROUND_ONLY,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND);
+ return true;
+ });
+ mDenyPermissionPreference.setOnPreferenceClickListener(v -> {
+ checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.DENY);
+ setResult(GrantPermissionsViewHandler.DENIED);
+ requestChange(AppPermissionViewModel.ChangeRequest.REVOKE_BOTH,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY);
+ return true;
+ });
+
mDetailsPreference = new AutoTwoTargetPreference(requireContext());
screen.addPreference(mDetailsPreference);
}
@@ -232,17 +225,11 @@
super.onStart();
Activity activity = requireActivity();
- mPermissionChangeListener = new PermissionChangeListener(
- mGroup.getApp().applicationInfo.uid);
- PackageManager pm = activity.getPackageManager();
- pm.addOnPermissionsChangeListener(mPermissionChangeListener);
-
// Get notified when the package is removed.
- String packageName = mGroup.getApp().packageName;
- mPackageRemovalMonitor = new PackageRemovalMonitor(requireContext(), packageName) {
+ mPackageRemovalMonitor = new PackageRemovalMonitor(requireContext(), mPackageName) {
@Override
public void onPackageRemoved() {
- Log.w(LOG_TAG, packageName + " was uninstalled");
+ Log.w(LOG_TAG, mPackageName + " was uninstalled");
activity.setResult(Activity.RESULT_CANCELED);
activity.finish();
}
@@ -251,18 +238,22 @@
// Check if the package was removed while this activity was not started.
try {
- activity.createPackageContextAsUser(packageName, /* flags= */ 0,
- mGroup.getUser()).getPackageManager().getPackageInfo(packageName,
+ activity.createPackageContextAsUser(mPackageName, /* flags= */ 0,
+ mUser).getPackageManager().getPackageInfo(mPackageName,
/* flags= */ 0);
} catch (PackageManager.NameNotFoundException e) {
- Log.w(LOG_TAG, packageName + " was uninstalled while this activity was stopped", e);
+ Log.w(LOG_TAG, mPackageName + " was uninstalled while this activity was stopped", e);
activity.setResult(Activity.RESULT_CANCELED);
activity.finish();
}
- // Re-create the permission group in case permissions have changed and update the UI.
- mGroup = getAppPermissionGroup();
- updateUi();
+ long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+ AppPermissionViewModelFactory factory = new AppPermissionViewModelFactory(
+ getActivity().getApplication(), mPackageName, mPermGroupName, mUser, sessionId);
+ mViewModel = new ViewModelProvider(this, factory).get(AppPermissionViewModel.class);
+ mViewModel.getButtonStateLiveData().observe(this, this::setRadioButtonsState);
+ mViewModel.getDetailResIdLiveData().observe(this, this::setDetail);
+ mViewModel.getShowAdminSupportLiveData().observe(this, this::setAdminSupportDetail);
}
@Override
@@ -273,453 +264,16 @@
mPackageRemovalMonitor.unregister();
mPackageRemovalMonitor = null;
}
-
- if (mPermissionChangeListener != null) {
- requireActivity().getPackageManager().removeOnPermissionsChangeListener(
- mPermissionChangeListener);
- mPermissionChangeListener = null;
- }
}
- private void updateUi() {
- mDetailsPreference.setOnSecondTargetClickListener(null);
- mDetailsPreference.setVisible(false);
-
- if (mGroup.areRuntimePermissionsGranted()) {
- if (!mGroup.hasPermissionWithBackgroundMode()
- || (mGroup.getBackgroundPermissions() != null
- && mGroup.getBackgroundPermissions().areRuntimePermissionsGranted())) {
- setSelectedPermissionState(mAlwaysPermissionPreference);
- } else {
- setSelectedPermissionState(mForegroundOnlyPermissionPreference);
- }
- } else {
- setSelectedPermissionState(mDenyPermissionPreference);
- }
-
- mAlwaysPermissionPreference.setOnPreferenceClickListener(v -> {
- setResult(GrantPermissionsViewHandler.GRANTED_ALWAYS);
- return requestChange(/* requestGrant= */true, CHANGE_BOTH);
- });
- mForegroundOnlyPermissionPreference.setOnPreferenceClickListener(v -> {
- setResult(GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY);
- requestChange(/* requestGrant= */false, CHANGE_BACKGROUND);
- requestChange(/* requestGrant= */true, CHANGE_FOREGROUND);
- return true;
- });
- mDenyPermissionPreference.setOnPreferenceClickListener(v -> {
- setResult(GrantPermissionsViewHandler.DENIED);
- return requestChange(/* requestGrant= */ false, CHANGE_BOTH);
- });
-
- // Set the allow and foreground-only button states appropriately.
- if (mGroup.hasPermissionWithBackgroundMode()) {
- if (mGroup.getBackgroundPermissions() == null) {
- mAlwaysPermissionPreference.setVisible(false);
- } else {
- mForegroundOnlyPermissionPreference.setVisible(true);
- mAlwaysPermissionPreference.setTitle(R.string.app_permission_button_allow_always);
- }
- } else {
- mForegroundOnlyPermissionPreference.setVisible(false);
- mAlwaysPermissionPreference.setTitle(R.string.app_permission_button_allow);
- }
-
- // Handle the UI for various special cases.
- if (isSystemFixed() || isPolicyFullyFixed() || isForegroundDisabledByPolicy()) {
- // Disable changing permissions and potentially show administrator message.
- mAlwaysPermissionPreference.setEnabled(false);
- mForegroundOnlyPermissionPreference.setEnabled(false);
- mDenyPermissionPreference.setEnabled(false);
-
- RestrictedLockUtils.EnforcedAdmin admin = getAdmin();
- if (admin != null) {
- mDetailsPreference.setWidgetLayoutResource(R.layout.info_preference_widget);
- mDetailsPreference.setOnSecondTargetClickListener(
- preference -> RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
- getContext(), admin));
- }
-
- updateDetailForFixedByPolicyPermissionGroup();
- } else if (Utils.areGroupPermissionsIndividuallyControlled(requireContext(),
- mGroup.getName())) {
- // If the permissions are individually controlled, also show a link to the page that
- // lets you control them.
- mDetailsPreference.setWidgetLayoutResource(R.layout.settings_preference_widget);
- mDetailsPreference.setOnSecondTargetClickListener(
- preference -> showAllPermissions(mGroup.getName()));
-
- updateDetailForIndividuallyControlledPermissionGroup();
- } else {
- if (mGroup.hasPermissionWithBackgroundMode()) {
- if (mGroup.getBackgroundPermissions() == null) {
- // The group has background permissions but the app did not request any. I.e.
- // The app can only switch between 'never" and "only in foreground".
- mAlwaysPermissionPreference.setEnabled(false);
-
- mDenyPermissionPreference.setOnPreferenceClickListener(v -> requestChange(false,
- CHANGE_FOREGROUND));
- } else {
- if (isBackgroundPolicyFixed()) {
- // If background policy is fixed, we only allow switching the foreground.
- // Note that this assumes that the background policy is fixed to deny,
- // since if it is fixed to grant, so is the foreground.
- mAlwaysPermissionPreference.setEnabled(false);
- setSelectedPermissionState(mForegroundOnlyPermissionPreference);
-
- mDenyPermissionPreference.setOnPreferenceClickListener(
- v -> requestChange(false, CHANGE_FOREGROUND));
-
- updateDetailForFixedByPolicyPermissionGroup();
- } else if (isForegroundPolicyFixed()) {
- // Foreground permissions are fixed to allow (the first case above handles
- // fixing to deny), so we only allow toggling background permissions.
- mDenyPermissionPreference.setEnabled(false);
-
- mAlwaysPermissionPreference.setOnPreferenceClickListener(
- v -> requestChange(true, CHANGE_BACKGROUND));
- mForegroundOnlyPermissionPreference.setOnPreferenceClickListener(
- v -> requestChange(false, CHANGE_BACKGROUND));
-
- updateDetailForFixedByPolicyPermissionGroup();
- } else {
- // The default tri-state case is handled by default.
- }
- }
-
- } else {
- // The default bi-state case is handled by default.
- }
- }
- }
-
- /**
- * Set the given permission state as the only checked permission state.
- */
- private void setSelectedPermissionState(@NonNull TwoStatePreference permissionState) {
- permissionState.setChecked(true);
- if (permissionState != mAlwaysPermissionPreference) {
- mAlwaysPermissionPreference.setChecked(false);
- }
- if (permissionState != mForegroundOnlyPermissionPreference) {
- mForegroundOnlyPermissionPreference.setChecked(false);
- }
- if (permissionState != mDenyPermissionPreference) {
- mDenyPermissionPreference.setChecked(false);
- }
- }
-
- /**
- * Are any permissions of this group fixed by the system, i.e. not changeable by the user.
- *
- * @return {@code true} iff any permission is fixed
- */
- private boolean isSystemFixed() {
- return mGroup.isSystemFixed();
- }
-
- /**
- * Is any foreground permissions of this group fixed by the policy, i.e. not changeable by the
- * user.
- *
- * @return {@code true} iff any foreground permission is fixed
- */
- private boolean isForegroundPolicyFixed() {
- return mGroup.isPolicyFixed();
- }
-
- /**
- * Is any background permissions of this group fixed by the policy, i.e. not changeable by the
- * user.
- *
- * @return {@code true} iff any background permission is fixed
- */
- private boolean isBackgroundPolicyFixed() {
- return mGroup.getBackgroundPermissions() != null
- && mGroup.getBackgroundPermissions().isPolicyFixed();
- }
-
- /**
- * Are there permissions fixed, so that the user cannot change the preference at all?
- *
- * @return {@code true} iff the permissions of this group are fixed
- */
- private boolean isPolicyFullyFixed() {
- return isForegroundPolicyFixed() && (mGroup.getBackgroundPermissions() == null
- || isBackgroundPolicyFixed());
- }
-
- /**
- * Is the foreground part of this group disabled. If the foreground is disabled, there is no
- * need to possible grant background access.
- *
- * @return {@code true} iff the permissions of this group are fixed
- */
- private boolean isForegroundDisabledByPolicy() {
- return isForegroundPolicyFixed() && !mGroup.areRuntimePermissionsGranted();
- }
-
- /**
- * Get the app that acts as admin for this profile.
- *
- * @return The admin or {@code null} if there is no admin.
- */
- @Nullable
- private RestrictedLockUtils.EnforcedAdmin getAdmin() {
- return RestrictedLockUtils.getProfileOrDeviceOwner(getContext(), mGroup.getUser());
- }
-
- /**
- * Update the detail in the case the permission group has individually controlled permissions.
- */
- private void updateDetailForIndividuallyControlledPermissionGroup() {
- int revokedCount = 0;
- List<Permission> permissions = mGroup.getPermissions();
- for (Permission permission : permissions) {
- if (!permission.isGrantedIncludingAppOp()) {
- revokedCount++;
- }
- }
-
- int resId;
- if (revokedCount == 0) {
- resId = R.string.permission_revoked_none;
- } else if (revokedCount == permissions.size()) {
- resId = R.string.permission_revoked_all;
- } else {
- resId = R.string.permission_revoked_count;
- }
-
- mDetailsPreference.setSummary(getString(resId, revokedCount));
- mDetailsPreference.setVisible(true);
- }
-
- /**
- * Update the detail of a permission group that is at least partially fixed by policy.
- */
- private void updateDetailForFixedByPolicyPermissionGroup() {
- RestrictedLockUtils.EnforcedAdmin admin = getAdmin();
- AppPermissionGroup backgroundGroup = mGroup.getBackgroundPermissions();
-
- boolean hasAdmin = admin != null;
-
- if (isSystemFixed()) {
- // Permission is fully controlled by the system and cannot be switched
-
- setDetail(R.string.permission_summary_enabled_system_fixed);
- } else if (isForegroundDisabledByPolicy()) {
- // Permission is fully controlled by policy and cannot be switched
-
- if (hasAdmin) {
- setDetail(R.string.disabled_by_admin);
- } else {
- // Disabled state will be displayed by switch, so no need to add text for that
- setDetail(R.string.permission_summary_enforced_by_policy);
- }
- } else if (isPolicyFullyFixed()) {
- // Permission is fully controlled by policy and cannot be switched
-
- if (backgroundGroup == null) {
- if (hasAdmin) {
- setDetail(R.string.enabled_by_admin);
- } else {
- // Enabled state will be displayed by switch, so no need to add text for
- // that
- setDetail(R.string.permission_summary_enforced_by_policy);
- }
- } else {
- if (backgroundGroup.areRuntimePermissionsGranted()) {
- if (hasAdmin) {
- setDetail(R.string.enabled_by_admin);
- } else {
- // Enabled state will be displayed by switch, so no need to add text for
- // that
- setDetail(R.string.permission_summary_enforced_by_policy);
- }
- } else {
- if (hasAdmin) {
- setDetail(
- R.string.permission_summary_enabled_by_admin_foreground_only);
- } else {
- setDetail(
- R.string.permission_summary_enabled_by_policy_foreground_only);
- }
- }
- }
- } else {
- // Part of the permission group can still be switched
-
- if (isBackgroundPolicyFixed()) {
- if (backgroundGroup.areRuntimePermissionsGranted()) {
- if (hasAdmin) {
- setDetail(R.string.permission_summary_enabled_by_admin_background_only);
- } else {
- setDetail(R.string.permission_summary_enabled_by_policy_background_only);
- }
- } else {
- if (hasAdmin) {
- setDetail(R.string.permission_summary_disabled_by_admin_background_only);
- } else {
- setDetail(R.string.permission_summary_disabled_by_policy_background_only);
- }
- }
- } else if (isForegroundPolicyFixed()) {
- if (hasAdmin) {
- setDetail(R.string.permission_summary_enabled_by_admin_foreground_only);
- } else {
- setDetail(R.string.permission_summary_enabled_by_policy_foreground_only);
- }
- }
- }
- }
-
- /**
- * Show the given string as informative text below permission picker preferences.
- *
- * @param strId the resourceId of the string to display.
- */
- private void setDetail(int strId) {
- mDetailsPreference.setSummary(strId);
- mDetailsPreference.setVisible(true);
- }
-
- /**
- * Show all individual permissions in this group in a new fragment.
- */
- private void showAllPermissions(@NonNull String filterGroup) {
- Fragment frag = AutoAllAppPermissionsFragment.newInstance(mGroup.getApp().packageName,
- filterGroup, UserHandle.getUserHandleForUid(mGroup.getApp().applicationInfo.uid));
- requireFragmentManager().beginTransaction()
- .replace(android.R.id.content, frag)
- .addToBackStack("AllPerms")
- .commit();
- }
-
- /**
- * Request to grant/revoke permissions group.
- *
- * <p>Does <u>not</u> handle:
- * <ul>
- * <li>Individually granted permissions</li>
- * <li>Permission groups with background permissions</li>
- * </ul>
- * <p><u>Does</u> handle:
- * <ul>
- * <li>Default grant permissions</li>
- * </ul>
- *
- * @param requestGrant If this group should be granted
- * @param changeTarget Which permission group (foreground/background/both) should be changed
- * @return If the request was processed.
- */
- private boolean requestChange(boolean requestGrant, @ChangeTarget int changeTarget) {
- if (LocationUtils.isLocationGroupAndProvider(getContext(), mGroup.getName(),
- mGroup.getApp().packageName)) {
- LocationUtils.showLocationDialog(getContext(),
- Utils.getAppLabel(mGroup.getApp().applicationInfo, requireContext()));
-
- // The request was denied, so update the buttons.
- updateUi();
- return false;
- }
-
- if (requestGrant) {
- if ((changeTarget & CHANGE_FOREGROUND) != 0) {
- if (!mGroup.areRuntimePermissionsGranted()) {
- SafetyNetLogger.logPermissionToggled(mGroup);
- }
-
- mGroup.grantRuntimePermissions(true, false);
- }
- if ((changeTarget & CHANGE_BACKGROUND) != 0) {
- if (mGroup.getBackgroundPermissions() != null) {
- if (!mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
- SafetyNetLogger.logPermissionToggled(mGroup.getBackgroundPermissions());
- }
-
- mGroup.getBackgroundPermissions().grantRuntimePermissions(true, false);
- }
- }
- } else {
- boolean showDefaultDenyDialog = false;
-
- if ((changeTarget & CHANGE_FOREGROUND) != 0
- && mGroup.areRuntimePermissionsGranted()) {
- showDefaultDenyDialog = mGroup.hasGrantedByDefaultPermission()
- || !mGroup.doesSupportRuntimePermissions()
- || mGroup.hasInstallToRuntimeSplit();
- }
- if ((changeTarget & CHANGE_BACKGROUND) != 0) {
- if (mGroup.getBackgroundPermissions() != null
- && mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
- AppPermissionGroup bgPerm = mGroup.getBackgroundPermissions();
- showDefaultDenyDialog |= bgPerm.hasGrantedByDefaultPermission()
- || !bgPerm.doesSupportRuntimePermissions()
- || bgPerm.hasInstallToRuntimeSplit();
- }
- }
-
- if (showDefaultDenyDialog && !mHasConfirmedRevoke) {
- showDefaultDenyDialog(changeTarget);
- updateUi();
- return false;
- } else {
- if ((changeTarget & CHANGE_FOREGROUND) != 0
- && mGroup.areRuntimePermissionsGranted()) {
- if (mGroup.areRuntimePermissionsGranted()) {
- SafetyNetLogger.logPermissionToggled(mGroup);
- }
-
- mGroup.revokeRuntimePermissions(false);
- }
- if ((changeTarget & CHANGE_BACKGROUND) != 0) {
- if (mGroup.getBackgroundPermissions() != null
- && mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
- if (mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
- SafetyNetLogger.logPermissionToggled(mGroup.getBackgroundPermissions());
- }
-
- mGroup.getBackgroundPermissions().revokeRuntimePermissions(false);
- }
- }
- }
- }
-
- updateUi();
-
- return true;
- }
-
- /**
- * Show a dialog that warns the user that she/he is about to revoke permissions that were
- * granted by default.
- *
- * <p>The order of operation to revoke a permission granted by default is:
- * <ol>
- * <li>{@code showDefaultDenyDialog}</li>
- * <li>{@link DefaultDenyDialog#onCreateDialog}</li>
- * <li>{@link AutoAppPermissionFragment#onDenyAnyWay}</li>
- * </ol>
- *
- * @param changeTarget Whether background or foreground should be changed
- */
- private void showDefaultDenyDialog(@ChangeTarget int changeTarget) {
+ @Override
+ public void showConfirmDialog(AppPermissionViewModel.ChangeRequest changeRequest, int messageId,
+ int buttonPressed, boolean oneTime) {
Bundle args = new Bundle();
- boolean showGrantedByDefaultWarning = false;
- if ((changeTarget & CHANGE_FOREGROUND) != 0) {
- showGrantedByDefaultWarning = mGroup.hasGrantedByDefaultPermission();
- }
- if ((changeTarget & CHANGE_BACKGROUND) != 0) {
- if (mGroup.getBackgroundPermissions() != null) {
- showGrantedByDefaultWarning |=
- mGroup.getBackgroundPermissions().hasGrantedByDefaultPermission();
- }
- }
-
- args.putInt(DefaultDenyDialog.MSG, showGrantedByDefaultWarning ? R.string.system_warning
- : R.string.old_sdk_deny_warning);
- args.putInt(DefaultDenyDialog.CHANGE_TARGET, changeTarget);
+ args.putInt(DefaultDenyDialog.MSG, messageId);
+ args.putSerializable(DefaultDenyDialog.CHANGE_REQUEST, changeRequest);
+ args.putSerializable(DefaultDenyDialog.BUTTON, buttonPressed);
DefaultDenyDialog defaultDenyDialog = new DefaultDenyDialog();
defaultDenyDialog.setArguments(args);
@@ -728,39 +282,97 @@
DefaultDenyDialog.class.getName());
}
+ private void setResult(@GrantPermissionsViewHandler.Result int result) {
+ Intent intent = new Intent()
+ .putExtra(EXTRA_RESULT_PERMISSION_INTERACTED,
+ requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME))
+ .putExtra(EXTRA_RESULT_PERMISSION_RESULT, result);
+ requireActivity().setResult(Activity.RESULT_OK, intent);
+ }
+
+ private void setRadioButtonsState(
+ Map<AppPermissionViewModel.ButtonType, AppPermissionViewModel.ButtonState> states) {
+ setButtonState(mAllowPermissionPreference,
+ states.get(AppPermissionViewModel.ButtonType.ALLOW));
+ setButtonState(mAlwaysPermissionPreference,
+ states.get(AppPermissionViewModel.ButtonType.ALLOW_ALWAYS));
+ setButtonState(mForegroundOnlyPermissionPreference,
+ states.get(AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND));
+ setButtonState(mDenyPermissionPreference,
+ states.get(AppPermissionViewModel.ButtonType.DENY));
+ }
+
+ private void setButtonState(TwoStatePreference button,
+ AppPermissionViewModel.ButtonState state) {
+ button.setVisible(state.isShown());
+ if (state.isShown()) {
+ button.setChecked(state.isChecked());
+ button.setEnabled(state.isEnabled());
+ }
+ }
+
/**
- * Once we user has confirmed that he/she wants to revoke a permission that was granted by
- * default, actually revoke the permissions.
- *
- * @param changeTarget whether to change foreground, background, or both.
- * @see #showDefaultDenyDialog(int)
+ * Helper method to handle the UX edge case where the confirmation dialog is shown and two
+ * buttons are selected at once. This happens since the Auto UI doesn't use a proper radio
+ * group, so there is nothing that enforces that tapping on a button unchecks a previously
+ * checked button. Apart from this case, this UI is not necessary since the UI is entirely
+ * driven by the ViewModel.
*/
- private void onDenyAnyWay(@ChangeTarget int changeTarget) {
- boolean hasDefaultPermissions = false;
- if ((changeTarget & CHANGE_FOREGROUND) != 0) {
- if (mGroup.areRuntimePermissionsGranted()) {
- SafetyNetLogger.logPermissionToggled(mGroup);
- }
+ private void checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType buttonType) {
+ mAllowPermissionPreference.setChecked(
+ buttonType == AppPermissionViewModel.ButtonType.ALLOW);
+ mAlwaysPermissionPreference.setChecked(
+ buttonType == AppPermissionViewModel.ButtonType.ALLOW_ALWAYS);
+ mForegroundOnlyPermissionPreference.setChecked(
+ buttonType == AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND);
+ mDenyPermissionPreference.setChecked(buttonType == AppPermissionViewModel.ButtonType.DENY);
+ }
- mGroup.revokeRuntimePermissions(false);
- hasDefaultPermissions = mGroup.hasGrantedByDefaultPermission();
+ private void setDetail(Pair<Integer, Integer> detailResIds) {
+ if (detailResIds == null) {
+ mDetailsPreference.setVisible(false);
+ return;
}
- if ((changeTarget & CHANGE_BACKGROUND) != 0) {
- if (mGroup.getBackgroundPermissions() != null) {
- if (mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
- SafetyNetLogger.logPermissionToggled(mGroup.getBackgroundPermissions());
- }
+ if (detailResIds.getSecond() != null) {
+ mDetailsPreference.setWidgetLayoutResource(R.layout.settings_preference_widget);
+ mDetailsPreference.setOnSecondTargetClickListener(
+ v -> showAllPermissions(mPermGroupName));
+ mDetailsPreference.setSummary(
+ getString(detailResIds.getFirst(), detailResIds.getSecond()));
+ } else {
+ mDetailsPreference.setSummary(detailResIds.getFirst());
+ }
+ }
- mGroup.getBackgroundPermissions().revokeRuntimePermissions(false);
- hasDefaultPermissions |=
- mGroup.getBackgroundPermissions().hasGrantedByDefaultPermission();
- }
- }
+ /**
+ * Show all individual permissions in this group in a new fragment.
+ */
+ private void showAllPermissions(@NonNull String filterGroup) {
+ Fragment frag = AutoAllAppPermissionsFragment.newInstance(mPackageName,
+ filterGroup, mUser,
+ getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID));
+ requireFragmentManager().beginTransaction()
+ .replace(android.R.id.content, frag)
+ .addToBackStack("AllPerms")
+ .commit();
+ }
- if (hasDefaultPermissions || !mGroup.doesSupportRuntimePermissions()) {
- mHasConfirmedRevoke = true;
+ private void setAdminSupportDetail(RestrictedLockUtils.EnforcedAdmin admin) {
+ if (admin != null) {
+ mDetailsPreference.setWidgetLayoutResource(R.layout.info_preference_widget);
+ mDetailsPreference.setOnSecondTargetClickListener(v ->
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), admin)
+ );
}
- updateUi();
+ }
+
+ /**
+ * Request to grant/revoke permissions group.
+ */
+ private void requestChange(AppPermissionViewModel.ChangeRequest changeRequest,
+ int buttonClicked) {
+ mViewModel.requestChange(/* setOneTime= */false, /* fragment= */ this,
+ /* defaultDeny= */this, changeRequest, buttonClicked);
}
/** Preference used to represent apps that can be picked as a default app. */
@@ -787,12 +399,14 @@
* A dialog warning the user that they are about to deny a permission that was granted by
* default.
*
- * @see #showDefaultDenyDialog(int)
+ * @see #showConfirmDialog(AppPermissionViewModel.ChangeRequest, int, int, boolean)
*/
public static class DefaultDenyDialog extends DialogFragment {
private static final String MSG = DefaultDenyDialog.class.getName() + ".arg.msg";
- private static final String CHANGE_TARGET = DefaultDenyDialog.class.getName()
- + ".arg.changeTarget";
+ private static final String CHANGE_REQUEST = DefaultDenyDialog.class.getName()
+ + ".arg.changeRequest";
+ private static final String BUTTON = DefaultDenyDialog.class.getName()
+ + ".arg.button";
@NonNull
@Override
@@ -801,31 +415,21 @@
return new AlertDialogBuilder(getContext())
.setMessage(requireArguments().getInt(MSG))
.setNegativeButton(R.string.cancel,
- (dialog, which) -> fragment.updateUi())
+ (dialog, which) -> dialog.cancel())
.setPositiveButton(R.string.grant_dialog_button_deny_anyway,
(dialog, which) ->
- fragment.onDenyAnyWay(requireArguments().getInt(CHANGE_TARGET)))
+ fragment.mViewModel.onDenyAnyWay(
+ (AppPermissionViewModel.ChangeRequest)
+ getArguments().getSerializable(CHANGE_REQUEST),
+ getArguments().getInt(BUTTON),
+ false))
.create();
}
- }
-
- /**
- * A listener for permission changes.
- */
- private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener {
- private final int mUid;
-
- PermissionChangeListener(int uid) {
- mUid = uid;
- }
@Override
- public void onPermissionsChanged(int uid) {
- if (uid == mUid) {
- Log.w(LOG_TAG, "Permissions changed.");
- mGroup = getAppPermissionGroup();
- updateUi();
- }
+ public void onCancel(DialogInterface dialog) {
+ AutoAppPermissionFragment fragment = (AutoAppPermissionFragment) getTargetFragment();
+ fragment.setRadioButtonsState(fragment.mViewModel.getButtonStateLiveData().getValue());
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
index fed489e..659ed3d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
@@ -17,52 +17,69 @@
package com.android.permissioncontroller.permission.ui.auto;
import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED;
import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
+import static java.util.concurrent.TimeUnit.DAYS;
+
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
-import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
-import androidx.preference.PreferenceScreen;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.permissioncontroller.PermissionControllerStatsLog;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
-import com.android.permissioncontroller.permission.model.AppPermissionGroup;
-import com.android.permissioncontroller.permission.model.AppPermissions;
+import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
import com.android.permissioncontroller.permission.ui.Category;
import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel;
import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModelFactory;
-import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.permissioncontroller.permission.utils.KotlinUtils;
import java.text.Collator;
+import java.time.Instant;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Random;
/** Screen to show the permissions for a specific application. */
-public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment {
+public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment implements
+ PermissionUsages.PermissionsUsagesChangeCallback {
private static final String LOG_TAG = AutoAppPermissionsFragment.class.getSimpleName();
- private static final String KEY_ALLOWED_PERMISSIONS_GROUP = "allowed_permissions_group";
- private static final String KEY_DENIED_PERMISSIONS_GROUP = "denied_permissions_group";
+ private static final String IS_SYSTEM_PERMS_SCREEN = "_is_system_screen";
+ private static final String KEY_ALLOWED_PERMISSIONS_GROUP = Category.ALLOWED.getCategoryName();
+ private static final String KEY_DENIED_PERMISSIONS_GROUP = Category.DENIED.getCategoryName();
private AppPermissionGroupsViewModel mViewModel;
- private AppPermissions mAppPermissions;
- private PreferenceScreen mExtraScreen;
private String mPackageName;
+ private boolean mIsFirstLoad;
+ private UserHandle mUser;
+ private PermissionUsages mPermissionUsages;
+ private List<AppPermissionUsage> mAppPermissionUsages = new ArrayList<>();
+ private boolean mIsSystemPermsScreen;
private Collator mCollator;
@@ -70,16 +87,19 @@
* @return A new fragment
*/
public static AutoAppPermissionsFragment newInstance(@NonNull String packageName,
- @NonNull UserHandle userHandle) {
+ @NonNull UserHandle userHandle, long sessionId, boolean isSystemPermsScreen) {
return setPackageNameAndUserHandle(new AutoAppPermissionsFragment(), packageName,
- userHandle);
+ userHandle, sessionId, isSystemPermsScreen);
}
private static <T extends Fragment> T setPackageNameAndUserHandle(@NonNull T fragment,
- @NonNull String packageName, @NonNull UserHandle userHandle) {
+ @NonNull String packageName, @NonNull UserHandle userHandle, long sessionId,
+ boolean isSystemPermsScreen) {
Bundle arguments = new Bundle();
arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
arguments.putParcelable(Intent.EXTRA_USER, userHandle);
+ arguments.putLong(EXTRA_SESSION_ID, sessionId);
+ arguments.putBoolean(IS_SYSTEM_PERMS_SCREEN, isSystemPermsScreen);
fragment.setArguments(arguments);
return fragment;
}
@@ -90,6 +110,8 @@
setLoading(true);
mPackageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+ mUser = getArguments().getParcelable(Intent.EXTRA_USER);
+ mIsSystemPermsScreen = getArguments().getBoolean(IS_SYSTEM_PERMS_SCREEN, true);
UserHandle userHandle = getArguments().getParcelable(Intent.EXTRA_USER);
Activity activity = requireActivity();
PackageInfo packageInfo = AutoPermissionsUtils.getPackageInfo(activity, mPackageName,
@@ -101,8 +123,6 @@
return;
}
- mAppPermissions = new AppPermissions(activity, packageInfo, /* sortGroups= */ true,
- () -> getActivity().finish());
mCollator = Collator.getInstance(
getContext().getResources().getConfiguration().getLocales().get(0));
AppPermissionGroupsViewModelFactory factory =
@@ -111,13 +131,27 @@
mViewModel = new ViewModelProvider(this, factory).get(AppPermissionGroupsViewModel.class);
setHeaderLabel(getContext().getString(R.string.app_permissions));
- setAction(getContext().getString(R.string.all_permissions), v -> showAllPermissions());
+ if (mIsSystemPermsScreen) {
+ setAction(getContext().getString(R.string.all_permissions), v -> showAllPermissions());
+ }
createPreferenceCategories(packageInfo);
mViewModel.getPackagePermGroupsLiveData().observe(this, this::updatePreferences);
if (mViewModel.getPackagePermGroupsLiveData().getValue() != null) {
updatePreferences(mViewModel.getPackagePermGroupsLiveData().getValue());
}
+
+ if (SdkLevel.isAtLeastS()) {
+ mPermissionUsages = new PermissionUsages(getContext());
+
+ long filterTimeBeginMillis = Math.max(System.currentTimeMillis()
+ - DAYS.toMillis(
+ AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS),
+ Instant.EPOCH.toEpochMilli());
+ mPermissionUsages.load(null, null, filterTimeBeginMillis, Long.MAX_VALUE,
+ PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(),
+ false, false, this, false);
+ }
}
@Override
@@ -125,10 +159,27 @@
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
}
+
+ @Override
+ @RequiresApi(Build.VERSION_CODES.S)
+ public void onPermissionUsagesChanged() {
+ if (mPermissionUsages.getUsages().isEmpty()) {
+ return;
+ }
+ if (getContext() == null) {
+ // Async result has come in after our context is gone.
+ return;
+ }
+
+ mAppPermissionUsages = new ArrayList<>(mPermissionUsages.getUsages());
+ updatePreferences(mViewModel.getPackagePermGroupsLiveData().getValue());
+ }
+
private void showAllPermissions() {
Fragment frag = AutoAllAppPermissionsFragment.newInstance(
getArguments().getString(Intent.EXTRA_PACKAGE_NAME),
- getArguments().getParcelable(Intent.EXTRA_USER));
+ getArguments().getParcelable(Intent.EXTRA_USER),
+ getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID));
getFragmentManager().beginTransaction()
.replace(android.R.id.content, frag)
.addToBackStack("AllPerms")
@@ -155,10 +206,8 @@
bindUi(packageInfo);
}
- // TODO(b/179383241): Make full use of groupMap data in this method
private void updatePreferences(
Map<Category, List<AppPermissionGroupsViewModel.GroupUiInfo>> groupMap) {
- mAppPermissions.refresh();
Context context = getPreferenceManager().getContext();
if (context == null) {
return;
@@ -173,108 +222,73 @@
return;
}
- PreferenceCategory allowed = findPreference(KEY_ALLOWED_PERMISSIONS_GROUP);
- PreferenceCategory denied = findPreference(KEY_DENIED_PERMISSIONS_GROUP);
+ Map<String, Long> groupUsageLastAccessTime = new HashMap<>();
+ mViewModel.extractGroupUsageLastAccessTime(groupUsageLastAccessTime, mAppPermissionUsages,
+ mPackageName);
- allowed.removeAll();
- denied.removeAll();
-
- if (mExtraScreen != null) {
- mExtraScreen.removeAll();
- mExtraScreen.addPreference(AutoPermissionsUtils.createHeaderPreference(getContext(),
- mAppPermissions.getPackageInfo().applicationInfo));
- }
-
- Preference extraPerms = new Preference(context);
- extraPerms.setIcon(R.drawable.ic_toc);
- extraPerms.setTitle(R.string.additional_permissions);
- boolean extraPermsAreAllowed = false;
-
- ArrayList<AppPermissionGroup> groups = new ArrayList<>(
- mAppPermissions.getPermissionGroups());
- groups.sort((x, y) -> mCollator.compare(x.getLabel(), y.getLabel()));
- allowed.setOrderingAsAdded(true);
- denied.setOrderingAsAdded(true);
-
- for (int i = 0; i < groups.size(); i++) {
- AppPermissionGroup group = groups.get(i);
- if (!Utils.shouldShowPermission(getContext(), group)) {
+ for (Category grantCategory : groupMap.keySet()) {
+ if (Category.ASK.equals(grantCategory)) {
+ // skip ask category for auto
continue;
}
+ PreferenceCategory category = getPreferenceScreen().findPreference(
+ grantCategory.getCategoryName());
+ if (grantCategory.equals(Category.ALLOWED_FOREGROUND)) {
+ category = findPreference(Category.ALLOWED.getCategoryName());
+ }
+ int numExtraPerms = 0;
- boolean isPlatform = group.getDeclaringPackage().equals(Utils.OS_PKG);
+ category.removeAll();
- Preference preference = createPermissionPreference(getContext(), group);
- if (isPlatform) {
- PreferenceCategory category =
- group.areRuntimePermissionsGranted() ? allowed : denied;
- category.addPreference(preference);
- } else {
- if (mExtraScreen == null) {
- mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
- mExtraScreen.addPreference(
- AutoPermissionsUtils.createHeaderPreference(getContext(),
- mAppPermissions.getPackageInfo().applicationInfo));
- }
- mExtraScreen.addPreference(preference);
- if (group.areRuntimePermissionsGranted()) {
- extraPermsAreAllowed = true;
+
+ for (AppPermissionGroupsViewModel.GroupUiInfo groupInfo : groupMap.get(grantCategory)) {
+ if (groupInfo.isSystem() == mIsSystemPermsScreen) {
+ Preference preference = createPermissionPreference(getContext(), groupInfo,
+ groupUsageLastAccessTime);
+ category.addPreference(preference);
+ } else if (!groupInfo.isSystem()) {
+ numExtraPerms++;
}
}
+
+
+ if (numExtraPerms > 0) {
+ setAdditionalPermissionsPreference(category, numExtraPerms, context);
+ }
+
+ if (category.getPreferenceCount() == 0) {
+ setNoPermissionPreference(category, grantCategory, context);
+ }
+
+ KotlinUtils.INSTANCE.sortPreferenceGroup(category, this::comparePreferences, false);
}
- if (mExtraScreen != null) {
- extraPerms.setOnPreferenceClickListener(preference -> {
- AutoAppPermissionsFragment.AdditionalPermissionsFragment
- frag = new AutoAppPermissionsFragment.AdditionalPermissionsFragment();
- setPackageNameAndUserHandle(frag,
- getArguments().getString(Intent.EXTRA_PACKAGE_NAME),
- getArguments().getParcelable(Intent.EXTRA_USER));
- frag.setTargetFragment(AutoAppPermissionsFragment.this, 0);
- getFragmentManager().beginTransaction()
- .replace(android.R.id.content, frag)
- .addToBackStack(null)
- .commit();
- return true;
- });
- // Delete 1 to account for app header preference.
- int count = mExtraScreen.getPreferenceCount() - 1;
- extraPerms.setSummary(getResources().getQuantityString(
- R.plurals.additional_permissions_more, count,
- count));
- PreferenceCategory category = extraPermsAreAllowed ? allowed : denied;
- category.addPreference(extraPerms);
- }
- if (allowed.getPreferenceCount() == 0) {
- Preference empty = new Preference(context);
- empty.setTitle(getString(R.string.no_permissions_allowed));
- empty.setSelectable(false);
- allowed.addPreference(empty);
+ if (mIsFirstLoad) {
+ logAppPermissionsFragmentView();
+ mIsFirstLoad = false;
}
- if (denied.getPreferenceCount() == 0) {
- Preference empty = new Preference(context);
- empty.setTitle(getString(R.string.no_permissions_denied));
- empty.setSelectable(false);
- denied.addPreference(empty);
- }
-
setLoading(false);
}
- private Preference createPermissionPreference(Context context, AppPermissionGroup group) {
+ private Preference createPermissionPreference(Context context,
+ AppPermissionGroupsViewModel.GroupUiInfo groupInfo,
+ Map<String, Long> groupUsageLastAccessTime) {
+ String groupName = groupInfo.getGroupName();
Preference preference = new Preference(context);
- Drawable icon = Utils.loadDrawable(context.getPackageManager(),
- group.getIconPkg(), group.getIconResId());
- preference.setKey(group.getName());
- preference.setTitle(group.getFullLabel());
- preference.setIcon(Utils.applyTint(context, icon, android.R.attr.colorControlNormal));
- preference.setSummary(getPreferenceSummary(group));
+ preference.setTitle(KotlinUtils.INSTANCE.getPermGroupLabel(context, groupName));
+ preference.setIcon(KotlinUtils.INSTANCE.getPermGroupIcon(context, groupName));
+ preference.setKey(groupName);
+ String summary = mViewModel.getPreferenceSummary(groupInfo, context,
+ groupUsageLastAccessTime.get(groupName));
+ if (!summary.isEmpty()) {
+ preference.setSummary(summary);
+ }
preference.setOnPreferenceClickListener(pref -> {
Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, group.getApp().packageName);
- intent.putExtra(Intent.EXTRA_PERMISSION_NAME, group.getPermissions().get(0).getName());
- intent.putExtra(Intent.EXTRA_USER, group.getUser());
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName);
+ intent.putExtra(Intent.EXTRA_PERMISSION_NAME, groupName);
+ intent.putExtra(Intent.EXTRA_USER, mUser);
intent.putExtra(EXTRA_CALLER_NAME, AutoAppPermissionsFragment.class.getName());
context.startActivity(intent);
return true;
@@ -282,39 +296,107 @@
return preference;
}
- private String getPreferenceSummary(AppPermissionGroup group) {
- return getGroupSummary(group);
+ private void setAdditionalPermissionsPreference(PreferenceCategory category, int numExtraPerms,
+ Context context) {
+ Preference extraPerms = new Preference(context);
+ extraPerms.setIcon(R.drawable.ic_toc);
+ extraPerms.setTitle(R.string.additional_permissions);
+ extraPerms.setOnPreferenceClickListener(preference -> {
+ AutoAppPermissionsFragment
+ frag = AutoAppPermissionsFragment.newInstance(mPackageName, mUser,
+ getArguments().getLong(EXTRA_SESSION_ID), false);
+ frag.setTargetFragment(AutoAppPermissionsFragment.this, 0);
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, frag)
+ .addToBackStack(null)
+ .commit();
+ return true;
+ });
+ extraPerms.setSummary(getResources().getQuantityString(
+ R.plurals.additional_permissions_more, numExtraPerms,
+ numExtraPerms));
+ category.addPreference(extraPerms);
}
- private String getGroupSummary(AppPermissionGroup group) {
- if (group.hasPermissionWithBackgroundMode() && group.areRuntimePermissionsGranted()) {
- AppPermissionGroup backgroundGroup = group.getBackgroundPermissions();
- if (backgroundGroup == null || !backgroundGroup.areRuntimePermissionsGranted()) {
- return getContext().getString(R.string.permission_subtitle_only_in_foreground);
+ private void setNoPermissionPreference(PreferenceCategory category, Category grantCategory,
+ Context context) {
+ Preference empty = new Preference(context);
+ empty.setKey(getString(grantCategory.equals(Category.DENIED)
+ ? R.string.no_permissions_denied : R.string.no_permissions_allowed));
+ empty.setTitle(empty.getKey());
+ empty.setSelectable(false);
+ category.addPreference(empty);
+ }
+
+ private int comparePreferences(Preference lhs, Preference rhs) {
+ String additionalTitle = lhs.getContext().getString(R.string.additional_permissions);
+ if (lhs.getTitle().equals(additionalTitle)) {
+ return 1;
+ } else if (rhs.getTitle().equals(additionalTitle)) {
+ return -1;
+ }
+ return mCollator.compare(lhs.getTitle().toString(),
+ rhs.getTitle().toString());
+ }
+
+ private void logAppPermissionsFragmentView() {
+ Context context = getPreferenceManager().getContext();
+ if (context == null) {
+ return;
+ }
+ String permissionSubtitleOnlyInForeground =
+ context.getString(R.string.permission_subtitle_only_in_foreground);
+
+
+ long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+ long viewId = new Random().nextLong();
+
+ PreferenceCategory allowed = findPreference(KEY_ALLOWED_PERMISSIONS_GROUP);
+
+ int numAllowed = allowed.getPreferenceCount();
+ for (int i = 0; i < numAllowed; i++) {
+ Preference preference = allowed.getPreference(i);
+ if (preference.getTitle().equals(getString(R.string.no_permissions_allowed))) {
+ // R.string.no_permission_allowed was added to PreferenceCategory
+ continue;
}
+
+ int category = APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED;
+ if (preference.getSummary() != null
+ && permissionSubtitleOnlyInForeground.contentEquals(preference.getSummary())) {
+ category = APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
+ }
+
+ logAppPermissionsFragmentViewEntry(sessionId, viewId, preference.getKey(), category);
}
- return null;
+
+ PreferenceCategory denied = findPreference(KEY_DENIED_PERMISSIONS_GROUP);
+
+ int numDenied = denied.getPreferenceCount();
+ for (int i = 0; i < numDenied; i++) {
+ Preference preference = denied.getPreference(i);
+ if (preference.getTitle().equals(getString(R.string.no_permissions_denied))) {
+ // R.string.no_permission_denied was added to PreferenceCategory
+ continue;
+ }
+ logAppPermissionsFragmentViewEntry(sessionId, viewId, preference.getKey(),
+ APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED);
+ }
}
- /**
- * Class that shows additional permissions.
- */
- public static class AdditionalPermissionsFragment extends AutoSettingsFrameFragment {
- AutoAppPermissionsFragment mOuterFragment;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- // Set this before calling super.onCreate as it is needed in onCreatePreferences
- // (which is called from super.onCreate).
- mOuterFragment = (AutoAppPermissionsFragment) getTargetFragment();
- super.onCreate(savedInstanceState);
- setHeaderLabel(mOuterFragment.getHeaderLabel());
+ private void logAppPermissionsFragmentViewEntry(
+ long sessionId, long viewId, String permissionGroupName, int category) {
+ Integer uid = KotlinUtils.INSTANCE.getPackageUid(getActivity().getApplication(),
+ mPackageName, mUser);
+ if (uid == null) {
+ return;
}
-
- @Override
- public void onCreatePreferences(Bundle bundle, String s) {
- setPreferenceScreen(mOuterFragment.mExtraScreen);
- }
+ PermissionControllerStatsLog.write(APP_PERMISSIONS_FRAGMENT_VIEWED, sessionId, viewId,
+ permissionGroupName, uid, mPackageName, category);
+ Log.v(LOG_TAG, "AutoAppPermissionFragment view logged with sessionId=" + sessionId
+ + " viewId=" + viewId + " permissionGroupName=" + permissionGroupName + " uid="
+ + uid + " packageName="
+ + mPackageName + " category=" + category);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageCustomPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageCustomPermissionsFragment.java
deleted file mode 100644
index 0de51c8..0000000
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageCustomPermissionsFragment.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2019 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.auto;
-
-import com.android.permissioncontroller.R;
-
-/** Shows additional non-system permissions that can be granted/denied. */
-public class AutoManageCustomPermissionsFragment extends AutoManagePermissionsFragment {
-
- @Override
- protected int getScreenHeaderRes() {
- return R.string.additional_permissions;
- }
-
- @Override
- protected void updatePermissionsUi() {
- updatePermissionsUi(/* addSystemPermissions= */ false);
- }
-}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageOtherPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageOtherPermissionsFragment.java
new file mode 100644
index 0000000..e603399
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageOtherPermissionsFragment.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2021 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.auto;
+
+import android.app.Application;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
+import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo;
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils;
+
+import java.util.List;
+
+/**
+ * Shows additional non-system permissions that can be granted/denied.
+ *
+ * Largely based on
+ * {@link com.android.permissioncontroller.permission.ui.television.ManagePermissionsOtherFragment}.
+ */
+public class AutoManageOtherPermissionsFragment extends AutoSettingsFrameFragment {
+ private static final String KEY_UNUSED_CATEGORY = "category_unused";
+ private static final String KEY_ADDITIONAL_CATEGORY = "category_additional";
+
+ private PreferenceCategory mUnusedCategory;
+ private PreferenceCategory mAdditionalCategory;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setLoading(true);
+ setHeaderLabel(getString(R.string.other_permissions_label));
+
+ final Application application = getActivity().getApplication();
+ final ViewModelProvider.Factory factory =
+ ViewModelProvider.AndroidViewModelFactory.getInstance(application);
+ final ManagePermissionsViewModel viewModel = new ViewModelProvider(this, factory)
+ .get(ManagePermissionsViewModel.class);
+
+ viewModel.getUnusedPermissionGroups().observe(this, this::onUnusedPermissionGroupsChanged);
+ viewModel.getAdditionalPermissionGroups().observe(this,
+ this::onAdditionalPermissionGroupsChanged);
+ viewModel.hasUnusedOrAdditionalPermissionGroups().observe(this,
+ this::onHasUnusedOrAdditionalPermissionGroups);
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle bundle, String s) {
+ setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ final Context context = getPreferenceManager().getContext();
+ final PreferenceScreen screen = getPreferenceScreen();
+ // We add categories here, but make them invisible until the data is loaded.
+ mUnusedCategory = new PreferenceCategory(context);
+ mUnusedCategory.setKey(KEY_UNUSED_CATEGORY);
+ mUnusedCategory.setTitle(R.string.not_used_permissions_label);
+ mUnusedCategory.setSummary(R.string.not_used_permissions_description);
+ mUnusedCategory.setVisible(false);
+ screen.addPreference(mUnusedCategory);
+
+ mAdditionalCategory = new PreferenceCategory(context);
+ mAdditionalCategory.setKey(KEY_ADDITIONAL_CATEGORY);
+ mAdditionalCategory.setTitle(R.string.additional_permissions_label);
+ mAdditionalCategory.setSummary(R.string.additional_permissions_description);
+ mAdditionalCategory.setVisible(false);
+ screen.addPreference(mAdditionalCategory);
+ }
+
+ private void onUnusedPermissionGroupsChanged(List<PermGroupPackagesUiInfo> permissionGroups) {
+ updateCategory(mUnusedCategory, permissionGroups);
+ }
+
+ private void onAdditionalPermissionGroupsChanged(
+ List<PermGroupPackagesUiInfo> permissionGroups) {
+ updateCategory(mAdditionalCategory, permissionGroups);
+ }
+
+ private void onHasUnusedOrAdditionalPermissionGroups(Boolean hasPGs) {
+ if (!hasPGs) {
+ // There are not more permissions on this screen - go back.
+ getParentFragmentManager().popBackStack();
+ }
+ }
+
+ private void updateCategory(PreferenceCategory category,
+ List<PermGroupPackagesUiInfo> permissionGroups) {
+ final Context context = getPreferenceManager().getContext();
+ if (context == null) {
+ return;
+ }
+
+ PermissionGroupPreferenceUtils.updateGroupOfPermissionPreferences(context, category,
+ permissionGroups);
+ // Only show the category if it's not empty.
+ category.setVisible(!permissionGroups.isEmpty());
+
+ setLoading(false);
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManagePermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManagePermissionsFragment.java
deleted file mode 100644
index 49ea5eb..0000000
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManagePermissionsFragment.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2019 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.auto;
-
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.preference.Preference;
-
-import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
-import com.android.permissioncontroller.permission.model.legacy.PermissionGroup;
-import com.android.permissioncontroller.permission.model.legacy.PermissionGroups;
-import com.android.permissioncontroller.permission.utils.Utils;
-
-import java.text.Collator;
-import java.util.ArrayList;
-
-/** Base class to show the list of permissions that can be granted/denied. */
-abstract class AutoManagePermissionsFragment extends AutoSettingsFrameFragment implements
- PermissionGroups.PermissionsGroupsChangeCallback, Preference.OnPreferenceClickListener {
-
- private static final String LOG_TAG = "ManagePermissionsFragment";
-
- static final String OS_PKG = "android";
-
- private PermissionGroups mPermissions;
-
- private Collator mCollator;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setLoading(true);
-
- mPermissions = new PermissionGroups(getContext(), requireActivity().getLoaderManager(),
- /* callback= */ this, /* getAppUiInfo= */ false,
- /* getNonPlatformPermissions= */ true);
- mCollator = Collator.getInstance(
- getContext().getResources().getConfiguration().getLocales().get(0));
-
- setHeaderLabel(getString(getScreenHeaderRes()));
- }
-
- @Override
- public void onCreatePreferences(Bundle bundle, String s) {
- setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
- }
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- String key = preference.getKey();
-
- PermissionGroup group = mPermissions.getGroup(key);
- if (group == null) {
- return false;
- }
-
- Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
- .putExtra(Intent.EXTRA_PERMISSION_NAME, key);
- try {
- getActivity().startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Log.w(LOG_TAG, "No app to handle " + intent);
- }
-
- return true;
- }
-
- /** Returns the header string resource. */
- @StringRes
- protected abstract int getScreenHeaderRes();
-
- /** Returns the current permissions. */
- protected PermissionGroups getPermissions() {
- return mPermissions;
- }
-
- @Override
- public void onPermissionGroupsChanged() {
- updatePermissionsUi();
- }
-
- /** Update the preferences to show the new {@link #getPermissions() permissions}. */
- protected abstract void updatePermissionsUi();
-
- /**
- * Add preferences for all permissions of a type to the preference screen.
- */
- protected void updatePermissionsUi(boolean addSystemPermissions) {
- Context context = getPreferenceManager().getContext();
- if (context == null || getActivity() == null) {
- return;
- }
-
- ArrayList<PermissionGroup> groups = new ArrayList<>(mPermissions.getGroups());
- groups.sort((x, y) -> mCollator.compare(x.getLabel(), y.getLabel()));
- getPreferenceScreen().removeAll();
- getPreferenceScreen().setOrderingAsAdded(true);
-
- // Use this to speed up getting the info for all of the PermissionApps below.
- // Create a new one for each refresh to make sure it has fresh data.
- for (int i = 0; i < groups.size(); i++) {
- PermissionGroup group = groups.get(i);
- boolean isSystemPermission = group.getDeclaringPackage().equals(OS_PKG);
-
- if (addSystemPermissions == isSystemPermission) {
- Preference preference = findPreference(group.getName());
-
- if (preference == null) {
- preference = new Preference(context);
- preference.setOnPreferenceClickListener(this);
- preference.setKey(group.getName());
- preference.setIcon(Utils.applyTint(context, group.getIcon(),
- android.R.attr.colorControlNormal));
- preference.setTitle(group.getLabel());
- // Set blank summary so that no resizing/jumping happens when the summary is
- // loaded.
- preference.setSummary(" ");
- preference.setPersistent(false);
- getPreferenceScreen().addPreference(preference);
- }
- preference.setSummary(
- getString(R.string.app_permissions_group_summary, group.getGranted(),
- group.getTotal()));
- }
- }
- if (getPreferenceScreen().getPreferenceCount() != 0) {
- setLoading(false);
- }
- }
-}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java
index c3d9ed2..8bbcde6 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoManageStandardPermissionsFragment.java
@@ -16,31 +16,43 @@
package com.android.permissioncontroller.permission.ui.auto;
+
import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
import android.app.Application;
+import android.content.Context;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.permission.model.legacy.PermissionGroup;
+import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
+import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo;
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment;
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel;
import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils;
import com.android.permissioncontroller.permission.utils.Utils;
import java.util.List;
-/** Shows the standard permissions that can be granted/denied. */
-public class AutoManageStandardPermissionsFragment extends AutoManagePermissionsFragment {
+/**
+ * Shows the standard permissions that can be granted/denied.
+ *
+ * Largely based on the implementation of
+ * {@link com.android.permissioncontroller.permission.ui.television.ManagePermissionsFragment}.
+ */
+public class AutoManageStandardPermissionsFragment extends AutoSettingsFrameFragment {
- private static final String EXTRA_PREFS_KEY = "extra_prefs_key";
- private static final String AUTO_REVOKE_KEY = "auto_revoke_key";
- private ManageStandardPermissionsViewModel mViewModel;
+ private static final String KEY_OTHER_PERMISSIONS = "other_permissions";
+ private static final String KEY_AUTO_REVOKE = "auto_revoke_key";
+ private ManagePermissionsViewModel mManagePermissionsViewModel;
+ private ManageStandardPermissionsViewModel mManageStandardPermissionsViewModel;
/** Returns a new instance of {@link AutoManageStandardPermissionsFragment}. */
public static AutoManageStandardPermissionsFragment newInstance() {
@@ -50,84 +62,108 @@
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setLoading(true);
+ setHeaderLabel(getString(R.string.app_permission_manager));
final Application application = getActivity().getApplication();
- mViewModel = new ViewModelProvider(this,
+ final ViewModelProvider.Factory viewModelFactory =
+ ViewModelProvider.AndroidViewModelFactory.getInstance(application);
+ mManagePermissionsViewModel = new ViewModelProvider(this, viewModelFactory)
+ .get(ManagePermissionsViewModel.class);
+ mManageStandardPermissionsViewModel = new ViewModelProvider(this,
ViewModelProvider.AndroidViewModelFactory.getInstance(application))
.get(ManageStandardPermissionsViewModel.class);
-
- mViewModel.getUiDataLiveData().observe(this, permissionGroups -> {
- if (permissionGroups != null) {
- updatePermissionsUi();
- } else {
- getActivity().finish();
- }
- });
-
- mViewModel.getNumAutoRevoked().observe(this, show -> updatePermissionsUi());
+ mManagePermissionsViewModel.getUsedPermissionGroups().observe(this,
+ this::onPermissionGroupsChanged);
+ mManageStandardPermissionsViewModel.getNumAutoRevoked().observe(this,
+ this::onNumAutoRevokedChanged);
}
@Override
- protected int getScreenHeaderRes() {
- return R.string.app_permission_manager;
+ public void onCreatePreferences(Bundle bundle, String s) {
+ setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
}
- @Override
- protected void updatePermissionsUi() {
- updatePermissionsUi(/* addSystemPermissions= */ true);
-
- // Check if we need an additional permissions preference
- List<PermissionGroup> groups = getPermissions().getGroups();
- int numExtraPermissions = 0;
- for (PermissionGroup group : groups) {
- if (!group.getDeclaringPackage().equals(AutoManagePermissionsFragment.OS_PKG)) {
- numExtraPermissions++;
- }
+ private void onPermissionGroupsChanged(List<PermGroupPackagesUiInfo> permissionGroups) {
+ final Context context = getPreferenceManager().getContext();
+ if (context == null) {
+ return;
}
- Preference additionalPermissionsPreference = getPreferenceScreen().findPreference(
- EXTRA_PREFS_KEY);
- if (numExtraPermissions == 0) {
- if (additionalPermissionsPreference != null) {
- getPreferenceScreen().removePreference(additionalPermissionsPreference);
- }
+ final PreferenceScreen screen = getPreferenceScreen();
+
+ // First check if "Other preferences" button exists. If it does: remove it, but hold on to
+ // the reference - we'll it add back later, after the preferences for the permission groups
+ // have been updated. If it does not exist: create and hold on to it.
+ Preference otherPermissionsPreference = screen.findPreference(KEY_OTHER_PERMISSIONS);
+ if (otherPermissionsPreference == null) {
+ otherPermissionsPreference = buildOtherPermissionsPreference(context);
} else {
- if (additionalPermissionsPreference == null) {
- additionalPermissionsPreference = new Preference(
- getPreferenceManager().getContext());
- additionalPermissionsPreference.setKey(EXTRA_PREFS_KEY);
- additionalPermissionsPreference.setIcon(Utils.applyTint(getActivity(),
- R.drawable.ic_more_items,
- android.R.attr.colorControlNormal));
- additionalPermissionsPreference.setTitle(R.string.additional_permissions);
- additionalPermissionsPreference.setOnPreferenceClickListener(preference -> {
- AutoManageCustomPermissionsFragment frag =
- new AutoManageCustomPermissionsFragment();
- frag.setTargetFragment(AutoManageStandardPermissionsFragment.this,
- /* requestCode= */ 0);
- FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.replace(android.R.id.content, frag);
- ft.addToBackStack(null);
- ft.commit();
- return true;
- });
-
- getPreferenceScreen().addPreference(additionalPermissionsPreference);
- }
-
- additionalPermissionsPreference.setSummary(getResources().getQuantityString(
- R.plurals.additional_permissions_more, numExtraPermissions,
- numExtraPermissions));
+ screen.removePreference(otherPermissionsPreference);
+ // The PreferenceScreen is ordering items as added
+ // (see PreferenceGroup#setOrderingAsAdded()), which means that it assigns positional
+ // indexes ("order") to Preferences incrementally as they are added, BUT ONLY IF their
+ // current "order" is the DEFAULT_ORDER.
+ // However, when the Preferences are removed from the group they keep their "order" and
+ // thus when they are re-added to a group (same or another) their "order" does not get
+ // re-assigned, so they may show up at the position they previously were at.
+ // We want the otherPermissionsPreference to always be the last in the list, so reset
+ // its "order" to DEFAULT, so that we add last to the group, it indeed goes into the
+ // last position.
+ otherPermissionsPreference.setOrder(Preference.DEFAULT_ORDER);
}
- Integer numAutoRevoked = mViewModel.getNumAutoRevoked().getValue();
+ PermissionGroupPreferenceUtils.updateGroupOfPermissionPreferences(context, screen,
+ permissionGroups);
- Preference autoRevokePreference = getPreferenceScreen().findPreference(AUTO_REVOKE_KEY);
+ screen.addPreference(otherPermissionsPreference);
+
+ // load initial auto-revoke count, if it is ready
+ Integer numAutoRevoked = mManageStandardPermissionsViewModel.getNumAutoRevoked().getValue();
+ onNumAutoRevokedChanged(numAutoRevoked);
+
+ setLoading(false);
+ }
+
+
+ private Preference buildOtherPermissionsPreference(Context context) {
+ final Preference preference = new Preference(context);
+ preference.setPersistent(false);
+ preference.setKey(KEY_OTHER_PERMISSIONS);
+ preference.setTitle(R.string.other_permissions_label);
+ preference.setIcon(
+ Utils.applyTint(
+ context, R.drawable.ic_more_items, android.R.attr.colorControlNormal));
+ preference.setOnPreferenceClickListener(p -> {
+ AutoManageOtherPermissionsFragment frag =
+ new AutoManageOtherPermissionsFragment();
+ frag.setTargetFragment(AutoManageStandardPermissionsFragment.this,
+ /* requestCode= */ 0);
+ FragmentTransaction ft = getFragmentManager().beginTransaction();
+ ft.replace(android.R.id.content, frag);
+ ft.addToBackStack(null);
+ ft.commit();
+ return true;
+ });
+ // Make invisible for now and subscribe to the LiveData that tracks whether there are any
+ // unused or additional permissions.
+ preference.setVisible(false);
+ mManagePermissionsViewModel.hasUnusedOrAdditionalPermissionGroups().observe(this,
+ preference::setVisible);
+ return preference;
+ }
+
+ private void onNumAutoRevokedChanged(Integer numAutoRevoked) {
+ // to prevent ui jank, don't display auto-revoke until categories have loaded
+ if (mManagePermissionsViewModel.getUsedPermissionGroups().getValue() == null) {
+ return;
+ }
+ Preference autoRevokePreference = getPreferenceScreen().findPreference(KEY_AUTO_REVOKE);
if (numAutoRevoked != null && numAutoRevoked != 0) {
if (autoRevokePreference == null) {
autoRevokePreference = new Preference(getPreferenceManager().getContext());
autoRevokePreference.setOrder(-1);
- autoRevokePreference.setKey(AUTO_REVOKE_KEY);
+ autoRevokePreference.setKey(KEY_AUTO_REVOKE);
autoRevokePreference.setSingleLineTitle(false);
autoRevokePreference.setIcon(R.drawable.ic_info_outline);
autoRevokePreference.setTitle(
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java
index dfd5361..94fd6b9 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionAppsFragment.java
@@ -16,82 +16,272 @@
package com.android.permissioncontroller.permission.ui.auto;
+import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
+import static com.android.permissioncontroller.permission.ui.Category.ALLOWED;
+import static com.android.permissioncontroller.permission.ui.Category.ALLOWED_FOREGROUND;
+import static com.android.permissioncontroller.permission.ui.Category.ASK;
+import static com.android.permissioncontroller.permission.ui.Category.DENIED;
+import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
+
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
+import android.os.UserHandle;
import android.util.ArrayMap;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceGroup;
+import com.android.modules.utils.build.SdkLevel;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
-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.ui.handheld.PermissionAppsFragment;
-import com.android.permissioncontroller.permission.ui.handheld.PermissionControlPreference;
+import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
+import com.android.permissioncontroller.permission.ui.Category;
+import com.android.permissioncontroller.permission.ui.handheld.SmartIconLoadPackagePermissionPreference;
+import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModelFactory;
+import com.android.permissioncontroller.permission.utils.KotlinUtils;
import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.settingslib.utils.applications.AppUtils;
import java.text.Collator;
import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
+import java.util.Random;
+
+import kotlin.Pair;
/** Shows the list of applications which have (or do not have) the given permission. */
-public class AutoPermissionAppsFragment extends AutoSettingsFrameFragment implements Callback {
+public class AutoPermissionAppsFragment extends AutoSettingsFrameFragment implements
+ PermissionUsages.PermissionsUsagesChangeCallback {
- private static final String KEY_SHOW_SYSTEM_PREFS = "_showSystem";
- private static final String KEY_ALLOWED_PERMISSIONS_GROUP = "allowed_permissions_group";
- private static final String KEY_ALLOWED_FOREGROUND_PERMISSIONS_GROUP =
- "allowed_foreground_permissions_group";
- private static final String KEY_DENIED_PERMISSIONS_GROUP = "denied_permissions_group";
-
- private static final String SHOW_SYSTEM_KEY = AutoPermissionAppsFragment.class.getName()
- + KEY_SHOW_SYSTEM_PREFS;
+ private static final String LOG_TAG = "AutoPermissionAppsFragment";
+ private static final String KEY_EMPTY = "_empty";
/** Creates a new instance of {@link AutoPermissionAppsFragment} for the given permission. */
- public static AutoPermissionAppsFragment newInstance(String permissionName) {
- return setPermissionName(new AutoPermissionAppsFragment(), permissionName);
+ public static AutoPermissionAppsFragment newInstance(String permissionName, long sessionId) {
+ return setPermissionName(new AutoPermissionAppsFragment(), permissionName, sessionId);
}
- private static <T extends Fragment> T setPermissionName(T fragment, String permissionName) {
+ private static <T extends Fragment> T setPermissionName(
+ T fragment, String permissionName, long sessionId) {
Bundle arguments = new Bundle();
arguments.putString(Intent.EXTRA_PERMISSION_NAME, permissionName);
+ arguments.putLong(EXTRA_SESSION_ID, sessionId);
fragment.setArguments(arguments);
return fragment;
}
- private PermissionApps mPermissionApps;
-
- private boolean mShowSystem;
- private boolean mHasSystemApps;
+ private PermissionAppsViewModel mViewModel;
+ private PermissionUsages mPermissionUsages;
+ private List<AppPermissionUsage> mAppPermissionUsages = new ArrayList<>();
+ private String mPermGroupName;
private Collator mCollator;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- if (savedInstanceState != null) {
- mShowSystem = savedInstanceState.getBoolean(SHOW_SYSTEM_KEY);
- }
-
setLoading(true);
- String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
- mPermissionApps = new PermissionApps(getActivity(), groupName, /* callback= */ this);
- mPermissionApps.refresh(/* getUiInfo= */ true);
+ mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME);
+ if (mPermGroupName == null) {
+ mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
+ }
+
+ Drawable icon = KotlinUtils.INSTANCE.getPermGroupIcon(getContext(), mPermGroupName);
+ CharSequence label = KotlinUtils.INSTANCE.getPermGroupLabel(getContext(), mPermGroupName);
+ CharSequence description = KotlinUtils.INSTANCE.getPermGroupDescription(getContext(),
+ mPermGroupName);
+
+ setHeaderLabel(label);
+ Preference header = new Preference(getContext());
+ header.setTitle(label);
+ header.setIcon(icon);
+ header.setSummary(Utils.getPermissionGroupDescriptionString(getContext(), mPermGroupName,
+ description));
+ getPreferenceScreen().addPreference(header);
mCollator = Collator.getInstance(
getContext().getResources().getConfiguration().getLocales().get(0));
- setShowSystemAppsToggle();
- bindUi(mPermissionApps, groupName);
+ PermissionAppsViewModelFactory factory =
+ new PermissionAppsViewModelFactory(getActivity().getApplication(), mPermGroupName,
+ this, new Bundle());
+ mViewModel = new ViewModelProvider(this, factory).get(PermissionAppsViewModel.class);
+
+ mViewModel.getCategorizedAppsLiveData().observe(this, this::onPackagesLoaded);
+ mViewModel.getShouldShowSystemLiveData().observe(this, this::updateMenu);
+ mViewModel.getHasSystemAppsLiveData().observe(this, this::hideSystemAppToggleIfNecessary);
+
+ // If the build type is below S, the app ops for permission usage can't be found. Thus, we
+ // shouldn't load permission usages, for them.
+ if (SdkLevel.isAtLeastS()) {
+ Context context = getPreferenceManager().getContext();
+ mPermissionUsages = new PermissionUsages(context);
+
+ long filterTimeBeginMillis = mViewModel.getFilterTimeBeginMillis();
+ mPermissionUsages.load(null, null, filterTimeBeginMillis, Long.MAX_VALUE,
+ PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(),
+ false, false, this, false);
+ }
+ }
+
+ private void updateMenu(Boolean showSystem) {
+ if (showSystem == null) {
+ showSystem = false;
+ }
+ // Show the opposite label from the current state.
+ String label;
+ if (showSystem) {
+ label = getString(R.string.menu_hide_system);
+ } else {
+ label = getString(R.string.menu_show_system);
+ }
+
+ boolean showSystemFinal = showSystem;
+ setAction(label, v -> mViewModel.updateShowSystem(!showSystemFinal));
+ }
+
+ /**
+ * Main differences between this phone implementation and this one are:
+ * <ul>
+ * <li>No special handling for scoped storage</li>
+ * </ul>
+ */
+ private void onPackagesLoaded(Map<Category, List<Pair<String, UserHandle>>> categories) {
+ Preference additionalPermissionsPreference = getPreferenceScreen().findPreference(
+ ALLOWED_FOREGROUND.getCategoryName());
+ if (additionalPermissionsPreference == null) {
+ // This preference resources includes the "Ask" permission group. That's okay for Auto
+ // even though Auto doesn't support the one-time permission because the code later in
+ // this method will hide unused permission groups.
+ addPreferencesFromResource(R.xml.allowed_denied);
+ }
+ // Hide allowed foreground label by default, to avoid briefly showing it before updating
+ findPreference(ALLOWED_FOREGROUND.getCategoryName()).setVisible(false);
+ Context context = getPreferenceManager().getContext();
+
+ if (context == null || getActivity() == null || categories == null) {
+ return;
+ }
+
+ Map<String, Preference> existingPrefs = new ArrayMap<>();
+
+ // Start at 1 since the header preference will always be in the 0th index
+ for (int i = 1; i < getPreferenceScreen().getPreferenceCount(); i++) {
+ PreferenceCategory category = (PreferenceCategory)
+ getPreferenceScreen().getPreference(i);
+ category.setOrderingAsAdded(true);
+ int numPreferences = category.getPreferenceCount();
+ for (int j = 0; j < numPreferences; j++) {
+ Preference preference = category.getPreference(j);
+ existingPrefs.put(preference.getKey(), preference);
+ }
+ category.removeAll();
+ }
+
+ long viewIdForLogging = new Random().nextLong();
+ long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+
+ Boolean showAlways = mViewModel.getShowAllowAlwaysStringLiveData().getValue();
+ if (showAlways != null && showAlways) {
+ findPreference(ALLOWED.getCategoryName()).setTitle(R.string.allowed_always_header);
+ } else {
+ findPreference(ALLOWED.getCategoryName()).setTitle(R.string.allowed_header);
+ }
+
+ // A mapping of user + packageName to their last access timestamps for the permission group.
+ Map<String, Long> groupUsageLastAccessTime =
+ mViewModel.extractGroupUsageLastAccessTime(mAppPermissionUsages);
+
+ for (Category grantCategory : categories.keySet()) {
+ List<Pair<String, UserHandle>> packages = categories.get(grantCategory);
+ PreferenceCategory category = findPreference(grantCategory.getCategoryName());
+
+ // If this category is empty set up the empty preference.
+ if (packages.size() == 0) {
+ Preference empty = new Preference(context);
+ empty.setSelectable(false);
+ empty.setKey(category.getKey() + KEY_EMPTY);
+ if (grantCategory.equals(ALLOWED)) {
+ empty.setTitle(getString(R.string.no_apps_allowed));
+ } else if (grantCategory.equals(ALLOWED_FOREGROUND)) {
+ category.setVisible(false);
+ } else if (grantCategory.equals(ASK)) {
+ category.setVisible(false);
+ } else {
+ empty.setTitle(getString(R.string.no_apps_denied));
+ }
+ category.addPreference(empty);
+ continue;
+ } else if (grantCategory.equals(ALLOWED_FOREGROUND)) {
+ category.setVisible(true);
+ } else if (grantCategory.equals(ASK)) {
+ category.setVisible(true);
+ }
+
+ for (Pair<String, UserHandle> packageUserLabel : packages) {
+ String packageName = packageUserLabel.getFirst();
+ UserHandle user = packageUserLabel.getSecond();
+
+ String key = user + packageName;
+
+ Long lastAccessTime = groupUsageLastAccessTime.get(key);
+ Pair<String, Integer> summaryTimestamp = Utils
+ .getPermissionLastAccessSummaryTimestamp(
+ lastAccessTime, context, mPermGroupName);
+
+ Preference existingPref = existingPrefs.get(key);
+ if (existingPref != null) {
+ updatePreferenceSummary(existingPref, summaryTimestamp);
+ category.addPreference(existingPref);
+ continue;
+ }
+
+ SmartIconLoadPackagePermissionPreference pref =
+ new SmartIconLoadPackagePermissionPreference(getActivity().getApplication(),
+ packageName, user, context);
+ pref.setKey(key);
+ pref.setTitle(KotlinUtils.INSTANCE.getPackageLabel(getActivity().getApplication(),
+ packageName, user));
+ pref.setOnPreferenceClickListener((Preference p) -> {
+ Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, mPermGroupName);
+ intent.putExtra(Intent.EXTRA_USER, user);
+ intent.putExtra(EXTRA_CALLER_NAME, getClass().getName());
+ intent.putExtra(EXTRA_SESSION_ID, sessionId);
+ startActivity(intent);
+ return true;
+ });
+ pref.setTitleContentDescription(AppUtils.getAppContentDescription(context,
+ packageName, user.getIdentifier()));
+
+ updatePreferenceSummary(pref, summaryTimestamp);
+
+ category.addPreference(pref);
+ if (!mViewModel.getCreationLogged()) {
+ logPermissionAppsFragmentCreated(packageName, user, viewIdForLogging,
+ grantCategory.equals(ALLOWED), grantCategory.equals(ALLOWED_FOREGROUND),
+ grantCategory.equals(DENIED));
+ }
+ }
+ KotlinUtils.INSTANCE.sortPreferenceGroup(category, this::comparePreference, false);
+ }
+
+ mViewModel.setCreationLogged(true);
+
+ setLoading(false);
}
@Override
@@ -99,206 +289,44 @@
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
}
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- outState.putBoolean(SHOW_SYSTEM_KEY, mShowSystem);
- }
-
- @Override
- public void onStart() {
- super.onStart();
- mPermissionApps.refresh(/* getUiInfo= */ true);
- }
-
- private void setShowSystemAppsToggle() {
- if (!mHasSystemApps) {
+ private void hideSystemAppToggleIfNecessary(Boolean hasSystemApps) {
+ if (hasSystemApps == null || !hasSystemApps) {
setAction(/* label= */ null, /* onClickListener= */ null);
- return;
}
-
- // Show the opposite label from the current state.
- String label;
- if (mShowSystem) {
- label = getString(R.string.menu_hide_system);
- } else {
- label = getString(R.string.menu_show_system);
- }
-
- setAction(label, v -> {
- mShowSystem = !mShowSystem;
- if (mPermissionApps.getApps() != null) {
- onPermissionsLoaded(mPermissionApps);
- }
- setShowSystemAppsToggle();
- });
}
- private void bindUi(PermissionApps permissionApps, @NonNull String groupName) {
- CharSequence label = permissionApps.getFullLabel();
- setHeaderLabel(label);
-
- Drawable icon = permissionApps.getIcon();
- Preference header = new Preference(getContext());
- header.setTitle(label);
- header.setIcon(icon);
- header.setSummary(Utils.getPermissionGroupDescriptionString(getContext(), groupName,
- permissionApps.getDescription()));
- getPreferenceScreen().addPreference(header);
-
- PreferenceGroup allowed = new PreferenceCategory(getContext());
- allowed.setKey(KEY_ALLOWED_PERMISSIONS_GROUP);
- allowed.setTitle(R.string.allowed_header);
- allowed.setVisible(false);
- getPreferenceScreen().addPreference(allowed);
-
- PreferenceGroup foreground = new PreferenceCategory(getContext());
- foreground.setKey(KEY_ALLOWED_FOREGROUND_PERMISSIONS_GROUP);
- foreground.setTitle(R.string.allowed_foreground_header);
- foreground.setVisible(false);
- getPreferenceScreen().addPreference(foreground);
-
- PreferenceGroup denied = new PreferenceCategory(getContext());
- denied.setKey(KEY_DENIED_PERMISSIONS_GROUP);
- denied.setTitle(R.string.denied_header);
- denied.setVisible(false);
- getPreferenceScreen().addPreference(denied);
+ private void updatePreferenceSummary(Preference preference,
+ Pair<String, Integer> summaryTimestamp) {
+ String summary = mViewModel.getPreferenceSummary(getResources(), summaryTimestamp);
+ if (!summary.isEmpty()) {
+ preference.setSummary(summary);
+ }
}
@Override
- public void onPermissionsLoaded(PermissionApps permissionApps) {
- Context context = getPreferenceManager().getContext();
-
- if (context == null || getActivity() == null) {
+ @RequiresApi(Build.VERSION_CODES.S)
+ public void onPermissionUsagesChanged() {
+ if (mPermissionUsages.getUsages().isEmpty()) {
+ return;
+ }
+ if (getContext() == null) {
+ // Async result has come in after our context is gone.
return;
}
- PreferenceCategory allowed = findPreference(KEY_ALLOWED_PERMISSIONS_GROUP);
- PreferenceCategory allowedForeground = findPreference(
- KEY_ALLOWED_FOREGROUND_PERMISSIONS_GROUP);
- PreferenceCategory denied = findPreference(KEY_DENIED_PERMISSIONS_GROUP);
+ mAppPermissionUsages = new ArrayList<>(mPermissionUsages.getUsages());
+ onPackagesLoaded(mViewModel.getCategorizedAppsLiveData().getValue());
+ }
- allowed.setOrderingAsAdded(true);
- allowedForeground.setOrderingAsAdded(true);
- denied.setOrderingAsAdded(true);
+ private int comparePreference(Preference lhs, Preference rhs) {
+ return mViewModel.comparePreference(mCollator, lhs, rhs);
+ }
- Map<String, Preference> existingPrefs = new ArrayMap<>();
- int numPreferences = allowed.getPreferenceCount();
- for (int i = 0; i < numPreferences; i++) {
- Preference preference = allowed.getPreference(i);
- existingPrefs.put(preference.getKey(), preference);
- }
- allowed.removeAll();
- numPreferences = allowedForeground.getPreferenceCount();
- for (int i = 0; i < numPreferences; i++) {
- Preference preference = allowedForeground.getPreference(i);
- existingPrefs.put(preference.getKey(), preference);
- }
- allowedForeground.removeAll();
- numPreferences = denied.getPreferenceCount();
- for (int i = 0; i < numPreferences; i++) {
- Preference preference = denied.getPreference(i);
- existingPrefs.put(preference.getKey(), preference);
- }
- denied.removeAll();
-
- mHasSystemApps = false;
- boolean hasPermissionWithBackgroundMode = false;
-
- ArrayList<PermissionApps.PermissionApp> sortedApps = new ArrayList<>(
- permissionApps.getApps());
- sortedApps.sort((x, y) -> {
- int result = mCollator.compare(x.getLabel(), y.getLabel());
- if (result == 0) {
- result = x.getUid() - y.getUid();
- }
- return result;
- });
-
- for (int i = 0; i < sortedApps.size(); i++) {
- PermissionApps.PermissionApp app = sortedApps.get(i);
- AppPermissionGroup group = app.getPermissionGroup();
-
- hasPermissionWithBackgroundMode =
- hasPermissionWithBackgroundMode || group.hasPermissionWithBackgroundMode();
-
- if (!Utils.shouldShowPermission(getContext(), group)) {
- continue;
- }
-
- if (!app.getAppInfo().enabled) {
- continue;
- }
-
- String key = app.getKey();
- Preference existingPref = existingPrefs.get(key);
- if (existingPref != null) {
- // Without this, existing preferences remember their old order.
- existingPref.setOrder(Preference.DEFAULT_ORDER);
- }
-
- boolean isSystemApp = !Utils.isGroupOrBgGroupUserSensitive(group);
-
- if (isSystemApp) {
- mHasSystemApps = true;
- }
-
- if (isSystemApp && !mShowSystem) {
- continue;
- }
-
- PreferenceCategory category;
- if (group.areRuntimePermissionsGranted()) {
- if (!group.hasPermissionWithBackgroundMode()
- || (group.getBackgroundPermissions() != null
- && group.getBackgroundPermissions().areRuntimePermissionsGranted())) {
- category = allowed;
- } else {
- category = allowedForeground;
- }
- } else {
- category = denied;
- }
-
- if (existingPref != null) {
- category.addPreference(existingPref);
- continue;
- }
-
- PermissionControlPreference pref = new PermissionControlPreference(context, group,
- PermissionAppsFragment.class.getName());
- pref.setKey(key);
- pref.setIcon(app.getIcon());
- pref.setTitle(Utils.getFullAppLabel(app.getAppInfo(), context));
- pref.setEllipsizeEnd();
- pref.useSmallerIcon();
- category.addPreference(pref);
- }
-
- if (hasPermissionWithBackgroundMode) {
- allowed.setTitle(R.string.allowed_always_header);
- }
-
- if (allowed.getPreferenceCount() == 0) {
- Preference empty = new Preference(context);
- empty.setTitle(R.string.no_apps_allowed);
- empty.setSelectable(false);
- allowed.addPreference(empty);
- }
- allowed.setVisible(true);
-
- allowedForeground.setVisible(allowedForeground.getPreferenceCount() > 0);
-
- if (denied.getPreferenceCount() == 0) {
- Preference empty = new Preference(context);
- empty.setTitle(R.string.no_apps_denied);
- empty.setSelectable(false);
- denied.addPreference(empty);
- }
- denied.setVisible(true);
-
- setShowSystemAppsToggle();
- setLoading(false);
+ private void logPermissionAppsFragmentCreated(String packageName, UserHandle user, long viewId,
+ boolean isAllowed, boolean isAllowedForeground, boolean isDenied) {
+ long sessionId = getArguments().getLong(EXTRA_SESSION_ID, 0);
+ mViewModel.logPermissionAppsFragmentCreated(packageName, user, viewId, isAllowed,
+ isAllowedForeground, isDenied, sessionId, getActivity().getApplication(),
+ mPermGroupName, LOG_TAG);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionsUtils.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionsUtils.java
index 075869c..16544fc 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionsUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoPermissionsUtils.java
@@ -59,4 +59,14 @@
preference.setTitle(Utils.getFullAppLabel(appInfo, context));
return preference;
}
+
+ /** Creates a {@link Preference} which shows the app icon and app name. */
+ public static Preference createHeaderPreference(Context context, Drawable packageIcon,
+ String packageName, String packageLabel) {
+ Preference preference = new Preference(context);
+ preference.setIcon(packageIcon);
+ preference.setKey(packageName);
+ preference.setTitle(packageLabel);
+ return preference;
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt
index 2a62d22..a700668 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoUnusedAppsFragment.kt
@@ -20,10 +20,12 @@
import android.os.Bundle
import android.os.UserHandle
import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
import com.android.permissioncontroller.R
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment
import com.android.permissioncontroller.hibernation.isHibernationEnabled
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment
+import com.android.permissioncontroller.permission.ui.UnusedAppsFragment.Companion.INFO_MSG_CATEGORY
import com.android.car.ui.utils.ViewUtils
import com.android.car.ui.utils.ViewUtils.LazyLayoutView
@@ -34,6 +36,8 @@
UnusedAppsFragment.Parent<AutoUnusedAppsPreference> {
companion object {
+ private const val UNUSED_PREFERENCE_KEY = "unused_pref_row_key"
+
/** Create a new instance of this fragment. */
@JvmStatic
fun newInstance(): AutoUnusedAppsFragment {
@@ -61,8 +65,8 @@
// initially focus on focus parking view and then shift focus to recyclerview once it has
// loaded
- ViewUtils.hideFocus(getListView().getRootView())
- val lazyLayoutView = getListView() as LazyLayoutView
+ ViewUtils.hideFocus(getCarUiRecyclerView().getView().getRootView())
+ val lazyLayoutView = getCarUiRecyclerView() as LazyLayoutView
ViewUtils.initFocus(lazyLayoutView)
}
@@ -97,4 +101,25 @@
override fun setTitle(title: CharSequence) {
headerLabel = title
}
+
+ override fun setEmptyState(empty: Boolean) {
+ val infoMsgCategory =
+ preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
+ val noUnusedAppsPreference: Preference? =
+ infoMsgCategory.findPreference<Preference>(UNUSED_PREFERENCE_KEY)
+ if (empty && noUnusedAppsPreference == null) {
+ infoMsgCategory.addPreference(createNoUnusedAppsPreference())
+ } else if (noUnusedAppsPreference != null) {
+ noUnusedAppsPreference.setVisible(empty)
+ }
+ }
+
+ private fun createNoUnusedAppsPreference(): Preference {
+ val preference = Preference(context)
+ preference.title = getString(R.string.zero_unused_apps)
+ preference.key = UNUSED_PREFERENCE_KEY
+ preference.isSelectable = false
+ preference.order = 0
+ return preference
+ }
}
\ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
index 9023a34..a66b15f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
@@ -160,6 +160,7 @@
}
CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.NONE);
+ item.setSecure(true);
item.setTitle(mContext.getString(stringId));
item.setOnItemClickedListener(i -> {
mDialog.setOnDismissListener(null);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
index 999ca59..bb0390f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
@@ -24,10 +24,6 @@
import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED;
import static com.android.permissioncontroller.hibernation.HibernationPolicyKt.isHibernationEnabled;
import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_CONTENT_PROVIDER;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_SENSOR_TODAY;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_SENSOR_YESTERDAY;
-import static com.android.permissioncontroller.permission.utils.Utils.NOT_IN_LAST_24H;
import static java.util.concurrent.TimeUnit.DAYS;
@@ -68,9 +64,9 @@
import com.android.permissioncontroller.PermissionControllerStatsLog;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState;
import com.android.permissioncontroller.permission.ui.Category;
-import com.android.permissioncontroller.permission.ui.handheld.dashboard.PermissionUsages;
import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel;
import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel.GroupUiInfo;
import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModelFactory;
@@ -106,7 +102,6 @@
private static final String ASSISTANT_MIC_CATEGORY_KEY = "_ASSISTANT_MIC_KEY";
private static final String ASSISTANT_MIC_SWITCH_KEY = "_ASSISTANT_MIC_SWITCH_KEY";
private static final String ASSISTANT_MIC_SUMMARY_KEY = "_ASSISTANT_MIC_SUMMARY_KEY";
- private static final int AGGREGATE_DATA_FILTER_BEGIN_DAYS = 1;
static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton";
@@ -187,7 +182,8 @@
mPermissionUsages = new PermissionUsages(context);
long filterTimeBeginMillis = Math.max(System.currentTimeMillis()
- - DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS),
+ - DAYS.toMillis(
+ AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS),
Instant.EPOCH.toEpochMilli());
mPermissionUsages.load(null, null, filterTimeBeginMillis, Long.MAX_VALUE,
PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(),
@@ -272,39 +268,6 @@
}
}
- private void extractGroupUsageLastAccessTime(Map<String, Long> accessTime) {
- accessTime.clear();
- long filterTimeBeginMillis = Math.max(System.currentTimeMillis()
- - DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS), Instant.EPOCH.toEpochMilli());
-
- int numApps = mAppPermissionUsages.size();
- for (int appIndex = 0; appIndex < numApps; appIndex++) {
- AppPermissionUsage appUsage = mAppPermissionUsages.get(appIndex);
- if (!appUsage.getPackageName().equals(mPackageName)) {
- continue;
- }
-
- List<AppPermissionUsage.GroupUsage> appGroups = appUsage.getGroupUsages();
- int numGroups = appGroups.size();
- for (int groupIndex = 0; groupIndex < numGroups; groupIndex++) {
- AppPermissionUsage.GroupUsage groupUsage = appGroups.get(groupIndex);
- long lastAccessTime = groupUsage.getLastAccessTime();
- String groupName = groupUsage.getGroup().getName();
- if (lastAccessTime == 0 || lastAccessTime < filterTimeBeginMillis) {
- continue;
- }
-
- // We might have another AppPermissionUsage entry that's of the same packageName
- // but with a different uid. In that case, we want to grab the max lastAccessTime
- // as the last usage to show.
- lastAccessTime = Math.max(
- accessTime.getOrDefault(groupName, Instant.EPOCH.toEpochMilli()),
- lastAccessTime);
- accessTime.put(groupName, lastAccessTime);
- }
- }
- }
-
private void updatePreferences(Map<Category, List<GroupUiInfo>> groupMap) {
if (groupMap == null) {
return;
@@ -328,7 +291,8 @@
}
Map<String, Long> groupUsageLastAccessTime = new HashMap<>();
- extractGroupUsageLastAccessTime(groupUsageLastAccessTime);
+ mViewModel.extractGroupUsageLastAccessTime(groupUsageLastAccessTime, mAppPermissionUsages,
+ mPackageName);
findPreference(Category.ALLOWED_FOREGROUND.getCategoryName()).setVisible(false);
@@ -367,100 +331,10 @@
preference.setTitle(KotlinUtils.INSTANCE.getPermGroupLabel(context, groupName));
preference.setIcon(KotlinUtils.INSTANCE.getPermGroupIcon(context, groupName));
preference.setKey(groupName);
- switch (groupInfo.getSubtitle()) {
- case BACKGROUND:
- switch (lastAccessType) {
- case LAST_24H_CONTENT_PROVIDER:
- preference.setSummary(
- R.string.app_perms_content_provider_background);
- break;
- case LAST_24H_SENSOR_TODAY:
- preference.setSummary(
- getString(
- R.string.app_perms_24h_access_background,
- summaryTimestamp.getFirst()));
- break;
- case LAST_24H_SENSOR_YESTERDAY:
- preference.setSummary(getString(
- R.string.app_perms_24h_access_yest_background,
- summaryTimestamp.getFirst()));
- break;
- case NOT_IN_LAST_24H:
- default:
- preference.setSummary(
- R.string.permission_subtitle_background);
- }
-
- break;
- case MEDIA_ONLY:
- switch (lastAccessType) {
- case LAST_24H_CONTENT_PROVIDER:
- preference.setSummary(
- R.string.app_perms_content_provider_media_only);
- break;
- case LAST_24H_SENSOR_TODAY:
- preference.setSummary(
- getString(
- R.string.app_perms_24h_access_media_only,
- summaryTimestamp.getFirst()));
- break;
- case LAST_24H_SENSOR_YESTERDAY:
- preference.setSummary(
- getString(
- R.string.app_perms_24h_access_yest_media_only,
- summaryTimestamp.getFirst()));
- break;
- case NOT_IN_LAST_24H:
- default:
- preference.setSummary(R.string.permission_subtitle_media_only);
- }
-
- break;
- case ALL_FILES:
- switch (lastAccessType) {
- case LAST_24H_CONTENT_PROVIDER:
- preference.setSummary(
- R.string.app_perms_content_provider_all_files);
- break;
- case LAST_24H_SENSOR_TODAY:
- preference.setSummary(
- getString(
- R.string.app_perms_24h_access_all_files,
- summaryTimestamp.getFirst()));
- break;
- case LAST_24H_SENSOR_YESTERDAY:
- preference.setSummary(
- getString(
- R.string.app_perms_24h_access_yest_all_files,
- summaryTimestamp.getFirst()));
- break;
- case NOT_IN_LAST_24H:
- default:
- preference.setSummary(R.string.permission_subtitle_all_files);
- }
-
- break;
- case FOREGROUND_ONLY:
- // We don't want to note the foreground access
- default:
- switch (lastAccessType) {
- case LAST_24H_CONTENT_PROVIDER:
- preference.setSummary(
- R.string.app_perms_content_provider);
- break;
- case LAST_24H_SENSOR_TODAY:
- preference.setSummary(
- getString(R.string.app_perms_24h_access,
- summaryTimestamp.getFirst()));
- break;
- case LAST_24H_SENSOR_YESTERDAY:
- preference.setSummary(
- getString(R.string.app_perms_24h_access_yest,
- summaryTimestamp.getFirst()));
- break;
- case NOT_IN_LAST_24H:
- default:
- }
+ String summary = mViewModel.getPreferenceSummary(groupInfo, context,
+ groupUsageLastAccessTime.get(groupName));
+ if (!summary.isEmpty()) {
+ preference.setSummary(summary);
}
// Add an info icon if the package handles ACTION_VIEW_PERMISSION_USAGE.
PackageManager packageManager = requireActivity().getPackageManager();
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt
index 55686cf..eaf90de 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/HandheldUnusedAppsFragment.kt
@@ -22,9 +22,11 @@
import android.os.UserHandle
import android.view.MenuItem
import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
import com.android.permissioncontroller.R
import com.android.permissioncontroller.hibernation.isHibernationEnabled
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment
+import com.android.permissioncontroller.permission.ui.UnusedAppsFragment.Companion.INFO_MSG_CATEGORY
/**
* Handheld wrapper, with customizations, around [UnusedAppsFragment].
@@ -109,4 +111,10 @@
override fun setTitle(title: CharSequence) {
requireActivity().setTitle(title)
}
+
+ override fun setEmptyState(empty: Boolean) {
+ val infoMsgCategory =
+ preferenceScreen.findPreference<PreferenceCategory>(INFO_MSG_CATEGORY)!!
+ infoMsgCategory.isVisible = !empty
+ }
}
\ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
index 4491557..b28f490 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
@@ -17,23 +17,12 @@
import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED;
-import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED;
import static com.android.permissioncontroller.permission.ui.Category.ALLOWED;
import static com.android.permissioncontroller.permission.ui.Category.ALLOWED_FOREGROUND;
import static com.android.permissioncontroller.permission.ui.Category.ASK;
import static com.android.permissioncontroller.permission.ui.Category.DENIED;
import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
import static com.android.permissioncontroller.permission.ui.handheld.dashboard.UtilsKt.shouldShowPermissionsDashboard;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_CONTENT_PROVIDER;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_SENSOR_TODAY;
-import static com.android.permissioncontroller.permission.utils.Utils.LAST_24H_SENSOR_YESTERDAY;
-import static com.android.permissioncontroller.permission.utils.Utils.NOT_IN_LAST_24H;
-
-import static java.util.concurrent.TimeUnit.DAYS;
import android.Manifest;
import android.app.ActionBar;
@@ -46,7 +35,6 @@
import android.os.Looper;
import android.os.UserHandle;
import android.util.ArrayMap;
-import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -59,12 +47,11 @@
import androidx.preference.PreferenceCategory;
import com.android.modules.utils.build.SdkLevel;
-import com.android.permissioncontroller.PermissionControllerStatsLog;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
import com.android.permissioncontroller.permission.ui.Category;
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity;
-import com.android.permissioncontroller.permission.ui.handheld.dashboard.PermissionUsages;
import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel;
import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModelFactory;
import com.android.permissioncontroller.permission.utils.KotlinUtils;
@@ -73,9 +60,7 @@
import com.android.settingslib.utils.applications.AppUtils;
import java.text.Collator;
-import java.time.Instant;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
@@ -98,7 +83,6 @@
private static final String STORAGE_ALLOWED_FULL = "allowed_storage_full";
private static final String STORAGE_ALLOWED_SCOPED = "allowed_storage_scoped";
private static final int SHOW_LOAD_DELAY_MS = 200;
- private static final int AGGREGATE_DATA_FILTER_BEGIN_DAYS = 1;
private static final int MENU_PERMISSION_USAGE = MENU_HIDE_SYSTEM + 1;
@@ -167,9 +151,7 @@
Context context = getPreferenceManager().getContext();
mPermissionUsages = new PermissionUsages(context);
- long filterTimeBeginMillis = Math.max(System.currentTimeMillis()
- - DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS),
- Instant.EPOCH.toEpochMilli());
+ long filterTimeBeginMillis = mViewModel.getFilterTimeBeginMillis();
mPermissionUsages.load(null, null, filterTimeBeginMillis, Long.MAX_VALUE,
PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(),
false, false, this, false);
@@ -309,8 +291,8 @@
}
// A mapping of user + packageName to their last access timestamps for the permission group.
- Map<String, Long> groupUsageLastAccessTime = new HashMap<>();
- extractGroupUsageLastAccessTime(groupUsageLastAccessTime);
+ Map<String, Long> groupUsageLastAccessTime =
+ mViewModel.extractGroupUsageLastAccessTime(mAppPermissionUsages);
for (Category grantCategory : categories.keySet()) {
List<Pair<String, UserHandle>> packages = categories.get(grantCategory);
@@ -422,57 +404,12 @@
private void updatePreferenceSummary(Preference preference,
Pair<String, Integer> summaryTimestamp) {
- @Utils.AppPermsLastAccessType int lastAccessType = summaryTimestamp.getSecond();
-
- switch (lastAccessType) {
- case LAST_24H_CONTENT_PROVIDER:
- preference.setSummary(
- R.string.app_perms_content_provider);
- break;
- case LAST_24H_SENSOR_TODAY:
- preference.setSummary(
- getString(R.string.app_perms_24h_access,
- summaryTimestamp.getFirst()));
- break;
- case LAST_24H_SENSOR_YESTERDAY:
- preference.setSummary(
- getString(R.string.app_perms_24h_access_yest,
- summaryTimestamp.getFirst()));
- break;
- case NOT_IN_LAST_24H:
- default:
+ String summary = mViewModel.getPreferenceSummary(getResources(), summaryTimestamp);
+ if (!summary.isEmpty()) {
+ preference.setSummary(summary);
}
}
- private void extractGroupUsageLastAccessTime(Map<String, Long> accessTime) {
- accessTime.clear();
- long filterTimeBeginMillis = Math.max(System.currentTimeMillis()
- - DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS), Instant.EPOCH.toEpochMilli());
-
- int numApps = mAppPermissionUsages.size();
- for (int appIndex = 0; appIndex < numApps; appIndex++) {
- AppPermissionUsage appUsage = mAppPermissionUsages.get(appIndex);
- String packageName = appUsage.getPackageName();
-
- List<AppPermissionUsage.GroupUsage> appGroups = appUsage.getGroupUsages();
- int numGroups = appGroups.size();
- for (int groupIndex = 0; groupIndex < numGroups; groupIndex++) {
- AppPermissionUsage.GroupUsage groupUsage = appGroups.get(groupIndex);
- String groupName = groupUsage.getGroup().getName();
- if (!mPermGroupName.equals(groupName)) {
- continue;
- }
-
- long lastAccessTime = groupUsage.getLastAccessTime();
- if (lastAccessTime == 0 || lastAccessTime < filterTimeBeginMillis) {
- continue;
- }
-
- String key = groupUsage.getGroup().getUser() + packageName;
- accessTime.put(key, lastAccessTime);
- }
- }
- }
private int comparePreference(Preference lhs, Preference rhs) {
int result = mCollator.compare(lhs.getTitle().toString(),
@@ -486,27 +423,8 @@
private void logPermissionAppsFragmentCreated(String packageName, UserHandle user, long viewId,
boolean isAllowed, boolean isAllowedForeground, boolean isDenied) {
long sessionId = getArguments().getLong(EXTRA_SESSION_ID, 0);
-
- int category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED;
- if (isAllowed) {
- category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED;
- } else if (isAllowedForeground) {
- category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
- } else if (isDenied) {
- category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED;
- }
-
- Integer uid = KotlinUtils.INSTANCE.getPackageUid(getActivity().getApplication(),
- packageName, user);
- if (uid == null) {
- return;
- }
-
- PermissionControllerStatsLog.write(PERMISSION_APPS_FRAGMENT_VIEWED, sessionId, viewId,
- mPermGroupName, uid, packageName, category);
- Log.v(LOG_TAG, "PermissionAppsFragment created with sessionId=" + sessionId
- + " permissionGroupName=" + mPermGroupName + " appUid="
- + uid + " packageName=" + packageName
- + " category=" + category);
+ mViewModel.logPermissionAppsFragmentCreated(packageName, user, viewId, isAllowed,
+ isAllowedForeground, isDenied, sessionId, getActivity().getApplication(),
+ mPermGroupName, LOG_TAG);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java
index 05e7292..9ab0dc4 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionDetailsFragment.java
@@ -58,6 +58,7 @@
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.AppPermissionGroup;
import com.android.permissioncontroller.permission.model.AppPermissionUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
import com.android.permissioncontroller.permission.model.legacy.PermissionApps;
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity;
import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionHistoryPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionHistoryPreference.java
index e7fe255..c7beffa 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionHistoryPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionHistoryPreference.java
@@ -247,6 +247,7 @@
viewUsageIntent.putExtra(Intent.EXTRA_START_TIME,
mAccessTimeList.get(mAccessTimeList.size() - 1));
viewUsageIntent.putExtra(Intent.EXTRA_END_TIME, mAccessTimeList.get(0));
+ viewUsageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PackageManager packageManager = mContext.getPackageManager();
ResolveInfo resolveInfo = packageManager.resolveActivity(viewUsageIntent,
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java
index caeca2d..acc39c2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/dashboard/PermissionUsageV2Fragment.java
@@ -52,6 +52,7 @@
import com.android.permissioncontroller.permission.model.AppPermissionGroup;
import com.android.permissioncontroller.permission.model.AppPermissionUsage;
import com.android.permissioncontroller.permission.model.AppPermissionUsage.GroupUsage;
+import com.android.permissioncontroller.permission.model.PermissionUsages;
import com.android.permissioncontroller.permission.model.legacy.PermissionApps;
import com.android.permissioncontroller.permission.ui.handheld.PermissionUsageV2ControlPreference;
import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java
index fa1ce2f..6386e51 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/AppPermissionActivity.java
@@ -19,6 +19,9 @@
import static android.content.Intent.ACTION_MANAGE_APP_PERMISSION;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
+
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
@@ -42,6 +45,7 @@
import com.android.permissioncontroller.permission.utils.Utils;
import java.util.List;
+import java.util.Random;
/**
* Manage a single permission of a single app.
@@ -146,8 +150,13 @@
if (DeviceUtils.isAuto(this)) {
Fragment androidXFragment;
+ long sessionId = getIntent().getLongExtra(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+ while (sessionId == INVALID_SESSION_ID) {
+ sessionId = new Random().nextLong();
+ }
+
androidXFragment = AutoAppPermissionFragment.newInstance(packageName, permissionName,
- groupName, userHandle);
+ groupName, userHandle, sessionId);
getSupportFragmentManager().beginTransaction().replace(android.R.id.content,
androidXFragment).commit();
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
index afedeca..0aaf99b 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
@@ -21,6 +21,8 @@
import android.app.AppOpsManager.MODE_ALLOWED
import android.app.AppOpsManager.MODE_IGNORED
import android.app.AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+import android.apphibernation.AppHibernationManager
+import android.content.Context
import android.os.Bundle
import android.os.UserHandle
import android.util.Log
@@ -34,6 +36,7 @@
import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_DISABLED
import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED
import com.android.permissioncontroller.R
+import com.android.permissioncontroller.hibernation.isHibernationEnabled
import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData
import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData
import com.android.permissioncontroller.permission.data.HibernationSettingStateLiveData
@@ -42,13 +45,18 @@
import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS
import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
import com.android.permissioncontroller.permission.data.get
+import com.android.permissioncontroller.permission.model.AppPermissionUsage
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState
import com.android.permissioncontroller.permission.ui.Category
import com.android.permissioncontroller.permission.utils.IPC
import com.android.permissioncontroller.permission.utils.Utils
+import com.android.permissioncontroller.permission.utils.Utils.AppPermsLastAccessType
import com.android.permissioncontroller.permission.utils.navigateSafe
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
+import java.time.Instant
+import java.util.concurrent.TimeUnit
+import kotlin.math.max
/**
* ViewModel for the AppPermissionGroupsFragment. Has a liveData with the UI information for all
@@ -64,6 +72,7 @@
) : ViewModel() {
companion object {
+ const val AGGREGATE_DATA_FILTER_BEGIN_DAYS = 1
val LOG_TAG: String = AppPermissionGroupsViewModel::class.java.simpleName
}
@@ -209,6 +218,11 @@
MODE_IGNORED
}
aom.setUidMode(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, uid, mode)
+ if (isHibernationEnabled() && !enabled) {
+ val ahm = app.getSystemService(AppHibernationManager::class.java)!!
+ ahm.setHibernatingForUser(packageName, false)
+ ahm.setHibernatingGlobally(packageName, false)
+ }
}
}
}
@@ -220,6 +234,124 @@
fun showAllPermissions(fragment: Fragment, args: Bundle) {
fragment.findNavController().navigateSafe(R.id.perm_groups_to_all_perms, args)
}
+
+ // This method should be consolidated with
+ // PermissionAppsViewModel#extractGroupUsageLastAccessTime
+ fun extractGroupUsageLastAccessTime(
+ accessTime: MutableMap<String, Long>,
+ appPermissionUsages: List<AppPermissionUsage>,
+ packageName: String
+ ) {
+ accessTime.clear()
+ val filterTimeBeginMillis = max(System.currentTimeMillis() -
+ TimeUnit.DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS.toLong()),
+ Instant.EPOCH.toEpochMilli())
+ val numApps: Int = appPermissionUsages.size
+ for (appIndex in 0 until numApps) {
+ val appUsage: AppPermissionUsage = appPermissionUsages[appIndex]
+ if (appUsage.packageName != packageName) {
+ continue
+ }
+ val appGroups = appUsage.groupUsages
+ val numGroups = appGroups.size
+ for (groupIndex in 0 until numGroups) {
+ val groupUsage = appGroups[groupIndex]
+ var lastAccessTime = groupUsage.lastAccessTime
+ val groupName = groupUsage.group.name
+ if (lastAccessTime == 0L || lastAccessTime < filterTimeBeginMillis) {
+ continue
+ }
+
+ // We might have another AppPermissionUsage entry that's of the same packageName
+ // but with a different uid. In that case, we want to grab the max lastAccessTime
+ // as the last usage to show.
+ lastAccessTime = Math.max(
+ accessTime.getOrDefault(groupName, Instant.EPOCH.toEpochMilli()),
+ lastAccessTime)
+ accessTime[groupName] = lastAccessTime
+ }
+ }
+ }
+
+ fun getPreferenceSummary(groupInfo: GroupUiInfo, context: Context, lastAccessTime: Long?):
+ String {
+ val summaryTimestamp = Utils
+ .getPermissionLastAccessSummaryTimestamp(
+ lastAccessTime, context, groupInfo.groupName)
+ @AppPermsLastAccessType val lastAccessType: Int = summaryTimestamp.second
+
+ return when (groupInfo.subtitle) {
+ PermSubtitle.BACKGROUND ->
+ when (lastAccessType) {
+ Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
+ R.string.app_perms_content_provider_background)
+ Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ R.string.app_perms_24h_access_background,
+ summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ R.string.app_perms_24h_access_yest_background,
+ summaryTimestamp.first)
+ Utils.NOT_IN_LAST_24H -> context.getString(
+ R.string.permission_subtitle_background)
+ else -> context.getString(
+ R.string.permission_subtitle_background)
+ }
+ PermSubtitle.MEDIA_ONLY ->
+ when (lastAccessType) {
+ Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
+ R.string.app_perms_content_provider_media_only)
+ Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ R.string.app_perms_24h_access_media_only,
+ summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ R.string.app_perms_24h_access_yest_media_only,
+ summaryTimestamp.first)
+ Utils.NOT_IN_LAST_24H -> context.getString(
+ R.string.permission_subtitle_media_only)
+ else -> context.getString(R.string.permission_subtitle_media_only)
+ }
+ PermSubtitle.ALL_FILES ->
+ when (lastAccessType) {
+ Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
+ R.string.app_perms_content_provider_all_files)
+ Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ R.string.app_perms_24h_access_all_files,
+ summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ R.string.app_perms_24h_access_yest_all_files,
+ summaryTimestamp.first)
+ Utils.NOT_IN_LAST_24H -> context.getString(
+ R.string.permission_subtitle_all_files)
+ else -> context.getString(R.string.permission_subtitle_all_files)
+ }
+ PermSubtitle.FOREGROUND_ONLY ->
+ when (lastAccessType) {
+ Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
+ R.string.app_perms_content_provider)
+ Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ R.string.app_perms_24h_access,
+ summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ R.string.app_perms_24h_access_yest,
+ summaryTimestamp.first)
+ Utils.NOT_IN_LAST_24H -> ""
+ else -> ""
+ }
+ else ->
+ when (lastAccessType) {
+ Utils.LAST_24H_CONTENT_PROVIDER -> context.getString(
+ R.string.app_perms_content_provider)
+ Utils.LAST_24H_SENSOR_TODAY -> context.getString(
+ R.string.app_perms_24h_access,
+ summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY -> context.getString(
+ R.string.app_perms_24h_access_yest,
+ summaryTimestamp.first)
+ Utils.NOT_IN_LAST_24H -> ""
+ else -> ""
+ }
+ }
+ }
}
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
index 7fac5ba..6454970 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
@@ -417,7 +417,8 @@
// Show location permission dialogs based on location permissions
val locationVisibilities = MutableList(NEXT_LOCATION_DIALOG) { false }
- if (groupState.group.permGroupName == LOCATION && isLocationAccuracyEnabled()) {
+ if (groupState.group.permGroupName == LOCATION && isLocationAccuracyEnabled() &&
+ packageInfo.targetSdkVersion >= Build.VERSION_CODES.S) {
if (needFgPermissions) {
locationVisibilities[LOCATION_ACCURACY_LAYOUT] = true
if (fgState != null &&
@@ -433,6 +434,12 @@
buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = false
}
} else {
+ if (!fgState.affectedPermissions.contains(ACCESS_COARSE_LOCATION)) {
+ Log.e(LOG_TAG, "ACCESS_FINE_LOCATION must be requested " +
+ "with ACCESS_COARSE_LOCATION.")
+ value = null
+ return
+ }
if (coarseLocationPerm?.isOneTime == false &&
!coarseLocationPerm.isUserSet &&
!coarseLocationPerm.isUserFixed) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt
similarity index 96%
rename from PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsViewModel.kt
rename to PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt
index 8d4e8c2..970569c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManagePermissionsViewModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.permission.ui.television
+package com.android.permissioncontroller.permission.ui.model
import android.app.Application
import androidx.lifecycle.AndroidViewModel
@@ -23,7 +23,6 @@
import com.android.permissioncontroller.permission.data.PermGroupsPackagesUiInfoLiveData
import com.android.permissioncontroller.permission.data.StandardPermGroupNamesLiveData
import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo
-import com.android.permissioncontroller.permission.ui.model.UsedCustomPermGroupNamesLiveData
/**
* A [androidx.lifecycle.ViewModel] for [ManagePermissionsFragment] and
@@ -35,6 +34,7 @@
* [ManagePermissionsViewModel].
*/
class ManagePermissionsViewModel(app: Application) : AndroidViewModel(app) {
+
private val standardPermGroupsLiveData: LiveData<List<PermGroupPackagesUiInfo>> =
MediatorLiveData<List<PermGroupPackagesUiInfo>>().apply {
addSource(PermGroupsPackagesUiInfoLiveData(app, StandardPermGroupNamesLiveData)) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
index c7a13db..c4473b1 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
@@ -19,20 +19,29 @@
import android.Manifest
import android.app.Application
import android.content.Intent
+import android.content.res.Resources
import android.os.Bundle
import android.os.UserHandle
+import android.util.Log
import androidx.fragment.app.Fragment
import androidx.lifecycle.AbstractSavedStateViewModelFactory
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.navigation.fragment.findNavController
+import androidx.preference.Preference
import androidx.savedstate.SavedStateRegistryOwner
+import com.android.permissioncontroller.PermissionControllerStatsLog
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.data.AllPackageInfosLiveData
import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData
import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState
import com.android.permissioncontroller.permission.data.SinglePermGroupPackagesUiInfoLiveData
+import com.android.permissioncontroller.permission.model.AppPermissionUsage
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState
import com.android.permissioncontroller.permission.ui.Category
import com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog
@@ -40,8 +49,14 @@
import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.HAS_SYSTEM_APPS_KEY
import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.SHOULD_SHOW_SYSTEM_KEY
import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.SHOW_ALWAYS_ALLOWED
+import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageUid
import com.android.permissioncontroller.permission.utils.LocationUtils
+import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.permission.utils.navigateSafe
+import java.text.Collator
+import java.time.Instant
+import java.util.concurrent.TimeUnit
+import kotlin.math.max
/**
* ViewModel for the PermissionAppsFragment. Has a liveData with all of the UI info for each
@@ -59,6 +74,7 @@
) : ViewModel() {
companion object {
+ const val AGGREGATE_DATA_FILTER_BEGIN_DAYS = 1
internal const val SHOULD_SHOW_SYSTEM_KEY = "showSystem"
internal const val HAS_SYSTEM_APPS_KEY = "hasSystem"
internal const val SHOW_ALWAYS_ALLOWED = "showAlways"
@@ -240,6 +256,110 @@
fragment.findNavController().navigateSafe(R.id.perm_apps_to_app, args)
}
+
+ fun getFilterTimeBeginMillis(): Long {
+ return max(System.currentTimeMillis() -
+ TimeUnit.DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS.toLong()),
+ Instant.EPOCH.toEpochMilli())
+ }
+
+ /**
+ * Return a mapping of user + packageName to their last access timestamps for the permission
+ * group.
+ */
+ fun extractGroupUsageLastAccessTime(appPermissionUsages: List<AppPermissionUsage>):
+ MutableMap<String, Long> {
+ val accessTime: MutableMap<String, Long> = HashMap()
+ val now = System.currentTimeMillis()
+ val filterTimeBeginMillis = max(
+ now - TimeUnit.DAYS.toMillis(AGGREGATE_DATA_FILTER_BEGIN_DAYS.toLong()),
+ Instant.EPOCH.toEpochMilli())
+ val numApps: Int = appPermissionUsages.size
+ for (appIndex in 0 until numApps) {
+ val appUsage: AppPermissionUsage = appPermissionUsages.get(appIndex)
+ val packageName = appUsage.packageName
+ val appGroups = appUsage.groupUsages
+ val numGroups = appGroups.size
+ for (groupIndex in 0 until numGroups) {
+ val groupUsage = appGroups[groupIndex]
+ val groupUsageGroupName = groupUsage.group.name
+ if (groupName != groupUsageGroupName) {
+ continue
+ }
+ val lastAccessTime = groupUsage.lastAccessTime
+ if (lastAccessTime == 0L || lastAccessTime < filterTimeBeginMillis) {
+ continue
+ }
+ val key = groupUsage.group.user.toString() + packageName
+ accessTime[key] = lastAccessTime
+ }
+ }
+ return accessTime
+ }
+
+ /**
+ * Return the String preference summary based on the last access time.
+ */
+ fun getPreferenceSummary(res: Resources, summaryTimestamp: Pair<String, Int>): String {
+ return when (summaryTimestamp.second) {
+ Utils.LAST_24H_CONTENT_PROVIDER -> res.getString(
+ R.string.app_perms_content_provider)
+ Utils.LAST_24H_SENSOR_TODAY -> res.getString(R.string.app_perms_24h_access,
+ summaryTimestamp.first)
+ Utils.LAST_24H_SENSOR_YESTERDAY -> res.getString(R.string.app_perms_24h_access_yest,
+ summaryTimestamp.first)
+ else -> ""
+ }
+ }
+
+ /**
+ * Return two preferences to determine their ordering.
+ */
+ fun comparePreference(collator: Collator, lhs: Preference, rhs: Preference): Int {
+ var result: Int = collator.compare(lhs.title.toString(),
+ rhs.title.toString())
+ if (result == 0) {
+ result = lhs.key.compareTo(rhs.key)
+ }
+ return result
+ }
+
+ /**
+ * Log that the fragment was created.
+ */
+ fun logPermissionAppsFragmentCreated(
+ packageName: String,
+ user: UserHandle,
+ viewId: Long,
+ isAllowed: Boolean,
+ isAllowedForeground: Boolean,
+ isDenied: Boolean,
+ sessionId: Long,
+ application: Application,
+ permGroupName: String,
+ tag: String
+ ) {
+ var category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED
+ when {
+ isAllowed -> {
+ category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED
+ }
+ isAllowedForeground -> {
+ category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND
+ }
+ isDenied -> {
+ category = PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED
+ }
+ }
+ val uid = getPackageUid(application,
+ packageName, user) ?: return
+ PermissionControllerStatsLog.write(
+ PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED, sessionId, viewId,
+ permGroupName, uid, packageName, category)
+ Log.v(tag, tag + " created with sessionId=" + sessionId +
+ " permissionGroupName=" + permGroupName + " appUid=" + uid +
+ " packageName=" + packageName + " category=" + category)
+ }
}
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java
similarity index 94%
rename from PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreference.java
rename to PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java
index bb45470..b69fb66 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.permission.ui.television;
+package com.android.permissioncontroller.permission.ui.model;
import android.content.Context;
import android.content.Intent;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreferenceUtils.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java
similarity index 89%
rename from PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreferenceUtils.java
rename to PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java
index a9578ac..49a01ef 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionGroupPreferenceUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.permission.ui.television;
+package com.android.permissioncontroller.permission.ui.model;
import android.content.Context;
@@ -27,9 +27,16 @@
import java.util.List;
import java.util.Objects;
-final class PermissionGroupPreferenceUtils {
+/**
+ * Utils related to displaying permission groups in preferences.
+ */
+public final class PermissionGroupPreferenceUtils {
- static void updateGroupOfPermissionPreferences(Context context, PreferenceGroup preferenceGroup,
+ /**
+ * Update a {@link PreferenceGroup} with the specified permission groups.
+ */
+ public static void updateGroupOfPermissionPreferences(Context context,
+ PreferenceGroup preferenceGroup,
List<PermGroupPackagesUiInfo> permissionGroups) {
if (!(permissionGroups instanceof ArrayList)) {
permissionGroups = new ArrayList<>(permissionGroups);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java
index 51af4bc..38a899b 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsFragment.java
@@ -31,6 +31,8 @@
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo;
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils;
import com.android.permissioncontroller.permission.utils.Utils;
import java.util.List;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java
index 6e13dfa..629cf34 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/ManagePermissionsOtherFragment.java
@@ -32,6 +32,8 @@
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo;
+import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils;
import java.util.List;