1 /*
<lambda>null2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.permissioncontroller.permission.ui.wear
18
19 import android.os.Build
20 import android.permission.flags.Flags
21 import androidx.annotation.RequiresApi
22 import androidx.compose.foundation.layout.fillMaxWidth
23 import androidx.compose.runtime.Composable
24 import androidx.compose.runtime.getValue
25 import androidx.compose.runtime.livedata.observeAsState
26 import androidx.compose.runtime.mutableStateOf
27 import androidx.compose.runtime.remember
28 import androidx.compose.runtime.setValue
29 import androidx.compose.ui.Modifier
30 import androidx.compose.ui.platform.LocalContext
31 import androidx.compose.ui.res.stringResource
32 import com.android.permissioncontroller.R
33 import com.android.permissioncontroller.permission.ui.handheld.v31.PermissionUsageControlPreference
34 import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModel
35 import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsagesUiState
36 import com.android.permissioncontroller.permission.utils.Utils
37 import com.android.permissioncontroller.wear.permission.components.ScrollableScreen
38 import com.android.permissioncontroller.wear.permission.components.material3.WearPermissionButton
39 import com.android.permissioncontroller.wear.permission.components.material3.WearPermissionIconBuilder
40 import java.text.Collator
41
42 @RequiresApi(Build.VERSION_CODES.S)
43 @Composable
44 fun WearPermissionUsageScreen(sessionId: Long, viewModel: PermissionUsageViewModel) {
45 val context = LocalContext.current
46 val permissionUsagesUiData = viewModel.permissionUsagesUiLiveData.observeAsState(null)
47 val showSystem = viewModel.showSystemAppsLiveData.observeAsState(false)
48 var isLoading by remember { mutableStateOf(true) }
49 val isDataLoaded = permissionUsagesUiData.value is PermissionUsagesUiState.Success
50 val hasSystemApps: Boolean =
51 if (isDataLoaded) {
52 val uiState = permissionUsagesUiData.value as PermissionUsagesUiState.Success
53 uiState.containsSystemAppUsage
54 } else {
55 false
56 }
57
58 val onShowSystemClick: (Boolean) -> Unit = { show -> run { viewModel.updateShowSystem(show) } }
59
60 val permissionGroupWithUsageCounts: Map<String, Int> =
61 if (isDataLoaded) {
62 val uiState = permissionUsagesUiData.value as PermissionUsagesUiState.Success
63 uiState.permissionGroupUsageCount
64 } else {
65 emptyMap()
66 }
67 val permissionGroupWithUsageCountsEntries: List<Map.Entry<String, Int>> =
68 ArrayList<Map.Entry<String, Int>>(permissionGroupWithUsageCounts.entries)
69
70 val collator = Collator.getInstance(context.resources.configuration.locales.get(0))
71 val permissionGroupPreferences =
72 permissionGroupWithUsageCountsEntries
73 // Removing Health Connect from the list of permissions to fix b/331260850
74 .let {
75 if (Flags.replaceBodySensorPermissionEnabled()) {
76 it
77 } else {
78 it.filterNot { Utils.isHealthPermissionGroup(it.key) }
79 }
80 }
81 .map {
82 PermissionUsageControlPreference(
83 context,
84 it.key,
85 it.value,
86 showSystem.value,
87 sessionId,
88 false,
89 )
90 }
91 .sortedWith { o1, o2 ->
92 var result = collator.compare(o1.title.toString(), o2.title.toString())
93 if (result == 0) {
94 result = o1.title.toString().compareTo(o2.title.toString())
95 }
96 result
97 }
98 .toList()
99
100 WearPermissionUsageContent(
101 isLoading,
102 hasSystemApps,
103 showSystem.value,
104 onShowSystemClick,
105 permissionGroupPreferences,
106 )
107
108 if (isLoading && isDataLoaded) {
109 isLoading = false
110 }
111 }
112
113 @Composable
WearPermissionUsageContentnull114 internal fun WearPermissionUsageContent(
115 isLoading: Boolean,
116 hasSystemApps: Boolean,
117 showSystem: Boolean,
118 onShowSystemClick: (Boolean) -> Unit,
119 permissionGroupPreferences: List<PermissionUsageControlPreference>,
120 ) {
121 ScrollableScreen(
122 title = stringResource(R.string.permission_usage_title),
123 isLoading = isLoading,
124 ) {
125 if (permissionGroupPreferences.isEmpty()) {
126 item {
127 WearPermissionButton(label = stringResource(R.string.no_permissions), onClick = {})
128 }
129 } else {
130 for (preference in permissionGroupPreferences) {
131 item {
132 WearPermissionButton(
133 label = preference.title.toString(),
134 labelMaxLines = Int.MAX_VALUE,
135 secondaryLabel = preference.summary.toString(),
136 secondaryLabelMaxLines = Int.MAX_VALUE,
137 iconBuilder =
138 preference.icon?.let { WearPermissionIconBuilder.builder(it) },
139 enabled = preference.isEnabled,
140 onClick = { preference.performClick() },
141 )
142 }
143 }
144 if (hasSystemApps) {
145 item {
146 WearPermissionButton(
147 label =
148 if (showSystem) {
149 stringResource(R.string.menu_hide_system)
150 } else {
151 stringResource(R.string.menu_show_system)
152 },
153 labelMaxLines = Int.MAX_VALUE,
154 onClick = { onShowSystemClick(!showSystem) },
155 modifier = Modifier.fillMaxWidth(),
156 )
157 }
158 }
159 }
160 }
161 }
162