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.wear.permission.components.material2
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.semantics.heading
26 import androidx.compose.ui.semantics.semantics
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.LocalTextStyle
35 import androidx.wear.compose.material.MaterialTheme
36 import androidx.wear.compose.material.Text
37 import androidx.wear.compose.material.dialog.Dialog
38 import com.android.permissioncontroller.wear.permission.components.material2.layout.ScalingLazyColumnDefaults
39 import com.android.permissioncontroller.wear.permission.components.material2.layout.ScalingLazyColumnState
40 import com.android.permissioncontroller.wear.permission.components.material2.layout.rememberColumnState
41 import com.android.permissioncontroller.wear.permission.components.material3.DialogButtonContent
42 import com.android.permissioncontroller.wear.permission.components.material3.WearPermissionIconBuilder
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 title: String? = null,
54 message: String,
55 positiveButtonContent: DialogButtonContent?,
56 negativeButtonContent: DialogButtonContent?,
57 showDialog: Boolean,
58 modifier: Modifier = Modifier,
59 iconRes: WearPermissionIconBuilder? = null,
60 scalingLazyListState: ScalingLazyListState,
61 ) {
62 Dialog(
63 showDialog = showDialog,
64 onDismissRequest = { negativeButtonContent?.onClick?.invoke() },
65 scrollState = scalingLazyListState,
66 modifier = modifier,
67 ) {
68 AlertContent(
69 title = title,
70 icon = { iconRes?.build() },
71 message = message,
72 positiveButtonContent = positiveButtonContent,
73 negativeButtonContent = negativeButtonContent,
74 )
75 }
76 }
77
78 @Composable
AlertContentnull79 fun AlertContent(
80 icon: @Composable (() -> Unit)? = null,
81 title: String? = null,
82 message: String? = null,
83 positiveButtonContent: DialogButtonContent?,
84 negativeButtonContent: DialogButtonContent?,
85 state: ScalingLazyColumnState =
86 rememberColumnState(ScalingLazyColumnDefaults.responsive(additionalPaddingAtBottom = 0.dp)),
87 showPositionIndicator: Boolean = true,
88 content: (ScalingLazyListScope.() -> Unit)? = null,
89 ) {
90 val density = LocalDensity.current
91 val maxScreenWidthPx = with(density) { LocalConfiguration.current.screenWidthDp.dp.toPx() }
92
93 ResponsiveDialogContent(
94 icon = icon,
95 title =
96 title?.let {
97 {
98 Text(
99 modifier = Modifier.fillMaxWidth().semantics() { heading() },
100 text = it,
101 color = MaterialTheme.colors.onBackground,
102 textAlign = TextAlign.Center,
103 overflow = TextOverflow.Ellipsis,
104 )
105 }
106 },
107 message =
108 message?.let {
109 {
110 // Should message be start or center aligned?
111 val textMeasurer = rememberTextMeasurer()
112 val textStyle = LocalTextStyle.current
113 val totalPaddingPercentage =
114 globalHorizontalPadding + messageExtraHorizontalPadding
115 val lineCount =
116 remember(it, density, textStyle, textMeasurer) {
117 textMeasurer
118 .measure(
119 text = it,
120 style = textStyle,
121 constraints =
122 Constraints(
123 // Available width is reduced by responsive dialog
124 // horizontal
125 // padding.
126 maxWidth =
127 (maxScreenWidthPx *
128 (1f - totalPaddingPercentage * 2f / 100f))
129 .toInt()
130 ),
131 )
132 .lineCount
133 }
134 val textAlign = if (lineCount <= 3) TextAlign.Center else TextAlign.Start
135 Text(
136 modifier = Modifier.fillMaxWidth(),
137 text = it,
138 color = MaterialTheme.colors.onBackground,
139 textAlign = textAlign,
140 )
141 }
142 },
143 content = content,
144 positiveButtonContent = positiveButtonContent,
145 negativeButtonContent = negativeButtonContent,
146 state = state,
147 showPositionIndicator = showPositionIndicator,
148 )
149 }
150