react-native-navigation的迁移库

NavigationActivity.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. package com.reactnativenavigation.controllers;
  2. import android.annotation.TargetApi;
  3. import android.content.Intent;
  4. import android.content.res.Configuration;
  5. import android.os.Build;
  6. import android.os.Bundle;
  7. import android.support.annotation.NonNull;
  8. import android.support.annotation.Nullable;
  9. import android.support.v7.app.AppCompatActivity;
  10. import android.view.KeyEvent;
  11. import android.view.Window;
  12. import com.facebook.react.bridge.Callback;
  13. import com.facebook.react.bridge.Promise;
  14. import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
  15. import com.facebook.react.modules.core.PermissionAwareActivity;
  16. import com.facebook.react.modules.core.PermissionListener;
  17. import com.reactnativenavigation.NavigationApplication;
  18. import com.reactnativenavigation.events.Event;
  19. import com.reactnativenavigation.events.EventBus;
  20. import com.reactnativenavigation.events.JsDevReloadEvent;
  21. import com.reactnativenavigation.events.ModalDismissedEvent;
  22. import com.reactnativenavigation.events.Subscriber;
  23. import com.reactnativenavigation.layouts.BottomTabsLayout;
  24. import com.reactnativenavigation.layouts.Layout;
  25. import com.reactnativenavigation.layouts.LayoutFactory;
  26. import com.reactnativenavigation.params.ActivityParams;
  27. import com.reactnativenavigation.params.AppStyle;
  28. import com.reactnativenavigation.params.ContextualMenuParams;
  29. import com.reactnativenavigation.params.FabParams;
  30. import com.reactnativenavigation.params.LightBoxParams;
  31. import com.reactnativenavigation.params.ScreenParams;
  32. import com.reactnativenavigation.params.SlidingOverlayParams;
  33. import com.reactnativenavigation.params.SnackbarParams;
  34. import com.reactnativenavigation.params.TitleBarButtonParams;
  35. import com.reactnativenavigation.params.TitleBarLeftButtonParams;
  36. import com.reactnativenavigation.react.ReactGateway;
  37. import com.reactnativenavigation.screens.NavigationType;
  38. import com.reactnativenavigation.screens.Screen;
  39. import com.reactnativenavigation.utils.OrientationHelper;
  40. import com.reactnativenavigation.utils.ReflectionUtils;
  41. import com.reactnativenavigation.views.SideMenu.Side;
  42. import java.util.List;
  43. public class NavigationActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler, Subscriber, PermissionAwareActivity {
  44. /**
  45. * Although we start multiple activities, we make sure to pass Intent.CLEAR_TASK | Intent.NEW_TASK
  46. * So that we actually have only 1 instance of the activity running at one time.
  47. * We hold the currentActivity (resume->pause) so we know when we need to destroy the javascript context
  48. * (when currentActivity is null, ie pause and destroy was called without resume).
  49. * This is somewhat weird, and in the future we better use a single activity with changing contentView similar to ReactNative impl.
  50. * Along with that, we should handle commands from the bridge using onNewIntent
  51. */
  52. static NavigationActivity currentActivity;
  53. private static Promise startAppPromise;
  54. private ActivityParams activityParams;
  55. private ModalController modalController;
  56. private Layout layout;
  57. @Nullable
  58. private PermissionListener mPermissionListener;
  59. @Override
  60. protected void onCreate(Bundle savedInstanceState) {
  61. super.onCreate(savedInstanceState);
  62. if (!NavigationApplication.instance.getReactGateway().hasStartedCreatingContext() ||
  63. getIntent() == null ||
  64. getIntent().getBundleExtra("ACTIVITY_PARAMS_BUNDLE") == null) {
  65. SplashActivity.start(this);
  66. finish();
  67. return;
  68. }
  69. activityParams = NavigationCommandsHandler.parseActivityParams(getIntent());
  70. disableActivityShowAnimationIfNeeded();
  71. setOrientation();
  72. createModalController();
  73. createLayout();
  74. NavigationApplication.instance.getActivityCallbacks().onActivityCreated(this, savedInstanceState);
  75. }
  76. private void setOrientation() {
  77. OrientationHelper.setOrientation(this, AppStyle.appStyle.orientation);
  78. }
  79. private void disableActivityShowAnimationIfNeeded() {
  80. if (!activityParams.animateShow) {
  81. overridePendingTransition(0, 0);
  82. }
  83. }
  84. private void createModalController() {
  85. modalController = new ModalController(this);
  86. }
  87. private void createLayout() {
  88. layout = LayoutFactory.create(this, activityParams);
  89. if (hasBackgroundColor()) {
  90. layout.asView().setBackgroundColor(AppStyle.appStyle.screenBackgroundColor.getColor());
  91. }
  92. String rootBackgroundImageName = getRootBackgroundImageName();
  93. if (rootBackgroundImageName != null) {
  94. layout.asView().setBackgroundResource(this.getResources().getIdentifier(rootBackgroundImageName, "drawable", this.getPackageName()));
  95. }
  96. setContentView(layout.asView());
  97. }
  98. private String getRootBackgroundImageName() {
  99. return activityParams.screenParams == null ? null : activityParams.screenParams.styleParams.rootBackgroundImageName;
  100. }
  101. private boolean hasBackgroundColor() {
  102. return AppStyle.appStyle.screenBackgroundColor != null &&
  103. AppStyle.appStyle.screenBackgroundColor.hasColor();
  104. }
  105. @Override
  106. protected void onStart() {
  107. super.onStart();
  108. NavigationApplication.instance.getActivityCallbacks().onActivityStarted(this);
  109. }
  110. @Override
  111. protected void onResume() {
  112. super.onResume();
  113. if (isFinishing() || !NavigationApplication.instance.isReactContextInitialized()) {
  114. return;
  115. }
  116. currentActivity = this;
  117. IntentDataHandler.onResume(getIntent());
  118. getReactGateway().onResumeActivity(this, this);
  119. resolveStartAppPromiseOnActivityResumed();
  120. NavigationApplication.instance.getActivityCallbacks().onActivityResumed(this);
  121. EventBus.instance.register(this);
  122. IntentDataHandler.onPostResume(getIntent());
  123. NavigationApplication.instance.getEventEmitter().sendActivityResumed(getCurrentlyVisibleEventId());
  124. }
  125. private void resolveStartAppPromiseOnActivityResumed() {
  126. if (startAppPromise != null) {
  127. startAppPromise.resolve(true);
  128. startAppPromise = null;
  129. }
  130. }
  131. @Override
  132. protected void onNewIntent(Intent intent) {
  133. super.onNewIntent(intent);
  134. getReactGateway().onNewIntent(intent);
  135. NavigationApplication.instance.getActivityCallbacks().onNewIntent(intent);
  136. }
  137. @Override
  138. protected void onPause() {
  139. super.onPause();
  140. currentActivity = null;
  141. IntentDataHandler.onPause(getIntent());
  142. getReactGateway().onPauseActivity(this);
  143. NavigationApplication.instance.getActivityCallbacks().onActivityPaused(this);
  144. EventBus.instance.unregister(this);
  145. }
  146. @Override
  147. protected void onStop() {
  148. super.onStop();
  149. clearStartAppPromise();
  150. NavigationApplication.instance.getActivityCallbacks().onActivityStopped(this);
  151. }
  152. private void clearStartAppPromise() {
  153. if (startAppPromise != null) {
  154. startAppPromise = null;
  155. }
  156. }
  157. @Override
  158. protected void onDestroy() {
  159. destroyLayouts();
  160. destroyJsIfNeeded();
  161. NavigationApplication.instance.getActivityCallbacks().onActivityDestroyed(this);
  162. super.onDestroy();
  163. }
  164. private void destroyLayouts() {
  165. if (modalController != null) {
  166. modalController.destroy();
  167. }
  168. if (layout != null) {
  169. layout.destroy();
  170. layout = null;
  171. }
  172. }
  173. private void destroyJsIfNeeded() {
  174. if (currentActivity == null || currentActivity.isFinishing()) {
  175. getReactGateway().onDestroyApp(this);
  176. }
  177. }
  178. @Override
  179. public void invokeDefaultOnBackPressed() {
  180. if (layout != null && !layout.onBackPressed()) {
  181. super.onBackPressed();
  182. }
  183. }
  184. @Override
  185. public void onBackPressed() {
  186. if (layout != null && layout.handleBackInJs()) {
  187. return;
  188. }
  189. if (getReactGateway().isInitialized()) {
  190. getReactGateway().onBackPressed();
  191. } else {
  192. super.onBackPressed();
  193. }
  194. }
  195. @Override
  196. public void onActivityResult(int requestCode, int resultCode, Intent data) {
  197. getReactGateway().onActivityResult(requestCode, resultCode, data);
  198. NavigationApplication.instance.getActivityCallbacks().onActivityResult(requestCode, resultCode, data);
  199. }
  200. @Override
  201. public boolean onKeyUp(int keyCode, KeyEvent event) {
  202. NavigationApplication.instance.getActivityCallbacks().onKeyUp(keyCode, event);
  203. return getReactGateway().onKeyUp(getCurrentFocus(), keyCode) || super.onKeyUp(keyCode, event);
  204. }
  205. private ReactGateway getReactGateway() {
  206. return NavigationApplication.instance.getReactGateway();
  207. }
  208. @Override
  209. public void onConfigurationChanged(Configuration newConfig) {
  210. OrientationHelper.onConfigurationChanged(newConfig);
  211. NavigationApplication.instance.getActivityCallbacks().onConfigurationChanged(newConfig);
  212. super.onConfigurationChanged(newConfig);
  213. }
  214. void push(ScreenParams params, Promise onPushComplete) {
  215. if (modalController.containsNavigator(params.getNavigatorId())) {
  216. modalController.push(params, onPushComplete);
  217. } else {
  218. layout.push(params, onPushComplete);
  219. }
  220. }
  221. void pop(ScreenParams params) {
  222. if (modalController.containsNavigator(params.getNavigatorId())) {
  223. modalController.pop(params);
  224. } else {
  225. layout.pop(params);
  226. }
  227. }
  228. void popToRoot(ScreenParams params) {
  229. if (modalController.containsNavigator(params.getNavigatorId())) {
  230. modalController.popToRoot(params);
  231. } else {
  232. layout.popToRoot(params);
  233. }
  234. }
  235. void newStack(ScreenParams params) {
  236. if (modalController.containsNavigator(params.getNavigatorId())) {
  237. modalController.newStack(params);
  238. } else {
  239. layout.newStack(params);
  240. }
  241. }
  242. void showModal(ScreenParams screenParams) {
  243. Screen previousScreen = layout.getCurrentScreen();
  244. NavigationApplication.instance.getEventEmitter().sendWillDisappearEvent(previousScreen.getScreenParams(), NavigationType.ShowModal);
  245. NavigationApplication.instance.getEventEmitter().sendDidDisappearEvent(previousScreen.getScreenParams(), NavigationType.ShowModal);
  246. modalController.showModal(screenParams);
  247. }
  248. void dismissTopModal(ScreenParams params) {
  249. modalController.dismissTopModal(params);
  250. }
  251. void dismissAllModals() {
  252. modalController.dismissAllModals();
  253. }
  254. public void showLightBox(LightBoxParams params) {
  255. layout.showLightBox(params);
  256. }
  257. public void dismissLightBox() {
  258. layout.dismissLightBox();
  259. }
  260. //TODO all these setters should be combined to something like setStyle
  261. void setTopBarVisible(String screenInstanceId, boolean hidden, boolean animated) {
  262. layout.setTopBarVisible(screenInstanceId, hidden, animated);
  263. modalController.setTopBarVisible(screenInstanceId, hidden, animated);
  264. }
  265. void setBottomTabsVisible(boolean hidden, boolean animated) {
  266. if (layout instanceof BottomTabsLayout) {
  267. ((BottomTabsLayout) layout).setBottomTabsVisible(hidden, animated);
  268. }
  269. }
  270. void setTitleBarTitle(String screenInstanceId, String title) {
  271. layout.setTitleBarTitle(screenInstanceId, title);
  272. modalController.setTitleBarTitle(screenInstanceId, title);
  273. }
  274. public void setTitleBarSubtitle(String screenInstanceId, String subtitle) {
  275. layout.setTitleBarSubtitle(screenInstanceId, subtitle);
  276. modalController.setTitleBarSubtitle(screenInstanceId, subtitle);
  277. }
  278. void setTitleBarButtons(String screenInstanceId, String navigatorEventId, List<TitleBarButtonParams> titleBarButtons) {
  279. layout.setTitleBarRightButtons(screenInstanceId, navigatorEventId, titleBarButtons);
  280. modalController.setTitleBarRightButtons(screenInstanceId, navigatorEventId, titleBarButtons);
  281. }
  282. void setTitleBarLeftButton(String screenInstanceId, String navigatorEventId, TitleBarLeftButtonParams titleBarLeftButton) {
  283. layout.setTitleBarLeftButton(screenInstanceId, navigatorEventId, titleBarLeftButton);
  284. modalController.setTitleBarLeftButton(screenInstanceId, navigatorEventId, titleBarLeftButton);
  285. }
  286. void setScreenFab(String screenInstanceId, String navigatorEventId, FabParams fab) {
  287. layout.setFab(screenInstanceId, navigatorEventId, fab);
  288. modalController.setFab(screenInstanceId, navigatorEventId, fab);
  289. }
  290. public void setScreenStyle(String screenInstanceId, Bundle styleParams) {
  291. layout.updateScreenStyle(screenInstanceId, styleParams);
  292. modalController.updateScreenStyle(screenInstanceId, styleParams);
  293. }
  294. public void toggleSideMenuVisible(boolean animated, Side side) {
  295. layout.toggleSideMenuVisible(animated, side);
  296. }
  297. public void setSideMenuVisible(boolean animated, boolean visible, Side side) {
  298. layout.setSideMenuVisible(animated, visible, side);
  299. }
  300. public void setSideMenuEnabled(boolean enabled, Side side) {
  301. layout.setSideMenuEnabled(enabled, side);
  302. }
  303. public void selectTopTabByTabIndex(String screenInstanceId, int index) {
  304. layout.selectTopTabByTabIndex(screenInstanceId, index);
  305. modalController.selectTopTabByTabIndex(screenInstanceId, index);
  306. }
  307. public void selectTopTabByScreen(String screenInstanceId) {
  308. layout.selectTopTabByScreen(screenInstanceId);
  309. modalController.selectTopTabByScreen(screenInstanceId);
  310. }
  311. public void selectBottomTabByTabIndex(Integer index) {
  312. if (layout instanceof BottomTabsLayout) {
  313. ((BottomTabsLayout) layout).selectBottomTabByTabIndex(index);
  314. }
  315. }
  316. public void selectBottomTabByNavigatorId(String navigatorId) {
  317. if (layout instanceof BottomTabsLayout) {
  318. ((BottomTabsLayout) layout).selectBottomTabByNavigatorId(navigatorId);
  319. }
  320. }
  321. public void setBottomTabBadgeByIndex(Integer index, String badge) {
  322. if (layout instanceof BottomTabsLayout) {
  323. ((BottomTabsLayout) layout).setBottomTabBadgeByIndex(index, badge);
  324. }
  325. }
  326. public void setBottomTabBadgeByNavigatorId(String navigatorId, String badge) {
  327. if (layout instanceof BottomTabsLayout) {
  328. ((BottomTabsLayout) layout).setBottomTabBadgeByNavigatorId(navigatorId, badge);
  329. }
  330. }
  331. public void setBottomTabButtonByIndex(Integer index, ScreenParams params) {
  332. if (layout instanceof BottomTabsLayout) {
  333. ((BottomTabsLayout) layout).setBottomTabButtonByIndex(index, params);
  334. }
  335. }
  336. public void setBottomTabButtonByNavigatorId(String navigatorId, ScreenParams params) {
  337. if (layout instanceof BottomTabsLayout) {
  338. ((BottomTabsLayout) layout).setBottomTabButtonByNavigatorId(navigatorId, params);
  339. }
  340. }
  341. public void showSlidingOverlay(SlidingOverlayParams params) {
  342. if (modalController.isShowing()) {
  343. modalController.showSlidingOverlay(params);
  344. } else {
  345. layout.showSlidingOverlay(params);
  346. }
  347. }
  348. public void hideSlidingOverlay() {
  349. if (modalController.isShowing()) {
  350. modalController.hideSlidingOverlay();
  351. } else {
  352. layout.hideSlidingOverlay();
  353. }
  354. }
  355. public void showSnackbar(SnackbarParams params) {
  356. layout.showSnackbar(params);
  357. }
  358. public void dismissSnackbar() {
  359. layout.dismissSnackbar();
  360. }
  361. public void showContextualMenu(String screenInstanceId, ContextualMenuParams params, Callback onButtonClicked) {
  362. if (modalController.isShowing()) {
  363. modalController.showContextualMenu(screenInstanceId, params, onButtonClicked);
  364. } else {
  365. layout.showContextualMenu(screenInstanceId, params, onButtonClicked);
  366. }
  367. }
  368. public void dismissContextualMenu(String screenInstanceId) {
  369. if (modalController.isShowing()) {
  370. modalController.dismissContextualMenu(screenInstanceId);
  371. } else {
  372. layout.dismissContextualMenu(screenInstanceId);
  373. }
  374. }
  375. @Override
  376. public void onEvent(Event event) {
  377. if (event.getType().equals(ModalDismissedEvent.TYPE)) {
  378. handleModalDismissedEvent();
  379. } else if (event.getType().equals(JsDevReloadEvent.TYPE)) {
  380. postHandleJsDevReloadEvent();
  381. }
  382. }
  383. private void handleModalDismissedEvent() {
  384. if (!modalController.isShowing()) {
  385. layout.onModalDismissed();
  386. OrientationHelper.setOrientation(this, AppStyle.appStyle.orientation);
  387. }
  388. }
  389. public Window getScreenWindow() {
  390. return modalController.isShowing() ? modalController.getWindow() : getWindow();
  391. }
  392. private void postHandleJsDevReloadEvent() {
  393. NavigationApplication.instance.runOnMainThread(new Runnable() {
  394. @Override
  395. public void run() {
  396. layout.destroy();
  397. modalController.destroy();
  398. Object devSupportManager = ReflectionUtils.getDeclaredField(getReactGateway().getReactInstanceManager(), "mDevSupportManager");
  399. if (ReflectionUtils.getDeclaredField(devSupportManager, "mRedBoxDialog") != null) { // RN >= 0.52
  400. ReflectionUtils.setField(devSupportManager, "mRedBoxDialog", null);
  401. }
  402. }
  403. });
  404. }
  405. @TargetApi(Build.VERSION_CODES.M)
  406. public void requestPermissions(String[] permissions, int requestCode, PermissionListener listener) {
  407. mPermissionListener = listener;
  408. requestPermissions(permissions, requestCode);
  409. }
  410. @Override
  411. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  412. NavigationApplication.instance.getActivityCallbacks().onRequestPermissionsResult(requestCode, permissions, grantResults);
  413. if (mPermissionListener != null && mPermissionListener.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
  414. mPermissionListener = null;
  415. }
  416. }
  417. public String getCurrentlyVisibleScreenId() {
  418. return modalController.isShowing() ? modalController.getCurrentlyVisibleScreenId() : layout.getCurrentlyVisibleScreenId();
  419. }
  420. public String getCurrentlyVisibleEventId() {
  421. return modalController.isShowing() ? modalController.getCurrentlyVisibleEventId() : layout.getCurrentScreen().getNavigatorEventId();
  422. }
  423. public static void setStartAppPromise(Promise promise) {
  424. NavigationActivity.startAppPromise = promise;
  425. }
  426. public static void onCatalystInstanceDestroy() {
  427. if (currentActivity == null) {
  428. return;
  429. }
  430. currentActivity.runOnUiThread(new Runnable() {
  431. @Override
  432. public void run() {
  433. if (currentActivity != null) {
  434. currentActivity.destroyLayouts();
  435. }
  436. }
  437. });
  438. }
  439. public Layout getLayout() {
  440. return layout;
  441. }
  442. }