capacitor-lottie-splash 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/android/src/main/java/com/headcount/plugins/lottiesplash/LottieSplashCoordinator.kt +1 -46
- package/android/src/main/java/com/headcount/plugins/lottiesplash/LottieSplashPlugin.kt +1 -2
- package/dist/esm/definitions.d.ts +1 -1
- package/dist/esm/state-machine.d.ts +0 -1
- package/dist/esm/state-machine.js +0 -6
- package/dist/esm/web.d.ts +0 -2
- package/dist/esm/web.js +0 -26
- package/dist/plugin.cjs.js +1 -17071
- package/dist/plugin.js +0 -32
- package/ios/Sources/LottieSplashPlugin/LottieSplashCoordinator.swift +1 -42
- package/ios/Sources/LottieSplashPlugin/LottieSplashPlugin.swift +0 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,9 +101,9 @@ The documented HeadCount defaults are light `#f0f0f0 → #f5f5f5` and dark `#1e1
|
|
|
101
101
|
|
|
102
102
|
## Recovery and accessibility
|
|
103
103
|
|
|
104
|
-
When playback completes before readiness, its final frame holds. After one second, a subtle indeterminate progress rail appears beneath the mark
|
|
104
|
+
When playback completes before readiness, its final frame holds. After one second, a subtle indeterminate progress rail appears beneath the mark and remains visible until the application is ready. Reduced-motion and asset failures retain the theme-matched native cover instead of exposing a blank screen.
|
|
105
105
|
|
|
106
|
-
For a staged native/over-the-air rollout, the consuming app must keep its web bundle and native host compatible. The native cover stays visible until the app-ready latch is set
|
|
106
|
+
For a staged native/over-the-air rollout, the consuming app must keep its web bundle and native host compatible. The native cover stays visible until the app-ready latch is set.
|
|
107
107
|
|
|
108
108
|
## Compatibility
|
|
109
109
|
|
|
@@ -12,10 +12,7 @@ import android.view.View
|
|
|
12
12
|
import android.view.ViewGroup
|
|
13
13
|
import android.view.animation.AccelerateDecelerateInterpolator
|
|
14
14
|
import android.view.animation.LinearInterpolator
|
|
15
|
-
import android.widget.Button
|
|
16
15
|
import android.widget.FrameLayout
|
|
17
|
-
import android.widget.LinearLayout
|
|
18
|
-
import android.widget.TextView
|
|
19
16
|
import com.airbnb.lottie.LottieAnimationView
|
|
20
17
|
import com.airbnb.lottie.LottieCompositionFactory
|
|
21
18
|
import androidx.activity.ComponentActivity
|
|
@@ -23,7 +20,7 @@ import java.util.UUID
|
|
|
23
20
|
import kotlin.math.max
|
|
24
21
|
import kotlin.math.roundToInt
|
|
25
22
|
|
|
26
|
-
enum class AndroidSplashPhase { PREPARING, PLAYING, HOLDING, HIDING, HIDDEN
|
|
23
|
+
enum class AndroidSplashPhase { PREPARING, PLAYING, HOLDING, HIDING, HIDDEN }
|
|
27
24
|
enum class AndroidSplashPlayback { PLAY, HOLD_FINAL_FRAME }
|
|
28
25
|
|
|
29
26
|
/** A small indeterminate rail that reads as loading without covering the finished logo. */
|
|
@@ -82,7 +79,6 @@ object LottieSplashCoordinator {
|
|
|
82
79
|
// incorrectly dismissing a healthy native cover during that startup work.
|
|
83
80
|
private const val WEB_ADOPTION_GRACE_MS = 30_000L
|
|
84
81
|
private const val PROGRESS_DELAY_MS = 1_000L
|
|
85
|
-
private const val RECOVERY_DELAY_MS = 15_000L
|
|
86
82
|
private const val COVER_FADE_OUT_MS = 500L
|
|
87
83
|
private val main = Handler(Looper.getMainLooper())
|
|
88
84
|
|
|
@@ -94,8 +90,6 @@ object LottieSplashCoordinator {
|
|
|
94
90
|
private var compositionReady = false
|
|
95
91
|
private var requestedPlayback = AndroidSplashPlayback.PLAY
|
|
96
92
|
private var loadingRail: LottieSplashLoadingRail? = null
|
|
97
|
-
private var recoveryMessage: TextView? = null
|
|
98
|
-
private var recoveryRetry: Button? = null
|
|
99
93
|
private var presentationId: String? = null
|
|
100
94
|
private var phase = AndroidSplashPhase.HIDDEN
|
|
101
95
|
private var playbackSettled = false
|
|
@@ -106,16 +100,13 @@ object LottieSplashCoordinator {
|
|
|
106
100
|
private var completion: ((String, String?) -> Unit)? = null
|
|
107
101
|
private var pendingHide: ((Throwable?) -> Unit)? = null
|
|
108
102
|
private var progressTask: Runnable? = null
|
|
109
|
-
private var recoveryTask: Runnable? = null
|
|
110
103
|
private var adoptionTask: Runnable? = null
|
|
111
104
|
private var adoptedByWeb = false
|
|
112
105
|
private var progressRemainingMs = PROGRESS_DELAY_MS
|
|
113
|
-
private var recoveryRemainingMs = RECOVERY_DELAY_MS
|
|
114
106
|
private var waitingClockStartedAtMs = 0L
|
|
115
107
|
private var waitingClockRunning = false
|
|
116
108
|
|
|
117
109
|
var onStateChange: (() -> Unit)? = null
|
|
118
|
-
var onRecoveryRequested: (() -> Unit)? = null
|
|
119
110
|
|
|
120
111
|
/**
|
|
121
112
|
* Called by AndroidX while deciding whether its system-owned launch surface may leave.
|
|
@@ -153,8 +144,6 @@ object LottieSplashCoordinator {
|
|
|
153
144
|
playbackOutcome = "static-fallback"
|
|
154
145
|
playbackReason = null
|
|
155
146
|
loadingRail = null
|
|
156
|
-
recoveryMessage = null
|
|
157
|
-
recoveryRetry = null
|
|
158
147
|
|
|
159
148
|
val backgroundStart = if (darkTheme) Color.rgb(30, 30, 30) else Color.rgb(240, 240, 240)
|
|
160
149
|
root.setBackgroundColor(backgroundStart)
|
|
@@ -354,7 +343,6 @@ object LottieSplashCoordinator {
|
|
|
354
343
|
private fun resetWaitingUi(id: String) {
|
|
355
344
|
cancelWaitingTasks()
|
|
356
345
|
progressRemainingMs = PROGRESS_DELAY_MS
|
|
357
|
-
recoveryRemainingMs = RECOVERY_DELAY_MS
|
|
358
346
|
scheduleWaitingUi(id)
|
|
359
347
|
}
|
|
360
348
|
|
|
@@ -371,24 +359,19 @@ object LottieSplashCoordinator {
|
|
|
371
359
|
waitingClockRunning = true
|
|
372
360
|
waitingClockStartedAtMs = SystemClock.elapsedRealtime()
|
|
373
361
|
progressTask = Runnable { showLoadingRail(id) }
|
|
374
|
-
recoveryTask = Runnable { showRecovery(id) }
|
|
375
362
|
if (loadingRail == null) main.postDelayed(progressTask!!, progressRemainingMs)
|
|
376
|
-
main.postDelayed(recoveryTask!!, recoveryRemainingMs)
|
|
377
363
|
}
|
|
378
364
|
|
|
379
365
|
private fun pauseWaitingClock() {
|
|
380
366
|
if (!waitingClockRunning) return
|
|
381
367
|
val elapsed = SystemClock.elapsedRealtime() - waitingClockStartedAtMs
|
|
382
368
|
progressRemainingMs = max(0, progressRemainingMs - elapsed)
|
|
383
|
-
recoveryRemainingMs = max(0, recoveryRemainingMs - elapsed)
|
|
384
369
|
cancelWaitingTasks()
|
|
385
370
|
}
|
|
386
371
|
|
|
387
372
|
private fun cancelWaitingTasks() {
|
|
388
373
|
progressTask?.let(main::removeCallbacks)
|
|
389
|
-
recoveryTask?.let(main::removeCallbacks)
|
|
390
374
|
progressTask = null
|
|
391
|
-
recoveryTask = null
|
|
392
375
|
waitingClockRunning = false
|
|
393
376
|
}
|
|
394
377
|
|
|
@@ -404,36 +387,10 @@ object LottieSplashCoordinator {
|
|
|
404
387
|
}
|
|
405
388
|
}
|
|
406
389
|
|
|
407
|
-
private fun showRecovery(id: String) {
|
|
408
|
-
recoveryRemainingMs = 0
|
|
409
|
-
if (presentationId != id || appReady || phase != AndroidSplashPhase.HOLDING) return
|
|
410
|
-
cancelWaitingTasks()
|
|
411
|
-
phase = AndroidSplashPhase.RECOVERY
|
|
412
|
-
loadingRail?.visibility = View.GONE
|
|
413
|
-
val context = cover?.context ?: return
|
|
414
|
-
val textColor = if (darkTheme) Color.WHITE else Color.rgb(30, 30, 30)
|
|
415
|
-
val message = TextView(context).apply { text = "Still starting…"; textSize = 16f; setTextColor(textColor); gravity = Gravity.CENTER }
|
|
416
|
-
val retry = Button(context).apply { text = "Try again"; setOnClickListener { onRecoveryRequested?.invoke() } }
|
|
417
|
-
val recoveryGroup = LinearLayout(context).apply {
|
|
418
|
-
orientation = LinearLayout.VERTICAL
|
|
419
|
-
gravity = Gravity.CENTER_HORIZONTAL
|
|
420
|
-
addView(message)
|
|
421
|
-
addView(retry)
|
|
422
|
-
}
|
|
423
|
-
recoveryMessage = message
|
|
424
|
-
recoveryRetry = retry
|
|
425
|
-
cover?.addView(recoveryGroup, FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM).apply { bottomMargin = dp(recoveryGroup, 48) })
|
|
426
|
-
recoveryGroup.announceForAccessibility("Still starting. Try again is available.")
|
|
427
|
-
emit()
|
|
428
|
-
}
|
|
429
|
-
|
|
430
390
|
private fun clearWaitingUi() {
|
|
431
391
|
cancelWaitingTasks()
|
|
432
392
|
loadingRail?.let { cover?.removeView(it) }
|
|
433
|
-
recoveryMessage?.parent?.let { parent -> (parent as? ViewGroup)?.let { cover?.removeView(it) } }
|
|
434
393
|
loadingRail = null
|
|
435
|
-
recoveryMessage = null
|
|
436
|
-
recoveryRetry = null
|
|
437
394
|
}
|
|
438
395
|
|
|
439
396
|
private fun dismiss(callback: (Throwable?) -> Unit) {
|
|
@@ -464,8 +421,6 @@ object LottieSplashCoordinator {
|
|
|
464
421
|
animation = null
|
|
465
422
|
hostActivity = null
|
|
466
423
|
loadingRail = null
|
|
467
|
-
recoveryMessage = null
|
|
468
|
-
recoveryRetry = null
|
|
469
424
|
presentationId = null
|
|
470
425
|
phase = AndroidSplashPhase.HIDDEN
|
|
471
426
|
compositionReady = false
|
|
@@ -20,7 +20,6 @@ class LottieSplashPlugin : Plugin() {
|
|
|
20
20
|
}
|
|
21
21
|
override fun load() {
|
|
22
22
|
LottieSplashCoordinator.onStateChange = { notifyListeners("stateChange", stateObject()) }
|
|
23
|
-
LottieSplashCoordinator.onRecoveryRequested = { bridge?.webView?.reload() }
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
@PluginMethod
|
|
@@ -89,7 +88,7 @@ class LottieSplashPlugin : Plugin() {
|
|
|
89
88
|
webView.postVisualStateCallback(SystemClock.uptimeMillis(), object : WebView.VisualStateCallback() {
|
|
90
89
|
override fun onComplete(requestId: Long) = finishHandoff()
|
|
91
90
|
})
|
|
92
|
-
// A stalled GPU/compositor must not leave an app-ready screen
|
|
91
|
+
// A stalled GPU/compositor must not leave an app-ready screen covered.
|
|
93
92
|
main.postDelayed(fallback, VISUAL_STATE_TIMEOUT_MS)
|
|
94
93
|
} catch (_: Throwable) {
|
|
95
94
|
finishHandoff()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PluginListenerHandle } from '@capacitor/core';
|
|
2
2
|
export type SplashRenderer = 'ios' | 'android' | 'web';
|
|
3
|
-
export type SplashPhase = 'preparing' | 'playing' | 'holding' | 'hiding' | 'hidden'
|
|
3
|
+
export type SplashPhase = 'preparing' | 'playing' | 'holding' | 'hiding' | 'hidden';
|
|
4
4
|
export type PlaybackOutcome = 'completed' | 'reduced-motion' | 'static-fallback';
|
|
5
5
|
export type SplashTheme = 'light' | 'dark';
|
|
6
6
|
export interface SplashAssets {
|
|
@@ -10,7 +10,6 @@ export declare class SplashStateMachine {
|
|
|
10
10
|
playing(presentationId: string): StateMachineSnapshot;
|
|
11
11
|
settlePlayback(presentationId: string, outcome: PlaybackOutcome, reason?: string): StateMachineSnapshot;
|
|
12
12
|
requestHide(presentationId: string): StateMachineSnapshot;
|
|
13
|
-
recovery(presentationId: string): StateMachineSnapshot;
|
|
14
13
|
hidden(presentationId: string): StateMachineSnapshot;
|
|
15
14
|
getPlaybackResult(presentationId: string): PlaybackResult | undefined;
|
|
16
15
|
current(): StateMachineSnapshot;
|
|
@@ -45,12 +45,6 @@ export class SplashStateMachine {
|
|
|
45
45
|
this.snapshot.phase = 'hiding';
|
|
46
46
|
return this.current();
|
|
47
47
|
}
|
|
48
|
-
recovery(presentationId) {
|
|
49
|
-
this.requireCurrent(presentationId);
|
|
50
|
-
if (!this.snapshot.appReady)
|
|
51
|
-
this.snapshot.phase = 'recovery';
|
|
52
|
-
return this.current();
|
|
53
|
-
}
|
|
54
48
|
hidden(presentationId) {
|
|
55
49
|
this.requireCurrent(presentationId);
|
|
56
50
|
this.snapshot = {
|
package/dist/esm/web.d.ts
CHANGED
|
@@ -6,7 +6,6 @@ export declare class LottieSplashWeb extends WebPlugin implements LottieSplashPl
|
|
|
6
6
|
private resolvePlayback?;
|
|
7
7
|
private overlay?;
|
|
8
8
|
private progressTimer?;
|
|
9
|
-
private recoveryTimer?;
|
|
10
9
|
show(options: SplashShowOptions): Promise<Presentation>;
|
|
11
10
|
setLaunchTheme(_options: SplashLaunchThemeOptions): Promise<void>;
|
|
12
11
|
waitForCompletion(options: {
|
|
@@ -21,7 +20,6 @@ export declare class LottieSplashWeb extends WebPlugin implements LottieSplashPl
|
|
|
21
20
|
private playComposition;
|
|
22
21
|
private settle;
|
|
23
22
|
private showLoadingRail;
|
|
24
|
-
private showRecovery;
|
|
25
23
|
private removeOverlay;
|
|
26
24
|
private ensureCurrent;
|
|
27
25
|
private emitState;
|
package/dist/esm/web.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { WebPlugin } from '@capacitor/core';
|
|
2
2
|
import { SplashStateMachine } from './state-machine';
|
|
3
|
-
const RECOVERY_TIMEOUT_MS = 15_000;
|
|
4
3
|
const PROGRESS_DELAY_MS = 1_000;
|
|
5
4
|
export class LottieSplashWeb extends WebPlugin {
|
|
6
5
|
stateMachine = new SplashStateMachine();
|
|
@@ -8,7 +7,6 @@ export class LottieSplashWeb extends WebPlugin {
|
|
|
8
7
|
resolvePlayback;
|
|
9
8
|
overlay;
|
|
10
9
|
progressTimer;
|
|
11
|
-
recoveryTimer;
|
|
12
10
|
async show(options) {
|
|
13
11
|
const state = this.stateMachine.current();
|
|
14
12
|
if (state.visible && state.presentationId)
|
|
@@ -104,7 +102,6 @@ export class LottieSplashWeb extends WebPlugin {
|
|
|
104
102
|
this.emitState();
|
|
105
103
|
if (!state.appReady) {
|
|
106
104
|
this.progressTimer = window.setTimeout(() => this.showLoadingRail(presentationId), PROGRESS_DELAY_MS);
|
|
107
|
-
this.recoveryTimer = window.setTimeout(() => this.showRecovery(presentationId), RECOVERY_TIMEOUT_MS);
|
|
108
105
|
}
|
|
109
106
|
}
|
|
110
107
|
catch {
|
|
@@ -127,31 +124,8 @@ export class LottieSplashWeb extends WebPlugin {
|
|
|
127
124
|
rail.append(segment);
|
|
128
125
|
this.overlay?.append(style, rail);
|
|
129
126
|
}
|
|
130
|
-
showRecovery(presentationId) {
|
|
131
|
-
let state;
|
|
132
|
-
try {
|
|
133
|
-
state = this.stateMachine.recovery(presentationId);
|
|
134
|
-
}
|
|
135
|
-
catch {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
if (state.presentationId !== presentationId || state.appReady)
|
|
139
|
-
return;
|
|
140
|
-
this.overlay?.querySelector('[data-splash-loading-rail]')?.remove();
|
|
141
|
-
const recovery = document.createElement('div');
|
|
142
|
-
recovery.style.cssText = 'position:absolute;bottom:12%;display:grid;gap:12px;place-items:center;color:inherit;font:500 16px system-ui;';
|
|
143
|
-
recovery.textContent = 'Still starting…';
|
|
144
|
-
const retry = document.createElement('button');
|
|
145
|
-
retry.type = 'button';
|
|
146
|
-
retry.textContent = 'Try again';
|
|
147
|
-
retry.onclick = () => window.location.reload();
|
|
148
|
-
recovery.append(retry);
|
|
149
|
-
this.overlay?.append(recovery);
|
|
150
|
-
this.emitState();
|
|
151
|
-
}
|
|
152
127
|
removeOverlay(presentationId) {
|
|
153
128
|
window.clearTimeout(this.progressTimer);
|
|
154
|
-
window.clearTimeout(this.recoveryTimer);
|
|
155
129
|
this.overlay?.remove();
|
|
156
130
|
this.overlay = undefined;
|
|
157
131
|
this.stateMachine.hidden(presentationId);
|