Browse Source

Merge pull request #41 from wix/passProps

Add support for passing props to a screen in Android
Yedidya Kennard 8 years ago
parent
commit
f166927ed6

+ 3
- 0
.gitignore View File

@@ -145,3 +145,6 @@ xcuserdata
145 145
 # UNKNOWN: recommended by others, but I can't discover what these files are
146 146
 #
147 147
 # ...none. Everything is now explained.:
148
+android/gradlew
149
+android/gradlew.bat
150
+android/local.properties

+ 9
- 4
android/app/src/main/java/com/reactnativenavigation/core/objects/Screen.java View File

@@ -6,10 +6,12 @@ import android.support.annotation.Nullable;
6 6
 
7 7
 import com.facebook.react.bridge.ReadableArray;
8 8
 import com.facebook.react.bridge.ReadableMap;
9
+import com.facebook.react.bridge.ReadableNativeMap;
9 10
 
10 11
 import java.io.Serializable;
11 12
 import java.util.ArrayList;
12 13
 import java.util.Collections;
14
+import java.util.HashMap;
13 15
 import java.util.List;
14 16
 
15 17
 /**
@@ -35,6 +37,7 @@ public class Screen extends JsonObject implements Serializable {
35 37
     private static final String KEY_TAB_NORMAL_TEXT_COLOR = "tabNormalTextColor";
36 38
     private static final String KEY_TAB_SELECTED_TEXT_COLOR = "tabSelectedTextColor";
37 39
     private static final String KEY_TAB_INDICATOR_COLOR = "tabIndicatorColor";
40
+    public static final String KEY_PROPS = "passProps";
38 41
 
39 42
     public final String title;
40 43
     public final String label;
@@ -44,6 +47,7 @@ public class Screen extends JsonObject implements Serializable {
44 47
     public final String navigatorEventId;
45 48
     public final int icon;
46 49
     public final ArrayList<Button> buttons;
50
+    public HashMap<String, Object> passedProps = new HashMap<>();
47 51
 
48 52
     // Navigation styling
49 53
     @Nullable @ColorInt public Integer toolBarColor;
@@ -57,7 +61,7 @@ public class Screen extends JsonObject implements Serializable {
57 61
 
58 62
     @NonNull
59 63
     public List<Button> getButtons() {
60
-        return buttons == null ? Collections.EMPTY_LIST : buttons;
64
+        return buttons == null ? Collections.<Button>emptyList() : buttons;
61 65
     }
62 66
 
63 67
     public Screen(ReadableMap screen) {
@@ -68,15 +72,16 @@ public class Screen extends JsonObject implements Serializable {
68 72
         navigatorId = getString(screen, KEY_NAVIGATOR_ID);
69 73
         navigatorEventId = getString(screen, KEY_NAVIGATOR_EVENT_ID);
70 74
         icon = getInt(screen, KEY_ICON);
71
-
75
+        if(screen.hasKey(KEY_PROPS)) {
76
+            passedProps = ((ReadableNativeMap) screen.getMap(KEY_PROPS)).toHashMap();
77
+        }
72 78
         buttons = getButtons(screen);
73 79
         setToolbarStyle(screen);
74 80
     }
75 81
 
76 82
     private ArrayList<Button> getButtons(ReadableMap screen) {
77
-        ArrayList<Button> ret = null;
83
+        ArrayList<Button> ret = new ArrayList<>();
78 84
         if (screen.hasKey(KEY_RIGHT_BUTTONS)) {
79
-            ret = new ArrayList<>();
80 85
             ReadableArray rightButtons = screen.getArray(KEY_RIGHT_BUTTONS);
81 86
             for (int i = 0; i < rightButtons.size(); i++) {
82 87
                 ret.add(new Button(rightButtons.getMap(i)));

+ 121
- 0
android/app/src/main/java/com/reactnativenavigation/utils/BridgeUtils.java View File

@@ -0,0 +1,121 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import android.os.Bundle;
4
+import android.util.Log;
5
+
6
+import java.util.ArrayList;
7
+import java.util.HashMap;
8
+
9
+/**
10
+ * Created by yedidyak on 26/05/2016.
11
+ */
12
+public class BridgeUtils {
13
+
14
+    @SuppressWarnings("unchecked")
15
+    public static Bundle addMapToBundle(HashMap<String, ?> map, Bundle bundle) {
16
+        for (String key : map.keySet()) {
17
+            Object value = map.get(key);
18
+            if (value instanceof String) {
19
+                bundle.putString(key, (String) value);
20
+            } else if (value instanceof Integer) {
21
+                bundle.putInt(key, (Integer) value);
22
+            } else if (value instanceof Double) {
23
+                bundle.putDouble(key, ((Double) value));
24
+            } else if (value instanceof Boolean) {
25
+                bundle.putBoolean(key, (Boolean) value);
26
+            } else if (value instanceof HashMap) {
27
+                bundle.putBundle(key, addMapToBundle((HashMap<String, Object>) value, new Bundle()));
28
+            } else if (value instanceof ArrayList) {
29
+                putArray(key, (ArrayList) value, bundle);
30
+            }
31
+        }
32
+        return bundle;
33
+    }
34
+
35
+    @SuppressWarnings("unchecked")
36
+    private static void putArray(String key, ArrayList arrayList, Bundle bundle) {
37
+        if (arrayList.size() == 0) {
38
+            bundle.putBooleanArray(key, new boolean[]{});
39
+        } else {
40
+            verifyArrayListIsSingleType(arrayList);
41
+            if (arrayList.get(0) instanceof String) {
42
+                bundle.putStringArray(key, toStringArray((ArrayList<String>) arrayList));
43
+            } else if (arrayList.get(0) instanceof Integer) {
44
+                bundle.putIntArray(key, toIntArray((ArrayList<Integer>) arrayList));
45
+            } else if (arrayList.get(0) instanceof Float) {
46
+                bundle.putFloatArray(key, toFloatArray((ArrayList<Float>) arrayList));
47
+            } else if (arrayList.get(0) instanceof Double) {
48
+                bundle.putDoubleArray(key, toDoubleArray((ArrayList<Double>) arrayList));
49
+            } else if (arrayList.get(0) instanceof Boolean) {
50
+                bundle.putBooleanArray(key, toBooleanArray((ArrayList<Boolean>) arrayList));
51
+            } else if (arrayList.get(0) instanceof HashMap) {
52
+                bundle.putParcelableArray(key, toBundleArray((ArrayList<HashMap>) arrayList));
53
+            } else if (arrayList.get(0) instanceof ArrayList) {
54
+                Log.w("RNNavigation", "Arrays of arrays passed in props are converted to dictionaries with indexes as keys");
55
+                Bundle innerArray = new Bundle();
56
+                for (int i = 0; i < arrayList.size(); i++) {
57
+                    putArray(String.valueOf(i), (ArrayList) arrayList.get(i), innerArray);
58
+                }
59
+                bundle.putParcelable(key, innerArray);
60
+            }
61
+        }
62
+    }
63
+
64
+    private static void verifyArrayListIsSingleType(ArrayList arrayList) {
65
+        for (int i = 1; i < arrayList.size(); i++) {
66
+            if (!arrayList.get(i - 1).getClass().isInstance(arrayList.get(i))) {
67
+                throw new IllegalArgumentException("Cannot pass array of multiple types via props");
68
+            }
69
+        }
70
+    }
71
+
72
+    @SuppressWarnings("unchecked")
73
+    private static Bundle[] toBundleArray(ArrayList<HashMap> arrayList) {
74
+        Bundle[] ret = new Bundle[arrayList.size()];
75
+        for (int i=0; i < ret.length; i++) {
76
+            ret[i] = addMapToBundle(arrayList.get(i), new Bundle());
77
+        }
78
+        return ret;
79
+    }
80
+
81
+    private static int[] toIntArray(ArrayList<Integer> arrayList) {
82
+        int[] ret = new int[arrayList.size()];
83
+        for (int i=0; i < ret.length; i++) {
84
+            ret[i] = arrayList.get(i);
85
+        }
86
+        return ret;
87
+    }
88
+
89
+    private static float[] toFloatArray(ArrayList<Float> arrayList) {
90
+        float[] ret = new float[arrayList.size()];
91
+        for (int i=0; i < ret.length; i++) {
92
+            ret[i] = arrayList.get(i);
93
+        }
94
+        return ret;
95
+    }
96
+
97
+    private static double[] toDoubleArray(ArrayList<Double> arrayList) {
98
+        double[] ret = new double[arrayList.size()];
99
+        for (int i=0; i < ret.length; i++) {
100
+            ret[i] = arrayList.get(i);
101
+        }
102
+        return ret;
103
+    }
104
+
105
+    private static boolean[] toBooleanArray(ArrayList<Boolean> arrayList) {
106
+        boolean[] ret = new boolean[arrayList.size()];
107
+        for (int i=0; i < ret.length; i++) {
108
+            ret[i] = arrayList.get(i);
109
+        }
110
+        return ret;
111
+    }
112
+
113
+    private static String[] toStringArray(ArrayList<String> arrayList) {
114
+        String[] ret = new String[arrayList.size()];
115
+        for (int i=0; i < ret.length; i++) {
116
+            ret[i] = arrayList.get(i);
117
+        }
118
+        return ret;
119
+    }
120
+
121
+}

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

@@ -8,6 +8,7 @@ import com.facebook.react.ReactInstanceManager;
8 8
 import com.facebook.react.ReactRootView;
9 9
 import com.reactnativenavigation.activities.BaseReactActivity;
10 10
 import com.reactnativenavigation.core.objects.Screen;
11
+import com.reactnativenavigation.utils.BridgeUtils;
11 12
 
12 13
 /**
13 14
  * Created by guyc on 10/03/16.
@@ -34,6 +35,7 @@ public class RctView extends FrameLayout {
34 35
         this(ctx, rctInstanceManager, screen, null);
35 36
     }
36 37
 
38
+    @SuppressWarnings("unchecked")
37 39
     public RctView(BaseReactActivity ctx, ReactInstanceManager rctInstanceManager, Screen screen,
38 40
                    final OnDisplayedListener onDisplayedListener) {
39 41
         super(ctx);
@@ -47,6 +49,9 @@ public class RctView extends FrameLayout {
47 49
         passProps.putString(Screen.KEY_SCREEN_INSTANCE_ID, screen.screenInstanceId);
48 50
         passProps.putString(Screen.KEY_NAVIGATOR_ID, screen.navigatorId);
49 51
         passProps.putString(Screen.KEY_NAVIGATOR_EVENT_ID, screen.navigatorEventId);
52
+        if (screen.passedProps != null) {
53
+            BridgeUtils.addMapToBundle(screen.passedProps, passProps);
54
+        }
50 55
 
51 56
         mReactRootView.startReactApplication(rctInstanceManager, componentName, passProps);
52 57
 

+ 48
- 3
example-redux/src/app.js View File

@@ -39,7 +39,28 @@ export default class App {
39 39
           screen: {
40 40
             screen: 'example.LoginScreen',
41 41
             title: 'Login',
42
-            navigatorStyle: {}
42
+            navigatorStyle: {},
43
+            passProps: {
44
+              str: 'This is a prop passed in \'startSingleScreenApp()\'!',
45
+              obj: {
46
+                str: 'This is a prop passed in an object!',
47
+                arr: [
48
+                  {
49
+                    str: 'This is a prop in an object in an array in an object!'
50
+                  }
51
+                ],
52
+                arr2: [
53
+                    [
54
+                        'array of strings',
55
+                        'with two strings'
56
+                    ],
57
+                    [
58
+                        1, 2, 3
59
+                    ]
60
+                ]
61
+              },
62
+              num: 1234
63
+            }
43 64
           }
44 65
         });
45 66
         return;
@@ -52,7 +73,19 @@ export default class App {
52 73
               icon: require('../img/one.png'),
53 74
               selectedIcon: require('../img/one_selected.png'),
54 75
               title: 'Screen One',
55
-              navigatorStyle: {}
76
+              navigatorStyle: {},
77
+              passProps: {
78
+                str: 'This is a prop passed in \'startTabBasedApp\'!',
79
+                obj: {
80
+                  str: 'This is a prop passed in an object!',
81
+                  arr: [
82
+                    {
83
+                      str: 'This is a prop in an object in an array in an object!'
84
+                    }
85
+                  ]
86
+                },
87
+                num: 1234
88
+              }
56 89
             },
57 90
             {
58 91
               label: 'Two',
@@ -60,7 +93,19 @@ export default class App {
60 93
               icon: require('../img/two.png'),
61 94
               selectedIcon: require('../img/two_selected.png'),
62 95
               title: 'Screen Two',
63
-              navigatorStyle: {}
96
+              navigatorStyle: {},
97
+              passProps: {
98
+                str: 'This is a prop passed in \'startTabBasedApp\'!',
99
+                obj: {
100
+                  str: 'This is a prop passed in an object!',
101
+                  arr: [
102
+                    {
103
+                      str: 'This is a prop in an object in an array in an object!'
104
+                    }
105
+                  ]
106
+                },
107
+                num: 1234
108
+              }
64 109
             }
65 110
           ],
66 111
           animationType: 'slide-down',

+ 40
- 3
example-redux/src/screens/FirstTabScreen.js View File

@@ -1,4 +1,4 @@
1
-import React, {Component} from 'react';
1
+import React, {Component, PropTypes} from 'react';
2 2
 import {
3 3
   Text,
4 4
   View,
@@ -36,11 +36,19 @@ class FirstTabScreen extends Component {
36 36
       }
37 37
     ]
38 38
   };
39
+
40
+  static propTypes = {
41
+    str: PropTypes.string.isRequired,
42
+    obj: PropTypes.object.isRequired,
43
+    num: PropTypes.number.isRequired
44
+  };
45
+
39 46
   constructor(props) {
40 47
     super(props);
41 48
     // if you want to listen on navigator events, set this up
42 49
     this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
43 50
   }
51
+
44 52
   onNavigatorEvent(event) {
45 53
     switch (event.id) {
46 54
       case 'edit':
@@ -75,6 +83,11 @@ class FirstTabScreen extends Component {
75 83
         <TouchableOpacity onPress={ this.onShowModalPress.bind(this) }>
76 84
           <Text style={styles.button}>Modal Screen</Text>
77 85
         </TouchableOpacity>
86
+
87
+        <Text style={{fontWeight: '500'}}>String prop: {this.props.str}</Text>
88
+        <Text style={{fontWeight: '500'}}>Number prop: {this.props.num}</Text>
89
+        <Text style={{fontWeight: '500'}}>Object prop: {this.props.obj.str}</Text>
90
+        <Text style={{fontWeight: '500'}}>Array prop: {this.props.obj.arr[0].str}</Text>
78 91
       </View>
79 92
     );
80 93
   }
@@ -86,14 +99,38 @@ class FirstTabScreen extends Component {
86 99
   onPushPress() {
87 100
     this.props.navigator.push({
88 101
       title: "More",
89
-      screen: "example.PushedScreen"
102
+      screen: "example.PushedScreen",
103
+      passProps: {
104
+        passed: 'This is a prop passed in \'navigator.push()\'!',
105
+        obj: {
106
+          str: 'This is a prop passed in an object!',
107
+          arr: [
108
+            {
109
+              str: 'This is a prop in an object in an array in an object!'
110
+            }
111
+          ]
112
+        },
113
+        num: 1234
114
+      }
90 115
     });
91 116
   }
92 117
 
93 118
   onShowModalPress() {
94 119
     this.props.navigator.showModal({
95 120
       title: "Modal Screen",
96
-      screen: "example.PushedScreen"
121
+      screen: "example.PushedScreen",
122
+      passProps: {
123
+        passed: 'This is a prop passed in \'navigator.showModal()\'!',
124
+        obj: {
125
+          str: 'This is a prop passed in an object!',
126
+          arr: [
127
+            {
128
+              str: 'This is a prop in an object in an array in an object!'
129
+            }
130
+          ]
131
+        },
132
+        num: 1234
133
+      }
97 134
     });
98 135
   }
99 136
 }

+ 16
- 1
example-redux/src/screens/LoginScreen.js View File

@@ -1,4 +1,4 @@
1
-import React, {Component} from 'react';
1
+import React, {Component, PropTypes} from 'react';
2 2
 import {
3 3
   Text,
4 4
   View,
@@ -12,9 +12,18 @@ import * as appActions from '../reducers/app/actions';
12 12
 
13 13
 // this is a traditional React component connected to the redux store
14 14
 class LoginScreen extends Component {
15
+
16
+  static propTypes = {
17
+    str: PropTypes.string.isRequired,
18
+    obj: PropTypes.object.isRequired,
19
+    num: PropTypes.number.isRequired
20
+  };
21
+
15 22
   constructor(props) {
16 23
     super(props);
24
+    console.log(props);
17 25
   }
26
+
18 27
   render() {
19 28
     return (
20 29
       <View style={{flex: 1, padding: 20}}>
@@ -31,6 +40,12 @@ class LoginScreen extends Component {
31 40
           <Text style={styles.button}>Login</Text>
32 41
         </TouchableOpacity>
33 42
 
43
+        <Text style={{fontWeight: '500'}}>String prop: {this.props.str}</Text>
44
+        <Text style={{fontWeight: '500'}}>Number prop: {this.props.num}</Text>
45
+        <Text style={{fontWeight: '500'}}>Object prop: {this.props.obj.str}</Text>
46
+        <Text style={{fontWeight: '500'}}>Array prop: {this.props.obj.arr[0].str}</Text>
47
+        <Text style={{fontWeight: '500'}}>Array of arrays prop: {JSON.stringify(this.props.obj.arr2)}</Text>
48
+
34 49
       </View>
35 50
     );
36 51
   }

+ 38
- 3
example-redux/src/screens/PushedScreen.js View File

@@ -1,4 +1,4 @@
1
-import React, {Component} from 'react';
1
+import React, {Component, PropTypes} from 'react';
2 2
 import {
3 3
   Text,
4 4
   View,
@@ -23,6 +23,12 @@ class PushedScreen extends Component {
23 23
     tabIndicatorColor: '#FF4081'
24 24
   };
25 25
 
26
+  static propTypes = {
27
+    str: PropTypes.string.isRequired,
28
+    obj: PropTypes.object.isRequired,
29
+    num: PropTypes.number.isRequired
30
+  };
31
+
26 32
   constructor(props) {
27 33
     super(props);
28 34
     this.bgColor = this.getRandomColor();
@@ -72,6 +78,11 @@ class PushedScreen extends Component {
72 78
 
73 79
         <TextInput style={{height: 40, borderColor: 'gray', borderWidth: 1}}/>
74 80
 
81
+        <Text style={{fontWeight: '500'}}>String prop: {this.props.str}</Text>
82
+        <Text style={{fontWeight: '500'}}>Number prop: {this.props.num}</Text>
83
+        <Text style={{fontWeight: '500'}}>Object prop: {this.props.obj.str}</Text>
84
+        <Text style={{fontWeight: '500'}}>Array prop: {this.props.obj.arr[0].str}</Text>
85
+
75 86
       </View>
76 87
     );
77 88
   }
@@ -83,7 +94,19 @@ class PushedScreen extends Component {
83 94
   onPushPress() {
84 95
     this.props.navigator.push({
85 96
       title: "More",
86
-      screen: "example.PushedScreen"
97
+      screen: "example.PushedScreen",
98
+      passProps: {
99
+        passed: 'This is a prop passed in \'navigator.push()\'!',
100
+        obj: {
101
+          str: 'This is a prop passed in an object!',
102
+          arr: [
103
+            {
104
+              str: 'This is a prop in an object in an array in an object!'
105
+            }
106
+          ]
107
+        },
108
+        num: 1234
109
+      }
87 110
     });
88 111
   }
89 112
 
@@ -94,7 +117,19 @@ class PushedScreen extends Component {
94 117
   onShowModalPress() {
95 118
     this.props.navigator.showModal({
96 119
       title: "Modal Screen",
97
-      screen: "example.PushedScreen"
120
+      screen: "example.PushedScreen",
121
+      passProps: {
122
+        passed: 'This is a prop passed in \'navigator.showModal()\'!',
123
+        obj: {
124
+          str: 'This is a prop passed in an object!',
125
+          arr: [
126
+            {
127
+              str: 'This is a prop in an object in an array in an object!'
128
+            }
129
+          ]
130
+        },
131
+        num: 1234
132
+      }
98 133
     });
99 134
   }
100 135
 

+ 13
- 1
example-redux/src/screens/SecondTabScreen.js View File

@@ -1,4 +1,4 @@
1
-import React, {Component} from 'react';
1
+import React, {Component, PropTypes} from 'react';
2 2
 import {
3 3
   Text,
4 4
   Image,
@@ -18,6 +18,13 @@ class SecondTabScreen extends Component {
18 18
     drawUnderTabBar: true,
19 19
     navBarTranslucent: true
20 20
   };
21
+
22
+  static propTypes = {
23
+    str: PropTypes.string.isRequired,
24
+    obj: PropTypes.object.isRequired,
25
+    num: PropTypes.number.isRequired
26
+  };
27
+
21 28
   constructor(props) {
22 29
     super(props);
23 30
     this.buttonsCounter = 0;
@@ -38,6 +45,11 @@ class SecondTabScreen extends Component {
38 45
             <Text style={styles.button}>Increment Counter</Text>
39 46
           </TouchableOpacity>
40 47
 
48
+          <Text style={{fontWeight: '500'}}>String prop: {this.props.str}</Text>
49
+          <Text style={{fontWeight: '500'}}>Number prop: {this.props.num}</Text>
50
+          <Text style={{fontWeight: '500'}}>Object prop: {this.props.obj.str}</Text>
51
+          <Text style={{fontWeight: '500'}}>Array prop: {this.props.obj.arr[0].str}</Text>
52
+
41 53
         </View>
42 54
 
43 55
       </ScrollView>