diff --git a/app/src/main/kotlin/androidx/compose/material3/Snackbar2.kt b/app/src/main/kotlin/androidx/compose/material3/Snackbar2.kt new file mode 100644 index 000000000..7fd2d4f3d --- /dev/null +++ b/app/src/main/kotlin/androidx/compose/material3/Snackbar2.kt @@ -0,0 +1,132 @@ +/* + * Copyright 2021 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 androidx.compose.material3 + +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.dp + +/** + * Material Design snackbar. + * + * Snackbars provide brief messages about app processes at the bottom of the screen. + * + * ![Snackbar image](https://developer.android.com/images/reference/androidx/compose/material3/snackbar.png) + * + * Snackbars inform users of a process that an app has performed or will perform. They appear + * temporarily, towards the bottom of the screen. They shouldn’t interrupt the user experience, + * and they don’t require user input to disappear. + * + * A Snackbar can contain a single action. "Dismiss" or "cancel" actions are optional. + * + * Snackbars with an action should not timeout or self-dismiss until the user performs another + * action. Here, moving the keyboard focus indicator to navigate through interactive elements in a + * page is not considered an action. + * + * This version of snackbar is designed to work with [SnackbarData] provided by the + * [SnackbarHost], which is usually used inside of the [Scaffold]. + * + * This components provides only the visuals of the Snackbar. If you need to show a Snackbar + * with defaults on the screen, use [SnackbarHostState.showSnackbar]: + * + * @sample androidx.compose.material3.samples.ScaffoldWithSimpleSnackbar + * + * If you want to customize appearance of the Snackbar, you can pass your own version as a child + * of the [SnackbarHost] to the [Scaffold]: + * + * @sample androidx.compose.material3.samples.ScaffoldWithCustomSnackbar + * + * When a [SnackbarData.visuals] sets the Snackbar's duration as [SnackbarDuration.Indefinite], it's + * recommended to display an additional close affordance action. + * See [SnackbarVisuals.withDismissAction]: + * + * @sample androidx.compose.material3.samples.ScaffoldWithIndefiniteSnackbar + * + * @param snackbarData data about the current snackbar showing via [SnackbarHostState] + * @param modifier the [Modifier] to be applied to this snackbar + * @param actionOnNewLine whether or not action should be put on a separate line. Recommended for + * action with long action text. + * @param shape defines the shape of this snackbar's container + * @param containerColor the color used for the background of this snackbar. Use [Color.Transparent] + * to have no color. + * @param contentColor the preferred color for content inside this snackbar + * @param actionColor the color of the snackbar's action + * @param actionContentColor the preferred content color for the optional action inside this + * snackbar. See [SnackbarVisuals.actionLabel]. + * @param dismissActionContentColor the preferred content color for the optional dismiss action + * inside this snackbar. See [SnackbarVisuals.withDismissAction]. + */ +@Composable +fun Snackbar2( + snackbarData: SnackbarData, + modifier: Modifier = Modifier, + actionOnNewLine: Boolean = false, + shape: Shape = SnackbarDefaults.shape, + containerColor: Color = SnackbarDefaults.color, + contentColor: Color = SnackbarDefaults.contentColor, + actionColor: Color = SnackbarDefaults.actionColor, + actionContentColor: Color = SnackbarDefaults.actionContentColor, + dismissActionContentColor: Color = SnackbarDefaults.dismissActionContentColor, +) { + val actionLabel = snackbarData.visuals.actionLabel + val actionComposable: (@Composable () -> Unit)? = if (actionLabel != null) { + @Composable { + TextButton( + colors = ButtonDefaults.textButtonColors(contentColor = actionColor), + onClick = { snackbarData.performAction() }, + content = { Text(actionLabel) }, + ) + } + } else { + null + } + val dismissActionComposable: (@Composable () -> Unit)? = + if (snackbarData.visuals.withDismissAction) { + @Composable { + IconButton( + onClick = { snackbarData.dismiss() }, + content = { + Icon( + Icons.Filled.Close, + contentDescription = null, + // Workaround for regression of https://android-review.googlesource.com/c/platform/frameworks/support/+/2881006 + tint = SnackbarDefaults.dismissActionContentColor, + ) + }, + ) + } + } else { + null + } + Snackbar( + modifier = modifier.padding(12.dp), + action = actionComposable, + dismissAction = dismissActionComposable, + actionOnNewLine = actionOnNewLine, + shape = shape, + containerColor = containerColor, + contentColor = contentColor, + actionContentColor = actionContentColor, + dismissActionContentColor = dismissActionContentColor, + content = { Text(snackbarData.visuals.message) }, + ) +} diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt index e5d5b291d..f1e9811d1 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt @@ -59,6 +59,7 @@ import androidx.compose.material3.NavigationDrawerItem import androidx.compose.material3.Scaffold import androidx.compose.material3.ShapeDefaults import androidx.compose.material3.SideDrawer +import androidx.compose.material3.Snackbar2 import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult @@ -307,7 +308,7 @@ class MainActivity : EhActivity() { val snackMessage = stringResource(R.string.clipboard_gallery_url_snack_message) val snackAction = stringResource(R.string.clipboard_gallery_url_snack_action) LifecycleResumeEffect { - scope.launch { + val job = scope.launch { delay(300) val text = clipboardManager.getUrlFromClipboard(applicationContext) val hashCode = text?.hashCode() ?: 0 @@ -328,7 +329,7 @@ class MainActivity : EhActivity() { } Settings.clipboardTextHashCode = hashCode } - onPauseOrDispose { } + onPauseOrDispose { job.cancel() } } val currentDestination by navController.currentDestinationAsState() val lockDrawerHandle = remember { mutableStateListOf() } @@ -340,7 +341,7 @@ class MainActivity : EhActivity() { LocalDrawerLockHandle provides lockDrawerHandle, LocalSnackbarHostState provides snackbarState, ) { - Scaffold(snackbarHost = { SnackbarHost(snackbarState) }) { + Scaffold(snackbarHost = { SnackbarHost(snackbarState) { Snackbar2(it) } }) { LocalTouchSlopProvider(Settings.touchSlopFactor.toFloat()) { ModalNavigationDrawer( drawerContent = {