Skip to content

Commit

Permalink
Merge pull request #17477 from zoontek/animate-splash-screen
Browse files Browse the repository at this point in the history
Animate splash screen
  • Loading branch information
roryabraham authored May 18, 2023
2 parents 78ec531 + 3edb908 commit 1381237
Show file tree
Hide file tree
Showing 31 changed files with 335 additions and 192 deletions.
1 change: 1 addition & 0 deletions __mocks__/react-native.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jest.doMock('react-native', () => {
BootSplash: {
getVisibilityStatus: jest.fn(),
hide: jest.fn(),
navigationBarHeight: 0,
},
StartupTimer: {stop: jest.fn()},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import androidx.annotation.NonNull;
import com.expensify.chat.R;

public class BootSplashDialog extends Dialog {

Expand All @@ -27,7 +26,6 @@ protected void onCreate(Bundle savedInstanceState) {

if (window != null) {
window.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
window.setWindowAnimations(R.style.Theme_SplashScreen_Dialog);
}

super.onCreate(savedInstanceState);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.expensify.chat.bootsplash;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Build;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.window.SplashScreen;
import android.window.SplashScreenView;
Expand All @@ -18,13 +22,17 @@
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.PixelUtil;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

@ReactModule(name = BootSplashModule.NAME)
public class BootSplashModule extends ReactContextBaseJavaModule {

public static final String NAME = "BootSplash";
private static final BootSplashQueue<Promise> mPromiseQueue = new BootSplashQueue<>();
private static boolean mShouldKeepOnScreen = true;

@Nullable
Expand All @@ -39,6 +47,24 @@ public String getName() {
return NAME;
}

@Override
public Map<String, Object> getConstants() {
final HashMap<String, Object> constants = new HashMap<>();
final Context context = getReactApplicationContext();
final Resources resources = context.getResources();

@SuppressLint({"DiscouragedApi", "InternalInsetResource"}) final int heightResId =
resources.getIdentifier("navigation_bar_height", "dimen", "android");

final float height =
heightResId > 0 && !ViewConfiguration.get(context).hasPermanentMenuKey()
? Math.round(PixelUtil.toDIPFromPixel(resources.getDimensionPixelSize(heightResId)))
: 0;

constants.put("navigationBarHeight", height);
return constants;
}

protected static void init(@Nullable final Activity activity) {
if (activity == null) {
FLog.w(ReactConstants.TAG, NAME + ": Ignored initialization, current activity is null.");
Expand Down Expand Up @@ -68,13 +94,14 @@ public boolean onPreDraw() {
});

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// This is not called on Android 12 when activity is started using Android studio / notifications
// This is not called on Android 12 when activity is started using intent
// (Android studio / CLI / notification / widget…)
activity
.getSplashScreen()
.setOnExitAnimationListener(new SplashScreen.OnExitAnimationListener() {
@Override
public void onSplashScreenExit(@NonNull SplashScreenView view) {
view.remove(); // Remove it without animation
view.remove(); // Remove it immediately, without animation
}
});
}
Expand All @@ -96,35 +123,39 @@ public void run() {
});
}

private void waitAndHide() {
final Timer timer = new Timer();
private void clearPromiseQueue() {
while (!mPromiseQueue.isEmpty()) {
Promise promise = mPromiseQueue.shift();

timer.schedule(new TimerTask() {
@Override
public void run() {
hide();
timer.cancel();
}
}, 250);
if (promise != null)
promise.resolve(true);
}
}

@ReactMethod
public void hide() {
private void hideAndClearPromiseQueue() {
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
final Activity activity = getReactApplicationContext().getCurrentActivity();

if (activity == null || activity.isFinishing()) {
waitAndHide();
return;
}
if (mShouldKeepOnScreen || activity == null || activity.isFinishing()) {
final Timer timer = new Timer();

if (mDialog != null) {
timer.schedule(new TimerTask() {
@Override
public void run() {
timer.cancel();
hideAndClearPromiseQueue();
}
}, 100);
} else if (mDialog == null) {
clearPromiseQueue();
} else {
mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
mDialog = null;
clearPromiseQueue();
}
});

Expand All @@ -134,8 +165,14 @@ public void onDismiss(DialogInterface dialog) {
});
}

@ReactMethod
public void hide(final Promise promise) {
mPromiseQueue.push(promise);
hideAndClearPromiseQueue();
}

@ReactMethod
public void getVisibilityStatus(final Promise promise) {
promise.resolve(mDialog != null ? "visible" : "hidden");
promise.resolve(mShouldKeepOnScreen || mDialog != null ? "visible" : "hidden");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.expensify.chat.bootsplash;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Vector;

/**
* Represents a first-in-first-out (FIFO) thread safe queue of objects.
* Its source code is based on Java internal <code>Stack</code>.
*/
public class BootSplashQueue<T> extends Vector<T> {

@Nullable
public synchronized T shift() {
if (size() == 0) {
return null;
}

T item = elementAt(0);
removeElementAt(0);

return item;
}

public void push(@NonNull T item) {
addElement(item);
}
}
7 changes: 0 additions & 7 deletions android/app/src/main/res/anim/fade_out.xml

This file was deleted.

Binary file modified android/app/src/main/res/mipmap-hdpi/bootsplash_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-mdpi/bootsplash_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xhdpi/bootsplash_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxhdpi/bootsplash_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxxhdpi/bootsplash_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions android/app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<resources>
<color name="bootsplash_background">#03D47C</color>
<color name="status_bar_background">#061B09</color>
<color name="white">#FFFFFF</color>
<color name="accent">#03D47C</color>
<color name="dark">#0b1b34</color>
Expand Down
10 changes: 2 additions & 8 deletions android/app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<!-- Base application theme. Applied to all Android versions -->
<style name="BaseAppTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:colorEdgeEffect">@color/gray4</item>
<item name="android:statusBarColor">#061B09</item>
<item name="android:statusBarColor">@color/status_bar_background</item>
<item name="colorAccent">@color/accent</item>
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="popupTheme">@style/AppTheme.Popup</item>
Expand Down Expand Up @@ -65,19 +65,13 @@
<item name="android:windowBackground">@drawable/bootsplash</item>
</style>

<style name="Theme.SplashScreen.Dialog">
<item name="android:windowEnterAnimation">@null</item>
<item name="android:windowExitAnimation">@anim/fade_out</item>
</style>

<style name="Theme.SplashScreen" parent="Theme.SplashScreen.Common">
<item name="windowSplashScreenAnimatedIcon">@android:drawable/sym_def_app_icon</item>
<item name="windowSplashScreenBackground">?android:colorBackground</item>
</style>

<style name="BootTheme" parent="Theme.SplashScreen">
<item name="android:navigationBarColor">@color/bootsplash_background</item>
<item name="android:statusBarColor">@color/bootsplash_background</item>
<item name="android:statusBarColor">@color/status_bar_background</item>
<item name="windowSplashScreenAnimatedIcon">@mipmap/bootsplash_logo</item>
<item name="windowSplashScreenBackground">@color/bootsplash_background</item>
</style>
Expand Down
1 change: 1 addition & 0 deletions assets/images/new-expensify-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 2 additions & 18 deletions config/webpack/webpack.common.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const path = require('path');
const fs = require('fs');
const {IgnorePlugin, DefinePlugin, ProvidePlugin, EnvironmentPlugin} = require('webpack');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const dotenv = require('dotenv');
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
const HtmlInlineScriptPlugin = require('html-inline-script-webpack-plugin');
const FontPreloadPlugin = require('webpack-font-preload-plugin');
const CustomVersionFilePlugin = require('./CustomVersionFilePlugin');

Expand Down Expand Up @@ -52,7 +52,6 @@ const webpackConfig = ({envFile = '.env', platform = 'web'}) => ({
devtool: 'source-map',
entry: {
main: ['babel-polyfill', './index.js'],
splash: ['./web/splash/splash.js'],
},
output: {
filename: '[name]-[contenthash].bundle.js',
Expand All @@ -73,11 +72,9 @@ const webpackConfig = ({envFile = '.env', platform = 'web'}) => ({
new HtmlWebpackPlugin({
template: 'web/index.html',
filename: 'index.html',
splashLogo: fs.readFileSync(path.resolve(__dirname, `../../assets/images/new-expensify${mapEnvToLogoSuffix(envFile)}.svg`), 'utf-8'),
usePolyfillIO: platform === 'web',
}),
new HtmlInlineScriptPlugin({
scriptMatchPattern: [/splash.+[.]js$/],
}),
new FontPreloadPlugin({
extensions: ['woff2'],
}),
Expand Down Expand Up @@ -173,18 +170,6 @@ const webpackConfig = ({envFile = '.env', platform = 'web'}) => ({
},
],
},
{
test: /splash.css$/i,
use: [
{
loader: 'style-loader',
options: {
insert: 'head',
injectType: 'singletonStyleTag',
},
},
],
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
Expand All @@ -201,7 +186,6 @@ const webpackConfig = ({envFile = '.env', platform = 'web'}) => ({
},
resolve: {
alias: {
logo$: path.resolve(__dirname, `../../assets/images/new-expensify${mapEnvToLogoSuffix(envFile)}.svg`),
'react-native-config': 'react-web-config',
'react-native$': '@expensify/react-native-web',
'react-native-web': '@expensify/react-native-web',
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1381237

Please sign in to comment.