summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author YoungJoon Yang <youngjoonyang@google.com> 2023-12-04 14:25:30 +0900
committer YoungJoon Yang <youngjoonyang@google.com> 2023-12-06 11:36:19 +0900
commitd2d700d3968c35badd7681b36cc70220451f7c2e (patch)
tree5744bf840d3226b25bb9bb98760a33508be2616e
parent7c4f2059ae4f6e356580556e0ebe2b8013cb84d4 (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
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt71
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt47
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearAppPermissionUsagesViewModel.kt33
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
+ }
+}