diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 534d0af3dc..a47c049207 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -165,6 +165,7 @@ dependencies {
implementation(androidx.palette)
implementation(androidx.activity)
implementation(androidx.core)
+ implementation(androidx.core.splashscreen)
implementation(libs.flexbox)
implementation(androidx.window)
implementation(androidx.swiperefreshlayout)
diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 036d09bc5f..0000000000
--- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 036d09bc5f..0000000000
--- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index bf8a00ba63..2e0348e25f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -54,7 +54,7 @@
android:name=".ui.main.MainActivity"
android:windowSoftInputMode="adjustNothing"
android:label="@string/app_short_name"
- android:theme="@style/Theme.Splash"
+ android:theme="@style/Theme.Tachiyomi.SplashScreen"
android:exported="true">
@@ -74,7 +74,7 @@
@@ -95,7 +95,7 @@
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt
index 0446bd0265..a490a358ec 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt
@@ -1129,6 +1129,7 @@ open class LibraryController(
emptyList()
},
)
+ (activity as? MainActivity)?.ready = true
}
adapter.setItems(mangaMap)
if (binding.libraryGridRecycler.recycler.translationX != 0f) {
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 4a67943703..356ff83cbe 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
@@ -43,6 +43,8 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.getSystemService
import androidx.core.graphics.ColorUtils
import androidx.core.net.toUri
+import androidx.core.splashscreen.SplashScreen
+import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
@@ -53,6 +55,8 @@ import androidx.core.view.forEach
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
+import androidx.interpolator.view.animation.FastOutSlowInInterpolator
+import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
@@ -236,7 +240,11 @@ open class MainActivity : BaseActivity() {
}
}
+ var ready = false
+
override fun onCreate(savedInstanceState: Bundle?) {
+ 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(
@@ -262,6 +270,7 @@ open class MainActivity : BaseActivity() {
window.sharedElementsUseOverlay = false
super.onCreate(savedInstanceState)
+
backPressedCallback = object : OnBackPressedCallback(enabled = true) {
var startTime: Long = 0
var lastX: Float = 0f
@@ -386,6 +395,21 @@ open class MainActivity : BaseActivity() {
val container: ViewGroup = binding.controllerContainer
val content: ViewGroup = binding.mainContent
+
+ if (savedInstanceState == null && this !is SearchActivity) {
+ // Reset Incognito Mode on relaunch
+ preferences.incognitoMode().set(false)
+
+ // Show changelog if needed
+ if (Migrations.upgrade(preferences, Injekt.get(), lifecycleScope)) {
+ if (!BuildConfig.DEBUG) {
+ content.post {
+ whatsNewSheet().show()
+ }
+ }
+ }
+ }
+
DownloadJob.downloadFlow.onEach(::downloadStatusChanged).launchIn(lifecycleScope)
lifecycleScope
WindowCompat.setDecorFitsSystemWindows(window, false)
@@ -629,19 +653,13 @@ open class MainActivity : BaseActivity() {
(router.backstack.lastOrNull()?.controller as? BaseLegacyController<*>)?.setTitle()
(router.backstack.lastOrNull()?.controller as? SettingsController)?.setTitle()
- if (savedInstanceState == null && this !is SearchActivity) {
- // Reset Incognito Mode on relaunch
- preferences.incognitoMode().set(false)
-
- // Show changelog if needed
- if (Migrations.upgrade(preferences, Injekt.get(), lifecycleScope)) {
- if (!BuildConfig.DEBUG) {
- content.post {
- whatsNewSheet().show()
- }
- }
- }
+ val startTime = System.currentTimeMillis()
+ splashScreen?.setKeepOnScreenCondition {
+ val elapsed = System.currentTimeMillis() - startTime
+ elapsed <= SPLASH_MIN_DURATION || (!ready && elapsed <= SPLASH_MAX_DURATION)
}
+ setSplashScreenExitAnimation(splashScreen)
+
getExtensionUpdates(true)
preferences.extensionUpdatesCount()
@@ -684,6 +702,43 @@ open class MainActivity : BaseActivity() {
}
}
+ private fun setSplashScreenExitAnimation(splashScreen: SplashScreen?) {
+ val root = findViewById(android.R.id.content)
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S && splashScreen != null) {
+ window.statusBarColor = Color.TRANSPARENT
+ window.navigationBarColor = Color.TRANSPARENT
+
+ splashScreen.setOnExitAnimationListener { splashProvider ->
+ // For some reason the SplashScreen applies (incorrect) Y translation to the iconView
+ splashProvider.iconView.translationY = 0F
+
+ val activityAnim = ValueAnimator.ofFloat(1F, 0F).apply {
+ interpolator = LinearOutSlowInInterpolator()
+ duration = SPLASH_EXIT_ANIM_DURATION
+ addUpdateListener { va ->
+ val value = va.animatedValue as Float
+ root.translationY = value * 16.dpToPx
+ }
+ }
+
+ val splashAnim = ValueAnimator.ofFloat(1F, 0F).apply {
+ interpolator = FastOutSlowInInterpolator()
+ duration = SPLASH_EXIT_ANIM_DURATION
+ addUpdateListener { va ->
+ val value = va.animatedValue as Float
+ splashProvider.view.alpha = value
+ }
+ doOnEnd {
+ splashProvider.remove()
+ }
+ }
+
+ activityAnim.start()
+ splashAnim.start()
+ }
+ }
+ }
+
fun reEnableBackPressedCallBack() {
val returnToStart = preferences.backReturnsToStart().get() && this !is SearchActivity
backPressedCallback?.isEnabled = actionMode != null ||
@@ -1568,6 +1623,11 @@ open class MainActivity : BaseActivity() {
const val INTENT_SEARCH_QUERY = "query"
const val INTENT_SEARCH_FILTER = "filter"
+ // Splash screen
+ private const val SPLASH_MIN_DURATION = 500 // ms
+ private const val SPLASH_MAX_DURATION = 5000 // ms
+ private const val SPLASH_EXIT_ANIM_DURATION = 400L // ms
+
var chapterIdToExitTo = 0L
var backVelocity = 0f
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt
index d3726adc12..20f77a6263 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt
@@ -78,6 +78,7 @@ import eu.kanade.tachiyomi.util.view.isControllerVisible
import eu.kanade.tachiyomi.util.view.isExpanded
import eu.kanade.tachiyomi.util.view.isHidden
import eu.kanade.tachiyomi.util.view.moveRecyclerViewUp
+import eu.kanade.tachiyomi.util.view.onAnimationsFinished
import eu.kanade.tachiyomi.util.view.requestFilePermissionsSafe
import eu.kanade.tachiyomi.util.view.scrollViewWith
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
@@ -170,6 +171,9 @@ class RecentsController(bundle: Bundle? = null) :
binding.recycler.setHasFixedSize(true)
binding.recycler.recycledViewPool.setMaxRecycledViews(0, 0)
binding.recycler.addItemDecoration(RecentMangaDivider(view.context))
+ binding.recycler.onAnimationsFinished {
+ (activity as? MainActivity)?.ready = true
+ }
adapter.isSwipeEnabled = true
adapter.itemTouchHelperCallback.setSwipeFlags(
if (view.resources.isLTR) ItemTouchHelper.LEFT else ItemTouchHelper.RIGHT,
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt
index 3e5af7cfb7..9ac866e281 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt
@@ -58,6 +58,7 @@ import eu.kanade.tachiyomi.util.view.expand
import eu.kanade.tachiyomi.util.view.isCollapsed
import eu.kanade.tachiyomi.util.view.isCompose
import eu.kanade.tachiyomi.util.view.isControllerVisible
+import eu.kanade.tachiyomi.util.view.onAnimationsFinished
import eu.kanade.tachiyomi.util.view.requestFilePermissionsSafe
import eu.kanade.tachiyomi.util.view.scrollViewWith
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
@@ -141,6 +142,9 @@ class BrowseController :
binding.sourceRecycler.layoutManager = LinearLayoutManagerAccurateOffset(view.context)
binding.sourceRecycler.adapter = adapter
+ binding.sourceRecycler.onAnimationsFinished {
+ (activity as? MainActivity)?.ready = true
+ }
adapter?.isSwipeEnabled = true
adapter?.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
scrollViewWith(
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt
index 3a414ac5f3..010f32ecef 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt
@@ -586,3 +586,20 @@ fun View?.isVisibleOnScreen(): Boolean {
val screen = Rect(0, 0, Resources.getSystem().displayMetrics.widthPixels, Resources.getSystem().displayMetrics.heightPixels)
return actualPosition.intersect(screen)
}
+
+/**
+ * Callback will be run immediately when no animation running
+ */
+fun RecyclerView.onAnimationsFinished(callback: (RecyclerView) -> Unit) = post(
+ object : Runnable {
+ override fun run() {
+ if (isAnimating) {
+ itemAnimator?.isRunning {
+ post(this)
+ }
+ } else {
+ callback(this@onAnimationsFinished)
+ }
+ }
+ }
+)
diff --git a/app/src/main/res/drawable/ic_yokai_splash.xml b/app/src/main/res/drawable/ic_yokai_splash.xml
index 1746ea616e..7135071e6d 100644
--- a/app/src/main/res/drawable/ic_yokai_splash.xml
+++ b/app/src/main/res/drawable/ic_yokai_splash.xml
@@ -2,7 +2,7 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/splash_background.xml b/app/src/main/res/drawable/splash_background.xml
deleted file mode 100644
index 546572247c..0000000000
--- a/app/src/main/res/drawable/splash_background.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index 036d09bc5f..43bed53c2a 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
index 036d09bc5f..43bed53c2a 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
index 80ce0395f2..0dec72d47c 100644
--- a/app/src/main/res/values-night/colors.xml
+++ b/app/src/main/res/values-night/colors.xml
@@ -36,7 +36,6 @@
#1F3399FF
#3399FF
#cce5ff
- #212121
@color/md_white_1000_38
@color/md_black_1000_87
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 7ac1f930aa..aedd2bc051 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -31,7 +31,7 @@
#54759E
@color/md_white_1000
#78bcff
- @color/primaryTachiyomi
+ @color/yokai_background
@color/md_blue_A400
#1F2979FF
#0d2f68
@@ -99,6 +99,9 @@
#CCA6CFD5
#CC7D1128
+
+ #0E1418
+
#a149bf
#cd95e0
@@ -178,4 +181,4 @@
#1F000000
#000000
@color/md_white_1000
-
+
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index b0fb2d8e4a..92bd844d1d 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -10,9 +10,6 @@
- false
- false
- true
- - @color/background
- - @drawable/ic_yokai_splash
- - 775
- @color/primaryTachiyomi
- @color/primaryInverseTachiyomi
@@ -219,12 +216,16 @@
-