From 5625239b65a8e3daad8ebfe3a45166e66cf4945b Mon Sep 17 00:00:00 2001 From: Helen Qin Date: Mon, 6 Feb 2023 05:11:24 +0000 Subject: [PATCH] Handle different window sizes. Bug: 265887886 Test: manual Change-Id: I2e4c33a0a601859ab7871ab449d1b24f3cb43609 --- .../CredentialManagerRepo.kt | 2 +- .../common/material/ModalBottomSheet.kt | 135 +++++++++++------- .../common/ui/BottomSheet.kt | 54 +++++++ .../credentialmanager/common/ui/Cards.kt | 5 +- .../createflow/CreateCredentialComponents.kt | 23 +-- .../getflow/GetCredentialComponents.kt | 24 +--- 6 files changed, 147 insertions(+), 96 deletions(-) create mode 100644 packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index 6ea1d8d522f93..9e021ada82564 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -64,7 +64,7 @@ class CredentialManagerRepo( requestInfo = intent.extras?.getParcelable( RequestInfo.EXTRA_REQUEST_INFO, RequestInfo::class.java - ) ?: testCreatePasswordRequestInfo() + ) ?: testGetRequestInfo() providerEnabledList = when (requestInfo.type) { RequestInfo.TYPE_CREATE -> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt index f1f453da4f38d..58edb25336f83 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt @@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.layout.offset import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -40,6 +41,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape @@ -131,7 +133,7 @@ class ModalBottomSheetState( if (isSkipHalfExpanded) { require(initialValue != HalfExpanded) { "The initial value must not be set to HalfExpanded if skipHalfExpanded is set to" + - " true." + " true." } } } @@ -209,10 +211,10 @@ class ModalBottomSheetState( message = "Please specify the skipHalfExpanded parameter", replaceWith = ReplaceWith( "ModalBottomSheetState.Saver(" + - "animationSpec = animationSpec," + - "skipHalfExpanded = ," + - "confirmStateChange = confirmStateChange" + - ")" + "animationSpec = animationSpec," + + "skipHalfExpanded = ," + + "confirmStateChange = confirmStateChange" + + ")" ) ) fun Saver( @@ -339,55 +341,77 @@ fun ModalBottomSheetLayout( visible = sheetState.targetValue != Hidden ) } - Surface( - Modifier - .fillMaxWidth() - .nestedScroll(sheetState.nestedScrollConnection) - .offset { - val y = if (sheetState.anchors.isEmpty()) { - // if we don't know our anchors yet, render the sheet as hidden - fullHeight.roundToInt() - } else { - // if we do know our anchors, respect them - sheetState.offset.value.roundToInt() - } - IntOffset(0, y) - } - .bottomSheetSwipeable(sheetState, fullHeight, sheetHeightState) - .onGloballyPositioned { - sheetHeightState.value = it.size.height.toFloat() - } - .semantics { - if (sheetState.isVisible) { - dismiss { - if (sheetState.confirmStateChange(Hidden)) { - scope.launch { sheetState.hide() } - } - true - } - if (sheetState.currentValue == HalfExpanded) { - expand { - if (sheetState.confirmStateChange(Expanded)) { - scope.launch { sheetState.expand() } - } - true - } - } else if (sheetState.hasHalfExpandedState) { - collapse { - if (sheetState.confirmStateChange(HalfExpanded)) { - scope.launch { sheetState.halfExpand() } - } - true - } - } - } - }, - shape = sheetShape, - shadowElevation = sheetElevation, - color = sheetBackgroundColor, - contentColor = sheetContentColor + + // For large screen, allow enough horizontal scrim space. + // Manually calculate the > compact width due to lack of corresponding jetpack dependency. + val maxSheetContentWidth: Dp = + if (maxWidth >= ModalBottomSheetDefaults.MaxCompactWidth && + maxWidth <= ModalBottomSheetDefaults.MaxCompactWidth + + ModalBottomSheetDefaults.StartPadding + ModalBottomSheetDefaults.EndPadding + ) + (maxWidth - ModalBottomSheetDefaults.StartPadding - + ModalBottomSheetDefaults.EndPadding) + else ModalBottomSheetDefaults.MaxSheetWidth + val maxSheetContentHeight = maxHeight - ModalBottomSheetDefaults.MinScrimHeight + Box( + Modifier.sizeIn( + maxWidth = maxSheetContentWidth, + // Allow enough vertical scrim space. + maxHeight = maxSheetContentHeight + ).align(Alignment.TopCenter) ) { - Column(content = sheetContent) + Surface( + Modifier + .fillMaxWidth() + .nestedScroll(sheetState.nestedScrollConnection) + .offset { + val y = if (sheetState.anchors.isEmpty()) { + // if we don't know our anchors yet, render the sheet as hidden + fullHeight.roundToInt() + } else { + // if we do know our anchors, respect them + sheetState.offset.value.roundToInt() + } + IntOffset(0, y) + } + .bottomSheetSwipeable(sheetState, fullHeight, sheetHeightState) + .onGloballyPositioned { + sheetHeightState.value = it.size.height.toFloat() + } + .semantics { + if (sheetState.isVisible) { + dismiss { + if (sheetState.confirmStateChange(Hidden)) { + scope.launch { sheetState.hide() } + } + true + } + if (sheetState.currentValue == HalfExpanded) { + expand { + if (sheetState.confirmStateChange(Expanded)) { + scope.launch { sheetState.expand() } + } + true + } + } else if (sheetState.hasHalfExpandedState) { + collapse { + if (sheetState.confirmStateChange(HalfExpanded)) { + scope.launch { sheetState.halfExpand() } + } + true + } + } + } + }, + shape = sheetShape, + shadowElevation = sheetElevation, + color = sheetBackgroundColor, + contentColor = sheetContentColor + ) { + Column( + content = sheetContent + ) + } } } } @@ -465,6 +489,11 @@ private fun Scrim( * Contains useful Defaults for [ModalBottomSheetLayout]. */ object ModalBottomSheetDefaults { + val MaxCompactWidth = 600.dp + val MaxSheetWidth = 640.dp + val MinScrimHeight = 56.dp + val StartPadding = 56.dp + val EndPadding = 56.dp /** * The default elevation used by [ModalBottomSheetLayout]. diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt new file mode 100644 index 0000000000000..c4d96ccf51f8a --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.credentialmanager.common.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import com.android.credentialmanager.common.material.ModalBottomSheetLayout +import com.android.credentialmanager.common.material.ModalBottomSheetValue +import com.android.credentialmanager.common.material.rememberModalBottomSheetState +import com.android.credentialmanager.ui.theme.EntryShape + +/** Draws a modal bottom sheet with the same styles and effects shared by various flows. */ +@Composable +fun ModalBottomSheet( + sheetContent: @Composable ColumnScope.() -> Unit, + onDismiss: () -> Unit +) { + val state = rememberModalBottomSheetState( + initialValue = ModalBottomSheetValue.Expanded, + skipHalfExpanded = true + ) + ModalBottomSheetLayout( + sheetBackgroundColor = MaterialTheme.colorScheme.surface, + modifier = Modifier.background(Color.Transparent), + sheetState = state, + sheetContent = sheetContent, + scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = 0.8f), + sheetShape = EntryShape.TopRoundedCorner, + ) {} + LaunchedEffect(state.currentValue) { + if (state.currentValue == ModalBottomSheetValue.Hidden) { + onDismiss() + } + } +} \ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt index aaabce36c0036..85e5c1ee69d5b 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt @@ -18,9 +18,9 @@ package com.android.credentialmanager.common.ui import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -30,7 +30,6 @@ import androidx.compose.ui.graphics.Shape * By default the card is filled with surfaceVariant color. This container card instead fills the * background color with surface corlor. */ -@OptIn(ExperimentalMaterial3Api::class) @Composable fun ContainerCard( modifier: Modifier = Modifier, @@ -39,7 +38,7 @@ fun ContainerCard( content: @Composable ColumnScope.() -> Unit, ) { Card( - modifier = modifier, + modifier = modifier.fillMaxWidth(), shape = shape, border = border, colors = CardDefaults.cardColors( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index 0b9e57821d12c..216428c3c10c3 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -45,18 +45,15 @@ import androidx.core.graphics.drawable.toBitmap import com.android.credentialmanager.R import com.android.credentialmanager.common.CredentialType import com.android.credentialmanager.common.ProviderActivityState -import com.android.credentialmanager.common.material.ModalBottomSheetLayout -import com.android.credentialmanager.common.material.ModalBottomSheetValue -import com.android.credentialmanager.common.material.rememberModalBottomSheetState import com.android.credentialmanager.common.ui.ActionButton import com.android.credentialmanager.common.ui.ConfirmButton import com.android.credentialmanager.common.ui.Entry +import com.android.credentialmanager.common.ui.ModalBottomSheet import com.android.credentialmanager.common.ui.TextOnSurface import com.android.credentialmanager.common.ui.TextSecondary import com.android.credentialmanager.common.ui.TextOnSurfaceVariant import com.android.credentialmanager.common.ui.ContainerCard import com.android.credentialmanager.common.ui.ToggleVisibilityButton -import com.android.credentialmanager.ui.theme.EntryShape import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme @OptIn(ExperimentalMaterial3Api::class) @@ -65,13 +62,7 @@ fun CreateCredentialScreen( viewModel: CreateCredentialViewModel, providerActivityLauncher: ManagedActivityResultLauncher ) { - val state = rememberModalBottomSheetState( - initialValue = ModalBottomSheetValue.Expanded, - skipHalfExpanded = true - ) - ModalBottomSheetLayout( - sheetBackgroundColor = MaterialTheme.colorScheme.surface, - sheetState = state, + ModalBottomSheet( sheetContent = { val uiState = viewModel.uiState // Hide the sheet content as opposed to the whole bottom sheet to maintain the scrim @@ -153,14 +144,8 @@ fun CreateCredentialScreen( } } }, - scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = 0.8f), - sheetShape = EntryShape.TopRoundedCorner, - ) {} - LaunchedEffect(state.currentValue) { - if (state.currentValue == ModalBottomSheetValue.Hidden) { - viewModel.onCancel() - } - } + onDismiss = viewModel::onCancel + ) } @OptIn(ExperimentalMaterial3Api::class) diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt index a5e19b69e4704..59d2f4dd89369 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt @@ -22,7 +22,6 @@ import androidx.activity.result.ActivityResult import androidx.activity.result.IntentSenderRequest import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -60,12 +59,10 @@ import androidx.core.graphics.drawable.toBitmap import com.android.credentialmanager.R import com.android.credentialmanager.common.CredentialType import com.android.credentialmanager.common.ProviderActivityState -import com.android.credentialmanager.common.material.ModalBottomSheetLayout -import com.android.credentialmanager.common.material.ModalBottomSheetValue -import com.android.credentialmanager.common.material.rememberModalBottomSheetState import com.android.credentialmanager.common.ui.ActionButton import com.android.credentialmanager.common.ui.ConfirmButton import com.android.credentialmanager.common.ui.Entry +import com.android.credentialmanager.common.ui.ModalBottomSheet import com.android.credentialmanager.common.ui.TextOnSurface import com.android.credentialmanager.common.ui.TextSecondary import com.android.credentialmanager.common.ui.TextOnSurfaceVariant @@ -79,16 +76,9 @@ fun GetCredentialScreen( viewModel: GetCredentialViewModel, providerActivityLauncher: ManagedActivityResultLauncher ) { - val state = rememberModalBottomSheetState( - initialValue = ModalBottomSheetValue.Expanded, - skipHalfExpanded = true - ) val uiState = viewModel.uiState if (uiState.currentScreenState != GetScreenState.REMOTE_ONLY) { - ModalBottomSheetLayout( - sheetBackgroundColor = MaterialTheme.colorScheme.surface, - modifier = Modifier.background(Color.Transparent), - sheetState = state, + ModalBottomSheet( sheetContent = { // Hide the sheet content as opposed to the whole bottom sheet to maintain the scrim // background color even when the content should be hidden while waiting for @@ -128,14 +118,8 @@ fun GetCredentialScreen( } } }, - scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = 0.8f), - sheetShape = EntryShape.TopRoundedCorner, - ) {} - LaunchedEffect(state.currentValue) { - if (state.currentValue == ModalBottomSheetValue.Hidden) { - viewModel.onCancel() - } - } + onDismiss = viewModel::onCancel, + ) } else { SnackBarScreen( onClick = viewModel::onMoreOptionOnSnackBarSelected,