diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 209ff44e0e..90ecac8501 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -268,6 +268,7 @@ dependencies { implementation(libs.cascade) implementation(libs.bundles.voyager) implementation(libs.wheelpicker) + implementation(libs.materialmotion.core) // Logging implementation(libs.logcat) diff --git a/app/src/main/java/eu/kanade/presentation/util/Constants.kt b/app/src/main/java/eu/kanade/presentation/util/Constants.kt index c3b0243dcb..651ad08530 100644 --- a/app/src/main/java/eu/kanade/presentation/util/Constants.kt +++ b/app/src/main/java/eu/kanade/presentation/util/Constants.kt @@ -1,10 +1,5 @@ package eu.kanade.presentation.util -import androidx.compose.animation.ExitTransition -import androidx.compose.animation.core.LinearEasing -import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn -import androidx.compose.animation.with import androidx.compose.foundation.layout.PaddingValues import androidx.compose.material3.MaterialTheme import androidx.compose.ui.unit.dp @@ -29,15 +24,3 @@ class Padding { val MaterialTheme.padding: Padding get() = Padding() - -object Transition { - - /** - * Mimics [eu.kanade.tachiyomi.ui.base.controller.OneWayFadeChangeHandler] - */ - val OneWayFade = fadeIn( - animationSpec = tween( - easing = LinearEasing, - ), - ) with ExitTransition.None -} diff --git a/app/src/main/java/eu/kanade/presentation/util/Navigator.kt b/app/src/main/java/eu/kanade/presentation/util/Navigator.kt index 71a50c284c..30daf481a3 100644 --- a/app/src/main/java/eu/kanade/presentation/util/Navigator.kt +++ b/app/src/main/java/eu/kanade/presentation/util/Navigator.kt @@ -1,8 +1,13 @@ package eu.kanade.presentation.util +import androidx.compose.runtime.Composable import androidx.compose.runtime.ProvidableCompositionLocal import androidx.compose.runtime.staticCompositionLocalOf +import cafe.adriel.voyager.core.stack.StackEvent import cafe.adriel.voyager.navigator.Navigator +import cafe.adriel.voyager.transitions.ScreenTransition +import soup.compose.material.motion.animation.materialSharedAxisX +import soup.compose.material.motion.animation.rememberSlideDistance /** * For invoking back press to the parent activity @@ -12,3 +17,17 @@ val LocalBackPress: ProvidableCompositionLocal<(() -> Unit)?> = staticCompositio interface Tab : cafe.adriel.voyager.navigator.tab.Tab { suspend fun onReselect(navigator: Navigator) {} } + +@Composable +fun DefaultNavigatorScreenTransition(navigator: Navigator) { + val slideDistance = rememberSlideDistance() + ScreenTransition( + navigator = navigator, + transition = { + materialSharedAxisX( + forward = navigator.lastEvent != StackEvent.Pop, + slideDistance = slideDistance, + ) + }, + ) +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt index 023b18cab3..1724422133 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically +import androidx.compose.animation.with import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope @@ -41,7 +42,6 @@ import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.presentation.components.NavigationBar import eu.kanade.presentation.components.NavigationRail import eu.kanade.presentation.components.Scaffold -import eu.kanade.presentation.util.Transition import eu.kanade.presentation.util.isTabletUi import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.BrowseTab @@ -55,6 +55,8 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch +import soup.compose.material.motion.animation.materialFadeThroughIn +import soup.compose.material.motion.animation.materialFadeThroughOut import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -64,6 +66,8 @@ object HomeScreen : Screen { private val openTabEvent = Channel() private val showBottomNavEvent = Channel() + private const val TabFadeDuration = 200 + private val tabs = listOf( LibraryTab, UpdatesTab, @@ -116,7 +120,10 @@ object HomeScreen : Screen { ) { AnimatedContent( targetState = tabNavigator.current, - transitionSpec = { Transition.OneWayFade }, + transitionSpec = { + materialFadeThroughIn(initialScale = 1f, durationMillis = TabFadeDuration) with + materialFadeThroughOut(durationMillis = TabFadeDuration) + }, content = { tabNavigator.saveableState(key = "currentTab", it) { it.Content() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 772c66b2d8..fe21c80351 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -7,7 +7,6 @@ import android.graphics.Color import android.os.Build import android.os.Bundle import android.view.View -import android.view.Window import android.widget.Toast import androidx.activity.compose.BackHandler import androidx.compose.foundation.isSystemInDarkTheme @@ -42,16 +41,14 @@ import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior import cafe.adriel.voyager.navigator.currentOrThrow -import cafe.adriel.voyager.transitions.ScreenTransition import com.google.accompanist.systemuicontroller.rememberSystemUiController -import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.category.model.Category import eu.kanade.domain.library.service.LibraryPreferences import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.components.AppStateBanners -import eu.kanade.presentation.util.Transition +import eu.kanade.presentation.util.DefaultNavigatorScreenTransition import eu.kanade.presentation.util.collectAsState import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.Migrations @@ -118,11 +115,6 @@ class MainActivity : BaseActivity() { // Prevent splash screen showing up on configuration changes val splashScreen = if (savedInstanceState == null) installSplashScreen() else null - // Set up shared element transition and disable overlay so views don't show above system bars - window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) - setExitSharedElementCallback(MaterialContainerTransformSharedElementCallback()) - window.sharedElementsUseOverlay = false - super.onCreate(savedInstanceState) val didMigration = if (savedInstanceState == null) { @@ -196,7 +188,7 @@ class MainActivity : BaseActivity() { } Box(modifier = boxModifier) { // Shows current screen - ScreenTransition(navigator = navigator, transition = { Transition.OneWayFade }) + DefaultNavigatorScreenTransition(navigator = navigator) } // Pop source-related screens when incognito mode is turned off diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 3343e42af9..e331a69a41 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -21,9 +21,7 @@ import android.view.KeyEvent import android.view.Menu import android.view.MenuItem import android.view.MotionEvent -import android.view.View import android.view.View.LAYER_TYPE_HARDWARE -import android.view.Window import android.view.WindowManager import android.view.animation.Animation import android.view.animation.AnimationUtils @@ -43,11 +41,9 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.slider.Slider import com.google.android.material.transition.platform.MaterialContainerTransform -import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback import dev.chrisbanes.insetter.applyInsetter import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.manga.model.Manga -import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications @@ -115,9 +111,6 @@ class ReaderActivity : BaseActivity() { private const val ENABLED_BUTTON_IMAGE_ALPHA = 255 private const val DISABLED_BUTTON_IMAGE_ALPHA = 64 - - const val EXTRA_IS_TRANSITION = "${BuildConfig.APPLICATION_ID}.READER_IS_TRANSITION" - const val SHARED_ELEMENT_NAME = "reader_shared_element_root" } private val readerPreferences: ReaderPreferences by injectLazy() @@ -168,20 +161,7 @@ class ReaderActivity : BaseActivity() { */ override fun onCreate(savedInstanceState: Bundle?) { registerSecureActivity(this) - - // Setup shared element transitions - if (intent.extras?.getBoolean(EXTRA_IS_TRANSITION) == true) { - window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) - findViewById(android.R.id.content)?.let { contentView -> - contentView.transitionName = SHARED_ELEMENT_NAME - setEnterSharedElementCallback(MaterialContainerTransformSharedElementCallback()) - window.sharedElementEnterTransition = buildContainerTransform(true) - window.sharedElementReturnTransition = buildContainerTransform(false) - - // Postpone custom transition until manga ready - postponeEnterTransition() - } - } + overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit) super.onCreate(savedInstanceState) @@ -357,6 +337,7 @@ class ReaderActivity : BaseActivity() { override fun finish() { viewModel.onActivityFinish() super.finish() + overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit) } override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt index 09915a5664..1fc9672d17 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt @@ -6,14 +6,13 @@ import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow -import cafe.adriel.voyager.transitions.ScreenTransition import eu.kanade.presentation.components.TwoPanelBox import eu.kanade.presentation.more.settings.screen.AboutScreen import eu.kanade.presentation.more.settings.screen.SettingsBackupScreen import eu.kanade.presentation.more.settings.screen.SettingsGeneralScreen import eu.kanade.presentation.more.settings.screen.SettingsMainScreen +import eu.kanade.presentation.util.DefaultNavigatorScreenTransition import eu.kanade.presentation.util.LocalBackPress -import eu.kanade.presentation.util.Transition import eu.kanade.presentation.util.isTabletUi class SettingsScreen private constructor( @@ -42,10 +41,7 @@ class SettingsScreen private constructor( } } CompositionLocalProvider(LocalBackPress provides pop) { - ScreenTransition( - navigator = it, - transition = { Transition.OneWayFade }, - ) + DefaultNavigatorScreenTransition(navigator = it) } }, ) @@ -65,12 +61,7 @@ class SettingsScreen private constructor( SettingsMainScreen.Content(twoPane = true) } }, - endContent = { - ScreenTransition( - navigator = it, - transition = { Transition.OneWayFade }, - ) - }, + endContent = { DefaultNavigatorScreenTransition(navigator = it) }, ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt index ba9fe85b8c..dd0d54f795 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt @@ -30,6 +30,7 @@ class WebViewActivity : BaseActivity() { } override fun onCreate(savedInstanceState: Bundle?) { + overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit) super.onCreate(savedInstanceState) if (!WebViewUtil.supportsWebView(this)) { @@ -58,6 +59,11 @@ class WebViewActivity : BaseActivity() { } } + override fun finish() { + super.finish() + overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit) + } + private fun shareWebpage(url: String) { try { startActivity(url.toUri().toShareIntent(this, type = "text/plain")) diff --git a/app/src/main/res/anim-v33/shared_axis_x_pop_enter.xml b/app/src/main/res/anim-v33/shared_axis_x_pop_enter.xml new file mode 100644 index 0000000000..e8862c359e --- /dev/null +++ b/app/src/main/res/anim-v33/shared_axis_x_pop_enter.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/app/src/main/res/anim-v33/shared_axis_x_pop_exit.xml b/app/src/main/res/anim-v33/shared_axis_x_pop_exit.xml new file mode 100644 index 0000000000..196a9a825e --- /dev/null +++ b/app/src/main/res/anim-v33/shared_axis_x_pop_exit.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/app/src/main/res/anim-v33/shared_axis_x_push_enter.xml b/app/src/main/res/anim-v33/shared_axis_x_push_enter.xml new file mode 100644 index 0000000000..d882fa8d2f --- /dev/null +++ b/app/src/main/res/anim-v33/shared_axis_x_push_enter.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/app/src/main/res/anim-v33/shared_axis_x_push_exit.xml b/app/src/main/res/anim-v33/shared_axis_x_push_exit.xml new file mode 100644 index 0000000000..343e2a04e8 --- /dev/null +++ b/app/src/main/res/anim-v33/shared_axis_x_push_exit.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/app/src/main/res/anim/shared_axis_x_pop_enter.xml b/app/src/main/res/anim/shared_axis_x_pop_enter.xml new file mode 100644 index 0000000000..de80fb2824 --- /dev/null +++ b/app/src/main/res/anim/shared_axis_x_pop_enter.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/app/src/main/res/anim/shared_axis_x_pop_exit.xml b/app/src/main/res/anim/shared_axis_x_pop_exit.xml new file mode 100644 index 0000000000..190190740f --- /dev/null +++ b/app/src/main/res/anim/shared_axis_x_pop_exit.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/app/src/main/res/anim/shared_axis_x_push_enter.xml b/app/src/main/res/anim/shared_axis_x_push_enter.xml new file mode 100644 index 0000000000..de63636dc3 --- /dev/null +++ b/app/src/main/res/anim/shared_axis_x_push_enter.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/app/src/main/res/anim/shared_axis_x_push_exit.xml b/app/src/main/res/anim/shared_axis_x_push_exit.xml new file mode 100644 index 0000000000..8881286e1c --- /dev/null +++ b/app/src/main/res/anim/shared_axis_x_push_exit.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3497bd8242..c0be55ee73 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -62,6 +62,7 @@ directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" insetter = "dev.chrisbanes.insetter:insetter:0.6.1" cascade = "me.saket.cascade:cascade-compose:2.0.0-beta1" wheelpicker = "com.github.commandiron:WheelPickerCompose:1.0.11" +materialmotion-core = "io.github.fornewid:material-motion-compose-core:0.10.3" logcat = "com.squareup.logcat:logcat:0.1"