From a2dc8f1c4e80fae248da79c4e65972209ac972e1 Mon Sep 17 00:00:00 2001 From: Guy Carmeli Date: Wed, 14 Aug 2019 13:35:55 +0300 Subject: [PATCH] V3 (#5372) # Changes * Support RN 0.60 * Migrate to AndroidX * Improve draw behind StatusBar (Preparation for #4258) * Don't push BottomTabs when keyboard is displayed (Fixes #4005, #3424) - It won't be needed to toggle the BottomTabs when Keyboard is visible * BottomTab badge and dot indicator are not animated by default on Android (parity with iOS) # Updating from v2 v3 is currently in alpha. To update simply npm install `3.0.0-alpha.11` - `npm install --save react-native-navigation@3.0.0-alpha.11`. Breaking changes are outlined below. ## Layout system changes on **Android** * Parent layouts (BottomsTabs, Stack, SideMenu) are always laid out behind the StatusBar. * Components (`component` and `externalComponent`) are measured and offset according to the StatusBar. In this release, We're changing the layout system in order to provide better support for immersive and full screen apps. In this release we've improved support for drawing behind the StatusBar, next we'll address drawing behind the NavigationBar. Use the `drawBehind` and `translucent` options to control the StatusBar ```js statusBar: { drawBehind: true, // will draw a screen behind the StatusBar translucent: true // Usually you'll want to have drawBehind: true when this is true } ``` While this isn't a breaking API change - there are a few breaking side effects. ### How will my app be effected 1. When the keyboard is opened, BottomTabs will now be drawn behind the keyboard and won't shift upwards. This is in parity with the current behaviour in iOS. For the most part, this isn't a breaking change. Toggling BottomTabs when TextInput's focus changes won't be needed anymore. 2. While parent controllers are drawn behind the StatusBar, their background isn't. This means that when transitioning from a destinations drawn under the StatusBar to a destination drawn behind it, the application's default background color will be visible behind the StatusBar. If you application's theme is dark, you might want to change the `windowBackground` property to mitigate this: Add the following to your application's `style.xml` ```xml #f00 ``` ## AndroidX migration We've migrated RNN to AndroidX, please follow migration instructions in the react-native repo. ## Removed SyncUiImplementation [SyncUiImplementation](https://github.com/wix/react-native-navigation/blob/master/lib/android/app/src/reactNative57WixFork/java/com/reactnativenavigation/react/SyncUiImplementation.java) was used to overcome a bug in RN's UiImplementation. This workaround was added to RN's `UiImplementation` in RN 0.60 (thanks @SudoPlz) and can be removed from RNN. If you're using `SyncUiImplementation` your app will fail to compile after upgrading to v3. Simply remove the following code from your `MainApplication.java` ```diff - import com.facebook.react.uimanager.UIImplementationProvider; - import com.reactnativenavigation.react.SyncUiImplementation; - @Override - protected UIImplementationProvider getUIImplementationProvider() { - return new SyncUiImplementation.Provider(); - } ``` ## BottomTab badge and dot indicator are not animated by default on Android (parity with iOS) Showing and hiding badge and dot indicator are now not animated by default. Badge animation is now controlled with the `bottomTab.animateBadge` property and dot indicator with `bottomTab.dotIndicator.animate` property. #### The following option will show a badge with animation ```js bottomTab: { badge: 'new, animateBadge: true } ``` #### The following option will show a dot indicator with animation ```js bottomTab: { dotIndicator: { visible: true, animate: true } } ``` closes #5228 --- docs/docs/Installing.md | 27 +- lib/android/app/build.gradle | 22 +- .../NavigationActivity.java | 6 +- .../NavigationApplication.java | 4 +- .../anim/BaseAnimator.java | 4 +- .../anim/FabCollapseBehaviour.java | 2 +- .../anim/NavigationAnimator.java | 2 +- .../anim/TopBarAnimator.java | 81 +-- .../anim/TopBarCollapseBehavior.java | 3 +- .../parse/AnimationOptions.java | 14 +- .../parse/BottomTabOptions.java | 10 +- .../parse/DotIndicatorOptions.java | 6 +- .../parse/LayoutFactory.java | 7 +- .../parse/LayoutOptions.java | 1 - .../parse/ModalOptions.java | 2 +- .../reactnativenavigation/parse/Options.java | 6 +- .../parse/OrientationOptions.java | 2 +- .../parse/StatusBarOptions.java | 14 +- .../parse/SubtitleOptions.java | 2 +- .../parse/TitleOptions.java | 2 +- .../parse/TopBarButtons.java | 2 +- .../parse/TopBarOptions.java | 4 + .../parse/TopTabOptions.java | 2 +- .../parse/TopTabsOptions.java | 4 +- .../parse/Transitions.java | 2 +- .../parse/ValueAnimationOptions.java | 20 +- .../parse/params/Button.java | 2 +- .../parse/params/Colour.java | 7 +- .../parse/params/Orientation.java | 2 +- .../parse/params/Text.java | 2 +- .../parse/params/TitleDisplayMode.java | 2 +- .../parse/parsers/LayoutNodeParser.java | 2 +- .../presentation/BottomTabPresenter.java | 104 +-- .../presentation/BottomTabsPresenter.java | 51 +- .../presentation/ComponentPresenter.java | 2 +- .../presentation/ComponentPresenterBase.java | 23 + .../ExternalComponentPresenter.java | 4 + .../presentation/FabPresenter.java | 2 +- .../presentation/OverlayManager.java | 5 +- .../presentation/Presenter.java | 97 ++- .../presentation/RootPresenter.java | 14 +- .../presentation/SideMenuPresenter.java | 14 +- .../presentation/StackPresenter.java | 176 ++--- .../react/NavigationModule.java | 13 +- .../react/NavigationPackage.java | 14 +- .../react/NavigationReactInitializer.java | 2 +- .../react/ReactView.java | 2 +- .../utils/ButtonPresenter.java | 6 +- .../utils/CollectionUtils.java | 18 +- .../utils/ColorUtils.java | 4 +- .../utils/CommandListenerAdapter.java | 2 +- .../utils/CoordinatorLayoutUtils.java | 17 + .../utils/ImageLoader.java | 4 +- .../utils/ImageLoadingListenerAdapter.java | 2 +- .../reactnativenavigation/utils/LateInit.java | 21 + .../utils/NativeCommandListener.java | 2 +- .../utils/ObjectUtils.java | 2 +- .../utils/ReflectionUtils.java | 2 +- .../utils/StatusBarUtils.java | 30 + .../utils/StringUtils.java | 2 +- .../utils/TextViewUtils.java | 2 +- .../utils/TypefaceLoader.java | 2 +- .../reactnativenavigation/utils/UiUtils.java | 20 +- .../utils/ViewUtils.java | 14 +- .../utils/WindowInsetsUtils.java | 15 + .../viewcontrollers/ChildController.java | 41 +- .../ChildControllersRegistry.java | 4 +- .../ComponentViewController.java | 49 +- .../viewcontrollers/IdStack.java | 2 +- .../viewcontrollers/ParentController.java | 75 ++- .../TitleBarButtonController.java | 8 +- .../viewcontrollers/ViewController.java | 63 +- .../viewcontrollers/YellowBoxDelegate.java | 2 +- .../viewcontrollers/YellowBoxHelper.java | 2 +- .../bottomtabs/AttachMode.java | 24 +- .../bottomtabs/BottomTabFinder.java | 13 +- .../bottomtabs/BottomTabsAttacher.java | 2 +- .../bottomtabs/BottomTabsController.java | 62 +- .../button/NavigationIconResolver.java | 4 +- .../ExternalComponentCreator.java | 2 +- .../ExternalComponentViewController.java | 40 +- .../viewcontrollers/modal/ModalPresenter.java | 22 +- .../viewcontrollers/modal/ModalStack.java | 5 +- .../viewcontrollers/navigator/Navigator.java | 36 +- .../sidemenu/SideMenuController.java | 105 +-- .../stack/BackButtonHelper.java | 14 +- .../stack/StackController.java | 74 ++- .../stack/StackControllerBuilder.java | 4 +- .../topbar/TopBarController.java | 74 ++- .../toptabs/TopTabsAdapter.java | 4 +- .../toptabs/TopTabsController.java | 11 +- .../views/BehaviourAdapter.java | 19 + .../views/BehaviourDelegate.java | 24 + .../views/BottomTabs.java | 21 +- .../views/Component.java | 4 - .../views/ComponentLayout.java | 36 +- .../views/ExternalComponentLayout.java | 27 +- .../com/reactnativenavigation/views/Fab.java | 2 +- .../reactnativenavigation/views/SideMenu.java | 5 +- .../views/SideMenuRoot.java | 84 +++ .../views/StackLayout.java | 30 +- .../views/bottomtabs/BottomTabsBehaviour.java | 21 + .../views/bottomtabs/BottomTabsLayout.java | 10 + .../views/element/Element.java | 6 +- .../element/ElementTransitionManager.java | 2 +- .../element/animators/LabColorEvaluator.java | 2 +- .../animators/PropertyAnimatorCreator.java | 2 +- .../views/stack/StackBehaviour.java | 20 + .../views/titlebar/TitleBar.java | 4 +- .../views/topbar/ScrollDIsabledBehavior.java | 16 + .../views/topbar/TopBar.java | 78 +-- .../views/toptabs/TopTabs.java | 4 +- .../views/toptabs/TopTabsViewPager.java | 19 +- .../views/touch/OverlayTouchDelegate.java | 5 +- lib/android/app/src/main/res/values/ids.xml | 1 + .../app/src/main/res/values/styles.xml | 15 +- .../react/NavigationReactNativeHost.java | 4 +- .../react/SyncUiImplementation.java | 2 +- .../react/NavigationReactNativeHost.java | 4 +- .../react/SyncUiImplementation.java | 2 +- .../react/NavigationReactNativeHost.java | 4 +- .../react/SyncUiImplementation.java | 2 +- .../react/NavigationReactNativeHost.java | 4 +- .../react/SyncUiImplementation.java | 2 +- .../react/NavigationReactNativeHost.java | 4 +- .../react/SyncUiImplementation.java | 2 +- .../react/NavigationReactNativeHost.java | 4 +- .../react/SyncUiImplementation.java | 2 +- .../DevBundleDownloadListenerAdapter.java | 28 + .../react/JsDevReloadHandlerFacade.java | 28 + .../react/NavigationReactNativeHost.java | 107 +++ .../react/ReloadHandlerFacade.java | 25 + .../com/reactnativenavigation/BaseTest.java | 15 +- .../EnvironmentTest.java | 4 +- .../com/reactnativenavigation/TestUtils.java | 17 +- .../mocks/ImageLoaderMock.java | 4 +- .../reactnativenavigation/mocks/Mocks.java | 15 + .../mocks/SimpleViewController.java | 38 +- .../mocks/TestReactView.java | 2 +- .../parse/OptionsTest.java | 2 +- .../utils/LayoutFactoryTest.java | 2 +- .../utils/TitleBarHelper.java | 4 +- .../utils/TypefaceLoaderTest.java | 4 +- .../BottomTabPresenterTest.java | 32 +- .../BottomTabsPresenterTest.java | 3 +- .../ComponentViewControllerTest.java | 48 +- .../ExternalComponentViewControllerTest.java | 8 +- .../FloatingActionButtonTest.java | 2 +- .../viewcontrollers/OptionsApplyingTest.java | 68 -- .../viewcontrollers/ParentControllerTest.java | 77 ++- .../viewcontrollers/StackPresenterTest.java | 198 +++--- .../viewcontrollers/TitleBarTest.java | 2 +- .../TopBarButtonControllerTest.java | 3 +- .../TopTabsViewControllerTest.java | 20 +- .../viewcontrollers/ViewControllerTest.java | 14 + .../bottomtabs/BottomTabsControllerTest.java | 119 ++-- .../bottomtabs/attachmode/AttachModeTest.java | 36 +- .../child/ChildControllerTest.java | 16 - .../child/ChildControllersRegistryTest.java | 5 + .../FragmentCreatorMock.java | 2 +- .../modal/ModalPresenterTest.java | 6 +- .../viewcontrollers/modal/ModalStackTest.java | 3 +- .../navigator/NavigatorTest.java | 24 +- .../navigator/RootPresenterTest.java | 10 +- .../overlay/OverlayManagerTest.java | 1 + .../sidemenu/SideMenuControllerTest.java | 89 ++- .../stack/StackControllerTest.java | 148 +++-- .../views/TopBarTest.java | 27 +- lib/android/build.gradle | 2 +- lib/android/gradle.properties | 5 - .../gradle/wrapper/gradle-wrapper.properties | 2 +- lib/android/prepare-robolectric.gradle | 43 -- .../Animations/SidebarAirbnbAnimation.h | 28 - .../Animations/SidebarAirbnbAnimation.m | 135 ---- .../Animations/SidebarAnimation.h | 80 --- .../Animations/SidebarAnimation.m | 101 --- .../Animations/SidebarFacebookAnimation.h | 28 - .../Animations/SidebarFacebookAnimation.m | 83 --- .../Animations/SidebarFeedlyAnimation.h | 28 - .../Animations/SidebarFeedlyAnimation.m | 89 --- .../Animations/SidebarFlipboardAnimation.h | 28 - .../Animations/SidebarFlipboardAnimation.m | 99 --- .../Animations/SidebarLuvocracyAnimation.h | 28 - .../Animations/SidebarLuvocracyAnimation.m | 111 ---- .../Animations/SidebarWunderlistAnimation.h | 28 - .../Animations/SidebarWunderlistAnimation.m | 103 --- .../TheSidebarController.h | 91 --- .../TheSidebarController.m | 414 ------------ .../project.pbxproj | 8 +- .../xcschemes/ReactNativeNavigation.xcscheme | 4 +- lib/src/interfaces/Options.ts | 1 + package.json | 12 +- playground/android/app/build.gradle | 25 +- .../playground/FragmentComponent.java | 8 +- .../playground/FragmentCreator.java | 2 +- .../playground/FragmentScreen.java | 4 +- .../playground/MainActivity.java | 2 +- .../playground/MainApplication.java | 11 +- .../app/src/main/res/values/styles.xml | 10 +- playground/android/build.gradle | 3 +- playground/android/gradle.properties | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- playground/img/city.png | Bin 0 -> 899295 bytes playground/img/city2.jpeg | Bin 0 -> 662378 bytes .../images => img}/colored_tab_icon@2x.png | Bin playground/img/menu@2x.android.png | Bin 0 -> 108 bytes .../{navicon_menu@2x.png => menu@2x.ios.png} | Bin playground/{src/images => img}/one@2x.png | Bin playground/{src/images => img}/two@2x.png | Bin .../{src/images => img}/two_selected@2x.png | Bin .../ios/playground.xcodeproj/project.pbxproj | 60 +- .../xcschemes/playground.xcscheme | 2 +- .../xcschemes/playground_release.xcscheme | 4 +- playground/src/commons/Layouts.js | 4 +- playground/src/commons/Options.js | 41 ++ playground/src/components/Button.js | 12 +- playground/src/components/Root.js | 35 +- .../TopBarBackground.js | 21 +- playground/src/flags.js | 4 + playground/src/images/one_selected@2x.png | Bin 964 -> 0 bytes playground/src/images/three@2x.png | Bin 682 -> 0 bytes playground/src/images/three_selected@2x.png | Bin 615 -> 0 bytes playground/src/screens/CustomTopBar.js | 30 +- .../src/screens/FirstBottomTabScreen.js | 4 +- playground/src/screens/FlatListScreen.js | 4 +- .../src/screens/FullScreenModalScreen.js | 70 ++ playground/src/screens/LayoutsScreen.js | 16 +- playground/src/screens/ModalScreen.js | 9 +- playground/src/screens/OptionsScreen.js | 570 +--------------- playground/src/screens/Screens.js | 80 ++- .../src/screens/SecondBottomTabScreen.js | 12 +- .../src/screens/SideMenuCenterScreen.js | 13 + playground/src/screens/SideMenuLeftScreen.js | 3 +- playground/src/screens/SideMenuScreen.js | 74 --- playground/src/screens/StackScreen.js | 3 +- playground/src/screens/StatusBarFirstTab.js | 76 +++ .../src/screens/StatusBarOptionsScreen.js | 73 +++ playground/src/screens/WelcomeScreen.js | 608 ------------------ playground/src/screens/index.js | 42 +- playground/src/services/Navigation.js | 4 +- playground/src/testIDs.js | 1 + scripts/test-unit.js | 2 +- 242 files changed, 2704 insertions(+), 4244 deletions(-) create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/presentation/ComponentPresenterBase.java create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/presentation/ExternalComponentPresenter.java create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/utils/CoordinatorLayoutUtils.java create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/utils/LateInit.java create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/utils/StatusBarUtils.java create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/utils/WindowInsetsUtils.java create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/views/BehaviourAdapter.java create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/views/BehaviourDelegate.java create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/views/SideMenuRoot.java create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/views/bottomtabs/BottomTabsBehaviour.java create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/views/bottomtabs/BottomTabsLayout.java create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/views/stack/StackBehaviour.java create mode 100644 lib/android/app/src/main/java/com/reactnativenavigation/views/topbar/ScrollDIsabledBehavior.java create mode 100644 lib/android/app/src/reactNative60/java/com/reactnativenavigation/react/DevBundleDownloadListenerAdapter.java create mode 100644 lib/android/app/src/reactNative60/java/com/reactnativenavigation/react/JsDevReloadHandlerFacade.java create mode 100644 lib/android/app/src/reactNative60/java/com/reactnativenavigation/react/NavigationReactNativeHost.java create mode 100644 lib/android/app/src/reactNative60/java/com/reactnativenavigation/react/ReloadHandlerFacade.java create mode 100644 lib/android/app/src/test/java/com/reactnativenavigation/mocks/Mocks.java delete mode 100644 lib/android/prepare-robolectric.gradle delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarAirbnbAnimation.h delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarAirbnbAnimation.m delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarAnimation.h delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarAnimation.m delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarFacebookAnimation.h delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarFacebookAnimation.m delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarFeedlyAnimation.h delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarFeedlyAnimation.m delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarFlipboardAnimation.h delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarFlipboardAnimation.m delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarLuvocracyAnimation.h delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarLuvocracyAnimation.m delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarWunderlistAnimation.h delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/Animations/SidebarWunderlistAnimation.m delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/TheSidebarController.h delete mode 100755 lib/ios/RNNSideMenu/TheSidebarController/TheSidebarController.m create mode 100644 playground/img/city.png create mode 100644 playground/img/city2.jpeg rename playground/{src/images => img}/colored_tab_icon@2x.png (100%) create mode 100755 playground/img/menu@2x.android.png rename playground/img/{navicon_menu@2x.png => menu@2x.ios.png} (100%) rename playground/{src/images => img}/one@2x.png (100%) rename playground/{src/images => img}/two@2x.png (100%) rename playground/{src/images => img}/two_selected@2x.png (100%) rename playground/src/{screens => components}/TopBarBackground.js (64%) create mode 100644 playground/src/flags.js delete mode 100644 playground/src/images/one_selected@2x.png delete mode 100644 playground/src/images/three@2x.png delete mode 100644 playground/src/images/three_selected@2x.png create mode 100644 playground/src/screens/FullScreenModalScreen.js delete mode 100644 playground/src/screens/SideMenuScreen.js create mode 100644 playground/src/screens/StatusBarFirstTab.js create mode 100644 playground/src/screens/StatusBarOptionsScreen.js delete mode 100644 playground/src/screens/WelcomeScreen.js diff --git a/docs/docs/Installing.md b/docs/docs/Installing.md index a87e34feff0..b16ba2fd777 100644 --- a/docs/docs/Installing.md +++ b/docs/docs/Installing.md @@ -45,18 +45,18 @@ $(SRCROOT)/../node_modules/react-native-navigation/lib/ios { NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; [ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions]; - + return YES; } @end ``` -3a. If, in Xcode, you see the following error message in `AppDelegate.m` next to `#import "RCTBundleURLProvider.h"`: +3a. If, in Xcode, you see the following error message in `AppDelegate.m` next to `#import "RCTBundleURLProvider.h"`: ``` ! 'RCTBundleURLProvider.h' file not found ``` -This is because the `React` scheme is missing from your project. You can verify this by opening the `Product` menu and the `Scheme` submenu. +This is because the `React` scheme is missing from your project. You can verify this by opening the `Product` menu and the `Scheme` submenu. To make the `React` scheme available to your project, run `npm install -g react-native-git-upgrade` followed by `react-native-git-upgrade`. Once this is done, you can click back to the menu in Xcode: `Product -> Scheme -> Manage Schemes`, then click '+' to add a new scheme. From the `Target` menu, select "React", and click the checkbox to make the scheme `shared`. This should make the error disappear. @@ -117,7 +117,6 @@ buildscript { allprojects { repositories { + google() -+ mavenCentral() mavenLocal() jcenter() maven { @@ -133,8 +132,6 @@ allprojects { } ext { -- buildToolsVersion = "26.0.3" -+ buildToolsVersion = "27.0.3" - minSdkVersion = 16 + minSdkVersion = 19 compileSdkVersion = 26 @@ -171,17 +168,14 @@ android { dependencies { - compile fileTree(dir: "libs", include: ["*.jar"]) -- compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" - compile "com.facebook.react:react-native:+" // From node_modules + implementation fileTree(dir: "libs", include: ["*.jar"]) -+ implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" + implementation "com.facebook.react:react-native:+" // From node_modules + implementation project(':react-native-navigation') } ``` ### 5 RNN and React Native version - react-native-navigation supports multiple React Native versions. Target the React Native version required by your project by specifying the RNN build flavor in `android/app/build.gradle`. ```diff @@ -205,11 +199,12 @@ android { >`reactNative55` - RN 0.55.x
>`reactNative56` - RN 0.56.x
>`reactNative57` - RN 0.57.0 - 0.57.4
->`reactNative57_5` - RN 0.57.5 and above
+>`reactNative57_5` - RN 0.57.5 - 0.59.9
+>`reactNative60` - RN 0.60.0 and above Now we need to instruct gradle how to build that flavor. To do so here two solutions: -#### 5.1 Build app with gradle command +#### 5.1 Build app with gradle command **prefered solution** The RNN flavor you would like to build is specified in `app/build.gradle`. Therefore in order to compile only that flavor, instead of building your entire project using `./gradlew assembleDebug`, you should instruct gradle to build the app module: `./gradlew app:assembleDebug`. The easiest way is to add a package.json command to build and install your debug Android APK . @@ -258,7 +253,7 @@ This file is located in `android/app/src/main/java/com//MainActivit -import com.facebook.react.ReactActivity; +import com.reactnativenavigation.NavigationActivity; --public class MainActivity extends ReactActivity { +-public class MainActivity extends ReactActivity { +public class MainActivity extends NavigationActivity { - @Override - protected String getMainComponentName() { @@ -272,7 +267,7 @@ If you have any **react-native** related methods, you can safely delete them. ### 7. Update `MainApplication.java` This file is located in `android/app/src/main/java/com//MainApplication.java`. - + ```diff ... import android.app.Application; @@ -292,7 +287,7 @@ import java.util.List; -public class MainApplication extends Application implements ReactApplication { +public class MainApplication extends NavigationApplication { -+ ++ + @Override + protected ReactGateway createReactGateway() { + ReactNativeHost host = new NavigationReactNativeHost(this, isDebug(), createAdditionalReactPackages()) { @@ -316,7 +311,7 @@ import java.util.List; + // eg. new VectorIconsPackage() + ); + } -+ ++ + @Override + public List createAdditionalReactPackages() { + return getPackages(); @@ -365,8 +360,6 @@ dependencies { ## You can use react-native-navigation \o/ Update `index.js` file - - ```diff +import { Navigation } from "react-native-navigation"; -import {AppRegistry} from 'react-native'; diff --git a/lib/android/app/build.gradle b/lib/android/app/build.gradle index 309e8fc22a5..f74291df05e 100644 --- a/lib/android/app/build.gradle +++ b/lib/android/app/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.android.library' -apply from: '../prepare-robolectric.gradle' def safeExtGet(prop, fallback) { rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback @@ -7,7 +6,6 @@ def safeExtGet(prop, fallback) { def DEFAULT_COMPILE_SDK_VERSION = 28 def DEFAULT_MIN_SDK_VERSION = 19 -def DEFAULT_SUPPORT_LIB_VERSION = '28.0.0' def DEFAULT_TARGET_SDK_VERSION = 28 android { @@ -32,6 +30,7 @@ android { } testOptions { + unitTests.includeAndroidResources = true unitTests.all { t -> reports { html.enabled true @@ -76,6 +75,10 @@ android { dimension "RNN.reactNativeVersion" buildConfigField("int", "REACT_NATVE_VERSION_MINOR", "57") } + reactNative60 { + dimension "RNN.reactNativeVersion" + buildConfigField("int", "REACT_NATVE_VERSION_MINOR", "60") + } } } @@ -89,14 +92,13 @@ allprojects { p -> } } -def supportLibVersion = safeExtGet('supportLibVersion', DEFAULT_SUPPORT_LIB_VERSION) - dependencies { - implementation "com.android.support:design:${supportLibVersion}" - implementation "com.android.support:appcompat-v7:${supportLibVersion}" - implementation "com.android.support:support-v4:${supportLibVersion}" + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.annotation:annotation:1.1.0' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'com.google.android.material:material:1.1.0-alpha08' - implementation 'com.github.wix-playground:ahbottomnavigation:2.4.15' + implementation 'com.github.wix-playground:ahbottomnavigation:3.0.2' implementation 'com.github.wix-playground:reflow-animator:1.0.4' implementation 'com.github.clans:fab:1.6.4' @@ -105,8 +107,8 @@ dependencies { // tests testImplementation 'junit:junit:4.12' - testImplementation 'org.robolectric:robolectric:3.5.1' + testImplementation "org.robolectric:robolectric:4.3-beta-1" testImplementation 'org.assertj:assertj-core:3.8.0' testImplementation 'com.squareup.assertj:assertj-android:1.1.1' - testImplementation 'org.mockito:mockito-core:2.13.0' + testImplementation 'org.mockito:mockito-core:2.28.2' } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java b/lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java index ce89e86d1c1..80b204549d7 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java @@ -5,9 +5,9 @@ import android.graphics.Color; import android.os.Build; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import android.view.KeyEvent; import android.view.View; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java b/lib/android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java index e82a0fa68f6..9c3df0531a1 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java @@ -1,8 +1,8 @@ package com.reactnativenavigation; import android.app.Application; -import android.support.annotation.Nullable; -import android.support.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.NonNull; import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/anim/BaseAnimator.java b/lib/android/app/src/main/java/com/reactnativenavigation/anim/BaseAnimator.java index 08b86f2b856..40ce5ce149c 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/anim/BaseAnimator.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/anim/BaseAnimator.java @@ -5,7 +5,7 @@ import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.content.Context; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.DecelerateInterpolator; @@ -34,6 +34,8 @@ AnimatorSet getDefaultPushAnimation(View view) { set.setDuration(DURATION); ObjectAnimator translationY = ObjectAnimator.ofFloat(view, TRANSLATION_Y, this.translationY, 0); ObjectAnimator alpha = ObjectAnimator.ofFloat(view, ALPHA, 0, 1); + translationY.setDuration(DURATION); + alpha.setDuration(DURATION); set.playTogether(translationY, alpha); return set; } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/anim/FabCollapseBehaviour.java b/lib/android/app/src/main/java/com/reactnativenavigation/anim/FabCollapseBehaviour.java index eed1b207fd6..43bae8e65cf 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/anim/FabCollapseBehaviour.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/anim/FabCollapseBehaviour.java @@ -1,7 +1,7 @@ package com.reactnativenavigation.anim; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.reactnativenavigation.interfaces.ScrollEventListener; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java b/lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java index c2dbfeab528..03813ae936c 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java @@ -4,7 +4,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.content.Context; -import android.support.annotation.RestrictTo; +import androidx.annotation.RestrictTo; import android.view.View; import android.view.ViewGroup; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarAnimator.java b/lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarAnimator.java index fbd51952938..5ffd763a503 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarAnimator.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarAnimator.java @@ -7,17 +7,16 @@ import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.view.animation.LinearInterpolator; import com.reactnativenavigation.parse.AnimationOptions; -import com.reactnativenavigation.utils.ViewUtils; +import com.reactnativenavigation.views.StackLayout; import com.reactnativenavigation.views.topbar.TopBar; -import javax.annotation.Nullable; - import static android.view.View.TRANSLATION_Y; +import static com.reactnativenavigation.utils.ObjectUtils.perform; +import static com.reactnativenavigation.utils.ViewUtils.getHeight; public class TopBarAnimator { @@ -25,73 +24,67 @@ public class TopBarAnimator { private static final int DURATION = 300; private static final TimeInterpolator DECELERATE = new DecelerateInterpolator(); private static final TimeInterpolator LINEAR = new LinearInterpolator(); - private static final TimeInterpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator(); private TopBar topBar; private String stackId; private Animator hideAnimator; private Animator showAnimator; - public TopBarAnimator(TopBar topBar) { + public TopBarAnimator() { + } + + TopBarAnimator(TopBar topBar) { this.topBar = topBar; } - public TopBarAnimator(TopBar topBar, @Nullable String stackId) { + public void bindView(TopBar topBar, StackLayout stack) { this.topBar = topBar; - this.stackId = stackId; + stackId = stack.getStackId(); } - public void show(AnimationOptions options) { + public void show(AnimationOptions options, int translationStartDy) { topBar.setVisibility(View.VISIBLE); if (options.hasValue() && (!options.id.hasValue() || options.id.get().equals(stackId))) { + options.setValueDy(TRANSLATION_Y, -translationStartDy, 0); showAnimator = options.getAnimation(topBar); } else { - showAnimator = getDefaultShowAnimator(-1 * ViewUtils.getHeight(topBar), DECELERATE, DURATION); + showAnimator = getDefaultShowAnimator(translationStartDy, DECELERATE, DURATION); } - show(); + showInternal(); } public void show(float startTranslation) { showAnimator = getDefaultShowAnimator(startTranslation, LINEAR, DEFAULT_COLLAPSE_DURATION); - show(); + showInternal(); } - private void show() { + private void showInternal() { showAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { topBar.setVisibility(View.VISIBLE); } }); - topBar.resetAnimationOptions(); if (isAnimatingHide()) hideAnimator.cancel(); showAnimator.start(); } - private AnimatorSet getDefaultShowAnimator(float startTranslation, TimeInterpolator interpolator, int duration) { - ObjectAnimator showAnimator = ObjectAnimator.ofFloat(topBar, TRANSLATION_Y, startTranslation, 0); - showAnimator.setInterpolator(interpolator); - showAnimator.setDuration(duration); - AnimatorSet set = new AnimatorSet(); - set.play(showAnimator); - return set; - } - - public void hide(AnimationOptions options, Runnable onAnimationEnd) { + public void hide(AnimationOptions options, Runnable onAnimationEnd, float translationStartDy, float translationEndDy) { if (options.hasValue() && (!options.id.hasValue() || options.id.get().equals(stackId))) { + options.setValueDy(TRANSLATION_Y, translationStartDy, -translationEndDy); hideAnimator = options.getAnimation(topBar); } else { - hideAnimator = getDefaultHideAnimator(0, ACCELERATE_DECELERATE, DURATION); + hideAnimator = getDefaultHideAnimator(translationStartDy, translationEndDy, DECELERATE, DURATION); } - hide(onAnimationEnd); + hideInternal(onAnimationEnd); } - void hide(float startTranslation) { - hideAnimator = getDefaultHideAnimator(startTranslation, LINEAR, DEFAULT_COLLAPSE_DURATION); - hide(() -> {}); + public void hide(float translationStart, float translationEndDy) { + hideAnimator = getDefaultHideAnimator(translationStart, translationEndDy, LINEAR, DEFAULT_COLLAPSE_DURATION); + hideInternal(() -> {}); } - private void hide(Runnable onAnimationEnd) { + private void hideInternal(Runnable onAnimationEnd) { hideAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -103,13 +96,6 @@ public void onAnimationEnd(Animator animation) { hideAnimator.start(); } - private Animator getDefaultHideAnimator(float startTranslation, TimeInterpolator interpolator, int duration) { - ObjectAnimator hideAnimator = ObjectAnimator.ofFloat(topBar, TRANSLATION_Y, startTranslation, -1 * topBar.getMeasuredHeight()); - hideAnimator.setInterpolator(interpolator); - hideAnimator.setDuration(duration); - return hideAnimator; - } - public boolean isAnimatingHide() { return hideAnimator != null && hideAnimator.isRunning(); } @@ -117,4 +103,25 @@ public boolean isAnimatingHide() { public boolean isAnimatingShow() { return showAnimator != null && showAnimator.isRunning(); } + + public boolean isAnimating() { + return perform(showAnimator, false, Animator::isRunning) || + perform(hideAnimator, false, Animator::isRunning); + } + + private AnimatorSet getDefaultShowAnimator(float translationStart, TimeInterpolator interpolator, int duration) { + ObjectAnimator showAnimator = ObjectAnimator.ofFloat(topBar, TRANSLATION_Y, -getHeight(topBar) - translationStart, 0); + showAnimator.setInterpolator(interpolator); + showAnimator.setDuration(duration); + AnimatorSet set = new AnimatorSet(); + set.play(showAnimator); + return set; + } + + private Animator getDefaultHideAnimator(float translationStart, float translationEndDy, TimeInterpolator interpolator, int duration) { + ObjectAnimator hideAnimator = ObjectAnimator.ofFloat(topBar, TRANSLATION_Y, translationStart, -topBar.getMeasuredHeight() - translationEndDy); + hideAnimator.setInterpolator(interpolator); + hideAnimator.setDuration(duration); + return hideAnimator; + } } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarCollapseBehavior.java b/lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarCollapseBehavior.java index 21f16bf9f8f..3f8c6417dc7 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarCollapseBehavior.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarCollapseBehavior.java @@ -2,6 +2,7 @@ import android.view.View; +import android.view.ViewGroup; import com.reactnativenavigation.interfaces.ScrollEventListener; import com.reactnativenavigation.views.topbar.TopBar; @@ -58,6 +59,6 @@ public void onShow() { @Override public void onHide() { - animator.hide(topBar.getTranslationY()); + animator.hide(topBar.getTranslationY(), ((ViewGroup.MarginLayoutParams) topBar.getLayoutParams()).topMargin); } } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationOptions.java index 704b18c2a20..100107c4edf 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationOptions.java @@ -20,8 +20,9 @@ import java.util.Iterator; import java.util.List; -public class AnimationOptions { +import static com.reactnativenavigation.utils.CollectionUtils.*; +public class AnimationOptions { public static AnimationOptions parse(JSONObject json) { AnimationOptions options = new AnimationOptions(); if (json == null) return options; @@ -78,9 +79,7 @@ public AnimatorSet getAnimation(View view, AnimatorSet defaultAnimation) { if (!hasAnimation()) return defaultAnimation; AnimatorSet animationSet = new AnimatorSet(); List animators = new ArrayList<>(); - for (ValueAnimationOptions options : valueOptions) { - animators.add(options.getAnimation(view)); - } + forEach(valueOptions, options -> animators.add(options.getAnimation(view))); animationSet.playTogether(animators); return animationSet; } @@ -110,4 +109,11 @@ private static Property getAnimProp(String key) { public boolean hasAnimation() { return !valueOptions.isEmpty(); } + + public void setValueDy(Property animation, float fromDelta, float toDelta) { + first(valueOptions, o -> o.equals(animation), param -> { + param.setFromDelta(fromDelta); + param.setToDelta(toDelta); + }); + } } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java index 86b911b12c2..ddb583cab3d 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java @@ -1,14 +1,16 @@ package com.reactnativenavigation.parse; import android.graphics.Typeface; -import android.support.annotation.Nullable; +import com.reactnativenavigation.parse.params.Bool; import com.reactnativenavigation.parse.params.Colour; +import com.reactnativenavigation.parse.params.NullBool; import com.reactnativenavigation.parse.params.NullColor; import com.reactnativenavigation.parse.params.NullNumber; import com.reactnativenavigation.parse.params.NullText; import com.reactnativenavigation.parse.params.Number; import com.reactnativenavigation.parse.params.Text; +import com.reactnativenavigation.parse.parsers.BoolParser; import com.reactnativenavigation.parse.parsers.ColorParser; import com.reactnativenavigation.parse.parsers.NumberParser; import com.reactnativenavigation.parse.parsers.TextParser; @@ -16,6 +18,8 @@ import org.json.JSONObject; +import androidx.annotation.Nullable; + public class BottomTabOptions { public static BottomTabOptions parse(TypefaceLoader typefaceManager, JSONObject json) { @@ -30,6 +34,7 @@ public static BottomTabOptions parse(TypefaceLoader typefaceManager, JSONObject options.selectedIconColor = ColorParser.parse(json, "selectedIconColor"); options.badge = TextParser.parse(json, "badge"); options.badgeColor = ColorParser.parse(json, "badgeColor"); + options.animateBadge = BoolParser.parse(json, "animateBadge"); options.testId = TextParser.parse(json, "testID"); options.fontFamily = typefaceManager.getTypeFace(json.optString("fontFamily", "")); options.fontSize = NumberParser.parse(json, "fontSize"); @@ -47,6 +52,7 @@ public static BottomTabOptions parse(TypefaceLoader typefaceManager, JSONObject public Text testId = new NullText(); public Text badge = new NullText(); public Colour badgeColor = new NullColor(); + public Bool animateBadge = new NullBool(); public DotIndicatorOptions dotIndicator = new DotIndicatorOptions(); public Number fontSize = new NullNumber(); public Number selectedFontSize = new NullNumber(); @@ -62,6 +68,7 @@ void mergeWith(final BottomTabOptions other) { if (other.selectedIconColor.hasValue()) selectedIconColor = other.selectedIconColor; if (other.badge.hasValue()) badge = other.badge; if (other.badgeColor.hasValue()) badgeColor = other.badgeColor; + if (other.animateBadge.hasValue()) animateBadge = other.animateBadge; if (other.testId.hasValue()) testId = other.testId; if (other.fontSize.hasValue()) fontSize = other.fontSize; if (other.selectedFontSize.hasValue()) selectedFontSize = other.selectedFontSize; @@ -78,6 +85,7 @@ void mergeWithDefault(final BottomTabOptions defaultOptions) { if (!selectedIconColor.hasValue()) selectedIconColor = defaultOptions.selectedIconColor; if (!badge.hasValue()) badge = defaultOptions.badge; if (!badgeColor.hasValue()) badgeColor = defaultOptions.badgeColor; + if (!animateBadge.hasValue()) animateBadge = defaultOptions.animateBadge; if (!fontSize.hasValue()) fontSize = defaultOptions.fontSize; if (!selectedFontSize.hasValue()) selectedFontSize = defaultOptions.selectedFontSize; if (fontFamily == null) fontFamily = defaultOptions.fontFamily; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/DotIndicatorOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/DotIndicatorOptions.java index e44651674eb..c7b2ff3ecc0 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/DotIndicatorOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/DotIndicatorOptions.java @@ -1,7 +1,5 @@ package com.reactnativenavigation.parse; -import android.support.annotation.Nullable; - import com.reactnativenavigation.parse.params.Bool; import com.reactnativenavigation.parse.params.Colour; import com.reactnativenavigation.parse.params.NullBool; @@ -14,6 +12,8 @@ import org.json.JSONObject; +import androidx.annotation.Nullable; + public class DotIndicatorOptions { public static DotIndicatorOptions parse(@Nullable JSONObject json) { DotIndicatorOptions options = new DotIndicatorOptions(); @@ -22,6 +22,7 @@ public static DotIndicatorOptions parse(@Nullable JSONObject json) { options.color = ColorParser.parse(json, "color"); options.size = NumberParser.parse(json, "size"); options.visible = BoolParser.parse(json, "visible"); + options.animate = BoolParser.parse(json, "animate"); return options; } @@ -29,6 +30,7 @@ public static DotIndicatorOptions parse(@Nullable JSONObject json) { public Colour color = new NullColor(); public Number size = new NullNumber(); public Bool visible = new NullBool(); + public Bool animate = new NullBool(); public boolean hasValue() { return visible.hasValue(); diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java index b943281ca4f..512fb5be19c 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java @@ -1,13 +1,12 @@ package com.reactnativenavigation.parse; import android.app.Activity; -import android.support.annotation.NonNull; -import android.support.annotation.RestrictTo; import com.facebook.react.ReactInstanceManager; import com.reactnativenavigation.presentation.BottomTabPresenter; import com.reactnativenavigation.presentation.BottomTabsPresenter; import com.reactnativenavigation.presentation.ComponentPresenter; +import com.reactnativenavigation.presentation.ExternalComponentPresenter; import com.reactnativenavigation.presentation.Presenter; import com.reactnativenavigation.presentation.RenderChecker; import com.reactnativenavigation.presentation.SideMenuPresenter; @@ -37,6 +36,9 @@ import java.util.List; import java.util.Map; +import androidx.annotation.NonNull; +import androidx.annotation.RestrictTo; + import static com.reactnativenavigation.parse.Options.parse; public class LayoutFactory { @@ -168,6 +170,7 @@ private ViewController createExternalComponent(LayoutNode node) { externalComponentCreators.get(externalComponent.name.get()), reactInstanceManager, new EventEmitter(reactInstanceManager.getCurrentReactContext()), + new ExternalComponentPresenter(), parse(typefaceManager, node.getOptions()) ); } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutOptions.java index 3932b54be39..1df00c89bb6 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutOptions.java @@ -35,7 +35,6 @@ public void mergeWith(LayoutOptions other) { if (other.topMargin.hasValue()) topMargin = other.topMargin; if (other.orientation.hasValue()) orientation = other.orientation; if (other.direction.hasValue()) direction = other.direction; - } public void mergeWithDefault(LayoutOptions defaultOptions) { diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/ModalOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/ModalOptions.java index 4740c63d76f..89ecfae2511 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/ModalOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/ModalOptions.java @@ -1,6 +1,6 @@ package com.reactnativenavigation.parse; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.reactnativenavigation.parse.params.Bool; import com.reactnativenavigation.parse.params.NullBool; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java index b55cc9236fa..7bd1f730e58 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java @@ -1,8 +1,5 @@ package com.reactnativenavigation.parse; -import android.support.annotation.CheckResult; -import android.support.annotation.NonNull; - import com.reactnativenavigation.parse.params.NullBool; import com.reactnativenavigation.parse.params.NullNumber; import com.reactnativenavigation.parse.params.NullText; @@ -10,6 +7,9 @@ import org.json.JSONObject; +import androidx.annotation.CheckResult; +import androidx.annotation.NonNull; + public class Options { public static final Options EMPTY = new Options(); diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/OrientationOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/OrientationOptions.java index 8a55e0b45b3..05284c72c8b 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/OrientationOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/OrientationOptions.java @@ -1,6 +1,6 @@ package com.reactnativenavigation.parse; -import android.support.annotation.CheckResult; +import androidx.annotation.CheckResult; import com.reactnativenavigation.parse.params.Orientation; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/StatusBarOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/StatusBarOptions.java index e7167fa1f79..70723352503 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/StatusBarOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/StatusBarOptions.java @@ -1,6 +1,6 @@ package com.reactnativenavigation.parse; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.reactnativenavigation.parse.params.Bool; import com.reactnativenavigation.parse.params.Colour; @@ -46,6 +46,7 @@ public static StatusBarOptions parse(JSONObject json) { result.textColorScheme = TextColorScheme.fromString(json.optString("style")); result.visible = BoolParser.parse(json, "visible"); result.drawBehind = BoolParser.parse(json, "drawBehind"); + result.translucent = BoolParser.parse(json, "translucent"); return result; } @@ -54,12 +55,14 @@ public static StatusBarOptions parse(JSONObject json) { public TextColorScheme textColorScheme = TextColorScheme.None; public Bool visible = new NullBool(); public Bool drawBehind = new NullBool(); + public Bool translucent = new NullBool(); public void mergeWith(StatusBarOptions other) { if (other.backgroundColor.hasValue()) backgroundColor = other.backgroundColor; if (other.textColorScheme.hasValue()) textColorScheme = other.textColorScheme; if (other.visible.hasValue()) visible = other.visible; if (other.drawBehind.hasValue()) drawBehind = other.drawBehind; + if (other.translucent.hasValue()) translucent = other.translucent; } public void mergeWithDefault(StatusBarOptions defaultOptions) { @@ -67,5 +70,14 @@ public void mergeWithDefault(StatusBarOptions defaultOptions) { if (!textColorScheme.hasValue()) textColorScheme = defaultOptions.textColorScheme; if (!visible.hasValue()) visible = defaultOptions.visible; if (!drawBehind.hasValue()) drawBehind = defaultOptions.drawBehind; + if (!translucent.hasValue()) translucent = defaultOptions.translucent; + } + + public boolean isHiddenOrDrawBehind() { + return drawBehind.isTrue() || visible.isFalse(); + } + + public boolean hasTransparency() { + return translucent.isTrue() || visible.isFalse() || backgroundColor.hasTransparency(); } } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/SubtitleOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/SubtitleOptions.java index b4a4af55177..c512becd791 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/SubtitleOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/SubtitleOptions.java @@ -1,7 +1,7 @@ package com.reactnativenavigation.parse; import android.graphics.Typeface; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.reactnativenavigation.parse.params.Colour; import com.reactnativenavigation.parse.params.Fraction; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/TitleOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/TitleOptions.java index 5215fcb6690..1524c71a3b3 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/TitleOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/TitleOptions.java @@ -1,7 +1,7 @@ package com.reactnativenavigation.parse; import android.graphics.Typeface; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.reactnativenavigation.parse.params.Colour; import com.reactnativenavigation.parse.params.Fraction; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarButtons.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarButtons.java index 8f703496f2a..4c102ac43f3 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarButtons.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarButtons.java @@ -1,6 +1,6 @@ package com.reactnativenavigation.parse; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.reactnativenavigation.parse.params.Button; import com.reactnativenavigation.utils.CollectionUtils; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java index 8a3bb3bd3c2..0e518dd14d3 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java @@ -134,4 +134,8 @@ public void validate() { subtitle.text = new NullText(); } } + + public boolean isHiddenOrDrawBehind() { + return drawBehind.isTrue() || visible.isFalse(); + } } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabOptions.java index b3f25e79f6e..3ec5187dcf4 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabOptions.java @@ -1,7 +1,7 @@ package com.reactnativenavigation.parse; import android.graphics.Typeface; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.reactnativenavigation.parse.params.NullText; import com.reactnativenavigation.parse.params.Text; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabsOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabsOptions.java index b80e79f3001..d89f17eca5e 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabsOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabsOptions.java @@ -1,7 +1,7 @@ package com.reactnativenavigation.parse; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.reactnativenavigation.parse.params.Bool; import com.reactnativenavigation.parse.params.Colour; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/Transitions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/Transitions.java index b945c8bc315..334694cbe8e 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/Transitions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/Transitions.java @@ -1,6 +1,6 @@ package com.reactnativenavigation.parse; -import android.support.annotation.RestrictTo; +import androidx.annotation.RestrictTo; import org.json.JSONArray; import org.json.JSONObject; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/ValueAnimationOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/ValueAnimationOptions.java index c73e85e0b2f..663a0730695 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/ValueAnimationOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/ValueAnimationOptions.java @@ -35,14 +35,28 @@ public static ValueAnimationOptions parse(JSONObject json, Property private Property animProp; private FloatParam from = new NullFloatParam(); + private FloatParam fromDelta = new FloatParam(0f); private FloatParam to = new NullFloatParam(); + private FloatParam toDelta = new FloatParam(0f); private Number duration = new NullNumber(); private Number startDelay = new NullNumber(); private Interpolation interpolation = Interpolation.NO_VALUE; + void setFromDelta(float fromDelta) { + this.fromDelta = new FloatParam(fromDelta); + } + + void setToDelta(float toDelta) { + this.toDelta = new FloatParam(toDelta); + } + Animator getAnimation(View view) { if (!from.hasValue() || !to.hasValue()) throw new IllegalArgumentException("Params 'from' and 'to' are mandatory"); - ObjectAnimator animator = ObjectAnimator.ofFloat(view, animProp, from.get(), to.get()); + ObjectAnimator animator = ObjectAnimator.ofFloat(view, + animProp, + from.get() + fromDelta.get(), + to.get() + toDelta.get() + ); animator.setInterpolator(interpolation.getInterpolator()); if (duration.hasValue()) animator.setDuration(duration.get()); if (startDelay.hasValue()) animator.setStartDelay(startDelay.get()); @@ -56,6 +70,10 @@ public boolean equals(Object o) { return animProp.equals(((ValueAnimationOptions) o).animProp); } + public boolean equals(Property animationProperty) { + return animProp.getName().equals(animationProperty.getName()); + } + @Override public int hashCode() { return animProp.hashCode(); diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java index b00108d9d71..6544eacb98e 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java @@ -1,7 +1,7 @@ package com.reactnativenavigation.parse.params; import android.graphics.Typeface; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.view.MenuItem; import com.reactnativenavigation.parse.Component; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Colour.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Colour.java index a942c689bab..5a023beecb1 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Colour.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Colour.java @@ -1,6 +1,7 @@ package com.reactnativenavigation.parse.params; -import android.support.annotation.ColorInt; +import android.graphics.Color; +import androidx.annotation.ColorInt; public class Colour extends Param{ @@ -13,4 +14,8 @@ public Colour(@ColorInt int color) { public String toString() { return String.format("#%06X", (0xFFFFFF & get())); } + + public boolean hasTransparency() { + return hasValue() && Color.alpha(value) < 1; + } } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Orientation.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Orientation.java index aad91973beb..cab27f4d1c6 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Orientation.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Orientation.java @@ -1,7 +1,7 @@ package com.reactnativenavigation.parse.params; import android.content.pm.ActivityInfo; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; public enum Orientation { Portrait("portrait", ActivityInfo.SCREEN_ORIENTATION_PORTRAIT), diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Text.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Text.java index b44ba97de72..e8b996c6775 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Text.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Text.java @@ -1,6 +1,6 @@ package com.reactnativenavigation.parse.params; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; public class Text extends Param { public Text(String value) { diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/TitleDisplayMode.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/TitleDisplayMode.java index 4c655264888..17163521784 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/TitleDisplayMode.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/params/TitleDisplayMode.java @@ -1,6 +1,6 @@ package com.reactnativenavigation.parse.params; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/LayoutNodeParser.java b/lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/LayoutNodeParser.java index d95505fdd73..890fb08abc9 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/LayoutNodeParser.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/LayoutNodeParser.java @@ -1,6 +1,6 @@ package com.reactnativenavigation.parse.parsers; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.reactnativenavigation.parse.LayoutNode; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabPresenter.java b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabPresenter.java index 002f55596d0..0a39314462f 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabPresenter.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabPresenter.java @@ -2,7 +2,6 @@ import android.content.Context; import android.graphics.drawable.Drawable; -import android.support.annotation.NonNull; import com.aurelhubert.ahbottomnavigation.notification.AHNotification; import com.reactnativenavigation.parse.BottomTabOptions; @@ -10,13 +9,16 @@ import com.reactnativenavigation.parse.Options; import com.reactnativenavigation.utils.ImageLoader; import com.reactnativenavigation.utils.ImageLoadingListenerAdapter; +import com.reactnativenavigation.utils.LateInit; import com.reactnativenavigation.viewcontrollers.ViewController; import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabFinder; import com.reactnativenavigation.views.BottomTabs; -import com.reactnativenavigation.views.Component; import java.util.List; +import androidx.annotation.NonNull; + +import static com.reactnativenavigation.utils.CollectionUtils.*; import static com.reactnativenavigation.utils.UiUtils.dpToPx; public class BottomTabPresenter { @@ -24,7 +26,7 @@ public class BottomTabPresenter { private ImageLoader imageLoader; private Options defaultOptions; private final BottomTabFinder bottomTabFinder; - private BottomTabs bottomTabs; + private LateInit bottomTabs = new LateInit<>(); private final List tabs; private final int defaultDotIndicatorSize; @@ -42,74 +44,94 @@ public void setDefaultOptions(Options defaultOptions) { } public void bindView(BottomTabs bottomTabs) { - this.bottomTabs = bottomTabs; + this.bottomTabs.set(bottomTabs); } public void applyOptions() { - for (int i = 0; i < tabs.size(); i++) { - BottomTabOptions tab = tabs.get(i).resolveCurrentOptions(defaultOptions).bottomTabOptions; - bottomTabs.setTitleTypeface(i, tab.fontFamily); - bottomTabs.setIconActiveColor(i, tab.selectedIconColor.get(null)); - bottomTabs.setIconInactiveColor(i, tab.iconColor.get(null)); - bottomTabs.setTitleActiveColor(i, tab.selectedTextColor.get(null)); - bottomTabs.setTitleInactiveColor(i, tab.textColor.get(null)); - bottomTabs.setTitleInactiveTextSizeInSp(i, tab.fontSize.hasValue() ? Float.valueOf(tab.fontSize.get()) : null); - bottomTabs.setTitleActiveTextSizeInSp(i, tab.selectedFontSize.hasValue() ? Float.valueOf(tab.selectedFontSize.get()) : null); - if (tab.testId.hasValue()) bottomTabs.setTag(i, tab.testId.get()); - if (shouldApplyDot(tab)) applyDotIndicator(i, tab.dotIndicator); else applyBadge(i, tab); - } + bottomTabs.perform(bottomTabs -> { + for (int i = 0; i < tabs.size(); i++) { + BottomTabOptions tab = tabs.get(i).resolveCurrentOptions(defaultOptions).bottomTabOptions; + bottomTabs.setTitleTypeface(i, tab.fontFamily); + bottomTabs.setIconActiveColor(i, tab.selectedIconColor.get(null)); + bottomTabs.setIconInactiveColor(i, tab.iconColor.get(null)); + bottomTabs.setTitleActiveColor(i, tab.selectedTextColor.get(null)); + bottomTabs.setTitleInactiveColor(i, tab.textColor.get(null)); + bottomTabs.setTitleInactiveTextSizeInSp(i, tab.fontSize.hasValue() ? Float.valueOf(tab.fontSize.get()) : null); + bottomTabs.setTitleActiveTextSizeInSp(i, tab.selectedFontSize.hasValue() ? Float.valueOf(tab.selectedFontSize.get()) : null); + if (tab.testId.hasValue()) bottomTabs.setTag(i, tab.testId.get()); + if (shouldApplyDot(tab)) applyDotIndicator(i, tab.dotIndicator); else applyBadge(i, tab); + } + }); + } + + public void mergeOptions(Options options) { + bottomTabs.perform(bottomTabs -> { + bottomTabs.disableItemsCreation(); + forEach(tabs, tab -> mergeChildOptions(options, tab)); + bottomTabs.enableItemsCreation(); + }); } - public void mergeChildOptions(Options options, Component child) { - int index = bottomTabFinder.findByComponent(child); - if (index >= 0) { - BottomTabOptions tab = options.bottomTabOptions; - if (tab.fontFamily != null) bottomTabs.setTitleTypeface(index, tab.fontFamily); - if (tab.selectedIconColor.hasValue()) bottomTabs.setIconActiveColor(index, tab.selectedIconColor.get()); - if (tab.iconColor.hasValue()) bottomTabs.setIconInactiveColor(index, tab.iconColor.get()); - if (tab.selectedTextColor.hasValue()) bottomTabs.setTitleActiveColor(index, tab.selectedTextColor.get()); - if (tab.textColor.hasValue()) bottomTabs.setTitleInactiveColor(index, tab.textColor.get()); - if (tab.text.hasValue()) bottomTabs.setText(index, tab.text.get()); - if (tab.icon.hasValue()) imageLoader.loadIcon(context, tab.icon.get(), new ImageLoadingListenerAdapter() { - @Override - public void onComplete(@NonNull Drawable drawable) { - bottomTabs.setIcon(index, drawable); - } - }); - if (tab.testId.hasValue()) bottomTabs.setTag(index, tab.testId.get()); - if (shouldApplyDot(tab)) mergeDotIndicator(index, tab.dotIndicator); else mergeBadge(index, tab); - } + public void mergeChildOptions(Options options, ViewController child) { + bottomTabs.perform(bottomTabs -> { + int index = bottomTabFinder.findByControllerId(child.getId()); + if (index >= 0) { + BottomTabOptions tab = options.bottomTabOptions; + if (tab.fontFamily != null) bottomTabs.setTitleTypeface(index, tab.fontFamily); + if (tab.selectedIconColor.hasValue()) bottomTabs.setIconActiveColor(index, tab.selectedIconColor.get()); + if (tab.iconColor.hasValue()) bottomTabs.setIconInactiveColor(index, tab.iconColor.get()); + if (tab.selectedTextColor.hasValue()) bottomTabs.setTitleActiveColor(index, tab.selectedTextColor.get()); + if (tab.textColor.hasValue()) bottomTabs.setTitleInactiveColor(index, tab.textColor.get()); + if (tab.text.hasValue()) bottomTabs.setText(index, tab.text.get()); + if (tab.icon.hasValue()) imageLoader.loadIcon(context, tab.icon.get(), new ImageLoadingListenerAdapter() { + @Override + public void onComplete(@NonNull Drawable drawable) { + bottomTabs.setIcon(index, drawable); + } + }); + if (tab.testId.hasValue()) bottomTabs.setTag(index, tab.testId.get()); + if (shouldApplyDot(tab)) mergeDotIndicator(index, tab.dotIndicator); else mergeBadge(index, tab); + } + }); } private void applyDotIndicator(int tabIndex, DotIndicatorOptions dotIndicator) { AHNotification.Builder builder = new AHNotification.Builder() .setText("") .setBackgroundColor(dotIndicator.color.get(null)) - .setSize(dotIndicator.size.get(defaultDotIndicatorSize)); - bottomTabs.setNotification(builder.build(), tabIndex); + .setSize(dotIndicator.size.get(defaultDotIndicatorSize)) + .animate(dotIndicator.animate.get(false)); + bottomTabs.perform(bottomTabs -> bottomTabs.setNotification(builder.build(), tabIndex)); } private void applyBadge(int tabIndex, BottomTabOptions tab) { + if (bottomTabs == null) return; AHNotification.Builder builder = new AHNotification.Builder() .setText(tab.badge.get("")) - .setBackgroundColor(tab.badgeColor.get(null)); - bottomTabs.setNotification(builder.build(), tabIndex); + .setBackgroundColor(tab.badgeColor.get(null)) + .animate(tab.animateBadge.get(false)); + bottomTabs.perform(bottomTabs -> bottomTabs.setNotification(builder.build(), tabIndex)); } private void mergeBadge(int index, BottomTabOptions tab) { + if (bottomTabs == null) return; if (!tab.badge.hasValue()) return; AHNotification.Builder builder = new AHNotification.Builder(); if (tab.badge.hasValue()) builder.setText(tab.badge.get()); if (tab.badgeColor.hasValue()) builder.setBackgroundColor(tab.badgeColor.get()); - bottomTabs.setNotification(builder.build(), index); + if (tab.badgeColor.hasValue()) builder.setBackgroundColor(tab.badgeColor.get()); + if (tab.animateBadge.hasValue()) builder.animate(tab.animateBadge.get()); + bottomTabs.perform(bottomTabs -> bottomTabs.setNotification(builder.build(), index)); } private void mergeDotIndicator(int index, DotIndicatorOptions dotIndicator) { + if (bottomTabs == null) return; AHNotification.Builder builder = new AHNotification.Builder(); if (dotIndicator.color.hasValue()) builder.setBackgroundColor(dotIndicator.color.get()); builder.setSize(dotIndicator.visible.isFalse() ? 0 : dotIndicator.size.get(defaultDotIndicatorSize)); + if (dotIndicator.animate.hasValue()) builder.animate(dotIndicator.animate.get()); AHNotification notification = builder.build(); - if (notification.hasValue()) bottomTabs.setNotification(notification, index); + if (notification.hasValue()) bottomTabs.perform(bottomTabs -> bottomTabs.setNotification(notification, index)); } private boolean shouldApplyDot(BottomTabOptions tab) { diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsPresenter.java b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsPresenter.java index b0d3a3130e4..d8b21fba479 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsPresenter.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsPresenter.java @@ -1,9 +1,7 @@ package com.reactnativenavigation.presentation; import android.graphics.Color; -import android.support.annotation.IntRange; import android.view.ViewGroup; -import android.view.ViewGroup.MarginLayoutParams; import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState; import com.reactnativenavigation.anim.BottomTabsAnimator; @@ -14,11 +12,10 @@ import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabFinder; import com.reactnativenavigation.viewcontrollers.bottomtabs.TabSelector; import com.reactnativenavigation.views.BottomTabs; -import com.reactnativenavigation.views.Component; import java.util.List; -import static com.reactnativenavigation.utils.ViewUtils.getHeight; +import androidx.annotation.IntRange; public class BottomTabsPresenter { private final BottomTabFinder bottomTabFinder; @@ -44,11 +41,6 @@ public void bindView(BottomTabs bottomTabs, TabSelector tabSelector) { animator = new BottomTabsAnimator(bottomTabs); } - public void applyLayoutParamsOptions(Options options, int tabIndex) { - Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions); - applyDrawBehind(withDefaultOptions.bottomTabsOptions, tabIndex); - } - public void mergeOptions(Options options) { mergeBottomTabsOptions(options); } @@ -57,19 +49,18 @@ public void applyOptions(Options options) { applyBottomTabsOptions(options.copy().withDefaultOptions(defaultOptions)); } - public void applyChildOptions(Options options, Component child) { - int tabIndex = bottomTabFinder.findByComponent(child); + public void applyChildOptions(Options options, ViewController child) { + int tabIndex = bottomTabFinder.findByControllerId(child.getId()); if (tabIndex >= 0) { - Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions); - applyBottomTabsOptions(withDefaultOptions); - applyDrawBehind(withDefaultOptions.bottomTabsOptions, tabIndex); + applyBottomTabsOptions(options.copy().withDefaultOptions(defaultOptions)); + applyDrawBehind(tabIndex); } } - public void mergeChildOptions(Options options, Component child) { + public void mergeChildOptions(Options options, ViewController child) { mergeBottomTabsOptions(options); - int tabIndex = bottomTabFinder.findByComponent(child); - if (tabIndex >= 0) mergeDrawBehind(options.bottomTabsOptions, tabIndex); + int tabIndex = bottomTabFinder.findByControllerId(child.getId()); + if (tabIndex >= 0) mergeDrawBehind(tabIndex); } private void mergeBottomTabsOptions(Options options) { @@ -110,24 +101,12 @@ private void mergeBottomTabsOptions(Options options) { } } - private void applyDrawBehind(BottomTabsOptions options, @IntRange(from = 0) int tabIndex) { - ViewGroup tab = tabs.get(tabIndex).getView(); - MarginLayoutParams lp = (MarginLayoutParams) tab.getLayoutParams(); - if (options.drawBehind.isTrue()) { - lp.bottomMargin = 0; - } else if (options.visible.isTrueOrUndefined()) { - lp.bottomMargin = getHeight(bottomTabs); - } + private void applyDrawBehind(@IntRange(from = 0) int tabIndex) { + tabs.get(tabIndex).applyBottomInset(); } - private void mergeDrawBehind(BottomTabsOptions options, int tabIndex) { - ViewGroup tab = tabs.get(tabIndex).getView(); - MarginLayoutParams lp = (MarginLayoutParams) tab.getLayoutParams(); - if (options.drawBehind.isTrue()) { - lp.bottomMargin = 0; - } else if (options.visible.isTrue() && options.drawBehind.isFalse()) { - lp.bottomMargin = getHeight(bottomTabs); - } + private void mergeDrawBehind(int tabIndex) { + tabs.get(tabIndex).applyBottomInset(); } private void applyBottomTabsOptions(Options options) { @@ -164,4 +143,10 @@ private void applyBottomTabsOptions(Options options) { bottomTabs.setUseElevation(true, bottomTabsOptions.elevation.get().floatValue()); } } + + public void applyBottomInset(int bottomInset) { + ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) bottomTabs.getLayoutParams(); + lp.bottomMargin = bottomInset; + bottomTabs.requestLayout(); + } } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/ComponentPresenter.java b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/ComponentPresenter.java index 8c6d060614f..b29f91fb688 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/ComponentPresenter.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/ComponentPresenter.java @@ -3,7 +3,7 @@ import com.reactnativenavigation.parse.Options; import com.reactnativenavigation.views.ComponentLayout; -public class ComponentPresenter { +public class ComponentPresenter extends ComponentPresenterBase { public Options defaultOptions; public ComponentPresenter(Options defaultOptions) { diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/ComponentPresenterBase.java b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/ComponentPresenterBase.java new file mode 100644 index 00000000000..bc5fc17b7bf --- /dev/null +++ b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/ComponentPresenterBase.java @@ -0,0 +1,23 @@ +package com.reactnativenavigation.presentation; + +import androidx.annotation.NonNull; +import android.view.View; +import android.view.ViewGroup.MarginLayoutParams; + +public class ComponentPresenterBase { + public void applyTopInsets(@NonNull View view, int topInsets) { + MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams(); + if (lp.topMargin != topInsets) { + lp.topMargin = topInsets; + view.requestLayout(); + } + } + + public void applyBottomInset(@NonNull View view, int bottomInset) { + MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams(); + if (lp.bottomMargin!= bottomInset) { + lp.bottomMargin = bottomInset; + view.requestLayout(); + } + } +} diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/ExternalComponentPresenter.java b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/ExternalComponentPresenter.java new file mode 100644 index 00000000000..b099981e039 --- /dev/null +++ b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/ExternalComponentPresenter.java @@ -0,0 +1,4 @@ +package com.reactnativenavigation.presentation; + +public class ExternalComponentPresenter extends ComponentPresenterBase { +} diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/FabPresenter.java b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/FabPresenter.java index 2f57f55e48d..aeeb31cbc39 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/FabPresenter.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/FabPresenter.java @@ -1,7 +1,7 @@ package com.reactnativenavigation.presentation; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/OverlayManager.java b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/OverlayManager.java index 5eaf3e3e4ea..20ed1f2280f 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/OverlayManager.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/OverlayManager.java @@ -4,16 +4,19 @@ import com.reactnativenavigation.utils.CommandListener; import com.reactnativenavigation.viewcontrollers.ViewController; +import com.reactnativenavigation.views.BehaviourDelegate; import java.util.HashMap; +import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour; + public class OverlayManager { private final HashMap overlayRegistry = new HashMap<>(); public void show(ViewGroup overlaysContainer, ViewController overlay, CommandListener listener) { overlayRegistry.put(overlay.getId(), overlay); overlay.addOnAppearedListener(() -> listener.onSuccess(overlay.getId())); - overlaysContainer.addView(overlay.getView()); + overlaysContainer.addView(overlay.getView(), matchParentWithBehaviour(new BehaviourDelegate(overlay))); } public void dismiss(String componentId, CommandListener listener) { diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/Presenter.java b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/Presenter.java index 8802b0760ec..a914db6e4d6 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/Presenter.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/Presenter.java @@ -2,16 +2,25 @@ import android.app.Activity; import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; import android.os.Build; import android.view.View; import android.view.ViewGroup.MarginLayoutParams; +import android.view.Window; import com.reactnativenavigation.parse.Options; import com.reactnativenavigation.parse.OrientationOptions; import com.reactnativenavigation.parse.StatusBarOptions; import com.reactnativenavigation.parse.StatusBarOptions.TextColorScheme; import com.reactnativenavigation.parse.params.Bool; -import com.reactnativenavigation.utils.UiUtils; +import com.reactnativenavigation.utils.StatusBarUtils; +import com.reactnativenavigation.viewcontrollers.ParentController; +import com.reactnativenavigation.viewcontrollers.ViewController; +import com.reactnativenavigation.viewcontrollers.navigator.Navigator; + +import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; @SuppressWarnings("FieldCanBeLocal") public class Presenter { @@ -32,32 +41,25 @@ public void mergeOptions(View view, Options options) { mergeStatusBarOptions(view, options.statusBar); } - public void applyOptions(View view, Options options) { + public void applyOptions(ViewController view, Options options) { Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions); applyOrientation(withDefaultOptions.layout.orientation); applyViewOptions(view, withDefaultOptions); - applyStatusBarOptions(view, withDefaultOptions.statusBar); - } - - public void applyRootOptions(View view, Options options) { - Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions); - setDrawBehindStatusBar(view, withDefaultOptions.statusBar); + applyStatusBarOptions(withDefaultOptions); } - public void onViewBroughtToFront(View view, Options options) { + public void onViewBroughtToFront(Options options) { Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions); - applyStatusBarOptions(view, withDefaultOptions.statusBar); + applyStatusBarOptions(withDefaultOptions); } private void applyOrientation(OrientationOptions options) { activity.setRequestedOrientation(options.getValue()); } - private void applyViewOptions(View view, Options options) { - if (options.layout.backgroundColor.hasValue()) { - view.setBackgroundColor(options.layout.backgroundColor.get()); - } - applyTopMargin(view, options); + private void applyViewOptions(ViewController view, Options options) { + applyBackgroundColor(view, options); + applyTopMargin(view.getView(), options); } private void applyTopMargin(View view, Options options) { @@ -66,23 +68,52 @@ private void applyTopMargin(View view, Options options) { } } - private void applyStatusBarOptions(View view, StatusBarOptions statusBar) { - setStatusBarBackgroundColor(statusBar); - setTextColorScheme(statusBar.textColorScheme); - setStatusBarVisible(view, statusBar.visible, statusBar.drawBehind); + private void applyBackgroundColor(ViewController view, Options options) { + if (options.layout.backgroundColor.hasValue()) { + if (view instanceof Navigator) return; + + LayerDrawable ld = new LayerDrawable(new Drawable[]{new ColorDrawable(options.layout.backgroundColor.get())}); + int top = view.resolveCurrentOptions().statusBar.drawBehind.isTrue() ? 0 : StatusBarUtils.getStatusBarHeight(view.getActivity()); + if (!(view instanceof ParentController)) { + MarginLayoutParams lp = (MarginLayoutParams) view.getView().getLayoutParams(); + if (lp.topMargin != 0) top = 0; + } + ld.setLayerInset(0, 0, top, 0, 0); + view.getView().setBackground(ld); + } } - private void setStatusBarVisible(View view, Bool visible, Bool drawBehind) { + private void applyStatusBarOptions(Options options) { + setStatusBarBackgroundColor(options.statusBar); + setTextColorScheme(options.statusBar.textColorScheme); + setTranslucent(options.statusBar); + setStatusBarVisible(options.statusBar.visible); + } + + private void setTranslucent(StatusBarOptions options) { + Window window = activity.getWindow(); + if (options.translucent.isTrue()) { + window.setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS); + } else { + window.clearFlags(FLAG_TRANSLUCENT_STATUS); + } + } + + private void setStatusBarVisible(Bool visible) { + View decorView = activity.getWindow().getDecorView(); + int flags = decorView.getSystemUiVisibility(); if (visible.isFalse()) { - view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_FULLSCREEN); - } else if (drawBehind.isTrue()) { - view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + flags |= View.SYSTEM_UI_FLAG_FULLSCREEN; + } else { + flags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; } + decorView.setSystemUiVisibility(flags); } private void setStatusBarBackgroundColor(StatusBarOptions statusBar) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - activity.getWindow().setStatusBarColor(statusBar.backgroundColor.get(Color.BLACK)); + int defaultColor = statusBar.visible.isTrueOrUndefined() ? Color.BLACK : Color.TRANSPARENT; + activity.getWindow().setStatusBarColor(statusBar.backgroundColor.get(defaultColor)); } } @@ -105,17 +136,10 @@ private static void clearDarkTextColorScheme(View view) { view.setSystemUiVisibility(flags); } - private void setDrawBehindStatusBar(View view, StatusBarOptions statusBar) { - if (statusBar.visible.isFalse()) { - ((MarginLayoutParams) view.getLayoutParams()).topMargin = statusBar.drawBehind.isTrue() ? - 0 : UiUtils.getStatusBarHeight(activity); - } - } - - private void mergeStatusBarOptions(View view, StatusBarOptions statusBar) { mergeStatusBarBackgroundColor(statusBar); mergeTextColorScheme(statusBar.textColorScheme); + mergeTranslucent(statusBar); mergeStatusBarVisible(view, statusBar.visible, statusBar.drawBehind); } @@ -137,6 +161,15 @@ private void mergeTextColorScheme(TextColorScheme scheme) { } } + private void mergeTranslucent(StatusBarOptions options) { + Window window = activity.getWindow(); + if (options.translucent.isTrue()) { + window.setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS); + } else if (options.translucent.isFalse()) { + window.clearFlags(FLAG_TRANSLUCENT_STATUS); + } + } + private void mergeStatusBarVisible(View view, Bool visible, Bool drawBehind) { if (visible.hasValue()) { int flags = view.getSystemUiVisibility(); diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/RootPresenter.java b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/RootPresenter.java index d86c9460379..75e80d08161 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/RootPresenter.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/RootPresenter.java @@ -1,21 +1,26 @@ package com.reactnativenavigation.presentation; import android.content.Context; -import android.widget.FrameLayout; import com.facebook.react.ReactInstanceManager; import com.reactnativenavigation.anim.NavigationAnimator; import com.reactnativenavigation.parse.Options; import com.reactnativenavigation.utils.CommandListener; import com.reactnativenavigation.viewcontrollers.ViewController; +import com.reactnativenavigation.views.BehaviourDelegate; import com.reactnativenavigation.views.element.ElementTransitionManager; +import androidx.annotation.VisibleForTesting; +import androidx.coordinatorlayout.widget.CoordinatorLayout; + +import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour; + public class RootPresenter { private NavigationAnimator animator; + private CoordinatorLayout rootLayout; private LayoutDirectionApplier layoutDirectionApplier; - private FrameLayout rootLayout; - public void setRootContainer(FrameLayout rootLayout) { + public void setRootContainer(CoordinatorLayout rootLayout) { this.rootLayout = rootLayout; } @@ -23,6 +28,7 @@ public RootPresenter(Context context) { this(new NavigationAnimator(context, new ElementTransitionManager()), new LayoutDirectionApplier()); } + @VisibleForTesting public RootPresenter(NavigationAnimator animator, LayoutDirectionApplier layoutDirectionApplier) { this.animator = animator; this.layoutDirectionApplier = layoutDirectionApplier; @@ -30,7 +36,7 @@ public RootPresenter(NavigationAnimator animator, LayoutDirectionApplier layoutD public void setRoot(ViewController root, Options defaultOptions, CommandListener listener, ReactInstanceManager reactInstanceManager) { layoutDirectionApplier.apply(root, defaultOptions, reactInstanceManager); - rootLayout.addView(root.getView()); + rootLayout.addView(root.getView(), matchParentWithBehaviour(new BehaviourDelegate(root))); Options options = root.resolveCurrentOptions(defaultOptions); root.setWaitForRender(options.animations.setRoot.waitForRender); if (options.animations.setRoot.waitForRender.isTrue()) { diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/SideMenuPresenter.java b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/SideMenuPresenter.java index 599bbca3dad..a58981ae7be 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/SideMenuPresenter.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/SideMenuPresenter.java @@ -1,16 +1,19 @@ package com.reactnativenavigation.presentation; -import android.support.v4.widget.DrawerLayout; import android.view.Gravity; import com.reactnativenavigation.parse.Options; import com.reactnativenavigation.parse.SideMenuRootOptions; +import com.reactnativenavigation.views.SideMenu; + +import androidx.annotation.RestrictTo; +import androidx.drawerlayout.widget.DrawerLayout; public class SideMenuPresenter { - private DrawerLayout sideMenu; + private SideMenu sideMenu; - public void bindView(DrawerLayout sideMenu) { + public void bindView(SideMenu sideMenu) { this.sideMenu = sideMenu; } @@ -76,4 +79,9 @@ private void mergeLockMode(SideMenuRootOptions options) { sideMenu.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT); } } + + @RestrictTo(RestrictTo.Scope.TESTS) + public SideMenu getSideMenu() { + return sideMenu; + } } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackPresenter.java b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackPresenter.java index eb025c45413..8147225adad 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackPresenter.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackPresenter.java @@ -2,9 +2,6 @@ import android.app.Activity; import android.graphics.Color; -import android.support.annotation.Nullable; -import android.support.annotation.RestrictTo; -import android.support.v7.widget.Toolbar; import android.view.Gravity; import android.view.View; import android.view.ViewGroup.LayoutParams; @@ -26,6 +23,7 @@ import com.reactnativenavigation.utils.CollectionUtils; import com.reactnativenavigation.utils.ImageLoader; import com.reactnativenavigation.utils.ObjectUtils; +import com.reactnativenavigation.utils.StatusBarUtils; import com.reactnativenavigation.utils.UiUtils; import com.reactnativenavigation.viewcontrollers.IReactView; import com.reactnativenavigation.viewcontrollers.ReactViewCreator; @@ -33,8 +31,9 @@ import com.reactnativenavigation.viewcontrollers.TitleBarReactViewController; import com.reactnativenavigation.viewcontrollers.ViewController; import com.reactnativenavigation.viewcontrollers.button.NavigationIconResolver; +import com.reactnativenavigation.viewcontrollers.stack.StackController; import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController; -import com.reactnativenavigation.views.Component; +import com.reactnativenavigation.viewcontrollers.topbar.TopBarController; import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator; import com.reactnativenavigation.views.topbar.TopBar; import com.reactnativenavigation.views.topbar.TopBarBackgroundViewCreator; @@ -46,6 +45,10 @@ import java.util.List; import java.util.Map; +import androidx.annotation.Nullable; +import androidx.annotation.RestrictTo; +import androidx.appcompat.widget.Toolbar; + import static com.reactnativenavigation.utils.CollectionUtils.*; import static com.reactnativenavigation.utils.ObjectUtils.perform; @@ -59,6 +62,7 @@ public class StackPresenter { private final Activity activity; private TopBar topBar; + private TopBarController topBarController; private final TitleBarReactViewCreator titleViewCreator; private TitleBarButtonController.OnClickListener onClickListener; private final ImageLoader imageLoader; @@ -66,11 +70,12 @@ public class StackPresenter { private final TopBarBackgroundViewCreator topBarBackgroundViewCreator; private final ReactViewCreator buttonCreator; private Options defaultOptions; - private Map titleControllers = new HashMap(); - private Map backgroundControllers = new HashMap(); - private Map> componentRightButtons = new HashMap(); - private Map> componentLeftButtons = new HashMap(); + private List currentRightButtons = new ArrayList<>(); + private Map titleControllers = new HashMap(); + private Map backgroundControllers = new HashMap(); + private Map> componentRightButtons = new HashMap(); + private Map> componentLeftButtons = new HashMap(); public StackPresenter(Activity activity, TitleBarReactViewCreator titleViewCreator, @@ -102,32 +107,22 @@ public Options getDefaultOptions() { return defaultOptions; } - public void bindView(TopBar topBar) { - this.topBar = topBar; + public void bindView(TopBarController topBarController) { + this.topBarController = topBarController; + topBar = topBarController.getView(); } - public boolean isRendered(Component component) { + public boolean isRendered(View component) { ArrayList controllers = new ArrayList<>(perform(componentRightButtons.get(component), new ArrayList<>(), Map::values)); controllers.add(backgroundControllers.get(component)); controllers.add(titleControllers.get(component)); return renderChecker.areRendered(filter(controllers, ObjectUtils::notNull)); } - public void applyLayoutParamsOptions(Options options, View view) { - Options withDefault = options.copy().withDefaultOptions(defaultOptions); - if (view instanceof Component) { - if (withDefault.topBar.drawBehind.isTrue() && !withDefault.layout.topMargin.hasValue()) { - ((Component) view).drawBehindTopBar(); - } else if (options.topBar.drawBehind.isFalseOrUndefined()) { - ((Component) view).drawBelowTopBar(topBar); - } - } - } - - public void mergeOptions(Options options, Component currentChild) { + public void mergeOptions(Options options, StackController stack, ViewController currentChild) { mergeOrientation(options.layout.orientation); // mergeButtons(topBar, withDefault.topBar.buttons, child); - mergeTopBarOptions(options, currentChild); + mergeTopBarOptions(options, stack, currentChild); mergeTopTabsOptions(options.topTabs); mergeTopTabOptions(options.topTabOptions); } @@ -137,11 +132,11 @@ public void applyInitialChildLayoutOptions(Options options) { setInitialTopBarVisibility(withDefault.topBar); } - public void applyChildOptions(Options options, Component child) { + public void applyChildOptions(Options options, StackController stack, ViewController child) { Options withDefault = options.copy().withDefaultOptions(defaultOptions); applyOrientation(withDefault.layout.orientation); applyButtons(withDefault.topBar, child); - applyTopBarOptions(withDefault, child, options); + applyTopBarOptions(withDefault, stack, child, options); applyTopTabsOptions(withDefault.topTabs); applyTopTabOptions(withDefault.topTabOptions); } @@ -151,23 +146,25 @@ public void applyOrientation(OrientationOptions options) { ((Activity) topBar.getContext()).setRequestedOrientation(withDefaultOptions.getValue()); } - public void onChildDestroyed(Component child) { - perform(titleControllers.remove(child), TitleBarReactViewController::destroy); - perform(backgroundControllers.remove(child), TopBarBackgroundViewController::destroy); - destroyButtons(componentRightButtons.get(child)); - destroyButtons(componentLeftButtons.get(child)); - componentRightButtons.remove(child); - componentLeftButtons.remove(child); + public void onChildDestroyed(ViewController child) { + perform(titleControllers.remove(child.getView()), TitleBarReactViewController::destroy); + perform(backgroundControllers.remove(child.getView()), TopBarBackgroundViewController::destroy); + destroyButtons(componentRightButtons.get(child.getView())); + destroyButtons(componentLeftButtons.get(child.getView())); + componentRightButtons.remove(child.getView()); + componentLeftButtons.remove(child.getView()); } private void destroyButtons(@Nullable Map buttons) { if (buttons != null) forEach(buttons.values(), ViewController::destroy); } - private void applyTopBarOptions(Options options, Component component, Options componentOptions) { + private void applyTopBarOptions(Options options, StackController stack, ViewController child, Options componentOptions) { + final View component = child.getView(); TopBarOptions topBarOptions = options.topBar; AnimationsOptions animationOptions = options.animations; + topBar.setTestId(topBarOptions.testId.get("")); topBar.setLayoutDirection(options.layout.direction); topBar.setHeight(topBarOptions.height.get(UiUtils.getTopBarHeightDp(activity))); topBar.setElevation(topBarOptions.elevation.get(DEFAULT_ELEVATION)); @@ -224,13 +221,7 @@ private void applyTopBarOptions(Options options, Component component, Options co topBar.clearBackgroundComponent(); } - if (topBarOptions.testId.hasValue()) topBar.setTestId(topBarOptions.testId.get()); - applyTopBarVisibility(topBarOptions, animationOptions, componentOptions); - if (topBarOptions.drawBehind.isTrue() && !componentOptions.layout.topMargin.hasValue()) { - component.drawBehindTopBar(); - } else if (topBarOptions.drawBehind.isFalseOrUndefined()) { - component.drawBelowTopBar(topBar); - } + applyTopBarVisibility(topBarOptions, animationOptions, componentOptions, stack, child); if (topBarOptions.hideOnScroll.isTrue()) { if (component instanceof IReactView) { topBar.enableCollapse(((IReactView) component).getScrollEventListener()); @@ -253,37 +244,38 @@ private View findBackgroundComponent(com.reactnativenavigation.parse.Component c private void setInitialTopBarVisibility(TopBarOptions options) { if (options.visible.isFalse()) { - topBar.hide(); + topBarController.hide(); } if (options.visible.isTrueOrUndefined()) { - topBar.show(); + topBarController.show(); } } - private void applyTopBarVisibility(TopBarOptions options, AnimationsOptions animationOptions, Options componentOptions) { + private void applyTopBarVisibility(TopBarOptions options, AnimationsOptions animationOptions, Options componentOptions, StackController stack, ViewController child) { if (options.visible.isFalse()) { + topBarController.resetViewProperties(); if (options.animate.isTrueOrUndefined() && componentOptions.animations.push.enabled.isTrueOrUndefined()) { - topBar.hideAnimate(animationOptions.pop.topBar); + topBarController.hideAnimate(animationOptions.pop.topBar, 0, getTopBarTranslationAnimationDelta(stack, child)); } else { - topBar.hide(); + topBarController.hide(); } - } - if (options.visible.isTrueOrUndefined()) { + } else if (options.visible.isTrueOrUndefined()) { + topBarController.resetViewProperties(); if (options.animate.isTrueOrUndefined() && componentOptions.animations.push.enabled.isTrueOrUndefined()) { - topBar.showAnimate(animationOptions.push.topBar); + topBarController.showAnimate(animationOptions.push.topBar, getTopBarTranslationAnimationDelta(stack, child)); } else { - topBar.show(); + topBarController.show(); } } } - private void applyButtons(TopBarOptions options, Component child) { + private void applyButtons(TopBarOptions options, ViewController child) { List