From b96c05fd8d226ee2f835ba1506f85802bbeba4f0 Mon Sep 17 00:00:00 2001 From: Jason Skuby Date: Thu, 23 Jun 2016 23:35:59 -0500 Subject: [PATCH 1/3] Drawer implementation --- .../activities/BaseReactActivity.java | 57 ++++++- .../activities/BottomTabActivity.java | 25 ++- .../activities/SingleScreenActivity.java | 23 +++ .../core/objects/Drawer.java | 23 +++ .../core/objects/Screen.java | 8 +- .../modules/RctActivityModule.java | 25 ++- .../views/RnnToolBar.java | 158 +++++++++++++++--- .../main/res/layout/bottom_tab_activity.xml | 17 +- .../res/layout/single_screen_activity.xml | 16 +- android/app/src/main/res/values/strings.xml | 5 + src/platformSpecific.android.js | 33 +++- 11 files changed, 353 insertions(+), 37 deletions(-) create mode 100644 android/app/src/main/java/com/reactnativenavigation/core/objects/Drawer.java create mode 100644 android/app/src/main/res/values/strings.xml diff --git a/android/app/src/main/java/com/reactnativenavigation/activities/BaseReactActivity.java b/android/app/src/main/java/com/reactnativenavigation/activities/BaseReactActivity.java index 1fc9342f921..56f2b0c055b 100644 --- a/android/app/src/main/java/com/reactnativenavigation/activities/BaseReactActivity.java +++ b/android/app/src/main/java/com/reactnativenavigation/activities/BaseReactActivity.java @@ -1,11 +1,13 @@ package com.reactnativenavigation.activities; import android.content.Intent; +import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.provider.Settings; import android.support.annotation.CallSuper; +import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.KeyEvent; @@ -48,8 +50,10 @@ public abstract class BaseReactActivity extends AppCompatActivity implements Def protected static final String KEY_ANIMATED = "animated"; protected static final String KEY_BADGE = "badge"; protected static final String KEY_HIDDEN = "hidden"; + protected static final String KEY_SIDE = "side"; protected static final String KEY_TAB_INDEX = "tabIndex"; protected static final String KEY_TITLE = "title"; + protected static final String KEY_TO = "to"; private static final String TAG = "BaseReactActivity"; private static final String REDBOX_PERMISSION_MESSAGE = "Overlay permissions needs to be granted in order for react native apps to run in dev mode"; @@ -59,6 +63,7 @@ public abstract class BaseReactActivity extends AppCompatActivity implements Def private boolean mDoRefresh = false; private Menu mMenu; protected RnnToolBar mToolbar; + protected ActionBarDrawerToggle mDrawerToggle; /** * Returns the name of the bundle in assets. If this is null, and no file path is specified for @@ -223,7 +228,7 @@ public void push(Screen screen) { if (getCurrentNavigatorId().equals(screen.navigatorId) && getScreenStackSize() >= 1) { - mToolbar.showBackButton(screen); + mToolbar.setNavUpButton(screen); } } } @@ -233,15 +238,16 @@ public Screen pop(String navigatorId) { if (mToolbar != null && getCurrentNavigatorId().equals(navigatorId) && getScreenStackSize() <= 2) { - mToolbar.hideBackButton(); + mToolbar.setNavUpButton(); } + return null; } @CallSuper public Screen popToRoot(String navigatorId) { if (mToolbar != null) { - mToolbar.hideBackButton(); + mToolbar.setNavUpButton(); } return null; @@ -251,7 +257,7 @@ public Screen popToRoot(String navigatorId) { public Screen resetTo(Screen screen) { StyleHelper.updateStyles(mToolbar, screen); if (mToolbar != null) { - mToolbar.hideBackButton(); + mToolbar.setNavUpButton(); } return null; @@ -273,6 +279,14 @@ public Screen getCurrentScreen() { public abstract int getScreenStackSize(); + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (mDrawerToggle != null) { + mDrawerToggle.onConfigurationChanged(newConfig); + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { mMenu = menu; @@ -285,6 +299,12 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { + if (mDrawerToggle != null && + getScreenStackSize() == 1 && + mDrawerToggle.onOptionsItemSelected(item)) { + return true; + } + if (item.getItemId() == android.R.id.home) { onBackPressed(); } else { @@ -297,6 +317,14 @@ public boolean onOptionsItemSelected(MenuItem item) { return super.onOptionsItemSelected(item); } + @Override + public void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + if (mDrawerToggle != null) { + mDrawerToggle.syncState(); + } + } + public Menu getMenu() { return mMenu; } @@ -384,4 +412,25 @@ public void toggleNavigationBar(ReadableMap params) { mToolbar.showToolbar(animated); } } + + public void toggleDrawer(ReadableMap params) { + if (mToolbar == null || mDrawerToggle == null) { + return; + } + + boolean animated = params.getBoolean(KEY_ANIMATED); + String side = params.getString(KEY_SIDE); + String to = params.getString(KEY_TO); + switch (to) { + case "open": + mToolbar.showDrawer(animated); + break; + case "closed": + mToolbar.hideDrawer(animated); + break; + default: + mToolbar.toggleDrawer(animated); + break; + } + } } diff --git a/android/app/src/main/java/com/reactnativenavigation/activities/BottomTabActivity.java b/android/app/src/main/java/com/reactnativenavigation/activities/BottomTabActivity.java index 1b59cc55eac..38208ed8326 100644 --- a/android/app/src/main/java/com/reactnativenavigation/activities/BottomTabActivity.java +++ b/android/app/src/main/java/com/reactnativenavigation/activities/BottomTabActivity.java @@ -4,6 +4,7 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; +import android.support.v4.widget.DrawerLayout; import android.view.Menu; import android.widget.FrameLayout; @@ -12,6 +13,7 @@ import com.facebook.react.bridge.ReadableMap; import com.reactnativenavigation.R; import com.reactnativenavigation.core.RctManager; +import com.reactnativenavigation.core.objects.Drawer; import com.reactnativenavigation.core.objects.Screen; import com.reactnativenavigation.utils.StyleHelper; import com.reactnativenavigation.views.RnnToolBar; @@ -25,6 +27,7 @@ * Created by guyc on 02/04/16. */ public class BottomTabActivity extends BaseReactActivity implements AHBottomNavigation.OnTabSelectedListener { + public static final String DRAWER_PARAMS = "drawerParams"; public static final String EXTRA_SCREENS = "extraScreens"; private static final String TAB_STYLE_BUTTON_COLOR = "tabBarButtonColor"; @@ -40,6 +43,8 @@ public class BottomTabActivity extends BaseReactActivity implements AHBottomNavi private AHBottomNavigation mBottomNavigation; private FrameLayout mContentFrame; private ArrayList mScreenStacks; + private ScreenStack mDrawerStack; + private DrawerLayout mDrawerLayout; private int mCurrentStackPosition = -1; @Override @@ -52,7 +57,9 @@ protected void handleOnCreate() { mContentFrame = (FrameLayout) findViewById(R.id.contentFrame); final ArrayList screens = (ArrayList) getIntent().getSerializableExtra(EXTRA_SCREENS); + final Drawer drawer = (Drawer) getIntent().getSerializableExtra(DRAWER_PARAMS); mBottomNavigation.setForceTint(true); + setupDrawer(drawer, screens.get(0)); setupTabs(getIntent().getExtras()); setupPages(screens); @@ -65,6 +72,20 @@ public void run() { }); } + protected void setupDrawer(Drawer drawer, Screen screen) { + if (drawer == null || drawer.left == null) { + return; + } + + mDrawerStack = new ScreenStack(this); + FrameLayout drawerFrame = (FrameLayout) findViewById(R.id.drawerFrame); + drawerFrame.addView(mDrawerStack); + mDrawerStack.push(drawer.left); + + mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout); + mDrawerToggle = mToolbar.setupDrawer(mDrawerLayout, drawer.left, screen); + } + private void setupPages(ArrayList screens) { new SetupTabsTask(this, mToolbar, screens).execute(); } @@ -189,9 +210,9 @@ public void onTabSelected(int position, boolean wasSelected) { // Hide or show back button if needed if (getScreenStackSize() > 1) { - mToolbar.showBackButton(getCurrentScreen()); + mToolbar.setNavUpButton(getCurrentScreen()); } else { - mToolbar.hideBackButton(); + mToolbar.setNavUpButton(); } } diff --git a/android/app/src/main/java/com/reactnativenavigation/activities/SingleScreenActivity.java b/android/app/src/main/java/com/reactnativenavigation/activities/SingleScreenActivity.java index 3c48bbae88c..8af9f39f584 100644 --- a/android/app/src/main/java/com/reactnativenavigation/activities/SingleScreenActivity.java +++ b/android/app/src/main/java/com/reactnativenavigation/activities/SingleScreenActivity.java @@ -1,9 +1,11 @@ package com.reactnativenavigation.activities; +import android.support.v4.widget.DrawerLayout; import android.widget.FrameLayout; import com.reactnativenavigation.R; import com.reactnativenavigation.core.RctManager; +import com.reactnativenavigation.core.objects.Drawer; import com.reactnativenavigation.core.objects.Screen; import com.reactnativenavigation.utils.StyleHelper; import com.reactnativenavigation.views.RnnToolBar; @@ -14,10 +16,13 @@ */ public class SingleScreenActivity extends BaseReactActivity { + public static final String DRAWER_PARAMS = "drawerParams"; public static final String EXTRA_SCREEN = "extraScreen"; private ScreenStack mScreenStack; private String mNavigatorId; + private ScreenStack mDrawerStack; + private DrawerLayout mDrawerLayout; @Override protected void handleOnCreate() { @@ -27,7 +32,11 @@ protected void handleOnCreate() { mToolbar = (RnnToolBar) findViewById(R.id.toolbar); final Screen screen = (Screen) getIntent().getSerializableExtra(EXTRA_SCREEN); + final Drawer drawer = (Drawer) getIntent().getSerializableExtra(DRAWER_PARAMS); + mNavigatorId = screen.navigatorId; + setupToolbar(screen); + setupDrawer(drawer, screen); mScreenStack = new ScreenStack(this); FrameLayout contentFrame = (FrameLayout) findViewById(R.id.contentFrame); @@ -44,6 +53,20 @@ public void run() { }); } + protected void setupDrawer(Drawer drawer, Screen screen) { + if (drawer == null || drawer.left == null) { + return; + } + + mDrawerStack = new ScreenStack(this); + FrameLayout drawerFrame = (FrameLayout) findViewById(R.id.drawerFrame); + drawerFrame.addView(mDrawerStack); + mDrawerStack.push(drawer.left); + + mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout); + mDrawerToggle = mToolbar.setupDrawer(mDrawerLayout, drawer.left, screen); + } + protected void setupToolbar(Screen screen) { mToolbar.update(screen); StyleHelper.updateStyles(mToolbar, screen); diff --git a/android/app/src/main/java/com/reactnativenavigation/core/objects/Drawer.java b/android/app/src/main/java/com/reactnativenavigation/core/objects/Drawer.java new file mode 100644 index 00000000000..4f87c55e813 --- /dev/null +++ b/android/app/src/main/java/com/reactnativenavigation/core/objects/Drawer.java @@ -0,0 +1,23 @@ +package com.reactnativenavigation.core.objects; + +import com.facebook.react.bridge.ReadableMap; + +import java.io.Serializable; + +public class Drawer extends JsonObject implements Serializable { + private static final long serialVersionUID = 982836768712398756L; + + private static final String KEY_LEFT = "left"; + private static final String KEY_RIGHT = "right"; + private static final String KEY_DISABLE_OPEN_GESTURE = "disableOpenGesture"; + + public final Screen left; + public final Screen right; + public final boolean disableOpenGesture; + + public Drawer(ReadableMap params) { + left = params.hasKey(KEY_LEFT) ? new Screen(params.getMap(KEY_LEFT)) : null; + right = params.hasKey(KEY_RIGHT) ? new Screen(params.getMap(KEY_RIGHT)) : null; + disableOpenGesture = getBoolean(params, KEY_DISABLE_OPEN_GESTURE); + } +} diff --git a/android/app/src/main/java/com/reactnativenavigation/core/objects/Screen.java b/android/app/src/main/java/com/reactnativenavigation/core/objects/Screen.java index f1935c38e82..5523aadbec5 100644 --- a/android/app/src/main/java/com/reactnativenavigation/core/objects/Screen.java +++ b/android/app/src/main/java/com/reactnativenavigation/core/objects/Screen.java @@ -34,7 +34,7 @@ public class Screen extends JsonObject implements Serializable { private static final String KEY_RIGHT_BUTTONS = "rightButtons"; private static final String KEY_TOOL_BAR_STYLE = "navigatorStyle"; private static final String KEY_STATUS_BAR_COLOR = "statusBarColor"; - private static final String KEY_TOOL_BAR_COLOR = "toolBarColor"; + private static final String KEY_TOOL_BAR_COLOR = "navBarBackgroundColor"; private static final String KEY_TOOL_BAR_HIDDEN = "navBarHidden"; private static final String KEY_NAVIGATION_BAR_COLOR = "navigationBarColor"; private static final String KEY_NAV_BAR_BUTTON_COLOR = "navBarButtonColor"; @@ -44,7 +44,7 @@ public class Screen extends JsonObject implements Serializable { private static final String KEY_TAB_INDICATOR_COLOR = "tabIndicatorColor"; private static final String KEY_PROPS = "passProps"; - public final String title; + public String title; public final String label; public final String screenId; public final String screenInstanceId; @@ -85,6 +85,10 @@ public Screen(ReadableMap screen) { setToolbarStyle(screen); } + public void setTitle(ReadableMap params) { + this.title = getString(params, KEY_TITLE); + } + public void setButtons(ReadableMap params) { this.buttons = getButtons(params); } diff --git a/android/app/src/main/java/com/reactnativenavigation/modules/RctActivityModule.java b/android/app/src/main/java/com/reactnativenavigation/modules/RctActivityModule.java index 0d355ed80d6..4a5035ce17a 100644 --- a/android/app/src/main/java/com/reactnativenavigation/modules/RctActivityModule.java +++ b/android/app/src/main/java/com/reactnativenavigation/modules/RctActivityModule.java @@ -15,6 +15,7 @@ import com.reactnativenavigation.activities.RootActivity; import com.reactnativenavigation.activities.SingleScreenActivity; import com.reactnativenavigation.controllers.ModalController; +import com.reactnativenavigation.core.objects.Drawer; import com.reactnativenavigation.core.objects.Screen; import com.reactnativenavigation.modal.RnnModal; import com.reactnativenavigation.utils.BridgeUtils; @@ -39,7 +40,7 @@ public String getName() { } @ReactMethod - public void startTabBasedApp(ReadableArray screens, ReadableMap style) { + public void startTabBasedApp(ReadableArray screens, ReadableMap style, ReadableMap drawerParams) { Activity context = ContextProvider.getActivityContext(); if (context != null && !context.isFinishing()) { Intent intent = new Intent(context, BottomTabActivity.class); @@ -47,6 +48,9 @@ public void startTabBasedApp(ReadableArray screens, ReadableMap style) { Bundle extras = new Bundle(); extras.putSerializable(BottomTabActivity.EXTRA_SCREENS, createScreens(screens)); + if (drawerParams != null) { + extras.putSerializable(BottomTabActivity.DRAWER_PARAMS, new Drawer(drawerParams)); + } if (style != null) { BridgeUtils.addMapToBundle(((ReadableNativeMap) style).toHashMap(), extras); } @@ -72,7 +76,7 @@ private ArrayList createScreens(ReadableArray screens) { } @ReactMethod - public void startSingleScreenApp(ReadableMap screen) { + public void startSingleScreenApp(ReadableMap screen, ReadableMap drawerParams) { BaseReactActivity context = ContextProvider.getActivityContext(); if (context != null && !context.isFinishing()) { Intent intent = new Intent(context, SingleScreenActivity.class); @@ -80,6 +84,9 @@ public void startSingleScreenApp(ReadableMap screen) { Bundle extras = new Bundle(); extras.putSerializable(SingleScreenActivity.EXTRA_SCREEN, new Screen(screen)); + if (drawerParams != null) { + extras.putSerializable(SingleScreenActivity.DRAWER_PARAMS, new Drawer(drawerParams)); + } intent.putExtras(extras); context.startActivity(intent); @@ -148,6 +155,20 @@ public void run() { }); } + @ReactMethod + public void toggleDrawer(final ReadableMap params) { + final BaseReactActivity context = ContextProvider.getActivityContext(); + if (context == null || context.isFinishing()) { + return; + } + context.runOnUiThread(new Runnable() { + @Override + public void run() { + context.toggleDrawer(params); + } + }); + } + @ReactMethod public void toggleNavigationBar(final ReadableMap params) { final BaseReactActivity context = ContextProvider.getActivityContext(); diff --git a/android/app/src/main/java/com/reactnativenavigation/views/RnnToolBar.java b/android/app/src/main/java/com/reactnativenavigation/views/RnnToolBar.java index f8807fe992e..93ae61634e9 100644 --- a/android/app/src/main/java/com/reactnativenavigation/views/RnnToolBar.java +++ b/android/app/src/main/java/com/reactnativenavigation/views/RnnToolBar.java @@ -12,10 +12,14 @@ import android.support.annotation.UiThread; import android.support.v4.content.ContextCompat; import android.support.v4.content.res.ResourcesCompat; +import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; +import android.support.v7.graphics.drawable.DrawerArrowDrawable; import android.support.v7.widget.Toolbar; import android.util.AttributeSet; +import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -27,6 +31,7 @@ import com.reactnativenavigation.core.objects.Button; import com.reactnativenavigation.core.objects.Screen; import com.reactnativenavigation.utils.ContextProvider; +import com.reactnativenavigation.utils.IconUtils; import com.reactnativenavigation.utils.ImageUtils; import java.lang.ref.WeakReference; @@ -43,8 +48,13 @@ public class RnnToolBar extends Toolbar { private static final int ANIMATE_DURATION = 180; private List mScreens; + private AsyncTask mDrawerIconTask; private AsyncTask mSetupToolbarTask; private Drawable mBackground; + private Drawable mDrawerIcon; + private Screen mDrawerScreen; + private DrawerLayout mDrawerLayout; + private ActionBarDrawerToggle mDrawerToggle; private ArrayList mMenuItems; public RnnToolBar(Context context) { @@ -105,6 +115,65 @@ public void handleOnCreateOptionsMenuAsync() { } } + public ActionBarDrawerToggle setupDrawer(DrawerLayout drawerLayout, Screen drawerScreen, Screen screen) { + if (drawerLayout == null || drawerScreen == null) { + return null; + } + + mDrawerLayout = drawerLayout; + mDrawerScreen = drawerScreen; + mDrawerToggle = new ActionBarDrawerToggle( + ContextProvider.getActivityContext(), + mDrawerLayout, + this, + R.string.drawer_open, + R.string.drawer_close + ); + mDrawerLayout.setDrawerListener(mDrawerToggle); + setupDrawerIconAsync(drawerScreen.icon, screen); + + return mDrawerToggle; + } + + public void setDrawerIcon(Drawable icon) { + mDrawerIcon = icon; + } + + public void showDrawer(boolean animated) { + if (mDrawerLayout == null) { + return; + } + + mDrawerLayout.openDrawer(Gravity.LEFT); + } + + public void hideDrawer(boolean animated) { + if (mDrawerLayout == null) { + return; + } + + mDrawerLayout.closeDrawer(Gravity.LEFT); + } + + public void toggleDrawer(boolean animated) { + if (mDrawerLayout == null) { + return; + } + + boolean visible = mDrawerLayout.isDrawerOpen(Gravity.LEFT); + if (visible) { + hideDrawer(animated); + } else { + showDrawer(animated); + } + } + + public void setupDrawerIconAsync(String drawerIconSource, Screen screen) { + if (mDrawerIconTask == null) { + mDrawerIconTask = new SetupDrawerIconTask(this, drawerIconSource, screen).execute(); + } + } + public void setupToolbarButtonsAsync(Screen newScreen) { if (newScreen != null) { this.setupToolbarButtonsAsync(null, newScreen); @@ -144,29 +213,44 @@ private void hideToolbar() { hideToolbar(false); } - @SuppressWarnings({"ConstantConditions"}) - public void showBackButton(Screen screen) { - ActionBar actionBar = ContextProvider.getActivityContext().getSupportActionBar(); - Drawable backButton = setupBackButton(screen); - actionBar.setHomeAsUpIndicator(backButton); - actionBar.setDisplayHomeAsUpEnabled(true); + public void setNavUpButton() { + setNavUpButton(null); } - @SuppressLint("PrivateResource") @SuppressWarnings({"ConstantConditions"}) - private Drawable setupBackButton(Screen screen) { - Resources resources = getResources(); - final Drawable backButton = ResourcesCompat.getDrawable(resources, - R.drawable.abc_ic_ab_back_mtrl_am_alpha, - ContextProvider.getActivityContext().getTheme()); - int tintColor = screen.navBarButtonColor != null ? screen.navBarButtonColor : Color.BLACK; - ImageUtils.tint(backButton, tintColor); - return backButton; - } + public void setNavUpButton(Screen screen) { + ActionBar actionBar = ContextProvider.getActivityContext().getSupportActionBar(); + if (actionBar == null) { + return; + } - @SuppressWarnings({"ConstantConditions"}) - public void hideBackButton() { - ContextProvider.getActivityContext().getSupportActionBar().setDisplayHomeAsUpEnabled(false); + boolean isBack = screen != null; + boolean hasDrawer = mDrawerToggle != null; + + Drawable navIcon = null; + DrawerArrowDrawable navArrow = null; + if (hasDrawer && mDrawerIcon == null) { + navArrow = (DrawerArrowDrawable) this.getNavigationIcon(); + } else { + if (isBack) { + navArrow = new DrawerArrowDrawable(ContextProvider.getActivityContext()); + } else if (hasDrawer) { + navIcon = mDrawerIcon; + } + } + + if (navArrow != null) { + navArrow.setProgress(isBack ? 1.0f : 0.0f); + if (screen.navBarButtonColor != null) { + navArrow.setColor(screen.navBarButtonColor); + } else { + navArrow.setColor(Color.BLACK); + } + navIcon = navArrow; + } + + actionBar.setHomeAsUpIndicator(navIcon); + actionBar.setDisplayHomeAsUpEnabled(navIcon != null); } /** @@ -186,6 +270,42 @@ public void updateAndSetButtons(Screen screen) { setupToolbarButtonsAsync(screen); } + private static class SetupDrawerIconTask extends AsyncTask { + private final WeakReference mToolbarWR; + private final String mDrawerIconSource; + private final Integer mTintColor; + + public SetupDrawerIconTask(RnnToolBar toolBar, String drawerIconSource, Screen screen) { + mToolbarWR = new WeakReference<>(toolBar); + mDrawerIconSource = drawerIconSource; + mTintColor = screen.navBarButtonColor; + } + + @Override + protected Drawable doInBackground(Void... params) { + Context context = ContextProvider.getActivityContext(); + if (context == null || mDrawerIconSource == null) { + return null; + } + + return IconUtils.getIcon(context, mDrawerIconSource); + } + + @Override + protected void onPostExecute(Drawable drawerIcon) { + RnnToolBar toolBar = mToolbarWR.get(); + if (drawerIcon != null) { + if (mTintColor != null) { + ImageUtils.tint(drawerIcon, mTintColor); + } + toolBar.setDrawerIcon(drawerIcon); + } + + toolBar.setNavUpButton(); + mToolbarWR.clear(); + } + } + private static class SetupToolbarButtonsTask extends AsyncTask> { private final List