Browse Source

Merge branch 'Android_implementation'

Guy Carmeli 8 years ago
parent
commit
4bf63425a8
56 changed files with 2223 additions and 197 deletions
  1. 34
    0
      android/app/build.gradle
  2. 9
    0
      android/app/src/main/AndroidManifest.xml
  3. 309
    0
      android/app/src/main/java/com/reactnativenavigation/activities/BaseReactActivity.java
  4. 44
    0
      android/app/src/main/java/com/reactnativenavigation/activities/RootActivity.java
  5. 70
    0
      android/app/src/main/java/com/reactnativenavigation/activities/SingleScreenActivity.java
  6. 86
    0
      android/app/src/main/java/com/reactnativenavigation/activities/TabActivity.java
  7. 124
    0
      android/app/src/main/java/com/reactnativenavigation/adapters/ViewPagerAdapter.java
  8. 112
    0
      android/app/src/main/java/com/reactnativenavigation/core/RctManager.java
  9. 120
    0
      android/app/src/main/java/com/reactnativenavigation/core/objects/Button.java
  10. 21
    0
      android/app/src/main/java/com/reactnativenavigation/core/objects/JsonObject.java
  11. 48
    0
      android/app/src/main/java/com/reactnativenavigation/core/objects/Screen.java
  12. 99
    0
      android/app/src/main/java/com/reactnativenavigation/modules/RctActivityModule.java
  13. 36
    0
      android/app/src/main/java/com/reactnativenavigation/packages/RnnPackage.java
  14. 14
    0
      android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java
  15. 32
    0
      android/app/src/main/java/com/reactnativenavigation/utils/ContextProvider.java
  16. 24
    0
      android/app/src/main/java/com/reactnativenavigation/utils/ReflectionUtils.java
  17. 54
    0
      android/app/src/main/java/com/reactnativenavigation/utils/ResourceDrawableIdHelper.java
  18. 40
    0
      android/app/src/main/java/com/reactnativenavigation/views/RctView.java
  19. 123
    0
      android/app/src/main/java/com/reactnativenavigation/views/RnnToolBar.java
  20. 80
    0
      android/app/src/main/java/com/reactnativenavigation/views/ScreenStack.java
  21. 24
    0
      android/app/src/main/res/layout/single_screen_activity.xml
  22. 32
    0
      android/app/src/main/res/layout/tab_activity.xml
  23. 4
    0
      android/app/src/main/res/values/ids.xml
  24. 24
    0
      android/build.gradle
  25. 20
    0
      android/gradle.properties
  26. 134
    0
      android/react-native-navigation.iml
  27. 19
    0
      android/react-navtive-navigation.iml
  28. 3
    0
      example-redux/android/app/build.gradle
  29. 1
    1
      example-redux/android/app/react.gradle
  30. 2
    35
      example-redux/android/app/src/main/java/com/exampleredux/MainActivity.java
  31. 3
    0
      example-redux/android/settings.gradle
  32. 2
    44
      example-redux/index.android.js
  33. 5
    2
      example-redux/src/app.js
  34. 15
    7
      example-redux/src/screens/FirstTabScreen.js
  35. 1
    2
      example-redux/src/screens/LoginScreen.js
  36. 17
    2
      example-redux/src/screens/PushedScreen.js
  37. 1
    1
      example-redux/src/screens/SecondTabScreen.js
  38. 6
    3
      example/android/app/build.gradle
  39. 1
    1
      example/android/app/react.gradle
  40. 14
    14
      example/android/app/src/main/AndroidManifest.xml
  41. 6
    28
      example/android/app/src/main/java/com/example/MainActivity.java
  42. 3
    0
      example/android/settings.gradle
  43. 1
    45
      example/index.android.js
  44. 19
    0
      example/src/app.js
  45. 111
    0
      example/src/screens/FirstTabScreen.android.js
  46. 0
    0
      example/src/screens/FirstTabScreen.ios.js
  47. 133
    0
      example/src/screens/SecondTabScreen.android.js
  48. 0
    0
      example/src/screens/SecondTabScreen.ios.js
  49. 20
    0
      example/src/screens/index.android.js
  50. 0
    0
      example/src/screens/index.ios.js
  51. 3
    1
      index.js
  52. 7
    4
      src/Navigation.js
  53. 28
    2
      src/Screen.js
  54. 2
    0
      src/modules/RctActivity.js
  55. 80
    0
      src/platformSpecific.android.js
  56. 3
    5
      src/platformSpecific.ios.js

+ 34
- 0
android/app/build.gradle View File

@@ -0,0 +1,34 @@
1
+apply plugin: 'com.android.library'
2
+
3
+android {
4
+    compileSdkVersion 23
5
+    buildToolsVersion "23.0.1"
6
+
7
+    defaultConfig {
8
+        minSdkVersion 16
9
+        targetSdkVersion 22
10
+        versionCode 1
11
+        versionName "1.0"
12
+    }
13
+    defaultPublishConfig 'release'
14
+    publishNonDefault true
15
+    productFlavors {
16
+        library {
17
+        }
18
+    }
19
+    buildTypes {
20
+        release {
21
+            minifyEnabled false
22
+        }
23
+        debug {
24
+            minifyEnabled false
25
+        }
26
+    }
27
+}
28
+
29
+dependencies {
30
+    compile fileTree(dir: "libs", include: ["*.jar"])
31
+    compile "com.android.support:appcompat-v7:23.0.1"
32
+    compile 'com.android.support:design:23.1.1'
33
+    compile "com.facebook.react:react-native:+"  // From node_modules
34
+}

+ 9
- 0
android/app/src/main/AndroidManifest.xml View File

@@ -0,0 +1,9 @@
1
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+    package="com.reactnativenavigation">
3
+
4
+    <application>
5
+        <activity android:name="com.reactnativenavigation.activities.TabActivity" />
6
+        <activity android:name="com.reactnativenavigation.activities.SingleScreenActivity" />
7
+    </application>
8
+
9
+</manifest>

+ 309
- 0
android/app/src/main/java/com/reactnativenavigation/activities/BaseReactActivity.java View File

@@ -0,0 +1,309 @@
1
+package com.reactnativenavigation.activities;
2
+
3
+import android.content.Intent;
4
+import android.os.Build;
5
+import android.os.Bundle;
6
+import android.os.Handler;
7
+import android.provider.Settings;
8
+import android.support.annotation.CallSuper;
9
+import android.support.v7.app.AppCompatActivity;
10
+import android.util.Log;
11
+import android.view.KeyEvent;
12
+import android.view.Menu;
13
+import android.view.MenuItem;
14
+import android.widget.EditText;
15
+import android.widget.Toast;
16
+
17
+import com.facebook.common.logging.FLog;
18
+import com.facebook.react.ReactInstanceManager;
19
+import com.facebook.react.ReactPackage;
20
+import com.facebook.react.ReactRootView;
21
+import com.facebook.react.bridge.Arguments;
22
+import com.facebook.react.bridge.WritableMap;
23
+import com.facebook.react.common.ReactConstants;
24
+import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
25
+import com.facebook.react.shell.MainReactPackage;
26
+import com.reactnativenavigation.BuildConfig;
27
+import com.reactnativenavigation.core.RctManager;
28
+import com.reactnativenavigation.core.objects.Button;
29
+import com.reactnativenavigation.core.objects.Screen;
30
+import com.reactnativenavigation.packages.RnnPackage;
31
+import com.reactnativenavigation.utils.ContextProvider;
32
+import com.reactnativenavigation.views.RnnToolBar;
33
+
34
+import java.util.Arrays;
35
+import java.util.List;
36
+
37
+import javax.annotation.Nullable;
38
+
39
+/**
40
+ * Base Activity for React Native applications.
41
+ */
42
+public abstract class BaseReactActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
43
+
44
+    private static final String TAG = "BaseReactActivity";
45
+    private static final String REDBOX_PERMISSION_MESSAGE =
46
+            "Overlay permissions needs to be granted in order for react native apps to run in dev mode";
47
+
48
+    @Nullable
49
+    protected ReactInstanceManager mReactInstanceManager;
50
+    private boolean mDoRefresh = false;
51
+    private Menu mMenu;
52
+    protected RnnToolBar mToolbar;
53
+
54
+    /**
55
+     * Returns the name of the bundle in assets. If this is null, and no file path is specified for
56
+     * the bundle, the app will only work with {@code getUseDeveloperSupport} enabled and will
57
+     * always try to load the JS bundle from the packager server.
58
+     * e.g. "index.android.bundle"
59
+     */
60
+    @Nullable
61
+    public String getBundleAssetName() {
62
+        return "index.android.bundle";
63
+    }
64
+
65
+    /**
66
+     * Returns a custom path of the bundle file. This is used in cases the bundle should be loaded
67
+     * from a custom path. By default it is loaded from Android assets, from a path specified
68
+     * by {getBundleAssetName}.
69
+     * e.g. "file://sdcard/myapp_cache/index.android.bundle"
70
+     */
71
+    @Nullable
72
+    public String getJSBundleFile() {
73
+        return null;
74
+    }
75
+
76
+    /**
77
+     * Returns the name of the main module. Determines the URL used to fetch the JS bundle
78
+     * from the packager server. It is only used when dev support is enabled.
79
+     * This is the first file to be executed once the {@link ReactInstanceManager} is created.
80
+     * e.g. "index.android"
81
+     */
82
+    public String getJSMainModuleName() {
83
+        return "index.android";
84
+    }
85
+
86
+    /**
87
+     * Returns the launchOptions which will be passed to the {@link ReactInstanceManager}
88
+     * when the application is started. By default, this will return null and an empty
89
+     * object will be passed to your top level component as its initial props.
90
+     * If your React Native application requires props set outside of JS, override
91
+     * this method to return the Android.os.Bundle of your desired initial props.
92
+     */
93
+    @Nullable
94
+    protected Bundle getLaunchOptions() {
95
+        return null;
96
+    }
97
+
98
+    /**
99
+     * Returns the name of the main component registered from JavaScript.
100
+     * This is used to schedule rendering of the component.
101
+     * e.g. "MoviesApp"
102
+     */
103
+    protected String getMainComponentName() {
104
+        return "";
105
+    }
106
+
107
+    /**
108
+     * Returns whether dev mode should be enabled. This enables e.g. the dev menu.
109
+     */
110
+    public boolean getUseDeveloperSupport() {
111
+        return BuildConfig.DEBUG;
112
+    }
113
+
114
+    /**
115
+     * Returns a list of {@link ReactPackage} used by the app.
116
+     * You'll most likely want to return at least the {@code MainReactPackage}.
117
+     * If your app uses additional views or modules besides the default ones,
118
+     * you'll want to include more packages here.
119
+     */
120
+    public List<ReactPackage> getPackages() {
121
+        return Arrays.asList(
122
+                new MainReactPackage(),
123
+                new RnnPackage()
124
+        );
125
+    }
126
+
127
+    /**
128
+     * A subclass may override this method if it needs to use a custom {@link ReactRootView}.
129
+     */
130
+    protected ReactRootView createRootView() {
131
+        return new ReactRootView(this);
132
+    }
133
+
134
+    @Override
135
+    protected void onCreate(Bundle savedInstanceState) {
136
+        super.onCreate(savedInstanceState);
137
+        mReactInstanceManager = createReactInstanceManager();
138
+        handleOnCreate();
139
+    }
140
+
141
+    /**
142
+     * A subclass may override this method if it needs to use a custom instance.
143
+     */
144
+    protected ReactInstanceManager createReactInstanceManager() {
145
+        return getReactInstanceManager();
146
+    }
147
+
148
+    protected ReactInstanceManager getReactInstanceManager() {
149
+        RctManager rctManager = RctManager.getInstance();
150
+        if (!rctManager.isInitialized()) {
151
+            rctManager.init(this);
152
+        }
153
+        return rctManager.getReactInstanceManager();
154
+    }
155
+
156
+    protected void handleOnCreate() {
157
+        if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
158
+            // Get permission to show redbox in dev builds.
159
+            if (!Settings.canDrawOverlays(this)) {
160
+                Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
161
+                startActivity(serviceIntent);
162
+                FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
163
+                Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
164
+            }
165
+        }
166
+
167
+        ReactRootView mReactRootView = createRootView();
168
+        mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
169
+        setContentView(mReactRootView);
170
+    }
171
+
172
+    @Override
173
+    protected void onResume() {
174
+        super.onResume();
175
+        ContextProvider.setActivityContext(this);
176
+
177
+        if (mReactInstanceManager != null) {
178
+            mReactInstanceManager.onResume(this, this);
179
+        }
180
+    }
181
+
182
+    @Override
183
+    protected void onPause() {
184
+        super.onPause();
185
+
186
+        if (mReactInstanceManager != null) {
187
+            mReactInstanceManager.onPause();
188
+        }
189
+
190
+        ContextProvider.clearActivityContext();
191
+    }
192
+
193
+    @Override
194
+    protected void onDestroy() {
195
+        super.onDestroy();
196
+
197
+        // Destroy react instance manager only if there are no resumed react activities
198
+        BaseReactActivity activity = ContextProvider.getActivityContext();
199
+        if (mReactInstanceManager != null && (activity == null || activity.isFinishing())) {
200
+            Log.i(TAG, "Destroying ReactInstanceManager");
201
+            mReactInstanceManager.onDestroy();
202
+        } else {
203
+            Log.d(TAG, "Not destroying ReactInstanceManager");
204
+        }
205
+    }
206
+
207
+    @CallSuper
208
+    public void push(Screen screen) {
209
+        if (mToolbar != null &&
210
+            getCurrentNavigatorId().equals(screen.navigatorId) &&
211
+            getScreenStackSize() >= 1) {
212
+            mToolbar.showBackButton();
213
+        }
214
+    }
215
+
216
+    @CallSuper
217
+    public Screen pop(String navigatorId) {
218
+        if (mToolbar != null &&
219
+            getCurrentNavigatorId().equals(navigatorId) &&
220
+            getScreenStackSize() <= 2) {
221
+            mToolbar.hideBackButton();
222
+        }
223
+        return null;
224
+    }
225
+
226
+    protected abstract String getCurrentNavigatorId();
227
+
228
+    protected abstract Screen getCurrentScreen();
229
+
230
+    public abstract int getScreenStackSize();
231
+
232
+    @Override
233
+    public boolean onCreateOptionsMenu(Menu menu) {
234
+        mMenu = menu;
235
+        return super.onCreateOptionsMenu(menu);
236
+    }
237
+
238
+    @Override
239
+    public boolean onOptionsItemSelected(MenuItem item) {
240
+        if (item.getItemId() == android.R.id.home) {
241
+            onBackPressed();
242
+        } else {
243
+            String eventId = Button.getButtonEventId(item);
244
+            assert eventId != null;
245
+
246
+            WritableMap params = Arguments.createMap();
247
+            RctManager.getInstance().sendEvent(eventId, getCurrentScreen(), params);
248
+        }
249
+        return super.onOptionsItemSelected(item);
250
+    }
251
+
252
+    public Menu getMenu() {
253
+        return mMenu;
254
+    }
255
+
256
+    @Override
257
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
258
+        if (mReactInstanceManager != null) {
259
+            mReactInstanceManager.onActivityResult(requestCode, resultCode, data);
260
+        }
261
+    }
262
+
263
+    @Override
264
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
265
+        if (mReactInstanceManager != null &&
266
+            mReactInstanceManager.getDevSupportManager().getDevSupportEnabled()) {
267
+            if (keyCode == KeyEvent.KEYCODE_MENU) {
268
+                mReactInstanceManager.showDevOptionsDialog();
269
+                return true;
270
+            }
271
+            if (keyCode == KeyEvent.KEYCODE_R && !(getCurrentFocus() instanceof EditText)) {
272
+                // Enable double-tap-R-to-reload
273
+                if (mDoRefresh) {
274
+                    mReactInstanceManager.getDevSupportManager().handleReloadJS();
275
+                    mDoRefresh = false;
276
+                } else {
277
+                    mDoRefresh = true;
278
+                    new Handler().postDelayed(
279
+                            new Runnable() {
280
+                                @Override
281
+                                public void run() {
282
+                                    mDoRefresh = false;
283
+                                }
284
+                            },
285
+                            200);
286
+                }
287
+            }
288
+        }
289
+        return super.onKeyUp(keyCode, event);
290
+    }
291
+
292
+    @Override
293
+    public void onBackPressed() {
294
+        if (getScreenStackSize() > 1) {
295
+            pop(getCurrentNavigatorId());
296
+        } else {
297
+            if (mReactInstanceManager != null) {
298
+                mReactInstanceManager.onBackPressed();
299
+            } else {
300
+                super.onBackPressed();
301
+            }
302
+        }
303
+    }
304
+
305
+    @Override
306
+    public void invokeDefaultOnBackPressed() {
307
+        super.onBackPressed();
308
+    }
309
+}

+ 44
- 0
android/app/src/main/java/com/reactnativenavigation/activities/RootActivity.java View File

@@ -0,0 +1,44 @@
1
+package com.reactnativenavigation.activities;
2
+
3
+import com.facebook.react.ReactRootView;
4
+import com.reactnativenavigation.core.objects.Screen;
5
+
6
+/**
7
+ * This activity is used to start the JS execution where we load our actual app/screens (index.android.js)
8
+ * Triggering react context initialization execute global code before any {@link ReactRootView}
9
+ * are displayed.
10
+ * <p>Only your MainActivity or activities with {@code action.MAIN} and {@code category.LAUNCHER}
11
+ * should extend this activity.
12
+ * Created by guyc on 13/04/16.
13
+ */
14
+public class RootActivity extends BaseReactActivity {
15
+
16
+    @Override
17
+    protected void handleOnCreate() {
18
+        // Trigger react context initialization, global javascript code will now execute
19
+        getReactInstanceManager().createReactContextInBackground();
20
+    }
21
+
22
+    @Override
23
+    protected void onPause() {
24
+        super.onPause();
25
+        finish();
26
+    }
27
+
28
+     // No need to implement stack interface since this activity is only used to start other
29
+    // activities such as TabActivity or SingleScreenActivity.
30
+    @Override
31
+    protected Screen getCurrentScreen() {
32
+        return null;
33
+    }
34
+
35
+    @Override
36
+    public String getCurrentNavigatorId() {
37
+        return null;
38
+    }
39
+
40
+    @Override
41
+    public int getScreenStackSize() {
42
+        return 0;
43
+    }
44
+}

+ 70
- 0
android/app/src/main/java/com/reactnativenavigation/activities/SingleScreenActivity.java View File

@@ -0,0 +1,70 @@
1
+package com.reactnativenavigation.activities;
2
+
3
+import android.support.v7.widget.Toolbar;
4
+import android.widget.FrameLayout;
5
+
6
+import com.reactnativenavigation.R;
7
+import com.reactnativenavigation.core.RctManager;
8
+import com.reactnativenavigation.core.objects.Screen;
9
+import com.reactnativenavigation.views.ScreenStack;
10
+
11
+/**
12
+ * Created by guyc on 05/04/16.
13
+ */
14
+public class SingleScreenActivity extends BaseReactActivity {
15
+
16
+    public static final String EXTRA_SCREEN = "extraScreen";
17
+
18
+    private Toolbar mToolbar;
19
+    private ScreenStack mScreenStack;
20
+    private String mNavigatorId;
21
+
22
+    @Override
23
+    protected void handleOnCreate() {
24
+        mReactInstanceManager = RctManager.getInstance().getReactInstanceManager();
25
+
26
+        setContentView(R.layout.single_screen_activity);
27
+        mToolbar = (Toolbar) findViewById(R.id.toolbar);
28
+
29
+        Screen screen = (Screen) getIntent().getSerializableExtra(EXTRA_SCREEN);
30
+        mNavigatorId = screen.navigatorId;
31
+        setupToolbar(screen.title);
32
+
33
+        mScreenStack = new ScreenStack(this);
34
+        FrameLayout contentFrame = (FrameLayout) findViewById(R.id.contentFrame);
35
+        contentFrame.addView(mScreenStack);
36
+        mScreenStack.push(screen);
37
+    }
38
+
39
+    private void setupToolbar(String title) {
40
+        mToolbar.setTitle(title);
41
+        setSupportActionBar(mToolbar);
42
+    }
43
+    
44
+    @Override
45
+    public void push(Screen screen) {
46
+        super.push(screen);
47
+        mScreenStack.push(screen);
48
+    }
49
+
50
+    @Override
51
+    public Screen pop(String navigatorId) {
52
+        super.pop(navigatorId);
53
+        return mScreenStack.pop();
54
+    }
55
+
56
+    @Override
57
+    public String getCurrentNavigatorId() {
58
+        return mNavigatorId;
59
+    }
60
+
61
+    @Override
62
+    protected Screen getCurrentScreen() {
63
+        return mScreenStack.peek();
64
+    }
65
+
66
+    @Override
67
+    public int getScreenStackSize() {
68
+        return mScreenStack.getStackSize();
69
+    }
70
+}

+ 86
- 0
android/app/src/main/java/com/reactnativenavigation/activities/TabActivity.java View File

@@ -0,0 +1,86 @@
1
+package com.reactnativenavigation.activities;
2
+
3
+import android.support.design.widget.TabLayout;
4
+import android.support.v4.view.ViewPager;
5
+import android.view.Menu;
6
+
7
+import com.reactnativenavigation.R;
8
+import com.reactnativenavigation.adapters.ViewPagerAdapter;
9
+import com.reactnativenavigation.core.RctManager;
10
+import com.reactnativenavigation.core.objects.Screen;
11
+import com.reactnativenavigation.views.RnnToolBar;
12
+
13
+import java.util.ArrayList;
14
+
15
+/**
16
+ * Created by guyc on 02/04/16.
17
+ */
18
+public class TabActivity extends BaseReactActivity {
19
+    public static final String EXTRA_SCREENS = "extraScreens";
20
+
21
+    private TabLayout mTabLayout;
22
+    private ViewPager mViewPager;
23
+    private ViewPagerAdapter mAdapter;
24
+
25
+    @Override
26
+    protected void handleOnCreate() {
27
+        mReactInstanceManager = RctManager.getInstance().getReactInstanceManager();
28
+
29
+        setContentView(R.layout.tab_activity);
30
+        mToolbar = (RnnToolBar) findViewById(R.id.toolbar);
31
+        mTabLayout = (TabLayout) findViewById(R.id.tabLayout);
32
+        mViewPager = (ViewPager) findViewById(R.id.viewPager);
33
+
34
+        ArrayList<Screen> screens = (ArrayList<Screen>) getIntent().getSerializableExtra(EXTRA_SCREENS);
35
+
36
+        setupToolbar(screens);
37
+        setupViewPager(screens);
38
+    }
39
+
40
+    private void setupToolbar(ArrayList<Screen> screens) {
41
+        setSupportActionBar(mToolbar);
42
+        mToolbar.setScreens(screens);
43
+    }
44
+
45
+    private void setupViewPager(ArrayList<Screen> screens) {
46
+        mAdapter = new ViewPagerAdapter(this, mViewPager, mToolbar, screens);
47
+        mViewPager.setAdapter(mAdapter);
48
+        mTabLayout.setupWithViewPager(mViewPager);
49
+        mTabLayout.setOnTabSelectedListener(mAdapter);
50
+        mAdapter.notifyDataSetChanged();
51
+    }
52
+
53
+    @Override
54
+    public boolean onCreateOptionsMenu(Menu menu) {
55
+        boolean ret = super.onCreateOptionsMenu(menu);
56
+        mToolbar.handleOnCreateOptionsMenuAsync();
57
+        return ret;
58
+    }
59
+
60
+    @Override
61
+    public void push(Screen screen) {
62
+        super.push(screen);
63
+        mAdapter.push(screen);
64
+    }
65
+
66
+    @Override
67
+    public Screen pop(String navigatorId) {
68
+        super.pop(navigatorId);
69
+        return mAdapter.pop(navigatorId);
70
+    }
71
+
72
+    @Override
73
+    protected Screen getCurrentScreen() {
74
+        return mAdapter.peek(getCurrentNavigatorId());
75
+    }
76
+
77
+    @Override
78
+    protected String getCurrentNavigatorId() {
79
+        return mAdapter.getNavigatorId(mViewPager.getCurrentItem());
80
+    }
81
+
82
+    @Override
83
+    public int getScreenStackSize() {
84
+        return mAdapter.getStackSizeForNavigatorId(getCurrentNavigatorId());
85
+    }
86
+}

+ 124
- 0
android/app/src/main/java/com/reactnativenavigation/adapters/ViewPagerAdapter.java View File

@@ -0,0 +1,124 @@
1
+package com.reactnativenavigation.adapters;
2
+
3
+import android.support.design.widget.TabLayout;
4
+import android.support.v4.view.PagerAdapter;
5
+import android.support.v4.view.ViewPager;
6
+import android.view.View;
7
+import android.view.ViewGroup;
8
+
9
+import com.facebook.react.bridge.Arguments;
10
+import com.facebook.react.bridge.WritableMap;
11
+import com.reactnativenavigation.activities.BaseReactActivity;
12
+import com.reactnativenavigation.core.RctManager;
13
+import com.reactnativenavigation.core.objects.Screen;
14
+import com.reactnativenavigation.views.RnnToolBar;
15
+import com.reactnativenavigation.views.ScreenStack;
16
+
17
+import java.util.ArrayList;
18
+import java.util.HashMap;
19
+import java.util.Map;
20
+
21
+/**
22
+ * Created by guyc on 02/04/16.
23
+ */
24
+public class ViewPagerAdapter extends PagerAdapter implements TabLayout.OnTabSelectedListener {
25
+
26
+    private static final String EVENT_ON_TAB_SELECTED = "OnTabSelected";
27
+
28
+    private ViewPager mViewPager;
29
+    private RnnToolBar mToolbar;
30
+    private final ArrayList<ScreenStack> mScreenStacks;
31
+    private final ArrayList<String> mNavigatorIds;
32
+    private final Map<String, ScreenStack> mStackByNavigatorId;
33
+
34
+
35
+    public ViewPagerAdapter(BaseReactActivity context, ViewPager viewPager, RnnToolBar toolbar,
36
+                            ArrayList<Screen> screens) {
37
+        mViewPager = viewPager;
38
+        mToolbar = toolbar;
39
+        mScreenStacks = new ArrayList<>();
40
+        mNavigatorIds = new ArrayList<>();
41
+        mStackByNavigatorId = new HashMap<>();
42
+        for (Screen screen : screens) {
43
+            ScreenStack stack = new ScreenStack(context);
44
+            stack.push(screen);
45
+            mScreenStacks.add(stack);
46
+            mNavigatorIds.add(screen.navigatorId);
47
+            mStackByNavigatorId.put(screen.navigatorId, stack);
48
+        }
49
+    }
50
+
51
+    public void push(Screen screen) {
52
+        ScreenStack stack = mStackByNavigatorId.get(screen.navigatorId);
53
+        stack.push(screen);
54
+    }
55
+
56
+    public Screen pop(String navigatorId) {
57
+        ScreenStack stack = mStackByNavigatorId.get(navigatorId);
58
+        return stack != null ? stack.pop() : null;
59
+    }
60
+
61
+    public Screen peek(String navigatorId) {
62
+        ScreenStack stack = mStackByNavigatorId.get(navigatorId);
63
+        return stack != null ? stack.peek() : null;
64
+    }
65
+
66
+    @Override
67
+    public Object instantiateItem(ViewGroup container, int position) {
68
+        ScreenStack view = mScreenStacks.get(position);
69
+        container.addView(view);
70
+        return view;
71
+    }
72
+
73
+    @Override
74
+    public void destroyItem(ViewGroup container, int position, Object view) {
75
+        container.removeView((View) view);
76
+    }
77
+
78
+    @Override
79
+    public int getCount() {
80
+        return mScreenStacks.size();
81
+    }
82
+
83
+    @Override
84
+    public boolean isViewFromObject(View view, Object object) {
85
+        return view == object;
86
+    }
87
+
88
+    @Override
89
+    public CharSequence getPageTitle(int position) {
90
+        return mScreenStacks.get(position).peek().title;
91
+    }
92
+
93
+    @Override
94
+    public void onTabSelected(TabLayout.Tab tab) {
95
+        // Set the viewPager's current item
96
+        int position = tab.getPosition();
97
+        mViewPager.setCurrentItem(position);
98
+
99
+        mToolbar.setupToolbarButtonsAsync(mScreenStacks.get(position).peek());
100
+
101
+        // Send tab selected event
102
+        WritableMap params = Arguments.createMap();
103
+        Screen screen = mScreenStacks.get(position).peek();
104
+        RctManager.getInstance().sendEvent(EVENT_ON_TAB_SELECTED, screen, params);
105
+    }
106
+
107
+    @Override
108
+    public void onTabUnselected(TabLayout.Tab tab) {
109
+
110
+    }
111
+
112
+    @Override
113
+    public void onTabReselected(TabLayout.Tab tab) {
114
+
115
+    }
116
+
117
+    public String getNavigatorId(int position) {
118
+        return mNavigatorIds.get(position);
119
+    }
120
+
121
+    public int getStackSizeForNavigatorId(String activeNavigatorID) {
122
+        return mStackByNavigatorId.get(activeNavigatorID).getStackSize();
123
+    }
124
+}

+ 112
- 0
android/app/src/main/java/com/reactnativenavigation/core/RctManager.java View File

@@ -0,0 +1,112 @@
1
+package com.reactnativenavigation.core;
2
+
3
+import android.app.Application;
4
+
5
+import com.facebook.react.LifecycleState;
6
+import com.facebook.react.ReactInstanceManager;
7
+import com.facebook.react.ReactPackage;
8
+import com.facebook.react.bridge.ReactContext;
9
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
10
+import com.facebook.react.bridge.WritableMap;
11
+import com.reactnativenavigation.activities.BaseReactActivity;
12
+import com.reactnativenavigation.core.objects.Screen;
13
+
14
+import static com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
15
+
16
+/**
17
+ * Created by guyc on 22/02/16.
18
+ */
19
+public class RctManager {
20
+    private static final String KEY_EVENT_ID = "id";
21
+    private static RctManager sInstance;
22
+
23
+    private ReactInstanceManager mReactManager;
24
+
25
+    private RctManager() {
26
+        // Singleton
27
+    }
28
+
29
+    public static synchronized RctManager getInstance() {
30
+        if (sInstance == null) {
31
+            sInstance = new RctManager();
32
+        }
33
+        return sInstance;
34
+    }
35
+
36
+    public ReactInstanceManager getReactInstanceManager() {
37
+        return mReactManager;
38
+    }
39
+
40
+    public boolean isInitialized() {
41
+        return mReactManager != null;
42
+    }
43
+
44
+    public void init(BaseReactActivity context) {
45
+        createReactInstanceManager(context);
46
+    }
47
+
48
+    /**
49
+     * Creates a React Instance Manager associated with this component name
50
+     */
51
+    public ReactInstanceManager createReactInstanceManager(BaseReactActivity reactActivity) {
52
+        ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
53
+                .setApplication((Application) reactActivity.getApplicationContext())
54
+                .setJSMainModuleName(reactActivity.getJSMainModuleName())
55
+                .setUseDeveloperSupport(reactActivity.getUseDeveloperSupport())
56
+                .setInitialLifecycleState(LifecycleState.BEFORE_RESUME);
57
+
58
+        for (ReactPackage reactPackage : reactActivity.getPackages()) {
59
+            builder.addPackage(reactPackage);
60
+        }
61
+
62
+        String jsBundleFile = reactActivity.getJSBundleFile();
63
+
64
+        if (jsBundleFile != null) {
65
+            builder.setJSBundleFile(jsBundleFile);
66
+        } else {
67
+            builder.setBundleAssetName(reactActivity.getBundleAssetName());
68
+        }
69
+
70
+        mReactManager = builder.build();
71
+        return mReactManager;
72
+    }
73
+
74
+    public <T extends ReactContextBaseJavaModule> T getNativeModule(Class<T> nativeModuleClass) {
75
+        if (mReactManager == null || mReactManager.getCurrentReactContext() == null) {
76
+            return null;
77
+        }
78
+
79
+        return mReactManager.getCurrentReactContext().getNativeModule(nativeModuleClass);
80
+    }
81
+
82
+    /**
83
+     * Sends an event to JavaScript using <a href="https://facebook.github.io/react-native/docs/native-modules-android.html#sending-events-to-javascript">RCTDeviceEventEmitter</a>
84
+     * @param eventName Name of the event
85
+     * @param params Event params
86
+     * @param screen screen which should receive the event
87
+     */
88
+    public void sendEvent(String eventName, Screen screen, WritableMap params) {
89
+        RCTDeviceEventEmitter eventEmitter = getEventEmitter();
90
+        if (eventEmitter == null) {
91
+            return;
92
+        }
93
+
94
+        params.putString(KEY_EVENT_ID, eventName);
95
+        params.putString(Screen.KEY_NAVIGATOR_EVENT_ID, screen.navigatorEventId);
96
+        eventEmitter.emit(screen.navigatorEventId, params);
97
+    }
98
+
99
+    private RCTDeviceEventEmitter getEventEmitter() {
100
+        if (mReactManager == null) {
101
+            return null;
102
+        }
103
+
104
+        ReactContext currentReactContext = mReactManager.getCurrentReactContext();
105
+        if (currentReactContext == null) {
106
+            return null;
107
+        }
108
+
109
+        return currentReactContext.getJSModule(RCTDeviceEventEmitter.class);
110
+    }
111
+}
112
+

+ 120
- 0
android/app/src/main/java/com/reactnativenavigation/core/objects/Button.java View File

@@ -0,0 +1,120 @@
1
+package com.reactnativenavigation.core.objects;
2
+
3
+import android.content.Context;
4
+import android.graphics.Bitmap;
5
+import android.graphics.BitmapFactory;
6
+import android.graphics.drawable.BitmapDrawable;
7
+import android.graphics.drawable.Drawable;
8
+import android.net.Uri;
9
+import android.view.MenuItem;
10
+
11
+import com.facebook.react.bridge.ReadableMap;
12
+import com.reactnativenavigation.BuildConfig;
13
+import com.reactnativenavigation.utils.ResourceDrawableIdHelper;
14
+
15
+import java.io.Serializable;
16
+import java.net.URL;
17
+import java.util.HashMap;
18
+import java.util.Map;
19
+import java.util.concurrent.atomic.AtomicInteger;
20
+
21
+/**
22
+ * Created by guyc on 08/04/16.
23
+ */
24
+public class Button extends JsonObject implements Serializable {
25
+    private static final long serialVersionUID = -570145217281069067L;
26
+
27
+    public static final String LOCAL_RESOURCE_URI_SCHEME = "res";
28
+    private static final String KEY_ID = "id";
29
+    private static final String KEY_TITLE = "title";
30
+    private static final String KEY_ICON = "icon";
31
+
32
+    private static ResourceDrawableIdHelper sResDrawableIdHelper = new ResourceDrawableIdHelper();
33
+
34
+    public String id;
35
+    public String title;
36
+    private String mIconSource;
37
+
38
+    private static AtomicInteger sAtomicIdGenerator = new AtomicInteger();
39
+    private static Map<String, Integer> sStringToNumericId = new HashMap<>();
40
+
41
+    public Button(ReadableMap button) {
42
+        id = getString(button, KEY_ID);
43
+        title = getString(button, KEY_TITLE, "");
44
+        mIconSource = getString(button, KEY_ICON);
45
+    }
46
+
47
+    public boolean hasIcon() {
48
+        return mIconSource != null;
49
+    }
50
+
51
+    public Drawable getIcon(Context ctx) {
52
+        if (mIconSource == null) {
53
+            return null;
54
+        }
55
+
56
+        try {
57
+            Drawable icon;
58
+            Uri iconUri = getIconUri(ctx);
59
+
60
+            if (LOCAL_RESOURCE_URI_SCHEME.equals(iconUri.getScheme())) {
61
+                icon = sResDrawableIdHelper.getResourceDrawable(ctx, mIconSource);
62
+            } else {
63
+                URL url = new URL(iconUri.toString());
64
+                Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
65
+                icon = new BitmapDrawable(bitmap);
66
+            }
67
+            return icon;
68
+        } catch (Exception e) {
69
+            if (BuildConfig.DEBUG) {
70
+                e.printStackTrace();
71
+            }
72
+        }
73
+        return null;
74
+    }
75
+
76
+    private Uri getIconUri(Context context) {
77
+        Uri ret = null;
78
+        if (mIconSource != null) {
79
+            try {
80
+                ret = Uri.parse(mIconSource);
81
+                // Verify scheme is set, so that relative uri (used by static resources) are not handled.
82
+                if (ret.getScheme() == null) {
83
+                    ret = null;
84
+                }
85
+            } catch (Exception e) {
86
+                // Ignore malformed uri, then attempt to extract resource ID.
87
+            }
88
+            if (ret == null) {
89
+                ret = sResDrawableIdHelper.getResourceDrawableUri(context, mIconSource);
90
+            }
91
+        }
92
+        return ret;
93
+    }
94
+
95
+    public int getItemId() {
96
+        if (sStringToNumericId.containsKey(id)) {
97
+            return sStringToNumericId.get(id);
98
+        }
99
+
100
+        int itemId = sAtomicIdGenerator.addAndGet(1);
101
+        sStringToNumericId.put(id, itemId);
102
+        return itemId;
103
+    }
104
+
105
+    /**
106
+     * Each button has a string id, defined in JS, which is used to identify the button when
107
+     * handling events.
108
+     * @param item Toolbar button
109
+     * @return Returns the event id associated with the given menu item
110
+     */
111
+    public static String getButtonEventId(MenuItem item) {
112
+        for (Map.Entry<String, Integer> entry : sStringToNumericId.entrySet()) {
113
+            if (entry.getValue() == item.getItemId()) {
114
+                return entry.getKey();
115
+            }
116
+        }
117
+
118
+        return null;
119
+    }
120
+}

+ 21
- 0
android/app/src/main/java/com/reactnativenavigation/core/objects/JsonObject.java View File

@@ -0,0 +1,21 @@
1
+package com.reactnativenavigation.core.objects;
2
+
3
+import com.facebook.react.bridge.ReadableMap;
4
+
5
+/**
6
+ * Created by guyc on 08/04/16.
7
+ */
8
+public class JsonObject {
9
+
10
+    protected String getString(ReadableMap map, String key) {
11
+        return getString(map, key, null);
12
+    }
13
+
14
+    protected String getString(ReadableMap map, String key, String defaultValue) {
15
+        return map.hasKey(key) ? map.getString(key) : defaultValue;
16
+    }
17
+
18
+    protected int getInt(ReadableMap map, String key) {
19
+        return map.hasKey(key) ? map.getInt(key) : -1;
20
+    }
21
+}

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

@@ -0,0 +1,48 @@
1
+package com.reactnativenavigation.core.objects;
2
+
3
+import com.facebook.react.bridge.ReadableArray;
4
+import com.facebook.react.bridge.ReadableMap;
5
+
6
+import java.io.Serializable;
7
+import java.util.ArrayList;
8
+import java.util.List;
9
+
10
+/**
11
+ * Created by guyc on 02/04/16.
12
+ */
13
+public class Screen extends JsonObject implements Serializable {
14
+    private static final long serialVersionUID = -1033475305421107791L;
15
+
16
+    private static final String KEY_TITLE = "title";
17
+    private static final String KEY_SCREEN = "screen";
18
+    public static final String KEY_SCREEN_INSTANCE_ID = "screenInstanceID";
19
+    public static final String KEY_NAVIGATOR_ID = "navigatorID";
20
+    public static final String KEY_NAVIGATOR_EVENT_ID = "navigatorEventID";
21
+    private static final String KEY_ICON = "icon";
22
+    private static final String KEY_RIGHT_BUTTONS = "rightButtons";
23
+
24
+    public String title;
25
+    public String screenId;
26
+    public String screenInstanceId;
27
+    public String navigatorId;
28
+    public String navigatorEventId;
29
+    public int icon;
30
+    public List<Button> buttons;
31
+
32
+    public Screen(ReadableMap screen) {
33
+        title = getString(screen, KEY_TITLE);
34
+        screenId = getString(screen, KEY_SCREEN);
35
+        screenInstanceId = getString(screen, KEY_SCREEN_INSTANCE_ID);
36
+        navigatorId = getString(screen, KEY_NAVIGATOR_ID);
37
+        navigatorEventId = getString(screen, KEY_NAVIGATOR_EVENT_ID);
38
+        icon = getInt(screen, KEY_ICON);
39
+
40
+        if (screen.hasKey(KEY_RIGHT_BUTTONS)) {
41
+            buttons = new ArrayList<>();
42
+            ReadableArray rightButtons = screen.getArray(KEY_RIGHT_BUTTONS);
43
+            for (int i = 0; i < rightButtons.size(); i++) {
44
+                buttons.add(new Button(rightButtons.getMap(i)));
45
+            }
46
+        }
47
+    }
48
+}

+ 99
- 0
android/app/src/main/java/com/reactnativenavigation/modules/RctActivityModule.java View File

@@ -0,0 +1,99 @@
1
+package com.reactnativenavigation.modules;
2
+
3
+import android.app.Activity;
4
+import android.content.Intent;
5
+import android.os.Bundle;
6
+
7
+import com.facebook.react.bridge.ReactApplicationContext;
8
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
9
+import com.facebook.react.bridge.ReactMethod;
10
+import com.facebook.react.bridge.ReadableArray;
11
+import com.facebook.react.bridge.ReadableMap;
12
+import com.reactnativenavigation.activities.BaseReactActivity;
13
+import com.reactnativenavigation.activities.SingleScreenActivity;
14
+import com.reactnativenavigation.activities.TabActivity;
15
+import com.reactnativenavigation.core.objects.Screen;
16
+import com.reactnativenavigation.utils.ContextProvider;
17
+
18
+import java.util.ArrayList;
19
+
20
+/**
21
+ * Created by guyc on 10/03/16.
22
+ */
23
+public class RctActivityModule extends ReactContextBaseJavaModule {
24
+    public static final String REACT_CLASS = "RctActivity";
25
+
26
+    public RctActivityModule(ReactApplicationContext reactContext) {
27
+        super(reactContext);
28
+    }
29
+
30
+    @Override
31
+    public String getName() {
32
+        return REACT_CLASS;
33
+    }
34
+
35
+    @ReactMethod
36
+    public void startTabBasedApp(ReadableArray screens) {
37
+        Activity context = ContextProvider.getActivityContext();
38
+        if (context != null && !context.isFinishing()) {
39
+            Intent intent = new Intent(context, TabActivity.class);
40
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
41
+
42
+            Bundle extras = new Bundle();
43
+            extras.putSerializable(TabActivity.EXTRA_SCREENS, createScreens(screens));
44
+            intent.putExtras(extras);
45
+            
46
+            context.startActivity(intent);
47
+        }
48
+    }
49
+
50
+    private ArrayList<Screen> createScreens(ReadableArray screens) {
51
+        ArrayList<Screen> ret = new ArrayList<>();
52
+        for(int i = 0; i < screens.size(); i++) {
53
+            ret.add(new Screen(screens.getMap(i)));
54
+        }
55
+        return ret;
56
+    }
57
+
58
+    @ReactMethod
59
+    public void startSingleScreenApp(ReadableMap screen) {
60
+        BaseReactActivity context = ContextProvider.getActivityContext();
61
+        if (context != null && !context.isFinishing()) {
62
+            Intent intent = new Intent(context, SingleScreenActivity.class);
63
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
64
+
65
+            Bundle extras = new Bundle();
66
+            extras.putSerializable(SingleScreenActivity.EXTRA_SCREEN, new Screen(screen));
67
+            intent.putExtras(extras);
68
+
69
+            context.startActivity(intent);
70
+        }
71
+    }
72
+
73
+    @ReactMethod
74
+    public void navigatorPush(final ReadableMap screen) {
75
+        final BaseReactActivity context = ContextProvider.getActivityContext();
76
+         if (context != null && !context.isFinishing()) {
77
+             context.runOnUiThread(new Runnable() {
78
+                 @Override
79
+                 public void run() {
80
+                     context.push(new Screen(screen));
81
+                 }
82
+             });
83
+        }
84
+    }
85
+
86
+    @ReactMethod
87
+    public void navigatorPop(final ReadableMap navigator) {
88
+        final String navID = navigator.getString("navigatorID");
89
+        final BaseReactActivity context = ContextProvider.getActivityContext();
90
+        if (context != null && !context.isFinishing()) {
91
+            context.runOnUiThread(new Runnable() {
92
+                @Override
93
+                public void run() {
94
+                    context.pop(navID);
95
+                }
96
+            });
97
+        }
98
+    }
99
+}

+ 36
- 0
android/app/src/main/java/com/reactnativenavigation/packages/RnnPackage.java View File

@@ -0,0 +1,36 @@
1
+package com.reactnativenavigation.packages;
2
+
3
+import com.facebook.react.ReactPackage;
4
+import com.facebook.react.bridge.JavaScriptModule;
5
+import com.facebook.react.bridge.NativeModule;
6
+import com.facebook.react.bridge.ReactApplicationContext;
7
+import com.facebook.react.uimanager.ViewManager;
8
+import com.reactnativenavigation.modules.RctActivityModule;
9
+
10
+import java.util.Arrays;
11
+import java.util.Collections;
12
+import java.util.List;
13
+
14
+/**
15
+ *
16
+ * Created by guyc on 07/03/16.
17
+ */
18
+public class RnnPackage implements ReactPackage {
19
+
20
+    @Override
21
+    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
22
+        return Arrays.<NativeModule>asList(
23
+                new RctActivityModule(reactContext)
24
+        );
25
+    }
26
+
27
+    @Override
28
+    public List<Class<? extends JavaScriptModule>> createJSModules() {
29
+        return Collections.emptyList();
30
+    }
31
+
32
+    @Override
33
+    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
34
+        return Collections.emptyList();
35
+    }
36
+}

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

@@ -0,0 +1,14 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import java.util.Collection;
4
+
5
+/**
6
+ * Created by guyc on 11/04/16.
7
+ */
8
+public class CollectionUtils {
9
+
10
+    public static boolean isNullOrEmpty(Collection collection) {
11
+        return collection == null || collection.size() == 0;
12
+    }
13
+
14
+}

+ 32
- 0
android/app/src/main/java/com/reactnativenavigation/utils/ContextProvider.java View File

@@ -0,0 +1,32 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import com.reactnativenavigation.activities.BaseReactActivity;
4
+
5
+import java.lang.ref.WeakReference;
6
+
7
+/**
8
+ * Created by guyc on 10/03/16.
9
+ */
10
+public class ContextProvider {
11
+    private static WeakReference<BaseReactActivity> sActivityWR;
12
+
13
+    public static void setActivityContext(BaseReactActivity activity) {
14
+        if (sActivityWR == null) {
15
+            sActivityWR = new WeakReference<>(activity);
16
+        }
17
+    }
18
+
19
+    /**
20
+     * Returns the currently resumed activity or {@code null} if there is none.
21
+     */
22
+    public static BaseReactActivity getActivityContext() {
23
+        return sActivityWR != null ? sActivityWR.get() : null;
24
+    }
25
+
26
+    public static void clearActivityContext() {
27
+        if (sActivityWR != null) {
28
+            sActivityWR.clear();
29
+        }
30
+        sActivityWR = null;
31
+    }
32
+}

+ 24
- 0
android/app/src/main/java/com/reactnativenavigation/utils/ReflectionUtils.java View File

@@ -0,0 +1,24 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import java.lang.reflect.Field;
4
+
5
+/**
6
+ * Created by guyc on 14/04/16.
7
+ */
8
+public class ReflectionUtils {
9
+
10
+    public static boolean setBooleanField(Object obj, String name, Boolean value) {
11
+        Field field;
12
+        try {
13
+            field = obj.getClass().getDeclaredField(name);
14
+            field.setAccessible(true);
15
+            field.set(obj, value);
16
+            return true;
17
+        } catch (NoSuchFieldException e) {
18
+            e.printStackTrace();
19
+        } catch (IllegalAccessException e) {
20
+            e.printStackTrace();
21
+        }
22
+        return false;
23
+    }
24
+}

+ 54
- 0
android/app/src/main/java/com/reactnativenavigation/utils/ResourceDrawableIdHelper.java View File

@@ -0,0 +1,54 @@
1
+package com.reactnativenavigation.utils;// Copyright 2004-present Facebook. All Rights Reserved.
2
+
3
+import android.content.Context;
4
+import android.graphics.drawable.Drawable;
5
+import android.net.Uri;
6
+
7
+import com.facebook.common.util.UriUtil;
8
+
9
+import java.util.HashMap;
10
+import java.util.Map;
11
+
12
+import javax.annotation.Nullable;
13
+
14
+/**
15
+ * Helper class for obtaining information about local images.
16
+ */
17
+public class ResourceDrawableIdHelper {
18
+
19
+    private Map<String, Integer> mResourceDrawableIdMap;
20
+
21
+    public ResourceDrawableIdHelper() {
22
+        mResourceDrawableIdMap = new HashMap<>();
23
+    }
24
+
25
+    public int getResourceDrawableId(Context context, @Nullable String name) {
26
+        if (name == null || name.isEmpty()) {
27
+            return 0;
28
+        }
29
+        name = name.toLowerCase().replace("-", "_");
30
+        if (mResourceDrawableIdMap.containsKey(name)) {
31
+            return mResourceDrawableIdMap.get(name);
32
+        }
33
+        int id = context.getResources().getIdentifier(
34
+                name,
35
+                "drawable",
36
+                context.getPackageName());
37
+        mResourceDrawableIdMap.put(name, id);
38
+        return id;
39
+    }
40
+
41
+    @Nullable
42
+    public Drawable getResourceDrawable(Context context, @Nullable String name) {
43
+        int resId = getResourceDrawableId(context, name);
44
+        return resId > 0 ? context.getResources().getDrawable(resId) : null;
45
+    }
46
+
47
+    public Uri getResourceDrawableUri(Context context, @Nullable String name) {
48
+        int resId = getResourceDrawableId(context, name);
49
+        return resId > 0 ? new Uri.Builder()
50
+                .scheme(UriUtil.LOCAL_RESOURCE_SCHEME)
51
+                .path(String.valueOf(resId))
52
+                .build() : Uri.EMPTY;
53
+    }
54
+}

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

@@ -0,0 +1,40 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.os.Bundle;
4
+import android.widget.FrameLayout;
5
+
6
+import com.facebook.react.ReactInstanceManager;
7
+import com.facebook.react.ReactRootView;
8
+import com.reactnativenavigation.activities.BaseReactActivity;
9
+import com.reactnativenavigation.core.objects.Screen;
10
+
11
+/**
12
+ * Created by guyc on 10/03/16.
13
+ */
14
+public class RctView extends FrameLayout {
15
+
16
+    private ReactRootView mReactRootView;
17
+
18
+    public ReactRootView getReactRootView() {
19
+        return mReactRootView;
20
+    }
21
+
22
+    public RctView(BaseReactActivity ctx, ReactInstanceManager rctInstanceManager, Screen screen) {
23
+        super(ctx);
24
+        setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
25
+
26
+        mReactRootView = new ReactRootView(ctx);
27
+        mReactRootView.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
28
+
29
+        String componentName = screen.screenId;
30
+        Bundle passProps = new Bundle();
31
+        passProps.putString(Screen.KEY_SCREEN_INSTANCE_ID, screen.screenInstanceId);
32
+        passProps.putString(Screen.KEY_NAVIGATOR_ID, screen.navigatorId);
33
+        passProps.putString(Screen.KEY_NAVIGATOR_EVENT_ID, screen.navigatorEventId);
34
+
35
+        mReactRootView.startReactApplication(rctInstanceManager, componentName, passProps);
36
+
37
+        addView(mReactRootView);
38
+    }
39
+}
40
+

+ 123
- 0
android/app/src/main/java/com/reactnativenavigation/views/RnnToolBar.java View File

@@ -0,0 +1,123 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+import android.graphics.drawable.Drawable;
5
+import android.os.AsyncTask;
6
+import android.support.v7.widget.Toolbar;
7
+import android.util.AttributeSet;
8
+import android.view.Menu;
9
+import android.view.MenuItem;
10
+
11
+import com.reactnativenavigation.activities.BaseReactActivity;
12
+import com.reactnativenavigation.core.objects.Button;
13
+import com.reactnativenavigation.core.objects.Screen;
14
+import com.reactnativenavigation.utils.ContextProvider;
15
+
16
+import java.lang.ref.WeakReference;
17
+import java.util.Collections;
18
+import java.util.HashMap;
19
+import java.util.List;
20
+import java.util.Map;
21
+
22
+/**
23
+ * Created by guyc on 09/04/16.
24
+ */
25
+public class RnnToolBar extends Toolbar {
26
+
27
+    private List<Screen> mScreens;
28
+    private AsyncTask mSetupToolbarTask;
29
+
30
+    public RnnToolBar(Context context) {
31
+        super(context);
32
+    }
33
+
34
+    public RnnToolBar(Context context, AttributeSet attrs) {
35
+        super(context, attrs);
36
+    }
37
+
38
+    public RnnToolBar(Context context, AttributeSet attrs, int defStyleAttr) {
39
+        super(context, attrs, defStyleAttr);
40
+    }
41
+
42
+    public void setScreens(List<Screen> screens) {
43
+        mScreens = screens;
44
+    }
45
+
46
+    public void handleOnCreateOptionsMenuAsync() {
47
+        setupToolbarButtonsAsync(mScreens.get(0));
48
+    }
49
+
50
+    public void setupToolbarButtonsAsync(Screen screen) {
51
+        if (mSetupToolbarTask == null) {
52
+            mSetupToolbarTask = new SetupToolbarButtonsTask(this, screen).execute();
53
+        }
54
+    }
55
+
56
+    @SuppressWarnings({"ConstantConditions"})
57
+    public void showBackButton() {
58
+        ContextProvider.getActivityContext().getSupportActionBar().setDisplayHomeAsUpEnabled(true);
59
+    }
60
+
61
+    @SuppressWarnings({"ConstantConditions"})
62
+    public void hideBackButton() {
63
+        ContextProvider.getActivityContext().getSupportActionBar().setDisplayHomeAsUpEnabled(false);
64
+    }
65
+
66
+    private static class SetupToolbarButtonsTask extends AsyncTask<Void, Void, Map<String, Drawable>> {
67
+        private final List<Button> mButtons;
68
+        private final WeakReference<RnnToolBar> mToolbarWR;
69
+
70
+        public SetupToolbarButtonsTask(RnnToolBar toolBar, Screen newScreen) {
71
+            mToolbarWR = new WeakReference<>(toolBar);
72
+            mButtons = newScreen.buttons == null ? Collections.EMPTY_LIST : newScreen.buttons;
73
+        }
74
+
75
+        @Override
76
+        protected Map<String, Drawable> doInBackground(Void... params) {
77
+            Context context = ContextProvider.getActivityContext();
78
+            if (context == null) {
79
+                return null;
80
+            }
81
+
82
+            Map<String, Drawable> icons = new HashMap<>();
83
+            for (Button button : mButtons) {
84
+                if (button.hasIcon()) {
85
+                    icons.put(button.id, button.getIcon(context));
86
+                }
87
+            }
88
+            return icons;
89
+        }
90
+
91
+        @Override
92
+        protected void onPostExecute(Map<String, Drawable> icons) {
93
+            Context context = ContextProvider.getActivityContext();
94
+            if (context == null) {
95
+                return;
96
+            }
97
+
98
+            Menu menu = ((BaseReactActivity) context).getMenu();
99
+
100
+            // Add new screen buttons
101
+            int i;
102
+            for (i = 0; i < mButtons.size(); i++) {
103
+                Button button = mButtons.get(i);
104
+                MenuItem item = menu.add(Menu.NONE, button.getItemId(), i, button.title);
105
+                if (icons.containsKey(button.id)) {
106
+                    Drawable icon = icons.get(button.id);
107
+                    item.setIcon(icon).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
108
+                }
109
+            }
110
+
111
+            // Remove prev screen buttons
112
+            for (int j = i; j < menu.size(); j++) {
113
+                menu.removeItem(j);
114
+            }
115
+
116
+            RnnToolBar toolBar = mToolbarWR.get();
117
+            if (toolBar != null) {
118
+                toolBar.mSetupToolbarTask = null;
119
+                mToolbarWR.clear();
120
+            }
121
+        }
122
+    }
123
+}

+ 80
- 0
android/app/src/main/java/com/reactnativenavigation/views/ScreenStack.java View File

@@ -0,0 +1,80 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.animation.LayoutTransition;
4
+import android.widget.FrameLayout;
5
+
6
+import com.facebook.react.ReactInstanceManager;
7
+import com.facebook.react.ReactRootView;
8
+import com.reactnativenavigation.activities.BaseReactActivity;
9
+import com.reactnativenavigation.core.RctManager;
10
+import com.reactnativenavigation.core.objects.Screen;
11
+import com.reactnativenavigation.utils.ReflectionUtils;
12
+
13
+import java.util.Stack;
14
+
15
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
16
+
17
+public class ScreenStack extends FrameLayout {
18
+
19
+    private static class ScreenView {
20
+        Screen screen;
21
+        RctView view;
22
+
23
+        public ScreenView(Screen screen, RctView view) {
24
+            this.screen = screen;
25
+            this.view = view;
26
+        }
27
+    }
28
+
29
+    private final Stack<ScreenView> mStack = new Stack<>();
30
+    private final ReactInstanceManager mReactInstanceManager =
31
+            RctManager.getInstance().getReactInstanceManager();
32
+    private final BaseReactActivity mReactActivity;
33
+
34
+    public ScreenStack(BaseReactActivity context) {
35
+        super(context);
36
+        mReactActivity = context;
37
+        setLayoutTransition(new LayoutTransition());
38
+    }
39
+
40
+    public void push(Screen screen) {
41
+        RctView oldView = null;
42
+        if (!mStack.isEmpty()) {
43
+            oldView = mStack.peek().view;
44
+        }
45
+        RctView view = new RctView(mReactActivity, mReactInstanceManager, screen);
46
+        addView(view, MATCH_PARENT, MATCH_PARENT);
47
+        if (oldView != null) {
48
+            ReactRootView reactRootView = oldView.getReactRootView();
49
+            ReflectionUtils.setBooleanField(reactRootView, "mAttachScheduled", true);
50
+            removeView(oldView);
51
+        }
52
+        mStack.push(new ScreenView(screen, view));
53
+    }
54
+
55
+    public Screen pop() {
56
+        if (mStack.isEmpty()) {
57
+            return null;
58
+        }
59
+        ScreenView popped = mStack.pop();
60
+        if (!mStack.isEmpty()) {
61
+            addView(mStack.peek().view, 0);
62
+        }
63
+
64
+        ReflectionUtils.setBooleanField(popped.view.getReactRootView(), "mAttachScheduled", false);
65
+        removeView(popped.view);
66
+        return popped.screen;
67
+    }
68
+
69
+    public boolean isEmpty() {
70
+        return mStack.isEmpty();
71
+    }
72
+
73
+    public int getStackSize() {
74
+        return mStack.size();
75
+    }
76
+
77
+    public Screen peek() {
78
+        return mStack.peek().screen;
79
+    }
80
+}

+ 24
- 0
android/app/src/main/res/layout/single_screen_activity.xml View File

@@ -0,0 +1,24 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
+    xmlns:app="http://schemas.android.com/apk/res-auto"
4
+    android:orientation="vertical"
5
+    android:layout_width="match_parent"
6
+    android:layout_height="match_parent">
7
+
8
+    <android.support.design.widget.AppBarLayout
9
+        android:id="@+id/appbar"
10
+        android:layout_width="match_parent"
11
+        android:layout_height="wrap_content"
12
+        android:fitsSystemWindows="true">
13
+        <android.support.v7.widget.Toolbar
14
+            android:id="@+id/toolbar"
15
+            android:layout_width="match_parent"
16
+            android:layout_height="?attr/actionBarSize"
17
+            app:layout_scrollFlags="scroll|enterAlways"/>
18
+    </android.support.design.widget.AppBarLayout>
19
+
20
+    <FrameLayout
21
+        android:id="@+id/contentFrame"
22
+        android:layout_width="match_parent"
23
+        android:layout_height="match_parent"/>
24
+</LinearLayout>

+ 32
- 0
android/app/src/main/res/layout/tab_activity.xml View File

@@ -0,0 +1,32 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
+    xmlns:tools="http://schemas.android.com/tools"
4
+    xmlns:app="http://schemas.android.com/apk/res-auto"
5
+    android:orientation="vertical"
6
+    android:layout_width="match_parent"
7
+    android:layout_height="match_parent"
8
+    tools:context=".activities.TabActivity">
9
+
10
+    <android.support.design.widget.AppBarLayout
11
+        android:id="@+id/appbar"
12
+        android:layout_width="match_parent"
13
+        android:layout_height="wrap_content"
14
+        android:fitsSystemWindows="true">
15
+        <com.reactnativenavigation.views.RnnToolBar
16
+            android:id="@+id/toolbar"
17
+            android:layout_width="match_parent"
18
+            android:layout_height="?attr/actionBarSize"
19
+            app:layout_scrollFlags="scroll|enterAlways"/>
20
+        <android.support.design.widget.TabLayout
21
+            android:id="@+id/tabLayout"
22
+            android:layout_width="match_parent"
23
+            android:layout_height="?attr/actionBarSize"
24
+            app:layout_scrollFlags="scroll|enterAlways"/>
25
+    </android.support.design.widget.AppBarLayout>
26
+
27
+    <android.support.v4.view.ViewPager
28
+        android:id="@+id/viewPager"
29
+        app:layout_behavior="@string/appbar_scrolling_view_behavior"
30
+        android:layout_width="match_parent"
31
+        android:layout_height="match_parent"/>
32
+</LinearLayout>

+ 4
- 0
android/app/src/main/res/values/ids.xml View File

@@ -0,0 +1,4 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<resources>
3
+    <item name="react_root_view" type="id"/>
4
+</resources>

+ 24
- 0
android/build.gradle View File

@@ -0,0 +1,24 @@
1
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
2
+
3
+buildscript {
4
+    repositories {
5
+        jcenter()
6
+    }
7
+    dependencies {
8
+        classpath 'com.android.tools.build:gradle:1.3.1'
9
+
10
+        // NOTE: Do not place your application dependencies here; they belong
11
+        // in the individual module build.gradle files
12
+    }
13
+}
14
+
15
+allprojects {
16
+    repositories {
17
+        mavenLocal()
18
+        jcenter()
19
+        maven {
20
+            // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
21
+            url "$projectDir/../../node_modules/react-native/android"
22
+        }
23
+    }
24
+}

+ 20
- 0
android/gradle.properties View File

@@ -0,0 +1,20 @@
1
+# Project-wide Gradle settings.
2
+
3
+# IDE (e.g. Android Studio) users:
4
+# Gradle settings configured through the IDE *will override*
5
+# any settings specified in this file.
6
+
7
+# For more details on how to configure your build environment visit
8
+# http://www.gradle.org/docs/current/userguide/build_environment.html
9
+
10
+# Specifies the JVM arguments used for the daemon process.
11
+# The setting is particularly useful for tweaking memory settings.
12
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
13
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14
+
15
+# When configured, Gradle will run in incubating parallel mode.
16
+# This option should only be used with decoupled projects. More details, visit
17
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18
+# org.gradle.parallel=true
19
+
20
+android.useDeprecatedNdk=true

+ 134
- 0
android/react-native-navigation.iml View File

@@ -0,0 +1,134 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<module external.linked.project.id=":react-native-navigation" external.linked.project.path="$MODULE_DIR$/app" external.root.project.path="$MODULE_DIR$/../../../android" external.system.id="GRADLE" external.system.module.group="example" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
3
+  <component name="FacetManager">
4
+    <facet type="java-gradle" name="Java-Gradle">
5
+      <configuration>
6
+        <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
7
+        <option name="BUILDABLE" value="false" />
8
+      </configuration>
9
+    </facet>
10
+    <facet type="android-gradle" name="Android-Gradle">
11
+      <configuration>
12
+        <option name="GRADLE_PROJECT_PATH" value=":react-native-navigation" />
13
+      </configuration>
14
+    </facet>
15
+    <facet type="android" name="Android">
16
+      <configuration>
17
+        <option name="SELECTED_BUILD_VARIANT" value="libraryDebug" />
18
+        <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
19
+        <option name="ASSEMBLE_TASK_NAME" value="assembleLibraryDebug" />
20
+        <option name="COMPILE_JAVA_TASK_NAME" value="compileLibraryDebugSources" />
21
+        <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleLibraryDebugAndroidTest" />
22
+        <option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileLibraryDebugAndroidTestSources" />
23
+        <afterSyncTasks>
24
+          <task>generateLibraryDebugAndroidTestSources</task>
25
+          <task>generateLibraryDebugSources</task>
26
+        </afterSyncTasks>
27
+        <option name="ALLOW_USER_CONFIGURATION" value="false" />
28
+        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
29
+        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
30
+        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/app/src/main/res" />
31
+        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
32
+        <option name="LIBRARY_PROJECT" value="true" />
33
+      </configuration>
34
+    </facet>
35
+  </component>
36
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
37
+    <output url="file://$MODULE_DIR$/app/build/intermediates/classes/library/debug" />
38
+    <output-test url="file://$MODULE_DIR$/app/build/intermediates/classes/androidTest/library/debug" />
39
+    <exclude-output />
40
+    <content url="file://$MODULE_DIR$/app">
41
+      <sourceFolder url="file://$MODULE_DIR$/app/build/generated/source/r/library/debug" isTestSource="false" generated="true" />
42
+      <sourceFolder url="file://$MODULE_DIR$/app/build/generated/source/aidl/library/debug" isTestSource="false" generated="true" />
43
+      <sourceFolder url="file://$MODULE_DIR$/app/build/generated/source/buildConfig/library/debug" isTestSource="false" generated="true" />
44
+      <sourceFolder url="file://$MODULE_DIR$/app/build/generated/source/rs/library/debug" isTestSource="false" generated="true" />
45
+      <sourceFolder url="file://$MODULE_DIR$/app/build/generated/res/rs/library/debug" type="java-resource" />
46
+      <sourceFolder url="file://$MODULE_DIR$/app/build/generated/res/resValues/library/debug" type="java-resource" />
47
+      <sourceFolder url="file://$MODULE_DIR$/app/src/libraryDebug/res" type="java-resource" />
48
+      <sourceFolder url="file://$MODULE_DIR$/app/src/libraryDebug/resources" type="java-resource" />
49
+      <sourceFolder url="file://$MODULE_DIR$/app/src/libraryDebug/assets" type="java-resource" />
50
+      <sourceFolder url="file://$MODULE_DIR$/app/src/libraryDebug/aidl" isTestSource="false" />
51
+      <sourceFolder url="file://$MODULE_DIR$/app/src/libraryDebug/java" isTestSource="false" />
52
+      <sourceFolder url="file://$MODULE_DIR$/app/src/libraryDebug/jni" isTestSource="false" />
53
+      <sourceFolder url="file://$MODULE_DIR$/app/src/libraryDebug/rs" isTestSource="false" />
54
+      <sourceFolder url="file://$MODULE_DIR$/app/build/generated/source/r/androidTest/library/debug" isTestSource="true" generated="true" />
55
+      <sourceFolder url="file://$MODULE_DIR$/app/build/generated/source/aidl/androidTest/library/debug" isTestSource="true" generated="true" />
56
+      <sourceFolder url="file://$MODULE_DIR$/app/build/generated/source/buildConfig/androidTest/library/debug" isTestSource="true" generated="true" />
57
+      <sourceFolder url="file://$MODULE_DIR$/app/build/generated/source/rs/androidTest/library/debug" isTestSource="true" generated="true" />
58
+      <sourceFolder url="file://$MODULE_DIR$/app/build/generated/res/rs/androidTest/library/debug" type="java-test-resource" />
59
+      <sourceFolder url="file://$MODULE_DIR$/app/build/generated/res/resValues/androidTest/library/debug" type="java-test-resource" />
60
+      <sourceFolder url="file://$MODULE_DIR$/app/src/library/res" type="java-resource" />
61
+      <sourceFolder url="file://$MODULE_DIR$/app/src/library/resources" type="java-resource" />
62
+      <sourceFolder url="file://$MODULE_DIR$/app/src/library/assets" type="java-resource" />
63
+      <sourceFolder url="file://$MODULE_DIR$/app/src/library/aidl" isTestSource="false" />
64
+      <sourceFolder url="file://$MODULE_DIR$/app/src/library/java" isTestSource="false" />
65
+      <sourceFolder url="file://$MODULE_DIR$/app/src/library/jni" isTestSource="false" />
66
+      <sourceFolder url="file://$MODULE_DIR$/app/src/library/rs" isTestSource="false" />
67
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTestLibrary/res" type="java-test-resource" />
68
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTestLibrary/resources" type="java-test-resource" />
69
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTestLibrary/assets" type="java-test-resource" />
70
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTestLibrary/aidl" isTestSource="true" />
71
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTestLibrary/java" isTestSource="true" />
72
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTestLibrary/jni" isTestSource="true" />
73
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTestLibrary/rs" isTestSource="true" />
74
+      <sourceFolder url="file://$MODULE_DIR$/app/src/debug/res" type="java-resource" />
75
+      <sourceFolder url="file://$MODULE_DIR$/app/src/debug/resources" type="java-resource" />
76
+      <sourceFolder url="file://$MODULE_DIR$/app/src/debug/assets" type="java-resource" />
77
+      <sourceFolder url="file://$MODULE_DIR$/app/src/debug/aidl" isTestSource="false" />
78
+      <sourceFolder url="file://$MODULE_DIR$/app/src/debug/java" isTestSource="false" />
79
+      <sourceFolder url="file://$MODULE_DIR$/app/src/debug/jni" isTestSource="false" />
80
+      <sourceFolder url="file://$MODULE_DIR$/app/src/debug/rs" isTestSource="false" />
81
+      <sourceFolder url="file://$MODULE_DIR$/app/src/main/res" type="java-resource" />
82
+      <sourceFolder url="file://$MODULE_DIR$/app/src/main/resources" type="java-resource" />
83
+      <sourceFolder url="file://$MODULE_DIR$/app/src/main/assets" type="java-resource" />
84
+      <sourceFolder url="file://$MODULE_DIR$/app/src/main/aidl" isTestSource="false" />
85
+      <sourceFolder url="file://$MODULE_DIR$/app/src/main/java" isTestSource="false" />
86
+      <sourceFolder url="file://$MODULE_DIR$/app/src/main/jni" isTestSource="false" />
87
+      <sourceFolder url="file://$MODULE_DIR$/app/src/main/rs" isTestSource="false" />
88
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTest/res" type="java-test-resource" />
89
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTest/resources" type="java-test-resource" />
90
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTest/assets" type="java-test-resource" />
91
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTest/aidl" isTestSource="true" />
92
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTest/java" isTestSource="true" />
93
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTest/jni" isTestSource="true" />
94
+      <sourceFolder url="file://$MODULE_DIR$/app/src/androidTest/rs" isTestSource="true" />
95
+      <excludeFolder url="file://$MODULE_DIR$/app/build/intermediates/annotations" />
96
+      <excludeFolder url="file://$MODULE_DIR$/app/build/intermediates/assets" />
97
+      <excludeFolder url="file://$MODULE_DIR$/app/build/intermediates/bundles" />
98
+      <excludeFolder url="file://$MODULE_DIR$/app/build/intermediates/classes" />
99
+      <excludeFolder url="file://$MODULE_DIR$/app/build/intermediates/dependency-cache" />
100
+      <excludeFolder url="file://$MODULE_DIR$/app/build/intermediates/exploded-aar/com.facebook.react/react-native/0.20.1/jars" />
101
+      <excludeFolder url="file://$MODULE_DIR$/app/build/intermediates/incremental" />
102
+      <excludeFolder url="file://$MODULE_DIR$/app/build/intermediates/lint" />
103
+      <excludeFolder url="file://$MODULE_DIR$/app/build/intermediates/res" />
104
+      <excludeFolder url="file://$MODULE_DIR$/app/build/intermediates/rs" />
105
+      <excludeFolder url="file://$MODULE_DIR$/app/build/intermediates/symbols" />
106
+      <excludeFolder url="file://$MODULE_DIR$/app/build/outputs" />
107
+      <excludeFolder url="file://$MODULE_DIR$/app/build/tmp" />
108
+    </content>
109
+    <orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
110
+    <orderEntry type="sourceFolder" forTests="false" />
111
+    <orderEntry type="library" exported="" name="support-annotations-23.1.1" level="project" />
112
+    <orderEntry type="library" exported="" name="fresco-0.8.1" level="project" />
113
+    <orderEntry type="library" exported="" name="design-23.1.1" level="project" />
114
+    <orderEntry type="library" exported="" name="imagepipeline-okhttp-0.8.1" level="project" />
115
+    <orderEntry type="library" exported="" name="imagepipeline-0.8.1" level="project" />
116
+    <orderEntry type="library" exported="" name="stetho-1.2.0" level="project" />
117
+    <orderEntry type="library" exported="" name="jsr305-3.0.0" level="project" />
118
+    <orderEntry type="library" exported="" name="jackson-core-2.2.3" level="project" />
119
+    <orderEntry type="library" exported="" name="recyclerview-v7-23.1.1" level="project" />
120
+    <orderEntry type="library" exported="" name="react-native-0.20.1" level="project" />
121
+    <orderEntry type="library" exported="" name="support-v4-23.1.1" level="project" />
122
+    <orderEntry type="library" exported="" name="appcompat-v7-23.1.1" level="project" />
123
+    <orderEntry type="library" exported="" name="okhttp-2.5.0" level="project" />
124
+    <orderEntry type="library" exported="" name="okhttp-ws-2.5.0" level="project" />
125
+    <orderEntry type="library" exported="" name="okio-1.6.0" level="project" />
126
+    <orderEntry type="library" exported="" name="fbcore-0.8.1" level="project" />
127
+    <orderEntry type="library" exported="" name="android-jsc-r174650" level="project" />
128
+    <orderEntry type="library" exported="" name="drawee-0.8.1" level="project" />
129
+    <orderEntry type="library" exported="" name="stetho-okhttp-1.2.0" level="project" />
130
+    <orderEntry type="library" exported="" name="bolts-android-1.1.4" level="project" />
131
+    <orderEntry type="library" exported="" name="commons-cli-1.2" level="project" />
132
+    <orderEntry type="library" exported="" name="library-2.4.0" level="project" />
133
+  </component>
134
+</module>

+ 19
- 0
android/react-navtive-navigation.iml View File

@@ -0,0 +1,19 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<module external.linked.project.id="react-navtive-navigation" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
3
+  <component name="FacetManager">
4
+    <facet type="java-gradle" name="Java-Gradle">
5
+      <configuration>
6
+        <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
7
+        <option name="BUILDABLE" value="false" />
8
+      </configuration>
9
+    </facet>
10
+  </component>
11
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
12
+    <exclude-output />
13
+    <content url="file://$MODULE_DIR$">
14
+      <excludeFolder url="file://$MODULE_DIR$/.gradle" />
15
+    </content>
16
+    <orderEntry type="inheritedJdk" />
17
+    <orderEntry type="sourceFolder" forTests="false" />
18
+  </component>
19
+</module>

+ 3
- 0
example-redux/android/app/build.gradle View File

@@ -123,4 +123,7 @@ dependencies {
123 123
     compile fileTree(dir: "libs", include: ["*.jar"])
124 124
     compile "com.android.support:appcompat-v7:23.0.1"
125 125
     compile "com.facebook.react:react-native:+"  // From node_modules
126
+    debugCompile project(path: ':react-native-navigation', configuration: 'libraryDebug')
127
+    releaseCompile project(path: ':react-native-navigation', configuration: 'libraryRelease')
128
+//    compile project(':react-native-navigation')
126 129
 }

+ 1
- 1
example-redux/android/app/react.gradle View File

@@ -41,7 +41,7 @@ gradle.projectsEvaluated {
41 41
             def jsBundleDir = elvisFile(config."$jsBundleDirConfigName") ?:
42 42
                     file("$buildDir/intermediates/assets/${targetPath}")
43 43
 
44
-            def resourcesDirConfigName = "jsBundleDir${targetName}"
44
+            def resourcesDirConfigName = "resourcesDir${targetName}"
45 45
             def resourcesDir = elvisFile(config."${resourcesDirConfigName}") ?:
46 46
                     file("$buildDir/intermediates/res/merged/${targetPath}")
47 47
             def jsBundleFile = file("$jsBundleDir/$bundleAssetName")

+ 2
- 35
example-redux/android/app/src/main/java/com/exampleredux/MainActivity.java View File

@@ -1,40 +1,7 @@
1 1
 package com.exampleredux;
2 2
 
3
-import com.facebook.react.ReactActivity;
4
-import com.facebook.react.ReactPackage;
5
-import com.facebook.react.shell.MainReactPackage;
3
+import com.reactnativenavigation.activities.RootActivity;
6 4
 
7
-import java.util.Arrays;
8
-import java.util.List;
5
+public class MainActivity extends RootActivity {
9 6
 
10
-public class MainActivity extends ReactActivity {
11
-
12
-    /**
13
-     * Returns the name of the main component registered from JavaScript.
14
-     * This is used to schedule rendering of the component.
15
-     */
16
-    @Override
17
-    protected String getMainComponentName() {
18
-        return "exampleRedux";
19
-    }
20
-
21
-    /**
22
-     * Returns whether dev mode should be enabled.
23
-     * This enables e.g. the dev menu.
24
-     */
25
-    @Override
26
-    protected boolean getUseDeveloperSupport() {
27
-        return BuildConfig.DEBUG;
28
-    }
29
-
30
-    /**
31
-     * A list of packages used by the app. If the app uses additional views
32
-     * or modules besides the default ones, add more packages here.
33
-     */
34
-    @Override
35
-    protected List<ReactPackage> getPackages() {
36
-        return Arrays.<ReactPackage>asList(
37
-            new MainReactPackage()
38
-        );
39
-    }
40 7
 }

+ 3
- 0
example-redux/android/settings.gradle View File

@@ -1,3 +1,6 @@
1 1
 rootProject.name = 'exampleRedux'
2 2
 
3 3
 include ':app'
4
+include ':react-native-navigation'
5
+project(':react-native-navigation').projectDir = new File(
6
+        rootProject.projectDir, '../node_modules/react-native-navigation/android/app/')

+ 2
- 44
example-redux/index.android.js View File

@@ -1,45 +1,3 @@
1
-/**
2
- * Sample React Native App
3
- * https://github.com/facebook/react-native
4
- */
5
-'use strict';
6
-import React, {
7
-  AppRegistry,
8
-  Component,
9
-  StyleSheet,
10
-  Text,
11
-  View
12
-} from 'react-native';
1
+import App from './src/app';
13 2
 
14
-class exampleRedux extends Component {
15
-  render() {
16
-    return (
17
-      <View style={styles.container}>
18
-        <Text style={styles.welcome}>
19
-          Coming Soon
20
-        </Text>
21
-      </View>
22
-    );
23
-  }
24
-}
25
-
26
-const styles = StyleSheet.create({
27
-  container: {
28
-    flex: 1,
29
-    justifyContent: 'center',
30
-    alignItems: 'center',
31
-    backgroundColor: '#F5FCFF',
32
-  },
33
-  welcome: {
34
-    fontSize: 20,
35
-    textAlign: 'center',
36
-    margin: 10,
37
-  },
38
-  instructions: {
39
-    textAlign: 'center',
40
-    color: '#333333',
41
-    marginBottom: 5,
42
-  },
43
-});
44
-
45
-AppRegistry.registerComponent('exampleRedux', () => exampleRedux);
3
+const app = new App();

+ 5
- 2
example-redux/src/app.js View File

@@ -21,15 +21,17 @@ export default class App {
21 21
     store.subscribe(this.onStoreUpdate.bind(this));
22 22
     store.dispatch(appActions.appInitialized());
23 23
   }
24
+
24 25
   onStoreUpdate() {
25 26
     const { root } = store.getState().app;
26 27
     // handle a root change
27 28
     // if your app doesn't change roots in runtime, you can remove onStoreUpdate() altogether
28
-    if (this.currentRoot !== root) {
29
+    if (this.currentRoot != root) {
29 30
       this.currentRoot = root;
30 31
       this.startApp(root);
31 32
     }
32 33
   }
34
+
33 35
   startApp(root) {
34 36
     switch (root) {
35 37
       case 'login':
@@ -61,7 +63,8 @@ export default class App {
61 63
               navigatorStyle: {}
62 64
             }
63 65
           ],
64
-          animationType: 'slide-down'
66
+          animationType: 'slide-down',
67
+          title: 'Redux Example'
65 68
         });
66 69
         return;
67 70
       default:

+ 15
- 7
example-redux/src/screens/FirstTabScreen.js View File

@@ -5,7 +5,7 @@ import React, {
5 5
   ScrollView,
6 6
   TouchableOpacity,
7 7
   StyleSheet,
8
-  AlertIOS
8
+  Alert
9 9
 } from 'react-native';
10 10
 import { connect } from 'react-redux';
11 11
 import * as counterActions from '../reducers/counter/actions';
@@ -20,6 +20,7 @@ class FirstTabScreen extends Component {
20 20
       },
21 21
       {
22 22
         icon: require('../../img/navicon_add.png'),
23
+        title: 'Add',
23 24
         id: 'add'
24 25
       }
25 26
     ]
@@ -30,11 +31,18 @@ class FirstTabScreen extends Component {
30 31
     this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
31 32
   }
32 33
   onNavigatorEvent(event) {
33
-    if (event.id == 'edit') {
34
-      AlertIOS.alert('NavBar', 'Edit button pressed');
35
-    }
36
-    if (event.id == 'add') {
37
-      AlertIOS.alert('NavBar', 'Add button pressed');
34
+    switch (event.id) {
35
+      case 'edit':
36
+        Alert.alert('NavBar', 'Edit button pressed');
37
+        break;
38
+
39
+      case 'add':
40
+        Alert.alert('NavBar', 'Add button pressed');
41
+        break;
42
+
43
+      default:
44
+        console.log('Unhandled event ' + event.id);
45
+        break;
38 46
     }
39 47
   }
40 48
   render() {
@@ -72,7 +80,7 @@ const styles = StyleSheet.create({
72 80
     textAlign: 'center',
73 81
     fontSize: 18,
74 82
     marginBottom: 10,
75
-    marginTop:10,
83
+    marginTop:10
76 84
   },
77 85
   button: {
78 86
     textAlign: 'center',

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

@@ -4,8 +4,7 @@ import React, {
4 4
   View,
5 5
   ScrollView,
6 6
   TouchableOpacity,
7
-  StyleSheet,
8
-  AlertIOS
7
+  StyleSheet
9 8
 } from 'react-native';
10 9
 import { connect } from 'react-redux';
11 10
 import * as counterActions from '../reducers/counter/actions';

+ 17
- 2
example-redux/src/screens/PushedScreen.js View File

@@ -4,7 +4,8 @@ import React, {
4 4
   View,
5 5
   ScrollView,
6 6
   TouchableOpacity,
7
-  StyleSheet
7
+  StyleSheet,
8
+  TextInput
8 9
 } from 'react-native';
9 10
 import { connect } from 'react-redux';
10 11
 import * as counterActions from '../reducers/counter/actions';
@@ -13,10 +14,22 @@ import * as counterActions from '../reducers/counter/actions';
13 14
 class PushedScreen extends Component {
14 15
   constructor(props) {
15 16
     super(props);
17
+    this.bgColor = this.getRandomColor();
18
+    console.log('constructor');
16 19
   }
20
+
21
+  getRandomColor() {
22
+    var letters = '0123456789ABCDEF'.split('');
23
+    var color = '#';
24
+    for (var i = 0; i < 6; i++ ) {
25
+      color += letters[Math.floor(Math.random() * 16)];
26
+    }
27
+    return color;
28
+  }
29
+
17 30
   render() {
18 31
     return (
19
-      <View style={{flex: 1, padding: 20}}>
32
+      <View style={{flex: 1, padding: 20, backgroundColor: this.bgColor}}>
20 33
 
21 34
         <Text style={styles.text}>
22 35
           <Text style={{fontWeight: '500'}}>Counter: </Text> {this.props.counter.count}
@@ -34,6 +47,8 @@ class PushedScreen extends Component {
34 47
           <Text style={styles.button}>Pop Screen</Text>
35 48
         </TouchableOpacity>
36 49
 
50
+        <TextInput style={{height: 40, borderColor: 'gray', borderWidth: 1}}/>
51
+
37 52
       </View>
38 53
     );
39 54
   }

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

@@ -6,7 +6,7 @@ import React, {
6 6
   ScrollView,
7 7
   TouchableOpacity,
8 8
   StyleSheet,
9
-  AlertIOS
9
+  Alert
10 10
 } from 'react-native';
11 11
 import { connect } from 'react-redux';
12 12
 import * as counterActions from '../reducers/counter/actions';

+ 6
- 3
example/android/app/build.gradle View File

@@ -120,7 +120,10 @@ android {
120 120
 }
121 121
 
122 122
 dependencies {
123
-    compile fileTree(dir: "libs", include: ["*.jar"])
124
-    compile "com.android.support:appcompat-v7:23.0.1"
125
-    compile "com.facebook.react:react-native:+"  // From node_modules
123
+    compile fileTree(dir: 'libs', include: ['*.jar'])
124
+    compile 'com.android.support:appcompat-v7:23.0.1'
125
+    compile 'com.facebook.react:react-native:+'
126
+        debugCompile project(path: ':react-native-navigation', configuration: 'libraryDebug')
127
+        releaseCompile project(path: ':react-native-navigation', configuration: 'libraryRelease')
128
+//    compile project(':react-native-navigation')
126 129
 }

+ 1
- 1
example/android/app/react.gradle View File

@@ -41,7 +41,7 @@ gradle.projectsEvaluated {
41 41
             def jsBundleDir = elvisFile(config."$jsBundleDirConfigName") ?:
42 42
                     file("$buildDir/intermediates/assets/${targetPath}")
43 43
 
44
-            def resourcesDirConfigName = "jsBundleDir${targetName}"
44
+            def resourcesDirConfigName = "resourcesDir${targetName}"
45 45
             def resourcesDir = elvisFile(config."${resourcesDirConfigName}") ?:
46 46
                     file("$buildDir/intermediates/res/merged/${targetPath}")
47 47
             def jsBundleFile = file("$jsBundleDir/$bundleAssetName")

+ 14
- 14
example/android/app/src/main/AndroidManifest.xml View File

@@ -1,23 +1,23 @@
1 1
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
-    package="com.example">
2
+          package="com.example">
3 3
 
4 4
     <uses-permission android:name="android.permission.INTERNET" />
5 5
 
6 6
     <application
7
-      android:allowBackup="true"
8
-      android:label="@string/app_name"
9
-      android:icon="@mipmap/ic_launcher"
10
-      android:theme="@style/AppTheme">
11
-      <activity
12
-        android:name=".MainActivity"
7
+        android:allowBackup="true"
13 8
         android:label="@string/app_name"
14
-        android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
15
-        <intent-filter>
16
-            <action android:name="android.intent.action.MAIN" />
17
-            <category android:name="android.intent.category.LAUNCHER" />
18
-        </intent-filter>
19
-      </activity>
20
-      <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
9
+        android:icon="@mipmap/ic_launcher"
10
+        android:theme="@style/AppTheme">
11
+        <activity
12
+            android:name=".MainActivity"
13
+            android:label="@string/app_name"
14
+            android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
15
+            <intent-filter>
16
+                <action android:name="android.intent.action.MAIN" />
17
+                <category android:name="android.intent.category.LAUNCHER" />
18
+            </intent-filter>
19
+        </activity>
20
+        <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
21 21
     </application>
22 22
 
23 23
 </manifest>

+ 6
- 28
example/android/app/src/main/java/com/example/MainActivity.java View File

@@ -1,40 +1,18 @@
1 1
 package com.example;
2 2
 
3
-import com.facebook.react.ReactActivity;
4
-import com.facebook.react.ReactPackage;
5
-import com.facebook.react.shell.MainReactPackage;
6 3
 
7
-import java.util.Arrays;
8
-import java.util.List;
4
+import com.reactnativenavigation.activities.RctActivity;
9 5
 
10
-public class MainActivity extends ReactActivity {
6
+public class MainActivity extends RctActivity {
11 7
 
12
-    /**
13
-     * Returns the name of the main component registered from JavaScript.
14
-     * This is used to schedule rendering of the component.
15
-     */
16 8
     @Override
17 9
     protected String getMainComponentName() {
18
-        return "example";
10
+        return "App";
19 11
     }
20 12
 
21
-    /**
22
-     * Returns whether dev mode should be enabled.
23
-     * This enables e.g. the dev menu.
24
-     */
25 13
     @Override
26
-    protected boolean getUseDeveloperSupport() {
27
-        return BuildConfig.DEBUG;
28
-    }
29
-
30
-    /**
31
-     * A list of packages used by the app. If the app uses additional views
32
-     * or modules besides the default ones, add more packages here.
33
-     */
34
-    @Override
35
-    protected List<ReactPackage> getPackages() {
36
-        return Arrays.<ReactPackage>asList(
37
-            new MainReactPackage()
38
-        );
14
+    protected void onPause() {
15
+        super.onPause();
16
+        finish();
39 17
     }
40 18
 }

+ 3
- 0
example/android/settings.gradle View File

@@ -1,3 +1,6 @@
1 1
 rootProject.name = 'example'
2 2
 
3 3
 include ':app'
4
+include ':react-native-navigation'
5
+project(':react-native-navigation').projectDir = new File(
6
+        rootProject.projectDir, '../node_modules/react-native-navigation/android/app/')

+ 1
- 45
example/index.android.js View File

@@ -1,45 +1 @@
1
-/**
2
- * Sample React Native App
3
- * https://github.com/facebook/react-native
4
- */
5
-'use strict';
6
-import React, {
7
-  AppRegistry,
8
-  Component,
9
-  StyleSheet,
10
-  Text,
11
-  View
12
-} from 'react-native';
13
-
14
-class example extends Component {
15
-  render() {
16
-    return (
17
-      <View style={styles.container}>
18
-        <Text style={styles.welcome}>
19
-          Coming Soon
20
-        </Text>
21
-      </View>
22
-    );
23
-  }
24
-}
25
-
26
-const styles = StyleSheet.create({
27
-  container: {
28
-    flex: 1,
29
-    justifyContent: 'center',
30
-    alignItems: 'center',
31
-    backgroundColor: '#F5FCFF',
32
-  },
33
-  welcome: {
34
-    fontSize: 20,
35
-    textAlign: 'center',
36
-    margin: 10,
37
-  },
38
-  instructions: {
39
-    textAlign: 'center',
40
-    color: '#333333',
41
-    marginBottom: 5,
42
-  },
43
-});
44
-
45
-AppRegistry.registerComponent('example', () => example);
1
+import App from './src/app';

+ 19
- 0
example/src/app.js View File

@@ -1,9 +1,28 @@
1
+import React, {
2
+  AppRegistry,
3
+  Component,
4
+  View
5
+} from 'react-native';
1 6
 import { Navigation } from 'react-native-navigation';
2 7
 
3 8
 // screen related book keeping
4 9
 import { registerScreens } from './screens';
5 10
 registerScreens();
6 11
 
12
+AppRegistry.registerComponent('App', () => App);
13
+
14
+export default class App extends React.Component {
15
+  constructor(props) {
16
+    super(props);
17
+  }
18
+
19
+  render() {
20
+    return (
21
+      <View />
22
+    );
23
+  }
24
+}
25
+
7 26
 // this will start our app
8 27
 Navigation.startTabBasedApp({
9 28
   tabs: [

+ 111
- 0
example/src/screens/FirstTabScreen.android.js View File

@@ -0,0 +1,111 @@
1
+import React, {
2
+  Component,
3
+  Text,
4
+  View,
5
+  ScrollView,
6
+  TouchableOpacity,
7
+  StyleSheet
8
+} from 'react-native';
9
+
10
+export default class FirstTabScreen extends Component {
11
+  static navigatorButtons = {
12
+    leftButtons: [{
13
+      icon: require('../../img/navicon_menu.png'),
14
+      id: 'menu'
15
+    }],
16
+    rightButtons: [
17
+      {
18
+        title: 'Edit',
19
+        id: 'edit'
20
+      },
21
+      {
22
+        icon: require('../../img/navicon_add.png'),
23
+        id: 'add'
24
+      }
25
+    ]
26
+  };
27
+  static navigatorStyle = {
28
+    drawUnderTabBar: true
29
+  };
30
+  constructor(props) {
31
+    super(props);
32
+    // if you want to listen on navigator events, set this up
33
+    // this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
34
+  }
35
+  onNavigatorEvent(event) {
36
+    if (event.id == 'menu') {
37
+      this.props.navigator.toggleDrawer({
38
+        side: 'left',
39
+        animated: true
40
+      });
41
+    }
42
+    if (event.id == 'edit') {
43
+      // AlertIOS.alert('NavBar', 'Edit button pressed');
44
+    }
45
+    if (event.id == 'add') {
46
+      // AlertIOS.alert('NavBar', 'Add button pressed');
47
+    }
48
+  }
49
+  render() {
50
+    return (
51
+      <View style={{flex: 1, padding: 20}}>
52
+
53
+        <TouchableOpacity onPress={ this.onPushPress.bind(this) }>
54
+          <Text style={styles.button}>Push Plain Screen</Text>
55
+        </TouchableOpacity>
56
+
57
+        <TouchableOpacity onPress={ this.onPushStyledPress.bind(this) }>
58
+          <Text style={styles.button}>Push Styled Screen</Text>
59
+        </TouchableOpacity>
60
+
61
+        <TouchableOpacity onPress={ this.onModalPress.bind(this) }>
62
+          <Text style={styles.button}>Show Modal Screen</Text>
63
+        </TouchableOpacity>
64
+
65
+        <TouchableOpacity onPress={ this.onLightBoxPress.bind(this) }>
66
+          <Text style={styles.button}>Show LightBox</Text>
67
+        </TouchableOpacity>
68
+
69
+      </View>
70
+    );
71
+  }
72
+  onPushPress() {
73
+    this.props.navigator.push({
74
+      title: "More",
75
+      screen: "example.PushedScreen"
76
+    });
77
+  }
78
+  onPushStyledPress() {
79
+    console.warn('navigator.push not implemented yet');
80
+    // this.props.navigator.push({
81
+    //   title: "Styled",
82
+    //   screen: "example.StyledScreen"
83
+    // });
84
+  }
85
+  onModalPress() {
86
+    console.warn('navigator.showModal not implemented yet');
87
+    // this.props.navigator.showModal({
88
+    //   title: "Modal",
89
+    //   screen: "example.ModalScreen"
90
+    // });
91
+  }
92
+  onLightBoxPress() {
93
+    console.warn('navigator.showLightBox not implemented yet');
94
+    // this.props.navigator.showLightBox({
95
+    //   screen: "example.LightBoxScreen",
96
+    //   style: {
97
+    //     backgroundBlur: "dark"
98
+    //   }
99
+    // });
100
+  }
101
+}
102
+
103
+const styles = StyleSheet.create({
104
+  button: {
105
+    textAlign: 'center',
106
+    fontSize: 18,
107
+    marginBottom: 10,
108
+    marginTop:10,
109
+    color: 'blue'
110
+  }
111
+});

example/src/screens/FirstTabScreen.js → example/src/screens/FirstTabScreen.ios.js View File


+ 133
- 0
example/src/screens/SecondTabScreen.android.js View File

@@ -0,0 +1,133 @@
1
+import React, {
2
+  Component,
3
+  Text,
4
+  View,
5
+  ScrollView,
6
+  TouchableOpacity,
7
+  StyleSheet
8
+} from 'react-native';
9
+
10
+export default class SecondTabScreen extends Component {
11
+  static navigatorStyle = {
12
+    drawUnderTabBar: true
13
+  };
14
+  constructor(props) {
15
+    super(props);
16
+    this.buttonsCounter = 0;
17
+    // if you want to listen on navigator events, set this up
18
+    // this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
19
+  }
20
+  render() {
21
+    return (
22
+      <View style={{flex: 1, padding: 20}}>
23
+
24
+        <TouchableOpacity onPress={ this.onChangeButtonsPress.bind(this) }>
25
+          <Text style={styles.button}>Change Buttons</Text>
26
+        </TouchableOpacity>
27
+
28
+        <TouchableOpacity onPress={ this.onChangeTitlePress.bind(this) }>
29
+          <Text style={styles.button}>Change Title</Text>
30
+        </TouchableOpacity>
31
+
32
+        <TouchableOpacity onPress={ this.onSwitchTabPress.bind(this) }>
33
+          <Text style={styles.button}>Switch To Tab#1</Text>
34
+        </TouchableOpacity>
35
+
36
+        <TouchableOpacity onPress={ this.onSetTabBadgePress.bind(this) }>
37
+          <Text style={styles.button}>Set Tab Badge</Text>
38
+        </TouchableOpacity>
39
+
40
+        <TouchableOpacity onPress={ this.onToggleTabsPress.bind(this) }>
41
+          <Text style={styles.button}>Toggle Tabs</Text>
42
+        </TouchableOpacity>
43
+
44
+      </View>
45
+    );
46
+  }
47
+  onChangeTitlePress() {
48
+    this.props.navigator.setTitle({
49
+      title: Math.round(Math.random() * 100000).toString()
50
+    });
51
+  }
52
+  onChangeButtonsPress() {
53
+    let buttons;
54
+    if (this.buttonsCounter % 3 == 0) {
55
+      buttons = [
56
+        {
57
+          title: 'Edit',
58
+          id: 'edit',
59
+          disabled: true
60
+        },
61
+        {
62
+          icon: require('../../img/navicon_add.png'),
63
+          id: 'add'
64
+        }
65
+      ];
66
+    } else if (this.buttonsCounter % 3 == 1) {
67
+      buttons = [{
68
+        title: 'Save',
69
+        id: 'save'
70
+      }];
71
+    } else {
72
+      buttons = [];
73
+    }
74
+    this.buttonsCounter++;
75
+
76
+    this.props.navigator.setButtons({
77
+      rightButtons: buttons,
78
+      animated: true
79
+    });
80
+  }
81
+  onSwitchTabPress() {
82
+    this.props.navigator.switchToTab({
83
+      tabIndex: 0
84
+    });
85
+  }
86
+  onSetTabBadgePress() {
87
+    this.props.navigator.setTabBadge({
88
+      badge: 12
89
+    });
90
+  }
91
+  onToggleTabsPress() {
92
+    this.props.navigator.toggleTabs({
93
+      to: this.tabsHidden ? 'shown' : 'hidden'
94
+    });
95
+    this.tabsHidden = !this.tabsHidden;
96
+  }
97
+  onNavigatorEvent(event) {
98
+    // handle a deep link
99
+    if (event.type == 'DeepLink') {
100
+      const parts = event.link.split('/');
101
+      if (parts[0] == 'tab2') {
102
+        this.props.navigator.resetTo({
103
+          title: "Replaced Root",
104
+          screen: parts[1],
105
+          animated: true
106
+        });
107
+        this.props.navigator.switchToTab();
108
+      }
109
+    }
110
+    // handle a button press
111
+    // if (event.type == 'NavBarButtonPress') {
112
+    //   if (event.id == 'edit') {
113
+    //     AlertIOS.alert('NavBar', 'Dynamic Edit button pressed');
114
+    //   }
115
+    //   if (event.id == 'add') {
116
+    //     AlertIOS.alert('NavBar', 'Dynamic Add button pressed');
117
+    //   }
118
+    //   if (event.id == 'save') {
119
+    //     AlertIOS.alert('NavBar', 'Dynamic Save button pressed');
120
+    //   }
121
+    // }
122
+  }
123
+}
124
+
125
+const styles = StyleSheet.create({
126
+  button: {
127
+    textAlign: 'center',
128
+    fontSize: 18,
129
+    marginBottom: 10,
130
+    marginTop:10,
131
+    color: 'blue'
132
+  }
133
+});

example/src/screens/SecondTabScreen.js → example/src/screens/SecondTabScreen.ios.js View File


+ 20
- 0
example/src/screens/index.android.js View File

@@ -0,0 +1,20 @@
1
+import { Navigation } from 'react-native-navigation';
2
+
3
+import FirstTabScreen from './FirstTabScreen';
4
+import SecondTabScreen from './SecondTabScreen';
5
+import ThirdTabScreen from './ThirdTabScreen';
6
+import PushedScreen from './PushedScreen';
7
+import StyledScreen from './StyledScreen';
8
+import ModalScreen from './ModalScreen';
9
+import LightBoxScreen from './LightBoxScreen';
10
+import SideMenu from './SideMenu';
11
+
12
+// register all screens of the app (including internal ones)
13
+export function registerScreens() {
14
+  Navigation.registerComponent('example.FirstTabScreen', () => FirstTabScreen);
15
+  Navigation.registerComponent('example.SecondTabScreen', () => SecondTabScreen);
16
+  Navigation.registerComponent('example.ThirdTabScreen', () => ThirdTabScreen);
17
+  Navigation.registerComponent('example.PushedScreen', () => PushedScreen);
18
+  Navigation.registerComponent('example.StyledScreen', () => StyledScreen);
19
+  Navigation.registerComponent('example.SideMenu', () => SideMenu);
20
+}

example/src/screens/index.js → example/src/screens/index.ios.js View File


+ 3
- 1
index.js View File

@@ -1,7 +1,9 @@
1 1
 import Navigation from './src/Navigation';
2 2
 import Screen from './src/Screen';
3
+import RctActivity from './src/modules/RctActivity';
3 4
 
4 5
 export {
5 6
   Navigation,
6
-  Screen
7
+  Screen,
8
+  RctActivity
7 9
 };

+ 7
- 4
src/Navigation.js View File

@@ -10,8 +10,11 @@ function registerScreen(screenID, generator) {
10 10
 }
11 11
 
12 12
 function registerComponent(screenID, generator, store = undefined, Provider = undefined) {
13
-  if (store && Provider) return _registerComponentRedux(screenID, generator, store, Provider);
14
-  else return _registerComponentNoRedux(screenID, generator);
13
+  if (store && Provider) {
14
+    return _registerComponentRedux(screenID, generator, store, Provider);
15
+  } else {
16
+    return _registerComponentNoRedux(screenID, generator);
17
+  }
15 18
 }
16 19
 
17 20
 function _registerComponentNoRedux(screenID, generator) {
@@ -26,7 +29,7 @@ function _registerComponentNoRedux(screenID, generator) {
26 29
         );
27 30
       }
28 31
     };
29
-  }
32
+  };
30 33
   registerScreen(screenID, generatorWrapper);
31 34
   return generatorWrapper;
32 35
 }
@@ -45,7 +48,7 @@ function _registerComponentRedux(screenID, generator, store, Provider) {
45 48
         );
46 49
       }
47 50
     };
48
-  }
51
+  };
49 52
   registerScreen(screenID, generatorWrapper);
50 53
   return generatorWrapper;
51 54
 }

+ 28
- 2
src/Screen.js View File

@@ -1,4 +1,9 @@
1
-import { Component, NativeAppEventEmitter } from 'react-native';
1
+import {
2
+  Component,
3
+  NativeAppEventEmitter,
4
+  DeviceEventEmitter,
5
+  Platform
6
+} from 'react-native';
2 7
 import platformSpecific from './platformSpecific';
3 8
 import Navigation from './Navigation';
4 9
 
@@ -11,55 +16,72 @@ class Navigator {
11 16
     this.navigatorEventHandler = null;
12 17
     this.navigatorEventSubscription = null;
13 18
   }
19
+
14 20
   push(params = {}) {
15 21
     return platformSpecific.navigatorPush(this, params);
16 22
   }
23
+
17 24
   pop(params = {}) {
18 25
     return platformSpecific.navigatorPop(this, params);
19 26
   }
27
+
20 28
   popToRoot(params = {}) {
21 29
     return platformSpecific.navigatorPopToRoot(this, params);
22 30
   }
31
+
23 32
   resetTo(params = {}) {
24 33
     return platformSpecific.navigatorResetTo(this, params);
25 34
   }
35
+
26 36
   showModal(params = {}) {
27 37
     return Navigation.showModal(params);
28 38
   }
39
+
29 40
   dismissModal(params = {}) {
30 41
     return Navigation.dismissModal(params);
31 42
   }
43
+
32 44
   dismissAllModals(params = {}) {
33 45
     return Navigation.dismissAllModals(params);
34 46
   }
47
+
35 48
   showLightBox(params = {}) {
36 49
     return Navigation.showLightBox(params);
37 50
   }
51
+
38 52
   dismissLightBox(params = {}) {
39 53
     return Navigation.dismissLightBox(params);
40 54
   }
55
+
41 56
   setButtons(params = {}) {
42 57
     return platformSpecific.navigatorSetButtons(this, this.navigatorEventID, params);
43 58
   }
59
+
44 60
   setTitle(params = {}) {
45 61
     return platformSpecific.navigatorSetTitle(this, params);
46 62
   }
63
+
47 64
   toggleDrawer(params = {}) {
48 65
     return platformSpecific.navigatorToggleDrawer(this, params);
49 66
   }
67
+
50 68
   toggleTabs(params = {}) {
51 69
     return platformSpecific.navigatorToggleTabs(this, params);
52 70
   }
71
+
53 72
   setTabBadge(params = {}) {
54 73
     return platformSpecific.navigatorSetTabBadge(this, params);
55 74
   }
75
+
56 76
   switchToTab(params = {}) {
57 77
     return platformSpecific.navigatorSwitchToTab(this, params);
58 78
   }
79
+
59 80
   setOnNavigatorEvent(callback) {
60 81
     this.navigatorEventHandler = callback;
61 82
     if (!this.navigatorEventSubscription) {
62
-      this.navigatorEventSubscription = NativeAppEventEmitter.addListener(this.navigatorEventID, (event) => this.onNavigatorEvent(event));
83
+      let Emitter = Platform.OS === 'android' ? DeviceEventEmitter : NativeAppEventEmitter;
84
+      this.navigatorEventSubscription = Emitter.addListener(this.navigatorEventID, (event) => this.onNavigatorEvent(event));
63 85
       _allNavigatorEventHandlers[this.navigatorEventID] = (event) => this.onNavigatorEvent(event);
64 86
     }
65 87
   }
@@ -73,11 +95,13 @@ class Navigator {
73 95
       _allNavigatorEventHandlers[i](event);
74 96
     }
75 97
   }
98
+
76 99
   onNavigatorEvent(event) {
77 100
     if (this.navigatorEventHandler) {
78 101
       this.navigatorEventHandler(event);
79 102
     }
80 103
   }
104
+
81 105
   cleanup() {
82 106
     if (this.navigatorEventSubscription) {
83 107
       this.navigatorEventSubscription.remove();
@@ -89,12 +113,14 @@ class Navigator {
89 113
 export default class Screen extends Component {
90 114
   static navigatorStyle = {};
91 115
   static navigatorButtons = {};
116
+  
92 117
   constructor(props) {
93 118
     super(props);
94 119
     if (props.navigatorID) {
95 120
       this.navigator = new Navigator(props.navigatorID, props.navigatorEventID);
96 121
     }
97 122
   }
123
+
98 124
   componentWillUnmount() {
99 125
     if (this.navigator) {
100 126
       this.navigator.cleanup();

+ 2
- 0
src/modules/RctActivity.js View File

@@ -0,0 +1,2 @@
1
+import { NativeModules } from 'react-native';
2
+module.exports = NativeModules.RctActivity;

+ 80
- 0
src/platformSpecific.android.js View File

@@ -0,0 +1,80 @@
1
+import React, {
2
+  AppRegistry,
3
+  Component
4
+} from 'react-native';
5
+
6
+import Navigation from './Navigation';
7
+import utils from './utils';
8
+
9
+import {
10
+  RctActivity
11
+} from 'react-native-navigation';
12
+
13
+var resolveAssetSource = require('resolveAssetSource');
14
+
15
+function startSingleScreenApp(params) {
16
+  let screen = params.screen;
17
+  if (!screen.screen) {
18
+    console.error('startSingleScreenApp(params): screen must include a screen property');
19
+    return;
20
+  }
21
+
22
+  addNavigatorParams(screen);
23
+  addNavigatorButtons(screen);
24
+  RctActivity.startSingleScreenApp(screen);
25
+}
26
+
27
+function startTabBasedApp(params) {
28
+  if (!params.tabs) {
29
+    console.error('startTabBasedApp(params): params.tabs is required');
30
+    return;
31
+  }
32
+
33
+  params.tabs.forEach(function (tab, idx) {
34
+    addNavigatorParams(tab, null, idx)
35
+    addNavigatorButtons(tab);
36
+  });
37
+
38
+  RctActivity.startTabBasedApp(params.tabs);
39
+}
40
+
41
+function navigatorPush(navigator, params) {
42
+  addNavigatorParams(params, navigator)
43
+  addNavigatorButtons(params);
44
+  RctActivity.navigatorPush(params);
45
+}
46
+
47
+function navigatorPop(navigator, params) {
48
+  RctActivity.navigatorPop(navigator);
49
+}
50
+
51
+function addNavigatorParams(screen, navigator = null, idx = '') {
52
+  screen.navigatorID = navigator ? navigator.navigatorID : utils.getRandomId() + '_nav' + idx;
53
+  screen.screenInstanceID = utils.getRandomId();
54
+  screen.navigatorEventID = navigator ? navigator.navigatorEventID : screen.screenInstanceID + '_events';
55
+}
56
+
57
+function addNavigatorButtons(screen) {
58
+  const Screen = Navigation.getRegisteredScreen(screen.screen);
59
+  Object.assign(screen, Screen.navigatorButtons);
60
+
61
+  // Get image uri from image id
62
+  if (screen.rightButtons) {
63
+    screen.rightButtons.forEach(function(button) {
64
+      if (button.icon) {
65
+        const icon = resolveAssetSource(button.icon);
66
+        if (icon) {
67
+          button.icon = icon.uri;
68
+        }
69
+        console.log('final icon: ' + button.icon);
70
+      }
71
+    });
72
+  }
73
+}
74
+
75
+export default {
76
+  startSingleScreenApp,
77
+  startTabBasedApp,
78
+  navigatorPush,
79
+  navigatorPop
80
+}

+ 3
- 5
src/platformSpecific.ios.js View File

@@ -38,9 +38,8 @@ function startTabBasedApp(params) {
38 38
       return (
39 39
         <TabBarControllerIOS
40 40
           id={controllerID + '_tabs'}
41
-          style={params.tabsStyle}
42
-        >
43
-        {
41
+          style={params.tabsStyle}>
42
+          {
44 43
           params.tabs.map(function(tab, index) {
45 44
             const navigatorID = controllerID + '_nav' + index;
46 45
             const screenInstanceID = utils.getRandomId();
@@ -98,8 +97,7 @@ function startSingleScreenApp(params) {
98 97
             passPropsLeft={{navigatorID: navigatorID}}
99 98
             componentRight={params.drawer.right ? params.drawer.right.screen : undefined}
100 99
             passPropsRight={{navigatorID: navigatorID}}
101
-            disableOpenGesture={params.drawer.disableOpenGesture}
102
-          >
100
+            disableOpenGesture={params.drawer.disableOpenGesture}>
103 101
             {this.renderBody()}
104 102
           </DrawerControllerIOS>
105 103
         );