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 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.model.AppPermissionViewModel
30 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonState
31 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType
32 import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs
33 import com.android.permissioncontroller.permission.ui.wear.elements.AlertDialog
34 import com.android.permissioncontroller.permission.ui.wear.elements.ListFooter
35 import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
36 import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip
37 import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl
38 import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionConfirmDialogViewModel
39 import com.android.permissioncontroller.permission.ui.wear.model.ConfirmDialogArgs
40 import com.android.settingslib.RestrictedLockUtils
41 
42 @Composable
43 fun WearAppPermissionScreen(
44     title: String,
45     viewModel: AppPermissionViewModel,
46     confirmDialogViewModel: AppPermissionConfirmDialogViewModel,
47     onLocationSwitchChanged: (Boolean) -> Unit,
48     onGrantedStateChanged: (ButtonType, Boolean) -> Unit,
49     onFooterClicked: (RestrictedLockUtils.EnforcedAdmin) -> Unit,
50     onConfirmDialogOkButtonClick: (ConfirmDialogArgs) -> Unit,
51     onConfirmDialogCancelButtonClick: () -> Unit,
52     onAdvancedConfirmDialogOkButtonClick: (AdvancedConfirmDialogArgs) -> Unit,
53     onAdvancedConfirmDialogCancelButtonClick: () -> Unit
54 ) {
55     val buttonState = viewModel.buttonStateLiveData.observeAsState(null)
56     val detailResIds = viewModel.detailResIdLiveData.observeAsState(null)
57     val admin = viewModel.showAdminSupportLiveData.observeAsState(null)
58     var isLoading by remember { mutableStateOf(true) }
59     val showConfirmDialog = confirmDialogViewModel.showConfirmDialogLiveData.observeAsState(false)
60     val showAdvancedConfirmDialog =
61         confirmDialogViewModel.showAdvancedConfirmDialogLiveData.observeAsState(false)
62 
63     Box {
64         WearAppPermissionContent(
65             title,
66             buttonState.value,
67             detailResIds.value,
68             admin.value,
69             isLoading,
70             onLocationSwitchChanged,
71             onGrantedStateChanged,
72             onFooterClicked,
73         )
74         ConfirmDialog(
75             showDialog = showConfirmDialog.value,
76             args = confirmDialogViewModel.confirmDialogArgs,
77             onOkButtonClick = onConfirmDialogOkButtonClick,
78             onCancelButtonClick = onConfirmDialogCancelButtonClick
79         )
80         AdvancedConfirmDialog(
81             showDialog = showAdvancedConfirmDialog.value,
82             args = confirmDialogViewModel.advancedConfirmDialogArgs,
83             onOkButtonClick = onAdvancedConfirmDialogOkButtonClick,
84             onCancelButtonClick = onAdvancedConfirmDialogCancelButtonClick
85         )
86     }
87     if (isLoading && !buttonState.value.isNullOrEmpty()) {
88         isLoading = false
89     }
90 }
91 
92 @Composable
WearAppPermissionContentnull93 internal fun WearAppPermissionContent(
94     title: String,
95     buttonState: Map<ButtonType, ButtonState>?,
96     detailResIds: Pair<Int, Int?>?,
97     admin: RestrictedLockUtils.EnforcedAdmin?,
98     isLoading: Boolean,
99     onLocationSwitchChanged: (Boolean) -> Unit,
100     onGrantedStateChanged: (ButtonType, Boolean) -> Unit,
101     onFooterClicked: (RestrictedLockUtils.EnforcedAdmin) -> Unit
102 ) {
103     ScrollableScreen(title = title, isLoading = isLoading) {
104         buttonState?.get(ButtonType.LOCATION_ACCURACY)?.let {
105             if (it.isShown) {
106                 item {
107                     ToggleChip(
108                         checked = it.isChecked,
109                         enabled = it.isEnabled,
110                         label = stringResource(R.string.app_permission_location_accuracy),
111                         toggleControl = ToggleChipToggleControl.Switch,
112                         onCheckedChanged = onLocationSwitchChanged,
113                         labelMaxLine = Integer.MAX_VALUE
114                     )
115                 }
116             }
117         }
118         for (buttonType in buttonTypeOrder) {
119             buttonState?.get(buttonType)?.let {
120                 if (it.isShown) {
121                     item {
122                         ToggleChip(
123                             checked = it.isChecked,
124                             enabled = it.isEnabled,
125                             label = labelsByButton(buttonType),
126                             toggleControl = ToggleChipToggleControl.Radio,
127                             onCheckedChanged = { checked ->
128                                 onGrantedStateChanged(buttonType, checked)
129                             },
130                             labelMaxLine = Integer.MAX_VALUE
131                         )
132                     }
133                 }
134             }
135         }
136         detailResIds?.let {
137             item {
138                 ListFooter(
139                     description = stringResource(detailResIds.first),
140                     iconRes = R.drawable.ic_info,
141                     onClick =
142                         if (admin != null) {
143                             { onFooterClicked(admin) }
144                         } else {
145                             null
146                         }
147                 )
148             }
149         }
150     }
151 }
152 
153 internal val buttonTypeOrder =
154     listOf(
155         ButtonType.ALLOW,
156         ButtonType.ALLOW_ALWAYS,
157         ButtonType.ALLOW_FOREGROUND,
158         ButtonType.ASK_ONCE,
159         ButtonType.ASK,
160         ButtonType.DENY,
161         ButtonType.DENY_FOREGROUND
162     )
163 
164 @Composable
labelsByButtonnull165 internal fun labelsByButton(buttonType: ButtonType) =
166     when (buttonType) {
167         ButtonType.ALLOW -> stringResource(R.string.app_permission_button_allow)
168         ButtonType.ALLOW_ALWAYS -> stringResource(R.string.app_permission_button_allow_always)
169         ButtonType.ALLOW_FOREGROUND ->
170             stringResource(R.string.app_permission_button_allow_foreground)
171         ButtonType.ASK_ONCE -> stringResource(R.string.app_permission_button_ask)
172         ButtonType.ASK -> stringResource(R.string.app_permission_button_ask)
173         ButtonType.DENY -> stringResource(R.string.app_permission_button_deny)
174         ButtonType.DENY_FOREGROUND -> stringResource(R.string.app_permission_button_deny)
175         else -> ""
176     }
177 
178 @Composable
ConfirmDialognull179 internal fun ConfirmDialog(
180     showDialog: Boolean,
181     args: ConfirmDialogArgs?,
182     onOkButtonClick: (ConfirmDialogArgs) -> Unit,
183     onCancelButtonClick: () -> Unit
184 ) {
185     args?.let {
186         AlertDialog(
187             showDialog = showDialog,
188             message = stringResource(it.messageId),
189             onOKButtonClick = { onOkButtonClick(it) },
190             onCancelButtonClick = onCancelButtonClick,
191             scalingLazyListState = rememberScalingLazyListState()
192         )
193     }
194 }
195 
196 @Composable
AdvancedConfirmDialognull197 internal fun AdvancedConfirmDialog(
198     showDialog: Boolean,
199     args: AdvancedConfirmDialogArgs?,
200     onOkButtonClick: (AdvancedConfirmDialogArgs) -> Unit,
201     onCancelButtonClick: () -> Unit
202 ) {
203     args?.let {
204         AlertDialog(
205             showDialog = showDialog,
206             title =
207                 if (it.titleId != 0) {
208                     stringResource(it.titleId)
209                 } else {
210                     ""
211                 },
212             iconRes = it.iconId,
213             message = stringResource(it.messageId),
214             okButtonContentDescription = stringResource(it.positiveButtonTextId),
215             cancelButtonContentDescription = stringResource(it.negativeButtonTextId),
216             onOKButtonClick = { onOkButtonClick(it) },
217             onCancelButtonClick = onCancelButtonClick,
218             scalingLazyListState = rememberScalingLazyListState()
219         )
220     }
221 }
222