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
 # UNKNOWN: recommended by others, but I can't discover what these files are
145
 # UNKNOWN: recommended by others, but I can't discover what these files are
146
 #
146
 #
147
 # ...none. Everything is now explained.:
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
 
6
 
7
 import com.facebook.react.bridge.ReadableArray;
7
 import com.facebook.react.bridge.ReadableArray;
8
 import com.facebook.react.bridge.ReadableMap;
8
 import com.facebook.react.bridge.ReadableMap;
9
+import com.facebook.react.bridge.ReadableNativeMap;
9
 
10
 
10
 import java.io.Serializable;
11
 import java.io.Serializable;
11
 import java.util.ArrayList;
12
 import java.util.ArrayList;
12
 import java.util.Collections;
13
 import java.util.Collections;
14
+import java.util.HashMap;
13
 import java.util.List;
15
 import java.util.List;
14
 
16
 
15
 /**
17
 /**
35
     private static final String KEY_TAB_NORMAL_TEXT_COLOR = "tabNormalTextColor";
37
     private static final String KEY_TAB_NORMAL_TEXT_COLOR = "tabNormalTextColor";
36
     private static final String KEY_TAB_SELECTED_TEXT_COLOR = "tabSelectedTextColor";
38
     private static final String KEY_TAB_SELECTED_TEXT_COLOR = "tabSelectedTextColor";
37
     private static final String KEY_TAB_INDICATOR_COLOR = "tabIndicatorColor";
39
     private static final String KEY_TAB_INDICATOR_COLOR = "tabIndicatorColor";
40
+    public static final String KEY_PROPS = "passProps";
38
 
41
 
39
     public final String title;
42
     public final String title;
40
     public final String label;
43
     public final String label;
44
     public final String navigatorEventId;
47
     public final String navigatorEventId;
45
     public final int icon;
48
     public final int icon;
46
     public final ArrayList<Button> buttons;
49
     public final ArrayList<Button> buttons;
50
+    public HashMap<String, Object> passedProps = new HashMap<>();
47
 
51
 
48
     // Navigation styling
52
     // Navigation styling
49
     @Nullable @ColorInt public Integer toolBarColor;
53
     @Nullable @ColorInt public Integer toolBarColor;
57
 
61
 
58
     @NonNull
62
     @NonNull
59
     public List<Button> getButtons() {
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
     public Screen(ReadableMap screen) {
67
     public Screen(ReadableMap screen) {
68
         navigatorId = getString(screen, KEY_NAVIGATOR_ID);
72
         navigatorId = getString(screen, KEY_NAVIGATOR_ID);
69
         navigatorEventId = getString(screen, KEY_NAVIGATOR_EVENT_ID);
73
         navigatorEventId = getString(screen, KEY_NAVIGATOR_EVENT_ID);
70
         icon = getInt(screen, KEY_ICON);
74
         icon = getInt(screen, KEY_ICON);
71
-
75
+        if(screen.hasKey(KEY_PROPS)) {
76
+            passedProps = ((ReadableNativeMap) screen.getMap(KEY_PROPS)).toHashMap();
77
+        }
72
         buttons = getButtons(screen);
78
         buttons = getButtons(screen);
73
         setToolbarStyle(screen);
79
         setToolbarStyle(screen);
74
     }
80
     }
75
 
81
 
76
     private ArrayList<Button> getButtons(ReadableMap screen) {
82
     private ArrayList<Button> getButtons(ReadableMap screen) {
77
-        ArrayList<Button> ret = null;
83
+        ArrayList<Button> ret = new ArrayList<>();
78
         if (screen.hasKey(KEY_RIGHT_BUTTONS)) {
84
         if (screen.hasKey(KEY_RIGHT_BUTTONS)) {
79
-            ret = new ArrayList<>();
80
             ReadableArray rightButtons = screen.getArray(KEY_RIGHT_BUTTONS);
85
             ReadableArray rightButtons = screen.getArray(KEY_RIGHT_BUTTONS);
81
             for (int i = 0; i < rightButtons.size(); i++) {
86
             for (int i = 0; i < rightButtons.size(); i++) {
82
                 ret.add(new Button(rightButtons.getMap(i)));
87
                 ret.add(new Button(rightButtons.getMap(i)));

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

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

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

39
           screen: {
39
           screen: {
40
             screen: 'example.LoginScreen',
40
             screen: 'example.LoginScreen',
41
             title: 'Login',
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
         return;
66
         return;
52
               icon: require('../img/one.png'),
73
               icon: require('../img/one.png'),
53
               selectedIcon: require('../img/one_selected.png'),
74
               selectedIcon: require('../img/one_selected.png'),
54
               title: 'Screen One',
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
               label: 'Two',
91
               label: 'Two',
60
               icon: require('../img/two.png'),
93
               icon: require('../img/two.png'),
61
               selectedIcon: require('../img/two_selected.png'),
94
               selectedIcon: require('../img/two_selected.png'),
62
               title: 'Screen Two',
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
           animationType: 'slide-down',
111
           animationType: 'slide-down',

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

1
-import React, {Component} from 'react';
1
+import React, {Component, PropTypes} from 'react';
2
 import {
2
 import {
3
   Text,
3
   Text,
4
   View,
4
   View,
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
   constructor(props) {
46
   constructor(props) {
40
     super(props);
47
     super(props);
41
     // if you want to listen on navigator events, set this up
48
     // if you want to listen on navigator events, set this up
42
     this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
49
     this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
43
   }
50
   }
51
+
44
   onNavigatorEvent(event) {
52
   onNavigatorEvent(event) {
45
     switch (event.id) {
53
     switch (event.id) {
46
       case 'edit':
54
       case 'edit':
75
         <TouchableOpacity onPress={ this.onShowModalPress.bind(this) }>
83
         <TouchableOpacity onPress={ this.onShowModalPress.bind(this) }>
76
           <Text style={styles.button}>Modal Screen</Text>
84
           <Text style={styles.button}>Modal Screen</Text>
77
         </TouchableOpacity>
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
       </View>
91
       </View>
79
     );
92
     );
80
   }
93
   }
86
   onPushPress() {
99
   onPushPress() {
87
     this.props.navigator.push({
100
     this.props.navigator.push({
88
       title: "More",
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
   onShowModalPress() {
118
   onShowModalPress() {
94
     this.props.navigator.showModal({
119
     this.props.navigator.showModal({
95
       title: "Modal Screen",
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
-import React, {Component} from 'react';
1
+import React, {Component, PropTypes} from 'react';
2
 import {
2
 import {
3
   Text,
3
   Text,
4
   View,
4
   View,
12
 
12
 
13
 // this is a traditional React component connected to the redux store
13
 // this is a traditional React component connected to the redux store
14
 class LoginScreen extends Component {
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
   constructor(props) {
22
   constructor(props) {
16
     super(props);
23
     super(props);
24
+    console.log(props);
17
   }
25
   }
26
+
18
   render() {
27
   render() {
19
     return (
28
     return (
20
       <View style={{flex: 1, padding: 20}}>
29
       <View style={{flex: 1, padding: 20}}>
31
           <Text style={styles.button}>Login</Text>
40
           <Text style={styles.button}>Login</Text>
32
         </TouchableOpacity>
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
       </View>
49
       </View>
35
     );
50
     );
36
   }
51
   }

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

1
-import React, {Component} from 'react';
1
+import React, {Component, PropTypes} from 'react';
2
 import {
2
 import {
3
   Text,
3
   Text,
4
   View,
4
   View,
23
     tabIndicatorColor: '#FF4081'
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
   constructor(props) {
32
   constructor(props) {
27
     super(props);
33
     super(props);
28
     this.bgColor = this.getRandomColor();
34
     this.bgColor = this.getRandomColor();
72
 
78
 
73
         <TextInput style={{height: 40, borderColor: 'gray', borderWidth: 1}}/>
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
       </View>
86
       </View>
76
     );
87
     );
77
   }
88
   }
83
   onPushPress() {
94
   onPushPress() {
84
     this.props.navigator.push({
95
     this.props.navigator.push({
85
       title: "More",
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
   onShowModalPress() {
117
   onShowModalPress() {
95
     this.props.navigator.showModal({
118
     this.props.navigator.showModal({
96
       title: "Modal Screen",
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
-import React, {Component} from 'react';
1
+import React, {Component, PropTypes} from 'react';
2
 import {
2
 import {
3
   Text,
3
   Text,
4
   Image,
4
   Image,
18
     drawUnderTabBar: true,
18
     drawUnderTabBar: true,
19
     navBarTranslucent: true
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
   constructor(props) {
28
   constructor(props) {
22
     super(props);
29
     super(props);
23
     this.buttonsCounter = 0;
30
     this.buttonsCounter = 0;
38
             <Text style={styles.button}>Increment Counter</Text>
45
             <Text style={styles.button}>Increment Counter</Text>
39
           </TouchableOpacity>
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
         </View>
53
         </View>
42
 
54
 
43
       </ScrollView>
55
       </ScrollView>