Browse Source

Implement statusBar drawBehind and visible option

Guy Carmeli 6 years ago
parent
commit
126f31c2fb
20 changed files with 228 additions and 50 deletions
  1. 2
    0
      lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java
  2. 11
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/StatusBarOptions.java
  3. 31
    5
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java
  4. 13
    4
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ChildController.java
  5. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java
  6. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java
  7. 6
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java
  8. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/SideMenuController.java
  9. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java
  10. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java
  11. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java
  12. 19
    8
      lib/android/app/src/test/java/com/reactnativenavigation/BaseTest.java
  13. 9
    5
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleViewController.java
  14. 32
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigationActivityTest.java
  15. 24
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ParentControllerTest.java
  16. 5
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ViewControllerTest.java
  17. 22
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/child/ChildControllerTest.java
  18. 5
    0
      playground/src/screens/ModalScreen.js
  19. 4
    0
      playground/src/screens/PushedScreen.js
  20. 33
    18
      playground/src/screens/WelcomeScreen.js

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

5
 import android.support.annotation.Nullable;
5
 import android.support.annotation.Nullable;
6
 import android.support.v7.app.AppCompatActivity;
6
 import android.support.v7.app.AppCompatActivity;
7
 import android.view.KeyEvent;
7
 import android.view.KeyEvent;
8
+import android.view.View;
8
 
9
 
9
 import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
10
 import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
10
 import com.reactnativenavigation.presentation.OverlayManager;
11
 import com.reactnativenavigation.presentation.OverlayManager;
22
         navigator = new Navigator(this, new ChildControllersRegistry(), new OverlayManager());
23
         navigator = new Navigator(this, new ChildControllersRegistry(), new OverlayManager());
23
         getReactGateway().onActivityCreated(this);
24
         getReactGateway().onActivityCreated(this);
24
         getReactGateway().addReloadListener(navigator);
25
         getReactGateway().addReloadListener(navigator);
26
+        navigator.getView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
25
         setContentView(navigator.getView());
27
         setContentView(navigator.getView());
26
     }
28
     }
27
 
29
 

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

2
 
2
 
3
 import android.support.annotation.Nullable;
3
 import android.support.annotation.Nullable;
4
 
4
 
5
+import com.reactnativenavigation.parse.params.Bool;
5
 import com.reactnativenavigation.parse.params.Color;
6
 import com.reactnativenavigation.parse.params.Color;
7
+import com.reactnativenavigation.parse.params.NullBool;
6
 import com.reactnativenavigation.parse.params.NullColor;
8
 import com.reactnativenavigation.parse.params.NullColor;
9
+import com.reactnativenavigation.parse.parsers.BoolParser;
7
 import com.reactnativenavigation.parse.parsers.ColorParser;
10
 import com.reactnativenavigation.parse.parsers.ColorParser;
8
 
11
 
9
 import org.json.JSONObject;
12
 import org.json.JSONObject;
41
 
44
 
42
         result.backgroundColor = ColorParser.parse(json, "backgroundColor");
45
         result.backgroundColor = ColorParser.parse(json, "backgroundColor");
43
         result.textColorScheme = TextColorScheme.fromString(json.optString("style"));
46
         result.textColorScheme = TextColorScheme.fromString(json.optString("style"));
47
+        result.visible = BoolParser.parse(json, "visible");
48
+        result.drawBehind = BoolParser.parse(json, "drawBehind");
44
 
49
 
45
         return result;
50
         return result;
46
     }
51
     }
47
 
52
 
48
     public Color backgroundColor = new NullColor();
53
     public Color backgroundColor = new NullColor();
49
     public TextColorScheme textColorScheme = TextColorScheme.None;
54
     public TextColorScheme textColorScheme = TextColorScheme.None;
55
+    public Bool visible = new NullBool();
56
+    public Bool drawBehind = new NullBool();
50
 
57
 
51
     public void mergeWith(StatusBarOptions other) {
58
     public void mergeWith(StatusBarOptions other) {
52
         if (other.backgroundColor.hasValue()) backgroundColor = other.backgroundColor;
59
         if (other.backgroundColor.hasValue()) backgroundColor = other.backgroundColor;
53
         if (other.textColorScheme.hasValue()) textColorScheme = other.textColorScheme;
60
         if (other.textColorScheme.hasValue()) textColorScheme = other.textColorScheme;
61
+        if (other.visible.hasValue()) visible = other.visible;
62
+        if (other.drawBehind.hasValue()) drawBehind = other.drawBehind;
54
     }
63
     }
55
 
64
 
56
     public void mergeWithDefault(StatusBarOptions defaultOptions) {
65
     public void mergeWithDefault(StatusBarOptions defaultOptions) {
57
         if (!backgroundColor.hasValue()) backgroundColor = defaultOptions.backgroundColor;
66
         if (!backgroundColor.hasValue()) backgroundColor = defaultOptions.backgroundColor;
58
         if (!textColorScheme.hasValue()) textColorScheme = defaultOptions.textColorScheme;
67
         if (!textColorScheme.hasValue()) textColorScheme = defaultOptions.textColorScheme;
68
+        if (!visible.hasValue()) visible = defaultOptions.visible;
69
+        if (!drawBehind.hasValue()) drawBehind = defaultOptions.drawBehind;
59
     }
70
     }
60
 }
71
 }

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

4
 import android.graphics.Color;
4
 import android.graphics.Color;
5
 import android.os.Build;
5
 import android.os.Build;
6
 import android.view.View;
6
 import android.view.View;
7
+import android.view.ViewGroup;
7
 
8
 
8
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.parse.OrientationOptions;
10
 import com.reactnativenavigation.parse.OrientationOptions;
10
 import com.reactnativenavigation.parse.StatusBarOptions;
11
 import com.reactnativenavigation.parse.StatusBarOptions;
11
 import com.reactnativenavigation.parse.StatusBarOptions.TextColorScheme;
12
 import com.reactnativenavigation.parse.StatusBarOptions.TextColorScheme;
13
+import com.reactnativenavigation.parse.params.Bool;
14
+import com.reactnativenavigation.utils.UiUtils;
12
 
15
 
13
 @SuppressWarnings("FieldCanBeLocal")
16
 @SuppressWarnings("FieldCanBeLocal")
14
 public class OptionsPresenter {
17
 public class OptionsPresenter {
22
     public void present(View view, Options options) {
25
     public void present(View view, Options options) {
23
         applyOrientation(options.orientationOptions);
26
         applyOrientation(options.orientationOptions);
24
         applyViewOptions(view, options);
27
         applyViewOptions(view, options);
25
-        applyStatusBarOptions(options.statusBar);
28
+        applyStatusBarOptions(view, options.statusBar);
29
+    }
30
+
31
+    public void applyRootOptions(View view, Options options) {
32
+        setDrawBehindStatusBar(view, options.statusBar);
26
     }
33
     }
27
 
34
 
28
     private void applyOrientation(OrientationOptions options) {
35
     private void applyOrientation(OrientationOptions options) {
35
         }
42
         }
36
     }
43
     }
37
 
44
 
38
-    public void onViewBroughtToFront(Options options) {
39
-        applyStatusBarOptions(options.statusBar);
45
+    public void onViewBroughtToFront(View view, Options options) {
46
+        applyStatusBarOptions(view, options.statusBar);
47
+    }
48
+
49
+    private void applyStatusBarOptions(View view, StatusBarOptions statusBar) {
50
+        setStatusBarBackgroundColor(statusBar);
51
+        setTextColorScheme(statusBar.textColorScheme);
52
+        setStatusBarVisible(view, statusBar.visible, statusBar.drawBehind);
40
     }
53
     }
41
 
54
 
42
-    private void applyStatusBarOptions(StatusBarOptions statusBar) {
55
+    private void setStatusBarVisible(View view, Bool visible, Bool drawBehind) {
56
+        if (visible.isFalse()) {
57
+            view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_FULLSCREEN);
58
+        } else if (drawBehind.isTrue()) {
59
+            view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
60
+        }
61
+    }
62
+
63
+    private void setStatusBarBackgroundColor(StatusBarOptions statusBar) {
43
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
64
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
44
             activity.getWindow().setStatusBarColor(statusBar.backgroundColor.get(Color.BLACK));
65
             activity.getWindow().setStatusBarColor(statusBar.backgroundColor.get(Color.BLACK));
45
         }
66
         }
46
-        setTextColorScheme(statusBar.textColorScheme);
47
     }
67
     }
48
 
68
 
49
     private void setTextColorScheme(TextColorScheme scheme) {
69
     private void setTextColorScheme(TextColorScheme scheme) {
64
         flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
84
         flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
65
         view.setSystemUiVisibility(flags);
85
         view.setSystemUiVisibility(flags);
66
     }
86
     }
87
+
88
+    private void setDrawBehindStatusBar(View view, StatusBarOptions statusBar) {
89
+        ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin = statusBar.drawBehind.isFalseOrUndefined() ?
90
+                UiUtils.getStatusBarHeight(activity) :
91
+                0;
92
+    }
67
 }
93
 }

+ 13
- 4
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ChildController.java View File

7
 import com.reactnativenavigation.presentation.OptionsPresenter;
7
 import com.reactnativenavigation.presentation.OptionsPresenter;
8
 
8
 
9
 public abstract class ChildController<T extends ViewGroup> extends ViewController<T>  {
9
 public abstract class ChildController<T extends ViewGroup> extends ViewController<T>  {
10
-    private final OptionsPresenter presenter;
10
+    final OptionsPresenter presenter;
11
     private final ChildControllersRegistry childRegistry;
11
     private final ChildControllersRegistry childRegistry;
12
 
12
 
13
     public ChildControllersRegistry getChildRegistry() {
13
     public ChildControllersRegistry getChildRegistry() {
14
         return childRegistry;
14
         return childRegistry;
15
     }
15
     }
16
 
16
 
17
-    public ChildController(Activity activity, ChildControllersRegistry childRegistry, String id, Options initialOptions) {
17
+    public ChildController(Activity activity, ChildControllersRegistry childRegistry, String id, OptionsPresenter presenter, Options initialOptions) {
18
         super(activity, id, initialOptions);
18
         super(activity, id, initialOptions);
19
-        presenter = new OptionsPresenter(activity);
19
+        this.presenter = presenter;
20
         this.childRegistry = childRegistry;
20
         this.childRegistry = childRegistry;
21
     }
21
     }
22
 
22
 
33
     }
33
     }
34
 
34
 
35
     public void onViewBroughtToFront() {
35
     public void onViewBroughtToFront() {
36
-        presenter.onViewBroughtToFront(options);
36
+        presenter.onViewBroughtToFront(getView(), options);
37
     }
37
     }
38
 
38
 
39
     @Override
39
     @Override
40
     public void applyOptions(Options options) {
40
     public void applyOptions(Options options) {
41
         super.applyOptions(options);
41
         super.applyOptions(options);
42
         presenter.present(getView(), options);
42
         presenter.present(getView(), options);
43
+        if (isRoot()) {
44
+            presenter.applyRootOptions(getView(), options);
45
+        }
46
+    }
47
+
48
+    protected boolean isRoot() {
49
+        return getParentController() == null &&
50
+               !(this instanceof Navigator) &&
51
+                getView().getParent() != null;
43
     }
52
     }
44
 }
53
 }

+ 2
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java View File

4
 import android.support.annotation.NonNull;
4
 import android.support.annotation.NonNull;
5
 
5
 
6
 import com.reactnativenavigation.parse.Options;
6
 import com.reactnativenavigation.parse.Options;
7
+import com.reactnativenavigation.presentation.OptionsPresenter;
7
 import com.reactnativenavigation.views.ComponentLayout;
8
 import com.reactnativenavigation.views.ComponentLayout;
8
 import com.reactnativenavigation.views.ReactComponent;
9
 import com.reactnativenavigation.views.ReactComponent;
9
 
10
 
19
                                    final String componentName,
20
                                    final String componentName,
20
                                    final ReactViewCreator viewCreator,
21
                                    final ReactViewCreator viewCreator,
21
                                    final Options initialOptions) {
22
                                    final Options initialOptions) {
22
-        super(activity, childRegistry, id, initialOptions);
23
+        super(activity, childRegistry, id, new OptionsPresenter(activity), initialOptions);
23
         this.componentName = componentName;
24
         this.componentName = componentName;
24
         this.viewCreator = viewCreator;
25
         this.viewCreator = viewCreator;
25
     }
26
     }

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

11
 import com.reactnativenavigation.anim.ModalAnimator;
11
 import com.reactnativenavigation.anim.ModalAnimator;
12
 import com.reactnativenavigation.anim.NavigationAnimator;
12
 import com.reactnativenavigation.anim.NavigationAnimator;
13
 import com.reactnativenavigation.parse.Options;
13
 import com.reactnativenavigation.parse.Options;
14
+import com.reactnativenavigation.presentation.OptionsPresenter;
14
 import com.reactnativenavigation.presentation.OverlayManager;
15
 import com.reactnativenavigation.presentation.OverlayManager;
15
 import com.reactnativenavigation.react.JsDevReloadHandler;
16
 import com.reactnativenavigation.react.JsDevReloadHandler;
16
 import com.reactnativenavigation.utils.CommandListener;
17
 import com.reactnativenavigation.utils.CommandListener;
41
     }
42
     }
42
 
43
 
43
     public Navigator(final Activity activity, ChildControllersRegistry childRegistry, OverlayManager overlayManager) {
44
     public Navigator(final Activity activity, ChildControllersRegistry childRegistry, OverlayManager overlayManager) {
44
-        super(activity, childRegistry,"navigator" + CompatUtils.generateViewId(), new Options());
45
+        super(activity, childRegistry,"navigator" + CompatUtils.generateViewId(), new OptionsPresenter(activity), new Options());
45
         modalStack = new ModalStack(new ModalPresenter(new ModalAnimator(activity)));
46
         modalStack = new ModalStack(new ModalPresenter(new ModalAnimator(activity)));
46
         this.overlayManager = overlayManager;
47
         this.overlayManager = overlayManager;
47
     }
48
     }

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

8
 import android.view.ViewGroup;
8
 import android.view.ViewGroup;
9
 
9
 
10
 import com.reactnativenavigation.parse.Options;
10
 import com.reactnativenavigation.parse.Options;
11
+import com.reactnativenavigation.presentation.OptionsPresenter;
11
 import com.reactnativenavigation.views.Component;
12
 import com.reactnativenavigation.views.Component;
12
 
13
 
13
 import java.util.Collection;
14
 import java.util.Collection;
14
 
15
 
15
 public abstract class ParentController<T extends ViewGroup> extends ChildController {
16
 public abstract class ParentController<T extends ViewGroup> extends ChildController {
16
 
17
 
17
-	public ParentController(Activity activity, ChildControllersRegistry childRegistry, String id, Options initialOptions) {
18
-		super(activity, childRegistry, id, initialOptions);
18
+	public ParentController(Activity activity, ChildControllersRegistry childRegistry, String id, OptionsPresenter presenter, Options initialOptions) {
19
+		super(activity, childRegistry, id, presenter, initialOptions);
19
 	}
20
 	}
20
 
21
 
21
 	@NonNull
22
 	@NonNull
59
     @CallSuper
60
     @CallSuper
60
     public void applyChildOptions(Options options, Component child) {
61
     public void applyChildOptions(Options options, Component child) {
61
         this.options = this.options.mergeWith(options);
62
         this.options = this.options.mergeWith(options);
63
+        if (isRoot()) {
64
+            presenter.applyRootOptions(getView(), options);
65
+        }
62
     }
66
     }
63
 
67
 
64
     @CallSuper
68
     @CallSuper

+ 2
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/SideMenuController.java View File

8
 import android.view.View;
8
 import android.view.View;
9
 
9
 
10
 import com.reactnativenavigation.parse.Options;
10
 import com.reactnativenavigation.parse.Options;
11
+import com.reactnativenavigation.presentation.OptionsPresenter;
11
 import com.reactnativenavigation.presentation.SideMenuOptionsPresenter;
12
 import com.reactnativenavigation.presentation.SideMenuOptionsPresenter;
12
 import com.reactnativenavigation.views.Component;
13
 import com.reactnativenavigation.views.Component;
13
 
14
 
23
 	private ViewController rightController;
24
 	private ViewController rightController;
24
 
25
 
25
 	public SideMenuController(Activity activity, ChildControllersRegistry childRegistry, String id, Options initialOptions) {
26
 	public SideMenuController(Activity activity, ChildControllersRegistry childRegistry, String id, Options initialOptions) {
26
-		super(activity, childRegistry, id, initialOptions);
27
+		super(activity, childRegistry, id, new OptionsPresenter(activity), initialOptions);
27
 	}
28
 	}
28
 
29
 
29
 	@NonNull
30
 	@NonNull

+ 2
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java View File

8
 import com.reactnativenavigation.anim.NavigationAnimator;
8
 import com.reactnativenavigation.anim.NavigationAnimator;
9
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.parse.Options;
10
 import com.reactnativenavigation.parse.params.Button;
10
 import com.reactnativenavigation.parse.params.Button;
11
+import com.reactnativenavigation.presentation.OptionsPresenter;
11
 import com.reactnativenavigation.react.Constants;
12
 import com.reactnativenavigation.react.Constants;
12
 import com.reactnativenavigation.utils.CommandListener;
13
 import com.reactnativenavigation.utils.CommandListener;
13
 import com.reactnativenavigation.utils.CommandListenerAdapter;
14
 import com.reactnativenavigation.utils.CommandListenerAdapter;
36
     private TopBarController topBarController;
37
     private TopBarController topBarController;
37
 
38
 
38
     public StackController(Activity activity, ChildControllersRegistry childRegistry, ReactViewCreator topBarButtonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions) {
39
     public StackController(Activity activity, ChildControllersRegistry childRegistry, ReactViewCreator topBarButtonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions) {
39
-        super(activity, childRegistry, id, initialOptions);
40
+        super(activity, childRegistry, id, new OptionsPresenter(activity), initialOptions);
40
         this.topBarController = topBarController;
41
         this.topBarController = topBarController;
41
         this.topBarButtonCreator = topBarButtonCreator;
42
         this.topBarButtonCreator = topBarButtonCreator;
42
         this.titleBarReactViewCreator = titleBarReactViewCreator;
43
         this.titleBarReactViewCreator = titleBarReactViewCreator;

+ 2
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java View File

12
 import com.reactnativenavigation.parse.BottomTabOptions;
12
 import com.reactnativenavigation.parse.BottomTabOptions;
13
 import com.reactnativenavigation.parse.Options;
13
 import com.reactnativenavigation.parse.Options;
14
 import com.reactnativenavigation.presentation.BottomTabsOptionsPresenter;
14
 import com.reactnativenavigation.presentation.BottomTabsOptionsPresenter;
15
+import com.reactnativenavigation.presentation.OptionsPresenter;
15
 import com.reactnativenavigation.react.EventEmitter;
16
 import com.reactnativenavigation.react.EventEmitter;
16
 import com.reactnativenavigation.utils.CommandListener;
17
 import com.reactnativenavigation.utils.CommandListener;
17
 import com.reactnativenavigation.utils.ImageLoader;
18
 import com.reactnativenavigation.utils.ImageLoader;
40
     private final BottomTabFinder bottomTabFinder = new BottomTabFinder();
41
     private final BottomTabFinder bottomTabFinder = new BottomTabFinder();
41
 
42
 
42
     public BottomTabsController(Activity activity, List<ViewController> tabs, ChildControllersRegistry childRegistry, EventEmitter eventEmitter, ImageLoader imageLoader, String id, Options initialOptions) {
43
     public BottomTabsController(Activity activity, List<ViewController> tabs, ChildControllersRegistry childRegistry, EventEmitter eventEmitter, ImageLoader imageLoader, String id, Options initialOptions) {
43
-		super(activity, childRegistry, id, initialOptions);
44
+		super(activity, childRegistry, id, new OptionsPresenter(activity), initialOptions);
44
         this.tabs = tabs;
45
         this.tabs = tabs;
45
         this.eventEmitter = eventEmitter;
46
         this.eventEmitter = eventEmitter;
46
         this.imageLoader = imageLoader;
47
         this.imageLoader = imageLoader;

+ 2
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java View File

5
 import android.view.View;
5
 import android.view.View;
6
 
6
 
7
 import com.reactnativenavigation.parse.Options;
7
 import com.reactnativenavigation.parse.Options;
8
+import com.reactnativenavigation.presentation.OptionsPresenter;
8
 import com.reactnativenavigation.utils.Task;
9
 import com.reactnativenavigation.utils.Task;
9
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
10
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
10
 import com.reactnativenavigation.viewcontrollers.ParentController;
11
 import com.reactnativenavigation.viewcontrollers.ParentController;
23
     private TopTabsLayoutCreator viewCreator;
24
     private TopTabsLayoutCreator viewCreator;
24
 
25
 
25
     public TopTabsController(Activity activity, ChildControllersRegistry childRegistry, String id, List<ViewController> tabs, TopTabsLayoutCreator viewCreator, Options options) {
26
     public TopTabsController(Activity activity, ChildControllersRegistry childRegistry, String id, List<ViewController> tabs, TopTabsLayoutCreator viewCreator, Options options) {
26
-        super(activity, childRegistry, id, options);
27
+        super(activity, childRegistry, id, new OptionsPresenter(activity), options);
27
         this.viewCreator = viewCreator;
28
         this.viewCreator = viewCreator;
28
         this.tabs = tabs;
29
         this.tabs = tabs;
29
         for (ViewController tab : tabs) {
30
         for (ViewController tab : tabs) {

+ 19
- 8
lib/android/app/src/test/java/com/reactnativenavigation/BaseTest.java View File

1
 package com.reactnativenavigation;
1
 package com.reactnativenavigation;
2
 
2
 
3
-import android.app.*;
4
-import android.support.v7.app.*;
5
-import android.view.*;
3
+import android.app.Activity;
4
+import android.content.Context;
5
+import android.support.v7.app.AppCompatActivity;
6
+import android.view.View;
7
+import android.view.ViewGroup;
8
+import android.widget.FrameLayout;
6
 
9
 
7
 import com.reactnativenavigation.parse.params.Bool;
10
 import com.reactnativenavigation.parse.params.Bool;
8
 import com.reactnativenavigation.utils.ViewUtils;
11
 import com.reactnativenavigation.utils.ViewUtils;
9
 import com.reactnativenavigation.viewcontrollers.ViewController;
12
 import com.reactnativenavigation.viewcontrollers.ViewController;
10
 
13
 
11
-import org.junit.*;
12
-import org.junit.runner.*;
13
-import org.robolectric.*;
14
+import org.junit.After;
15
+import org.junit.Before;
16
+import org.junit.runner.RunWith;
17
+import org.robolectric.Robolectric;
18
+import org.robolectric.RobolectricTestRunner;
14
 import org.robolectric.android.controller.ActivityController;
19
 import org.robolectric.android.controller.ActivityController;
15
-import org.robolectric.annotation.*;
20
+import org.robolectric.annotation.Config;
16
 
21
 
17
-import static org.assertj.core.api.Java6Assertions.*;
22
+import static org.assertj.core.api.Java6Assertions.assertThat;
18
 
23
 
19
 @RunWith(RobolectricTestRunner.class)
24
 @RunWith(RobolectricTestRunner.class)
20
 @Config(sdk = 27, application = TestApplication.class)
25
 @Config(sdk = 27, application = TestApplication.class)
74
     protected void dispatchOnGlobalLayout(View view) {
79
     protected void dispatchOnGlobalLayout(View view) {
75
         view.getViewTreeObserver().dispatchOnGlobalLayout();
80
         view.getViewTreeObserver().dispatchOnGlobalLayout();
76
     }
81
     }
82
+
83
+    protected void addToParent(Context context, ViewController... controllers) {
84
+        for (ViewController controller : controllers) {
85
+            new FrameLayout(context).addView(controller.getView());
86
+        }
87
+    }
77
 }
88
 }

+ 9
- 5
lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleViewController.java View File

9
 
9
 
10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
11
 import com.reactnativenavigation.parse.Options;
11
 import com.reactnativenavigation.parse.Options;
12
+import com.reactnativenavigation.presentation.OptionsPresenter;
12
 import com.reactnativenavigation.viewcontrollers.ChildController;
13
 import com.reactnativenavigation.viewcontrollers.ChildController;
13
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
14
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
14
-import com.reactnativenavigation.views.Component;
15
 import com.reactnativenavigation.views.ReactComponent;
15
 import com.reactnativenavigation.views.ReactComponent;
16
 import com.reactnativenavigation.views.topbar.TopBar;
16
 import com.reactnativenavigation.views.topbar.TopBar;
17
 
17
 
18
-public class SimpleViewController extends ChildController<FrameLayout> {
18
+public class SimpleViewController extends ChildController<SimpleViewController.SimpleView> {
19
 
19
 
20
     private SimpleView simpleView;
20
     private SimpleView simpleView;
21
 
21
 
22
     public SimpleViewController(Activity activity, ChildControllersRegistry childRegistry, String id, Options options) {
22
     public SimpleViewController(Activity activity, ChildControllersRegistry childRegistry, String id, Options options) {
23
-        super(activity, childRegistry, id, options);
23
+        this(activity, childRegistry, id, new OptionsPresenter(activity), options);
24
+    }
25
+
26
+    public SimpleViewController(Activity activity, ChildControllersRegistry childRegistry, String id, OptionsPresenter presenter, Options options) {
27
+        super(activity, childRegistry, id, presenter, options);
24
     }
28
     }
25
 
29
 
26
     @Override
30
     @Override
27
-    protected FrameLayout createView() {
31
+    protected SimpleView createView() {
28
         simpleView = new SimpleView(getActivity());
32
         simpleView = new SimpleView(getActivity());
29
         return simpleView;
33
         return simpleView;
30
     }
34
     }
41
 
45
 
42
     @Override
46
     @Override
43
     public void mergeOptions(Options options) {
47
     public void mergeOptions(Options options) {
44
-        applyOnParentController(parentController -> parentController.mergeChildOptions(options, (Component) view));
48
+        applyOnParentController(parentController -> parentController.mergeChildOptions(options, view));
45
         super.mergeOptions(options);
49
         super.mergeOptions(options);
46
     }
50
     }
47
 
51
 

+ 32
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigationActivityTest.java View File

1
+package com.reactnativenavigation.viewcontrollers;
2
+
3
+import android.view.View;
4
+
5
+import com.reactnativenavigation.BaseTest;
6
+import com.reactnativenavigation.NavigationActivity;
7
+import com.reactnativenavigation.TestActivity;
8
+
9
+import org.junit.Test;
10
+import org.robolectric.android.controller.ActivityController;
11
+
12
+import static org.assertj.core.api.Java6Assertions.assertThat;
13
+
14
+public class NavigationActivityTest extends BaseTest {
15
+    private ActivityController<? extends NavigationActivity> controller;
16
+    private NavigationActivity uut;
17
+
18
+    @Override
19
+    public void beforeEach() {
20
+        controller = newActivityController(TestActivity.class);
21
+        uut = controller.get();
22
+    }
23
+
24
+    @Test
25
+    public void onCreate_setSystemUiVisibility() {
26
+        controller.setup();
27
+        assertThat(uut
28
+                .getNavigator()
29
+                .getView()
30
+                .getSystemUiVisibility()).isEqualTo(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
31
+    }
32
+}

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

12
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
12
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
13
 import com.reactnativenavigation.parse.Options;
13
 import com.reactnativenavigation.parse.Options;
14
 import com.reactnativenavigation.parse.params.Text;
14
 import com.reactnativenavigation.parse.params.Text;
15
+import com.reactnativenavigation.presentation.OptionsPresenter;
15
 import com.reactnativenavigation.utils.CommandListenerAdapter;
16
 import com.reactnativenavigation.utils.CommandListenerAdapter;
16
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
17
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
17
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
18
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
19
 
20
 
20
 import org.junit.Test;
21
 import org.junit.Test;
21
 import org.mockito.ArgumentCaptor;
22
 import org.mockito.ArgumentCaptor;
23
+import org.mockito.Mockito;
22
 
24
 
23
 import java.util.ArrayList;
25
 import java.util.ArrayList;
24
 import java.util.Collection;
26
 import java.util.Collection;
36
     private ChildControllersRegistry childRegistry;
38
     private ChildControllersRegistry childRegistry;
37
     private List<ViewController> children;
39
     private List<ViewController> children;
38
     private ParentController uut;
40
     private ParentController uut;
41
+    private OptionsPresenter presenter;
39
 
42
 
40
     @Override
43
     @Override
41
     public void beforeEach() {
44
     public void beforeEach() {
45
         children = new ArrayList<>();
48
         children = new ArrayList<>();
46
         Options initialOptions = new Options();
49
         Options initialOptions = new Options();
47
         initialOptions.topBar.title.text = new Text(INITIAL_TITLE);
50
         initialOptions.topBar.title.text = new Text(INITIAL_TITLE);
48
-        uut = spy(new ParentController(activity, childRegistry, "uut", initialOptions) {
51
+        presenter = spy(new OptionsPresenter(activity));
52
+        uut = spy(new ParentController(activity, childRegistry, "uut", presenter, initialOptions) {
49
 
53
 
50
             @NonNull
54
             @NonNull
51
             @Override
55
             @Override
156
         assertThat(uut.initialOptions.topBar.title.text.get()).isEqualTo(INITIAL_TITLE);
160
         assertThat(uut.initialOptions.topBar.title.text.get()).isEqualTo(INITIAL_TITLE);
157
     }
161
     }
158
 
162
 
163
+    @Test
164
+    public void applyChildOptions_appliesRootOptionsIfRoot() {
165
+        addToParent(activity, uut);
166
+
167
+        Options options = new Options();
168
+        SimpleViewController child1 = spy(new SimpleViewController(activity, childRegistry, "child1", options));
169
+        uut.applyChildOptions(options, child1.getView());
170
+        verify(presenter, times(1)).applyRootOptions(uut.getView(), options);
171
+    }
172
+
173
+    @Test
174
+    public void applyChildOptions_doesNotApplyRootOptionsIfHasParent() {
175
+        Options options = new Options();
176
+        uut.setParentController(Mockito.mock(ParentController.class));
177
+        SimpleViewController child1 = spy(new SimpleViewController(activity, childRegistry, "child1", options));
178
+        uut.applyChildOptions(options, child1.getView());
179
+        verify(presenter, times(0)).applyRootOptions(uut.getView(), options);
180
+    }
181
+
159
     private StackController createStack() {
182
     private StackController createStack() {
160
         return new StackControllerBuilder(activity)
183
         return new StackControllerBuilder(activity)
161
                 .setTopBarButtonCreator(new TopBarButtonCreatorMock())
184
                 .setTopBarButtonCreator(new TopBarButtonCreatorMock())

+ 5
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ViewControllerTest.java View File

18
 
18
 
19
 import org.assertj.android.api.Assertions;
19
 import org.assertj.android.api.Assertions;
20
 import org.junit.Test;
20
 import org.junit.Test;
21
+import org.mockito.Mockito;
21
 import org.robolectric.Shadows;
22
 import org.robolectric.Shadows;
22
 
23
 
23
 import java.lang.reflect.Field;
24
 import java.lang.reflect.Field;
40
         activity = newActivity();
41
         activity = newActivity();
41
         childRegistry = new ChildControllersRegistry();
42
         childRegistry = new ChildControllersRegistry();
42
         uut = new SimpleViewController(activity, childRegistry, "uut", new Options());
43
         uut = new SimpleViewController(activity, childRegistry, "uut", new Options());
44
+        uut.setParentController(Mockito.mock(ParentController.class));
43
     }
45
     }
44
 
46
 
45
     @Test
47
     @Test
71
 
73
 
72
     @Test
74
     @Test
73
     public void holdsAReferenceToStackControllerOrNull() {
75
     public void holdsAReferenceToStackControllerOrNull() {
76
+        //noinspection ConstantConditions
77
+        uut.setParentController(null);
78
+
74
         assertThat(uut.getParentController()).isNull();
79
         assertThat(uut.getParentController()).isNull();
75
         StackController nav = new StackControllerBuilder(activity)
80
         StackController nav = new StackControllerBuilder(activity)
76
                 .setTopBarButtonCreator(new TopBarButtonCreatorMock())
81
                 .setTopBarButtonCreator(new TopBarButtonCreatorMock())

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

3
 import com.reactnativenavigation.BaseTest;
3
 import com.reactnativenavigation.BaseTest;
4
 import com.reactnativenavigation.mocks.SimpleViewController;
4
 import com.reactnativenavigation.mocks.SimpleViewController;
5
 import com.reactnativenavigation.parse.Options;
5
 import com.reactnativenavigation.parse.Options;
6
+import com.reactnativenavigation.presentation.OptionsPresenter;
6
 import com.reactnativenavigation.viewcontrollers.ChildController;
7
 import com.reactnativenavigation.viewcontrollers.ChildController;
7
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
8
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
9
+import com.reactnativenavigation.viewcontrollers.ParentController;
8
 
10
 
9
 import org.junit.Test;
11
 import org.junit.Test;
12
+import org.mockito.Mockito;
10
 
13
 
11
 import static org.mockito.Mockito.spy;
14
 import static org.mockito.Mockito.spy;
12
 import static org.mockito.Mockito.times;
15
 import static org.mockito.Mockito.times;
16
 
19
 
17
     private ChildController uut;
20
     private ChildController uut;
18
     private ChildControllersRegistry childRegistry;
21
     private ChildControllersRegistry childRegistry;
22
+    private OptionsPresenter presenter;
19
 
23
 
20
     @Override
24
     @Override
21
     public void beforeEach() {
25
     public void beforeEach() {
22
         childRegistry = spy(new ChildControllersRegistry());
26
         childRegistry = spy(new ChildControllersRegistry());
23
-        uut = new SimpleViewController(newActivity(), childRegistry, "childId", new Options());
27
+        presenter = Mockito.mock(OptionsPresenter.class);
28
+        uut = new SimpleViewController(newActivity(), childRegistry, "childId", presenter, new Options());
24
     }
29
     }
25
 
30
 
26
     @Test
31
     @Test
36
         uut.onViewDisappear();
41
         uut.onViewDisappear();
37
         verify(childRegistry, times(1)).onViewDisappear(uut);
42
         verify(childRegistry, times(1)).onViewDisappear(uut);
38
     }
43
     }
44
+
45
+    @Test
46
+    public void applyOptions_applyRootOptionsIfRoot() {
47
+        addToParent(newActivity(), uut);
48
+        Options options = new Options();
49
+        uut.applyOptions(options);
50
+        verify(presenter, times(1)).applyRootOptions(uut.getView(), options);
51
+    }
52
+
53
+    @Test
54
+    public void applyOptions_doesNotApplyRootOptionsIfHasParent() {
55
+        Options options = new Options();
56
+        uut.setParentController(Mockito.mock(ParentController.class));
57
+        uut.applyOptions(options);
58
+        verify(presenter, times(0)).applyRootOptions(uut.getView(), options);
59
+    }
39
 }
60
 }

+ 5
- 0
playground/src/screens/ModalScreen.js View File

11
 class ModalScreen extends Component {
11
 class ModalScreen extends Component {
12
   static get options() {
12
   static get options() {
13
     return {
13
     return {
14
+      statusBar: {
15
+        visible: false,
16
+        drawBehind: true,
17
+        backgroundColor: 'transparent'
18
+      },
14
       orientation: ['portrait']
19
       orientation: ['portrait']
15
     };
20
     };
16
   }
21
   }

+ 4
- 0
playground/src/screens/PushedScreen.js View File

11
 class PushedScreen extends Component {
11
 class PushedScreen extends Component {
12
   static get options() {
12
   static get options() {
13
     return {
13
     return {
14
+      _statusBar: {
15
+        visible: false,
16
+        drawBehind: true
17
+      },
14
       topBar: {
18
       topBar: {
15
         testID: testIDs.TOP_BAR_ELEMENT
19
         testID: testIDs.TOP_BAR_ELEMENT
16
       }
20
       }

+ 33
- 18
playground/src/screens/WelcomeScreen.js View File

9
 class WelcomeScreen extends Component {
9
 class WelcomeScreen extends Component {
10
   static get options() {
10
   static get options() {
11
     return {
11
     return {
12
+      _statusBar: {
13
+        backgroundColor: 'transparent',
14
+        style: 'dark',
15
+        drawBehind: true
16
+      },
12
       topBar: {
17
       topBar: {
13
         title: {
18
         title: {
14
           largeTitle: false,
19
           largeTitle: false,
23
 
28
 
24
   render() {
29
   render() {
25
     return (
30
     return (
26
-      <View style={styles.root} key={'root'}>
27
-        <Text testID={testIDs.WELCOME_SCREEN_HEADER} style={styles.h1}>{`React Native Navigation!`}</Text>
28
-        <Button title='Switch to tab based app' testID={testIDs.TAB_BASED_APP_BUTTON} onPress={this.onClickSwitchToTabs} />
29
-        <Button title='Switch to app with side menus' testID={testIDs.TAB_BASED_APP_SIDE_BUTTON} onPress={this.onClickSwitchToSideMenus} />
30
-        <Button title='Push Lifecycle Screen' testID={testIDs.PUSH_LIFECYCLE_BUTTON} onPress={this.onClickLifecycleScreen} />
31
-        <Button title='Static Lifecycle Events' testID={testIDs.PUSH_STATIC_LIFECYCLE_BUTTON} onPress={this.onClickShowStaticLifecycleOverlay} />
32
-        <Button title='Push' testID={testIDs.PUSH_BUTTON} onPress={this.onClickPush} />
33
-        <Button title='Push Options Screen' testID={testIDs.PUSH_OPTIONS_BUTTON} onPress={this.onClickPushOptionsScreen} />
34
-        <Button title='Push External Component' testID={testIDs.PUSH_EXTERNAL_COMPONENT_BUTTON} onPress={this.onClickPushExternalComponent} />
35
-        {Platform.OS === 'android' && <Button title='Push Top Tabs screen' testID={testIDs.PUSH_TOP_TABS_BUTTON} onPress={this.onClickPushTopTabsScreen} />}
36
-        {Platform.OS === 'android' && <Button title='Back Handler' testID={testIDs.BACK_HANDLER_BUTTON} onPress={this.onClickBackHandler} />}
37
-        <Button title='Show Modal' testID={testIDs.SHOW_MODAL_BUTTON} onPress={this.onClickShowModal} />
38
-        <Button title='Show Redbox' testID={testIDs.SHOW_REDBOX_BUTTON} onPress={this.onClickShowRedbox} />
39
-        <Button title='Orientation' testID={testIDs.ORIENTATION_BUTTON} onPress={this.onClickPushOrientationMenuScreen} />
40
-        <Button title='Provided Id' testID={testIDs.PROVIDED_ID} onPress={this.onClickProvidedId} />
41
-        <Button title='Complex Layout' testID={testIDs.COMPLEX_LAYOUT_BUTTON} onPress={this.onClickComplexLayout} />
42
-        <Text style={styles.footer}>{`this.props.componentId = ${this.props.componentId}`}</Text>
31
+      <View style={styles.bar}>
32
+        <View style={{ width: 2, height: 2, borderRadius: 1, backgroundColor: 'red', alignSelf: 'center' }} />
33
+        <View style={styles.root} key={'root'}>
34
+          <Text testID={testIDs.WELCOME_SCREEN_HEADER} style={styles.h1}>{`React Native Navigation!`}</Text>
35
+          <Button title='Switch to tab based app' testID={testIDs.TAB_BASED_APP_BUTTON} onPress={this.onClickSwitchToTabs} />
36
+          <Button title='Switch to app with side menus' testID={testIDs.TAB_BASED_APP_SIDE_BUTTON} onPress={this.onClickSwitchToSideMenus} />
37
+          <Button title='Push Lifecycle Screen' testID={testIDs.PUSH_LIFECYCLE_BUTTON} onPress={this.onClickLifecycleScreen} />
38
+          <Button title='Static Lifecycle Events' testID={testIDs.PUSH_STATIC_LIFECYCLE_BUTTON} onPress={this.onClickShowStaticLifecycleOverlay} />
39
+          <Button title='Push' testID={testIDs.PUSH_BUTTON} onPress={this.onClickPush} />
40
+          <Button title='Push Options Screen' testID={testIDs.PUSH_OPTIONS_BUTTON} onPress={this.onClickPushOptionsScreen} />
41
+          <Button title='Push External Component' testID={testIDs.PUSH_EXTERNAL_COMPONENT_BUTTON} onPress={this.onClickPushExternalComponent} />
42
+          {Platform.OS === 'android' && <Button title='Push Top Tabs screen' testID={testIDs.PUSH_TOP_TABS_BUTTON} onPress={this.onClickPushTopTabsScreen} />}
43
+          {Platform.OS === 'android' && <Button title='Back Handler' testID={testIDs.BACK_HANDLER_BUTTON} onPress={this.onClickBackHandler} />}
44
+          <Button title='Show Modal' testID={testIDs.SHOW_MODAL_BUTTON} onPress={this.onClickShowModal} />
45
+          <Button title='Show Redbox' testID={testIDs.SHOW_REDBOX_BUTTON} onPress={this.onClickShowRedbox} />
46
+          <Button title='Orientation' testID={testIDs.ORIENTATION_BUTTON} onPress={this.onClickPushOrientationMenuScreen} />
47
+          <Button title='Provided Id' testID={testIDs.PROVIDED_ID} onPress={this.onClickProvidedId} />
48
+          <Button title='Complex Layout' testID={testIDs.COMPLEX_LAYOUT_BUTTON} onPress={this.onClickComplexLayout} />
49
+          <Text style={styles.footer}>{`this.props.componentId = ${this.props.componentId}`}</Text>
50
+        </View>
51
+        <View style={{ width: 2, height: 2, borderRadius: 1, backgroundColor: 'red', alignSelf: 'center' }} />
43
       </View>
52
       </View>
44
     );
53
     );
45
   }
54
   }
447
     flexGrow: 1,
456
     flexGrow: 1,
448
     justifyContent: 'center',
457
     justifyContent: 'center',
449
     alignItems: 'center',
458
     alignItems: 'center',
450
-    backgroundColor: 'white'
459
+    backgroundColor: '#e8e8e8',
460
+  },
461
+  bar: {
462
+    flex: 1,
463
+    flexDirection: 'column',
464
+    backgroundColor: '#e8e8e8',
465
+    justifyContent: 'space-between'
451
   },
466
   },
452
   h1: {
467
   h1: {
453
     fontSize: 24,
468
     fontSize: 24,