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.credentialmanager.common.ui
18 
19 import androidx.compose.foundation.layout.Box
20 import androidx.compose.foundation.layout.BoxWithConstraints
21 import androidx.compose.foundation.layout.Row
22 import androidx.compose.foundation.layout.fillMaxSize
23 import androidx.compose.foundation.layout.padding
24 import androidx.compose.foundation.layout.wrapContentSize
25 import androidx.compose.material.icons.Icons
26 import androidx.compose.material.icons.filled.Close
27 import androidx.compose.material3.Card
28 import androidx.compose.material3.CardDefaults
29 import androidx.compose.material3.Icon
30 import androidx.compose.material3.IconButton
31 import androidx.compose.material3.MaterialTheme
32 import androidx.compose.runtime.Composable
33 import androidx.compose.runtime.LaunchedEffect
34 import androidx.compose.ui.Alignment
35 import androidx.compose.ui.Modifier
36 import androidx.compose.ui.graphics.Color
37 import androidx.compose.ui.platform.LocalAccessibilityManager
38 import androidx.compose.ui.res.stringResource
39 import androidx.compose.ui.unit.dp
40 import com.android.compose.rememberSystemUiController
41 import com.android.credentialmanager.R
42 import com.android.credentialmanager.common.material.Scrim
43 import com.android.credentialmanager.ui.theme.Shapes
44 import kotlinx.coroutines.delay
45 
46 @Composable
47 fun Snackbar(
48     contentText: String,
49     action: (@Composable () -> Unit)? = null,
50     onDismiss: () -> Unit,
51     dismissOnTimeout: Boolean = false,
52 ) {
53     val sysUiController = rememberSystemUiController()
54     setTransparentSystemBarsColor(sysUiController)
55     BoxWithConstraints {
56         Box(Modifier.fillMaxSize()) {
57             Scrim(
58                 color = Color.Transparent,
59                 onDismiss = onDismiss,
60                 visible = true
61             )
62         }
63         Box(
64             modifier = Modifier
65                 .align(Alignment.BottomCenter).wrapContentSize().padding(bottom = 18.dp)
66         ) {
67             Card(
68                 shape = Shapes.medium,
69                 modifier = Modifier.wrapContentSize(),
70                 colors = CardDefaults.cardColors(
71                     containerColor = MaterialTheme.colorScheme.inverseSurface,
72                 )
73             ) {
74                 Row(
75                     modifier = Modifier.wrapContentSize(),
76                     verticalAlignment = Alignment.CenterVertically,
77                 ) {
78                     SnackbarContentText(contentText, modifier = Modifier.padding(
79                         top = 18.dp, bottom = 18.dp, start = 24.dp,
80                     ))
81                     if (action != null) {
82                         action()
83                     }
84                     IconButton(onClick = onDismiss, modifier = Modifier.padding(
85                         top = 4.dp, bottom = 4.dp, start = 2.dp, end = 10.dp,
86                     )) {
87                         Icon(
88                             Icons.Filled.Close,
89                             contentDescription = stringResource(
90                                 R.string.accessibility_snackbar_dismiss
91                             ),
92                             tint = MaterialTheme.colorScheme.inverseOnSurface,
93                         )
94                     }
95                 }
96             }
97         }
98     }
99     val accessibilityManager = LocalAccessibilityManager.current
100     LaunchedEffect(true) {
101         if (dismissOnTimeout) {
102             // Same as SnackbarDuration.Short
103             val originalDuration = 4000L
104             val duration = if (accessibilityManager == null) originalDuration else
105                 accessibilityManager.calculateRecommendedTimeoutMillis(
106                     originalDuration,
107                     containsIcons = true,
108                     containsText = true,
109                     containsControls = action != null,
110                 )
111             delay(duration)
112             onDismiss()
113         }
114     }
115 }