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 android.credentials.flags.Flags
20 import androidx.compose.animation.animateContentSize
21 import androidx.compose.foundation.background
22 import androidx.compose.foundation.layout.Box
23 import androidx.compose.foundation.layout.WindowInsets
24 import androidx.compose.foundation.layout.fillMaxWidth
25 import androidx.compose.foundation.layout.navigationBars
26 import androidx.compose.foundation.layout.padding
27 import androidx.compose.foundation.layout.wrapContentHeight
28 import androidx.compose.material3.ExperimentalMaterial3Api
29 import androidx.compose.material3.MaterialTheme
30 import androidx.compose.runtime.Composable
31 import androidx.compose.runtime.LaunchedEffect
32 import androidx.compose.runtime.rememberCoroutineScope
33 import androidx.compose.ui.Modifier
34 import androidx.compose.ui.graphics.Color
35 import com.android.compose.rememberSystemUiController
36 import com.android.compose.theme.LocalAndroidColorScheme
37 import androidx.compose.ui.unit.dp
38 import com.android.credentialmanager.common.material.ModalBottomSheetLayout
39 import com.android.credentialmanager.common.material.ModalBottomSheetValue
40 import com.android.credentialmanager.common.material.rememberModalBottomSheetState
41 import com.android.credentialmanager.ui.theme.EntryShape
42 import kotlinx.coroutines.launch
43 
44 /** Draws a modal bottom sheet with the same styles and effects shared by various flows. */
45 @Composable
46 @OptIn(ExperimentalMaterial3Api::class)
ModalBottomSheetnull47 fun ModalBottomSheet(
48         sheetContent: @Composable () -> Unit,
49         onDismiss: () -> Unit,
50         isInitialRender: Boolean,
51         onInitialRenderComplete: () -> Unit,
52         isAutoSelectFlow: Boolean,
53 ) {
54     if (Flags.selectorUiImprovementsEnabled()) {
55         val state = androidx.compose.material3.rememberModalBottomSheetState(
56                 skipPartiallyExpanded = true
57         )
58         androidx.compose.material3.ModalBottomSheet(
59                 onDismissRequest = onDismiss,
60                 containerColor = LocalAndroidColorScheme.current.surfaceBright,
61                 sheetState = state,
62                 content = {
63                     Box(
64                             modifier = Modifier
65                                     .animateContentSize()
66                                     .wrapContentHeight()
67                                     .fillMaxWidth()
68                     ) {
69                         sheetContent()
70                     }
71                 },
72                 scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = .32f),
73                 shape = EntryShape.TopRoundedCorner,
74                 contentWindowInsets = { WindowInsets.navigationBars },
75                 dragHandle = null,
76                 // Never take over the full screen. We always want to leave some top scrim space
77                 // for exiting and viewing the underlying app to help a user gain context.
78                 modifier = Modifier.padding(top = 72.dp),
79         )
80     } else {
81         val scope = rememberCoroutineScope()
82         val state = rememberModalBottomSheetState(
83                 initialValue = if (isAutoSelectFlow) ModalBottomSheetValue.Expanded
84                 else ModalBottomSheetValue.Hidden,
85                 skipHalfExpanded = true
86         )
87         val sysUiController = rememberSystemUiController()
88         if (state.targetValue == ModalBottomSheetValue.Hidden || isAutoSelectFlow) {
89             setTransparentSystemBarsColor(sysUiController)
90         } else {
91             setBottomSheetSystemBarsColor(sysUiController)
92         }
93         ModalBottomSheetLayout(
94                 sheetBackgroundColor = LocalAndroidColorScheme.current.surfaceBright,
95                 modifier = Modifier.background(Color.Transparent),
96                 sheetState = state,
97                 sheetContent = { sheetContent() },
98                 sheetShape = EntryShape.TopRoundedCorner,
99         ) {}
100         LaunchedEffect(state.currentValue, state.targetValue) {
101             if (state.currentValue == ModalBottomSheetValue.Hidden) {
102                 if (isInitialRender) {
103                     onInitialRenderComplete()
104                     scope.launch { state.show() }
105                 } else if (state.targetValue == ModalBottomSheetValue.Hidden) {
106                     // Only dismiss ui when the motion is downwards
107                     onDismiss()
108                 }
109             }
110         }
111     }
112 }
113