Browse Source

Add back button to pushed screens

Guy Carmeli 6 years ago
parent
commit
3c1933b76b
17 changed files with 278 additions and 43 deletions
  1. 5
    0
      lib/android/app/src/main/java/com/reactnativenavigation/react/Constants.java
  2. 9
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java
  3. 9
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoader.java
  4. 5
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoadingListenerAdapter.java
  5. 34
    5
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java
  6. 12
    21
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TopBarButtonController.java
  7. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java
  8. 44
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/button/NavigationIconResolver.java
  9. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/views/Fab.java
  10. 10
    1
      lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBar.java
  11. 9
    0
      lib/android/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml
  12. 8
    1
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/ImageLoaderMock.java
  13. 3
    0
      lib/android/app/src/test/java/com/reactnativenavigation/utils/OptionHelper.java
  14. 47
    9
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java
  15. 2
    3
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TitleBarTest.java
  16. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarButtonControllerTest.java
  17. 75
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/button/NavigationIconResolverTest.java

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

1
+package com.reactnativenavigation.react;
2
+
3
+public class Constants {
4
+    public static final String BACK_BUTTON_ID = "RNN.back";
5
+}

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

1
+package com.reactnativenavigation.utils;
2
+
3
+import java.util.Collection;
4
+
5
+public class CollectionUtils {
6
+    public static boolean isNullOrEmpty(Collection collection) {
7
+        return collection == null || collection.isEmpty();
8
+    }
9
+}

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

26
     public interface ImagesLoadingListener {
26
     public interface ImagesLoadingListener {
27
         void onComplete(@NonNull List<Drawable> drawable);
27
         void onComplete(@NonNull List<Drawable> drawable);
28
 
28
 
29
+        void onComplete(@NonNull Drawable drawable);
30
+
29
         void onError(Throwable error);
31
         void onError(Throwable error);
30
     }
32
     }
31
 
33
 
32
     private static final String FILE_SCHEME = "file";
34
     private static final String FILE_SCHEME = "file";
33
 
35
 
36
+    public void loadIcon(Context context, String uri, ImagesLoadingListener listener) {
37
+        try {
38
+            listener.onComplete(getDrawable(context, uri));
39
+        } catch (IOException e) {
40
+            listener.onError(e);
41
+        }
42
+    }
34
 
43
 
35
     public void loadIcons(final Context context, List<String> uris, ImagesLoadingListener listener) {
44
     public void loadIcons(final Context context, List<String> uris, ImagesLoadingListener listener) {
36
         try {
45
         try {

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

11
 
11
 
12
     }
12
     }
13
 
13
 
14
+    @Override
15
+    public void onComplete(@NonNull Drawable drawable) {
16
+
17
+    }
18
+
14
     @Override
19
     @Override
15
     public void onError(Throwable error) {
20
     public void onError(Throwable error) {
16
         error.printStackTrace();
21
         error.printStackTrace();

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

7
 
7
 
8
 import com.reactnativenavigation.anim.NavigationAnimator;
8
 import com.reactnativenavigation.anim.NavigationAnimator;
9
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.parse.Options;
10
+import com.reactnativenavigation.parse.params.Button;
11
+import com.reactnativenavigation.react.Constants;
10
 import com.reactnativenavigation.utils.CommandListener;
12
 import com.reactnativenavigation.utils.CommandListener;
11
 import com.reactnativenavigation.utils.CommandListenerAdapter;
13
 import com.reactnativenavigation.utils.CommandListenerAdapter;
12
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
14
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
17
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
19
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
18
 import com.reactnativenavigation.views.topbar.TopBar;
20
 import com.reactnativenavigation.views.topbar.TopBar;
19
 
21
 
22
+import java.util.ArrayList;
20
 import java.util.Collection;
23
 import java.util.Collection;
24
+import java.util.Collections;
21
 import java.util.Iterator;
25
 import java.util.Iterator;
22
 
26
 
23
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
27
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
88
         final ViewController toRemove = stack.peek();
92
         final ViewController toRemove = stack.peek();
89
         child.setParentController(this);
93
         child.setParentController(this);
90
         stack.push(child.getId(), child);
94
         stack.push(child.getId(), child);
95
+        addBackButton(child);
91
         getView().addView(child.getView(), MATCH_PARENT, MATCH_PARENT);
96
         getView().addView(child.getView(), MATCH_PARENT, MATCH_PARENT);
92
 
97
 
93
         if (toRemove != null) {
98
         if (toRemove != null) {
105
         }
110
         }
106
     }
111
     }
107
 
112
 
113
+    private void addBackButton(ViewController child) {
114
+        if (size() <= 1 || child.options.topBar.leftButtons != null) return;
115
+        Options options = new Options();
116
+        Button back = new Button();
117
+        back.id = Constants.BACK_BUTTON_ID;
118
+        options.topBar.leftButtons = new ArrayList<>(Collections.singleton(back));
119
+        child.mergeOptions(options);
120
+    }
121
+
108
     public void setRoot(ViewController child, CommandListener listener) {
122
     public void setRoot(ViewController child, CommandListener listener) {
109
         push(child, new CommandListenerAdapter() {
123
         push(child, new CommandListenerAdapter() {
110
             @Override
124
             @Override
227
         return stack.size() > 1;
241
         return stack.size() > 1;
228
     }
242
     }
229
 
243
 
244
+    @NonNull
230
     @Override
245
     @Override
231
-    public void sendOnNavigationButtonPressed(String buttonId) {
232
-        peek().sendOnNavigationButtonPressed(buttonId);
246
+    protected StackLayout createView() {
247
+        return new StackLayout(getActivity(),
248
+                topBarButtonCreator,
249
+                titleBarReactViewCreator,
250
+                topBarBackgroundViewController,
251
+                topBarController,
252
+                this::onNavigationButtonPressed,
253
+                getId()
254
+        );
255
+    }
256
+
257
+    private void onNavigationButtonPressed(String buttonId) {
258
+        if (Constants.BACK_BUTTON_ID.equals(buttonId)) {
259
+            pop(new CommandListenerAdapter());
260
+        } else {
261
+            sendOnNavigationButtonPressed(buttonId);
262
+        }
233
     }
263
     }
234
 
264
 
235
-    @NonNull
236
     @Override
265
     @Override
237
-    protected StackLayout createView() {
238
-        return new StackLayout(getActivity(), topBarButtonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarController, this::sendOnNavigationButtonPressed, getId());
266
+    public void sendOnNavigationButtonPressed(String buttonId) {
267
+        peek().sendOnNavigationButtonPressed(buttonId);
239
     }
268
     }
240
 
269
 
241
     @NonNull
270
     @NonNull

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

1
 package com.reactnativenavigation.viewcontrollers;
1
 package com.reactnativenavigation.viewcontrollers;
2
 
2
 
3
+import android.annotation.SuppressLint;
3
 import android.app.Activity;
4
 import android.app.Activity;
4
 import android.graphics.Color;
5
 import android.graphics.Color;
5
 import android.graphics.drawable.Drawable;
6
 import android.graphics.drawable.Drawable;
6
 import android.support.annotation.NonNull;
7
 import android.support.annotation.NonNull;
7
 import android.support.v7.widget.ActionMenuView;
8
 import android.support.v7.widget.ActionMenuView;
8
 import android.support.v7.widget.Toolbar;
9
 import android.support.v7.widget.Toolbar;
9
-import android.util.Log;
10
 import android.view.MenuItem;
10
 import android.view.MenuItem;
11
 import android.widget.ImageButton;
11
 import android.widget.ImageButton;
12
 import android.widget.TextView;
12
 import android.widget.TextView;
20
 import com.reactnativenavigation.utils.ImageLoadingListenerAdapter;
20
 import com.reactnativenavigation.utils.ImageLoadingListenerAdapter;
21
 import com.reactnativenavigation.utils.UiUtils;
21
 import com.reactnativenavigation.utils.UiUtils;
22
 import com.reactnativenavigation.utils.ViewUtils;
22
 import com.reactnativenavigation.utils.ViewUtils;
23
+import com.reactnativenavigation.viewcontrollers.button.NavigationIconResolver;
23
 import com.reactnativenavigation.views.titlebar.TitleBarReactButtonView;
24
 import com.reactnativenavigation.views.titlebar.TitleBarReactButtonView;
24
 
25
 
25
 import java.util.Collections;
26
 import java.util.Collections;
30
         void onPress(String buttonId);
31
         void onPress(String buttonId);
31
     }
32
     }
32
 
33
 
34
+    private final NavigationIconResolver navigationIconResolver;
33
     private final ImageLoader imageLoader;
35
     private final ImageLoader imageLoader;
34
     private ButtonOptionsPresenter optionsPresenter;
36
     private ButtonOptionsPresenter optionsPresenter;
35
     private final Button button;
37
     private final Button button;
37
     private TopBarButtonController.OnClickListener onPressListener;
39
     private TopBarButtonController.OnClickListener onPressListener;
38
     private Drawable icon;
40
     private Drawable icon;
39
 
41
 
40
-    public TopBarButtonController(Activity activity, ImageLoader imageLoader, ButtonOptionsPresenter optionsPresenter, Button button, ReactViewCreator viewCreator, OnClickListener onClickListener) {
42
+    public TopBarButtonController(Activity activity, NavigationIconResolver navigationIconResolver, ImageLoader imageLoader, ButtonOptionsPresenter optionsPresenter, Button button, ReactViewCreator viewCreator, OnClickListener onClickListener) {
41
         super(activity, button.id, new Options());
43
         super(activity, button.id, new Options());
44
+        this.navigationIconResolver = navigationIconResolver;
42
         this.imageLoader = imageLoader;
45
         this.imageLoader = imageLoader;
43
         this.optionsPresenter = optionsPresenter;
46
         this.optionsPresenter = optionsPresenter;
44
         this.button = button;
47
         this.button = button;
46
         this.onPressListener = onClickListener;
49
         this.onPressListener = onClickListener;
47
     }
50
     }
48
 
51
 
52
+    @SuppressLint("MissingSuperCall")
49
     @Override
53
     @Override
50
     public void onViewAppeared() {
54
     public void onViewAppeared() {
51
         view.sendComponentStart();
55
         view.sendComponentStart();
52
     }
56
     }
53
 
57
 
58
+    @SuppressLint("MissingSuperCall")
54
     @Override
59
     @Override
55
     public void onViewDisappear() {
60
     public void onViewDisappear() {
56
         view.sendComponentStop();
61
         view.sendComponentStop();
75
     }
80
     }
76
 
81
 
77
     public void applyNavigationIcon(Toolbar toolbar) {
82
     public void applyNavigationIcon(Toolbar toolbar) {
78
-        if (!button.hasIcon()) {
79
-            Log.w("RNN", "Left button needs to have an icon");
80
-            return;
81
-        }
82
-
83
-        imageLoader.loadIcons(toolbar.getContext(), Collections.singletonList(button.icon.get()), new ImageLoader.ImagesLoadingListener() {
84
-            @Override
85
-            public void onComplete(@NonNull List<Drawable> drawables) {
86
-                icon = drawables.get(0);
87
-                setIconColor(icon);
88
-                toolbar.setNavigationOnClickListener(view -> onPressListener.onPress(button.id));
89
-                toolbar.setNavigationIcon(icon);
90
-                setLeftButtonTestId(toolbar);
91
-            }
92
-
93
-            @Override
94
-            public void onError(Throwable error) {
95
-                error.printStackTrace();
96
-            }
83
+        navigationIconResolver.resolve(button, icon -> {
84
+            setIconColor(icon);
85
+            toolbar.setNavigationOnClickListener(view -> onPressListener.onPress(button.id));
86
+            toolbar.setNavigationIcon(icon);
87
+            setLeftButtonTestId(toolbar);
97
         });
88
         });
98
     }
89
     }
99
 
90
 

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

15
 import com.reactnativenavigation.react.EventEmitter;
15
 import com.reactnativenavigation.react.EventEmitter;
16
 import com.reactnativenavigation.utils.CommandListener;
16
 import com.reactnativenavigation.utils.CommandListener;
17
 import com.reactnativenavigation.utils.ImageLoader;
17
 import com.reactnativenavigation.utils.ImageLoader;
18
+import com.reactnativenavigation.utils.ImageLoadingListenerAdapter;
18
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
19
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
19
 import com.reactnativenavigation.viewcontrollers.ParentController;
20
 import com.reactnativenavigation.viewcontrollers.ParentController;
20
 import com.reactnativenavigation.viewcontrollers.ViewController;
21
 import com.reactnativenavigation.viewcontrollers.ViewController;
124
             icons.add(tabOptions.icon.get());
125
             icons.add(tabOptions.icon.get());
125
         }
126
         }
126
 
127
 
127
-        imageLoader.loadIcons(getActivity(), icons, new ImageLoader.ImagesLoadingListener() {
128
+        imageLoader.loadIcons(getActivity(), icons, new ImageLoadingListenerAdapter() {
128
 
129
 
129
             @Override
130
             @Override
130
             public void onComplete(@NonNull List<Drawable> drawables) {
131
             public void onComplete(@NonNull List<Drawable> drawables) {

+ 44
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/button/NavigationIconResolver.java View File

1
+package com.reactnativenavigation.viewcontrollers.button;
2
+
3
+import android.content.Context;
4
+import android.graphics.drawable.Drawable;
5
+import android.support.annotation.NonNull;
6
+import android.support.v4.content.ContextCompat;
7
+
8
+import com.reactnativenavigation.R;
9
+import com.reactnativenavigation.parse.params.Button;
10
+import com.reactnativenavigation.react.Constants;
11
+import com.reactnativenavigation.utils.ImageLoader;
12
+import com.reactnativenavigation.utils.ImageLoadingListenerAdapter;
13
+import com.reactnativenavigation.utils.Task;
14
+
15
+public class NavigationIconResolver {
16
+
17
+    private Context context;
18
+    private ImageLoader imageLoader;
19
+
20
+    public NavigationIconResolver(Context context, ImageLoader imageLoader) {
21
+        this.context = context;
22
+        this.imageLoader = imageLoader;
23
+    }
24
+
25
+    public void resolve(Button button, Task<Drawable> onSuccess) {
26
+        if (button.icon.hasValue()) {
27
+            imageLoader.loadIcon(context, button.icon.get(), new ImageLoadingListenerAdapter() {
28
+                @Override
29
+                public void onComplete(@NonNull Drawable icon) {
30
+                    onSuccess.run(icon);
31
+                }
32
+
33
+                @Override
34
+                public void onError(Throwable error) {
35
+                    throw new RuntimeException(error);
36
+                }
37
+            });
38
+        } else if (Constants.BACK_BUTTON_ID.equals(button.id)) {
39
+            onSuccess.run(ContextCompat.getDrawable(context, R.drawable.ic_arrow_back_black_24dp));
40
+        } else {
41
+            throw new RuntimeException("Left button needs to have an icon");
42
+        }
43
+    }
44
+}

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

9
 import com.reactnativenavigation.anim.FabCollapseBehaviour;
9
 import com.reactnativenavigation.anim.FabCollapseBehaviour;
10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
11
 import com.reactnativenavigation.utils.ImageLoader;
11
 import com.reactnativenavigation.utils.ImageLoader;
12
+import com.reactnativenavigation.utils.ImageLoadingListenerAdapter;
12
 
13
 
13
 import java.util.Collections;
14
 import java.util.Collections;
14
 import java.util.List;
15
 import java.util.List;
26
     }
27
     }
27
 
28
 
28
     public void applyIcon(String icon) {
29
     public void applyIcon(String icon) {
29
-        new ImageLoader().loadIcons(getContext(), Collections.singletonList(icon), new ImageLoader.ImagesLoadingListener() {
30
+        new ImageLoader().loadIcons(getContext(), Collections.singletonList(icon), new ImageLoadingListenerAdapter() {
30
             @Override
31
             @Override
31
             public void onComplete(@NonNull List<Drawable> drawables) {
32
             public void onComplete(@NonNull List<Drawable> drawables) {
32
                 setImageDrawable(drawables.get(0));
33
                 setImageDrawable(drawables.get(0));

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

20
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
20
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
21
 import com.reactnativenavigation.viewcontrollers.TitleBarReactViewController;
21
 import com.reactnativenavigation.viewcontrollers.TitleBarReactViewController;
22
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
22
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
23
+import com.reactnativenavigation.viewcontrollers.button.NavigationIconResolver;
23
 
24
 
24
 import java.util.ArrayList;
25
 import java.util.ArrayList;
25
 import java.util.List;
26
 import java.util.List;
194
     }
195
     }
195
 
196
 
196
     public TopBarButtonController createButtonController(Button button) {
197
     public TopBarButtonController createButtonController(Button button) {
197
-        return new TopBarButtonController((Activity) getContext(), new ImageLoader(), new ButtonOptionsPresenter(this, button), button, buttonCreator, onClickListener);
198
+        ImageLoader imageLoader = new ImageLoader();
199
+        return new TopBarButtonController((Activity) getContext(),
200
+                new NavigationIconResolver(getContext(), imageLoader),
201
+                imageLoader,
202
+                new ButtonOptionsPresenter(this, button),
203
+                button,
204
+                buttonCreator,
205
+                onClickListener
206
+        );
198
     }
207
     }
199
 
208
 
200
     public Toolbar.LayoutParams getComponentLayoutParams(Component component) {
209
     public Toolbar.LayoutParams getComponentLayoutParams(Component component) {

+ 9
- 0
lib/android/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml View File

1
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+        android:width="24dp"
3
+        android:height="24dp"
4
+        android:viewportWidth="24.0"
5
+        android:viewportHeight="24.0">
6
+    <path
7
+        android:fillColor="#FF000000"
8
+        android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
9
+</vector>

+ 8
- 1
lib/android/app/src/test/java/com/reactnativenavigation/mocks/ImageLoaderMock.java View File

9
 
9
 
10
 import org.mockito.Mockito;
10
 import org.mockito.Mockito;
11
 
11
 
12
+import java.util.Collection;
12
 import java.util.Collections;
13
 import java.util.Collections;
13
 import java.util.List;
14
 import java.util.List;
14
 
15
 
42
         ImageLoader imageLoader = Mockito.mock(ImageLoader.class);
43
         ImageLoader imageLoader = Mockito.mock(ImageLoader.class);
43
         doAnswer(
44
         doAnswer(
44
                 invocation -> {
45
                 invocation -> {
45
-                    int urlCount = ((List) invocation.getArguments()[1]).size();
46
+                    int urlCount = ((Collection) invocation.getArguments()[1]).size();
46
                     List<Drawable> drawables = Collections.nCopies(urlCount, mockDrawable);
47
                     List<Drawable> drawables = Collections.nCopies(urlCount, mockDrawable);
47
                     ((ImageLoader.ImagesLoadingListener) invocation.getArguments()[2]).onComplete(drawables);
48
                     ((ImageLoader.ImagesLoadingListener) invocation.getArguments()[2]).onComplete(drawables);
48
                     return null;
49
                     return null;
49
                 }
50
                 }
50
         ).when(imageLoader).loadIcons(any(), any(), any());
51
         ).when(imageLoader).loadIcons(any(), any(), any());
52
+        doAnswer(
53
+                invocation -> {
54
+                    ((ImageLoader.ImagesLoadingListener) invocation.getArguments()[2]).onComplete(mockDrawable);
55
+                    return null;
56
+                }
57
+        ).when(imageLoader).loadIcon(any(), any(), any());
51
         return imageLoader;
58
         return imageLoader;
52
     }
59
     }
53
 }
60
 }

+ 3
- 0
lib/android/app/src/test/java/com/reactnativenavigation/utils/OptionHelper.java View File

3
 import com.reactnativenavigation.parse.Options;
3
 import com.reactnativenavigation.parse.Options;
4
 import com.reactnativenavigation.parse.params.Text;
4
 import com.reactnativenavigation.parse.params.Text;
5
 
5
 
6
+import java.util.ArrayList;
7
+
6
 public class OptionHelper {
8
 public class OptionHelper {
7
     public static Options createBottomTabOptions() {
9
     public static Options createBottomTabOptions() {
8
         Options options = new Options();
10
         Options options = new Options();
11
+        options.topBar.leftButtons = new ArrayList<>();
9
         options.bottomTabOptions.title = new Text("Tab");
12
         options.bottomTabOptions.title = new Text("Tab");
10
         options.bottomTabOptions.icon = new Text("http://127.0.0.1/icon.png");
13
         options.bottomTabOptions.icon = new Text("http://127.0.0.1/icon.png");
11
         return options;
14
         return options;

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

13
 import com.reactnativenavigation.parse.NestedAnimationsOptions;
13
 import com.reactnativenavigation.parse.NestedAnimationsOptions;
14
 import com.reactnativenavigation.parse.Options;
14
 import com.reactnativenavigation.parse.Options;
15
 import com.reactnativenavigation.parse.params.Bool;
15
 import com.reactnativenavigation.parse.params.Bool;
16
+import com.reactnativenavigation.parse.params.Button;
16
 import com.reactnativenavigation.parse.params.Text;
17
 import com.reactnativenavigation.parse.params.Text;
17
 import com.reactnativenavigation.utils.CommandListenerAdapter;
18
 import com.reactnativenavigation.utils.CommandListenerAdapter;
18
 import com.reactnativenavigation.utils.ViewHelper;
19
 import com.reactnativenavigation.utils.ViewHelper;
30
 import org.mockito.ArgumentCaptor;
31
 import org.mockito.ArgumentCaptor;
31
 import org.mockito.Mockito;
32
 import org.mockito.Mockito;
32
 
33
 
34
+import java.util.ArrayList;
35
+import java.util.Collections;
36
+
33
 import static org.assertj.core.api.Java6Assertions.assertThat;
37
 import static org.assertj.core.api.Java6Assertions.assertThat;
34
 import static org.mockito.ArgumentMatchers.any;
38
 import static org.mockito.ArgumentMatchers.any;
35
 import static org.mockito.ArgumentMatchers.eq;
39
 import static org.mockito.ArgumentMatchers.eq;
87
         verify(listener, times(1)).onSuccess(child1.getId());
91
         verify(listener, times(1)).onSuccess(child1.getId());
88
     }
92
     }
89
 
93
 
94
+    @Test
95
+    public void push_backButtonIsAddedIfStackContainsMoreThenOneScreen() {
96
+        uut.push(child1, new CommandListenerAdapter());
97
+        verify(child1, times(0)).mergeOptions(any());
98
+
99
+        uut.push(child2, new CommandListenerAdapter());
100
+        ArgumentCaptor<Options> captor = ArgumentCaptor.forClass(Options.class);
101
+        verify(child2, times(1)).mergeOptions(captor.capture());
102
+        assertThat(captor.getValue().topBar.leftButtons).isNotNull();
103
+        assertThat(captor.getValue().topBar.leftButtons.get(0).id).isEqualTo("RNN.back");
104
+    }
105
+
106
+    @Test
107
+    public void push_backButtonIsNotAddedIfScreenContainsLeftButtons() {
108
+        uut.push(child1, new CommandListenerAdapter());
109
+
110
+        Button leftButton = new Button();
111
+        leftButton.id = "someButton";
112
+        child2.options.topBar.leftButtons = new ArrayList<>(Collections.singleton(leftButton));
113
+        uut.push(child2, new CommandListenerAdapter());
114
+        verify(child2, times(0)).mergeOptions(any());
115
+    }
116
+
117
+    @Test
118
+    public void push_backButtonIsNotAddedIfScreenClearsLeftButton() {
119
+        child1.options.topBar.leftButtons = new ArrayList<>();
120
+        uut.push(child1, new CommandListenerAdapter());
121
+        verify(child1, times(0)).mergeOptions(any());
122
+    }
123
+
90
     @Test
124
     @Test
91
     public void animateSetRoot() {
125
     public void animateSetRoot() {
92
         assertThat(uut.isEmpty()).isTrue();
126
         assertThat(uut.isEmpty()).isTrue();
105
         assertThat(uut.isEmpty()).isTrue();
139
         assertThat(uut.isEmpty()).isTrue();
106
         uut.push(child1, new CommandListenerAdapter());
140
         uut.push(child1, new CommandListenerAdapter());
107
         uut.push(child2, new CommandListenerAdapter());
141
         uut.push(child2, new CommandListenerAdapter());
142
+        assertThat(uut.getTopBar().getTitleBar().getNavigationIcon()).isNotNull();
108
         uut.setRoot(child3, new CommandListenerAdapter() {
143
         uut.setRoot(child3, new CommandListenerAdapter() {
109
             @Override
144
             @Override
110
             public void onSuccess(String childId) {
145
             public void onSuccess(String childId) {
111
                 assertContainsOnlyId(child3.getId());
146
                 assertContainsOnlyId(child3.getId());
147
+                assertThat(uut.getTopBar().getTitleBar().getNavigationIcon()).isNull();
112
             }
148
             }
113
         });
149
         });
114
     }
150
     }
141
     @Test
177
     @Test
142
     public void pop_layoutHandlesChildWillDisappear() {
178
     public void pop_layoutHandlesChildWillDisappear() {
143
         final StackLayout[] stackLayout = new StackLayout[1];
179
         final StackLayout[] stackLayout = new StackLayout[1];
144
-        uut =
145
-                new StackControllerBuilder(activity)
180
+        uut = new StackControllerBuilder(activity)
146
                         .setTopBarButtonCreator(new TopBarButtonCreatorMock())
181
                         .setTopBarButtonCreator(new TopBarButtonCreatorMock())
147
                         .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
182
                         .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
148
                         .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
183
                         .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
722
     }
757
     }
723
 
758
 
724
     private StackController createStackController(String id) {
759
     private StackController createStackController(String id) {
725
-        topBarController = spy(new TopBarController() {
726
-            @Override
727
-            protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout) {
728
-                TopBar topBar = spy(super.createTopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout));
729
-                return topBar;
730
-            }
731
-        });
760
+        createTopBarController();
732
         return new StackControllerBuilder(activity)
761
         return new StackControllerBuilder(activity)
733
                 .setChildRegistry(childRegistry)
762
                 .setChildRegistry(childRegistry)
734
                 .setTopBarButtonCreator(new TopBarButtonCreatorMock())
763
                 .setTopBarButtonCreator(new TopBarButtonCreatorMock())
740
                 .setInitialOptions(new Options())
769
                 .setInitialOptions(new Options())
741
                 .createStackController();
770
                 .createStackController();
742
     }
771
     }
772
+
773
+    private void createTopBarController() {
774
+        topBarController = spy(new TopBarController() {
775
+            @Override
776
+            protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout) {
777
+                return spy(super.createTopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout));
778
+            }
779
+        });
780
+    }
743
 }
781
 }

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

12
 import com.reactnativenavigation.parse.Component;
12
 import com.reactnativenavigation.parse.Component;
13
 import com.reactnativenavigation.parse.params.Button;
13
 import com.reactnativenavigation.parse.params.Button;
14
 import com.reactnativenavigation.parse.params.Text;
14
 import com.reactnativenavigation.parse.params.Text;
15
+import com.reactnativenavigation.react.Constants;
15
 import com.reactnativenavigation.react.ReactView;
16
 import com.reactnativenavigation.react.ReactView;
16
 import com.reactnativenavigation.utils.ViewUtils;
17
 import com.reactnativenavigation.utils.ViewUtils;
17
 import com.reactnativenavigation.views.titlebar.TitleBar;
18
 import com.reactnativenavigation.views.titlebar.TitleBar;
60
 
61
 
61
     private void createButtons() {
62
     private void createButtons() {
62
         leftButton = new Button();
63
         leftButton = new Button();
63
-        leftButton.id = "back";
64
-        leftButton.title = new Text("abc");
64
+        leftButton.id = Constants.BACK_BUTTON_ID;
65
 
65
 
66
         textButton = new Button();
66
         textButton = new Button();
67
         textButton.id = "textButton";
67
         textButton.id = "textButton";
100
 
100
 
101
     @Test
101
     @Test
102
     public void setRightButtons_destroysRightButtons() {
102
     public void setRightButtons_destroysRightButtons() {
103
-        uut.setLeftButtons(leftButton(leftButton));
104
         uut.setRightButtons(rightButtons(customButton));
103
         uut.setRightButtons(rightButtons(customButton));
105
         uut.setLeftButtons(leftButton(leftButton));
104
         uut.setLeftButtons(leftButton(leftButton));
106
         uut.setRightButtons(rightButtons(textButton));
105
         uut.setRightButtons(rightButtons(textButton));

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

18
 import com.reactnativenavigation.parse.params.Number;
18
 import com.reactnativenavigation.parse.params.Number;
19
 import com.reactnativenavigation.parse.params.Text;
19
 import com.reactnativenavigation.parse.params.Text;
20
 import com.reactnativenavigation.utils.ButtonOptionsPresenter;
20
 import com.reactnativenavigation.utils.ButtonOptionsPresenter;
21
+import com.reactnativenavigation.viewcontrollers.button.NavigationIconResolver;
21
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
22
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
22
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
23
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
23
 
24
 
58
         getTitleBar().layout(0, 0, 1080, 200);
59
         getTitleBar().layout(0, 0, 1080, 200);
59
 
60
 
60
         optionsPresenter = spy(new ButtonOptionsPresenter(getTitleBar(), button));
61
         optionsPresenter = spy(new ButtonOptionsPresenter(getTitleBar(), button));
61
-        uut = new TopBarButtonController(activity, ImageLoaderMock.mock(), optionsPresenter, button, buttonCreatorMock, (buttonId) -> {});
62
+        uut = new TopBarButtonController(activity, new NavigationIconResolver(activity, ImageLoaderMock.mock()), ImageLoaderMock.mock(), optionsPresenter, button, buttonCreatorMock, (buttonId) -> {});
62
 
63
 
63
         stackController.ensureViewIsCreated();
64
         stackController.ensureViewIsCreated();
64
     }
65
     }

+ 75
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/button/NavigationIconResolverTest.java View File

1
+package com.reactnativenavigation.viewcontrollers.button;
2
+
3
+import android.content.Context;
4
+import android.graphics.drawable.Drawable;
5
+
6
+import com.reactnativenavigation.BaseTest;
7
+import com.reactnativenavigation.mocks.ImageLoaderMock;
8
+import com.reactnativenavigation.parse.params.Button;
9
+import com.reactnativenavigation.parse.params.Color;
10
+import com.reactnativenavigation.parse.params.Text;
11
+import com.reactnativenavigation.react.Constants;
12
+import com.reactnativenavigation.utils.ImageLoader;
13
+import com.reactnativenavigation.utils.Task;
14
+
15
+import org.junit.Test;
16
+
17
+import static org.mockito.ArgumentMatchers.any;
18
+import static org.mockito.ArgumentMatchers.eq;
19
+import static org.mockito.Mockito.spy;
20
+import static org.mockito.Mockito.verify;
21
+import static org.mockito.Mockito.verifyZeroInteractions;
22
+
23
+public class NavigationIconResolverTest extends BaseTest {
24
+    private static final String ICON_URI = "someIconUri";
25
+    private NavigationIconResolver uut;
26
+    private ImageLoader imageLoader;
27
+    private Context context;
28
+
29
+    @Override
30
+    public void beforeEach() {
31
+        imageLoader = ImageLoaderMock.mock();
32
+        context = newActivity();
33
+        uut = new NavigationIconResolver(context, imageLoader);
34
+    }
35
+
36
+    @Test
37
+    public void create_iconButton() {
38
+        @SuppressWarnings("Convert2Lambda") Task<Drawable> onSuccess = spy(new Task<Drawable>() {
39
+            @Override
40
+            public void run(Drawable icon) {
41
+
42
+            }
43
+        });
44
+        uut.resolve(iconButton(), onSuccess);
45
+        verify(imageLoader).loadIcon(eq(context), eq(ICON_URI), any());
46
+        verify(onSuccess).run(any(Drawable.class));
47
+    }
48
+
49
+    @Test
50
+    public void create_backButton() {
51
+        @SuppressWarnings("Convert2Lambda") Task<Drawable> onSuccess = spy(new Task<Drawable>() {
52
+            @Override
53
+            public void run(Drawable param) {
54
+
55
+            }
56
+        });
57
+        uut.resolve(backButton(), onSuccess);
58
+        verifyZeroInteractions(imageLoader);
59
+        verify(onSuccess).run(any());
60
+    }
61
+
62
+    private Button iconButton() {
63
+        Button button = new Button();
64
+        button.id = "iconBtnId";
65
+        button.icon = new Text(ICON_URI);
66
+        button.color = new Color(android.graphics.Color.RED);
67
+        return button;
68
+    }
69
+
70
+    private Button backButton() {
71
+        Button button = new Button();
72
+        button.id = Constants.BACK_BUTTON_ID;
73
+        return button;
74
+    }
75
+}