Handle different window sizes.

Bug: 265887886
Test: manual

Change-Id: I2e4c33a0a601859ab7871ab449d1b24f3cb43609
This commit is contained in:
Helen Qin
2023-02-06 05:11:24 +00:00
parent bffece6618
commit 5625239b65
6 changed files with 147 additions and 96 deletions

View File

@@ -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 ->

View File

@@ -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].

View File

@@ -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()
}
}
}

View File

@@ -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(

View File

@@ -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<IntentSenderRequest, ActivityResult>
) {
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)

View File

@@ -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<IntentSenderRequest, ActivityResult>
) {
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,