Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animate splash screen #17477

Merged
merged 30 commits into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e5341e7
Switch Android to promises queue + remove fade
zoontek Apr 15, 2023
cd83444
Animate splash screen in JS
zoontek Apr 15, 2023
6bd38c8
Generate new assets (previous were 1px off)
zoontek Apr 15, 2023
c22765a
Add iOS support
zoontek Apr 15, 2023
a47a79d
Tweak the animation
zoontek Apr 15, 2023
76e8729
Export to styles
zoontek Apr 15, 2023
75a8ca3
Tweak values
zoontek Apr 15, 2023
29785af
Tweak values
zoontek Apr 15, 2023
d24eeee
Don't apply negative margin on other platforms than Android
zoontek Apr 16, 2023
b9f849b
Tweak animations values
zoontek Apr 16, 2023
809f5c9
Add a comment
zoontek Apr 16, 2023
4842634
Don't run staggering animations
zoontek Apr 16, 2023
6d2f66e
Handle visibility in parent component
zoontek Apr 16, 2023
78196fa
Use @color/status_bar_background
zoontek Apr 16, 2023
d95c1a0
Remove Platform.OS usage
zoontek Apr 27, 2023
8e86ce1
Publish shrinking animation
zoontek Apr 27, 2023
3868784
Fix linting
zoontek Apr 28, 2023
f956e8a
AnimatedSplashScreen -> SplashScreenHider
zoontek Apr 28, 2023
be9b588
Remove Platform usage
zoontek May 4, 2023
0def2a1
Fix linting
zoontek May 4, 2023
34a305b
Fix CSP issue by inlining CSS + logo and hide once the bundle is loaded
zoontek May 10, 2023
961f452
Remove extra logic
zoontek May 10, 2023
710f955
Run prettier
zoontek May 14, 2023
aa6010f
Resolve with a Promise
zoontek May 14, 2023
eba16b2
Replace .web by .native
zoontek May 14, 2023
cc6928a
Remove RCT_REMAP_METHOD
zoontek May 14, 2023
01711d2
Fix splash screen re-rendered on login / logout
zoontek May 14, 2023
5e51f05
Merge branch 'main' into animate-splash-screen
zoontek May 17, 2023
2b892bf
Merge branch 'main' into animate-splash-screen
zoontek May 17, 2023
3edb908
Switch to reanimated
zoontek May 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line caused a bug to arise, resulting in a regression in #19777. We should have verified that the storybook builds and runs successfully.

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