package com.reactnativenavigation.controllers;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.KeyEvent;
import android.view.Window;
import com.facebook.react.bridge.Callback;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.modules.core.PermissionAwareActivity;
import com.facebook.react.modules.core.PermissionListener;
import com.reactnativenavigation.NavigationApplication;
import com.reactnativenavigation.events.Event;
import com.reactnativenavigation.events.EventBus;
import com.reactnativenavigation.events.JsDevReloadEvent;
import com.reactnativenavigation.events.ModalDismissedEvent;
import com.reactnativenavigation.events.Subscriber;
import com.reactnativenavigation.layouts.BottomTabsLayout;
import com.reactnativenavigation.layouts.Layout;
import com.reactnativenavigation.layouts.LayoutFactory;
import com.reactnativenavigation.params.ActivityParams;
import com.reactnativenavigation.params.AppStyle;
import com.reactnativenavigation.params.ContextualMenuParams;
import com.reactnativenavigation.params.FabParams;
import com.reactnativenavigation.params.LightBoxParams;
import com.reactnativenavigation.params.ScreenParams;
import com.reactnativenavigation.params.SlidingOverlayParams;
import com.reactnativenavigation.params.SnackbarParams;
import com.reactnativenavigation.params.TitleBarButtonParams;
import com.reactnativenavigation.params.TitleBarLeftButtonParams;
import com.reactnativenavigation.react.ReactGateway;
import com.reactnativenavigation.screens.Screen;
import com.reactnativenavigation.utils.OrientationHelper;
import com.reactnativenavigation.views.SideMenu.Side;
import java.util.List;
public class NavigationActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler, Subscriber, PermissionAwareActivity {
/**
* Although we start multiple activities, we make sure to pass Intent.CLEAR_TASK | Intent.NEW_TASK
* So that we actually have only 1 instance of the activity running at one time.
* We hold the currentActivity (resume->pause) so we know when we need to destroy the javascript context
* (when currentActivity is null, ie pause and destroy was called without resume).
* This is somewhat weird, and in the future we better use a single activity with changing contentView similar to ReactNative impl.
* Along with that, we should handle commands from the bridge using onNewIntent
*/
static NavigationActivity currentActivity;
private ActivityParams activityParams;
private ModalController modalController;
private Layout layout;
@Nullable private PermissionListener mPermissionListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!NavigationApplication.instance.isReactContextInitialized()) {
NavigationApplication.instance.startReactContextOnceInBackgroundAndExecuteJS();
return;
}
activityParams = NavigationCommandsHandler.parseActivityParams(getIntent());
disableActivityShowAnimationIfNeeded();
setOrientation();
createModalController();
createLayout();
NavigationApplication.instance.getActivityCallbacks().onActivityCreated(this, savedInstanceState);
}
private void setOrientation() {
OrientationHelper.setOrientation(this, AppStyle.appStyle.orientation);
}
private void disableActivityShowAnimationIfNeeded() {
if (!activityParams.animateShow) {
overridePendingTransition(0, 0);
}
}
private void createModalController() {
modalController = new ModalController(this);
}
private void createLayout() {
layout = LayoutFactory.create(this, activityParams);
if (hasBackgroundColor()) {
layout.asView().setBackgroundColor(AppStyle.appStyle.screenBackgroundColor.getColor());
}
setContentView(layout.asView());
}
private boolean hasBackgroundColor() {
return AppStyle.appStyle.screenBackgroundColor != null &&
AppStyle.appStyle.screenBackgroundColor.hasColor();
}
@Override
protected void onStart() {
super.onStart();
NavigationApplication.instance.getActivityCallbacks().onActivityStarted(this);
}
@Override
protected void onResume() {
super.onResume();
if (isFinishing() || !NavigationApplication.instance.isReactContextInitialized()) {
return;
}
currentActivity = this;
IntentDataHandler.onResume(getIntent());
getReactGateway().onResumeActivity(this, this);
NavigationApplication.instance.getActivityCallbacks().onActivityResumed(this);
EventBus.instance.register(this);
IntentDataHandler.onPostResume(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
getReactGateway().onNewIntent(intent);
NavigationApplication.instance.getActivityCallbacks().onNewIntent(intent);
}
@Override
protected void onPause() {
super.onPause();
currentActivity = null;
IntentDataHandler.onPause(getIntent());
getReactGateway().onPauseActivity();
NavigationApplication.instance.getActivityCallbacks().onActivityPaused(this);
EventBus.instance.unregister(this);
}
@Override
protected void onStop() {
super.onStop();
NavigationApplication.instance.getActivityCallbacks().onActivityStopped(this);
}
@Override
protected void onDestroy() {
destroyLayouts();
destroyJsIfNeeded();
NavigationApplication.instance.getActivityCallbacks().onActivityDestroyed(this);
super.onDestroy();
}
private void destroyLayouts() {
if (modalController != null) {
modalController.destroy();
}
if (layout != null) {
layout.destroy();
layout = null;
}
}
private void destroyJsIfNeeded() {
if (currentActivity == null || currentActivity.isFinishing()) {
getReactGateway().onDestroyApp();
}
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
@Override
public void onBackPressed() {
if (layout != null && !layout.onBackPressed()) {
getReactGateway().onBackPressed();
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
getReactGateway().onActivityResult(requestCode, resultCode, data);
NavigationApplication.instance.getActivityCallbacks().onActivityResult(requestCode, resultCode, data);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return getReactGateway().onKeyUp(getCurrentFocus(), keyCode) || super.onKeyUp(keyCode, event);
}
private ReactGateway getReactGateway() {
return NavigationApplication.instance.getReactGateway();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
OrientationHelper.onConfigurationChanged(newConfig);
NavigationApplication.instance.getActivityCallbacks().onConfigurationChanged(newConfig);
super.onConfigurationChanged(newConfig);
}
void push(ScreenParams params) {
if (modalController.containsNavigator(params.getNavigatorId())) {
modalController.push(params);
} else {
layout.push(params);
}
}
void pop(ScreenParams params) {
if (modalController.containsNavigator(params.getNavigatorId())) {
modalController.pop(params);
} else {
layout.pop(params);
}
}
void popToRoot(ScreenParams params) {
if (modalController.containsNavigator(params.getNavigatorId())) {
modalController.popToRoot(params);
} else {
layout.popToRoot(params);
}
}
void newStack(ScreenParams params) {
if (modalController.containsNavigator(params.getNavigatorId())) {
modalController.newStack(params);
} else {
layout.newStack(params);
}
}
void showModal(ScreenParams screenParams) {
Screen previousScreen = layout.getCurrentScreen();
NavigationApplication.instance.getEventEmitter().sendScreenChangedEvent("willDisappear", previousScreen.getNavigatorEventId());
NavigationApplication.instance.getEventEmitter().sendScreenChangedEvent("didDisappear", previousScreen.getNavigatorEventId());
modalController.showModal(screenParams);
}
void dismissTopModal() {
modalController.dismissTopModal();
Screen previousScreen = layout.getCurrentScreen();
NavigationApplication.instance.getEventEmitter().sendScreenChangedEvent("willAppear", previousScreen.getNavigatorEventId());
NavigationApplication.instance.getEventEmitter().sendScreenChangedEvent("didAppear", previousScreen.getNavigatorEventId());
}
void dismissAllModals() {
modalController.dismissAllModals();
Screen previousScreen = layout.getCurrentScreen();
NavigationApplication.instance.getEventEmitter().sendScreenChangedEvent("willAppear", previousScreen.getNavigatorEventId());
NavigationApplication.instance.getEventEmitter().sendScreenChangedEvent("didAppear", previousScreen.getNavigatorEventId());
}
public void showLightBox(LightBoxParams params) {
layout.showLightBox(params);
}
public void dismissLightBox() {
layout.dismissLightBox();
}
//TODO all these setters should be combined to something like setStyle
void setTopBarVisible(String screenInstanceId, boolean hidden, boolean animated) {
layout.setTopBarVisible(screenInstanceId, hidden, animated);
modalController.setTopBarVisible(screenInstanceId, hidden, animated);
}
void setBottomTabsVisible(boolean hidden, boolean animated) {
if (layout instanceof BottomTabsLayout) {
((BottomTabsLayout) layout).setBottomTabsVisible(hidden, animated);
}
}
void setTitleBarTitle(String screenInstanceId, String title) {
layout.setTitleBarTitle(screenInstanceId, title);
modalController.setTitleBarTitle(screenInstanceId, title);
}
public void setTitleBarSubtitle(String screenInstanceId, String subtitle) {
layout.setTitleBarSubtitle(screenInstanceId, subtitle);
modalController.setTitleBarSubtitle(screenInstanceId, subtitle);
}
void setTitleBarButtons(String screenInstanceId, String navigatorEventId, List<TitleBarButtonParams> titleBarButtons) {
layout.setTitleBarRightButtons(screenInstanceId, navigatorEventId, titleBarButtons);
modalController.setTitleBarRightButtons(screenInstanceId, navigatorEventId, titleBarButtons);
}
void setTitleBarLeftButton(String screenInstanceId, String navigatorEventId, TitleBarLeftButtonParams titleBarLeftButton) {
layout.setTitleBarLeftButton(screenInstanceId, navigatorEventId, titleBarLeftButton);
modalController.setTitleBarLeftButton(screenInstanceId, navigatorEventId, titleBarLeftButton);
}
void setScreenFab(String screenInstanceId, String navigatorEventId, FabParams fab) {
layout.setFab(screenInstanceId, navigatorEventId, fab);
modalController.setFab(screenInstanceId, navigatorEventId, fab);
}
public void setScreenStyle(String screenInstanceId, Bundle styleParams) {
layout.updateScreenStyle(screenInstanceId, styleParams);
modalController.updateScreenStyle(screenInstanceId, styleParams);
}
public void toggleSideMenuVisible(boolean animated, Side side) {
layout.toggleSideMenuVisible(animated, side);
}
public void setSideMenuVisible(boolean animated, boolean visible, Side side) {
layout.setSideMenuVisible(animated, visible, side);
}
public void selectTopTabByTabIndex(String screenInstanceId, int index) {
layout.selectTopTabByTabIndex(screenInstanceId, index);
modalController.selectTopTabByTabIndex(screenInstanceId, index);
}
public void selectTopTabByScreen(String screenInstanceId) {
layout.selectTopTabByScreen(screenInstanceId);
modalController.selectTopTabByScreen(screenInstanceId);
}
public void selectBottomTabByTabIndex(Integer index) {
if (layout instanceof BottomTabsLayout) {
((BottomTabsLayout) layout).selectBottomTabByTabIndex(index);
}
}
public void selectBottomTabByNavigatorId(String navigatorId) {
if (layout instanceof BottomTabsLayout) {
((BottomTabsLayout) layout).selectBottomTabByNavigatorId(navigatorId);
}
}
public void setBottomTabBadgeByIndex(Integer index, String badge) {
if (layout instanceof BottomTabsLayout) {
((BottomTabsLayout) layout).setBottomTabBadgeByIndex(index, badge);
}
}
public void setBottomTabBadgeByNavigatorId(String navigatorId, String badge) {
if (layout instanceof BottomTabsLayout) {
((BottomTabsLayout) layout).setBottomTabBadgeByNavigatorId(navigatorId, badge);
}
}
public void setBottomTabButtonByIndex(Integer index, ScreenParams params) {
if (layout instanceof BottomTabsLayout) {
((BottomTabsLayout) layout).setBottomTabButtonByIndex(index, params);
}
}
public void setBottomTabButtonByNavigatorId(String navigatorId, ScreenParams params) {
if (layout instanceof BottomTabsLayout) {
((BottomTabsLayout) layout).setBottomTabButtonByNavigatorId(navigatorId, params);
}
}
public void showSlidingOverlay(SlidingOverlayParams params) {
if (modalController.isShowing()) {
modalController.showSlidingOverlay(params);
} else {
layout.showSlidingOverlay(params);
}
}
public void hideSlidingOverlay() {
if (modalController.isShowing()) {
modalController.hideSlidingOverlay();
} else {
layout.hideSlidingOverlay();
}
}
public void showSnackbar(SnackbarParams params) {
layout.showSnackbar(params);
}
public void dismissSnackbar() {
layout.dismissSnackbar();
}
public void showContextualMenu(String screenInstanceId, ContextualMenuParams params, Callback onButtonClicked) {
if (modalController.isShowing()) {
modalController.showContextualMenu(screenInstanceId, params, onButtonClicked);
} else
{
layout.showContextualMenu(screenInstanceId, params, onButtonClicked);
}
}
public void dismissContextualMenu(String screenInstanceId) {
if (modalController.isShowing()) {
modalController.dismissContextualMenu(screenInstanceId);
} else {
layout.dismissContextualMenu(screenInstanceId);
}
}
@Override
public void onEvent(Event event) {
if (event.getType().equals(ModalDismissedEvent.TYPE)) {
handleModalDismissedEvent();
} else if (event.getType().equals(JsDevReloadEvent.TYPE)) {
postHandleJsDevReloadEvent();
}
}
private void handleModalDismissedEvent() {
if (!modalController.isShowing()) {
layout.onModalDismissed();
OrientationHelper.setOrientation(this, AppStyle.appStyle.orientation);
}
}
public Window getScreenWindow() {
return modalController.isShowing() ? modalController.getWindow() : getWindow();
}
private void postHandleJsDevReloadEvent() {
NavigationApplication.instance.runOnMainThread(new Runnable() {
@Override
public void run() {
layout.destroy();
modalController.destroy();
}
});
}
@TargetApi(Build.VERSION_CODES.M)
public void requestPermissions(String[] permissions, int requestCode, PermissionListener listener) {
mPermissionListener = listener;
requestPermissions(permissions, requestCode);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
NavigationApplication.instance.getActivityCallbacks().onRequestPermissionsResult(requestCode, permissions, grantResults);
if (mPermissionListener != null && mPermissionListener.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
mPermissionListener = null;
}
}
}