diff options
author | 2023-12-04 14:25:30 +0900 | |
---|---|---|
committer | 2023-12-06 11:36:19 +0900 | |
commit | d2d700d3968c35badd7681b36cc70220451f7c2e (patch) | |
tree | 5744bf840d3226b25bb9bb98760a33508be2616e | |
parent | 7c4f2059ae4f6e356580556e0ebe2b8013cb84d4 (diff) |
Show usage timestamps in WearAppPermissionGroups
Screenshot: https://screenshot.googleplex.com/AVxWjubrYye632y.png
Bug: 309721296
Test: Privacy dashboard -> Location -> Choose app -> Check timestamps on
the chips
Change-Id: Icd73e5102e2a955225dd34b2b93db7d54e62a512
4 files changed, 150 insertions, 8 deletions
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt index 84acd09fd..a0703b10c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt @@ -20,6 +20,7 @@ import android.app.Activity import android.content.Intent import android.content.pm.PackageInfo import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle import android.os.UserHandle import android.util.Log @@ -27,20 +28,36 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.annotation.RequiresApi import androidx.compose.ui.platform.ComposeView import androidx.core.os.BundleCompat import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.model.AppPermissions +import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage +import com.android.permissioncontroller.permission.model.v31.PermissionUsages +import com.android.permissioncontroller.permission.model.v31.PermissionUsages.PermissionsUsagesChangeCallback import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModelFactory import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModel import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModelFactory +import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel +import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModelFactory +import com.android.permissioncontroller.permission.utils.KotlinUtils.is7DayToggleEnabled +import java.time.Instant +import java.util.concurrent.TimeUnit -class WearAppPermissionGroupsFragment : Fragment() { +class WearAppPermissionGroupsFragment : Fragment(), PermissionsUsagesChangeCallback { + private lateinit var permissionUsages: PermissionUsages + private lateinit var wearViewModel: WearAppPermissionUsagesViewModel private lateinit var helper: WearAppPermissionGroupsHelper + + // Suppress warning of the deprecated class [android.app.LoaderManager] since other form factors + // are using the class to load PermissionUsages. + @Suppress("DEPRECATION") override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -74,17 +91,54 @@ class WearAppPermissionGroupsFragment : Fragment() { val factory = AppPermissionGroupsViewModelFactory(packageName, user, sessionId) val viewModel = ViewModelProvider(this, factory).get(AppPermissionGroupsViewModel::class.java) + wearViewModel = + ViewModelProvider(this, WearAppPermissionUsagesViewModelFactory()) + .get(WearAppPermissionUsagesViewModel::class.java) val revokeDialogViewModel = ViewModelProvider(this, AppPermissionGroupsRevokeDialogViewModelFactory()) .get(AppPermissionGroupsRevokeDialogViewModel::class.java) + + val context = requireContext() + + // 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()) { + permissionUsages = PermissionUsages(context) + val aggregateDataFilterBeginDays = + (if (is7DayToggleEnabled()) + AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 + else AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1) + .toLong() + + val filterTimeBeginMillis = + Math.max( + System.currentTimeMillis() - + TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays), + Instant.EPOCH.toEpochMilli() + ) + permissionUsages.load( + null, + null, + filterTimeBeginMillis, + Long.MAX_VALUE, + PermissionUsages.USAGE_FLAG_LAST, + requireActivity().getLoaderManager(), + false, + false, + this, + false + ) + } helper = WearAppPermissionGroupsHelper( - context = requireContext(), + context = context, fragment = this, user = user, + packageName = packageName, sessionId = sessionId, appPermissions = appPermissions, viewModel = viewModel, + wearViewModel = wearViewModel, revokeDialogViewModel = revokeDialogViewModel ) @@ -96,6 +150,19 @@ class WearAppPermissionGroupsFragment : Fragment() { helper.logAndClearToggledGroups() } + @RequiresApi(Build.VERSION_CODES.S) + override fun onPermissionUsagesChanged() { + if (permissionUsages.usages.isEmpty()) { + return + } + if (getContext() == null) { + // Async result has come in after our context is gone. + return + } + wearViewModel.appPermissionUsages.value = + ArrayList<AppPermissionUsage>(permissionUsages.usages) + } + companion object { const val LOG_TAG = "WearAppPermissionGroups" } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt index d1e198b3d..0ccde86be 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt @@ -26,12 +26,14 @@ import android.util.ArraySet import android.util.Log import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController +import com.android.permission.flags.Flags import com.android.permissioncontroller.R import com.android.permissioncontroller.hibernation.isHibernationEnabled import com.android.permissioncontroller.permission.model.AppPermissionGroup import com.android.permissioncontroller.permission.model.AppPermissions import com.android.permissioncontroller.permission.model.Permission import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState +import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage import com.android.permissioncontroller.permission.ui.Category import com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog import com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment @@ -40,6 +42,7 @@ import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsV import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel.PermSubtitle import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModel import com.android.permissioncontroller.permission.ui.wear.model.RevokeDialogArgs +import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel import com.android.permissioncontroller.permission.utils.ArrayUtils import com.android.permissioncontroller.permission.utils.LocationUtils import com.android.permissioncontroller.permission.utils.Utils @@ -50,16 +53,26 @@ class WearAppPermissionGroupsHelper( val context: Context, val fragment: Fragment, val user: UserHandle, + val packageName: String, val sessionId: Long, private val appPermissions: AppPermissions, val viewModel: AppPermissionGroupsViewModel, + val wearViewModel: WearAppPermissionUsagesViewModel, val revokeDialogViewModel: AppPermissionGroupsRevokeDialogViewModel, private val toggledGroups: ArraySet<AppPermissionGroup> = ArraySet() ) { - fun getPermissionGroupChipParams(): List<PermissionGroupChipParam> { + fun getPermissionGroupChipParams( + appPermissionUsages: List<AppPermissionUsage> + ): List<PermissionGroupChipParam> { if (DEBUG) { Log.d(TAG, "getPermissionGroupChipParams() called") } + val groupUsageLastAccessTime: MutableMap<String, Long> = HashMap() + viewModel.extractGroupUsageLastAccessTime( + groupUsageLastAccessTime, + appPermissionUsages, + packageName + ) val groupUiInfos = viewModel.packagePermGroupsLiveData.value val groups: List<AppPermissionGroup> = appPermissions.permissionGroups @@ -107,7 +120,11 @@ class WearAppPermissionGroupsHelper( label = group.label.toString(), summary = bookKeeping[group.name]?.let { - getSummary(category, it.subtitle) + getSummary( + category, + it, + groupUsageLastAccessTime[it.groupName] + ) }, onClick = { onPermissionGroupClicked(group, category.categoryName) } ) @@ -118,7 +135,29 @@ class WearAppPermissionGroupsHelper( return list } - private fun getSummary(category: Category?, subtitle: PermSubtitle): Int? { + private fun getSummary( + category: Category?, + groupUiInfo: GroupUiInfo, + lastAccessTime: Long? + ): String { + val grantSummary = + getGrantSummary(category, groupUiInfo)?.let { context.getString(it) } ?: "" + if (!Flags.wearPrivacyDashboardEnabled()) { + return grantSummary + } + val accessSummary = + viewModel.getPreferenceSummary(groupUiInfo, context, lastAccessTime).let { + if (it.isNotEmpty()) { + System.lineSeparator() + it + } else { + it + } + } + return grantSummary + accessSummary + } + + private fun getGrantSummary(category: Category?, groupUiInfo: GroupUiInfo): Int? { + val subtitle = groupUiInfo.subtitle if (category != null) { when (category) { Category.ALLOWED -> return R.string.allowed_header @@ -335,7 +374,7 @@ data class PermissionGroupChipParam( val group: AppPermissionGroup, val perm: PermissionInfo? = null, val label: String, - val summary: Int? = null, + val summary: String? = null, val enabled: Boolean = true, val checked: Boolean? = null, val onClick: () -> Unit = {}, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt index b84e23566..bc840bac9 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt @@ -37,13 +37,14 @@ import com.android.permissioncontroller.permission.ui.wear.model.RevokeDialogArg fun WearAppPermissionGroupsScreen(helper: WearAppPermissionGroupsHelper) { val packagePermGroups = helper.viewModel.packagePermGroupsLiveData.observeAsState(emptyMap()) val autoRevoke = helper.viewModel.autoRevokeLiveData.observeAsState(null) + val appPermissionUsages = helper.wearViewModel.appPermissionUsages.observeAsState(emptyList()) val showRevokeDialog = helper.revokeDialogViewModel.showDialogLiveData.observeAsState(false) var isLoading by remember { mutableStateOf(true) } Box { WearAppPermissionGroupsContent( isLoading, - helper.getPermissionGroupChipParams(), + helper.getPermissionGroupChipParams(appPermissionUsages.value), helper.getAutoRevokeChipParam(autoRevoke.value) ) RevokeDialog( @@ -80,7 +81,9 @@ internal fun WearAppPermissionGroupsContent( } else { Chip( label = info.label, - secondaryLabel = info.summary?.let { stringResource(info.summary) }, + labelMaxLines = Integer.MAX_VALUE, + secondaryLabel = info.summary?.let { info.summary }, + secondaryLabelMaxLines = Integer.MAX_VALUE, enabled = info.enabled, onClick = info.onClick ) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearAppPermissionUsagesViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearAppPermissionUsagesViewModel.kt new file mode 100644 index 000000000..85e9eaef2 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearAppPermissionUsagesViewModel.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 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.wear.model + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage + +class WearAppPermissionUsagesViewModel : ViewModel() { + val appPermissionUsages = MutableLiveData<List<AppPermissionUsage>>() +} + +/** Factory for a WearAppPermissionGroupsViewModel */ +class WearAppPermissionUsagesViewModelFactory : ViewModelProvider.Factory { + override fun <T : ViewModel> create(modelClass: Class<T>): T { + @Suppress("UNCHECKED_CAST") return WearAppPermissionUsagesViewModel() as T + } +} |