mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 06:17:57 +01:00 
			
		
		
		
	This reverts commit c12bdbae8e.
			
			
This commit is contained in:
		| @@ -1,9 +1,9 @@ | ||||
| package eu.kanade.presentation.components | ||||
|  | ||||
| import androidx.compose.animation.SizeTransform | ||||
| import androidx.compose.animation.core.tween | ||||
| import androidx.compose.animation.fadeIn | ||||
| import androidx.compose.animation.fadeOut | ||||
| import androidx.compose.animation.togetherWith | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.window.Dialog | ||||
| @@ -32,9 +32,10 @@ fun NavigatorAdaptiveSheet( | ||||
|             ) { | ||||
|                 ScreenTransition( | ||||
|                     navigator = sheetNavigator, | ||||
|                     enterTransition = { fadeIn(animationSpec = tween(220, delayMillis = 90)) }, | ||||
|                     exitTransition = { fadeOut(animationSpec = tween(90)) }, | ||||
|                     sizeTransform = { SizeTransform() }, | ||||
|                     transition = { | ||||
|                         fadeIn(animationSpec = tween(220, delayMillis = 90)) togetherWith | ||||
|                             fadeOut(animationSpec = tween(90)) | ||||
|                     }, | ||||
|                 ) | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -2,9 +2,6 @@ package eu.kanade.presentation.manga.components | ||||
|  | ||||
| import android.graphics.Bitmap | ||||
| import android.graphics.drawable.BitmapDrawable | ||||
| import androidx.activity.compose.PredictiveBackHandler | ||||
| import androidx.compose.animation.core.animate | ||||
| import androidx.compose.animation.core.tween | ||||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.layout.Row | ||||
| @@ -27,18 +24,15 @@ import androidx.compose.material3.SnackbarHostState | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.mutableFloatStateOf | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.draw.clip | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.graphics.graphicsLayer | ||||
| import androidx.compose.ui.platform.LocalDensity | ||||
| import androidx.compose.ui.unit.DpOffset | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.util.lerp | ||||
| import androidx.compose.ui.viewinterop.AndroidView | ||||
| import androidx.compose.ui.window.Dialog | ||||
| import androidx.compose.ui.window.DialogProperties | ||||
| @@ -55,14 +49,11 @@ import eu.kanade.presentation.components.DropdownMenu | ||||
| import eu.kanade.presentation.manga.EditCoverAction | ||||
| import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView | ||||
| import kotlinx.collections.immutable.persistentListOf | ||||
| import soup.compose.material.motion.MotionConstants | ||||
| import tachiyomi.domain.manga.model.Manga | ||||
| import tachiyomi.i18n.MR | ||||
| import tachiyomi.presentation.core.components.material.Scaffold | ||||
| import tachiyomi.presentation.core.i18n.stringResource | ||||
| import tachiyomi.presentation.core.util.PredictiveBack | ||||
| import tachiyomi.presentation.core.util.clickableNoIndication | ||||
| import kotlin.coroutines.cancellation.CancellationException | ||||
|  | ||||
| @Composable | ||||
| fun MangaCoverDialog( | ||||
| @@ -161,32 +152,10 @@ fun MangaCoverDialog( | ||||
|             val statusBarPaddingPx = with(LocalDensity.current) { contentPadding.calculateTopPadding().roundToPx() } | ||||
|             val bottomPaddingPx = with(LocalDensity.current) { contentPadding.calculateBottomPadding().roundToPx() } | ||||
|  | ||||
|             var scale by remember { mutableFloatStateOf(1f) } | ||||
|             PredictiveBackHandler { progress -> | ||||
|                 try { | ||||
|                     progress.collect { backEvent -> | ||||
|                         scale = lerp(1f, 0.8f, PredictiveBack.transform(backEvent.progress)) | ||||
|                     } | ||||
|                     onDismissRequest() | ||||
|                 } catch (e: CancellationException) { | ||||
|                     animate( | ||||
|                         initialValue = scale, | ||||
|                         targetValue = 1f, | ||||
|                         animationSpec = tween(durationMillis = MotionConstants.DefaultMotionDuration), | ||||
|                     ) { value, _ -> | ||||
|                         scale = value | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Box( | ||||
|                 modifier = Modifier | ||||
|                     .fillMaxSize() | ||||
|                     .clickableNoIndication(onClick = onDismissRequest) | ||||
|                     .graphicsLayer { | ||||
|                         scaleX = scale | ||||
|                         scaleY = scale | ||||
|                     }, | ||||
|                     .clickableNoIndication(onClick = onDismissRequest), | ||||
|             ) { | ||||
|                 AndroidView( | ||||
|                     factory = { | ||||
|   | ||||
| @@ -1,46 +1,13 @@ | ||||
| package eu.kanade.presentation.util | ||||
|  | ||||
| import android.annotation.SuppressLint | ||||
| import androidx.activity.BackEventCompat | ||||
| import androidx.activity.compose.PredictiveBackHandler | ||||
| import androidx.activity.compose.BackHandler | ||||
| import androidx.compose.animation.AnimatedContent | ||||
| import androidx.compose.animation.AnimatedContentTransitionScope | ||||
| import androidx.compose.animation.ContentTransform | ||||
| import androidx.compose.animation.EnterTransition | ||||
| import androidx.compose.animation.ExitTransition | ||||
| import androidx.compose.animation.SizeTransform | ||||
| import androidx.compose.animation.core.AnimationSpec | ||||
| import androidx.compose.animation.core.SeekableTransitionState | ||||
| import androidx.compose.animation.core.Spring | ||||
| import androidx.compose.animation.core.animate | ||||
| import androidx.compose.animation.core.rememberTransition | ||||
| import androidx.compose.animation.core.spring | ||||
| import androidx.compose.animation.fadeIn | ||||
| import androidx.compose.animation.fadeOut | ||||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.DisposableEffect | ||||
| import androidx.compose.runtime.LaunchedEffect | ||||
| import androidx.compose.runtime.MutableState | ||||
| import androidx.compose.runtime.ProvidableCompositionLocal | ||||
| import androidx.compose.runtime.Stable | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.mutableFloatStateOf | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| 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.runtime.setValue | ||||
| import androidx.compose.runtime.snapshotFlow | ||||
| import androidx.compose.runtime.staticCompositionLocalOf | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.geometry.Offset | ||||
| import androidx.compose.ui.platform.LocalView | ||||
| import androidx.compose.ui.platform.LocalViewConfiguration | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.util.lerp | ||||
| import cafe.adriel.voyager.core.annotation.InternalVoyagerApi | ||||
| import cafe.adriel.voyager.core.model.ScreenModel | ||||
| import cafe.adriel.voyager.core.model.ScreenModelStore | ||||
| import cafe.adriel.voyager.core.screen.Screen | ||||
| @@ -49,28 +16,18 @@ import cafe.adriel.voyager.core.screen.uniqueScreenKey | ||||
| import cafe.adriel.voyager.core.stack.StackEvent | ||||
| import cafe.adriel.voyager.navigator.Navigator | ||||
| import cafe.adriel.voyager.transitions.ScreenTransitionContent | ||||
| import eu.kanade.tachiyomi.util.view.getWindowRadius | ||||
| import kotlinx.coroutines.CoroutineName | ||||
| import kotlinx.coroutines.CoroutineScope | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.Job | ||||
| import kotlinx.coroutines.SupervisorJob | ||||
| import kotlinx.coroutines.cancel | ||||
| import kotlinx.coroutines.flow.dropWhile | ||||
| import kotlinx.coroutines.flow.onCompletion | ||||
| import kotlinx.coroutines.launch | ||||
| import kotlinx.coroutines.plus | ||||
| import soup.compose.material.motion.animation.materialSharedAxisXIn | ||||
| import soup.compose.material.motion.animation.materialSharedAxisXOut | ||||
| import soup.compose.material.motion.animation.materialSharedAxisX | ||||
| import soup.compose.material.motion.animation.rememberSlideDistance | ||||
| import tachiyomi.presentation.core.util.PredictiveBack | ||||
| import kotlin.coroutines.cancellation.CancellationException | ||||
| import kotlin.math.absoluteValue | ||||
|  | ||||
| /** | ||||
|  * For invoking back press to the parent activity | ||||
|  */ | ||||
| @SuppressLint("ComposeCompositionLocalUsage") | ||||
| val LocalBackPress: ProvidableCompositionLocal<(() -> Unit)?> = staticCompositionLocalOf { null } | ||||
|  | ||||
| interface Tab : cafe.adriel.voyager.navigator.tab.Tab { | ||||
| @@ -98,278 +55,41 @@ interface AssistContentScreen { | ||||
|     fun onProvideAssistUrl(): String? | ||||
| } | ||||
|  | ||||
| @OptIn(InternalVoyagerApi::class) | ||||
| @Composable | ||||
| fun DefaultNavigatorScreenTransition( | ||||
|     navigator: Navigator, | ||||
|     modifier: Modifier = Modifier, | ||||
| ) { | ||||
|     val screenCandidatesToDispose = rememberSaveable(saver = screenCandidatesToDisposeSaver()) { | ||||
|         mutableStateOf(emptySet()) | ||||
|     } | ||||
|     val currentScreens = navigator.items | ||||
|     DisposableEffect(currentScreens) { | ||||
|         onDispose { | ||||
|             val newScreenKeys = navigator.items.map { it.key } | ||||
|             screenCandidatesToDispose.value += currentScreens.filter { it.key !in newScreenKeys } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     val slideDistance = rememberSlideDistance(slideDistance = 30.dp) | ||||
|     val slideDistance = rememberSlideDistance() | ||||
|     ScreenTransition( | ||||
|         navigator = navigator, | ||||
|         transition = { | ||||
|             materialSharedAxisX( | ||||
|                 forward = navigator.lastEvent != StackEvent.Pop, | ||||
|                 slideDistance = slideDistance, | ||||
|             ) | ||||
|         }, | ||||
|         modifier = modifier, | ||||
|         enterTransition = { | ||||
|             if (it == SwipeEdge.Right) { | ||||
|                 materialSharedAxisXIn(forward = false, slideDistance = slideDistance) | ||||
|             } else { | ||||
|                 materialSharedAxisXIn(forward = true, slideDistance = slideDistance) | ||||
|             } | ||||
|         }, | ||||
|         exitTransition = { | ||||
|             if (it == SwipeEdge.Right) { | ||||
|                 materialSharedAxisXOut(forward = false, slideDistance = slideDistance) | ||||
|             } else { | ||||
|                 materialSharedAxisXOut(forward = true, slideDistance = slideDistance) | ||||
|             } | ||||
|         }, | ||||
|         popEnterTransition = { | ||||
|             if (it == SwipeEdge.Right) { | ||||
|                 materialSharedAxisXIn(forward = true, slideDistance = slideDistance) | ||||
|             } else { | ||||
|                 materialSharedAxisXIn(forward = false, slideDistance = slideDistance) | ||||
|             } | ||||
|         }, | ||||
|         popExitTransition = { | ||||
|             if (it == SwipeEdge.Right) { | ||||
|                 materialSharedAxisXOut(forward = true, slideDistance = slideDistance) | ||||
|             } else { | ||||
|                 materialSharedAxisXOut(forward = false, slideDistance = slideDistance) | ||||
|             } | ||||
|         }, | ||||
|         content = { screen -> | ||||
|             if (this.transition.targetState == this.transition.currentState) { | ||||
|                 LaunchedEffect(Unit) { | ||||
|                     val newScreens = navigator.items.map { it.key } | ||||
|                     val screensToDispose = screenCandidatesToDispose.value.filterNot { it.key in newScreens } | ||||
|                     if (screensToDispose.isNotEmpty()) { | ||||
|                         screensToDispose.forEach { navigator.dispose(it) } | ||||
|                         navigator.clearEvent() | ||||
|                     } | ||||
|                     screenCandidatesToDispose.value = emptySet() | ||||
|                 } | ||||
|             } | ||||
|             screen.Content() | ||||
|         }, | ||||
|     ) | ||||
| } | ||||
|  | ||||
| enum class SwipeEdge { | ||||
|     Unknown, | ||||
|     Left, | ||||
|     Right, | ||||
| } | ||||
|  | ||||
| private enum class AnimationType { | ||||
|     Pop, | ||||
|     Cancel, | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| fun ScreenTransition( | ||||
|     navigator: Navigator, | ||||
|     transition: AnimatedContentTransitionScope<Screen>.() -> ContentTransform, | ||||
|     modifier: Modifier = Modifier, | ||||
|     enterTransition: AnimatedContentTransitionScope<Screen>.(SwipeEdge) -> EnterTransition = { fadeIn() }, | ||||
|     exitTransition: AnimatedContentTransitionScope<Screen>.(SwipeEdge) -> ExitTransition = { fadeOut() }, | ||||
|     popEnterTransition: AnimatedContentTransitionScope<Screen>.(SwipeEdge) -> EnterTransition = enterTransition, | ||||
|     popExitTransition: AnimatedContentTransitionScope<Screen>.(SwipeEdge) -> ExitTransition = exitTransition, | ||||
|     sizeTransform: (AnimatedContentTransitionScope<Screen>.() -> SizeTransform?)? = null, | ||||
|     flingAnimationSpec: () -> AnimationSpec<Float> = { spring(stiffness = Spring.StiffnessLow) }, | ||||
|     content: ScreenTransitionContent = { it.Content() }, | ||||
| ) { | ||||
|     val view = LocalView.current | ||||
|     val viewConfig = LocalViewConfiguration.current | ||||
|     val scope = rememberCoroutineScope() | ||||
|     val state = remember { | ||||
|         ScreenTransitionState( | ||||
|             navigator = navigator, | ||||
|             scope = scope, | ||||
|             flingAnimationSpec = flingAnimationSpec(), | ||||
|             windowCornerRadius = view.getWindowRadius().toFloat(), | ||||
|         ) | ||||
|     } | ||||
|     val transitionState = remember { SeekableTransitionState(navigator.lastItem) } | ||||
|     val transition = rememberTransition(transitionState = transitionState) | ||||
|  | ||||
|     if (state.isPredictiveBack || state.isAnimating) { | ||||
|         LaunchedEffect(state.progress) { | ||||
|             if (!state.isPredictiveBack) return@LaunchedEffect | ||||
|             val previousEntry = navigator.items.getOrNull(navigator.size - 2) | ||||
|             if (previousEntry != null) { | ||||
|                 transitionState.seekTo(fraction = state.progress, targetState = previousEntry) | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         LaunchedEffect(navigator) { | ||||
|             snapshotFlow { navigator.lastItem } | ||||
|                 .collect { | ||||
|                     state.cancelCancelAnimation() | ||||
|                     if (it != transitionState.currentState) { | ||||
|                         transitionState.animateTo(it) | ||||
|                     } else { | ||||
|                         transitionState.snapTo(it) | ||||
|                     } | ||||
|                 } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     PredictiveBackHandler(enabled = navigator.canPop) { backEvent -> | ||||
|         state.cancelCancelAnimation() | ||||
|         var startOffset: Offset? = null | ||||
|         backEvent | ||||
|             .dropWhile { | ||||
|                 if (startOffset == null) startOffset = Offset(it.touchX, it.touchY) | ||||
|                 if (state.isAnimating) return@dropWhile true | ||||
|                 // Touch slop check | ||||
|                 val diff = Offset(it.touchX, it.touchY) - startOffset!! | ||||
|                 diff.x.absoluteValue < viewConfig.touchSlop && diff.y.absoluteValue < viewConfig.touchSlop | ||||
|             } | ||||
|             .onCompletion { | ||||
|                 if (it == null) { | ||||
|                     state.finish() | ||||
|                 } else { | ||||
|                     state.cancel() | ||||
|                 } | ||||
|             } | ||||
|             .collect { | ||||
|                 state.setPredictiveBackProgress( | ||||
|                     progress = it.progress, | ||||
|                     swipeEdge = when (it.swipeEdge) { | ||||
|                         BackEventCompat.EDGE_LEFT -> SwipeEdge.Left | ||||
|                         BackEventCompat.EDGE_RIGHT -> SwipeEdge.Right | ||||
|                         else -> SwipeEdge.Unknown | ||||
|                     }, | ||||
|                 ) | ||||
|             } | ||||
|     } | ||||
|  | ||||
|     transition.AnimatedContent( | ||||
|     AnimatedContent( | ||||
|         targetState = navigator.lastItem, | ||||
|         transitionSpec = transition, | ||||
|         modifier = modifier, | ||||
|         transitionSpec = { | ||||
|             val pop = navigator.lastEvent == StackEvent.Pop || state.isPredictiveBack | ||||
|             ContentTransform( | ||||
|                 targetContentEnter = if (pop) { | ||||
|                     popEnterTransition(state.swipeEdge) | ||||
|                 } else { | ||||
|                     enterTransition(state.swipeEdge) | ||||
|                 }, | ||||
|                 initialContentExit = if (pop) { | ||||
|                     popExitTransition(state.swipeEdge) | ||||
|                 } else { | ||||
|                     exitTransition(state.swipeEdge) | ||||
|                 }, | ||||
|                 targetContentZIndex = if (pop) 0f else 1f, | ||||
|                 sizeTransform = sizeTransform?.invoke(this), | ||||
|             ) | ||||
|         }, | ||||
|         contentKey = { it.key }, | ||||
|     ) { | ||||
|         navigator.saveableState("transition", it) { | ||||
|             content(it) | ||||
|         label = "transition", | ||||
|     ) { screen -> | ||||
|         navigator.saveableState("transition", screen) { | ||||
|             content(screen) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Stable | ||||
| private class ScreenTransitionState( | ||||
|     private val navigator: Navigator, | ||||
|     private val scope: CoroutineScope, | ||||
|     private val flingAnimationSpec: AnimationSpec<Float>, | ||||
|     windowCornerRadius: Float, | ||||
| ) { | ||||
|     var isPredictiveBack: Boolean by mutableStateOf(false) | ||||
|         private set | ||||
|  | ||||
|     var progress: Float by mutableFloatStateOf(0f) | ||||
|         private set | ||||
|  | ||||
|     var swipeEdge: SwipeEdge by mutableStateOf(SwipeEdge.Unknown) | ||||
|         private set | ||||
|  | ||||
|     private var animationJob: Pair<Job, AnimationType>? by mutableStateOf(null) | ||||
|  | ||||
|     val isAnimating: Boolean | ||||
|         get() = animationJob?.first?.isActive == true | ||||
|  | ||||
|     val windowCornerShape = RoundedCornerShape(windowCornerRadius) | ||||
|  | ||||
|     private fun reset() { | ||||
|         this.isPredictiveBack = false | ||||
|         this.swipeEdge = SwipeEdge.Unknown | ||||
|         this.animationJob = null | ||||
|     } | ||||
|  | ||||
|     fun setPredictiveBackProgress(progress: Float, swipeEdge: SwipeEdge) { | ||||
|         this.progress = lerp(0f, 0.65f, PredictiveBack.transform(progress)) | ||||
|         this.swipeEdge = swipeEdge | ||||
|         this.isPredictiveBack = true | ||||
|     } | ||||
|  | ||||
|     fun finish() { | ||||
|         if (!isPredictiveBack) { | ||||
|             navigator.pop() | ||||
|             return | ||||
|         } | ||||
|         animationJob = scope.launch { | ||||
|             try { | ||||
|                 animate( | ||||
|                     initialValue = progress, | ||||
|                     targetValue = 1f, | ||||
|                     animationSpec = flingAnimationSpec, | ||||
|                     block = { i, _ -> progress = i }, | ||||
|                 ) | ||||
|                 navigator.pop() | ||||
|             } catch (e: CancellationException) { | ||||
|                 // Cancelled | ||||
|                 progress = 0f | ||||
|             } finally { | ||||
|                 reset() | ||||
|             } | ||||
|         } to AnimationType.Pop | ||||
|     } | ||||
|  | ||||
|     fun cancel() { | ||||
|         if (!isPredictiveBack) { | ||||
|             return | ||||
|         } | ||||
|         animationJob = scope.launch { | ||||
|             try { | ||||
|                 animate( | ||||
|                     initialValue = progress, | ||||
|                     targetValue = 0f, | ||||
|                     animationSpec = flingAnimationSpec, | ||||
|                     block = { i, _ -> progress = i }, | ||||
|                 ) | ||||
|             } catch (e: CancellationException) { | ||||
|                 // Cancelled | ||||
|                 progress = 1f | ||||
|             } finally { | ||||
|                 reset() | ||||
|             } | ||||
|         } to AnimationType.Cancel | ||||
|     } | ||||
|  | ||||
|     fun cancelCancelAnimation() { | ||||
|         if (animationJob?.second == AnimationType.Cancel) { | ||||
|             animationJob?.first?.cancel() | ||||
|             animationJob = null | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| private fun screenCandidatesToDisposeSaver(): Saver<MutableState<Set<Screen>>, List<Screen>> { | ||||
|     return Saver( | ||||
|         save = { it.value.toList() }, | ||||
|         restore = { mutableStateOf(it.toSet()) }, | ||||
|     ) | ||||
|  | ||||
|     BackHandler(enabled = navigator.canPop, onBack = navigator::pop) | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,8 @@ | ||||
| package eu.kanade.tachiyomi.ui.home | ||||
|  | ||||
| import androidx.activity.compose.PredictiveBackHandler | ||||
| import androidx.activity.compose.BackHandler | ||||
| import androidx.compose.animation.AnimatedContent | ||||
| import androidx.compose.animation.AnimatedVisibility | ||||
| import androidx.compose.animation.core.animate | ||||
| import androidx.compose.animation.core.tween | ||||
| import androidx.compose.animation.expandVertically | ||||
| import androidx.compose.animation.shrinkVertically | ||||
| import androidx.compose.animation.togetherWith | ||||
| @@ -24,19 +22,13 @@ import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.CompositionLocalProvider | ||||
| import androidx.compose.runtime.LaunchedEffect | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.mutableFloatStateOf | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.produceState | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.rememberCoroutineScope | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.graphics.graphicsLayer | ||||
| import androidx.compose.ui.semantics.contentDescription | ||||
| import androidx.compose.ui.semantics.semantics | ||||
| import androidx.compose.ui.text.style.TextOverflow | ||||
| import androidx.compose.ui.util.fastForEach | ||||
| import androidx.compose.ui.util.lerp | ||||
| import cafe.adriel.voyager.navigator.LocalNavigator | ||||
| import cafe.adriel.voyager.navigator.currentOrThrow | ||||
| import cafe.adriel.voyager.navigator.tab.LocalTabNavigator | ||||
| @@ -56,7 +48,6 @@ import kotlinx.coroutines.flow.collectLatest | ||||
| import kotlinx.coroutines.flow.combine | ||||
| import kotlinx.coroutines.flow.receiveAsFlow | ||||
| import kotlinx.coroutines.launch | ||||
| import soup.compose.material.motion.MotionConstants | ||||
| import soup.compose.material.motion.animation.materialFadeThroughIn | ||||
| import soup.compose.material.motion.animation.materialFadeThroughOut | ||||
| import tachiyomi.domain.library.service.LibraryPreferences | ||||
| @@ -65,10 +56,8 @@ import tachiyomi.presentation.core.components.material.NavigationBar | ||||
| import tachiyomi.presentation.core.components.material.NavigationRail | ||||
| import tachiyomi.presentation.core.components.material.Scaffold | ||||
| import tachiyomi.presentation.core.i18n.pluralStringResource | ||||
| import tachiyomi.presentation.core.util.PredictiveBack | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| import kotlin.coroutines.cancellation.CancellationException | ||||
|  | ||||
| object HomeScreen : Screen() { | ||||
|  | ||||
| @@ -93,8 +82,6 @@ object HomeScreen : Screen() { | ||||
|     @Composable | ||||
|     override fun Content() { | ||||
|         val navigator = LocalNavigator.currentOrThrow | ||||
|         var scale by remember { mutableFloatStateOf(1f) } | ||||
|  | ||||
|         TabNavigator( | ||||
|             tab = LibraryTab, | ||||
|             key = TabNavigatorKey, | ||||
| @@ -134,11 +121,7 @@ object HomeScreen : Screen() { | ||||
|                     Box( | ||||
|                         modifier = Modifier | ||||
|                             .padding(contentPadding) | ||||
|                             .consumeWindowInsets(contentPadding) | ||||
|                             .graphicsLayer { | ||||
|                                 scaleX = scale | ||||
|                                 scaleY = scale | ||||
|                             }, | ||||
|                             .consumeWindowInsets(contentPadding), | ||||
|                     ) { | ||||
|                         AnimatedContent( | ||||
|                             targetState = tabNavigator.current, | ||||
| @@ -158,31 +141,7 @@ object HomeScreen : Screen() { | ||||
|  | ||||
|             val goToLibraryTab = { tabNavigator.current = LibraryTab } | ||||
|  | ||||
|             var handlingBack by remember { mutableStateOf(false) } | ||||
|             PredictiveBackHandler( | ||||
|                 enabled = handlingBack || tabNavigator.current::class != LibraryTab::class, | ||||
|             ) { progress -> | ||||
|                 handlingBack = true | ||||
|                 val currentTab = tabNavigator.current | ||||
|                 try { | ||||
|                     progress.collect { backEvent -> | ||||
|                         scale = lerp(1f, 0.92f, PredictiveBack.transform(backEvent.progress)) | ||||
|                         tabNavigator.current = if (backEvent.progress > 0.25f) TABS[0] else currentTab | ||||
|                     } | ||||
|                     goToLibraryTab() | ||||
|                 } catch (e: CancellationException) { | ||||
|                     tabNavigator.current = currentTab | ||||
|                 } finally { | ||||
|                     animate( | ||||
|                         initialValue = scale, | ||||
|                         targetValue = 1f, | ||||
|                         animationSpec = tween(durationMillis = MotionConstants.DefaultMotionDuration), | ||||
|                     ) { value, _ -> | ||||
|                         scale = value | ||||
|                     } | ||||
|                     handlingBack = false | ||||
|                 } | ||||
|             } | ||||
|             BackHandler(enabled = tabNavigator.current != LibraryTab, onBack = goToLibraryTab) | ||||
|  | ||||
|             LaunchedEffect(Unit) { | ||||
|                 launch { | ||||
|   | ||||
| @@ -4,11 +4,9 @@ package eu.kanade.tachiyomi.util.view | ||||
|  | ||||
| import android.content.res.Resources | ||||
| import android.graphics.Rect | ||||
| import android.os.Build | ||||
| import android.view.Gravity | ||||
| import android.view.Menu | ||||
| import android.view.MenuItem | ||||
| import android.view.RoundedCorner | ||||
| import android.view.View | ||||
| import androidx.activity.ComponentActivity | ||||
| import androidx.activity.compose.setContent | ||||
| @@ -97,22 +95,3 @@ fun View?.isVisibleOnScreen(): Boolean { | ||||
|         Rect(0, 0, Resources.getSystem().displayMetrics.widthPixels, Resources.getSystem().displayMetrics.heightPixels) | ||||
|     return actualPosition.intersect(screen) | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Returns window radius (in pixel) applied to this view | ||||
|  */ | ||||
| fun View.getWindowRadius(): Int { | ||||
|     val rad = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { | ||||
|         val windowInsets = rootWindowInsets | ||||
|         listOfNotNull( | ||||
|             windowInsets.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT), | ||||
|             windowInsets.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT), | ||||
|             windowInsets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT), | ||||
|             windowInsets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT), | ||||
|         ) | ||||
|             .minOfOrNull { it.radius } | ||||
|     } else { | ||||
|         null | ||||
|     } | ||||
|     return rad ?: 0 | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user