Skip to content

Commit 4162390

Browse files
authored
Apply color animations on tab-switching (Android-only) (#7975)
1 parent 9e99874 commit 4162390

File tree

24 files changed

+440
-278
lines changed

24 files changed

+440
-278
lines changed

lib/android/app/src/main/java/com/reactnativenavigation/FeatureToggles.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ package com.reactnativenavigation
33
import androidx.annotation.VisibleForTesting
44

55
enum class RNNToggles {
6-
TOP_BAR_COLOR_ANIMATION,
6+
TOP_BAR_COLOR_ANIMATION__PUSH,
7+
TOP_BAR_COLOR_ANIMATION__TABS,
78
}
89

910
private val ToggleDefaults = mapOf(
10-
RNNToggles.TOP_BAR_COLOR_ANIMATION to false
11+
RNNToggles.TOP_BAR_COLOR_ANIMATION__PUSH to false,
12+
RNNToggles.TOP_BAR_COLOR_ANIMATION__TABS to false,
1113
)
1214

1315
object RNNFeatureToggles {

lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import android.annotation.TargetApi;
44
import android.content.Intent;
55
import android.content.res.Configuration;
6-
import android.graphics.Color;
76
import android.os.Build;
87
import android.os.Bundle;
98
import android.view.KeyEvent;
@@ -12,8 +11,8 @@
1211
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
1312
import com.facebook.react.modules.core.PermissionAwareActivity;
1413
import com.facebook.react.modules.core.PermissionListener;
15-
import com.reactnativenavigation.options.Options;
1614
import com.reactnativenavigation.viewcontrollers.overlay.OverlayManager;
15+
import com.reactnativenavigation.viewcontrollers.statusbar.StatusBarPresenter;
1716
import com.reactnativenavigation.viewcontrollers.viewcontroller.RootPresenter;
1817
import com.reactnativenavigation.react.JsDevReloadHandler;
1918
import com.reactnativenavigation.react.ReactGateway;
@@ -51,6 +50,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
5150
navigator.bindViews();
5251
getReactGateway().onActivityCreated(this);
5352
setBackPressedCallback();
53+
StatusBarPresenter.Companion.init(this);
5454
}
5555

5656
@Override

lib/android/app/src/main/java/com/reactnativenavigation/utils/ColorUtils.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.reactnativenavigation.utils;
22

3+
import android.graphics.Color;
4+
35
public class ColorUtils {
46
public static double[] colorToLAB(int color) {
57
final double[] result = new double[3];
@@ -10,4 +12,13 @@ public static double[] colorToLAB(int color) {
1012
public static int labToColor(double[] lab) {
1113
return androidx.core.graphics.ColorUtils.LABToColor(lab[0], lab[1], lab[2]);
1214
}
15+
16+
public static boolean isColorLight(int color) {
17+
double darkness = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;
18+
return darkness < 0.5;
19+
}
20+
21+
public static int setAlpha(int color, int alpha) {
22+
return (color & 0x00FFFFFF) | (alpha << 24);
23+
}
1324
}

lib/android/app/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package com.reactnativenavigation.utils
33
import android.app.Activity
44
import android.graphics.Color
55
import android.graphics.Rect
6-
import android.os.Build
76
import android.view.View
87
import android.view.Window
98
import androidx.annotation.ColorInt
@@ -16,7 +15,6 @@ import kotlin.math.ceil
1615

1716
object SystemUiUtils {
1817
private const val STATUS_BAR_HEIGHT_M = 24
19-
private const val STATUS_BAR_HEIGHT_L = 25
2018
internal const val STATUS_BAR_HEIGHT_TRANSLUCENCY = 0.65f
2119
private var statusBarHeight = -1
2220
var navigationBarDefaultColor = -1
@@ -38,7 +36,7 @@ object SystemUiUtils {
3836
val contentViewTop = contentView.top
3937
abs(contentViewTop - statusBarHeight)
4038
}
41-
} ?: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) STATUS_BAR_HEIGHT_M else STATUS_BAR_HEIGHT_L
39+
} ?: STATUS_BAR_HEIGHT_M
4240
statusBarHeight
4341
}
4442
return res
@@ -77,8 +75,6 @@ object SystemUiUtils {
7775

7876
@JvmStatic
7977
fun setStatusBarColorScheme(window: Window?, view: View, isDark: Boolean) {
80-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return
81-
8278
window?.let {
8379
WindowInsetsControllerCompat(window, view).isAppearanceLightStatusBars = isDark
8480
// Workaround: on devices with api 30 status bar icons flickers or get hidden when removing view
@@ -121,18 +117,17 @@ object SystemUiUtils {
121117
@ColorInt color: Int,
122118
translucent: Boolean
123119
) {
124-
val opaqueColor =
125-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
126-
Color.BLACK
127-
} else {
128-
val colorAlpha = Color.alpha(color)
129-
val alpha = if (translucent && colorAlpha == 255) STATUS_BAR_HEIGHT_TRANSLUCENCY else colorAlpha/255.0f
130-
val red: Int = Color.red(color)
131-
val green: Int = Color.green(color)
132-
val blue: Int = Color.blue(color)
133-
Color.argb(ceil(alpha * 255).toInt(), red, green, blue)
134-
}
135-
window?.statusBarColor = opaqueColor
120+
val colorAlpha = Color.alpha(color)
121+
val alpha = if (translucent && colorAlpha == 255) STATUS_BAR_HEIGHT_TRANSLUCENCY else colorAlpha/255.0f
122+
val red: Int = Color.red(color)
123+
val green: Int = Color.green(color)
124+
val blue: Int = Color.blue(color)
125+
val opaqueColor = Color.argb(ceil(alpha * 255).toInt(), red, green, blue)
126+
setStatusBarColor(window, opaqueColor)
127+
}
128+
129+
fun setStatusBarColor(window: Window?, color: Int) {
130+
window?.statusBarColor = color
136131
}
137132

138133
@JvmStatic

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabPresenter.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
import androidx.annotation.NonNull;
1111

12-
import com.aurelhubert.ahbottomnavigation.AHTextView;
1312
import com.aurelhubert.ahbottomnavigation.notification.AHNotification;
1413
import com.reactnativenavigation.options.BottomTabOptions;
1514
import com.reactnativenavigation.options.DotIndicatorOptions;

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,11 @@ public void selectTab(final int newIndex) {
271271
private void selectTab(int newIndex, boolean enableSelectionHistory) {
272272
saveTabSelection(newIndex, enableSelectionHistory);
273273
tabsAttacher.onTabSelected(tabs.get(newIndex));
274-
getCurrentView().setVisibility(View.INVISIBLE);
274+
getCurrentChild().onDeselected();
275+
276+
ViewController<?> previouslyVisible = getCurrentChild();
275277
bottomTabs.setCurrentItem(newIndex, false);
276-
getCurrentView().setVisibility(View.VISIBLE);
277-
getCurrentChild().onViewWillAppear();
278-
getCurrentChild().onViewDidAppear();
278+
getCurrentChild().onSelected(previouslyVisible);
279279
}
280280

281281
private void saveTabSelection(int newIndex, boolean enableSelectionHistory) {
@@ -287,11 +287,6 @@ private void saveTabSelection(int newIndex, boolean enableSelectionHistory) {
287287
}
288288
}
289289

290-
@NonNull
291-
private ViewGroup getCurrentView() {
292-
return tabs.get(bottomTabs.getCurrentItem()).getView();
293-
}
294-
295290
public Animator getPushAnimation(Options appearingOptions) {
296291
return presenter.getPushAnimation(appearingOptions);
297292
}

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/component/ComponentPresenter.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package com.reactnativenavigation.viewcontrollers.component;
22

3+
import android.animation.Animator;
4+
5+
import androidx.annotation.NonNull;
6+
import androidx.annotation.Nullable;
7+
38
import com.reactnativenavigation.options.Options;
9+
import com.reactnativenavigation.viewcontrollers.statusbar.StatusBarPresenter;
410
import com.reactnativenavigation.views.component.ComponentLayout;
511

612
public class ComponentPresenter extends ComponentPresenterBase {
@@ -35,4 +41,16 @@ public void onConfigurationChanged(ComponentLayout view, Options options) {
3541
Options withDefault = options.withDefaultOptions(defaultOptions);
3642
setBackgroundColor(view, withDefault);
3743
}
44+
45+
@Nullable
46+
public Animator getStatusBarPushAnimation(@NonNull Options appearingOptions) {
47+
Options appearingOptionsWithDefault = appearingOptions.copy().withDefaultOptions(defaultOptions);
48+
return StatusBarPresenter.instance.getStatusBarPushAnimation(appearingOptionsWithDefault);
49+
}
50+
51+
@Nullable
52+
public Animator getStatusBarPopAnimation(@NonNull Options appearingOptions, @NonNull Options disappearingOptions) {
53+
Options appearingOptionsWithDefault = appearingOptions.copy().withDefaultOptions(defaultOptions);
54+
return StatusBarPresenter.instance.getStatusBarPopAnimation(appearingOptionsWithDefault, disappearingOptions);
55+
}
3856
}

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/component/ComponentViewController.java

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@
1717
import com.reactnativenavigation.utils.SystemUiUtils;
1818
import com.reactnativenavigation.viewcontrollers.child.ChildController;
1919
import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry;
20-
import com.reactnativenavigation.viewcontrollers.stack.statusbar.StatusBarController;
2120
import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter;
2221
import com.reactnativenavigation.viewcontrollers.viewcontroller.ReactViewCreator;
2322
import com.reactnativenavigation.viewcontrollers.viewcontroller.ScrollEventListener;
2423
import com.reactnativenavigation.views.component.ComponentLayout;
2524

26-
public class ComponentViewController extends ChildController<ComponentLayout> implements StatusBarController {
25+
public class ComponentViewController extends ChildController<ComponentLayout> {
2726
private final String componentName;
2827
private final ComponentPresenter presenter;
2928
private final ReactViewCreator viewCreator;
@@ -65,27 +64,16 @@ public void setDefaultOptions(Options defaultOptions) {
6564
presenter.setDefaultOptions(defaultOptions);
6665
}
6766

68-
@Override
69-
public StatusBarController getStatusBarController() {
70-
return this;
71-
}
72-
7367
@Nullable
7468
@Override
75-
public Animator getStatusBarPushAnimation(@NonNull Options appearingOptions) {
76-
if (super.presenter != null) {
77-
return super.presenter.getStatusBarPushAnimation(appearingOptions);
78-
}
79-
return null;
69+
public Animator getPushAnimations(Options appearingOptions) {
70+
return this.presenter.getStatusBarPushAnimation(appearingOptions);
8071
}
8172

8273
@Nullable
8374
@Override
84-
public Animator getStatusBarPopAnimation(@NonNull Options appearingOptions, @NonNull Options disappearingOptions) {
85-
if (super.presenter != null) {
86-
return super.presenter.getStatusBarPopAnimation(appearingOptions, disappearingOptions);
87-
}
88-
return null;
75+
public Animator getPopAnimations(Options appearingOptions, Options disappearingOptions) {
76+
return this.presenter.getStatusBarPopAnimation(appearingOptions, disappearingOptions);
8977
}
9078

9179
@Override

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
package com.reactnativenavigation.viewcontrollers.stack;
22

3+
import static com.reactnativenavigation.react.Constants.HARDWARE_BACK_BUTTON_ID;
4+
import static com.reactnativenavigation.utils.CollectionUtils.requireLast;
5+
import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour;
6+
import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.updateBottomMargin;
7+
import static com.reactnativenavigation.utils.ObjectUtils.perform;
8+
39
import android.app.Activity;
410
import android.content.res.Configuration;
511
import android.view.View;
612
import android.view.ViewGroup;
713

14+
import androidx.annotation.NonNull;
15+
import androidx.annotation.RestrictTo;
16+
import androidx.annotation.Size;
17+
import androidx.annotation.VisibleForTesting;
18+
import androidx.coordinatorlayout.widget.CoordinatorLayout;
19+
import androidx.viewpager.widget.ViewPager;
20+
821
import com.facebook.react.ReactRootView;
922
import com.reactnativenavigation.options.ButtonOptions;
1023
import com.reactnativenavigation.options.Options;
@@ -18,7 +31,9 @@
1831
import com.reactnativenavigation.viewcontrollers.stack.topbar.TopBarController;
1932
import com.reactnativenavigation.viewcontrollers.stack.topbar.button.BackButtonHelper;
2033
import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter;
34+
import com.reactnativenavigation.viewcontrollers.viewcontroller.TopBarVisibilityInfo;
2135
import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController;
36+
import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewControllerVisibilityInfo;
2237
import com.reactnativenavigation.views.component.Component;
2338
import com.reactnativenavigation.views.stack.StackBehaviour;
2439
import com.reactnativenavigation.views.stack.StackLayout;
@@ -31,19 +46,6 @@
3146
import java.util.Iterator;
3247
import java.util.List;
3348

34-
import androidx.annotation.NonNull;
35-
import androidx.annotation.RestrictTo;
36-
import androidx.annotation.Size;
37-
import androidx.annotation.VisibleForTesting;
38-
import androidx.coordinatorlayout.widget.CoordinatorLayout;
39-
import androidx.viewpager.widget.ViewPager;
40-
41-
import static com.reactnativenavigation.react.Constants.HARDWARE_BACK_BUTTON_ID;
42-
import static com.reactnativenavigation.utils.CollectionUtils.*;
43-
import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour;
44-
import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.updateBottomMargin;
45-
import static com.reactnativenavigation.utils.ObjectUtils.perform;
46-
4749
public class StackController extends ParentController<StackLayout> {
4850

4951
private IdStack<ViewController<?>> stack = new IdStack<>();
@@ -95,6 +97,19 @@ public ViewController<?> getCurrentChild() {
9597
return stack.peek();
9698
}
9799

100+
@Override
101+
public void onSelected(ViewController<?> previousVC) {
102+
presenter.bindNewViewController(previousVC, this);
103+
super.onSelected(previousVC);
104+
}
105+
106+
@NonNull
107+
@Override
108+
public ViewControllerVisibilityInfo getVisibilityInfo() {
109+
TopBarVisibilityInfo topBarInfo = topBarController.getVisibilityInfo();
110+
return new ViewControllerVisibilityInfo(topBarInfo);
111+
}
112+
98113
@Override
99114
public void onAttachToParent() {
100115
if (!isEmpty() && !getCurrentChild().isDestroyed() && !isViewShown()) {

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenter.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.reactnativenavigation.viewcontrollers.stack.topbar.button.ButtonPresenter;
3838
import com.reactnativenavigation.viewcontrollers.stack.topbar.button.IconResolver;
3939
import com.reactnativenavigation.viewcontrollers.stack.topbar.title.TitleBarReactViewController;
40+
import com.reactnativenavigation.viewcontrollers.statusbar.StatusBarPresenter;
4041
import com.reactnativenavigation.viewcontrollers.viewcontroller.IReactView;
4142
import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController;
4243
import com.reactnativenavigation.views.stack.topbar.TopBar;
@@ -191,6 +192,12 @@ public void onChildDestroyed(ViewController<?> child) {
191192
componentLeftButtons.remove(child.getView());
192193
}
193194

195+
public void bindNewViewController(ViewController<?> previousVC, ViewController<?> newVC) {
196+
Options options = newVC.resolveCurrentOptions(defaultOptions);
197+
topBarController.bindNewViewController(previousVC, newVC);
198+
StatusBarPresenter.instance.bindViewController(options.statusBar);
199+
}
200+
194201
private void destroyButtons(@Nullable Map<String, ButtonController> buttons) {
195202
if (buttons != null)
196203
forEach(buttons.values(), ViewController::destroy);
@@ -423,17 +430,17 @@ public List<Animator> getAdditionalPushAnimations(
423430
Options appearingOptions) {
424431
return CollectionUtils.asList(
425432
topBarController.getPushAnimation(appearingOptions, getTopBarTranslationAnimationDelta(stack, appearingCtrl)),
426-
perform(appearingCtrl.getStatusBarController(), null, sbc -> sbc.getStatusBarPushAnimation(appearingOptions)),
427-
perform(bottomTabsController, null, btc -> btc.getPushAnimation(appearingOptions))
428-
);
433+
perform(appearingCtrl, null, vc -> vc.getPushAnimations(appearingOptions)),
434+
perform(bottomTabsController, null, btc -> btc.getPushAnimation(appearingOptions)
435+
));
429436
}
430437

431438
public List<Animator> getAdditionalPopAnimations(Options appearingOptions, Options disappearingOptions, ViewController<?> appearingCtrl) {
432439
return CollectionUtils.asList(
433440
topBarController.getPopAnimation(appearingOptions, disappearingOptions),
434-
perform(appearingCtrl.getStatusBarController(), null, sbc -> sbc.getStatusBarPopAnimation(appearingOptions, disappearingOptions)),
435-
perform(bottomTabsController, null, btc -> btc.getPopAnimation(appearingOptions, disappearingOptions))
436-
);
441+
perform(appearingCtrl, null, vc -> vc.getPopAnimations(appearingOptions, disappearingOptions)),
442+
perform(bottomTabsController, null, btc -> btc.getPopAnimation(appearingOptions, disappearingOptions)
443+
));
437444
}
438445

439446
public List<Animator> getAdditionalSetRootAnimations(StackController stack, ViewController<?> appearing,

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/statusbar/StatusBarController.kt

Lines changed: 0 additions & 9 deletions
This file was deleted.

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/statusbar/StatusBarPresenter.kt

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)