Browse Source

Top bar buttons android (#2876)

* TopBar custom buttons initial commit

* f

* Consolidate TopBarButtonController and TopBarButton

* Move Toolbar related stuff to TitleBar

* TitleBarTest

* More tests
Guy Carmeli 6 years ago
parent
commit
21d2103475
No account linked to committer's email address
34 changed files with 838 additions and 323 deletions
  1. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  2. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java
  3. 10
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java
  4. 7
    2
      lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java
  5. 22
    0
      lib/android/app/src/main/java/com/reactnativenavigation/react/TopBarReactButtonView.java
  6. 16
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoadingListenerAdapter.java
  7. 12
    10
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java
  8. 192
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TopBarButtonController.java
  9. 3
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  10. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java
  11. 5
    7
      lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java
  12. 140
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/TitleBar.java
  13. 0
    176
      lib/android/app/src/main/java/com/reactnativenavigation/views/TitleBarButton.java
  14. 23
    81
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopBar.java
  15. 21
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopBarButtonCreator.java
  16. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsViewPager.java
  17. 2
    2
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentLayout.java
  18. 28
    0
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TopBarButtonCreatorMock.java
  19. 4
    3
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java
  20. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ComponentViewControllerTest.java
  21. 4
    4
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/FloatingActionButtonTest.java
  22. 4
    3
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java
  23. 5
    4
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java
  24. 3
    2
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ParentControllerTest.java
  25. 7
    6
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java
  26. 124
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TitleBarTest.java
  27. 57
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarButtonControllerTest.java
  28. 3
    2
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java
  29. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ViewControllerTest.java
  30. 8
    6
      lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarTest.java
  31. 55
    0
      playground/src/screens/CustomRoundedButton.js
  32. 46
    0
      playground/src/screens/CustomTextButton.js
  33. 21
    7
      playground/src/screens/OptionsScreen.js
  34. 4
    0
      playground/src/screens/index.js

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

15
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentViewController;
15
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentViewController;
16
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
16
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
17
 import com.reactnativenavigation.views.ComponentViewCreator;
17
 import com.reactnativenavigation.views.ComponentViewCreator;
18
+import com.reactnativenavigation.views.TopBarButtonCreator;
18
 import com.reactnativenavigation.views.TopTabsLayoutCreator;
19
 import com.reactnativenavigation.views.TopTabsLayoutCreator;
19
 
20
 
20
 import java.util.ArrayList;
21
 import java.util.ArrayList;
117
     }
118
     }
118
 
119
 
119
 	private ViewController createStack(LayoutNode node) {
120
 	private ViewController createStack(LayoutNode node) {
120
-        StackController stackController = new StackController(activity, node.id, getOptions(node));
121
+        StackController stackController = new StackController(activity, new TopBarButtonCreator(reactInstanceManager), node.id, getOptions(node));
121
         addChildrenToStack(node.children, stackController);
122
         addChildrenToStack(node.children, stackController);
122
         return stackController;
123
         return stackController;
123
 	}
124
 	}

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

55
     public Bool animate = new NullBool();
55
     public Bool animate = new NullBool();
56
     public Bool hideOnScroll = new NullBool();
56
     public Bool hideOnScroll = new NullBool();
57
     public Bool drawBehind = new NullBool();
57
     public Bool drawBehind = new NullBool();
58
-    public ArrayList<Button> leftButtons;
59
-    public ArrayList<Button> rightButtons;
58
+    @Nullable public ArrayList<Button> leftButtons;
59
+    @Nullable public ArrayList<Button> rightButtons;
60
 
60
 
61
     void mergeWith(final TopBarOptions other) {
61
     void mergeWith(final TopBarOptions other) {
62
         if (other.testId.hasValue()) {
62
         if (other.testId.hasValue()) {

+ 10
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java View File

24
 	private Text buttonFontWeight = new NullText();
24
 	private Text buttonFontWeight = new NullText();
25
 	public Text icon = new NullText();
25
 	public Text icon = new NullText();
26
 	public Text testId = new NullText();
26
 	public Text testId = new NullText();
27
+    public Text component = new NullText();
27
 
28
 
28
 	private static Button parseJson(JSONObject json)  {
29
 	private static Button parseJson(JSONObject json)  {
29
 		Button button = new Button();
30
 		Button button = new Button();
36
 		button.buttonFontSize = json.optInt("buttonFontSize", NO_INT_VALUE);
37
 		button.buttonFontSize = json.optInt("buttonFontSize", NO_INT_VALUE);
37
 		button.buttonFontWeight = TextParser.parse(json, "buttonFontWeight");
38
 		button.buttonFontWeight = TextParser.parse(json, "buttonFontWeight");
38
         button.testId = TextParser.parse(json, "testID");
39
         button.testId = TextParser.parse(json, "testID");
40
+        button.component = TextParser.parse(json, "component");
39
 
41
 
40
 		if (json.has("icon")) {
42
 		if (json.has("icon")) {
41
 			button.icon = TextParser.parse(json.optJSONObject("icon"), "uri");
43
 			button.icon = TextParser.parse(json.optJSONObject("icon"), "uri");
60
 		return buttons;
62
 		return buttons;
61
 	}
63
 	}
62
 
64
 
65
+    public boolean hasComponent() {
66
+        return component.hasValue();
67
+    }
68
+
69
+    public boolean hasIcon() {
70
+        return icon.hasValue();
71
+    }
72
+
63
 	private static int parseShowAsAction(JSONObject json) {
73
 	private static int parseShowAsAction(JSONObject json) {
64
 	    final Text showAsAction = TextParser.parse(json, "showAsAction");
74
 	    final Text showAsAction = TextParser.parse(json, "showAsAction");
65
 		if (!showAsAction.hasValue()) {
75
 		if (!showAsAction.hasValue()) {

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

3
 import android.annotation.SuppressLint;
3
 import android.annotation.SuppressLint;
4
 import android.content.Context;
4
 import android.content.Context;
5
 import android.os.Bundle;
5
 import android.os.Bundle;
6
+import android.support.annotation.RestrictTo;
6
 import android.view.MotionEvent;
7
 import android.view.MotionEvent;
7
-import android.view.View;
8
 
8
 
9
 import com.facebook.react.ReactInstanceManager;
9
 import com.facebook.react.ReactInstanceManager;
10
 import com.facebook.react.ReactRootView;
10
 import com.facebook.react.ReactRootView;
49
 	}
49
 	}
50
 
50
 
51
 	@Override
51
 	@Override
52
-	public View asView() {
52
+	public ReactView asView() {
53
 		return this;
53
 		return this;
54
 	}
54
 	}
55
 
55
 
87
         ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
87
         ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
88
         return reactContext == null ? null : reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
88
         return reactContext == null ? null : reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
89
     }
89
     }
90
+
91
+    @RestrictTo(RestrictTo.Scope.TESTS)
92
+    public String getComponentName() {
93
+        return componentName;
94
+    }
90
 }
95
 }

+ 22
- 0
lib/android/app/src/main/java/com/reactnativenavigation/react/TopBarReactButtonView.java View File

1
+package com.reactnativenavigation.react;
2
+
3
+import android.annotation.SuppressLint;
4
+import android.content.Context;
5
+
6
+import com.facebook.react.ReactInstanceManager;
7
+
8
+@SuppressLint("ViewConstructor")
9
+public class TopBarReactButtonView extends ReactView {
10
+
11
+    public TopBarReactButtonView(Context context, ReactInstanceManager reactInstanceManager, String componentId, String componentName) {
12
+        super(context, reactInstanceManager, componentId, componentName);
13
+    }
14
+
15
+    @Override
16
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
17
+        super.onMeasure(
18
+                getChildCount() > 0 ? MeasureSpec.makeMeasureSpec(getChildAt(0).getWidth(), MeasureSpec.EXACTLY) : widthMeasureSpec,
19
+                heightMeasureSpec
20
+        );
21
+    }
22
+}

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

1
+package com.reactnativenavigation.utils;
2
+
3
+import android.graphics.drawable.Drawable;
4
+import android.support.annotation.NonNull;
5
+
6
+public class ImageLoadingListenerAdapter implements ImageLoader.ImageLoadingListener {
7
+    @Override
8
+    public void onComplete(@NonNull Drawable drawable) {
9
+
10
+    }
11
+
12
+    @Override
13
+    public void onError(Throwable error) {
14
+        error.printStackTrace();
15
+    }
16
+}

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

25
     private static final NoOpPromise NO_OP = new NoOpPromise();
25
     private static final NoOpPromise NO_OP = new NoOpPromise();
26
     private final IdStack<ViewController> stack = new IdStack<>();
26
     private final IdStack<ViewController> stack = new IdStack<>();
27
     private final NavigationAnimator animator;
27
     private final NavigationAnimator animator;
28
+    private ReactViewCreator topBarButtonCreator;
28
 
29
 
29
-    public StackController(final Activity activity, String id, Options initialOptions) {
30
+    public StackController(final Activity activity, ReactViewCreator topBarButtonCreator, String id, Options initialOptions) {
30
         super(activity, id, initialOptions);
31
         super(activity, id, initialOptions);
31
         animator = new NavigationAnimator(activity);
32
         animator = new NavigationAnimator(activity);
33
+        this.topBarButtonCreator = topBarButtonCreator;
32
     }
34
     }
33
 
35
 
34
-    @RestrictTo(RestrictTo.Scope.TESTS)
35
-    TopBar getTopBar() {
36
-        return getView().getTopBar();
37
-    }
38
-
39
-    @RestrictTo(RestrictTo.Scope.TESTS)
40
-    StackLayout getStackLayout() {return getView();}
41
-
42
     public void applyOptions(Options options) {
36
     public void applyOptions(Options options) {
43
         super.applyOptions(options);
37
         super.applyOptions(options);
44
         getView().applyOptions(options);
38
         getView().applyOptions(options);
208
     @NonNull
202
     @NonNull
209
     @Override
203
     @Override
210
     protected StackLayout createView() {
204
     protected StackLayout createView() {
211
-        return new StackLayout(getActivity(), this::sendOnNavigationButtonPressed);
205
+        return new StackLayout(getActivity(), topBarButtonCreator, this::sendOnNavigationButtonPressed);
212
     }
206
     }
213
 
207
 
214
 	@NonNull
208
 	@NonNull
226
     public void clearTopTabs() {
220
     public void clearTopTabs() {
227
         getView().clearTopTabs();
221
         getView().clearTopTabs();
228
     }
222
     }
223
+
224
+    @RestrictTo(RestrictTo.Scope.TESTS)
225
+    TopBar getTopBar() {
226
+        return getView().getTopBar();
227
+    }
228
+
229
+    @RestrictTo(RestrictTo.Scope.TESTS)
230
+    StackLayout getStackLayout() {return getView();}
229
 }
231
 }

+ 192
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TopBarButtonController.java View File

1
+package com.reactnativenavigation.viewcontrollers;
2
+
3
+import android.app.Activity;
4
+import android.graphics.Color;
5
+import android.graphics.drawable.Drawable;
6
+import android.support.annotation.NonNull;
7
+import android.support.v7.widget.ActionMenuView;
8
+import android.support.v7.widget.Toolbar;
9
+import android.text.Spannable;
10
+import android.text.SpannableString;
11
+import android.text.style.AbsoluteSizeSpan;
12
+import android.util.Log;
13
+import android.view.MenuItem;
14
+import android.view.View;
15
+import android.widget.ImageButton;
16
+import android.widget.TextView;
17
+
18
+import com.reactnativenavigation.parse.Options;
19
+import com.reactnativenavigation.parse.params.Button;
20
+import com.reactnativenavigation.parse.params.Text;
21
+import com.reactnativenavigation.react.TopBarReactButtonView;
22
+import com.reactnativenavigation.utils.ArrayUtils;
23
+import com.reactnativenavigation.utils.ImageLoader;
24
+import com.reactnativenavigation.utils.ImageLoadingListenerAdapter;
25
+import com.reactnativenavigation.utils.UiUtils;
26
+import com.reactnativenavigation.utils.ViewUtils;
27
+
28
+import java.util.ArrayList;
29
+import java.util.List;
30
+
31
+public class TopBarButtonController extends ViewController<TopBarReactButtonView> implements MenuItem.OnMenuItemClickListener {
32
+    public interface OnClickListener {
33
+        void onPress(String buttonId);
34
+    }
35
+
36
+    private final Button button;
37
+    private final ReactViewCreator viewCreator;
38
+    private TopBarButtonController.OnClickListener onPressListener;
39
+    private Drawable icon;
40
+
41
+    public TopBarButtonController(Activity activity, Button button, ReactViewCreator viewCreator, TopBarButtonController.OnClickListener onClickListener) {
42
+        super(activity, button.id, new Options());
43
+        this.button = button;
44
+        this.viewCreator = viewCreator;
45
+        this.onPressListener = onClickListener;
46
+    }
47
+
48
+    @Override
49
+    public void onViewAppeared() {
50
+        view.sendComponentStart();
51
+    }
52
+
53
+    @Override
54
+    public void onViewDisappear() {
55
+        view.sendComponentStop();
56
+    }
57
+
58
+    @Override
59
+    public void sendOnNavigationButtonPressed(String buttonId) {
60
+        getView().sendOnNavigationButtonPressed(buttonId);
61
+    }
62
+
63
+    @NonNull
64
+    @Override
65
+    protected TopBarReactButtonView createView() {
66
+        view = (TopBarReactButtonView) viewCreator.create(getActivity(), getId(), button.component.get());
67
+        return (TopBarReactButtonView) view.asView();
68
+    }
69
+
70
+    @Override
71
+    public boolean onMenuItemClick(MenuItem item) {
72
+        onPressListener.onPress(button.id);
73
+        return true;
74
+    }
75
+
76
+    public void applyNavigationIcon(Toolbar toolbar) {
77
+        if (!button.hasIcon()) {
78
+            Log.w("RNN", "Left button needs to have an icon");
79
+            return;
80
+        }
81
+
82
+        new ImageLoader().loadIcon(toolbar.getContext(), button.icon.get(), new ImageLoader.ImageLoadingListener() {
83
+            @Override
84
+            public void onComplete(@NonNull Drawable drawable) {
85
+                icon = drawable;
86
+                setIconColor(drawable);
87
+                toolbar.setNavigationOnClickListener(view -> onPressListener.onPress(button.id));
88
+                toolbar.setNavigationIcon(icon);
89
+                setLeftButtonTestId(toolbar);
90
+            }
91
+
92
+            @Override
93
+            public void onError(Throwable error) {
94
+                error.printStackTrace();
95
+            }
96
+        });
97
+    }
98
+
99
+    private void setLeftButtonTestId(Toolbar toolbar) {
100
+        if (!button.testId.hasValue()) return;
101
+        toolbar.post(() -> {
102
+            ImageButton leftButton = ViewUtils.findChildByClass(toolbar, ImageButton.class);
103
+            if (leftButton != null) {
104
+                leftButton.setTag(button.testId.get());
105
+            }
106
+        });
107
+    }
108
+
109
+    public void addToMenu(Toolbar toolbar) {
110
+        MenuItem menuItem = toolbar.getMenu().add(button.title.get(""));
111
+        menuItem.setShowAsAction(button.showAsAction);
112
+        menuItem.setEnabled(button.enabled.isTrueOrUndefined());
113
+        menuItem.setOnMenuItemClickListener(this);
114
+        if (button.hasComponent()) {
115
+            menuItem.setActionView(getView());
116
+        } else {
117
+            if (button.hasIcon()) {
118
+                loadIcon(new ImageLoadingListenerAdapter() {
119
+                    @Override
120
+                    public void onComplete(@NonNull Drawable icon) {
121
+                        TopBarButtonController.this.icon = icon;
122
+                        setIconColor(icon);
123
+                        menuItem.setIcon(icon);
124
+                    }
125
+                });
126
+            } else {
127
+                setTextColor(toolbar);
128
+                setFontSize(menuItem);
129
+            }
130
+        }
131
+        setTestId(toolbar, button.testId);
132
+    }
133
+
134
+    private void loadIcon(ImageLoader.ImageLoadingListener callbacks) {
135
+        new ImageLoader().loadIcon(getActivity(), button.icon.get(), callbacks);
136
+    }
137
+
138
+    private void setIconColor(Drawable icon) {
139
+        if (button.enabled.isTrueOrUndefined()) {
140
+            UiUtils.tintDrawable(icon, button.buttonColor);
141
+            return;
142
+        }
143
+        if (button.disableIconTint.isTrue()) {
144
+            UiUtils.tintDrawable(icon, button.buttonColor);
145
+        } else {
146
+            UiUtils.tintDrawable(icon, Color.LTGRAY);
147
+        }
148
+    }
149
+
150
+    private void setTextColor(Toolbar toolbar) {
151
+        UiUtils.runOnPreDrawOnce(toolbar, () -> {
152
+            ArrayList<View> outViews = findActualTextViewInMenuByText(toolbar);
153
+            setTextColorForFoundButtonViews(outViews);
154
+        });
155
+    }
156
+
157
+    @NonNull
158
+    private ArrayList<View> findActualTextViewInMenuByText(Toolbar toolbar) {
159
+        ArrayList<View> outViews = new ArrayList<>();
160
+        toolbar.findViewsWithText(outViews, button.title.get(), View.FIND_VIEWS_WITH_TEXT);
161
+        return outViews;
162
+    }
163
+
164
+    private void setTextColorForFoundButtonViews(ArrayList<View> buttons) {
165
+        for (View button : buttons) {
166
+            ((TextView) button).setTextColor(this.button.buttonColor);
167
+        }
168
+    }
169
+
170
+    private void setFontSize(MenuItem menuItem) {
171
+        SpannableString spanString = new SpannableString(button.title.get());
172
+        spanString.setSpan(new AbsoluteSizeSpan(button.buttonFontSize, true), 0, button.title.get().length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
173
+        menuItem.setTitleCondensed(spanString);
174
+    }
175
+
176
+    private void setTestId(Toolbar toolbar, Text testId) {
177
+        if (!testId.hasValue()) return;
178
+        UiUtils.runOnPreDrawOnce(toolbar, () -> {
179
+            ActionMenuView buttonsLayout = ViewUtils.findChildByClass(toolbar, ActionMenuView.class);
180
+            List<TextView> buttons = ViewUtils.findChildrenByClass(buttonsLayout, TextView.class);
181
+            for (TextView view : buttons) {
182
+                if (button.title.hasValue() && button.title.get().equals(view.getText().toString())) {
183
+                    view.setTag(testId.get());
184
+                } else if (button.icon.hasValue() && ArrayUtils.contains(view.getCompoundDrawables(), icon)) {
185
+                    view.setTag(testId.get());
186
+                }
187
+            }
188
+        });
189
+    }
190
+
191
+
192
+}

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

113
                 throw new RuntimeException("Tried to create view after it has already been destroyed");
113
                 throw new RuntimeException("Tried to create view after it has already been destroyed");
114
             }
114
             }
115
             view = createView();
115
             view = createView();
116
-            view.setId(CompatUtils.generateViewId());
116
+            if (view.getId() < 0) {
117
+                view.setId(CompatUtils.generateViewId());
118
+            }
117
             view.getViewTreeObserver().addOnGlobalLayoutListener(this);
119
             view.getViewTreeObserver().addOnGlobalLayoutListener(this);
118
         }
120
         }
119
         return view;
121
         return view;

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

11
 import com.reactnativenavigation.parse.Options;
11
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.presentation.ComponentOptionsPresenter;
12
 import com.reactnativenavigation.presentation.ComponentOptionsPresenter;
13
 import com.reactnativenavigation.viewcontrollers.IReactView;
13
 import com.reactnativenavigation.viewcontrollers.IReactView;
14
+import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
14
 import com.reactnativenavigation.views.touch.OverlayTouchDelegate;
15
 import com.reactnativenavigation.views.touch.OverlayTouchDelegate;
15
 
16
 
16
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
17
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
17
 import static android.widget.RelativeLayout.BELOW;
18
 import static android.widget.RelativeLayout.BELOW;
18
 
19
 
19
 @SuppressLint("ViewConstructor")
20
 @SuppressLint("ViewConstructor")
20
-public class ComponentLayout extends FrameLayout implements ReactComponent, TitleBarButton.OnClickListener {
21
+public class ComponentLayout extends FrameLayout implements ReactComponent, TopBarButtonController.OnClickListener {
21
 
22
 
22
     private IReactView reactView;
23
     private IReactView reactView;
23
     private final OverlayTouchDelegate touchDelegate;
24
     private final OverlayTouchDelegate touchDelegate;

+ 5
- 7
lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java View File

9
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.parse.Options;
10
 import com.reactnativenavigation.presentation.OptionsPresenter;
10
 import com.reactnativenavigation.presentation.OptionsPresenter;
11
 import com.reactnativenavigation.utils.CompatUtils;
11
 import com.reactnativenavigation.utils.CompatUtils;
12
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
13
+import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
12
 
14
 
13
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
15
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
14
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
16
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
17
 public class StackLayout extends RelativeLayout {
19
 public class StackLayout extends RelativeLayout {
18
     private TopBar topBar;
20
     private TopBar topBar;
19
 
21
 
20
-    public StackLayout(Context context, TitleBarButton.OnClickListener topBarButtonClickListener) {
22
+    public StackLayout(Context context, ReactViewCreator topBarButtonCreator, TopBarButtonController.OnClickListener topBarButtonClickListener) {
21
         super(context);
23
         super(context);
22
-        topBar = new TopBar(context, topBarButtonClickListener, this);
24
+        topBar = new TopBar(context, topBarButtonCreator, topBarButtonClickListener, this);
23
         topBar.setId(CompatUtils.generateViewId());
25
         topBar.setId(CompatUtils.generateViewId());
24
-        createLayout();
25
-        setContentDescription("StackLayout");
26
-    }
27
-
28
-    void createLayout() {
29
         addView(topBar, MATCH_PARENT, WRAP_CONTENT);
26
         addView(topBar, MATCH_PARENT, WRAP_CONTENT);
27
+        setContentDescription("StackLayout");
30
     }
28
     }
31
 
29
 
32
     public void applyOptions(Options options) {
30
     public void applyOptions(Options options) {

+ 140
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/TitleBar.java View File

1
+package com.reactnativenavigation.views;
2
+
3
+import android.annotation.SuppressLint;
4
+import android.app.Activity;
5
+import android.content.Context;
6
+import android.graphics.Typeface;
7
+import android.support.annotation.Nullable;
8
+import android.support.v7.widget.Toolbar;
9
+import android.util.Log;
10
+import android.view.View;
11
+import android.view.ViewGroup;
12
+import android.widget.TextView;
13
+
14
+import com.reactnativenavigation.parse.params.Button;
15
+import com.reactnativenavigation.parse.params.Color;
16
+import com.reactnativenavigation.parse.params.Fraction;
17
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
18
+import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
19
+
20
+import java.util.ArrayList;
21
+import java.util.List;
22
+
23
+@SuppressLint("ViewConstructor")
24
+public class TitleBar extends Toolbar {
25
+    private final ReactViewCreator buttonCreator;
26
+    private final TopBarButtonController.OnClickListener onClickListener;
27
+    private final List<TopBarButtonController> rightButtonControllers = new ArrayList<>();
28
+    private TopBarButtonController leftButtonController;
29
+
30
+    public TitleBar(Context context, ReactViewCreator buttonCreator, TopBarButtonController.OnClickListener onClickListener) {
31
+        super(context);
32
+        this.buttonCreator = buttonCreator;
33
+        this.onClickListener = onClickListener;
34
+        getMenu();
35
+        setContentDescription("titleBar");
36
+    }
37
+
38
+    public String getTitle() {
39
+        return super.getTitle() == null ? "" : (String) super.getTitle();
40
+    }
41
+
42
+    void setTitleTextColor(Color color) {
43
+        if (color.hasValue()) setTitleTextColor(color.get());
44
+    }
45
+
46
+    void setBackgroundColor(Color color) {
47
+        if (color.hasValue()) setBackgroundColor(color.get());
48
+    }
49
+
50
+    void setTitleFontSize(Fraction size) {
51
+        TextView titleTextView = getTitleTextView();
52
+        if (titleTextView != null && size.hasValue()) {
53
+            titleTextView.setTextSize(size.get());
54
+        }
55
+    }
56
+
57
+    void setTitleTypeface(Typeface typeface) {
58
+        TextView titleTextView = getTitleTextView();
59
+        if (titleTextView != null) {
60
+            titleTextView.setTypeface(typeface);
61
+        }
62
+    }
63
+
64
+    TextView getTitleTextView() {
65
+        return findTextView(this);
66
+    }
67
+
68
+    public void clear() {
69
+        setTitle(null);
70
+        clearRightButtons();
71
+        clearLeftButton();
72
+    }
73
+
74
+    private void clearLeftButton() {
75
+        setNavigationIcon(null);
76
+        if (leftButtonController != null) {
77
+            leftButtonController.destroy();
78
+            leftButtonController = null;
79
+        }
80
+    }
81
+
82
+    private void clearRightButtons() {
83
+        for (TopBarButtonController button : rightButtonControllers) {
84
+            button.destroy();
85
+        }
86
+        rightButtonControllers.clear();
87
+        getMenu().clear();
88
+    }
89
+
90
+    public void setButtons(List<Button> leftButtons, List<Button> rightButtons) {
91
+        setLeftButtons(leftButtons);
92
+        setRightButtons(rightButtons);
93
+    }
94
+
95
+    private void setLeftButtons(List<Button> leftButtons) {
96
+        if (leftButtons == null) return;
97
+        if (leftButtons.isEmpty()) {
98
+            clearLeftButton();
99
+            return;
100
+        }
101
+        if (leftButtons.size() > 1) {
102
+            Log.w("RNN", "Use a custom TopBar to have more than one left button");
103
+        }
104
+        setLeftButton(leftButtons.get(0));
105
+    }
106
+
107
+    private void setLeftButton(final Button button) {
108
+        TopBarButtonController controller = createButtonController(button);
109
+        leftButtonController = controller;
110
+        controller.applyNavigationIcon(this);
111
+    }
112
+
113
+    public void setRightButtons(List<Button> rightButtons) {
114
+        if (rightButtons == null) return;
115
+        clearRightButtons();
116
+        for (Button button : rightButtons) {
117
+            TopBarButtonController controller = createButtonController(button);
118
+            rightButtonControllers.add(controller);
119
+            controller.addToMenu(this);
120
+        }
121
+    }
122
+
123
+    public TopBarButtonController createButtonController(Button button) {
124
+        return new TopBarButtonController((Activity) getContext(), button, buttonCreator, onClickListener);
125
+    }
126
+
127
+    @Nullable
128
+    private TextView findTextView(ViewGroup root) {
129
+        for (int i = 0; i < root.getChildCount(); i++) {
130
+            View view = root.getChildAt(i);
131
+            if (view instanceof ViewGroup) {
132
+                view = findTextView((ViewGroup) view);
133
+            }
134
+            if (view instanceof TextView) {
135
+                return (TextView) view;
136
+            }
137
+        }
138
+        return null;
139
+    }
140
+}

+ 0
- 176
lib/android/app/src/main/java/com/reactnativenavigation/views/TitleBarButton.java View File

1
-package com.reactnativenavigation.views;
2
-
3
-import android.content.Context;
4
-import android.graphics.Color;
5
-import android.graphics.drawable.Drawable;
6
-import android.support.annotation.NonNull;
7
-import android.support.v7.widget.ActionMenuView;
8
-import android.support.v7.widget.Toolbar;
9
-import android.text.Spannable;
10
-import android.text.SpannableString;
11
-import android.text.style.AbsoluteSizeSpan;
12
-import android.util.Log;
13
-import android.view.Menu;
14
-import android.view.MenuItem;
15
-import android.view.View;
16
-import android.widget.ImageButton;
17
-import android.widget.TextView;
18
-
19
-import com.reactnativenavigation.parse.params.Button;
20
-import com.reactnativenavigation.parse.params.Text;
21
-import com.reactnativenavigation.utils.ArrayUtils;
22
-import com.reactnativenavigation.utils.ImageLoader;
23
-import com.reactnativenavigation.utils.UiUtils;
24
-import com.reactnativenavigation.utils.ViewUtils;
25
-
26
-import java.util.ArrayList;
27
-import java.util.List;
28
-
29
-public class TitleBarButton implements MenuItem.OnMenuItemClickListener {
30
-    public interface OnClickListener {
31
-        void onPress(String buttonId);
32
-    }
33
-
34
-	private Toolbar toolbar;
35
-	private final Button button;
36
-	private Drawable icon;
37
-    private OnClickListener onPressListener;
38
-
39
-    TitleBarButton(Toolbar toolbar, Button button, OnClickListener onPressListener) {
40
-        this.onPressListener = onPressListener;
41
-		this.toolbar = toolbar;
42
-		this.button = button;
43
-	}
44
-
45
-	void addToMenu(Context context, final Menu menu) {
46
-		MenuItem menuItem = menu.add(button.title.get(""));
47
-		menuItem.setShowAsAction(button.showAsAction);
48
-		menuItem.setEnabled(button.enabled.isTrueOrUndefined());
49
-		menuItem.setOnMenuItemClickListener(this);
50
-
51
-		if (hasIcon()) {
52
-			applyIcon(context, menuItem);
53
-		} else {
54
-			setTextColor();
55
-			setFontSize(menuItem);
56
-		}
57
-
58
-        setTestId(button.testId);
59
-    }
60
-
61
-    void applyNavigationIcon(Context context) {
62
-		if (!hasIcon()) {
63
-			Log.w("RNN", "Left button needs to have an icon");
64
-			return;
65
-		}
66
-
67
-		new ImageLoader().loadIcon(context, button.icon.get(), new ImageLoader.ImageLoadingListener() {
68
-			@Override
69
-			public void onComplete(@NonNull Drawable drawable) {
70
-				icon = drawable;
71
-                setIconColor();
72
-                setNavigationClickListener();
73
-                toolbar.setNavigationIcon(icon);
74
-                setLeftButtonTestId();
75
-            }
76
-
77
-			@Override
78
-			public void onError(Throwable error) {
79
-				error.printStackTrace();
80
-			}
81
-		});
82
-	}
83
-
84
-    private void setLeftButtonTestId() {
85
-        if (!button.testId.hasValue()) return;
86
-        toolbar.post(() -> {
87
-            ImageButton leftButton = ViewUtils.findChildByClass(toolbar, ImageButton.class);
88
-            if (leftButton != null) {
89
-                leftButton.setTag(button.testId.get());
90
-            }
91
-        });
92
-    }
93
-
94
-    private void applyIcon(Context context, final MenuItem menuItem) {
95
-        new ImageLoader().loadIcon(context, button.icon.get(), new ImageLoader.ImageLoadingListener() {
96
-			@Override
97
-			public void onComplete(@NonNull Drawable drawable) {
98
-				icon = drawable;
99
-                menuItem.setIcon(icon);
100
-                setIconColor();
101
-			}
102
-
103
-			@Override
104
-			public void onError(Throwable error) {
105
-				error.printStackTrace();
106
-			}
107
-		});
108
-	}
109
-
110
-	private void setIconColor() {
111
-		if (button.enabled.isTrueOrUndefined()) {
112
-			UiUtils.tintDrawable(icon, button.buttonColor);
113
-			return;
114
-		}
115
-		if (button.disableIconTint.isTrue()) {
116
-			UiUtils.tintDrawable(icon, button.buttonColor);
117
-		} else {
118
-			UiUtils.tintDrawable(icon, Color.LTGRAY);
119
-		}
120
-	}
121
-
122
-	private void setTextColor() {
123
-		UiUtils.runOnPreDrawOnce(this.toolbar, () -> {
124
-            ArrayList<View> outViews = findActualTextViewInMenuByText();
125
-            setTextColorForFoundButtonViews(outViews);
126
-        });
127
-	}
128
-
129
-	private void setFontSize(MenuItem menuItem) {
130
-		SpannableString spanString = new SpannableString(button.title.get());
131
-		spanString.setSpan(new AbsoluteSizeSpan(button.buttonFontSize, true), 0, button.title.get().length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
132
-		menuItem.setTitleCondensed(spanString);
133
-	}
134
-
135
-	private void setNavigationClickListener() {
136
-		toolbar.setNavigationOnClickListener(view -> onPressListener.onPress(button.id));
137
-	}
138
-
139
-	@Override
140
-	public boolean onMenuItemClick(MenuItem menuItem) {
141
-		onPressListener.onPress(button.id);
142
-		return true;
143
-	}
144
-
145
-	private void setTextColorForFoundButtonViews(ArrayList<View> buttons) {
146
-		for (View button : buttons) {
147
-			((TextView) button).setTextColor(this.button.buttonColor);
148
-		}
149
-	}
150
-
151
-	private boolean hasIcon() {
152
-		return button.icon.hasValue();
153
-	}
154
-
155
-    private void setTestId(Text testId) {
156
-        if (!testId.hasValue()) return;
157
-        UiUtils.runOnPreDrawOnce(this.toolbar, () -> {
158
-            ActionMenuView buttonsLayout = ViewUtils.findChildByClass(toolbar, ActionMenuView.class);
159
-            List<TextView> buttons = ViewUtils.findChildrenByClass(buttonsLayout, TextView.class);
160
-            for (TextView view : buttons) {
161
-                if (button.title.hasValue() && button.title.get().equals(view.getText().toString())) {
162
-                    view.setTag(testId.get());
163
-                } else if (button.icon.hasValue() && ArrayUtils.contains(view.getCompoundDrawables(), icon)) {
164
-                    view.setTag(testId.get());
165
-                }
166
-            }
167
-        });
168
-    }
169
-
170
-    @NonNull
171
-    private ArrayList<View> findActualTextViewInMenuByText() {
172
-        ArrayList<View> outViews = new ArrayList<>();
173
-        this.toolbar.findViewsWithText(outViews, button.title.get(), View.FIND_VIEWS_WITH_TEXT);
174
-        return outViews;
175
-    }
176
-}

+ 23
- 81
lib/android/app/src/main/java/com/reactnativenavigation/views/TopBar.java View File

3
 import android.annotation.SuppressLint;
3
 import android.annotation.SuppressLint;
4
 import android.content.Context;
4
 import android.content.Context;
5
 import android.graphics.Typeface;
5
 import android.graphics.Typeface;
6
-import android.support.annotation.Nullable;
6
+import android.support.annotation.RestrictTo;
7
 import android.support.annotation.VisibleForTesting;
7
 import android.support.annotation.VisibleForTesting;
8
 import android.support.design.widget.AppBarLayout;
8
 import android.support.design.widget.AppBarLayout;
9
 import android.support.v4.view.ViewPager;
9
 import android.support.v4.view.ViewPager;
10
 import android.support.v7.widget.Toolbar;
10
 import android.support.v7.widget.Toolbar;
11
-import android.util.Log;
12
-import android.view.Menu;
13
 import android.view.View;
11
 import android.view.View;
14
-import android.view.ViewGroup;
15
 import android.widget.TextView;
12
 import android.widget.TextView;
16
 
13
 
17
 import com.reactnativenavigation.anim.TopBarAnimator;
14
 import com.reactnativenavigation.anim.TopBarAnimator;
22
 import com.reactnativenavigation.parse.params.Color;
19
 import com.reactnativenavigation.parse.params.Color;
23
 import com.reactnativenavigation.parse.params.Fraction;
20
 import com.reactnativenavigation.parse.params.Fraction;
24
 import com.reactnativenavigation.parse.params.Number;
21
 import com.reactnativenavigation.parse.params.Number;
22
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
23
+import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
25
 
24
 
26
-import java.util.ArrayList;
25
+import java.util.List;
27
 
26
 
28
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
27
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
29
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
28
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
30
 
29
 
31
 @SuppressLint("ViewConstructor")
30
 @SuppressLint("ViewConstructor")
32
 public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAwareView {
31
 public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAwareView {
33
-    private final Toolbar titleBar;
34
-    private TitleBarButton.OnClickListener onClickListener;
32
+    private final TitleBar titleBar;
35
     private final TopBarCollapseBehavior collapsingBehavior;
33
     private final TopBarCollapseBehavior collapsingBehavior;
36
     private TopBarAnimator animator;
34
     private TopBarAnimator animator;
37
     private TopTabs topTabs;
35
     private TopTabs topTabs;
38
     private StackLayout parentView;
36
     private StackLayout parentView;
39
 
37
 
40
-    public TopBar(final Context context, TitleBarButton.OnClickListener onClickListener, StackLayout parentView) {
38
+    public TopBar(final Context context, ReactViewCreator buttonCreator, TopBarButtonController.OnClickListener onClickListener, StackLayout parentView) {
41
         super(context);
39
         super(context);
42
-        this.onClickListener = onClickListener;
43
         collapsingBehavior = new TopBarCollapseBehavior(this);
40
         collapsingBehavior = new TopBarCollapseBehavior(this);
44
-        titleBar = new Toolbar(context);
45
-        titleBar.getMenu();
46
         topTabs = new TopTabs(getContext());
41
         topTabs = new TopTabs(getContext());
47
-        this.animator = new TopBarAnimator(this);
42
+        animator = new TopBarAnimator(this);
48
         this.parentView = parentView;
43
         this.parentView = parentView;
44
+        titleBar = createTitleBar(context, buttonCreator, onClickListener);
49
         addView(titleBar);
45
         addView(titleBar);
50
-        titleBar.setContentDescription("titleBar");
51
         setContentDescription("TopBar");
46
         setContentDescription("TopBar");
52
     }
47
     }
53
 
48
 
49
+    protected TitleBar createTitleBar(Context context, ReactViewCreator buttonCreator, TopBarButtonController.OnClickListener onClickListener) {
50
+        return new TitleBar(context, buttonCreator, onClickListener);
51
+    }
52
+
54
     public void setTitle(String title) {
53
     public void setTitle(String title) {
55
         titleBar.setTitle(title);
54
         titleBar.setTitle(title);
56
     }
55
     }
57
 
56
 
58
     public String getTitle() {
57
     public String getTitle() {
59
-        return titleBar.getTitle() != null ? titleBar.getTitle().toString() : "";
58
+        return titleBar.getTitle();
60
     }
59
     }
61
 
60
 
62
     public void setTestId(String testId) {
61
     public void setTestId(String testId) {
64
     }
63
     }
65
 
64
 
66
     public void setTitleTextColor(Color color) {
65
     public void setTitleTextColor(Color color) {
67
-        if (color.hasValue()) titleBar.setTitleTextColor(color.get());
66
+        titleBar.setTitleTextColor(color);
68
     }
67
     }
69
 
68
 
70
     public void setTitleFontSize(Fraction size) {
69
     public void setTitleFontSize(Fraction size) {
71
-        TextView titleTextView = getTitleTextView();
72
-        if (titleTextView != null && size.hasValue()) {
73
-            titleTextView.setTextSize(size.get());
74
-        }
70
+        titleBar.setTitleFontSize(size);
75
     }
71
     }
76
 
72
 
77
     public void setTitleTypeface(Typeface typeface) {
73
     public void setTitleTypeface(Typeface typeface) {
78
-        TextView titleTextView = getTitleTextView();
79
-        if (titleTextView != null) {
80
-            titleTextView.setTypeface(typeface);
81
-        }
74
+        titleBar.setTitleTypeface(typeface);
82
     }
75
     }
83
 
76
 
84
     public void setTopTabFontFamily(int tabIndex, Typeface fontFamily) {
77
     public void setTopTabFontFamily(int tabIndex, Typeface fontFamily) {
97
         topTabs.setVisibility(this, visible);
90
         topTabs.setVisibility(this, visible);
98
     }
91
     }
99
 
92
 
100
-    public void setButtons(ArrayList<Button> leftButtons, ArrayList<Button> rightButtons) {
101
-        setLeftButtons(leftButtons);
102
-        setRightButtons(rightButtons);
93
+    public void setButtons(List<Button> leftButtons, List<Button> rightButtons) {
94
+        titleBar.setButtons(leftButtons, rightButtons);
103
     }
95
     }
104
 
96
 
97
+    @RestrictTo(RestrictTo.Scope.TESTS)
105
     public TextView getTitleTextView() {
98
     public TextView getTitleTextView() {
106
-        return findTextView(titleBar);
99
+        return titleBar.getTitleTextView();
107
     }
100
     }
108
 
101
 
109
     public void setBackgroundColor(Color color) {
102
     public void setBackgroundColor(Color color) {
110
-        if (color.hasValue()) titleBar.setBackgroundColor(color.get());
111
-    }
112
-
113
-    @Nullable
114
-    private TextView findTextView(ViewGroup root) {
115
-        for (int i = 0; i < root.getChildCount(); i++) {
116
-            View view = root.getChildAt(i);
117
-            if (view instanceof ViewGroup) {
118
-                view = findTextView((ViewGroup) view);
119
-            }
120
-            if (view instanceof TextView) {
121
-                return (TextView) view;
122
-            }
123
-        }
124
-        return null;
125
-    }
126
-
127
-    private void setLeftButtons(ArrayList<Button> leftButtons) {
128
-        if (leftButtons == null) return;
129
-        if (leftButtons.isEmpty()) {
130
-            titleBar.setNavigationIcon(null);
131
-            return;
132
-        }
133
-
134
-        if (leftButtons.size() > 1) {
135
-            Log.w("RNN", "Use a custom TopBar to have more than one left button");
136
-        }
137
-
138
-        Button leftButton = leftButtons.get(0);
139
-        setLeftButton(leftButton);
140
-    }
141
-
142
-    private void setLeftButton(final Button button) {
143
-        TitleBarButton leftBarButton = new TitleBarButton(this.titleBar, button, onClickListener);
144
-        leftBarButton.applyNavigationIcon(getContext());
145
-    }
146
-
147
-    private void setRightButtons(ArrayList<Button> rightButtons) {
148
-        if (rightButtons == null || rightButtons.size() == 0) {
149
-            return;
150
-        }
151
-
152
-        Menu menu = getTitleBar().getMenu();
153
-        menu.clear();
154
-
155
-        for (int i = 0; i < rightButtons.size(); i++) {
156
-            Button button = rightButtons.get(i);
157
-            TitleBarButton titleBarButton = new TitleBarButton(this.titleBar, button, onClickListener);
158
-            titleBarButton.addToMenu(getContext(), menu);
159
-        }
103
+        titleBar.setBackgroundColor(color);
160
     }
104
     }
161
 
105
 
162
     public Toolbar getTitleBar() {
106
     public Toolbar getTitleBar() {
210
     }
154
     }
211
 
155
 
212
     public void clear() {
156
     public void clear() {
213
-        titleBar.setTitle(null);
214
-        titleBar.setNavigationIcon(null);
215
-        titleBar.getMenu().clear();
157
+        titleBar.clear();
216
     }
158
     }
217
 
159
 
218
     public void clearTopTabs() {
160
     public void clearTopTabs() {
219
         topTabs.clear(this);
161
         topTabs.clear(this);
220
     }
162
     }
221
 
163
 
222
-    @VisibleForTesting()
164
+    @VisibleForTesting
223
     public TopTabs getTopTabs() {
165
     public TopTabs getTopTabs() {
224
         return topTabs;
166
         return topTabs;
225
     }
167
     }

+ 21
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/TopBarButtonCreator.java View File

1
+package com.reactnativenavigation.views;
2
+
3
+import android.app.Activity;
4
+
5
+import com.facebook.react.ReactInstanceManager;
6
+import com.reactnativenavigation.react.TopBarReactButtonView;
7
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
8
+
9
+public class TopBarButtonCreator implements ReactViewCreator {
10
+
11
+    private ReactInstanceManager instanceManager;
12
+
13
+    public TopBarButtonCreator(ReactInstanceManager instanceManager) {
14
+        this.instanceManager = instanceManager;
15
+	}
16
+
17
+	@Override
18
+	public TopBarReactButtonView create(Activity activity, String componentId, String componentName) {
19
+        return new TopBarReactButtonView(activity, instanceManager, componentId, componentName);
20
+    }
21
+}

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

9
 
9
 
10
 import com.reactnativenavigation.parse.Options;
10
 import com.reactnativenavigation.parse.Options;
11
 import com.reactnativenavigation.viewcontrollers.IReactView;
11
 import com.reactnativenavigation.viewcontrollers.IReactView;
12
+import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
12
 import com.reactnativenavigation.viewcontrollers.ViewController;
13
 import com.reactnativenavigation.viewcontrollers.ViewController;
13
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
14
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
14
 
15
 
18
 import static android.widget.RelativeLayout.BELOW;
19
 import static android.widget.RelativeLayout.BELOW;
19
 
20
 
20
 @SuppressLint("ViewConstructor")
21
 @SuppressLint("ViewConstructor")
21
-public class TopTabsViewPager extends ViewPager implements Component, TitleBarButton.OnClickListener {
22
+public class TopTabsViewPager extends ViewPager implements Component, TopBarButtonController.OnClickListener {
22
 
23
 
23
     private static final int OFFSCREEN_PAGE_LIMIT = 99;
24
     private static final int OFFSCREEN_PAGE_LIMIT = 99;
24
     private List<ViewController> tabs;
25
     private List<ViewController> tabs;

+ 2
- 2
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentLayout.java View File

7
 import com.reactnativenavigation.interfaces.ScrollEventListener;
7
 import com.reactnativenavigation.interfaces.ScrollEventListener;
8
 import com.reactnativenavigation.parse.Options;
8
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.viewcontrollers.IReactView;
9
 import com.reactnativenavigation.viewcontrollers.IReactView;
10
+import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
10
 import com.reactnativenavigation.views.ComponentLayout;
11
 import com.reactnativenavigation.views.ComponentLayout;
11
-import com.reactnativenavigation.views.TitleBarButton;
12
 
12
 
13
-public class TestComponentLayout extends ComponentLayout implements TitleBarButton.OnClickListener {
13
+public class TestComponentLayout extends ComponentLayout implements TopBarButtonController.OnClickListener {
14
 
14
 
15
     private IReactView reactView;
15
     private IReactView reactView;
16
 
16
 

+ 28
- 0
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TopBarButtonCreatorMock.java View File

1
+package com.reactnativenavigation.mocks;
2
+
3
+import android.app.Activity;
4
+
5
+import com.facebook.react.ReactInstanceManager;
6
+import com.reactnativenavigation.react.TopBarReactButtonView;
7
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
8
+
9
+import static org.mockito.Mockito.mock;
10
+
11
+public class TopBarButtonCreatorMock implements ReactViewCreator {
12
+
13
+    @Override
14
+    public TopBarReactButtonView create(Activity activity, String componentId, String componentName) {
15
+        final ReactInstanceManager reactInstanceManager = mock(ReactInstanceManager.class);
16
+        return new TopBarReactButtonView(activity, reactInstanceManager, componentId, componentName) {
17
+            @Override
18
+            public void sendComponentStart() {
19
+
20
+            }
21
+
22
+            @Override
23
+            public void sendComponentStop() {
24
+
25
+            }
26
+        };
27
+    }
28
+}

+ 4
- 3
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java View File

8
 import com.reactnativenavigation.mocks.ImageLoaderMock;
8
 import com.reactnativenavigation.mocks.ImageLoaderMock;
9
 import com.reactnativenavigation.mocks.MockPromise;
9
 import com.reactnativenavigation.mocks.MockPromise;
10
 import com.reactnativenavigation.mocks.SimpleViewController;
10
 import com.reactnativenavigation.mocks.SimpleViewController;
11
-import com.reactnativenavigation.parse.params.Color;
11
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
12
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.parse.Options;
13
+import com.reactnativenavigation.parse.params.Color;
13
 import com.reactnativenavigation.parse.params.Number;
14
 import com.reactnativenavigation.parse.params.Number;
14
 import com.reactnativenavigation.utils.ImageLoader;
15
 import com.reactnativenavigation.utils.ImageLoader;
15
 import com.reactnativenavigation.utils.OptionHelper;
16
 import com.reactnativenavigation.utils.OptionHelper;
99
     public void findControllerById_ReturnsSelfOrChildren() throws Exception {
100
     public void findControllerById_ReturnsSelfOrChildren() throws Exception {
100
         assertThat(uut.findControllerById("123")).isNull();
101
         assertThat(uut.findControllerById("123")).isNull();
101
         assertThat(uut.findControllerById(uut.getId())).isEqualTo(uut);
102
         assertThat(uut.findControllerById(uut.getId())).isEqualTo(uut);
102
-        StackController inner = new StackController(activity, "inner", tabOptions);
103
+        StackController inner = new StackController(activity, new TopBarButtonCreatorMock(), "inner", tabOptions);
103
         inner.animatePush(child1, new MockPromise());
104
         inner.animatePush(child1, new MockPromise());
104
         assertThat(uut.findControllerById(child1.getId())).isNull();
105
         assertThat(uut.findControllerById(child1.getId())).isNull();
105
         uut.setTabs(Collections.singletonList(inner));
106
         uut.setTabs(Collections.singletonList(inner));
130
         uut.setTabs(tabs);
131
         uut.setTabs(tabs);
131
         uut.ensureViewIsCreated();
132
         uut.ensureViewIsCreated();
132
 
133
 
133
-        StackController stack = spy(new StackController(activity, "stack", new Options()));
134
+        StackController stack = spy(new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options()));
134
         stack.ensureViewIsCreated();
135
         stack.ensureViewIsCreated();
135
         stack.push(uut, new MockPromise());
136
         stack.push(uut, new MockPromise());
136
 
137
 

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

5
 import com.reactnativenavigation.BaseTest;
5
 import com.reactnativenavigation.BaseTest;
6
 import com.reactnativenavigation.mocks.TestComponentLayout;
6
 import com.reactnativenavigation.mocks.TestComponentLayout;
7
 import com.reactnativenavigation.mocks.TestReactView;
7
 import com.reactnativenavigation.mocks.TestReactView;
8
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
8
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.views.StackLayout;
10
 import com.reactnativenavigation.views.StackLayout;
10
 
11
 
25
         super.beforeEach();
26
         super.beforeEach();
26
         Activity activity = newActivity();
27
         Activity activity = newActivity();
27
         view = spy(new TestComponentLayout(activity, new TestReactView(activity)));
28
         view = spy(new TestComponentLayout(activity, new TestReactView(activity)));
28
-        ParentController<StackLayout> parentController = new StackController(activity, "stack", new Options());
29
+        ParentController<StackLayout> parentController = new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options());
29
         uut = new ComponentViewController(activity, "componentId1", "componentName", (activity1, componentId, componentName) -> view, new Options());
30
         uut = new ComponentViewController(activity, "componentId1", "componentName", (activity1, componentId, componentName) -> view, new Options());
30
         uut.setParentController(parentController);
31
         uut.setParentController(parentController);
31
         parentController.ensureViewIsCreated();
32
         parentController.ensureViewIsCreated();

+ 4
- 4
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/FloatingActionButtonTest.java View File

3
 import android.app.Activity;
3
 import android.app.Activity;
4
 import android.support.annotation.NonNull;
4
 import android.support.annotation.NonNull;
5
 import android.view.View;
5
 import android.view.View;
6
+import android.view.ViewGroup;
6
 
7
 
7
 import com.reactnativenavigation.BaseTest;
8
 import com.reactnativenavigation.BaseTest;
8
 import com.reactnativenavigation.mocks.MockPromise;
9
 import com.reactnativenavigation.mocks.MockPromise;
9
 import com.reactnativenavigation.mocks.SimpleViewController;
10
 import com.reactnativenavigation.mocks.SimpleViewController;
11
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
10
 import com.reactnativenavigation.parse.FabOptions;
12
 import com.reactnativenavigation.parse.FabOptions;
11
 import com.reactnativenavigation.parse.Options;
13
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.parse.params.Text;
14
 import com.reactnativenavigation.parse.params.Text;
17
 import org.junit.Test;
19
 import org.junit.Test;
18
 
20
 
19
 import static org.assertj.core.api.Java6Assertions.assertThat;
21
 import static org.assertj.core.api.Java6Assertions.assertThat;
20
-import static org.mockito.Mockito.spy;
21
-import static org.mockito.Mockito.verify;
22
 
22
 
23
 public class FloatingActionButtonTest extends BaseTest {
23
 public class FloatingActionButtonTest extends BaseTest {
24
 
24
 
33
     public void beforeEach() {
33
     public void beforeEach() {
34
         super.beforeEach();
34
         super.beforeEach();
35
         activity = newActivity();
35
         activity = newActivity();
36
-        stackController = new StackController(activity, "stackController", new Options());
36
+        stackController = new StackController(activity, new TopBarButtonCreatorMock(), "stackController", new Options());
37
         Options options = getOptionsWithFab();
37
         Options options = getOptionsWithFab();
38
         childFab = new SimpleViewController(activity, "child1", options);
38
         childFab = new SimpleViewController(activity, "child1", options);
39
         childNoFab = new SimpleViewController(activity, "child2", new Options());
39
         childNoFab = new SimpleViewController(activity, "child2", new Options());
128
         for (int i = 0; i < stackLayout.getChildCount(); i++) {
128
         for (int i = 0; i < stackLayout.getChildCount(); i++) {
129
             View child = stackLayout.getChildAt(i);
129
             View child = stackLayout.getChildAt(i);
130
             if (child instanceof FabMenu) {
130
             if (child instanceof FabMenu) {
131
-                return ((FabMenu) child).getChildCount() == CHILD_FAB_COUNT + 2;
131
+                return ((ViewGroup) child).getChildCount() == CHILD_FAB_COUNT + 2;
132
             }
132
             }
133
         }
133
         }
134
         return false;
134
         return false;

+ 4
- 3
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java View File

8
 import com.reactnativenavigation.mocks.MockPromise;
8
 import com.reactnativenavigation.mocks.MockPromise;
9
 import com.reactnativenavigation.mocks.SimpleComponentViewController;
9
 import com.reactnativenavigation.mocks.SimpleComponentViewController;
10
 import com.reactnativenavigation.mocks.SimpleViewController;
10
 import com.reactnativenavigation.mocks.SimpleViewController;
11
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
11
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.parse.params.Text;
13
 import com.reactnativenavigation.parse.params.Text;
13
 import com.reactnativenavigation.utils.CompatUtils;
14
 import com.reactnativenavigation.utils.CompatUtils;
45
         imageLoaderMock = ImageLoaderMock.mock();
46
         imageLoaderMock = ImageLoaderMock.mock();
46
         activity = newActivity();
47
         activity = newActivity();
47
         uut = new Navigator(activity);
48
         uut = new Navigator(activity);
48
-        parentController = spy(new StackController(activity, "stack", new Options()));
49
+        parentController = spy(new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options()));
49
         parentController.ensureViewIsCreated();
50
         parentController.ensureViewIsCreated();
50
         child1 = new SimpleViewController(activity, "child1", tabOptions);
51
         child1 = new SimpleViewController(activity, "child1", tabOptions);
51
         child2 = new SimpleViewController(activity, "child2", tabOptions);
52
         child2 = new SimpleViewController(activity, "child2", tabOptions);
247
 
248
 
248
     @NonNull
249
     @NonNull
249
     private StackController newStack() {
250
     private StackController newStack() {
250
-        return new StackController(activity, "stack" + CompatUtils.generateViewId(), tabOptions);
251
+        return new StackController(activity, new TopBarButtonCreatorMock(), "stack" + CompatUtils.generateViewId(), tabOptions);
251
     }
252
     }
252
 
253
 
253
     @Test
254
     @Test
324
 
325
 
325
     @Test
326
     @Test
326
     public void pushedStackCanBePopped() throws Exception {
327
     public void pushedStackCanBePopped() throws Exception {
327
-        StackController parent = new StackController(activity, "someStack", new Options());
328
+        StackController parent = new StackController(activity, new TopBarButtonCreatorMock(), "someStack", new Options());
328
         parent.ensureViewIsCreated();
329
         parent.ensureViewIsCreated();
329
         uut.setRoot(parent, new MockPromise());
330
         uut.setRoot(parent, new MockPromise());
330
         parent.push(parentController, new MockPromise());
331
         parent.push(parentController, new MockPromise());

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

10
 import com.reactnativenavigation.mocks.MockPromise;
10
 import com.reactnativenavigation.mocks.MockPromise;
11
 import com.reactnativenavigation.mocks.TestComponentLayout;
11
 import com.reactnativenavigation.mocks.TestComponentLayout;
12
 import com.reactnativenavigation.mocks.TestReactView;
12
 import com.reactnativenavigation.mocks.TestReactView;
13
-import com.reactnativenavigation.parse.params.Fraction;
13
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
14
 import com.reactnativenavigation.parse.Options;
14
 import com.reactnativenavigation.parse.Options;
15
-import com.reactnativenavigation.parse.params.Text;
16
 import com.reactnativenavigation.parse.params.Bool;
15
 import com.reactnativenavigation.parse.params.Bool;
16
+import com.reactnativenavigation.parse.params.Fraction;
17
+import com.reactnativenavigation.parse.params.Text;
17
 
18
 
18
 import org.junit.Test;
19
 import org.junit.Test;
19
 
20
 
44
                 (activity1, componentId, componentName) -> view,
45
                 (activity1, componentId, componentName) -> view,
45
                 initialNavigationOptions
46
                 initialNavigationOptions
46
         );
47
         );
47
-        stackController = new StackController(activity, "stack", new Options());
48
+        stackController = new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options());
48
         stackController.ensureViewIsCreated();
49
         stackController.ensureViewIsCreated();
49
         uut.setParentController(stackController);
50
         uut.setParentController(stackController);
50
     }
51
     }
61
     @Test
62
     @Test
62
     public void initialOptionsAppliedOnAppear() throws Exception {
63
     public void initialOptionsAppliedOnAppear() throws Exception {
63
         uut.options.topBarOptions.title = new Text("the title");
64
         uut.options.topBarOptions.title = new Text("the title");
64
-        StackController stackController = new StackController(activity, "stackId", new Options());
65
+        StackController stackController = new StackController(activity, new TopBarButtonCreatorMock(), "stackId", new Options());
65
         stackController.animatePush(uut, new MockPromise() {});
66
         stackController.animatePush(uut, new MockPromise() {});
66
         assertThat(stackController.getTopBar().getTitle()).isEmpty();
67
         assertThat(stackController.getTopBar().getTitle()).isEmpty();
67
 
68
 

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

8
 import com.reactnativenavigation.BaseTest;
8
 import com.reactnativenavigation.BaseTest;
9
 import com.reactnativenavigation.mocks.MockPromise;
9
 import com.reactnativenavigation.mocks.MockPromise;
10
 import com.reactnativenavigation.mocks.SimpleViewController;
10
 import com.reactnativenavigation.mocks.SimpleViewController;
11
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
11
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.parse.params.Text;
13
 import com.reactnativenavigation.parse.params.Text;
13
 import com.reactnativenavigation.views.ReactComponent;
14
 import com.reactnativenavigation.views.ReactComponent;
87
 
88
 
88
     @Test
89
     @Test
89
     public void findControllerById_Recursive() throws Exception {
90
     public void findControllerById_Recursive() throws Exception {
90
-        StackController stackController = new StackController(activity, "stack", new Options());
91
+        StackController stackController = new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options());
91
         SimpleViewController child1 = new SimpleViewController(activity, "child1", new Options());
92
         SimpleViewController child1 = new SimpleViewController(activity, "child1", new Options());
92
         SimpleViewController child2 = new SimpleViewController(activity, "child2", new Options());
93
         SimpleViewController child2 = new SimpleViewController(activity, "child2", new Options());
93
         stackController.animatePush(child1, new MockPromise());
94
         stackController.animatePush(child1, new MockPromise());
109
 
110
 
110
     @Test
111
     @Test
111
     public void optionsAreClearedWhenChildIsAppeared() throws Exception {
112
     public void optionsAreClearedWhenChildIsAppeared() throws Exception {
112
-        StackController stackController = spy(new StackController(activity, "stack", new Options()));
113
+        StackController stackController = spy(new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options()));
113
         SimpleViewController child1 = new SimpleViewController(activity, "child1", new Options());
114
         SimpleViewController child1 = new SimpleViewController(activity, "child1", new Options());
114
         stackController.animatePush(child1, new MockPromise());
115
         stackController.animatePush(child1, new MockPromise());
115
 
116
 

+ 7
- 6
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java View File

7
 import com.reactnativenavigation.BaseTest;
7
 import com.reactnativenavigation.BaseTest;
8
 import com.reactnativenavigation.mocks.MockPromise;
8
 import com.reactnativenavigation.mocks.MockPromise;
9
 import com.reactnativenavigation.mocks.SimpleViewController;
9
 import com.reactnativenavigation.mocks.SimpleViewController;
10
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
10
 import com.reactnativenavigation.parse.Options;
11
 import com.reactnativenavigation.parse.Options;
11
 import com.reactnativenavigation.parse.params.Bool;
12
 import com.reactnativenavigation.parse.params.Bool;
12
 import com.reactnativenavigation.parse.params.Text;
13
 import com.reactnativenavigation.parse.params.Text;
38
     public void beforeEach() {
39
     public void beforeEach() {
39
         super.beforeEach();
40
         super.beforeEach();
40
         activity = newActivity();
41
         activity = newActivity();
41
-        uut = new StackController(activity, "uut", new Options());
42
+        uut = new StackController(activity, new TopBarButtonCreatorMock(), "uut", new Options());
42
         child1 = spy(new SimpleViewController(activity, "child1", new Options()));
43
         child1 = spy(new SimpleViewController(activity, "child1", new Options()));
43
         child2 = spy(new SimpleViewController(activity, "child2", new Options()));
44
         child2 = spy(new SimpleViewController(activity, "child2", new Options()));
44
         child3 = spy(new SimpleViewController(activity, "child3", new Options()));
45
         child3 = spy(new SimpleViewController(activity, "child3", new Options()));
94
     @Test
95
     @Test
95
     public void pop_layoutHandlesChildWillDisappear() throws Exception {
96
     public void pop_layoutHandlesChildWillDisappear() throws Exception {
96
         final StackLayout[] stackLayout = new StackLayout[1];
97
         final StackLayout[] stackLayout = new StackLayout[1];
97
-        uut = new StackController(activity, "uut", new Options()) {
98
+        uut = new StackController(activity, new TopBarButtonCreatorMock(), "uut", new Options()) {
98
             @NonNull
99
             @NonNull
99
             @Override
100
             @Override
100
             protected StackLayout createView() {
101
             protected StackLayout createView() {
133
         uut.animatePush(child1, new MockPromise());
134
         uut.animatePush(child1, new MockPromise());
134
         assertThat(child1.getParentController()).isEqualTo(uut);
135
         assertThat(child1.getParentController()).isEqualTo(uut);
135
 
136
 
136
-        StackController anotherNavController = new StackController(activity, "another", new Options());
137
+        StackController anotherNavController = new StackController(activity, new TopBarButtonCreatorMock(), "another", new Options());
137
         anotherNavController.animatePush(child2, new MockPromise());
138
         anotherNavController.animatePush(child2, new MockPromise());
138
         assertThat(child2.getParentController()).isEqualTo(anotherNavController);
139
         assertThat(child2.getParentController()).isEqualTo(anotherNavController);
139
     }
140
     }
313
 
314
 
314
     @Test
315
     @Test
315
     public void findControllerById_Deeply() throws Exception {
316
     public void findControllerById_Deeply() throws Exception {
316
-        StackController stack = new StackController(activity, "stack2", new Options());
317
+        StackController stack = new StackController(activity, new TopBarButtonCreatorMock(), "stack2", new Options());
317
         stack.animatePush(child2, new MockPromise());
318
         stack.animatePush(child2, new MockPromise());
318
         uut.animatePush(stack, new MockPromise());
319
         uut.animatePush(stack, new MockPromise());
319
         assertThat(uut.findControllerById(child2.getId())).isEqualTo(child2);
320
         assertThat(uut.findControllerById(child2.getId())).isEqualTo(child2);
408
 
409
 
409
     @Test
410
     @Test
410
     public void stackCanBePushed() throws Exception {
411
     public void stackCanBePushed() throws Exception {
411
-        StackController parent = new StackController(activity, "someStack", new Options());
412
+        StackController parent = new StackController(activity, new TopBarButtonCreatorMock(), "someStack", new Options());
412
         parent.ensureViewIsCreated();
413
         parent.ensureViewIsCreated();
413
         parent.push(uut, new MockPromise());
414
         parent.push(uut, new MockPromise());
414
         uut.onViewAppeared();
415
         uut.onViewAppeared();
417
 
418
 
418
     @Test
419
     @Test
419
     public void applyOptions_applyOnlyOnFirstStack() throws Exception {
420
     public void applyOptions_applyOnlyOnFirstStack() throws Exception {
420
-        StackController parent = spy(new StackController(activity, "someStack", new Options()));
421
+        StackController parent = spy(new StackController(activity, new TopBarButtonCreatorMock(), "someStack", new Options()));
421
         parent.ensureViewIsCreated();
422
         parent.ensureViewIsCreated();
422
         parent.push(uut, new MockPromise());
423
         parent.push(uut, new MockPromise());
423
 
424
 

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

1
+package com.reactnativenavigation.viewcontrollers;
2
+
3
+import android.app.Activity;
4
+
5
+import com.reactnativenavigation.BaseTest;
6
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
7
+import com.reactnativenavigation.parse.params.Button;
8
+import com.reactnativenavigation.parse.params.Text;
9
+import com.reactnativenavigation.react.ReactView;
10
+import com.reactnativenavigation.views.TitleBar;
11
+
12
+import org.junit.Test;
13
+
14
+import java.util.ArrayList;
15
+import java.util.Arrays;
16
+import java.util.Collections;
17
+import java.util.HashMap;
18
+import java.util.List;
19
+import java.util.Map;
20
+
21
+import static org.assertj.core.api.Java6Assertions.assertThat;
22
+import static org.mockito.Mockito.spy;
23
+import static org.mockito.Mockito.times;
24
+import static org.mockito.Mockito.verify;
25
+
26
+public class TitleBarTest extends BaseTest {
27
+
28
+    private TitleBar uut;
29
+    private Button leftButton;
30
+    private Button textButton;
31
+    private Button customButton;
32
+    private Map<String, TopBarButtonController> buttonControllers;
33
+
34
+    @Override
35
+    public void beforeEach() {
36
+        final TopBarButtonCreatorMock buttonCreator = new TopBarButtonCreatorMock();
37
+        final Activity activity = newActivity();
38
+        createButtons();
39
+        buttonControllers = new HashMap<>();
40
+        uut = spy(new TitleBar(activity, buttonCreator, (buttonId -> {})) {
41
+            @Override
42
+            public TopBarButtonController createButtonController(Button button) {
43
+                TopBarButtonController controller = spy(super.createButtonController(button));
44
+                buttonControllers.put(button.id, controller);
45
+                return controller;
46
+            }
47
+        });
48
+    }
49
+
50
+    private void createButtons() {
51
+        leftButton = new Button();
52
+        leftButton.id = "back";
53
+        leftButton.title = new Text("jfjf");
54
+
55
+        textButton = new Button();
56
+        textButton.id = "textButton";
57
+        textButton.title = new Text("Btn");
58
+
59
+        customButton = new Button();
60
+        customButton.id = "customBtn";
61
+        customButton.component = new Text("com.rnn.customBtn");
62
+    }
63
+
64
+    @Test
65
+    public void setButton_setsTextButton() {
66
+        uut.setButtons(leftButton(leftButton), rightButtons(textButton));
67
+        assertThat(uut.getMenu().getItem(0).getTitle()).isEqualTo(textButton.title.get());
68
+    }
69
+
70
+    @Test
71
+    public void setButton_setsCustomButton() {
72
+        uut.setButtons(leftButton(leftButton), rightButtons(customButton));
73
+        ReactView btnView = (ReactView) uut.getMenu().getItem(0).getActionView();
74
+        assertThat(btnView.getComponentName()).isEqualTo(customButton.component.get());
75
+    }
76
+
77
+    @Test
78
+    public void destroy_destroysButtonControllers() throws Exception {
79
+        uut.setButtons(leftButton(leftButton), rightButtons(customButton, textButton));
80
+        uut.clear();
81
+        for (TopBarButtonController controller : buttonControllers.values()) {
82
+            verify(controller, times(1)).destroy();
83
+        }
84
+    }
85
+
86
+    @Test
87
+    public void setRightButtons_destroysRightButtons() throws Exception {
88
+        uut.setButtons(leftButton(leftButton), rightButtons(customButton));
89
+        uut.setButtons(leftButton(leftButton), rightButtons(textButton));
90
+        verify(buttonControllers.get(customButton.id), times(1)).destroy();
91
+    }
92
+
93
+    @Test
94
+    public void setRightButtons_onlyDestroysRightButtons() throws Exception {
95
+        uut.setButtons(leftButton(leftButton), rightButtons(customButton));
96
+        uut.setButtons(null, rightButtons(textButton));
97
+        verify(buttonControllers.get(leftButton.id), times(0)).destroy();
98
+    }
99
+
100
+    @Test
101
+    public void setRightButtons_emptyButtonsListClearsRightButtons() throws Exception {
102
+        uut.setButtons(new ArrayList<>(), rightButtons(customButton, textButton));
103
+        uut.setButtons(new ArrayList<>(), new ArrayList<>());
104
+        for (TopBarButtonController controller : buttonControllers.values()) {
105
+            verify(controller, times(1)).destroy();
106
+        }
107
+        assertThat(uut.getMenu().size()).isEqualTo(0);
108
+    }
109
+
110
+    @Test
111
+    public void setLeftButtons_emptyButtonsListClearsLeftButton() throws Exception {
112
+        uut.setButtons(leftButton(leftButton), rightButtons(customButton));
113
+        uut.setButtons(new ArrayList<>(), rightButtons(textButton));
114
+        verify(buttonControllers.get(leftButton.id), times(1)).destroy();
115
+    }
116
+
117
+    private List<Button> leftButton(Button leftButton) {
118
+        return Collections.singletonList(leftButton);
119
+    }
120
+
121
+    private List<Button> rightButtons(Button... buttons) {
122
+        return Arrays.asList(buttons);
123
+    }
124
+}

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

1
+package com.reactnativenavigation.viewcontrollers;
2
+
3
+import android.app.Activity;
4
+import android.support.annotation.NonNull;
5
+
6
+import com.reactnativenavigation.BaseTest;
7
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
8
+import com.reactnativenavigation.parse.Options;
9
+import com.reactnativenavigation.parse.params.Button;
10
+import com.reactnativenavigation.parse.params.Text;
11
+
12
+import org.junit.Ignore;
13
+import org.junit.Test;
14
+
15
+import static org.mockito.Mockito.spy;
16
+import static org.mockito.Mockito.times;
17
+import static org.mockito.Mockito.verify;
18
+
19
+public class TopBarButtonControllerTest extends BaseTest {
20
+
21
+    private TopBarButtonController uut;
22
+    private StackController stackController;
23
+
24
+    @Override
25
+    public void beforeEach() {
26
+        Button button = createButton();
27
+        final Activity activity = newActivity();
28
+
29
+        TopBarButtonCreatorMock buttonCreatorMock = new TopBarButtonCreatorMock();
30
+        uut = spy(new TopBarButtonController(activity, button, buttonCreatorMock, (buttonId) -> {}));
31
+        stackController = spy(new StackController(activity, buttonCreatorMock, "stack", new Options()));
32
+
33
+    }
34
+
35
+    @Test
36
+    public void buttonDoesNotClearStackOptionsOnAppear() throws Exception {
37
+        uut.ensureViewIsCreated();
38
+        uut.onViewAppeared();
39
+        verify(stackController, times(0)).clearOptions();
40
+    }
41
+
42
+    @Test @Ignore
43
+    public void destroy_buttonIsDestroyedWhenStackIsDestroyed() throws Exception {
44
+        uut.ensureViewIsCreated();
45
+        uut.onViewAppeared();
46
+        stackController.destroy();
47
+        verify(uut, times(1)).destroy();
48
+    }
49
+
50
+    @NonNull
51
+    private Button createButton() {
52
+        Button button = new Button();
53
+        button.id = "btnId";
54
+        button.component = new Text("com.example.customBtn");
55
+        return button;
56
+    }
57
+}

+ 3
- 2
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java View File

8
 import com.reactnativenavigation.mocks.MockPromise;
8
 import com.reactnativenavigation.mocks.MockPromise;
9
 import com.reactnativenavigation.mocks.TestComponentViewCreator;
9
 import com.reactnativenavigation.mocks.TestComponentViewCreator;
10
 import com.reactnativenavigation.mocks.TestReactView;
10
 import com.reactnativenavigation.mocks.TestReactView;
11
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
11
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.parse.params.Text;
13
 import com.reactnativenavigation.parse.params.Text;
13
 import com.reactnativenavigation.utils.ViewHelper;
14
 import com.reactnativenavigation.utils.ViewHelper;
59
         uut = spy(new TopTabsController(activity, "componentId", tabControllers, layoutCreator, options));
60
         uut = spy(new TopTabsController(activity, "componentId", tabControllers, layoutCreator, options));
60
         tabControllers.forEach(viewController -> viewController.setParentController(uut));
61
         tabControllers.forEach(viewController -> viewController.setParentController(uut));
61
 
62
 
62
-        parentController = spy(new StackController(activity, "stackId", new Options()));
63
+        parentController = spy(new StackController(activity, new TopBarButtonCreatorMock(), "stackId", new Options()));
63
         parentController.push(uut, new MockPromise());
64
         parentController.push(uut, new MockPromise());
64
         uut.setParentController(parentController);
65
         uut.setParentController(parentController);
65
     }
66
     }
214
     public void applyOptions_tabsAreRemovedAfterViewDisappears() throws Exception {
215
     public void applyOptions_tabsAreRemovedAfterViewDisappears() throws Exception {
215
         parentController.getView().removeAllViews();
216
         parentController.getView().removeAllViews();
216
 
217
 
217
-        StackController stackController = spy(new StackController(activity, "stack", new Options()));
218
+        StackController stackController = spy(new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options()));
218
         ComponentViewController first = new ComponentViewController(
219
         ComponentViewController first = new ComponentViewController(
219
                 activity,
220
                 activity,
220
                 "firstScreen",
221
                 "firstScreen",

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

9
 import com.reactnativenavigation.BaseTest;
9
 import com.reactnativenavigation.BaseTest;
10
 import com.reactnativenavigation.mocks.MockPromise;
10
 import com.reactnativenavigation.mocks.MockPromise;
11
 import com.reactnativenavigation.mocks.SimpleViewController;
11
 import com.reactnativenavigation.mocks.SimpleViewController;
12
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
12
 import com.reactnativenavigation.parse.Options;
13
 import com.reactnativenavigation.parse.Options;
13
 
14
 
14
 import org.assertj.android.api.Assertions;
15
 import org.assertj.android.api.Assertions;
65
     @Test
66
     @Test
66
     public void holdsAReferenceToStackControllerOrNull() throws Exception {
67
     public void holdsAReferenceToStackControllerOrNull() throws Exception {
67
         assertThat(uut.getParentController()).isNull();
68
         assertThat(uut.getParentController()).isNull();
68
-        StackController nav = new StackController(activity, "stack", new Options());
69
+        StackController nav = new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options());
69
         nav.animatePush(uut, new MockPromise());
70
         nav.animatePush(uut, new MockPromise());
70
         assertThat(uut.getParentController()).isEqualTo(nav);
71
         assertThat(uut.getParentController()).isEqualTo(nav);
71
     }
72
     }

+ 8
- 6
lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarTest.java View File

5
 
5
 
6
 import com.reactnativenavigation.BaseTest;
6
 import com.reactnativenavigation.BaseTest;
7
 import com.reactnativenavigation.anim.TopBarAnimator;
7
 import com.reactnativenavigation.anim.TopBarAnimator;
8
-import com.reactnativenavigation.parse.params.Button;
9
-import com.reactnativenavigation.parse.params.Text;
8
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
10
 import com.reactnativenavigation.parse.params.Bool;
9
 import com.reactnativenavigation.parse.params.Bool;
10
+import com.reactnativenavigation.parse.params.Button;
11
 import com.reactnativenavigation.parse.params.NullBool;
11
 import com.reactnativenavigation.parse.params.NullBool;
12
+import com.reactnativenavigation.parse.params.Text;
12
 import com.reactnativenavigation.utils.TitleBarHelper;
13
 import com.reactnativenavigation.utils.TitleBarHelper;
14
+import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
13
 
15
 
14
 import org.junit.Test;
16
 import org.junit.Test;
15
 
17
 
26
     private TopBarAnimator animator;
28
     private TopBarAnimator animator;
27
     private ArrayList<Button> leftButton;
29
     private ArrayList<Button> leftButton;
28
     private ArrayList<Button> rightButtons;
30
     private ArrayList<Button> rightButtons;
29
-    private TitleBarButton.OnClickListener onClickListener;
31
+    private TopBarButtonController.OnClickListener onClickListener;
30
 
32
 
31
     @Override
33
     @Override
32
     public void beforeEach() {
34
     public void beforeEach() {
33
-        onClickListener = spy(new TitleBarButton.OnClickListener() {
35
+        onClickListener = spy(new TopBarButtonController.OnClickListener() {
34
             @Override
36
             @Override
35
             public void onPress(String buttonId) {
37
             public void onPress(String buttonId) {
36
                 Log.i("TopBarTest", "onPress: " + buttonId);
38
                 Log.i("TopBarTest", "onPress: " + buttonId);
37
             }
39
             }
38
         });
40
         });
39
-        StackLayout parent = new StackLayout(newActivity(), this.onClickListener);
40
-        uut = new TopBar(newActivity(), this.onClickListener, parent);
41
+        StackLayout parent = new StackLayout(newActivity(), new TopBarButtonCreatorMock(), this.onClickListener);
42
+        uut = new TopBar(newActivity(), new TopBarButtonCreatorMock(), this.onClickListener, parent);
41
         animator = spy(new TopBarAnimator(uut));
43
         animator = spy(new TopBarAnimator(uut));
42
         uut.setAnimator(animator);
44
         uut.setAnimator(animator);
43
         leftButton = createLeftButton();
45
         leftButton = createLeftButton();

+ 55
- 0
playground/src/screens/CustomRoundedButton.js View File

1
+const React = require('react');
2
+const { Component } = require('react');
3
+const {
4
+  StyleSheet,
5
+  View,
6
+  TouchableOpacity,
7
+  Text,
8
+  Alert,
9
+  Platform
10
+} = require('react-native');
11
+
12
+class CustomRoundedButton extends Component {
13
+
14
+  constructor(props) {
15
+    super(props);
16
+    this.state = {};
17
+  }
18
+
19
+  render() {
20
+    return (
21
+      <View style={styles.container} key={'guyguy'}>
22
+        <View style={styles.button}>
23
+          <TouchableOpacity onPress={() => Alert.alert(this.props.title, 'Thanks for that :)')}>
24
+            <Text style={styles.text}>Hi:)</Text>
25
+          </TouchableOpacity>
26
+        </View>
27
+      </View>
28
+    );
29
+  }
30
+}
31
+
32
+module.exports = CustomRoundedButton;
33
+
34
+const styles = StyleSheet.create({
35
+  container: {
36
+    flex: 1,
37
+    width: 48,
38
+    height: 48,
39
+    backgroundColor: 'transparent',
40
+    flexDirection: 'column',
41
+    justifyContent: 'center',
42
+    alignItems: 'center'
43
+  },
44
+  button: {
45
+    width: 40,
46
+    height: 40,
47
+    borderRadius: 20,
48
+    backgroundColor: 'red',
49
+    justifyContent: 'center',
50
+  },
51
+  text: {
52
+    color: 'black',
53
+    alignSelf: 'center'
54
+  }
55
+});

+ 46
- 0
playground/src/screens/CustomTextButton.js View File

1
+const React = require('react');
2
+const { Component } = require('react');
3
+const {
4
+  StyleSheet,
5
+  View,
6
+  TouchableOpacity,
7
+  Text,
8
+  Alert,
9
+  Platform
10
+} = require('react-native');
11
+
12
+class CustomTextButton extends Component {
13
+
14
+  constructor(props) {
15
+    super(props);
16
+    this.state = {};
17
+  }
18
+
19
+  render() {
20
+    return (
21
+      <View style={styles.container}>
22
+        <TouchableOpacity stye={styles.button} onPress={() => Alert.alert(this.props.title, 'Thanks for that :)')}>
23
+          <Text style={styles.text}>Press Me</Text>
24
+        </TouchableOpacity>
25
+      </View>
26
+    );
27
+  }
28
+}
29
+
30
+module.exports = CustomTextButton;
31
+
32
+const styles = StyleSheet.create({
33
+  container: {
34
+    flex: 1,
35
+    width: 60,
36
+    flexDirection: 'column',
37
+    justifyContent: 'center',
38
+  },
39
+  button: {
40
+    flex: 1,
41
+    flexDirection: 'column',
42
+  },
43
+  text: {
44
+    color: 'black',
45
+  }
46
+});

+ 21
- 7
playground/src/screens/OptionsScreen.js View File

8
 
8
 
9
 const BUTTON_ONE = 'buttonOne';
9
 const BUTTON_ONE = 'buttonOne';
10
 const BUTTON_TWO = 'buttonTwo';
10
 const BUTTON_TWO = 'buttonTwo';
11
+const CUSTOM_BUTTON = 'customButton';
12
+const CUSTOM_BUTTON2 = 'customButton2';
11
 const BUTTON_LEFT = 'buttonLeft';
13
 const BUTTON_LEFT = 'buttonLeft';
12
 const FAB = 'fab';
14
 const FAB = 'fab';
13
 
15
 
26
         textFontSize: 16,
28
         textFontSize: 16,
27
         textFontFamily: 'HelveticaNeue-Italic',
29
         textFontFamily: 'HelveticaNeue-Italic',
28
         testID: testIDs.TOP_BAR_ELEMENT,
30
         testID: testIDs.TOP_BAR_ELEMENT,
29
-        rightButtons: [{
30
-          id: BUTTON_ONE,
31
-          testID: BUTTON_ONE,
32
-          title: 'One',
33
-          buttonFontSize: 28,
34
-          buttonColor: 'red'
35
-        }],
31
+        rightButtons: [
32
+          {
33
+            id: CUSTOM_BUTTON,
34
+            testID: CUSTOM_BUTTON,
35
+            component: 'CustomTextButton'
36
+          },
37
+          {
38
+            id: CUSTOM_BUTTON2,
39
+            testID: CUSTOM_BUTTON2,
40
+            component: 'CustomRoundedButton'
41
+          },
42
+          {
43
+            id: BUTTON_ONE,
44
+            testID: BUTTON_ONE,
45
+            title: 'One',
46
+            buttonFontSize: 28,
47
+            buttonColor: 'red'
48
+          }
49
+        ],
36
         leftButtons: [{
50
         leftButtons: [{
37
           id: BUTTON_LEFT,
51
           id: BUTTON_LEFT,
38
           testID: BUTTON_LEFT,
52
           testID: BUTTON_LEFT,

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

19
 const CustomTopBar = require('./CustomTopBar');
19
 const CustomTopBar = require('./CustomTopBar');
20
 const Alert = require('./Alert');
20
 const Alert = require('./Alert');
21
 const BackHandlerModalScreen = require('./BackHandlerModalScreen');
21
 const BackHandlerModalScreen = require('./BackHandlerModalScreen');
22
+const CustomTextButton = require('./CustomTextButton');
23
+const CustomRoundedButton = require('./CustomRoundedButton');
22
 
24
 
23
 function registerScreens() {
25
 function registerScreens() {
24
   Navigation.registerComponent(`navigation.playground.CustomTransitionDestination`, () => CustomTransitionDestination);
26
   Navigation.registerComponent(`navigation.playground.CustomTransitionDestination`, () => CustomTransitionDestination);
41
   Navigation.registerComponent('navigation.playground.CustomTopBar', () => CustomTopBar);
43
   Navigation.registerComponent('navigation.playground.CustomTopBar', () => CustomTopBar);
42
   Navigation.registerComponent('navigation.playground.alert', () => Alert);
44
   Navigation.registerComponent('navigation.playground.alert', () => Alert);
43
   Navigation.registerComponent('navigation.playground.BackHandlerModalScreen', () => BackHandlerModalScreen);
45
   Navigation.registerComponent('navigation.playground.BackHandlerModalScreen', () => BackHandlerModalScreen);
46
+  Navigation.registerComponent('CustomTextButton', () => CustomTextButton);
47
+  Navigation.registerComponent('CustomRoundedButton', () => CustomRoundedButton);
44
 }
48
 }
45
 
49
 
46
 module.exports = {
50
 module.exports = {