Skip to content

Commit

Permalink
Allow using lottie files for splashscreen (SDL2 bootstrap) (#2296)
Browse files Browse the repository at this point in the history
- add `--presplash-lottie` option to `build` command allowing to pass a
  json file
- add lottie to build.gradle if necessary
- use an xml layout file to define the view (added/removed appropriately
  by build.py)
- rework PythonActivity to select appropriate view at startup
  • Loading branch information
tshirtman authored Aug 12, 2020
1 parent 0c7efcd commit 524d621
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 56 deletions.
2 changes: 2 additions & 0 deletions doc/source/buildoptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ options (this list may not be exhaustive):
the application is loading.
- ``--presplash-color``: The presplash screen background color, of the
form ``#RRGGBB`` or a color name ``red``, ``green``, ``blue`` etc.
- ``--presplash-lottie``: use a lottie (json) file as a presplash animation. If
used, this will replace the static presplash image.
- ``--wakelock``: If the argument is included, the application will
prevent the device from sleeping.
- ``--window``: If the argument is included, the application will not
Expand Down
28 changes: 24 additions & 4 deletions pythonforandroid/bootstraps/common/build/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,11 +326,28 @@ def make_package(args):
args.icon or default_icon,
join(res_dir, 'drawable/icon.png')
)

if get_bootstrap_name() != "service_only":
shutil.copy(
args.presplash or default_presplash,
join(res_dir, 'drawable/presplash.jpg')
)
lottie_splashscreen = join(res_dir, 'raw/splashscreen.json')
if args.presplash_lottie:
shutil.copy(
'templates/lottie.xml',
join(res_dir, 'layout/lottie.xml')
)
ensure_dir(join(res_dir, 'raw'))
shutil.copy(
args.presplash_lottie,
join(res_dir, 'raw/splashscreen.json')
)
else:
if exists(lottie_splashscreen):
remove(lottie_splashscreen)
remove(join(res_dir, 'layout/lottie.xml'))

shutil.copy(
args.presplash or default_presplash,
join(res_dir, 'drawable/presplash.jpg')
)

# If extra Java jars were requested, copy them into the libs directory
jars = []
Expand Down Expand Up @@ -623,6 +640,9 @@ def parse_args_and_make_package(args=None):
ap.add_argument('--presplash', dest='presplash',
help=('A jpeg file to use as a screen while the '
'application is loading.'))
ap.add_argument('--presplash-lottie', dest='presplash_lottie',
help=('A lottie (json) file to use as an animation while the '
'application is loading.'))
ap.add_argument('--presplash-color',
dest='presplash_color',
default='#000000',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,7 @@ dependencies {
compile '{{ depend }}'
{%- endfor %}
{%- endif %}
{% if args.presplash_lottie %}
implementation 'com.airbnb.android:lottie:3.4.0'
{%- endif %}
}
22 changes: 22 additions & 0 deletions pythonforandroid/bootstraps/common/build/templates/lottie.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>

<com.airbnb.lottie.LottieAnimationView
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerInside"
android:layout_weight="4"
app:lottie_autoPlay="true"
app:lottie_loop="true"
app:lottie_rawRes="@raw/splashscreen"
/>
</LinearLayout>

Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
import android.util.Log;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import android.content.res.Resources.NotFoundException;

import org.libsdl.app.SDLActivity;

Expand Down Expand Up @@ -61,7 +63,7 @@ protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "Did super onCreate");

this.mActivity = this;
this.showLoadingScreen();
this.showLoadingScreen(this.getLoadingScreen());

new UnpackFilesTask().execute(getAppRoot());
}
Expand Down Expand Up @@ -121,7 +123,7 @@ protected void onPostExecute(String result) {
// removed the loading screen. However, we still need it to
// show until the app is ready to render, so pop it back up
// on top of the SDL view.
mActivity.showLoadingScreen();
mActivity.showLoadingScreen(getLoadingScreen());

String app_root_dir = getAppRoot();
if (getIntent() != null && getIntent().getAction() != null &&
Expand Down Expand Up @@ -338,6 +340,7 @@ public static void stop_service() {

/** Loading screen view **/
public static ImageView mImageView = null;
public static View mLottieView = null;
/** Whether main routine/actual app has started yet **/
protected boolean mAppConfirmedActive = false;
/** Timer for delayed loading screen removal. **/
Expand Down Expand Up @@ -404,11 +407,11 @@ public void run() {
public void removeLoadingScreen() {
runOnUiThread(new Runnable() {
public void run() {
if (PythonActivity.mImageView != null &&
PythonActivity.mImageView.getParent() != null) {
((ViewGroup)PythonActivity.mImageView.getParent()).removeView(
PythonActivity.mImageView);
PythonActivity.mImageView = null;
View view = mLottieView != null ? mLottieView : mImageView;
if (view != null && view.getParent() != null) {
((ViewGroup)view.getParent()).removeView(view);
mLottieView = null;
mImageView = null;
}
}
});
Expand All @@ -430,65 +433,97 @@ public String getEntryPoint(String search_dir) {
return "main.py";
}

protected void showLoadingScreen() {
protected void showLoadingScreen(View view) {
try {
if (mLayout == null) {
setContentView(view);
} else if (view.getParent() == null) {
mLayout.addView(view);
}
} catch (IllegalStateException e) {
// The loading screen can be attempted to be applied twice if app
// is tabbed in/out, quickly.
// (Gives error "The specified child already has a parent.
// You must call removeView() on the child's parent first.")
}
}

protected View getLoadingScreen() {
// load the bitmap
// 1. if the image is valid and we don't have layout yet, assign this bitmap
// as main view.
// 2. if we have a layout, just set it in the layout.
// 3. If we have an mImageView already, then do nothing because it will have
// already been made the content view or added to the layout.

if (mImageView == null) {
int presplashId = this.resourceManager.getIdentifier("presplash", "drawable");
InputStream is = this.getResources().openRawResource(presplashId);
Bitmap bitmap = null;
if (mLottieView != null || mImageView != null) {
// we already have a splash screen
return mLottieView != null ? mLottieView : mImageView;
}

// first try to load the lottie one
try {
mLottieView = getLayoutInflater().inflate(
this.resourceManager.getIdentifier("lottie", "layout"),
mLayout,
false
);
try {
bitmap = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
} catch (IOException e) {};
if (mLayout == null) {
setContentView(mLottieView);
} else if (PythonActivity.mLottieView.getParent() == null) {
mLayout.addView(mLottieView);
}
} catch (IllegalStateException e) {
// The loading screen can be attempted to be applied twice if app
// is tabbed in/out, quickly.
// (Gives error "The specified child already has a parent.
// You must call removeView() on the child's parent first.")
}

mImageView = new ImageView(this);
mImageView.setImageBitmap(bitmap);

/*
* Set the presplash loading screen background color
* https://developer.android.com/reference/android/graphics/Color.html
* Parse the color string, and return the corresponding color-int.
* If the string cannot be parsed, throws an IllegalArgumentException exception.
* Supported formats are: #RRGGBB #AARRGGBB or one of the following names:
* 'red', 'blue', 'green', 'black', 'white', 'gray', 'cyan', 'magenta', 'yellow',
* 'lightgray', 'darkgray', 'grey', 'lightgrey', 'darkgrey', 'aqua', 'fuchsia',
* 'lime', 'maroon', 'navy', 'olive', 'purple', 'silver', 'teal'.
*/
String backgroundColor = resourceManager.getString("presplash_color");
if (backgroundColor != null) {
try {
mImageView.setBackgroundColor(Color.parseColor(backgroundColor));
} catch (IllegalArgumentException e) {}
}
mImageView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT));
mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
return mLottieView;
}
catch (NotFoundException e) {
Log.v("SDL", "couldn't find lottie layout or animation, trying static splash");
}

// try to load the static image then
int presplashId = this.resourceManager.getIdentifier("presplash", "drawable");
InputStream is = this.getResources().openRawResource(presplashId);
Bitmap bitmap = null;
try {
if (mLayout == null) {
setContentView(mImageView);
} else if (PythonActivity.mImageView.getParent() == null) {
mLayout.addView(mImageView);
}
} catch (IllegalStateException e) {
// The loading screen can be attempted to be applied twice if app
// is tabbed in/out, quickly.
// (Gives error "The specified child already has a parent.
// You must call removeView() on the child's parent first.")
bitmap = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
} catch (IOException e) {};
}

mImageView = new ImageView(this);
mImageView.setImageBitmap(bitmap);

/*
* Set the presplash loading screen background color
* https://developer.android.com/reference/android/graphics/Color.html
* Parse the color string, and return the corresponding color-int.
* If the string cannot be parsed, throws an IllegalArgumentException exception.
* Supported formats are: #RRGGBB #AARRGGBB or one of the following names:
* 'red', 'blue', 'green', 'black', 'white', 'gray', 'cyan', 'magenta', 'yellow',
* 'lightgray', 'darkgray', 'grey', 'lightgrey', 'darkgrey', 'aqua', 'fuchsia',
* 'lime', 'maroon', 'navy', 'olive', 'purple', 'silver', 'teal'.
*/
String backgroundColor = resourceManager.getString("presplash_color");
if (backgroundColor != null) {
try {
mImageView.setBackgroundColor(Color.parseColor(backgroundColor));
} catch (IllegalArgumentException e) {}
}
mImageView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT));
mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
return mImageView;
}

@Override
protected void onPause() {
if (this.mWakeLock != null && mWakeLock.isHeld()) {
Expand Down

0 comments on commit 524d621

Please sign in to comment.