com.harlan.jxust.ui.view.bottombar.BottomBar.java Source code

Java tutorial

Introduction

Here is the source code for com.harlan.jxust.ui.view.bottombar.BottomBar.java

Source

package com.harlan.jxust.ui.view.bottombar;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.MenuRes;
import android.support.annotation.StyleRes;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Display;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.harlan.jxust.ui.view.behavior.BottomNavigationBehavior;
import com.harlan.jxust.ui.view.listener.OnMenuTabClickListener;
import com.harlan.jxust.ui.view.listener.OnMenuTabSelectedListener;
import com.harlan.jxust.ui.view.listener.OnSizeDeterminedListener;
import com.harlan.jxust.ui.view.listener.OnTabClickListener;
import com.harlan.jxust.ui.view.listener.OnTabSelectedListener;
import com.harlan.jxust.wecoder.R;

import java.util.HashMap;

/*
 * BottomBar library for Android
 * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
public class BottomBar extends FrameLayout implements View.OnClickListener, View.OnLongClickListener {
    private static final long ANIMATION_DURATION = 150;
    private static final int MAX_FIXED_TAB_COUNT = 3;

    private static final String STATE_CURRENT_SELECTED_TAB = "STATE_CURRENT_SELECTED_TAB";
    private static final String STATE_BADGE_STATES_BUNDLE = "STATE_BADGE_STATES_BUNDLE";
    private static final String TAG_BOTTOM_BAR_VIEW_INACTIVE = "BOTTOM_BAR_VIEW_INACTIVE";
    private static final String TAG_BOTTOM_BAR_VIEW_ACTIVE = "BOTTOM_BAR_VIEW_ACTIVE";
    private static final String TAG_BADGE = "BOTTOMBAR_BADGE_";

    private Context mContext;
    private boolean mIsComingFromRestoredState;
    private boolean mIgnoreTabletLayout;
    private boolean mIsTabletMode;
    private boolean mIsShy;
    private boolean mShyHeightAlreadyCalculated;
    private boolean mUseExtraOffset;

    private ViewGroup mUserContentContainer;
    private ViewGroup mOuterContainer;
    private ViewGroup mItemContainer;

    private View mBackgroundView;
    private View mBackgroundOverlay;
    private View mShadowView;
    private View mTabletRightBorder;
    private View mPendingUserContentView;

    private int mPrimaryColor;
    private int mInActiveColor;
    private int mDarkBackgroundColor;
    private int mWhiteColor;

    private int mScreenWidth;
    private int mTwoDp;
    private int mTenDp;
    private int mMaxFixedItemWidth;
    private int mMaxInActiveShiftingItemWidth;
    private int mInActiveShiftingItemWidth;
    private int mActiveShiftingItemWidth;

    private Object mListener;
    private Object mMenuListener;

    private int mCurrentTabPosition;
    private boolean mIsShiftingMode;

    private Object mFragmentManager;
    private int mFragmentContainer;

    private BottomBarItemBase[] mItems;
    private HashMap<Integer, Integer> mColorMap;
    private HashMap<Integer, Object> mBadgeMap;
    private HashMap<Integer, Boolean> mBadgeStateMap;

    private int mCurrentBackgroundColor;
    private int mDefaultBackgroundColor;

    private boolean mIsDarkTheme;
    private boolean mIgnoreNightMode;
    private boolean mIgnoreShiftingResize;

    private int mCustomActiveTabColor;

    private boolean mDrawBehindNavBar = true;
    private boolean mUseTopOffset = true;
    private boolean mUseOnlyStatusBarOffset;

    private int mPendingTextAppearance = -1;
    private Typeface mPendingTypeface;

    // For fragment state restoration
    private boolean mShouldUpdateFragmentInitially;

    /**
     * Bind the BottomBar to your Activity, and inflate your layout here.
     * <p/>
     * Remember to also call {@link #onRestoreInstanceState(Bundle)} inside
     * of your {@link Activity#onSaveInstanceState(Bundle)} to restore the state.
     *
     * @param activity           an Activity to attach to.
     * @param savedInstanceState a Bundle for restoring the state on configuration change.
     * @return a BottomBar at the bottom of the screen.
     */
    public static BottomBar attach(Activity activity, Bundle savedInstanceState) {
        BottomBar bottomBar = new BottomBar(activity);
        bottomBar.onRestoreInstanceState(savedInstanceState);

        ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
        View oldLayout = contentView.getChildAt(0);
        contentView.removeView(oldLayout);

        bottomBar.setPendingUserContentView(oldLayout);
        contentView.addView(bottomBar, 0);

        return bottomBar;
    }

    private void setPendingUserContentView(View oldLayout) {
        mPendingUserContentView = oldLayout;
    }

    /**
     * Bind the BottomBar to the specified View's parent, and inflate
     * your layout there. Useful when the BottomBar overlaps some content
     * that shouldn't be overlapped.
     * <p/>
     * Remember to also call {@link #onRestoreInstanceState(Bundle)} inside
     * of your {@link Activity#onSaveInstanceState(Bundle)} to restore the state.
     *
     * @param view               a View, which parent we're going to attach to.
     * @param savedInstanceState a Bundle for restoring the state on configuration change.
     * @return a BottomBar at the bottom of the screen.
     */
    public static BottomBar attach(View view, Bundle savedInstanceState) {
        BottomBar bottomBar = new BottomBar(view.getContext());
        bottomBar.onRestoreInstanceState(savedInstanceState);

        ViewGroup contentView = (ViewGroup) view.getParent();

        if (contentView != null) {
            View oldLayout = contentView.getChildAt(0);
            contentView.removeView(oldLayout);

            bottomBar.setPendingUserContentView(oldLayout);
            contentView.addView(bottomBar, 0);
        } else {
            bottomBar.setPendingUserContentView(view);
        }

        return bottomBar;
    }

    /**
     * Deprecated. Breaks support for tablets.
     * Use {@link #attachShy(CoordinatorLayout, View, Bundle)} instead.
     */
    @Deprecated
    public static BottomBar attachShy(CoordinatorLayout coordinatorLayout, Bundle savedInstanceState) {
        return attachShy(coordinatorLayout, null, savedInstanceState);
    }

    /**
     * Adds the BottomBar inside of your CoordinatorLayout and shows / hides
     * it according to scroll state changes.
     * <p/>
     * Remember to also call {@link #onRestoreInstanceState(Bundle)} inside
     * of your {@link Activity#onSaveInstanceState(Bundle)} to restore the state.
     *
     * @param coordinatorLayout  a CoordinatorLayout for the BottomBar to add itself into
     * @param userContentView    the view (usually a NestedScrollView) that has your scrolling content.
     *                           Needed for tablet support.
     * @param savedInstanceState a Bundle for restoring the state on configuration change.
     * @return a BottomBar at the bottom of the screen.
     */
    public static BottomBar attachShy(CoordinatorLayout coordinatorLayout, View userContentView,
            Bundle savedInstanceState) {
        final BottomBar bottomBar = new BottomBar(coordinatorLayout.getContext());
        bottomBar.onRestoreInstanceState(savedInstanceState);
        bottomBar.toughChildHood(ViewCompat.getFitsSystemWindows(coordinatorLayout));

        if (userContentView != null
                && coordinatorLayout.getContext().getResources().getBoolean(R.bool.bb_bottom_bar_is_tablet_mode)) {
            bottomBar.setPendingUserContentView(userContentView);
        }

        coordinatorLayout.addView(bottomBar);
        return bottomBar;
    }

    /**
     * Set tabs and fragments for this BottomBar. When setting more than 3 items,
     * only the icons will show by default, but the selected item
     * will have the text visible.
     *
     * @param fragmentManager   a FragmentManager for managing the Fragments.
     * @param containerResource id for the layout to inflate Fragments to.
     * @param fragmentItems     an array of {@link BottomBarFragment} objects.
     */
    public void setFragmentItems(android.app.FragmentManager fragmentManager, @IdRes int containerResource,
            BottomBarFragment... fragmentItems) {
        if (fragmentItems.length > 0) {
            int index = 0;

            for (BottomBarFragment fragmentItem : fragmentItems) {
                if (fragmentItem.getFragment() == null && fragmentItem.getSupportFragment() != null) {
                    throw new IllegalArgumentException("Conflict: cannot use android.app.FragmentManager "
                            + "to handle a android.support.v4.app.Fragment object at position " + index
                            + ". If you want BottomBar to handle support Fragments, use getSupportFragment"
                            + "Manager() instead of getFragmentManager().");
                }

                index++;
            }
        }

        clearItems();
        mFragmentManager = fragmentManager;
        mFragmentContainer = containerResource;
        mItems = fragmentItems;
        updateItems(mItems);
    }

    /**
     * Deprecated.
     * <p/>
     * Use either {@link #setItems(BottomBarTab...)} or
     * {@link #setItemsFromMenu(int, OnMenuTabClickListener)} and add a listener using
     * {@link #setOnTabClickListener(OnTabClickListener)} to handle tab changes by yourself.
     * <p/>
     * Set tabs and fragments for this BottomBar. When setting more than 3 items,
     * only the icons will show by default, but the selected item
     * will have the text visible.
     *
     * @param fragmentManager   a FragmentManager for managing the Fragments.
     * @param containerResource id for the layout to inflate Fragments to.
     * @param fragmentItems     an array of {@link BottomBarFragment} objects.
     */
    @Deprecated
    public void setFragmentItems(android.support.v4.app.FragmentManager fragmentManager,
            @IdRes int containerResource, BottomBarFragment... fragmentItems) {
        if (fragmentItems.length > 0) {
            int index = 0;

            for (BottomBarFragment fragmentItem : fragmentItems) {
                if (fragmentItem.getSupportFragment() == null && fragmentItem.getFragment() != null) {
                    throw new IllegalArgumentException(
                            "Conflict: cannot use android.support.v4.app.FragmentManager "
                                    + "to handle a android.app.Fragment object at position " + index
                                    + ". If you want BottomBar to handle normal Fragments, use getFragment"
                                    + "Manager() instead of getSupportFragmentManager().");
                }

                index++;
            }
        }
        clearItems();
        mFragmentManager = fragmentManager;
        mFragmentContainer = containerResource;
        mItems = fragmentItems;
        updateItems(mItems);
    }

    /**
     * Set tabs for this BottomBar. When setting more than 3 items,
     * only the icons will show by default, but the selected item
     * will have the text visible.
     *
     * @param bottomBarTabs an array of {@link BottomBarTab} objects.
     */
    public void setItems(BottomBarTab... bottomBarTabs) {
        clearItems();
        mItems = bottomBarTabs;
        updateItems(mItems);
    }

    /**
     * Deprecated. Use {@link #setItemsFromMenu(int, OnMenuTabClickListener)} instead.
     */
    @Deprecated
    public void setItemsFromMenu(@MenuRes int menuRes, OnMenuTabSelectedListener listener) {
        clearItems();
        mItems = MiscUtils.inflateMenuFromResource((Activity) getContext(), menuRes);
        mMenuListener = listener;
        updateItems(mItems);
    }

    /**
     * Set items from an XML menu resource file.
     *
     * @param menuRes  the menu resource to inflate items from.
     * @param listener listener for tab change events.
     */
    public void setItemsFromMenu(@MenuRes int menuRes, OnMenuTabClickListener listener) {
        clearItems();
        mItems = MiscUtils.inflateMenuFromResource((Activity) getContext(), menuRes);
        mMenuListener = listener;
        updateItems(mItems);

        if (mItems != null && mItems.length > 0 && mItems instanceof BottomBarTab[]) {
            listener.onMenuTabSelected(((BottomBarTab) mItems[mCurrentTabPosition]).id);
        }
    }

    /**
     * Deprecated. Use {@link #setOnTabClickListener(OnTabClickListener)} instead.
     */
    @Deprecated
    public void setOnItemSelectedListener(OnTabSelectedListener listener) {
        mListener = listener;
    }

    /**
     * Set a listener that gets fired when the selected tab changes.
     *
     * @param listener a listener for monitoring changes in tab selection.
     */
    public void setOnTabClickListener(OnTabClickListener listener) {
        mListener = listener;

        if (mItems != null && mItems.length > 0) {
            listener.onTabSelected(mCurrentTabPosition);
        }
    }

    /**
     * Select a tab at the specified position.
     *
     * @param position the position to select.
     */
    public void selectTabAtPosition(int position, boolean animate) {
        if (mItems == null || mItems.length == 0) {
            throw new UnsupportedOperationException(
                    "Can't select tab at " + "position " + position + ". This BottomBar has no items set yet.");
        } else if (position > mItems.length - 1 || position < 0) {
            throw new IndexOutOfBoundsException(
                    "Can't select tab at position " + position + ". This BottomBar has no items at that position.");
        }

        View oldTab = mItemContainer.findViewWithTag(TAG_BOTTOM_BAR_VIEW_ACTIVE);
        View newTab = mItemContainer.getChildAt(position);

        unselectTab(oldTab, animate);
        selectTab(newTab, animate);

        updateSelectedTab(position);
        shiftingMagic(oldTab, newTab, false);
    }

    /**
     * Sets the default tab for this BottomBar that is shown until the user changes
     * the selection.
     *
     * @param defaultTabPosition the default tab position.
     */
    public void setDefaultTabPosition(int defaultTabPosition) {
        if (mIsComingFromRestoredState)
            return;

        if (mItems == null) {
            mCurrentTabPosition = defaultTabPosition;
            return;
        } else if (mItems.length == 0 || defaultTabPosition > mItems.length - 1 || defaultTabPosition < 0) {
            throw new IndexOutOfBoundsException("Can't set default tab at position " + defaultTabPosition
                    + ". This BottomBar has no items at that position.");
        }

        selectTabAtPosition(defaultTabPosition, false);
    }

    /**
     * Get the current selected tab position.
     *
     * @return the position of currently selected tab.
     */
    public int getCurrentTabPosition() {
        return mCurrentTabPosition;
    }

    /**
     * Hide the BottomBar.
     */
    public void hide() {
        setBarVisibility(GONE);
    }

    /**
     * Show the BottomBar.
     */
    public void show() {
        setBarVisibility(VISIBLE);
    }

    /**
     * Call this method in your Activity's onSaveInstanceState
     * to keep the BottomBar's state on configuration change.
     *
     * @param outState the Bundle to save data to.
     */
    public void onSaveInstanceState(Bundle outState) {
        outState.putInt(STATE_CURRENT_SELECTED_TAB, mCurrentTabPosition);

        if (mBadgeMap != null && mBadgeMap.size() > 0) {
            if (mBadgeStateMap == null) {
                mBadgeStateMap = new HashMap<>();
            }

            for (Integer key : mBadgeMap.keySet()) {
                BottomBarBadge badgeCandidate = (BottomBarBadge) mOuterContainer
                        .findViewWithTag(mBadgeMap.get(key));

                if (badgeCandidate != null) {
                    mBadgeStateMap.put(key, badgeCandidate.isVisible());
                }
            }

            outState.putSerializable(STATE_BADGE_STATES_BUNDLE, mBadgeStateMap);
        }

        if (mFragmentManager != null && mFragmentContainer != 0 && mItems != null
                && mItems instanceof BottomBarFragment[]) {
            BottomBarFragment bottomBarFragment = (BottomBarFragment) mItems[mCurrentTabPosition];

            if (bottomBarFragment.getFragment() != null) {
                bottomBarFragment.getFragment().onSaveInstanceState(outState);
            } else if (bottomBarFragment.getSupportFragment() != null) {
                bottomBarFragment.getSupportFragment().onSaveInstanceState(outState);
            }
        }
    }

    /**
     * Map a background color for a Tab, that changes the whole BottomBar
     * background color when the Tab is selected.
     *
     * @param tabPosition zero-based index for the tab.
     * @param color       a hex color for the tab, such as 0xFF00FF00.
     */
    public void mapColorForTab(int tabPosition, int color) {
        if (mItems == null || mItems.length == 0) {
            throw new UnsupportedOperationException("You have no BottomBar Tabs set yet. "
                    + "Please set them first before calling the mapColorForTab method.");
        } else if (tabPosition > mItems.length - 1 || tabPosition < 0) {
            throw new IndexOutOfBoundsException("Cant map color for Tab " + "index " + tabPosition
                    + ". You have no BottomBar Tabs at that position.");
        }

        if (mIsDarkTheme || !mIsShiftingMode || mIsTabletMode)
            return;

        if (mColorMap == null) {
            mColorMap = new HashMap<>();
        }

        if (tabPosition == mCurrentTabPosition && mCurrentBackgroundColor != color) {
            mCurrentBackgroundColor = color;
            mBackgroundView.setBackgroundColor(color);
        }

        mColorMap.put(tabPosition, color);
    }

    /**
     * Map a background color for a Tab, that changes the whole BottomBar
     * background color when the Tab is selected.
     *
     * @param tabPosition zero-based index for the tab.
     * @param color       a hex color for the tab, such as "#00FF000".
     */
    public void mapColorForTab(int tabPosition, String color) {
        mapColorForTab(tabPosition, Color.parseColor(color));
    }

    /**
     * Deprecated. Use {@link #useDarkTheme()} instead.
     */
    @Deprecated
    public void useDarkTheme(boolean darkThemeEnabled) {
        mIsDarkTheme = darkThemeEnabled;
        useDarkTheme();
    }

    /**
     * Use dark theme instead of the light one.
     * <p/>
     * NOTE: You might want to change your active tab color to something else
     * using {@link #setActiveTabColor(int)}, as the default primary color might
     * not have enough contrast for the dark background.
     */
    public void useDarkTheme() {
        if (!mIsDarkTheme && mItems != null && mItems.length > 0) {
            darkThemeMagic();

            for (int i = 0; i < mItemContainer.getChildCount(); i++) {
                View bottomBarTab = mItemContainer.getChildAt(i);
                ((ImageView) bottomBarTab.findViewById(R.id.bb_bottom_bar_icon)).setColorFilter(mWhiteColor);

                if (i == mCurrentTabPosition) {
                    selectTab(bottomBarTab, false);
                } else {
                    unselectTab(bottomBarTab, false);
                }
            }
        }

        mIsDarkTheme = true;
    }

    /**
     * Ignore the automatic Night Mode detection and use a light theme by default,
     * even if the Night Mode is on.
     */
    public void ignoreNightMode() {
        if (mItems != null && mItems.length > 0) {
            throw new UnsupportedOperationException("This BottomBar "
                    + "already has items! You must call ignoreNightMode() " + "before setting any items.");
        }

        mIgnoreNightMode = true;
    }

    /**
     * Set a custom color for an active tab when there's three
     * or less items.
     * <p/>
     * NOTE: This value is ignored on mobile devices if you have more than
     * three items.
     *
     * @param activeTabColor a hex color used for active tabs, such as "#00FF000".
     */
    public void setActiveTabColor(String activeTabColor) {
        setActiveTabColor(Color.parseColor(activeTabColor));
    }

    /**
     * Set a custom color for an active tab when there's three
     * or less items.
     * <p/>
     * NOTE: This value is ignored if you have more than three items.
     *
     * @param activeTabColor a hex color used for active tabs, such as 0xFF00FF00.
     */
    public void setActiveTabColor(int activeTabColor) {
        mCustomActiveTabColor = activeTabColor;

        if (mItems != null && mItems.length > 0) {
            selectTabAtPosition(mCurrentTabPosition, false);
        }
    }

    /**
     * Creates a new Badge (for example, an indicator for unread messages) for a Tab at
     * the specified position.
     *
     * @param tabPosition     zero-based index for the tab.
     * @param backgroundColor a color for this badge, such as "#FF0000".
     * @param initialCount    text displayed initially for this Badge.
     * @return a {@link BottomBarBadge} object.
     */
    public BottomBarBadge makeBadgeForTabAt(int tabPosition, String backgroundColor, int initialCount) {
        return makeBadgeForTabAt(tabPosition, Color.parseColor(backgroundColor), initialCount);
    }

    /**
     * Creates a new Badge (for example, an indicator for unread messages) for a Tab at
     * the specified position.
     *
     * @param tabPosition     zero-based index for the tab.
     * @param backgroundColor a color for this badge, such as 0xFFFF0000.
     * @param initialCount    text displayed initially for this Badge.
     * @return a {@link BottomBarBadge} object.
     */
    public BottomBarBadge makeBadgeForTabAt(int tabPosition, int backgroundColor, int initialCount) {
        if (mItems == null || mItems.length == 0) {
            throw new UnsupportedOperationException("You have no BottomBar Tabs set yet. "
                    + "Please set them first before calling the makeBadgeForTabAt() method.");
        } else if (tabPosition > mItems.length - 1 || tabPosition < 0) {
            throw new IndexOutOfBoundsException("Cant make a Badge for Tab " + "index " + tabPosition
                    + ". You have no BottomBar Tabs at that position.");
        }

        BottomBarBadge badge = new BottomBarBadge(mContext, tabPosition, mItemContainer.getChildAt(tabPosition),
                backgroundColor);
        badge.setTag(TAG_BADGE + tabPosition);
        badge.setCount(initialCount);

        if (mBadgeMap == null) {
            mBadgeMap = new HashMap<>();
        }

        mBadgeMap.put(tabPosition, badge.getTag());

        boolean canShow = true;

        if (mIsComingFromRestoredState && mBadgeStateMap != null && mBadgeStateMap.containsKey(tabPosition)) {
            canShow = mBadgeStateMap.get(tabPosition);
        }

        if (canShow && mCurrentTabPosition != tabPosition && initialCount != 0) {
            badge.show();
        } else {
            badge.hide();
        }

        return badge;
    }

    /**
     * Set a custom TypeFace for the tab titles.
     * The .ttf file should be located at "/src/main/assets".
     *
     * @param typeFacePath path for the custom typeface in the assets directory.
     */
    public void setTypeFace(String typeFacePath) {
        Typeface typeface = Typeface.createFromAsset(mContext.getAssets(), typeFacePath);

        if (mItemContainer != null && mItemContainer.getChildCount() > 0) {
            for (int i = 0; i < mItemContainer.getChildCount(); i++) {
                View bottomBarTab = mItemContainer.getChildAt(i);
                TextView title = (TextView) bottomBarTab.findViewById(R.id.bb_bottom_bar_title);
                title.setTypeface(typeface);
            }
        } else {
            mPendingTypeface = typeface;
        }
    }

    /**
     * Set a custom text appearance for the tab title.
     *
     * @param resId path to the custom text appearance.
     */
    public void setTextAppearance(@StyleRes int resId) {
        if (mItemContainer != null && mItemContainer.getChildCount() > 0) {
            for (int i = 0; i < mItemContainer.getChildCount(); i++) {
                View bottomBarTab = mItemContainer.getChildAt(i);
                TextView title = (TextView) bottomBarTab.findViewById(R.id.bb_bottom_bar_title);
                MiscUtils.setTextAppearance(title, resId);
            }
        } else {
            mPendingTextAppearance = resId;
        }
    }

    /**
     * Hide the shadow that's normally above the BottomBar.
     */
    public void hideShadow() {
        if (mShadowView != null) {
            mShadowView.setVisibility(GONE);
        }
    }

    /**
     * Prevent the BottomBar drawing behind the Navigation Bar and making
     * it transparent. Must be called before setting items.
     */
    public void noNavBarGoodness() {
        if (mItems != null) {
            throw new UnsupportedOperationException("This BottomBar already has items! "
                    + "You must call noNavBarGoodness() before setting the items, preferably "
                    + "right after attaching it to your layout.");
        }

        mDrawBehindNavBar = false;
    }

    /**
     * Force the BottomBar to behave exactly same on tablets and phones,
     * instead of showing a left menu on tablets.
     */
    public void noTabletGoodness() {
        if (mItems != null) {
            throw new UnsupportedOperationException("This BottomBar already has items! "
                    + "You must call noTabletGoodness() before setting the items, preferably "
                    + "right after attaching it to your layout.");
        }

        mIgnoreTabletLayout = true;
    }

    /**
     * Don't resize the tabs when selecting a new one, so every tab is the same0 if you have more than three
     * tabs. The text still displays the scale animation and the icon moves up, but the badass width animation
     * is ignored.
     */
    public void noResizeGoodness() {
        mIgnoreShiftingResize = true;
    }

    /**
     * Get this BottomBar's height (or width), depending if the BottomBar
     * is on the bottom (phones) or the left (tablets) of the screen.
     *
     * @param listener {@link OnSizeDeterminedListener} to get the size when it's ready.
     */
    public void getBarSize(final OnSizeDeterminedListener listener) {
        final int sizeCandidate = mIsTabletMode ? mOuterContainer.getWidth() : mOuterContainer.getHeight();

        if (sizeCandidate == 0) {
            mOuterContainer.getViewTreeObserver()
                    .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                        @SuppressWarnings("deprecation")
                        @Override
                        public void onGlobalLayout() {
                            listener.onSizeReady(
                                    mIsTabletMode ? mOuterContainer.getWidth() : mOuterContainer.getHeight());
                            ViewTreeObserver obs = mOuterContainer.getViewTreeObserver();

                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                                obs.removeOnGlobalLayoutListener(this);
                            } else {
                                obs.removeGlobalOnLayoutListener(this);
                            }
                        }
                    });
            return;
        }

        listener.onSizeReady(sizeCandidate);
    }

    /**
     * Get the actual BottomBar that has the tabs inside it for whatever what you may want
     * to do with it.
     *
     * @return the BottomBar.
     */
    public View getBar() {
        return mOuterContainer;
    }

    /**
     * Super ugly hacks
     * ----------------------------/
     */

    /**
     * If you get some unwanted extra padding in the top (such as
     * when using CoordinatorLayout), this fixes it.
     */
    public void noTopOffset() {
        mUseTopOffset = false;
    }

    /**
     * If your ActionBar gets inside the status bar for some reason,
     * this fixes it.
     */
    public void useOnlyStatusBarTopOffset() {
        mUseOnlyStatusBarOffset = true;
    }

    /**
     * ------------------------------------------- //
     */
    public BottomBar(Context context) {
        super(context);
        init(context, null, 0, 0);
    }

    public BottomBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0, 0);
    }

    public BottomBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr, 0);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public BottomBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        mContext = context;

        mDarkBackgroundColor = ContextCompat.getColor(getContext(), R.color.bb_darkBackgroundColor);
        mWhiteColor = ContextCompat.getColor(getContext(), R.color.white);
        mPrimaryColor = MiscUtils.getColor(getContext(), R.attr.colorPrimary);
        mInActiveColor = ContextCompat.getColor(getContext(), R.color.bb_inActiveBottomBarItemColor);

        mScreenWidth = MiscUtils.getScreenWidth(mContext);
        mTwoDp = MiscUtils.dpToPixel(mContext, 2);
        mTenDp = MiscUtils.dpToPixel(mContext, 10);
        mMaxFixedItemWidth = MiscUtils.dpToPixel(mContext, 168);
        mMaxInActiveShiftingItemWidth = MiscUtils.dpToPixel(mContext, 96);
    }

    private void initializeViews() {
        mIsTabletMode = !mIgnoreTabletLayout
                && mContext.getResources().getBoolean(R.bool.bb_bottom_bar_is_tablet_mode);
        ViewCompat.setElevation(this, MiscUtils.dpToPixel(mContext, 8));
        View rootView = View.inflate(mContext, mIsTabletMode ? R.layout.bb_bottom_bar_item_container_tablet
                : R.layout.bb_bottom_bar_item_container, null);
        mTabletRightBorder = rootView.findViewById(R.id.bb_tablet_right_border);

        mUserContentContainer = (ViewGroup) rootView.findViewById(R.id.bb_user_content_container);
        mShadowView = rootView.findViewById(R.id.bb_bottom_bar_shadow);

        mOuterContainer = (ViewGroup) rootView.findViewById(R.id.bb_bottom_bar_outer_container);
        mItemContainer = (ViewGroup) rootView.findViewById(R.id.bb_bottom_bar_item_container);

        mBackgroundView = rootView.findViewById(R.id.bb_bottom_bar_background_view);
        mBackgroundOverlay = rootView.findViewById(R.id.bb_bottom_bar_background_overlay);

        if (mIsShy && mIgnoreTabletLayout) {
            mPendingUserContentView = null;
        }

        if (mPendingUserContentView != null) {
            ViewGroup.LayoutParams params = mPendingUserContentView.getLayoutParams();

            if (params == null) {
                params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT);
            }

            if (mIsTabletMode && mIsShy) {
                ((ViewGroup) mPendingUserContentView.getParent()).removeView(mPendingUserContentView);
            }

            mUserContentContainer.addView(mPendingUserContentView, 0, params);
            mPendingUserContentView = null;
        }

        if (mIsShy && !mIsTabletMode) {
            getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @SuppressWarnings("deprecation")
                @Override
                public void onGlobalLayout() {
                    if (!mShyHeightAlreadyCalculated) {
                        ((CoordinatorLayout.LayoutParams) getLayoutParams())
                                .setBehavior(new BottomNavigationBehavior(getOuterContainer().getHeight(), 0,
                                        isShy(), mIsTabletMode));
                    }

                    ViewTreeObserver obs = getViewTreeObserver();

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                        obs.removeOnGlobalLayoutListener(this);
                    } else {
                        obs.removeGlobalOnLayoutListener(this);
                    }
                }
            });
        }

        addView(rootView);
    }

    /**
     * Makes this BottomBar "shy". In other words, it hides on scroll.
     */
    private void toughChildHood(boolean useExtraOffset) {
        mIsShy = true;
        mUseExtraOffset = useExtraOffset;
    }

    protected boolean isShy() {
        return mIsShy;
    }

    protected void shyHeightAlreadyCalculated() {
        mShyHeightAlreadyCalculated = true;
    }

    protected boolean useExtraOffset() {
        return mUseExtraOffset;
    }

    protected ViewGroup getUserContainer() {
        return mUserContentContainer;
    }

    protected View getOuterContainer() {
        return mOuterContainer;
    }

    protected boolean drawBehindNavBar() {
        return mDrawBehindNavBar;
    }

    protected boolean useTopOffset() {
        return mUseTopOffset;
    }

    protected boolean useOnlyStatusbarOffset() {
        return mUseOnlyStatusBarOffset;
    }

    protected void setBarVisibility(int visibility) {
        if (mOuterContainer != null) {
            mOuterContainer.setVisibility(visibility);
        }

        if (mBackgroundView != null) {
            mBackgroundView.setVisibility(visibility);
        }

        if (mBackgroundOverlay != null) {
            mBackgroundOverlay.setVisibility(visibility);
        }
    }

    @Override
    public void onClick(View v) {
        if (v.getTag().equals(TAG_BOTTOM_BAR_VIEW_INACTIVE)) {
            View oldTab = findViewWithTag(TAG_BOTTOM_BAR_VIEW_ACTIVE);

            unselectTab(oldTab, true);
            selectTab(v, true);

            shiftingMagic(oldTab, v, true);
        }
        updateSelectedTab(findItemPosition(v));
    }

    private void shiftingMagic(View oldTab, View newTab, boolean animate) {
        if (!mIsTabletMode && mIsShiftingMode && !mIgnoreShiftingResize) {
            if (animate) {
                MiscUtils.resizeTab(oldTab, oldTab.getWidth(), mInActiveShiftingItemWidth);
                MiscUtils.resizeTab(newTab, newTab.getWidth(), mActiveShiftingItemWidth);
            } else {
                oldTab.getLayoutParams().width = mInActiveShiftingItemWidth;
                newTab.getLayoutParams().width = mActiveShiftingItemWidth;
            }
        }
    }

    private void updateSelectedTab(int newPosition) {
        final boolean notifyMenuListener = mMenuListener != null && mItems instanceof BottomBarTab[];
        final boolean notifyRegularListener = mListener != null;

        if (newPosition != mCurrentTabPosition) {
            handleBadgeVisibility(mCurrentTabPosition, newPosition);
            mCurrentTabPosition = newPosition;

            if (notifyRegularListener) {
                notifyRegularListener(mListener, false, mCurrentTabPosition);
            }

            if (notifyMenuListener) {
                notifyMenuListener(mMenuListener, false, ((BottomBarTab) mItems[mCurrentTabPosition]).id);
            }

            updateCurrentFragment();
        } else {
            if (notifyRegularListener) {
                notifyRegularListener(mListener, true, mCurrentTabPosition);
            }

            if (notifyMenuListener) {
                notifyMenuListener(mMenuListener, true, ((BottomBarTab) mItems[mCurrentTabPosition]).id);
            }
        }
    }

    @SuppressWarnings("deprecation")
    private void notifyRegularListener(Object listener, boolean isReselection, int position) {
        if (listener instanceof OnTabClickListener) {
            OnTabClickListener onTabClickListener = (OnTabClickListener) listener;

            if (!isReselection) {
                onTabClickListener.onTabSelected(position);
            } else {
                onTabClickListener.onTabReSelected(position);
            }
        } else if (listener instanceof OnTabSelectedListener) {
            OnTabSelectedListener onTabSelectedListener = (OnTabSelectedListener) listener;

            if (!isReselection) {
                onTabSelectedListener.onItemSelected(position);
            }
        }
    }

    @SuppressWarnings("deprecation")
    private void notifyMenuListener(Object listener, boolean isReselection, @IdRes int menuItemId) {
        if (listener instanceof OnMenuTabClickListener) {
            OnMenuTabClickListener onMenuTabClickListener = (OnMenuTabClickListener) listener;

            if (!isReselection) {
                onMenuTabClickListener.onMenuTabSelected(menuItemId);
            } else {
                onMenuTabClickListener.onMenuTabReSelected(menuItemId);
            }
        } else if (listener instanceof OnMenuTabSelectedListener) {
            OnMenuTabSelectedListener onMenuTabSelectedListener = (OnMenuTabSelectedListener) listener;

            if (!isReselection) {
                onMenuTabSelectedListener.onMenuItemSelected(menuItemId);
            }
        }
    }

    private void handleBadgeVisibility(int oldPosition, int newPosition) {
        if (mBadgeMap == null) {
            return;
        }

        if (mBadgeMap.containsKey(oldPosition)) {
            BottomBarBadge oldBadge = (BottomBarBadge) mOuterContainer.findViewWithTag(mBadgeMap.get(oldPosition));

            if (oldBadge.getAutoShowAfterUnSelection()) {
                oldBadge.show();
            } else {
                oldBadge.hide();
            }
        }

        if (mBadgeMap.containsKey(newPosition)) {
            BottomBarBadge newBadge = (BottomBarBadge) mOuterContainer.findViewWithTag(mBadgeMap.get(newPosition));
            newBadge.hide();
        }
    }

    @Override
    public boolean onLongClick(View v) {
        if ((mIsShiftingMode || mIsTabletMode) && v.getTag().equals(TAG_BOTTOM_BAR_VIEW_INACTIVE)) {
            Toast.makeText(mContext, mItems[findItemPosition(v)].getTitle(mContext), Toast.LENGTH_SHORT).show();
        }

        return true;
    }

    private void updateItems(final BottomBarItemBase[] bottomBarItems) {
        if (mItemContainer == null) {
            initializeViews();
        }

        int index = 0;
        int biggestWidth = 0;

        mIsShiftingMode = MAX_FIXED_TAB_COUNT < bottomBarItems.length;

        if (!mIsDarkTheme && !mIgnoreNightMode && MiscUtils.isNightMode(mContext)) {
            mIsDarkTheme = true;
        }

        if (mIsDarkTheme) {
            darkThemeMagic();
        } else if (!mIsTabletMode && mIsShiftingMode) {
            mDefaultBackgroundColor = mCurrentBackgroundColor = mPrimaryColor;
            mBackgroundView.setBackgroundColor(mDefaultBackgroundColor);

            if (mContext instanceof Activity) {
                navBarMagic((Activity) mContext, this);
            }
        }

        View[] viewsToAdd = new View[bottomBarItems.length];

        for (BottomBarItemBase bottomBarItemBase : bottomBarItems) {
            int layoutResource;

            if (mIsShiftingMode && !mIsTabletMode) {
                layoutResource = R.layout.bb_bottom_bar_item_shifting;
            } else {
                layoutResource = mIsTabletMode ? R.layout.bb_bottom_bar_item_fixed_tablet
                        : R.layout.bb_bottom_bar_item_fixed;
            }

            View bottomBarTab = View.inflate(mContext, layoutResource, null);
            ImageView icon = (ImageView) bottomBarTab.findViewById(R.id.bb_bottom_bar_icon);

            icon.setImageDrawable(bottomBarItemBase.getIcon(mContext));

            if (!mIsTabletMode) {
                TextView title = (TextView) bottomBarTab.findViewById(R.id.bb_bottom_bar_title);
                title.setText(bottomBarItemBase.getTitle(mContext));

                if (mPendingTextAppearance != -1) {
                    MiscUtils.setTextAppearance(title, mPendingTextAppearance);
                }

                if (mPendingTypeface != null) {
                    title.setTypeface(mPendingTypeface);
                }
            }

            if (mIsDarkTheme || (!mIsTabletMode && mIsShiftingMode)) {
                icon.setColorFilter(mWhiteColor);
            }

            if (bottomBarItemBase instanceof BottomBarTab) {
                bottomBarTab.setId(((BottomBarTab) bottomBarItemBase).id);
            }

            if (index == mCurrentTabPosition) {
                selectTab(bottomBarTab, false);
            } else {
                unselectTab(bottomBarTab, false);
            }

            if (!mIsTabletMode) {
                if (bottomBarTab.getWidth() > biggestWidth) {
                    biggestWidth = bottomBarTab.getWidth();
                }

                viewsToAdd[index] = bottomBarTab;
            } else {
                mItemContainer.addView(bottomBarTab);
            }

            bottomBarTab.setOnClickListener(this);
            bottomBarTab.setOnLongClickListener(this);
            index++;
        }

        if (!mIsTabletMode) {
            int proposedItemWidth = Math.min(MiscUtils.dpToPixel(mContext, mScreenWidth / bottomBarItems.length),
                    mMaxFixedItemWidth);

            mInActiveShiftingItemWidth = (int) (proposedItemWidth * 0.9);
            mActiveShiftingItemWidth = (int) (proposedItemWidth
                    + (proposedItemWidth * (bottomBarItems.length * 0.1)));

            for (View bottomBarView : viewsToAdd) {
                LinearLayout.LayoutParams params;

                if (mIsShiftingMode && !mIgnoreShiftingResize) {
                    if (TAG_BOTTOM_BAR_VIEW_ACTIVE.equals(bottomBarView.getTag())) {
                        params = new LinearLayout.LayoutParams(mActiveShiftingItemWidth,
                                LinearLayout.LayoutParams.WRAP_CONTENT);
                    } else {
                        params = new LinearLayout.LayoutParams(mInActiveShiftingItemWidth,
                                LinearLayout.LayoutParams.WRAP_CONTENT);
                    }
                } else {
                    params = new LinearLayout.LayoutParams(proposedItemWidth,
                            LinearLayout.LayoutParams.WRAP_CONTENT);
                }

                bottomBarView.setLayoutParams(params);
                mItemContainer.addView(bottomBarView);
            }
        }

        if (mPendingTextAppearance != -1) {
            mPendingTextAppearance = -1;
        }

        if (mPendingTypeface != null) {
            mPendingTypeface = null;
        }
    }

    private void darkThemeMagic() {
        if (!mIsTabletMode) {
            mBackgroundView.setBackgroundColor(mDarkBackgroundColor);
        } else {
            mItemContainer.setBackgroundColor(mDarkBackgroundColor);
            mTabletRightBorder
                    .setBackgroundColor(ContextCompat.getColor(mContext, R.color.bb_tabletRightBorderDark));
        }
    }

    private void onRestoreInstanceState(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            mCurrentTabPosition = savedInstanceState.getInt(STATE_CURRENT_SELECTED_TAB, -1);
            mBadgeStateMap = (HashMap<Integer, Boolean>) savedInstanceState
                    .getSerializable(STATE_BADGE_STATES_BUNDLE);

            if (mCurrentTabPosition == -1) {
                mCurrentTabPosition = 0;
                Log.e("BottomBar",
                        "You must override the Activity's onSave"
                                + "InstanceState(Bundle outState) and call BottomBar.onSaveInstanc"
                                + "eState(outState) there to restore the state properly.");
            }

            mIsComingFromRestoredState = true;
            mShouldUpdateFragmentInitially = true;
        }
    }

    private void selectTab(View tab, boolean animate) {
        tab.setTag(TAG_BOTTOM_BAR_VIEW_ACTIVE);
        ImageView icon = (ImageView) tab.findViewById(R.id.bb_bottom_bar_icon);
        TextView title = (TextView) tab.findViewById(R.id.bb_bottom_bar_title);

        int tabPosition = findItemPosition(tab);

        if (!mIsShiftingMode || mIsTabletMode) {
            int activeColor = mCustomActiveTabColor != 0 ? mCustomActiveTabColor : mPrimaryColor;
            icon.setColorFilter(activeColor);

            if (title != null) {
                title.setTextColor(activeColor);
            }
        }

        if (mIsDarkTheme) {
            if (title != null) {
                ViewCompat.setAlpha(title, 1.0f);
            }

            ViewCompat.setAlpha(icon, 1.0f);
        }

        if (title == null) {
            return;
        }

        int translationY = mIsShiftingMode ? mTenDp : mTwoDp;

        if (animate) {
            ViewCompat.animate(title).setDuration(ANIMATION_DURATION).scaleX(1).scaleY(1).start();
            ViewCompat.animate(tab).setDuration(ANIMATION_DURATION).translationY(-translationY).start();

            if (mIsShiftingMode) {
                ViewCompat.animate(icon).setDuration(ANIMATION_DURATION).alpha(1.0f).start();
            }

            handleBackgroundColorChange(tabPosition, tab);
        } else {
            ViewCompat.setScaleX(title, 1);
            ViewCompat.setScaleY(title, 1);
            ViewCompat.setTranslationY(tab, -translationY);

            if (mIsShiftingMode) {
                ViewCompat.setAlpha(icon, 1.0f);
            }
        }
    }

    private void unselectTab(View tab, boolean animate) {
        tab.setTag(TAG_BOTTOM_BAR_VIEW_INACTIVE);

        ImageView icon = (ImageView) tab.findViewById(R.id.bb_bottom_bar_icon);
        TextView title = (TextView) tab.findViewById(R.id.bb_bottom_bar_title);

        if (!mIsShiftingMode || mIsTabletMode) {
            int inActiveColor = mIsDarkTheme ? mWhiteColor : mInActiveColor;
            icon.setColorFilter(inActiveColor);

            if (title != null) {
                title.setTextColor(inActiveColor);
            }
        }

        if (mIsDarkTheme) {
            if (title != null) {
                ViewCompat.setAlpha(title, 0.6f);
            }

            ViewCompat.setAlpha(icon, 0.6f);
        }

        if (title == null) {
            return;
        }

        float scale = mIsShiftingMode ? 0 : 0.86f;

        if (animate) {
            ViewCompat.animate(title).setDuration(ANIMATION_DURATION).scaleX(scale).scaleY(scale).start();
            ViewCompat.animate(tab).setDuration(ANIMATION_DURATION).translationY(0).start();

            if (mIsShiftingMode) {
                ViewCompat.animate(icon).setDuration(ANIMATION_DURATION).alpha(0.6f).start();
            }
        } else {
            ViewCompat.setScaleX(title, scale);
            ViewCompat.setScaleY(title, scale);
            ViewCompat.setTranslationY(tab, 0);

            if (mIsShiftingMode) {
                ViewCompat.setAlpha(icon, 0.6f);
            }
        }
    }

    private void handleBackgroundColorChange(int tabPosition, View tab) {
        if (mIsDarkTheme || !mIsShiftingMode || mIsTabletMode)
            return;

        if (mColorMap != null && mColorMap.containsKey(tabPosition)) {
            handleBackgroundColorChange(tab, mColorMap.get(tabPosition));
        } else {
            handleBackgroundColorChange(tab, mDefaultBackgroundColor);
        }
    }

    private void handleBackgroundColorChange(View tab, int color) {
        MiscUtils.animateBGColorChange(tab, mBackgroundView, mBackgroundOverlay, color);
        mCurrentBackgroundColor = color;
    }

    private int findItemPosition(View viewToFind) {
        int position = 0;

        for (int i = 0; i < mItemContainer.getChildCount(); i++) {
            View candidate = mItemContainer.getChildAt(i);

            if (candidate.equals(viewToFind)) {
                position = i;
                break;
            }
        }

        return position;
    }

    private void updateCurrentFragment() {
        if (!mShouldUpdateFragmentInitially && mFragmentManager != null && mFragmentContainer != 0 && mItems != null
                && mItems instanceof BottomBarFragment[]) {
            BottomBarFragment newFragment = ((BottomBarFragment) mItems[mCurrentTabPosition]);

            if (mFragmentManager instanceof android.support.v4.app.FragmentManager
                    && newFragment.getSupportFragment() != null) {
                ((android.support.v4.app.FragmentManager) mFragmentManager).beginTransaction()
                        .replace(mFragmentContainer, newFragment.getSupportFragment()).commit();
            } else if (mFragmentManager instanceof android.app.FragmentManager
                    && newFragment.getFragment() != null) {
                ((android.app.FragmentManager) mFragmentManager).beginTransaction()
                        .replace(mFragmentContainer, newFragment.getFragment()).commit();
            }
        }

        mShouldUpdateFragmentInitially = false;
    }

    private void clearItems() {
        if (mItemContainer != null) {
            int childCount = mItemContainer.getChildCount();

            if (childCount > 0) {
                for (int i = 0; i < childCount; i++) {
                    mItemContainer.removeView(mItemContainer.getChildAt(i));
                }
            }
        }

        if (mFragmentManager != null) {
            mFragmentManager = null;
        }

        if (mFragmentContainer != 0) {
            mFragmentContainer = 0;
        }

        if (mItems != null) {
            mItems = null;
        }
    }

    private static void navBarMagic(Activity activity, final BottomBar bottomBar) {
        Resources res = activity.getResources();
        int softMenuIdentifier = res.getIdentifier("config_showNavigationBar", "bool", "android");
        int navBarIdentifier = res.getIdentifier("navigation_bar_height", "dimen", "android");
        int navBarHeight = 0;

        if (navBarIdentifier > 0) {
            navBarHeight = res.getDimensionPixelSize(navBarIdentifier);
        }

        if (!bottomBar.drawBehindNavBar() || navBarHeight == 0
                || (!(softMenuIdentifier > 0 && res.getBoolean(softMenuIdentifier)))) {
            return;
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH
                && ViewConfiguration.get(activity).hasPermanentMenuKey()) {
            return;
        }

        /**
         * Copy-paste coding made possible by:
         * http://stackoverflow.com/a/14871974/940036
         */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            Display d = activity.getWindowManager().getDefaultDisplay();

            DisplayMetrics realDisplayMetrics = new DisplayMetrics();
            d.getRealMetrics(realDisplayMetrics);

            int realHeight = realDisplayMetrics.heightPixels;
            int realWidth = realDisplayMetrics.widthPixels;

            DisplayMetrics displayMetrics = new DisplayMetrics();
            d.getMetrics(displayMetrics);

            int displayHeight = displayMetrics.heightPixels;
            int displayWidth = displayMetrics.widthPixels;

            boolean hasSoftwareKeys = (realWidth - displayWidth) > 0 || (realHeight - displayHeight) > 0;

            if (!hasSoftwareKeys) {
                return;
            }
        }
        /**
         * End of delicious copy-paste code
         */

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
                && res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            activity.getWindow().getAttributes().flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;

            if (bottomBar.useTopOffset()) {
                int offset;
                int statusBarResource = res.getIdentifier("status_bar_height", "dimen", "android");

                if (statusBarResource > 0) {
                    offset = res.getDimensionPixelSize(statusBarResource);
                } else {
                    offset = MiscUtils.dpToPixel(activity, 25);
                }

                if (!bottomBar.useOnlyStatusbarOffset()) {
                    TypedValue tv = new TypedValue();
                    if (activity.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
                        offset += TypedValue.complexToDimensionPixelSize(tv.data, res.getDisplayMetrics());
                    } else {
                        offset += MiscUtils.dpToPixel(activity, 56);
                    }
                }

                bottomBar.getUserContainer().setPadding(0, offset, 0, 0);
            }

            final View outerContainer = bottomBar.getOuterContainer();
            final int navBarHeightCopy = navBarHeight;
            bottomBar.getViewTreeObserver()
                    .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                        @SuppressWarnings("deprecation")
                        @Override
                        public void onGlobalLayout() {
                            bottomBar.shyHeightAlreadyCalculated();

                            int newHeight = outerContainer.getHeight() + navBarHeightCopy;
                            outerContainer.getLayoutParams().height = newHeight;

                            if (bottomBar.isShy()) {
                                int defaultOffset = bottomBar.useExtraOffset() ? navBarHeightCopy : 0;
                                bottomBar.setTranslationY(defaultOffset);
                                ((CoordinatorLayout.LayoutParams) bottomBar.getLayoutParams())
                                        .setBehavior(new BottomNavigationBehavior(newHeight, defaultOffset,
                                                bottomBar.isShy(), bottomBar.mIsTabletMode));
                            }

                            ViewTreeObserver obs = outerContainer.getViewTreeObserver();

                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                                obs.removeOnGlobalLayoutListener(this);
                            } else {
                                obs.removeGlobalOnLayoutListener(this);
                            }
                        }
                    });
        }
    }
}