Browse Source

Apply layout direction directly on views (#5368)

Apply layout direction directly on views

This commit changes how RNN handles layout direction and adds support for LOCALE layout direction on Android.
Until now RNN delegated handling layout direction to RN's I18nUtil. This usually is enough but under certain circumstances
layout direction has to be applied directly on relevant views by RNN - usually when there are conflicts with another dependency
which handles RTL.
Guy Carmeli 5 years ago
parent
commit
fffd2d23f1
No account linked to committer's email address
16 changed files with 236 additions and 141 deletions
  1. 7
    2
      lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java
  2. 53
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutDirection.java
  3. 2
    6
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutOptions.java
  4. 0
    5
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabPresenter.java
  5. 41
    34
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsPresenter.java
  6. 16
    0
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/LayoutDirectionApplier.java
  7. 9
    22
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/RootPresenter.java
  8. 74
    66
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackPresenter.java
  9. 1
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java
  10. 10
    1
      lib/android/app/src/main/java/com/reactnativenavigation/views/BottomTabs.java
  11. 5
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/topbar/TopBar.java
  12. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsPresenterTest.java
  13. 1
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/NavigatorTest.java
  14. 13
    3
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.java
  15. 1
    0
      playground/android/app/src/main/AndroidManifest.xml
  16. 2
    1
      playground/src/commons/Options.js

+ 7
- 2
lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java View File

21
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
21
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
22
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
22
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
23
 import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
23
 import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
24
-import com.reactnativenavigation.viewcontrollers.navigator.RootPresenter;
24
+import com.reactnativenavigation.presentation.RootPresenter;
25
 
25
 
26
 public class NavigationActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler, PermissionAwareActivity, JsDevReloadHandler.ReloadListener {
26
 public class NavigationActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler, PermissionAwareActivity, JsDevReloadHandler.ReloadListener {
27
     @Nullable
27
     @Nullable
33
     protected void onCreate(@Nullable Bundle savedInstanceState) {
33
     protected void onCreate(@Nullable Bundle savedInstanceState) {
34
         super.onCreate(savedInstanceState);
34
         super.onCreate(savedInstanceState);
35
         addDefaultSplashLayout();
35
         addDefaultSplashLayout();
36
-        navigator = new Navigator(this, new ChildControllersRegistry(), new ModalStack(this), new OverlayManager(), new RootPresenter(this));
36
+        navigator = new Navigator(this,
37
+                new ChildControllersRegistry(),
38
+                new ModalStack(this),
39
+                new OverlayManager(),
40
+                new RootPresenter(this)
41
+        );
37
         navigator.bindViews();
42
         navigator.bindViews();
38
         getReactGateway().onActivityCreated(this);
43
         getReactGateway().onActivityCreated(this);
39
     }
44
     }

+ 53
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutDirection.java View File

1
+package com.reactnativenavigation.parse;
2
+
3
+import android.text.TextUtils;
4
+import android.view.View;
5
+
6
+import java.util.Locale;
7
+
8
+public enum LayoutDirection {
9
+    RTL(View.LAYOUT_DIRECTION_RTL),
10
+    LTR(View.LAYOUT_DIRECTION_LTR),
11
+    LOCALE(View.LAYOUT_DIRECTION_LOCALE),
12
+    DEFAULT(View.LAYOUT_DIRECTION_LTR);
13
+
14
+    private final int direction;
15
+
16
+    LayoutDirection(int direction) {
17
+        this.direction = direction;
18
+    }
19
+
20
+    public static LayoutDirection fromString(String direction) {
21
+        switch (direction) {
22
+            case "rtl":
23
+                return RTL;
24
+            case "ltr":
25
+                return LTR;
26
+            case "locale":
27
+                return LOCALE;
28
+            default:
29
+                return DEFAULT;
30
+        }
31
+    }
32
+
33
+    public boolean hasValue() {
34
+        return this != DEFAULT;
35
+    }
36
+
37
+    public int get() {
38
+        return direction;
39
+    }
40
+
41
+    public boolean isRtl() {
42
+        switch (direction) {
43
+            case View.LAYOUT_DIRECTION_LTR:
44
+                return false;
45
+            case View.LAYOUT_DIRECTION_RTL:
46
+                return true;
47
+            case View.LAYOUT_DIRECTION_LOCALE:
48
+                return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL;
49
+            default:
50
+                return false;
51
+        }
52
+    }
53
+}

+ 2
- 6
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutOptions.java View File

4
 import com.reactnativenavigation.parse.params.NullColor;
4
 import com.reactnativenavigation.parse.params.NullColor;
5
 import com.reactnativenavigation.parse.params.NullNumber;
5
 import com.reactnativenavigation.parse.params.NullNumber;
6
 import com.reactnativenavigation.parse.params.Number;
6
 import com.reactnativenavigation.parse.params.Number;
7
-import com.reactnativenavigation.parse.params.Text;
8
-import com.reactnativenavigation.parse.params.NullText;
9
 import com.reactnativenavigation.parse.parsers.ColorParser;
7
 import com.reactnativenavigation.parse.parsers.ColorParser;
10
 import com.reactnativenavigation.parse.parsers.NumberParser;
8
 import com.reactnativenavigation.parse.parsers.NumberParser;
11
-import com.reactnativenavigation.parse.parsers.TextParser;
12
-
13
 
9
 
14
 import org.json.JSONObject;
10
 import org.json.JSONObject;
15
 
11
 
22
         result.componentBackgroundColor = ColorParser.parse(json, "componentBackgroundColor");
18
         result.componentBackgroundColor = ColorParser.parse(json, "componentBackgroundColor");
23
         result.topMargin = NumberParser.parse(json, "topMargin");
19
         result.topMargin = NumberParser.parse(json, "topMargin");
24
         result.orientation = OrientationOptions.parse(json);
20
         result.orientation = OrientationOptions.parse(json);
25
-        result.direction = TextParser.parse(json, "direction");
21
+        result.direction = LayoutDirection.fromString(json.optString("direction", ""));
26
 
22
 
27
         return result;
23
         return result;
28
     }
24
     }
31
     public Colour componentBackgroundColor = new NullColor();
27
     public Colour componentBackgroundColor = new NullColor();
32
     public Number topMargin = new NullNumber();
28
     public Number topMargin = new NullNumber();
33
     public OrientationOptions orientation = new OrientationOptions();
29
     public OrientationOptions orientation = new OrientationOptions();
34
-    public Text direction = new NullText();
30
+    public LayoutDirection direction = LayoutDirection.DEFAULT;
35
 
31
 
36
     public void mergeWith(LayoutOptions other) {
32
     public void mergeWith(LayoutOptions other) {
37
         if (other.backgroundColor.hasValue()) backgroundColor = other.backgroundColor;
33
         if (other.backgroundColor.hasValue()) backgroundColor = other.backgroundColor;

+ 0
- 5
lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabPresenter.java View File

3
 import android.content.Context;
3
 import android.content.Context;
4
 import android.graphics.drawable.Drawable;
4
 import android.graphics.drawable.Drawable;
5
 import android.support.annotation.NonNull;
5
 import android.support.annotation.NonNull;
6
-import android.support.v4.content.ContextCompat;
7
 
6
 
8
 import com.aurelhubert.ahbottomnavigation.notification.AHNotification;
7
 import com.aurelhubert.ahbottomnavigation.notification.AHNotification;
9
 import com.reactnativenavigation.parse.BottomTabOptions;
8
 import com.reactnativenavigation.parse.BottomTabOptions;
26
     private Options defaultOptions;
25
     private Options defaultOptions;
27
     private final BottomTabFinder bottomTabFinder;
26
     private final BottomTabFinder bottomTabFinder;
28
     private BottomTabs bottomTabs;
27
     private BottomTabs bottomTabs;
29
-    private final int defaultSelectedTextColor;
30
-    private final int defaultTextColor;
31
     private final List<ViewController> tabs;
28
     private final List<ViewController> tabs;
32
     private final int defaultDotIndicatorSize;
29
     private final int defaultDotIndicatorSize;
33
 
30
 
37
         this.bottomTabFinder = new BottomTabFinder(tabs);
34
         this.bottomTabFinder = new BottomTabFinder(tabs);
38
         this.imageLoader = imageLoader;
35
         this.imageLoader = imageLoader;
39
         this.defaultOptions = defaultOptions;
36
         this.defaultOptions = defaultOptions;
40
-        defaultSelectedTextColor = defaultOptions.bottomTabOptions.selectedIconColor.get(ContextCompat.getColor(context, com.aurelhubert.ahbottomnavigation.R.color.colorBottomNavigationAccent));
41
-        defaultTextColor = defaultOptions.bottomTabOptions.iconColor.get(ContextCompat.getColor(context, com.aurelhubert.ahbottomnavigation.R.color.colorBottomNavigationInactive));
42
         defaultDotIndicatorSize = dpToPx(context, 6);
37
         defaultDotIndicatorSize = dpToPx(context, 6);
43
     }
38
     }
44
 
39
 

+ 41
- 34
lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsPresenter.java View File

50
     }
50
     }
51
 
51
 
52
     public void mergeOptions(Options options) {
52
     public void mergeOptions(Options options) {
53
-        mergeBottomTabsOptions(options.bottomTabsOptions, options.animations);
53
+        mergeBottomTabsOptions(options);
54
     }
54
     }
55
 
55
 
56
     public void applyOptions(Options options) {
56
     public void applyOptions(Options options) {
57
-        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
58
-        applyBottomTabsOptions(withDefaultOptions.bottomTabsOptions, withDefaultOptions.animations);
57
+        applyBottomTabsOptions(options.copy().withDefaultOptions(defaultOptions));
59
     }
58
     }
60
 
59
 
61
     public void applyChildOptions(Options options, Component child) {
60
     public void applyChildOptions(Options options, Component child) {
62
         int tabIndex = bottomTabFinder.findByComponent(child);
61
         int tabIndex = bottomTabFinder.findByComponent(child);
63
         if (tabIndex >= 0) {
62
         if (tabIndex >= 0) {
64
             Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
63
             Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
65
-            applyBottomTabsOptions(withDefaultOptions.bottomTabsOptions, withDefaultOptions.animations);
64
+            applyBottomTabsOptions(withDefaultOptions);
66
             applyDrawBehind(withDefaultOptions.bottomTabsOptions, tabIndex);
65
             applyDrawBehind(withDefaultOptions.bottomTabsOptions, tabIndex);
67
         }
66
         }
68
     }
67
     }
69
 
68
 
70
     public void mergeChildOptions(Options options, Component child) {
69
     public void mergeChildOptions(Options options, Component child) {
71
-        mergeBottomTabsOptions(options.bottomTabsOptions, options.animations);
70
+        mergeBottomTabsOptions(options);
72
         int tabIndex = bottomTabFinder.findByComponent(child);
71
         int tabIndex = bottomTabFinder.findByComponent(child);
73
         if (tabIndex >= 0) mergeDrawBehind(options.bottomTabsOptions, tabIndex);
72
         if (tabIndex >= 0) mergeDrawBehind(options.bottomTabsOptions, tabIndex);
74
     }
73
     }
75
 
74
 
76
-    private void mergeBottomTabsOptions(BottomTabsOptions options, AnimationsOptions animations) {
77
-        if (options.titleDisplayMode.hasValue()) {
78
-            bottomTabs.setTitleState(options.titleDisplayMode.toState());
75
+    private void mergeBottomTabsOptions(Options options) {
76
+        BottomTabsOptions bottomTabsOptions = options.bottomTabsOptions;
77
+        AnimationsOptions animations = options.animations;
78
+
79
+        if (options.layout.direction.hasValue()) bottomTabs.setLayoutDirection(options.layout.direction);
80
+        if (bottomTabsOptions.titleDisplayMode.hasValue()) {
81
+            bottomTabs.setTitleState(bottomTabsOptions.titleDisplayMode.toState());
79
         }
82
         }
80
-        if (options.backgroundColor.hasValue()) {
81
-            bottomTabs.setBackgroundColor(options.backgroundColor.get());
83
+        if (bottomTabsOptions.backgroundColor.hasValue()) {
84
+            bottomTabs.setBackgroundColor(bottomTabsOptions.backgroundColor.get());
82
         }
85
         }
83
-        if (options.currentTabIndex.hasValue()) {
84
-            int tabIndex = options.currentTabIndex.get();
86
+        if (bottomTabsOptions.currentTabIndex.hasValue()) {
87
+            int tabIndex = bottomTabsOptions.currentTabIndex.get();
85
             if (tabIndex >= 0) tabSelector.selectTab(tabIndex);
88
             if (tabIndex >= 0) tabSelector.selectTab(tabIndex);
86
         }
89
         }
87
-        if (options.testId.hasValue()) {
88
-            bottomTabs.setTag(options.testId.get());
90
+        if (bottomTabsOptions.testId.hasValue()) {
91
+            bottomTabs.setTag(bottomTabsOptions.testId.get());
89
         }
92
         }
90
-        if (options.currentTabId.hasValue()) {
91
-            int tabIndex = bottomTabFinder.findByControllerId(options.currentTabId.get());
93
+        if (bottomTabsOptions.currentTabId.hasValue()) {
94
+            int tabIndex = bottomTabFinder.findByControllerId(bottomTabsOptions.currentTabId.get());
92
             if (tabIndex >= 0) tabSelector.selectTab(tabIndex);
95
             if (tabIndex >= 0) tabSelector.selectTab(tabIndex);
93
         }
96
         }
94
-        if (options.visible.isTrue()) {
95
-            if (options.animate.isTrueOrUndefined()) {
97
+        if (bottomTabsOptions.visible.isTrue()) {
98
+            if (bottomTabsOptions.animate.isTrueOrUndefined()) {
96
                 animator.show(animations);
99
                 animator.show(animations);
97
             } else {
100
             } else {
98
                 bottomTabs.restoreBottomNavigation(false);
101
                 bottomTabs.restoreBottomNavigation(false);
99
             }
102
             }
100
         }
103
         }
101
-        if (options.visible.isFalse()) {
102
-            if (options.animate.isTrueOrUndefined()) {
104
+        if (bottomTabsOptions.visible.isFalse()) {
105
+            if (bottomTabsOptions.animate.isTrueOrUndefined()) {
103
                 animator.hide(animations);
106
                 animator.hide(animations);
104
             } else {
107
             } else {
105
                 bottomTabs.hideBottomNavigation(false);
108
                 bottomTabs.hideBottomNavigation(false);
127
         }
130
         }
128
     }
131
     }
129
 
132
 
130
-    private void applyBottomTabsOptions(BottomTabsOptions options, AnimationsOptions animationsOptions) {
131
-        bottomTabs.setTitleState(options.titleDisplayMode.get(TitleState.SHOW_WHEN_ACTIVE));
132
-        bottomTabs.setBackgroundColor(options.backgroundColor.get(Color.WHITE));
133
-        if (options.currentTabIndex.hasValue()) {
134
-            int tabIndex = options.currentTabIndex.get();
133
+    private void applyBottomTabsOptions(Options options) {
134
+        BottomTabsOptions bottomTabsOptions = options.bottomTabsOptions;
135
+        AnimationsOptions animationsOptions = options.animations;
136
+
137
+        bottomTabs.setLayoutDirection(options.layout.direction);
138
+        bottomTabs.setTitleState(bottomTabsOptions.titleDisplayMode.get(TitleState.SHOW_WHEN_ACTIVE));
139
+        bottomTabs.setBackgroundColor(bottomTabsOptions.backgroundColor.get(Color.WHITE));
140
+        if (bottomTabsOptions.currentTabIndex.hasValue()) {
141
+            int tabIndex = bottomTabsOptions.currentTabIndex.get();
135
             if (tabIndex >= 0) tabSelector.selectTab(tabIndex);
142
             if (tabIndex >= 0) tabSelector.selectTab(tabIndex);
136
         }
143
         }
137
-        if (options.testId.hasValue()) bottomTabs.setTag(options.testId.get());
138
-        if (options.currentTabId.hasValue()) {
139
-            int tabIndex = bottomTabFinder.findByControllerId(options.currentTabId.get());
144
+        if (bottomTabsOptions.testId.hasValue()) bottomTabs.setTag(bottomTabsOptions.testId.get());
145
+        if (bottomTabsOptions.currentTabId.hasValue()) {
146
+            int tabIndex = bottomTabFinder.findByControllerId(bottomTabsOptions.currentTabId.get());
140
             if (tabIndex >= 0) tabSelector.selectTab(tabIndex);
147
             if (tabIndex >= 0) tabSelector.selectTab(tabIndex);
141
         }
148
         }
142
-        if (options.visible.isTrueOrUndefined()) {
143
-            if (options.animate.isTrueOrUndefined()) {
149
+        if (bottomTabsOptions.visible.isTrueOrUndefined()) {
150
+            if (bottomTabsOptions.animate.isTrueOrUndefined()) {
144
                 animator.show(animationsOptions);
151
                 animator.show(animationsOptions);
145
             } else {
152
             } else {
146
                 bottomTabs.restoreBottomNavigation(false);
153
                 bottomTabs.restoreBottomNavigation(false);
147
             }
154
             }
148
         }
155
         }
149
-        if (options.visible.isFalse()) {
150
-            if (options.animate.isTrueOrUndefined()) {
156
+        if (bottomTabsOptions.visible.isFalse()) {
157
+            if (bottomTabsOptions.animate.isTrueOrUndefined()) {
151
                 animator.hide(animationsOptions);
158
                 animator.hide(animationsOptions);
152
             } else {
159
             } else {
153
                 bottomTabs.hideBottomNavigation(false);
160
                 bottomTabs.hideBottomNavigation(false);
154
             }
161
             }
155
         }
162
         }
156
-        if (options.elevation.hasValue()) {
157
-            bottomTabs.setUseElevation(true, options.elevation.get().floatValue());
163
+        if (bottomTabsOptions.elevation.hasValue()) {
164
+            bottomTabs.setUseElevation(true, bottomTabsOptions.elevation.get().floatValue());
158
         }
165
         }
159
     }
166
     }
160
 }
167
 }

+ 16
- 0
lib/android/app/src/main/java/com/reactnativenavigation/presentation/LayoutDirectionApplier.java View File

1
+package com.reactnativenavigation.presentation;
2
+
3
+import com.facebook.react.ReactInstanceManager;
4
+import com.facebook.react.modules.i18nmanager.I18nUtil;
5
+import com.reactnativenavigation.parse.Options;
6
+import com.reactnativenavigation.viewcontrollers.ViewController;
7
+
8
+public class LayoutDirectionApplier {
9
+    public void apply(ViewController root, Options options, ReactInstanceManager instanceManager) {
10
+        if (options.layout.direction.hasValue() && instanceManager.getCurrentReactContext() != null) {
11
+            root.getActivity().getWindow().getDecorView().setLayoutDirection(options.layout.direction.get());
12
+            I18nUtil.getInstance().allowRTL(instanceManager.getCurrentReactContext(), options.layout.direction.isRtl());
13
+            I18nUtil.getInstance().forceRTL(instanceManager.getCurrentReactContext(), options.layout.direction.isRtl());
14
+        }
15
+    }
16
+}

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenter.java → lib/android/app/src/main/java/com/reactnativenavigation/presentation/RootPresenter.java View File

1
-package com.reactnativenavigation.viewcontrollers.navigator;
1
+package com.reactnativenavigation.presentation;
2
 
2
 
3
 import android.content.Context;
3
 import android.content.Context;
4
 import android.widget.FrameLayout;
4
 import android.widget.FrameLayout;
5
-import android.view.View;
6
 
5
 
6
+import com.facebook.react.ReactInstanceManager;
7
 import com.reactnativenavigation.anim.NavigationAnimator;
7
 import com.reactnativenavigation.anim.NavigationAnimator;
8
 import com.reactnativenavigation.parse.Options;
8
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.utils.CommandListener;
9
 import com.reactnativenavigation.utils.CommandListener;
10
 import com.reactnativenavigation.viewcontrollers.ViewController;
10
 import com.reactnativenavigation.viewcontrollers.ViewController;
11
 import com.reactnativenavigation.views.element.ElementTransitionManager;
11
 import com.reactnativenavigation.views.element.ElementTransitionManager;
12
 
12
 
13
-import com.facebook.react.modules.i18nmanager.I18nUtil;
14
-import com.facebook.react.bridge.ReactApplicationContext;
15
-import com.facebook.react.ReactInstanceManager;
16
-
17
 public class RootPresenter {
13
 public class RootPresenter {
18
     private NavigationAnimator animator;
14
     private NavigationAnimator animator;
15
+    private LayoutDirectionApplier layoutDirectionApplier;
19
     private FrameLayout rootLayout;
16
     private FrameLayout rootLayout;
20
 
17
 
21
-    void setRootContainer(FrameLayout rootLayout) {
18
+    public void setRootContainer(FrameLayout rootLayout) {
22
         this.rootLayout = rootLayout;
19
         this.rootLayout = rootLayout;
23
     }
20
     }
24
 
21
 
25
     public RootPresenter(Context context) {
22
     public RootPresenter(Context context) {
26
-        animator = new NavigationAnimator(context, new ElementTransitionManager());
23
+        this(new NavigationAnimator(context, new ElementTransitionManager()), new LayoutDirectionApplier());
27
     }
24
     }
28
 
25
 
29
-    RootPresenter(NavigationAnimator animator) {
26
+    public RootPresenter(NavigationAnimator animator, LayoutDirectionApplier layoutDirectionApplier) {
30
         this.animator = animator;
27
         this.animator = animator;
28
+        this.layoutDirectionApplier = layoutDirectionApplier;
31
     }
29
     }
32
 
30
 
33
-    void setRoot(ViewController root, Options defaultOptions, CommandListener listener, ReactInstanceManager reactInstanceManager) {
34
-        setLayoutDirection(root, defaultOptions, (ReactApplicationContext) reactInstanceManager.getCurrentReactContext());
31
+    public void setRoot(ViewController root, Options defaultOptions, CommandListener listener, ReactInstanceManager reactInstanceManager) {
32
+        layoutDirectionApplier.apply(root, defaultOptions, reactInstanceManager);
35
         rootLayout.addView(root.getView());
33
         rootLayout.addView(root.getView());
36
         Options options = root.resolveCurrentOptions(defaultOptions);
34
         Options options = root.resolveCurrentOptions(defaultOptions);
37
         root.setWaitForRender(options.animations.setRoot.waitForRender);
35
         root.setWaitForRender(options.animations.setRoot.waitForRender);
53
             listener.onSuccess(root.getId());
51
             listener.onSuccess(root.getId());
54
         }
52
         }
55
     }
53
     }
56
-
57
-    private void setLayoutDirection(ViewController root, Options defaultOptions, ReactApplicationContext reactContext) {
58
-        if (defaultOptions.layout.direction.hasValue()) {
59
-            I18nUtil i18nUtil = I18nUtil.getInstance();
60
-            Boolean isRtl = defaultOptions.layout.direction.get().equals("rtl");
61
-
62
-            root.getActivity().getWindow().getDecorView().setLayoutDirection(isRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
63
-            i18nUtil.allowRTL(reactContext, isRtl);
64
-            i18nUtil.forceRTL(reactContext, isRtl);
65
-        }
66
-    }
67
 }
54
 }

+ 74
- 66
lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackPresenter.java View File

127
     public void mergeOptions(Options options, Component currentChild) {
127
     public void mergeOptions(Options options, Component currentChild) {
128
         mergeOrientation(options.layout.orientation);
128
         mergeOrientation(options.layout.orientation);
129
 //        mergeButtons(topBar, withDefault.topBar.buttons, child);
129
 //        mergeButtons(topBar, withDefault.topBar.buttons, child);
130
-        mergeTopBarOptions(options.topBar, options.animations, currentChild);
130
+        mergeTopBarOptions(options, currentChild);
131
         mergeTopTabsOptions(options.topTabs);
131
         mergeTopTabsOptions(options.topTabs);
132
         mergeTopTabOptions(options.topTabOptions);
132
         mergeTopTabOptions(options.topTabOptions);
133
     }
133
     }
141
         Options withDefault = options.copy().withDefaultOptions(defaultOptions);
141
         Options withDefault = options.copy().withDefaultOptions(defaultOptions);
142
         applyOrientation(withDefault.layout.orientation);
142
         applyOrientation(withDefault.layout.orientation);
143
         applyButtons(withDefault.topBar, child);
143
         applyButtons(withDefault.topBar, child);
144
-        applyTopBarOptions(withDefault.topBar, withDefault.animations, child, options);
144
+        applyTopBarOptions(withDefault, child, options);
145
         applyTopTabsOptions(withDefault.topTabs);
145
         applyTopTabsOptions(withDefault.topTabs);
146
         applyTopTabOptions(withDefault.topTabOptions);
146
         applyTopTabOptions(withDefault.topTabOptions);
147
     }
147
     }
164
         if (buttons != null) forEach(buttons.values(), ViewController::destroy);
164
         if (buttons != null) forEach(buttons.values(), ViewController::destroy);
165
     }
165
     }
166
 
166
 
167
-    private void applyTopBarOptions(TopBarOptions options, AnimationsOptions animationOptions, Component component, Options componentOptions) {
168
-        topBar.setHeight(options.height.get(UiUtils.getTopBarHeightDp(activity)));
169
-        topBar.setElevation(options.elevation.get(DEFAULT_ELEVATION));
167
+    private void applyTopBarOptions(Options options, Component component, Options componentOptions) {
168
+        TopBarOptions topBarOptions = options.topBar;
169
+        AnimationsOptions animationOptions = options.animations;
170
+
171
+        topBar.setLayoutDirection(options.layout.direction);
172
+        topBar.setHeight(topBarOptions.height.get(UiUtils.getTopBarHeightDp(activity)));
173
+        topBar.setElevation(topBarOptions.elevation.get(DEFAULT_ELEVATION));
170
         if (topBar.getLayoutParams() instanceof MarginLayoutParams) {
174
         if (topBar.getLayoutParams() instanceof MarginLayoutParams) {
171
-            ((MarginLayoutParams) topBar.getLayoutParams()).topMargin = UiUtils.dpToPx(activity, options.topMargin.get(0));
175
+            ((MarginLayoutParams) topBar.getLayoutParams()).topMargin = UiUtils.dpToPx(activity, topBarOptions.topMargin.get(0));
172
         }
176
         }
173
 
177
 
174
-        topBar.setTitleHeight(options.title.height.get(UiUtils.getTopBarHeightDp(activity)));
175
-        topBar.setTitle(options.title.text.get(""));
176
-        topBar.setTitleTopMargin(options.title.topMargin.get(0));
178
+        topBar.setTitleHeight(topBarOptions.title.height.get(UiUtils.getTopBarHeightDp(activity)));
179
+        topBar.setTitle(topBarOptions.title.text.get(""));
180
+        topBar.setTitleTopMargin(topBarOptions.title.topMargin.get(0));
177
 
181
 
178
-        if (options.title.component.hasValue()) {
182
+        if (topBarOptions.title.component.hasValue()) {
179
             if (titleControllers.containsKey(component)) {
183
             if (titleControllers.containsKey(component)) {
180
                 topBar.setTitleComponent(titleControllers.get(component).getView());
184
                 topBar.setTitleComponent(titleControllers.get(component).getView());
181
             } else {
185
             } else {
182
                 TitleBarReactViewController controller = new TitleBarReactViewController(activity, titleViewCreator);
186
                 TitleBarReactViewController controller = new TitleBarReactViewController(activity, titleViewCreator);
183
-                controller.setWaitForRender(options.title.component.waitForRender);
187
+                controller.setWaitForRender(topBarOptions.title.component.waitForRender);
184
                 titleControllers.put(component, controller);
188
                 titleControllers.put(component, controller);
185
-                controller.setComponent(options.title.component);
186
-                controller.getView().setLayoutParams(getComponentLayoutParams(options.title.component));
189
+                controller.setComponent(topBarOptions.title.component);
190
+                controller.getView().setLayoutParams(getComponentLayoutParams(topBarOptions.title.component));
187
                 topBar.setTitleComponent(controller.getView());
191
                 topBar.setTitleComponent(controller.getView());
188
             }
192
             }
189
         }
193
         }
190
 
194
 
191
-        topBar.setTitleFontSize(options.title.fontSize.get(defaultTitleFontSize));
192
-        topBar.setTitleTextColor(options.title.color.get(DEFAULT_TITLE_COLOR));
193
-        topBar.setTitleTypeface(options.title.fontFamily);
194
-        topBar.setTitleAlignment(options.title.alignment);
195
+        topBar.setTitleFontSize(topBarOptions.title.fontSize.get(defaultTitleFontSize));
196
+        topBar.setTitleTextColor(topBarOptions.title.color.get(DEFAULT_TITLE_COLOR));
197
+        topBar.setTitleTypeface(topBarOptions.title.fontFamily);
198
+        topBar.setTitleAlignment(topBarOptions.title.alignment);
195
 
199
 
196
-        topBar.setSubtitle(options.subtitle.text.get(""));
197
-        topBar.setSubtitleFontSize(options.subtitle.fontSize.get(defaultSubtitleFontSize));
198
-        topBar.setSubtitleColor(options.subtitle.color.get(DEFAULT_SUBTITLE_COLOR));
199
-        topBar.setSubtitleFontFamily(options.subtitle.fontFamily);
200
-        topBar.setSubtitleAlignment(options.subtitle.alignment);
200
+        topBar.setSubtitle(topBarOptions.subtitle.text.get(""));
201
+        topBar.setSubtitleFontSize(topBarOptions.subtitle.fontSize.get(defaultSubtitleFontSize));
202
+        topBar.setSubtitleColor(topBarOptions.subtitle.color.get(DEFAULT_SUBTITLE_COLOR));
203
+        topBar.setSubtitleFontFamily(topBarOptions.subtitle.fontFamily);
204
+        topBar.setSubtitleAlignment(topBarOptions.subtitle.alignment);
201
 
205
 
202
-        topBar.setBorderHeight(options.borderHeight.get(0d));
203
-        topBar.setBorderColor(options.borderColor.get(DEFAULT_BORDER_COLOR));
206
+        topBar.setBorderHeight(topBarOptions.borderHeight.get(0d));
207
+        topBar.setBorderColor(topBarOptions.borderColor.get(DEFAULT_BORDER_COLOR));
204
 
208
 
205
-        topBar.setBackgroundColor(options.background.color.get(Color.WHITE));
209
+        topBar.setBackgroundColor(topBarOptions.background.color.get(Color.WHITE));
206
 
210
 
207
-        if (options.background.component.hasValue()) {
208
-            View createdComponent = findBackgroundComponent(options.background.component);
211
+        if (topBarOptions.background.component.hasValue()) {
212
+            View createdComponent = findBackgroundComponent(topBarOptions.background.component);
209
             if (createdComponent != null) {
213
             if (createdComponent != null) {
210
                 topBar.setBackgroundComponent(createdComponent);
214
                 topBar.setBackgroundComponent(createdComponent);
211
             } else {
215
             } else {
212
                 TopBarBackgroundViewController controller = new TopBarBackgroundViewController(activity, topBarBackgroundViewCreator);
216
                 TopBarBackgroundViewController controller = new TopBarBackgroundViewController(activity, topBarBackgroundViewCreator);
213
-                controller.setWaitForRender(options.background.waitForRender);
217
+                controller.setWaitForRender(topBarOptions.background.waitForRender);
214
                 backgroundControllers.put(component, controller);
218
                 backgroundControllers.put(component, controller);
215
-                controller.setComponent(options.background.component);
219
+                controller.setComponent(topBarOptions.background.component);
216
                 controller.getView().setLayoutParams(new RelativeLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
220
                 controller.getView().setLayoutParams(new RelativeLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
217
                 topBar.setBackgroundComponent(controller.getView());
221
                 topBar.setBackgroundComponent(controller.getView());
218
             }
222
             }
220
             topBar.clearBackgroundComponent();
224
             topBar.clearBackgroundComponent();
221
         }
225
         }
222
 
226
 
223
-        if (options.testId.hasValue()) topBar.setTestId(options.testId.get());
224
-        applyTopBarVisibility(options, animationOptions, componentOptions);
225
-        if (options.drawBehind.isTrue() && !componentOptions.layout.topMargin.hasValue()) {
227
+        if (topBarOptions.testId.hasValue()) topBar.setTestId(topBarOptions.testId.get());
228
+        applyTopBarVisibility(topBarOptions, animationOptions, componentOptions);
229
+        if (topBarOptions.drawBehind.isTrue() && !componentOptions.layout.topMargin.hasValue()) {
226
             component.drawBehindTopBar();
230
             component.drawBehindTopBar();
227
-        } else if (options.drawBehind.isFalseOrUndefined()) {
231
+        } else if (topBarOptions.drawBehind.isFalseOrUndefined()) {
228
             component.drawBelowTopBar(topBar);
232
             component.drawBelowTopBar(topBar);
229
         }
233
         }
230
-        if (options.hideOnScroll.isTrue()) {
234
+        if (topBarOptions.hideOnScroll.isTrue()) {
231
             if (component instanceof IReactView) {
235
             if (component instanceof IReactView) {
232
                 topBar.enableCollapse(((IReactView) component).getScrollEventListener());
236
                 topBar.enableCollapse(((IReactView) component).getScrollEventListener());
233
             }
237
             }
234
-        } else if (options.hideOnScroll.isFalseOrUndefined()) {
238
+        } else if (topBarOptions.hideOnScroll.isFalseOrUndefined()) {
235
             topBar.disableCollapse();
239
             topBar.disableCollapse();
236
         }
240
         }
237
     }
241
     }
351
         TopBarOptions topBar = toMerge.copy().mergeWith(resolvedOptions).withDefaultOptions(defaultOptions).topBar;
355
         TopBarOptions topBar = toMerge.copy().mergeWith(resolvedOptions).withDefaultOptions(defaultOptions).topBar;
352
         mergeOrientation(toMerge.layout.orientation);
356
         mergeOrientation(toMerge.layout.orientation);
353
         mergeButtons(topBar, toMerge.topBar.buttons, child);
357
         mergeButtons(topBar, toMerge.topBar.buttons, child);
354
-        mergeTopBarOptions(toMerge.topBar, toMerge.animations, child);
358
+        mergeTopBarOptions(toMerge, child);
355
         mergeTopTabsOptions(toMerge.topTabs);
359
         mergeTopTabsOptions(toMerge.topTabs);
356
         mergeTopTabOptions(toMerge.topTabOptions);
360
         mergeTopTabOptions(toMerge.topTabOptions);
357
     }
361
     }
403
         return result;
407
         return result;
404
     }
408
     }
405
 
409
 
406
-    private void mergeTopBarOptions(TopBarOptions options, AnimationsOptions animationsOptions, Component component) {
407
-        if (options.height.hasValue()) topBar.setHeight(options.height.get());
408
-        if (options.elevation.hasValue()) topBar.setElevation(options.elevation.get());
409
-        if (options.topMargin.hasValue() && topBar.getLayoutParams() instanceof MarginLayoutParams) {
410
-            ((MarginLayoutParams) topBar.getLayoutParams()).topMargin = UiUtils.dpToPx(activity, options.topMargin.get());
410
+    private void mergeTopBarOptions(Options options, Component component) {
411
+        TopBarOptions topBarOptions = options.topBar;
412
+        AnimationsOptions animationsOptions = options.animations;
413
+
414
+        if (options.layout.direction.hasValue()) topBar.setLayoutDirection(options.layout.direction);
415
+        if (topBarOptions.height.hasValue()) topBar.setHeight(topBarOptions.height.get());
416
+        if (topBarOptions.elevation.hasValue()) topBar.setElevation(topBarOptions.elevation.get());
417
+        if (topBarOptions.topMargin.hasValue() && topBar.getLayoutParams() instanceof MarginLayoutParams) {
418
+            ((MarginLayoutParams) topBar.getLayoutParams()).topMargin = UiUtils.dpToPx(activity, topBarOptions.topMargin.get());
411
         }
419
         }
412
 
420
 
413
-        if (options.title.height.hasValue()) topBar.setTitleHeight(options.title.height.get());
414
-        if (options.title.text.hasValue()) topBar.setTitle(options.title.text.get());
415
-        if (options.title.topMargin.hasValue()) topBar.setTitleTopMargin(options.title.topMargin.get());
421
+        if (topBarOptions.title.height.hasValue()) topBar.setTitleHeight(topBarOptions.title.height.get());
422
+        if (topBarOptions.title.text.hasValue()) topBar.setTitle(topBarOptions.title.text.get());
423
+        if (topBarOptions.title.topMargin.hasValue()) topBar.setTitleTopMargin(topBarOptions.title.topMargin.get());
416
 
424
 
417
-        if (options.title.component.hasValue()) {
425
+        if (topBarOptions.title.component.hasValue()) {
418
             if (titleControllers.containsKey(component)) {
426
             if (titleControllers.containsKey(component)) {
419
                 topBar.setTitleComponent(titleControllers.get(component).getView());
427
                 topBar.setTitleComponent(titleControllers.get(component).getView());
420
             } else {
428
             } else {
421
                 TitleBarReactViewController controller = new TitleBarReactViewController(activity, titleViewCreator);
429
                 TitleBarReactViewController controller = new TitleBarReactViewController(activity, titleViewCreator);
422
                 titleControllers.put(component, controller);
430
                 titleControllers.put(component, controller);
423
-                controller.setComponent(options.title.component);
424
-                controller.getView().setLayoutParams(getComponentLayoutParams(options.title.component));
431
+                controller.setComponent(topBarOptions.title.component);
432
+                controller.getView().setLayoutParams(getComponentLayoutParams(topBarOptions.title.component));
425
                 topBar.setTitleComponent(controller.getView());
433
                 topBar.setTitleComponent(controller.getView());
426
             }
434
             }
427
         }
435
         }
428
 
436
 
429
-        if (options.title.color.hasValue()) topBar.setTitleTextColor(options.title.color.get());
430
-        if (options.title.fontSize.hasValue()) topBar.setTitleFontSize(options.title.fontSize.get());
431
-        if (options.title.fontFamily != null) topBar.setTitleTypeface(options.title.fontFamily);
437
+        if (topBarOptions.title.color.hasValue()) topBar.setTitleTextColor(topBarOptions.title.color.get());
438
+        if (topBarOptions.title.fontSize.hasValue()) topBar.setTitleFontSize(topBarOptions.title.fontSize.get());
439
+        if (topBarOptions.title.fontFamily != null) topBar.setTitleTypeface(topBarOptions.title.fontFamily);
432
 
440
 
433
-        if (options.subtitle.text.hasValue()) topBar.setSubtitle(options.subtitle.text.get());
434
-        if (options.subtitle.color.hasValue()) topBar.setSubtitleColor(options.subtitle.color.get());
435
-        if (options.subtitle.fontSize.hasValue()) topBar.setSubtitleFontSize(options.subtitle.fontSize.get());
436
-        if (options.subtitle.fontFamily != null) topBar.setSubtitleFontFamily(options.subtitle.fontFamily);
441
+        if (topBarOptions.subtitle.text.hasValue()) topBar.setSubtitle(topBarOptions.subtitle.text.get());
442
+        if (topBarOptions.subtitle.color.hasValue()) topBar.setSubtitleColor(topBarOptions.subtitle.color.get());
443
+        if (topBarOptions.subtitle.fontSize.hasValue()) topBar.setSubtitleFontSize(topBarOptions.subtitle.fontSize.get());
444
+        if (topBarOptions.subtitle.fontFamily != null) topBar.setSubtitleFontFamily(topBarOptions.subtitle.fontFamily);
437
 
445
 
438
-        if (options.background.color.hasValue()) topBar.setBackgroundColor(options.background.color.get());
446
+        if (topBarOptions.background.color.hasValue()) topBar.setBackgroundColor(topBarOptions.background.color.get());
439
 
447
 
440
-        if (options.background.component.hasValue()) {
448
+        if (topBarOptions.background.component.hasValue()) {
441
             if (backgroundControllers.containsKey(component)) {
449
             if (backgroundControllers.containsKey(component)) {
442
                 topBar.setBackgroundComponent(backgroundControllers.get(component).getView());
450
                 topBar.setBackgroundComponent(backgroundControllers.get(component).getView());
443
             } else {
451
             } else {
444
                 TopBarBackgroundViewController controller = new TopBarBackgroundViewController(activity, topBarBackgroundViewCreator);
452
                 TopBarBackgroundViewController controller = new TopBarBackgroundViewController(activity, topBarBackgroundViewCreator);
445
                 backgroundControllers.put(component, controller);
453
                 backgroundControllers.put(component, controller);
446
-                controller.setComponent(options.background.component);
454
+                controller.setComponent(topBarOptions.background.component);
447
                 controller.getView().setLayoutParams(new RelativeLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
455
                 controller.getView().setLayoutParams(new RelativeLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
448
                 topBar.setBackgroundComponent(controller.getView());
456
                 topBar.setBackgroundComponent(controller.getView());
449
             }
457
             }
450
         }
458
         }
451
 
459
 
452
-        if (options.testId.hasValue()) topBar.setTestId(options.testId.get());
460
+        if (topBarOptions.testId.hasValue()) topBar.setTestId(topBarOptions.testId.get());
453
 
461
 
454
-        if (options.visible.isFalse()) {
455
-            if (options.animate.isTrueOrUndefined()) {
462
+        if (topBarOptions.visible.isFalse()) {
463
+            if (topBarOptions.animate.isTrueOrUndefined()) {
456
                 topBar.hideAnimate(animationsOptions.pop.topBar);
464
                 topBar.hideAnimate(animationsOptions.pop.topBar);
457
             } else {
465
             } else {
458
                 topBar.hide();
466
                 topBar.hide();
459
             }
467
             }
460
         }
468
         }
461
-        if (options.visible.isTrue()) {
462
-            if (options.animate.isTrueOrUndefined()) {
469
+        if (topBarOptions.visible.isTrue()) {
470
+            if (topBarOptions.animate.isTrueOrUndefined()) {
463
                 topBar.showAnimate(animationsOptions.push.topBar);
471
                 topBar.showAnimate(animationsOptions.push.topBar);
464
             } else {
472
             } else {
465
                 topBar.show();
473
                 topBar.show();
466
             }
474
             }
467
         }
475
         }
468
-        if (options.drawBehind.isTrue()) {
476
+        if (topBarOptions.drawBehind.isTrue()) {
469
             component.drawBehindTopBar();
477
             component.drawBehindTopBar();
470
         }
478
         }
471
-        if (options.drawBehind.isFalse()) {
479
+        if (topBarOptions.drawBehind.isFalse()) {
472
             component.drawBelowTopBar(topBar);
480
             component.drawBelowTopBar(topBar);
473
         }
481
         }
474
-        if (options.hideOnScroll.isTrue() && component instanceof IReactView) {
482
+        if (topBarOptions.hideOnScroll.isTrue() && component instanceof IReactView) {
475
             topBar.enableCollapse(((IReactView) component).getScrollEventListener());
483
             topBar.enableCollapse(((IReactView) component).getScrollEventListener());
476
         }
484
         }
477
-        if (options.hideOnScroll.isFalse()) {
485
+        if (topBarOptions.hideOnScroll.isFalse()) {
478
             topBar.disableCollapse();
486
             topBar.disableCollapse();
479
         }
487
         }
480
     }
488
     }

+ 1
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java View File

10
 import com.reactnativenavigation.parse.Options;
10
 import com.reactnativenavigation.parse.Options;
11
 import com.reactnativenavigation.presentation.OverlayManager;
11
 import com.reactnativenavigation.presentation.OverlayManager;
12
 import com.reactnativenavigation.presentation.Presenter;
12
 import com.reactnativenavigation.presentation.Presenter;
13
+import com.reactnativenavigation.presentation.RootPresenter;
13
 import com.reactnativenavigation.react.EventEmitter;
14
 import com.reactnativenavigation.react.EventEmitter;
14
 import com.reactnativenavigation.utils.CommandListener;
15
 import com.reactnativenavigation.utils.CommandListener;
15
 import com.reactnativenavigation.utils.CommandListenerAdapter;
16
 import com.reactnativenavigation.utils.CommandListenerAdapter;

+ 10
- 1
lib/android/app/src/main/java/com/reactnativenavigation/views/BottomTabs.java View File

4
 import android.content.Context;
4
 import android.content.Context;
5
 import android.graphics.drawable.Drawable;
5
 import android.graphics.drawable.Drawable;
6
 import android.support.annotation.IntRange;
6
 import android.support.annotation.IntRange;
7
+import android.widget.LinearLayout;
7
 
8
 
8
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
9
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
9
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
10
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
11
+import com.reactnativenavigation.parse.LayoutDirection;
10
 import com.reactnativenavigation.utils.CompatUtils;
12
 import com.reactnativenavigation.utils.CompatUtils;
11
 
13
 
14
+import static com.reactnativenavigation.utils.ViewUtils.findChildByClass;
15
+
12
 @SuppressLint("ViewConstructor")
16
 @SuppressLint("ViewConstructor")
13
 public class BottomTabs extends AHBottomNavigation {
17
 public class BottomTabs extends AHBottomNavigation {
14
     private boolean itemsCreationEnabled = true;
18
     private boolean itemsCreationEnabled = true;
39
 
43
 
40
     @Override
44
     @Override
41
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
45
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
42
-
46
+        // NOOP - don't recreate views on size change
43
     }
47
     }
44
 
48
 
45
     public void superCreateItems() {
49
     public void superCreateItems() {
75
             refresh();
79
             refresh();
76
         }
80
         }
77
     }
81
     }
82
+
83
+    public void setLayoutDirection(LayoutDirection direction) {
84
+         LinearLayout tabsContainer = findChildByClass(this, LinearLayout.class);
85
+        if (tabsContainer != null) tabsContainer.setLayoutDirection(direction.get());
86
+    }
78
 }
87
 }

+ 5
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/topbar/TopBar.java View File

26
 import com.reactnativenavigation.interfaces.ScrollEventListener;
26
 import com.reactnativenavigation.interfaces.ScrollEventListener;
27
 import com.reactnativenavigation.parse.Alignment;
27
 import com.reactnativenavigation.parse.Alignment;
28
 import com.reactnativenavigation.parse.AnimationOptions;
28
 import com.reactnativenavigation.parse.AnimationOptions;
29
+import com.reactnativenavigation.parse.LayoutDirection;
29
 import com.reactnativenavigation.parse.params.Colour;
30
 import com.reactnativenavigation.parse.params.Colour;
30
 import com.reactnativenavigation.parse.params.Number;
31
 import com.reactnativenavigation.parse.params.Number;
31
 import com.reactnativenavigation.utils.CompatUtils;
32
 import com.reactnativenavigation.utils.CompatUtils;
331
     public void setOverflowButtonColor(int color) {
332
     public void setOverflowButtonColor(int color) {
332
         titleBar.setOverflowButtonColor(color);
333
         titleBar.setOverflowButtonColor(color);
333
     }
334
     }
335
+
336
+    public void setLayoutDirection(LayoutDirection direction) {
337
+        titleBar.setLayoutDirection(direction.get());
338
+    }
334
 }
339
 }

+ 1
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsPresenterTest.java View File

40
     }
40
     }
41
 
41
 
42
     @Test
42
     @Test
43
-    public void mergeChildOptions_onlyDeclaredOptionsAreApplied() { // default options are not applies on merge
43
+    public void mergeChildOptions_onlyDeclaredOptionsAreApplied() { // default options are not applied on merge
44
         Options defaultOptions = new Options();
44
         Options defaultOptions = new Options();
45
         defaultOptions.bottomTabsOptions.visible = new Bool(false);
45
         defaultOptions.bottomTabsOptions.visible = new Bool(false);
46
         uut.setDefaultOptions(defaultOptions);
46
         uut.setDefaultOptions(defaultOptions);

+ 1
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/NavigatorTest.java View File

18
 import com.reactnativenavigation.presentation.BottomTabsPresenter;
18
 import com.reactnativenavigation.presentation.BottomTabsPresenter;
19
 import com.reactnativenavigation.presentation.Presenter;
19
 import com.reactnativenavigation.presentation.Presenter;
20
 import com.reactnativenavigation.presentation.OverlayManager;
20
 import com.reactnativenavigation.presentation.OverlayManager;
21
+import com.reactnativenavigation.presentation.RootPresenter;
21
 import com.reactnativenavigation.react.EventEmitter;
22
 import com.reactnativenavigation.react.EventEmitter;
22
 import com.reactnativenavigation.utils.CommandListener;
23
 import com.reactnativenavigation.utils.CommandListener;
23
 import com.reactnativenavigation.utils.CommandListenerAdapter;
24
 import com.reactnativenavigation.utils.CommandListenerAdapter;

+ 13
- 3
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.java View File

5
 import android.view.View;
5
 import android.view.View;
6
 import android.widget.FrameLayout;
6
 import android.widget.FrameLayout;
7
 
7
 
8
+import com.facebook.react.ReactInstanceManager;
8
 import com.reactnativenavigation.BaseTest;
9
 import com.reactnativenavigation.BaseTest;
9
 import com.reactnativenavigation.anim.NavigationAnimator;
10
 import com.reactnativenavigation.anim.NavigationAnimator;
10
 import com.reactnativenavigation.mocks.SimpleViewController;
11
 import com.reactnativenavigation.mocks.SimpleViewController;
11
 import com.reactnativenavigation.parse.AnimationOptions;
12
 import com.reactnativenavigation.parse.AnimationOptions;
12
 import com.reactnativenavigation.parse.Options;
13
 import com.reactnativenavigation.parse.Options;
13
 import com.reactnativenavigation.parse.params.Bool;
14
 import com.reactnativenavigation.parse.params.Bool;
15
+import com.reactnativenavigation.presentation.LayoutDirectionApplier;
16
+import com.reactnativenavigation.presentation.RootPresenter;
14
 import com.reactnativenavigation.utils.CommandListenerAdapter;
17
 import com.reactnativenavigation.utils.CommandListenerAdapter;
15
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
18
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
16
 import com.reactnativenavigation.viewcontrollers.ViewController;
19
 import com.reactnativenavigation.viewcontrollers.ViewController;
17
 import com.reactnativenavigation.views.element.ElementTransitionManager;
20
 import com.reactnativenavigation.views.element.ElementTransitionManager;
18
 
21
 
19
-import com.facebook.react.ReactInstanceManager;
20
-
21
 import org.junit.Test;
22
 import org.junit.Test;
22
 import org.mockito.ArgumentCaptor;
23
 import org.mockito.ArgumentCaptor;
23
 import org.mockito.Mockito;
24
 import org.mockito.Mockito;
36
     private FrameLayout rootContainer;
37
     private FrameLayout rootContainer;
37
     private ViewController root;
38
     private ViewController root;
38
     private NavigationAnimator animator;
39
     private NavigationAnimator animator;
40
+    private LayoutDirectionApplier layoutDirectionApplier;
39
     private Options defaultOptions;
41
     private Options defaultOptions;
40
     private ReactInstanceManager reactInstanceManager;
42
     private ReactInstanceManager reactInstanceManager;
41
 
43
 
47
         rootContainer = new FrameLayout(activity);
49
         rootContainer = new FrameLayout(activity);
48
         root = new SimpleViewController(activity, new ChildControllersRegistry(), "child1", new Options());
50
         root = new SimpleViewController(activity, new ChildControllersRegistry(), "child1", new Options());
49
         animator = spy(createAnimator(activity));
51
         animator = spy(createAnimator(activity));
50
-        uut = new RootPresenter(animator);
52
+        layoutDirectionApplier = Mockito.mock(LayoutDirectionApplier.class);
53
+        uut = new RootPresenter(animator, layoutDirectionApplier);
51
         uut.setRootContainer(rootContainer);
54
         uut.setRootContainer(rootContainer);
52
         defaultOptions = new Options();
55
         defaultOptions = new Options();
53
     }
56
     }
120
         verify(listener).onSuccess(spy.getId());
123
         verify(listener).onSuccess(spy.getId());
121
     }
124
     }
122
 
125
 
126
+    @Test
127
+    public void setRoot_appliesLayoutDirection() {
128
+        CommandListenerAdapter listener = spy(new CommandListenerAdapter());
129
+        uut.setRoot(root, defaultOptions, listener, reactInstanceManager);
130
+        verify(layoutDirectionApplier).apply(root, defaultOptions, reactInstanceManager);
131
+    }
132
+
123
     @NonNull
133
     @NonNull
124
     private NavigationAnimator createAnimator(Activity activity) {
134
     private NavigationAnimator createAnimator(Activity activity) {
125
         return new NavigationAnimator(activity, mock(ElementTransitionManager.class)) {
135
         return new NavigationAnimator(activity, mock(ElementTransitionManager.class)) {

+ 1
- 0
playground/android/app/src/main/AndroidManifest.xml View File

10
         android:allowBackup="false"
10
         android:allowBackup="false"
11
         android:icon="@mipmap/ic_launcher"
11
         android:icon="@mipmap/ic_launcher"
12
         android:label="@string/app_name"
12
         android:label="@string/app_name"
13
+        android:supportsRtl="true"
13
         android:theme="@style/AppTheme"
14
         android:theme="@style/AppTheme"
14
         android:usesCleartextTraffic="true"
15
         android:usesCleartextTraffic="true"
15
         tools:ignore="GoogleAppIndexingWarning">
16
         tools:ignore="GoogleAppIndexingWarning">

+ 2
- 1
playground/src/commons/Options.js View File

4
 const setDefaultOptions = () => Navigation.setDefaultOptions({
4
 const setDefaultOptions = () => Navigation.setDefaultOptions({
5
   layout: {
5
   layout: {
6
     componentBackgroundColor: Colors.background,
6
     componentBackgroundColor: Colors.background,
7
-    orientation: ['portrait']
7
+    orientation: ['portrait'],
8
+    direction: 'locale'
8
   },
9
   },
9
   bottomTabs: {
10
   bottomTabs: {
10
     titleDisplayMode: 'alwaysShow'
11
     titleDisplayMode: 'alwaysShow'