Skip to content

Commit

Permalink
Merge splash screen animation and progress bar patches onto Capacitor…
Browse files Browse the repository at this point in the history
… 5 (#10)

* Migrate animated splashscreen to splashscreen 2.4.0

* Bump package.json to 4.2.0-fork.0

* bump forked version in package.json to 5.0.8

* Include README changes after build

---------

Co-authored-by: fteng <[email protected]>
Co-authored-by: Fei Teng <[email protected]>
Co-authored-by: Benjamin Mo <[email protected]>
  • Loading branch information
4 people authored Oct 9, 2024
1 parent 33fcc9e commit 2d685c6
Show file tree
Hide file tree
Showing 11 changed files with 369 additions and 33 deletions.
68 changes: 66 additions & 2 deletions splash-screen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,39 @@ For iOS, `iosSpinnerStyle` has the following options:

To set the color of the spinner use `spinnerColor`, values are either `#RRGGBB` or `#RRGGBBAA`.

## Animated Splash Screen
### iOS
If you want to have an animated splash screen on iOS, set `animated` to `true`, and `launchAnimationDuration` to the desired animation length in milliseconds in your [Capacitor configuration file] (https://capacitorjs.com/docs/config).

Once done, you can add the splash screen assets into your app's `Assets.xcassets` file inside a folder called `Splash`. Each frame should be placed in order, with `Splash_xx` (where xx is the number it belongs to in sequence, e.g. splash_1.png, splash_2.png, ...).

## Android
If you want to have an animated splash screen on Android, create a `splash.xml` in your app's `res/drawable` folder with an animation list showing the location and duration of each frame.

For example:

```
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
<item android:duration="22" android:drawable="@drawable/splash_1">
</item>
<item android:duration="22" android:drawable="@drawable/splash_2">
</item>
<item android:duration="22" android:drawable="@drawable/splash_3">
</item>
<item android:duration="22" android:drawable="@drawable/splash_4">
</item>
<item android:duration="22" android:drawable="@drawable/splash_5">
</item>
<item android:duration="22" android:drawable="@drawable/splash_6">
</item>
<item android:duration="22" android:drawable="@drawable/splash_7">
</item>
</animation-list>
```

Be sure to correctly scale your images as well, as not scaling them may cause significant performance degradation when loading your app.

## Configuration

<docgen-config>
Expand All @@ -110,6 +143,8 @@ These config values are available:
| **`splashImmersive`** | <code>boolean</code> | Hide the status bar and the software navigation buttons on the Splash Screen. Doesn't work on launch when using the Android 12 API. Only available on Android. | | 1.0.0 |
| **`layoutName`** | <code>string</code> | If `useDialog` is set to true, configure the Dialog layout. If `useDialog` is not set or false, use a layout instead of the ImageView. Doesn't work on launch when using the Android 12 API. Only available on Android. | | 1.1.0 |
| **`useDialog`** | <code>boolean</code> | Use a Dialog instead of an ImageView. If `layoutName` is not configured, it will use a layout that uses the splash image as background. Doesn't work on launch when using the Android 12 API. Only available on Android. | | 1.1.0 |
| **`animated`** | <code>boolean</code> | Animate the splash screen using a series of image files. | | 1.2.3 |
| **`launchAnimationDuration`** | <code>number</code> | Play the multiple frames across the amount of milliseconds specified. | | 1.2.3 |

### Examples

Expand All @@ -132,7 +167,9 @@ In `capacitor.config.json`:
"splashFullScreen": true,
"splashImmersive": true,
"layoutName": "launch_screen",
"useDialog": true
"useDialog": true,
"animated": true,
"launchAnimationDuration": 3000
}
}
}
Expand All @@ -141,7 +178,7 @@ In `capacitor.config.json`:
In `capacitor.config.ts`:

```ts
/// <reference types="@capacitor/splash-screen" />
/// <reference types="@freelancercom/splash-screen" />

import { CapacitorConfig } from '@capacitor/cli';

Expand All @@ -162,6 +199,8 @@ const config: CapacitorConfig = {
splashImmersive: true,
layoutName: "launch_screen",
useDialog: true,
animated: true,
launchAnimationDuration: 3000,
},
},
};
Expand Down Expand Up @@ -198,6 +237,7 @@ This plugin will use the following project variables (defined in your app's `var
<docgen-index>

* [`show(...)`](#show)
* [`updateProgress(...)`](#updateprogress)
* [`hide(...)`](#hide)
* [Interfaces](#interfaces)

Expand All @@ -223,6 +263,23 @@ Show the splash screen
--------------------


### updateProgress(...)

```typescript
updateProgress(options: UpdateProgressOptions) => Promise<void>
```

Update progress of splash screen

| Param | Type |
| ------------- | ----------------------------------------------------------------------- |
| **`options`** | <code><a href="#updateprogressoptions">UpdateProgressOptions</a></code> |

**Since:** 1.2.3

--------------------


### hide(...)

```typescript
Expand Down Expand Up @@ -253,6 +310,13 @@ Hide the splash screen
| **`showDuration`** | <code>number</code> | How long to show the splash screen when autoHide is enabled (in ms) | <code>3000</code> | 1.0.0 |


#### UpdateProgressOptions

| Prop | Type | Description | Since |
| -------------- | ------------------- | ------------------------------- | ----- |
| **`progress`** | <code>number</code> | Set percentage of progress bar. | 1.2.3 |


#### HideOptions

| Prop | Type | Description | Default | Since |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
Expand Down Expand Up @@ -41,6 +42,7 @@ public class SplashScreen {
private Dialog dialog;
private View splashImage;
private ProgressBar spinnerBar;
private ProgressBar progressBar;
private WindowManager windowManager;
private boolean isVisible = false;
private boolean isHiding = false;
Expand Down Expand Up @@ -92,8 +94,10 @@ public void showOnLaunch(final AppCompatActivity activity) {
* @param activity
* @param settings Settings used to show the Splash Screen
*/
private void showWithAndroid12API(final AppCompatActivity activity, final SplashScreenSettings settings) {
if (activity == null || activity.isFinishing()) return;
private void showWithAndroid12API(final AppCompatActivity activity, final SplashScreenSettings settings) throws Exception {
if (config.getAnimated()) {
throw new Exception("Android 12 API doesn't support animation");
}

activity.runOnUiThread(
() -> {
Expand Down Expand Up @@ -365,6 +369,14 @@ private void buildViews() {
spinnerBar.setIndeterminateTintList(colorStateList);
}
}

if (progressBar == null) {
// Create a horizontal progress bar.
progressBar = new ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal);

// Ensure the progress filling is gray.
progressBar.setProgressTintList(ColorStateList.valueOf(Color.GRAY));
}
}

@SuppressWarnings("deprecation")
Expand All @@ -373,7 +385,17 @@ private void legacyStopFlickers(ImageView imageView) {
}

private Drawable getSplashDrawable() {
int splashId = context.getResources().getIdentifier(config.getResourceName(), "drawable", context.getPackageName());
int splashId;
// Disables animations on older versions of Android as it may cause OOM issues.
if (config.getAnimated() == true && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
// Uses the first image in the animation sequence in the aforementioned case.
splashId = context.getResources().getIdentifier(config.getResourceName() + "_0", "drawable", context.getPackageName());
} else {
// In all other cases, use the splash resource.
// In the case it is an animation, it would be an Animation List XML which would play in sequence.
// Otherwise, it should be just a standard image file.
splashId = context.getResources().getIdentifier(config.getResourceName(), "drawable", context.getPackageName());
}
try {
Drawable drawable = context.getResources().getDrawable(splashId, context.getTheme());
return drawable;
Expand Down Expand Up @@ -484,17 +506,19 @@ public void onAnimationStart(Animator animator) {}
}
}

splashImage.setAlpha(0f);
if (splashImage != null) {
splashImage.setAlpha(0f);

splashImage
.animate()
.alpha(1f)
.setInterpolator(new LinearInterpolator())
.setDuration(settings.getFadeInDuration())
.setListener(listener)
.start();
splashImage
.animate()
.alpha(1f)
.setInterpolator(new LinearInterpolator())
.setDuration(settings.getFadeInDuration())
.setListener(listener)
.start();

splashImage.setVisibility(View.VISIBLE);
splashImage.setVisibility(View.VISIBLE);
}

if (spinnerBar != null) {
spinnerBar.setVisibility(View.INVISIBLE);
Expand All @@ -503,10 +527,13 @@ public void onAnimationStart(Animator animator) {}
windowManager.removeView(spinnerBar);
}

params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
// Copy common Window Layout Params.
WindowManager.LayoutParams spinnerBarParams = params;

windowManager.addView(spinnerBar, params);
spinnerBarParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
spinnerBarParams.width = WindowManager.LayoutParams.WRAP_CONTENT;

windowManager.addView(spinnerBar, spinnerBarParams);

if (config.isShowSpinner()) {
spinnerBar.setAlpha(0f);
Expand All @@ -521,10 +548,53 @@ public void onAnimationStart(Animator animator) {}
spinnerBar.setVisibility(View.VISIBLE);
}
}

// If the progress bar is available.
if (progressBar != null) {
// Make it invisible so it can be set as visible when required by updateProgress.
progressBar.setVisibility(View.INVISIBLE);

// Remove any existing progress bar in the case it is already attached to a parent.
if (progressBar.getParent() != null) {
windowManager.removeView(progressBar);
}

// Copy common Window Layout Params.
WindowManager.LayoutParams progressBarParams = params;

// Set the dimensions of the progress bar.
progressBarParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
progressBarParams.width = activity.getResources().getDisplayMetrics().widthPixels / 2;

// Put the progress bar just a bit away from the center of the screen so there's room for a logo.
progressBarParams.y = (int) ((activity.getResources().getDisplayMetrics().heightPixels / 2) * 0.25);

// Add the progress bar.
windowManager.addView(progressBar, progressBarParams);
}
}
);
}

// This function when called will automatically add a progress bar to the splash screen
// if it is not available yet, and update the progress bar's progress.
public void updateProgress(final float percentage) {
// Show the progress bar if it is currently invisible.
if (progressBar.getVisibility() == View.INVISIBLE) {
Handler mainHandler = new Handler(context.getMainLooper());

// Updating UI from main thread would cause issues hence a Handler is used.
// This is similar to the approach used by functions `show` and `hide`.
mainHandler.post(
() -> {
progressBar.setVisibility(View.VISIBLE);
}
);
}
// Set the progress of the progress bar.
progressBar.setProgress((int) percentage);
}

@SuppressWarnings("deprecation")
private void legacyImmersive() {
final int flags =
Expand Down Expand Up @@ -606,15 +676,27 @@ public void onAnimationRepeat(Animator animator) {}
spinnerBar.animate().alpha(0).setInterpolator(new LinearInterpolator()).setDuration(fadeOutDuration).start();
}

splashImage.setAlpha(1f);
// In the case the progress bar has been added.
if (progressBar != null) {
// Make the progress bar invisible.
progressBar.setAlpha(1f);

// Start the animation to make it invisible and blend into the layer below.
progressBar.animate().alpha(0).setInterpolator(new LinearInterpolator()).setDuration(fadeOutDuration).start();
}

if (splashImage != null) {
splashImage.setAlpha(1f);

splashImage
.animate()
.alpha(0)
.setInterpolator(new LinearInterpolator())
.setDuration(fadeOutDuration)
.setListener(listener)
.start();
}

splashImage
.animate()
.alpha(0)
.setInterpolator(new LinearInterpolator())
.setDuration(fadeOutDuration)
.setListener(listener)
.start();
}
);
}
Expand Down Expand Up @@ -673,6 +755,18 @@ private void tearDown(boolean removeSpinner) {
splashImage.setVisibility(View.INVISIBLE);

windowManager.removeView(splashImage);
ImageView imageView = (ImageView) splashImage;
imageView.setImageDrawable(null);
splashImage = null;
}

// In the case that the progress bar doesn't exist.
if (progressBar != null && progressBar.getParent() != null) {
// Make it invisible.
progressBar.setVisibility(View.INVISIBLE);

// Remove the progress bar entirely.
windowManager.removeView(progressBar);
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && config.isFullScreen() || config.isImmersive()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public class SplashScreenConfig {
private ScaleType scaleType = ScaleType.FIT_XY;
private boolean usingDialog = false;
private String layoutName;
// This is used to determine whether a series of images should be used to animate the splash screen.
private boolean animated = false;

public Integer getBackgroundColor() {
return backgroundColor;
Expand Down Expand Up @@ -119,6 +121,16 @@ public void setLayoutName(String layoutName) {
this.layoutName = layoutName;
}

// This is used to read the config for whether the splash screen should be animated.
public boolean getAnimated() {
return animated;
}

// This is used to set the config for whether the splash screen should be animated.
public void setAnimated(Boolean animated) {
this.animated = animated;
}

public Integer getLaunchFadeOutDuration() {
return launchFadeOutDuration;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ public void error() {
);
}

// Show and update progress of progress bar.
@PluginMethod
public void updateProgress(final PluginCall call) {
splashScreen.updateProgress(call.getFloat("progress", (float) 0));
call.resolve();
}

@PluginMethod
public void hide(PluginCall call) {
if (config.isUsingDialog()) {
Expand Down Expand Up @@ -161,6 +168,12 @@ private SplashScreenConfig getSplashScreenConfig() {
config.setLayoutName(getConfig().getString("layoutName"));
}

// This reads the Capacitor config for whether the splash screen should be animated.
// Defaults to false.
Boolean animated = getConfig().getBoolean("animated", false);
// Set the config that the splash screen should/should not be animated.
config.setAnimated(animated);

return config;
}
}
Loading

0 comments on commit 2d685c6

Please sign in to comment.