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.elements
18
19 import androidx.compose.foundation.layout.fillMaxWidth
20 import androidx.compose.runtime.Composable
21 import androidx.compose.runtime.remember
22 import androidx.compose.ui.Modifier
23 import androidx.compose.ui.platform.LocalConfiguration
24 import androidx.compose.ui.platform.LocalDensity
25 import androidx.compose.ui.res.painterResource
26 import androidx.compose.ui.res.stringResource
27 import androidx.compose.ui.text.rememberTextMeasurer
28 import androidx.compose.ui.text.style.TextAlign
29 import androidx.compose.ui.text.style.TextOverflow
30 import androidx.compose.ui.unit.Constraints
31 import androidx.compose.ui.unit.dp
32 import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
33 import androidx.wear.compose.foundation.lazy.ScalingLazyListState
34 import androidx.wear.compose.material.Icon
35 import androidx.wear.compose.material.LocalTextStyle
36 import androidx.wear.compose.material.MaterialTheme
37 import androidx.wear.compose.material.Text
38 import androidx.wear.compose.material.dialog.Alert
39 import androidx.wear.compose.material.dialog.Dialog
40 import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnDefaults
41 import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnState
42 import com.android.permissioncontroller.permission.ui.wear.elements.layout.rememberColumnState
43
44 /**
45 * This component is an alternative to [AlertContent], providing the following:
46 * - a convenient way of passing a title and a message;
47 * - additional content can be specified between the message and the buttons
48 * - default positive and negative buttons;
49 * - wrapped in a [Dialog];
50 */
51 @Composable
AlertDialognull52 fun AlertDialog(
53 message: String,
54 iconRes: Int? = null,
55 onCancelButtonClick: () -> Unit,
56 onOKButtonClick: () -> Unit,
57 showDialog: Boolean,
58 scalingLazyListState: ScalingLazyListState,
59 modifier: Modifier = Modifier,
60 title: String? = null,
61 okButtonContentDescription: String = stringResource(android.R.string.ok),
62 cancelButtonContentDescription: String = stringResource(android.R.string.cancel)
63 ) {
64 Dialog(
65 showDialog = showDialog,
66 onDismissRequest = onCancelButtonClick,
67 scrollState = scalingLazyListState,
68 modifier = modifier
69 ) {
70 AlertContent(
71 title = title,
72 icon = { AlertIcon(iconRes) },
73 message = message,
74 onCancel = onCancelButtonClick,
75 onOk = onOKButtonClick,
76 okButtonContentDescription = okButtonContentDescription,
77 cancelButtonContentDescription = cancelButtonContentDescription
78 )
79 }
80 }
81
82 /**
83 * This component is an alternative to [Alert], providing the following:
84 * - a convenient way of passing a title and a message;
85 * - default one button;
86 * - wrapped in a [Dialog];
87 */
88 @Composable
SingleButtonAlertDialognull89 fun SingleButtonAlertDialog(
90 message: String,
91 iconRes: Int? = null,
92 onButtonClick: () -> Unit,
93 showDialog: Boolean,
94 scalingLazyListState: ScalingLazyListState,
95 modifier: Modifier = Modifier,
96 title: String? = null,
97 buttonContentDescription: String = stringResource(android.R.string.ok)
98 ) {
99 Dialog(
100 showDialog = showDialog,
101 onDismissRequest = {},
102 scrollState = scalingLazyListState,
103 modifier = modifier
104 ) {
105 AlertContent(
106 title = title,
107 icon = { AlertIcon(iconRes) },
108 message = message,
109 onOk = onButtonClick,
110 okButtonContentDescription = buttonContentDescription
111 )
112 }
113 }
114
115 @Composable
AlertContentnull116 public fun AlertContent(
117 onCancel: (() -> Unit)? = null,
118 onOk: (() -> Unit)? = null,
119 icon: @Composable (() -> Unit)? = null,
120 title: String? = null,
121 message: String? = null,
122 okButtonContentDescription: String = stringResource(android.R.string.ok),
123 cancelButtonContentDescription: String = stringResource(android.R.string.cancel),
124 state: ScalingLazyColumnState =
125 rememberColumnState(
126 ScalingLazyColumnDefaults.responsive(
127 additionalPaddingAtBottom = 0.dp,
128 ),
129 ),
130 showPositionIndicator: Boolean = true,
131 content: (ScalingLazyListScope.() -> Unit)? = null,
132 ) {
133 val density = LocalDensity.current
134 val maxScreenWidthPx = with(density) { LocalConfiguration.current.screenWidthDp.dp.toPx() }
135
136 ResponsiveDialogContent(
137 icon = icon,
138 title =
139 title?.let {
140 {
141 Text(
142 modifier = Modifier.fillMaxWidth(),
143 text = it,
144 color = MaterialTheme.colors.onBackground,
145 textAlign = TextAlign.Center,
146 maxLines = if (icon == null) 3 else 2,
147 overflow = TextOverflow.Ellipsis,
148 )
149 }
150 },
151 message =
152 message?.let {
153 {
154 // Should message be start or center aligned?
155 val textMeasurer = rememberTextMeasurer()
156 val textStyle = LocalTextStyle.current
157 val totalPaddingPercentage =
158 globalHorizontalPadding + messageExtraHorizontalPadding
159 val lineCount =
160 remember(it, density, textStyle, textMeasurer) {
161 textMeasurer
162 .measure(
163 text = it,
164 style = textStyle,
165 constraints =
166 Constraints(
167 // Available width is reduced by responsive dialog
168 // horizontal
169 // padding.
170 maxWidth =
171 (maxScreenWidthPx *
172 (1f - totalPaddingPercentage * 2f / 100f))
173 .toInt(),
174 ),
175 )
176 .lineCount
177 }
178 val textAlign = if (lineCount <= 3) TextAlign.Center else TextAlign.Start
179 Text(
180 modifier = Modifier.fillMaxWidth(),
181 text = it,
182 color = MaterialTheme.colors.onBackground,
183 textAlign = textAlign,
184 )
185 }
186 },
187 content = content,
188 onOk = onOk,
189 onCancel = onCancel,
190 okButtonContentDescription = okButtonContentDescription,
191 cancelButtonContentDescription = cancelButtonContentDescription,
192 state = state,
193 showPositionIndicator = showPositionIndicator,
194 )
195 }
196
197 @Composable
AlertIconnull198 private fun AlertIcon(iconRes: Int?) =
199 if (iconRes != null && iconRes != 0) {
200 Icon(painter = painterResource(iconRes), contentDescription = null)
201 } else {
202 null
203 }
204