Skip to content

Commit 614f857

Browse files
Haz3-joltS-H-Y-A
andcommitted
feat: add splash screen animation
- Fixed up the splash screen proposed in PR #14837 Co-authored-by: S-H-Y-A <74596628+s-h-y-a@users.noreply.github.com>
1 parent 6213d7a commit 614f857

File tree

6 files changed

+456
-55
lines changed

6 files changed

+456
-55
lines changed

AnkiDroid/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@
119119
<activity
120120
android:name="com.ichi2.anki.IntentHandler"
121121
android:configChanges="keyboardHidden|orientation|screenSize"
122-
android:theme="@android:style/Theme.Translucent.NoTitleBar"
122+
android:theme="@style/Theme_Dark.Launcher"
123123
android:exported="true"
124124
>
125125
<intent-filter>

AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler.kt

Lines changed: 76 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ package com.ichi2.anki
1919
import android.content.Context
2020
import android.content.Intent
2121
import android.net.Uri
22+
import android.os.Build
2223
import android.os.Bundle
24+
import android.os.Handler
25+
import android.os.Looper
2326
import android.os.Message
2427
import androidx.annotation.CheckResult
2528
import androidx.annotation.VisibleForTesting
@@ -54,6 +57,8 @@ import kotlinx.coroutines.Dispatchers
5457
import kotlinx.coroutines.launch
5558
import timber.log.Timber
5659
import java.io.File
60+
import java.time.Duration
61+
import java.time.Instant
5762
import kotlin.math.max
5863
import kotlin.math.min
5964

@@ -67,54 +72,81 @@ import kotlin.math.min
6772
class IntentHandler : AbstractIntentHandler() {
6873
override fun onCreate(savedInstanceState: Bundle?) {
6974
// Note: This is our entry point from the launcher with intent: android.intent.action.MAIN
70-
super.onCreate(savedInstanceState)
71-
val intent = intent
72-
Timber.v(intent.toString())
73-
val reloadIntent = Intent(this, DeckPicker::class.java)
74-
reloadIntent.setDataAndType(getIntent().data, getIntent().type)
75-
val action = intent.action
76-
// #6157 - We want to block actions that need permissions we don't have, but not the default case
77-
// as this requires nothing
78-
val runIfStoragePermissions = { runnable: () -> Unit -> performActionIfStorageAccessible(reloadIntent, action) { runnable() } }
79-
val launchType = getLaunchType(intent)
80-
// TODO block the UI with some kind of ProgressDialog instead of cancelling the sync work
81-
if (requiresCollectionAccess(launchType)) {
82-
// # 18899
83-
if (WorkManager.isInitialized()) {
84-
SyncWorker.cancel(this)
85-
}
86-
}
87-
when (launchType) {
88-
LaunchType.FILE_IMPORT ->
89-
runIfStoragePermissions {
90-
handleFileImport(fileIntent, reloadIntent, action)
91-
finish()
92-
}
93-
LaunchType.TEXT_IMPORT ->
94-
runIfStoragePermissions {
95-
onSelectedCsvForImport(fileIntent)
96-
finish()
75+
76+
val handleScreen = { delay: Long ->
77+
Handler(Looper.getMainLooper()).postDelayed({
78+
val intent = intent
79+
Timber.v(intent.toString())
80+
val reloadIntent = Intent(this, DeckPicker::class.java)
81+
reloadIntent.setDataAndType(getIntent().data, getIntent().type)
82+
val action = intent.action
83+
// #6157 - We want to block actions that need permissions we don't have, but not the default case
84+
// as this requires nothing
85+
val runIfStoragePermissions = { runnable: () -> Unit ->
86+
performActionIfStorageAccessible(reloadIntent, action) { runnable() }
9787
}
98-
LaunchType.IMAGE_IMPORT ->
99-
runIfStoragePermissions {
100-
handleImageImport(intent)
101-
finish()
88+
val launchType = getLaunchType(intent)
89+
// TODO block the UI with some kind of ProgressDialog instead of cancelling the sync work
90+
if (requiresCollectionAccess(launchType)) {
91+
// # 18899
92+
if (WorkManager.isInitialized()) {
93+
SyncWorker.cancel(this)
94+
}
10295
}
103-
LaunchType.SHARED_TEXT ->
104-
runIfStoragePermissions {
105-
handleSharedText(intent)
106-
finish()
96+
when (launchType) {
97+
LaunchType.FILE_IMPORT ->
98+
runIfStoragePermissions {
99+
handleFileImport(fileIntent, reloadIntent, action)
100+
finish()
101+
}
102+
LaunchType.TEXT_IMPORT ->
103+
runIfStoragePermissions {
104+
onSelectedCsvForImport(fileIntent)
105+
finish()
106+
}
107+
LaunchType.IMAGE_IMPORT ->
108+
runIfStoragePermissions {
109+
handleImageImport(intent)
110+
finish()
111+
}
112+
LaunchType.SHARED_TEXT ->
113+
runIfStoragePermissions {
114+
handleSharedText(intent)
115+
finish()
116+
}
117+
LaunchType.SYNC -> runIfStoragePermissions { handleSyncIntent(reloadIntent, action) }
118+
LaunchType.REVIEW -> runIfStoragePermissions { handleReviewIntent(reloadIntent, intent) }
119+
LaunchType.DEFAULT_START_APP_IF_NEW -> {
120+
Timber.d("onCreate() performing default action")
121+
launchDeckPickerIfNoOtherTasks(reloadIntent)
122+
}
123+
LaunchType.COPY_DEBUG_INFO -> {
124+
copyDebugInfoToClipboard(intent)
125+
finish()
126+
}
107127
}
108-
LaunchType.SYNC -> runIfStoragePermissions { handleSyncIntent(reloadIntent, action) }
109-
LaunchType.REVIEW -> runIfStoragePermissions { handleReviewIntent(reloadIntent, intent) }
110-
LaunchType.DEFAULT_START_APP_IF_NEW -> {
111-
Timber.d("onCreate() performing default action")
112-
launchDeckPickerIfNoOtherTasks(reloadIntent)
113-
}
114-
LaunchType.COPY_DEBUG_INFO -> {
115-
copyDebugInfoToClipboard(intent)
116-
finish()
128+
}, delay)
129+
}
130+
131+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
132+
splashScreen.setOnExitAnimationListener { splashScreenView ->
133+
val animationDuration = splashScreenView.iconAnimationDuration
134+
val animationStart = splashScreenView.iconAnimationStart
135+
136+
Timber.v("duration: ${animationDuration?.toMillis()} start: $animationStart")
137+
val remainingDuration =
138+
if (animationDuration != null && animationStart != null) {
139+
(animationDuration - Duration.between(animationStart, Instant.now())).toMillis().coerceAtLeast(0L)
140+
} else {
141+
0L
142+
}
143+
Timber.v("remainDuration: $remainingDuration")
144+
handleScreen(remainingDuration)
117145
}
146+
super.onCreate(savedInstanceState)
147+
} else {
148+
super.onCreate(savedInstanceState)
149+
handleScreen(0L) // No delay for older Android versions
118150
}
119151
}
120152

AnkiDroid/src/main/res/drawable/launch_screen.xml

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
<!-- The background color, preferably the same as your normal theme -->
33
<item android:drawable="@color/material_theme_grey"/>
44
<!-- Your product logo - 144dp color version of your app icon -->
5-
<item>
6-
<bitmap
7-
android:src="@drawable/logo_star_144dp"
8-
android:gravity="center"/>
9-
</item>
10-
<item android:top="200dp">
11-
<bitmap
12-
android:src="@drawable/ankidroid_txt"
13-
android:gravity="center"/>
14-
</item>
5+
<item
6+
android:width="288dp"
7+
android:height="288dp"
8+
android:drawable="@drawable/splash_icon"
9+
android:gravity="center"/>
10+
<item
11+
android:width="200dp"
12+
android:height="80dp"
13+
android:drawable="@drawable/splash_branding"
14+
android:gravity="center|bottom"
15+
android:bottom="48dp"/>
1516
</layer-list>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
3+
<item
4+
android:width="200dp"
5+
android:height="80dp"
6+
android:drawable="@android:color/transparent"
7+
android:gravity="center" />
8+
<item
9+
android:drawable="@drawable/ankidroid_txt"
10+
android:height="31.9dp"
11+
android:width="200dp"
12+
android:gravity="center" />
13+
</layer-list>

0 commit comments

Comments
 (0)