diff --git a/android/app/build.gradle b/android/app/build.gradle index 329e94262ca..5001d56add3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -35,6 +35,7 @@ repositories { dependencies { compile fileTree(dir: "libs", include: ["*.jar"]) + compile "com.aurelhubert:ahbottomnavigation:1.2.3" compile "com.android.support:appcompat-v7:23.0.1" compile 'com.android.support:design:23.1.1' compile "com.facebook.react:react-native:+" // From node_modules diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index db7d918eb97..811664cca20 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ + 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 34cbea43f1a..583da7f7c21 100644 --- a/android/app/src/main/java/com/reactnativenavigation/activities/BaseReactActivity.java +++ b/android/app/src/main/java/com/reactnativenavigation/activities/BaseReactActivity.java @@ -135,6 +135,7 @@ protected ReactRootView createRootView() { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + ContextProvider.setActivityContext(this); mReactInstanceManager = createReactInstanceManager(); handleOnCreate(); } diff --git a/android/app/src/main/java/com/reactnativenavigation/activities/BottomTabActivity.java b/android/app/src/main/java/com/reactnativenavigation/activities/BottomTabActivity.java new file mode 100644 index 00000000000..f0a703f6682 --- /dev/null +++ b/android/app/src/main/java/com/reactnativenavigation/activities/BottomTabActivity.java @@ -0,0 +1,182 @@ +package com.reactnativenavigation.activities; + +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.Menu; +import android.widget.FrameLayout; + +import com.aurelhubert.ahbottomnavigation.AHBottomNavigation; +import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem; +import com.reactnativenavigation.R; +import com.reactnativenavigation.core.RctManager; +import com.reactnativenavigation.core.objects.Screen; +import com.reactnativenavigation.views.RnnToolBar; +import com.reactnativenavigation.views.ScreenStack; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + +/** + * Created by guyc on 02/04/16. + */ +public class BottomTabActivity extends BaseReactActivity implements AHBottomNavigation.OnTabSelectedListener { + public static final String EXTRA_SCREENS = "extraScreens"; + + private static final String TAB_STYLE_BUTTON_COLOR = "tabBarButtonColor"; + private static final String TAB_STYLE_SELECTED_COLOR = "tabBarSelectedButtonColor"; + private static final String TAB_STYLE_BAR_BG_COLOR = "tabBarBackgroundColor"; + private static final String TAB_STYLE_INACTIVE_TITLES = "tabShowInactiveTitles"; + + private static int DEFAULT_TAB_BAR_BG_COLOR = 0xFFFFFFFF; + private static int DEFAULT_TAB_BUTTON_COLOR = Color.GRAY; + private static int DEFAULT_TAB_SELECTED_COLOR = 0xFF0000FF; + private static boolean DEFAULT_TAB_INACTIVE_TITLES = true; + + private AHBottomNavigation mBottomNavigation; + private FrameLayout mContentFrame; + private ArrayList mScreenStacks; + private int mCurrentStackPosition = 0; + + @Override + protected void handleOnCreate() { + mReactInstanceManager = RctManager.getInstance().getReactInstanceManager(); + + setContentView(R.layout.bottom_tab_activity); + mToolbar = (RnnToolBar) findViewById(R.id.toolbar); + mBottomNavigation = (AHBottomNavigation) findViewById(R.id.bottom_tab_bar); + mContentFrame = (FrameLayout) findViewById(R.id.contentFrame); + + ArrayList screens = (ArrayList) getIntent().getSerializableExtra(EXTRA_SCREENS); + mBottomNavigation.setForceTint(true); + setupToolbar(screens); + setupTabs(getIntent().getExtras()); + setupPages(screens); + } + + private void setupPages(ArrayList screens) { + new SetupTabsTask(this, screens).execute(); + } + + private void setupToolbar(ArrayList screens) { + Screen initialScreen = screens.get(0); + setNavigationStyle(initialScreen); + mToolbar.setScreens(screens); + mToolbar.setTitle(initialScreen.title); + setSupportActionBar(mToolbar); + } + + @Override + public void setNavigationStyle(Screen screen) { + super.setNavigationStyle(screen); + mToolbar.setTitle(screen.title); + } + + private void setupTabs(Bundle style) { + + mBottomNavigation.setForceTitlesDisplay(style.getBoolean(TAB_STYLE_INACTIVE_TITLES, DEFAULT_TAB_INACTIVE_TITLES)); + mBottomNavigation.setForceTint(true); + mBottomNavigation.setDefaultBackgroundColor(getColor(style, TAB_STYLE_BAR_BG_COLOR, DEFAULT_TAB_BAR_BG_COLOR)); + mBottomNavigation.setInactiveColor(getColor(style, TAB_STYLE_BUTTON_COLOR, DEFAULT_TAB_BUTTON_COLOR)); + mBottomNavigation.setAccentColor(getColor(style, TAB_STYLE_SELECTED_COLOR, DEFAULT_TAB_SELECTED_COLOR)); + } + + private static int getColor(Bundle bundle, String key, int defaultColor) { + if (bundle.containsKey(key)) { + return Color.parseColor(bundle.getString(key)); + } + else { + return defaultColor; + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + boolean ret = super.onCreateOptionsMenu(menu); + mToolbar.handleOnCreateOptionsMenuAsync(); + return ret; + } + + @Override + public void push(Screen screen) { + super.push(screen); + mScreenStacks.get(mCurrentStackPosition).push(screen); + } + + @Override + public Screen pop(String navigatorId) { + super.pop(navigatorId); + Screen screen = mScreenStacks.get(mCurrentStackPosition).pop(); + setNavigationStyle(screen); + return screen; + } + + @Override + protected Screen getCurrentScreen() { + return mScreenStacks.get(mCurrentStackPosition).peek(); + } + + @Override + protected String getCurrentNavigatorId() { + return mScreenStacks.get(mCurrentStackPosition).peek().navigatorId; + } + + @Override + public int getScreenStackSize() { + return mScreenStacks.get(mCurrentStackPosition).getStackSize(); + } + + @Override + public void onTabSelected(int position, boolean wasSelected) { + if (wasSelected) { + return; + } + mContentFrame.removeAllViews(); + mContentFrame.addView(mScreenStacks.get(position), new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + mCurrentStackPosition = position; + setNavigationStyle(mScreenStacks.get(mCurrentStackPosition).peek()); + } + + private static class SetupTabsTask extends AsyncTask> { + private BottomTabActivity mActivity; + private ArrayList mScreens; + + public SetupTabsTask(BottomTabActivity context, ArrayList screens) { + mActivity = context; + mScreens = screens; + } + + @Override + protected Map doInBackground(Void... params) { + Map icons = new HashMap<>(); + for (Screen screen : mScreens) { + if (screen.icon != null) { + icons.put(screen, screen.getIcon(this.mActivity)); + } + } + return icons; + } + + @Override + protected void onPostExecute(Map icons) { + mActivity.setTabsWithIcons(mScreens, icons); + } + } + + private void setTabsWithIcons(ArrayList screens, Map icons) { + mScreenStacks = new ArrayList<>(); + for(Screen screen: screens) { + ScreenStack stack = new ScreenStack(this); + stack.push(screen); + mScreenStacks.add(stack); + AHBottomNavigationItem item = new AHBottomNavigationItem(screen.label, icons.get(screen), Color.GRAY); + mBottomNavigation.addItem(item); + mBottomNavigation.setOnTabSelectedListener(this); + } + this.onTabSelected(0, false); + } +} diff --git a/android/app/src/main/java/com/reactnativenavigation/activities/TabActivity.java b/android/app/src/main/java/com/reactnativenavigation/activities/TabActivity.java index 7209e786408..09f06b7f74b 100644 --- a/android/app/src/main/java/com/reactnativenavigation/activities/TabActivity.java +++ b/android/app/src/main/java/com/reactnativenavigation/activities/TabActivity.java @@ -42,6 +42,7 @@ private void setupToolbar(ArrayList screens) { setNavigationStyle(initialScreen); mToolbar.setScreens(screens); mToolbar.setTitle(initialScreen.title); + mToolbar.setupToolbarButtonsAsync(initialScreen); setSupportActionBar(mToolbar); } diff --git a/android/app/src/main/java/com/reactnativenavigation/core/objects/Button.java b/android/app/src/main/java/com/reactnativenavigation/core/objects/Button.java index e464f187a0f..69abd12af9b 100644 --- a/android/app/src/main/java/com/reactnativenavigation/core/objects/Button.java +++ b/android/app/src/main/java/com/reactnativenavigation/core/objects/Button.java @@ -10,6 +10,7 @@ import com.facebook.react.bridge.ReadableMap; import com.reactnativenavigation.BuildConfig; +import com.reactnativenavigation.utils.IconUtils; import com.reactnativenavigation.utils.ResourceDrawableIdHelper; import java.io.Serializable; @@ -24,13 +25,10 @@ public class Button extends JsonObject implements Serializable { private static final long serialVersionUID = -570145217281069067L; - public static final String LOCAL_RESOURCE_URI_SCHEME = "res"; private static final String KEY_ID = "id"; private static final String KEY_TITLE = "title"; private static final String KEY_ICON = "icon"; - private static ResourceDrawableIdHelper sResDrawableIdHelper = new ResourceDrawableIdHelper(); - public String id; public String title; private String mIconSource; @@ -49,47 +47,7 @@ public boolean hasIcon() { } public Drawable getIcon(Context ctx) { - if (mIconSource == null) { - return null; - } - - try { - Drawable icon; - Uri iconUri = getIconUri(ctx); - - if (LOCAL_RESOURCE_URI_SCHEME.equals(iconUri.getScheme())) { - icon = sResDrawableIdHelper.getResourceDrawable(ctx, mIconSource); - } else { - URL url = new URL(iconUri.toString()); - Bitmap bitmap = BitmapFactory.decodeStream(url.openStream()); - icon = new BitmapDrawable(bitmap); - } - return icon; - } catch (Exception e) { - if (BuildConfig.DEBUG) { - e.printStackTrace(); - } - } - return null; - } - - private Uri getIconUri(Context context) { - Uri ret = null; - if (mIconSource != null) { - try { - ret = Uri.parse(mIconSource); - // Verify scheme is set, so that relative uri (used by static resources) are not handled. - if (ret.getScheme() == null) { - ret = null; - } - } catch (Exception e) { - // Ignore malformed uri, then attempt to extract resource ID. - } - if (ret == null) { - ret = sResDrawableIdHelper.getResourceDrawableUri(context, mIconSource); - } - } - return ret; + return IconUtils.getIcon(ctx, mIconSource); } public int getItemId() { 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 c227f755472..7d1b87b88b8 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 @@ -1,5 +1,7 @@ package com.reactnativenavigation.core.objects; +import android.content.Context; +import android.graphics.drawable.Drawable; import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -7,6 +9,7 @@ import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableNativeMap; +import com.reactnativenavigation.utils.IconUtils; import java.io.Serializable; import java.util.ArrayList; @@ -37,7 +40,7 @@ public class Screen extends JsonObject implements Serializable { private static final String KEY_TAB_NORMAL_TEXT_COLOR = "tabNormalTextColor"; private static final String KEY_TAB_SELECTED_TEXT_COLOR = "tabSelectedTextColor"; private static final String KEY_TAB_INDICATOR_COLOR = "tabIndicatorColor"; - public static final String KEY_PROPS = "passProps"; + private static final String KEY_PROPS = "passProps"; public final String title; public final String label; @@ -45,7 +48,7 @@ public class Screen extends JsonObject implements Serializable { public final String screenInstanceId; public final String navigatorId; public final String navigatorEventId; - public final int icon; + public final String icon; public final ArrayList