mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
refactor: Backport Android 12 SplashScreen to older Android versions
Fixes GH-20
This commit is contained in:
parent
27284d00f6
commit
04078084bd
17 changed files with 123 additions and 54 deletions
|
@ -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)
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
|
@ -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">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
@ -74,7 +74,7 @@
|
|||
</activity>
|
||||
<activity
|
||||
android:name=".ui.main.SearchActivity"
|
||||
android:theme="@style/Theme.Splash"
|
||||
android:theme="@style/Theme.Tachiyomi.SplashScreen"
|
||||
android:label="@string/label_global_search"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
|
@ -95,7 +95,7 @@
|
|||
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>
|
||||
</activity>
|
||||
<activity android:name=".ui.reader.ReaderActivity"
|
||||
android:theme="@style/Theme.Splash"
|
||||
android:theme="@style/Theme.Tachiyomi.SplashScreen"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
|
|
@ -1129,6 +1129,7 @@ open class LibraryController(
|
|||
emptyList()
|
||||
},
|
||||
)
|
||||
(activity as? MainActivity)?.ready = true
|
||||
}
|
||||
adapter.setItems(mangaMap)
|
||||
if (binding.libraryGridRecycler.recycler.translationX != 0f) {
|
||||
|
|
|
@ -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<MainActivityBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
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<MainActivityBinding>() {
|
|||
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<MainActivityBinding>() {
|
|||
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<MainActivityBinding>() {
|
|||
(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<MainActivityBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setSplashScreenExitAnimation(splashScreen: SplashScreen?) {
|
||||
val root = findViewById<View>(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<MainActivityBinding>() {
|
|||
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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:drawable="@mipmap/ic_launcher_round"
|
||||
android:width="160dp"
|
||||
android:height="160dp"
|
||||
android:width="72dp"
|
||||
android:height="72dp"
|
||||
android:gravity="center" />
|
||||
</layer-list>
|
||||
</layer-list>
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:drawable="@color/splashBackground" />
|
||||
|
||||
<item
|
||||
android:width="160dp"
|
||||
android:height="160dp"
|
||||
android:drawable="@mipmap/ic_launcher_round"
|
||||
android:gravity="center" />
|
||||
|
||||
</layer-list>
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<background android:drawable="@color/yokai_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<background android:drawable="@color/yokai_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
|
@ -36,7 +36,6 @@
|
|||
<color name="rippleColorTachiyomi">#1F3399FF</color>
|
||||
<color name="secondaryTachiyomi">#3399FF</color>
|
||||
<color name="secondaryVariantTachiyomi">#cce5ff</color>
|
||||
<color name="splashBackground">#212121</color>
|
||||
<color name="actionModeShadow">@color/md_white_1000_38</color>
|
||||
|
||||
<color name="textColorPrimaryInverse">@color/md_black_1000_87</color>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<color name="primaryTachiyomi">#54759E</color>
|
||||
<color name="onPrimaryTachiyomi">@color/md_white_1000</color>
|
||||
<color name="primaryInverseTachiyomi">#78bcff</color>
|
||||
<color name="splashBackground">@color/primaryTachiyomi</color>
|
||||
<color name="splashBackground">@color/yokai_background</color>
|
||||
<color name="secondaryTachiyomi">@color/md_blue_A400</color>
|
||||
<color name="rippleColorTachiyomi">#1F2979FF</color>
|
||||
<color name="secondaryVariantTachiyomi">#0d2f68</color>
|
||||
|
@ -99,6 +99,9 @@
|
|||
<color name="navigation_next">#CCA6CFD5</color>
|
||||
<color name="navigation_prev">#CC7D1128</color>
|
||||
|
||||
<!-- Yokai -->
|
||||
<color name="yokai_background">#0E1418</color>
|
||||
|
||||
<!-- Spring Blossom -->
|
||||
<color name="primaryDuskDawn">#a149bf</color>
|
||||
<color name="primaryInverseDuskDawn">#cd95e0</color>
|
||||
|
@ -178,4 +181,4 @@
|
|||
<color name="rippleColorYinYang">#1F000000</color>
|
||||
<color name="secondaryVariantYinYang">#000000</color>
|
||||
<color name="onSecondaryYinYang">@color/md_white_1000</color>
|
||||
</resources>
|
||||
</resources>
|
|
@ -10,9 +10,6 @@
|
|||
<item name="android:forceDarkAllowed" tools:targetApi="29">false</item>
|
||||
<item name="android:enforceNavigationBarContrast" tools:targetApi="29">false</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||
<item name="android:windowSplashScreenBackground" tools:targetApi="31">@color/background</item>
|
||||
<item name="android:windowSplashScreenAnimatedIcon" tools:targetApi="31">@drawable/ic_yokai_splash</item>
|
||||
<item name="android:windowSplashScreenAnimationDuration" tools:targetApi="31">775</item>
|
||||
<!--Standard Material colors -->
|
||||
<item name="colorPrimary">@color/primaryTachiyomi</item>
|
||||
<item name="colorPrimaryInverse">@color/primaryInverseTachiyomi</item>
|
||||
|
@ -219,12 +216,16 @@
|
|||
<!--===============-->
|
||||
<!-- Launch Screen -->
|
||||
<!--===============-->
|
||||
<style name="Theme.Splash" parent="Theme.Tachiyomi">
|
||||
<item name="android:windowBackground">@drawable/splash_background</item>
|
||||
<item name="android:statusBarColor">@color/splashBackground</item>
|
||||
<item name="android:navigationBarColor">@color/splashBackground</item>
|
||||
<item name="android:windowLightStatusBar">false</item>
|
||||
<item name="android:windowLightNavigationBar" tools:targetApi="27">false</item>
|
||||
|
||||
<!-- Splash -->
|
||||
<style name="Theme.Tachiyomi.SplashScreen" parent="Theme.SplashScreen">
|
||||
<item name="windowSplashScreenBackground">@color/splashBackground</item>
|
||||
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_yokai_splash</item>
|
||||
<item name="postSplashScreenTheme">@style/Theme.Tachiyomi</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
<item name="android:windowLightNavigationBar" tools:targetApi="27">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.OSS" parent="Theme.Tachiyomi.Monet">
|
||||
|
|
|
@ -11,6 +11,7 @@ browser = { module = "androidx.browser:browser", version = "1.6.0" }
|
|||
biometric = { module = "androidx.biometric:biometric", version = "1.1.0" }
|
||||
cardview = { module = "androidx.cardview:cardview", version = "1.0.0" }
|
||||
core = { module = "androidx.core:core-ktx", version = "1.12.0" }
|
||||
core-splashscreen = { module = "androidx.core:core-splashscreen", version = "1.0.1" }
|
||||
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.1.4" }
|
||||
glance-appwidget = { module = "androidx.glance:glance-appwidget", version = "1.0.0" }
|
||||
lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue