1 /*
2  * 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 androidx.compose.foundation.layout.Box
20 import androidx.compose.runtime.Composable
21 import androidx.compose.runtime.getValue
22 import androidx.compose.runtime.livedata.observeAsState
23 import androidx.compose.runtime.mutableStateOf
24 import androidx.compose.runtime.remember
25 import androidx.compose.runtime.setValue
26 import androidx.compose.ui.res.stringResource
27 import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
28 import com.android.permissioncontroller.R
29 import com.android.permissioncontroller.permission.ui.wear.elements.AlertDialog
30 import com.android.permissioncontroller.permission.ui.wear.elements.Chip
31 import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
32 import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip
33 import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl
34 import com.android.permissioncontroller.permission.ui.wear.model.RevokeDialogArgs
35 
36 @Composable
WearAppPermissionGroupsScreennull37 fun WearAppPermissionGroupsScreen(helper: WearAppPermissionGroupsHelper) {
38     val packagePermGroups = helper.viewModel.packagePermGroupsLiveData.observeAsState(null)
39     val autoRevoke = helper.viewModel.autoRevokeLiveData.observeAsState(null)
40     val appPermissionUsages = helper.wearViewModel.appPermissionUsages.observeAsState(emptyList())
41     val showRevokeDialog = helper.revokeDialogViewModel.showDialogLiveData.observeAsState(false)
42     var isLoading by remember { mutableStateOf(true) }
43 
44     Box {
45         WearAppPermissionGroupsContent(
46             isLoading,
47             helper.getPermissionGroupChipParams(appPermissionUsages.value),
48             helper.getAutoRevokeChipParam(autoRevoke.value)
49         )
50         RevokeDialog(
51             showDialog = showRevokeDialog.value,
52             args = helper.revokeDialogViewModel.revokeDialogArgs
53         )
54     }
55 
56     if (isLoading && !packagePermGroups.value.isNullOrEmpty()) {
57         isLoading = false
58     }
59 }
60 
61 @Composable
WearAppPermissionGroupsContentnull62 internal fun WearAppPermissionGroupsContent(
63     isLoading: Boolean,
64     permissionGroupChipParams: List<PermissionGroupChipParam>,
65     autoRevokeChipParam: AutoRevokeChipParam?
66 ) {
67     ScrollableScreen(title = stringResource(R.string.app_permissions), isLoading = isLoading) {
68         if (permissionGroupChipParams.isEmpty()) {
69             item { Chip(label = stringResource(R.string.no_permissions), onClick = {}) }
70         } else {
71             for (info in permissionGroupChipParams) {
72                 item {
73                     if (info.checked != null) {
74                         ToggleChip(
75                             checked = info.checked,
76                             label = info.label,
77                             enabled = info.enabled,
78                             toggleControl = ToggleChipToggleControl.Switch,
79                             onCheckedChanged = info.onCheckedChanged
80                         )
81                     } else {
82                         Chip(
83                             label = info.label,
84                             labelMaxLines = Integer.MAX_VALUE,
85                             secondaryLabel = info.summary?.let { info.summary },
86                             secondaryLabelMaxLines = Integer.MAX_VALUE,
87                             enabled = info.enabled,
88                             onClick = info.onClick
89                         )
90                     }
91                 }
92             }
93             autoRevokeChipParam?.let {
94                 if (it.visible) {
95                     item {
96                         ToggleChip(
97                             checked = it.checked,
98                             label = stringResource(it.labelRes),
99                             labelMaxLine = 3,
100                             toggleControl = ToggleChipToggleControl.Switch,
101                             onCheckedChanged = it.onCheckedChanged
102                         )
103                     }
104                 }
105             }
106         }
107     }
108 }
109 
110 @Composable
RevokeDialognull111 internal fun RevokeDialog(showDialog: Boolean, args: RevokeDialogArgs?) {
112     args?.let {
113         AlertDialog(
114             showDialog = showDialog,
115             message = stringResource(it.messageId),
116             onOKButtonClick = it.onOkButtonClick,
117             onCancelButtonClick = it.onCancelButtonClick,
118             scalingLazyListState = rememberScalingLazyListState()
119         )
120     }
121 }
122