com.github.shareme.gwsmaterialuikit.library.material.app.ToolbarManager.java Source code

Java tutorial

Introduction

Here is the source code for com.github.shareme.gwsmaterialuikit.library.material.app.ToolbarManager.java

Source

/*
 * Copyright 2015 Rey Pham.
 * Modifications Copyright(C) 2016 Fred Grott
 *
 * 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.
 *
 */
package com.github.shareme.gwsmaterialuikit.library.material.app;

import android.os.Build;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.v4.animation.AnimatorCompatHelper;
import android.support.v4.animation.AnimatorListenerCompat;
import android.support.v4.animation.AnimatorUpdateListenerCompat;
import android.support.v4.animation.ValueAnimatorCompat;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.TranslateAnimation;

import com.github.shareme.gwsmaterialuikit.library.material.drawable.NavigationDrawerDrawable;
import com.github.shareme.gwsmaterialuikit.library.material.drawable.ToolbarRippleDrawable;
import com.github.shareme.gwsmaterialuikit.library.material.util.ViewUtil;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

/**
 * A Manager class to help handling Toolbar used as ActionBar in ActionBarActivity.
 * It help grouping ActionItem in Toolbar and only show the items of current group.
 * It also help manager state of navigation icon.
 * Created by Rey on 1/6/2015.
 */
@SuppressWarnings("unused")
public class ToolbarManager {

    private AppCompatDelegate mAppCompatDelegate;
    private Toolbar mToolbar;
    private int mRippleStyle;
    private Animator mAnimator;
    private ActionMenuView mMenuView;
    private ToolbarRippleDrawable.Builder mBuilder;

    private int mCurrentGroup = 0;
    private boolean mGroupChanged = false;
    private boolean mMenuDataChanged = true;

    /**
     * Interface definition for a callback to be invoked when the current group is changed.
     */
    public interface OnToolbarGroupChangedListener {

        /**
         * Called when the current group changed.
         * @param oldGroupId The id of old group.
         * @param groupId The id of new group.
         */
        public void onToolbarGroupChanged(int oldGroupId, int groupId);

    }

    private ArrayList<WeakReference<OnToolbarGroupChangedListener>> mListeners = new ArrayList<>();

    private ViewTreeObserver.OnGlobalLayoutListener mOnGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            ToolbarManager.this.onGlobalLayout();
        }
    };

    private ArrayList<Animation> mAnimations = new ArrayList<>();

    private Animation.AnimationListener mOutAnimationEndListener = new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {
        }

        @Override
        public void onAnimationEnd(Animation animation) {
            if (mAppCompatDelegate != null)
                mAppCompatDelegate.invalidateOptionsMenu();
            else
                onPrepareMenu();
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
        }
    };

    private NavigationManager mNavigationManager;

    public ToolbarManager(AppCompatDelegate delegate, Toolbar toolbar, int defaultGroupId, int rippleStyle,
            int animIn, int animOut) {
        this(delegate, toolbar, defaultGroupId, rippleStyle, new SimpleAnimator(animIn, animOut));
    }

    public ToolbarManager(AppCompatDelegate delegate, Toolbar toolbar, int defaultGroupId, int rippleStyle,
            Animator animator) {
        mAppCompatDelegate = delegate;
        mToolbar = toolbar;
        mCurrentGroup = defaultGroupId;
        mRippleStyle = rippleStyle;
        mAnimator = animator;
        mAppCompatDelegate.setSupportActionBar(toolbar);
    }

    /**
     * Register a listener for current group changed event. Note that it doesn't hold any strong reference to listener, so don't use anonymous listener.
     */
    public void registerOnToolbarGroupChangedListener(OnToolbarGroupChangedListener listener) {
        for (int i = mListeners.size() - 1; i >= 0; i--) {
            WeakReference<OnToolbarGroupChangedListener> ref = mListeners.get(i);
            if (ref.get() == null)
                mListeners.remove(i);
            else if (ref.get() == listener)
                return;
        }

        mListeners.add(new WeakReference<OnToolbarGroupChangedListener>(listener));
    }

    /**
     * Unregister a listener.
     * @param listener
     */
    public void unregisterOnToolbarGroupChangedListener(OnToolbarGroupChangedListener listener) {
        for (int i = mListeners.size() - 1; i >= 0; i--) {
            WeakReference<OnToolbarGroupChangedListener> ref = mListeners.get(i);
            if (ref.get() == null || ref.get() == listener)
                mListeners.remove(i);
        }
    }

    private void dispatchOnToolbarGroupChanged(int oldGroupId, int groupId) {
        for (int i = mListeners.size() - 1; i >= 0; i--) {
            WeakReference<OnToolbarGroupChangedListener> ref = mListeners.get(i);
            if (ref.get() == null)
                mListeners.remove(i);
            else
                ref.get().onToolbarGroupChanged(oldGroupId, groupId);
        }
    }

    /**
     * @return The current group of the Toolbar.
     */
    public int getCurrentGroup() {
        return mCurrentGroup;
    }

    /**
     * Set current group of the Toolbar.
     * @param groupId The id of group.
     */
    public void setCurrentGroup(int groupId) {
        if (mCurrentGroup != groupId) {
            int oldGroupId = mCurrentGroup;
            mCurrentGroup = groupId;
            mGroupChanged = true;
            dispatchOnToolbarGroupChanged(oldGroupId, mCurrentGroup);
            animateOut();
        }
    }

    /**
     * This funcction should be called in onCreateOptionsMenu of Activity or Fragment to inflate a new menu.
     * @param menuId
     */
    public void createMenu(int menuId) {
        mToolbar.inflateMenu(menuId);
        mMenuDataChanged = true;
        if (mAppCompatDelegate == null)
            onPrepareMenu();
    }

    /**
     * This function should be called in onPrepareOptionsMenu(Menu) of Activity that use
     * Toolbar as ActionBar, or after inflating menu.
     */
    public void onPrepareMenu() {
        if (mGroupChanged || mMenuDataChanged) {
            mToolbar.getViewTreeObserver().addOnGlobalLayoutListener(mOnGlobalLayoutListener);

            Menu menu = mToolbar.getMenu();
            for (int i = 0, count = menu.size(); i < count; i++) {
                MenuItem item = menu.getItem(i);
                item.setVisible(item.getGroupId() == mCurrentGroup || item.getGroupId() == 0);
            }

            mMenuDataChanged = false;
        }
    }

    /**
     * Set a NavigationManager to manage navigation icon state.
     */
    public void setNavigationManager(NavigationManager navigationManager) {
        mNavigationManager = navigationManager;
        notifyNavigationStateInvalidated();
    }

    /**
     * Notify the current state of navigation icon is invalid. It should update the state immediately without showing animation.
     */
    public void notifyNavigationStateInvalidated() {
        if (mNavigationManager != null)
            mNavigationManager.notifyStateInvalidated();
    }

    /**
     * Notify the current state of navigation icon is invalid. It should update the state immediately without showing animation.
     */
    public void notifyNavigationStateChanged() {
        if (mNavigationManager != null)
            mNavigationManager.notifyStateChanged();
    }

    /**
     * Notify the progress of animation between 2 states changed. Use this function to sync the progress with another animation.
     * @param isBackState the current state (the end state of animation) is back state or not.
     * @param progress the current progress of animation.
     */
    public void notifyNavigationStateProgressChanged(boolean isBackState, float progress) {
        if (mNavigationManager != null)
            mNavigationManager.notifyStateProgressChanged(isBackState, progress);
    }

    /**
     * @return The navigation is in back state or not.
     */
    public boolean isNavigationBackState() {
        return mNavigationManager != null && mNavigationManager.isBackState();
    }

    public boolean isNavigationVisisble() {
        return mNavigationManager != null && mNavigationManager.isNavigationVisible();
    }

    public void setNavigationVisisble(boolean visible, boolean animation) {
        if (mNavigationManager != null)
            mNavigationManager.setNavigationVisible(visible, animation);
    }

    private ToolbarRippleDrawable getBackground() {
        if (mBuilder == null)
            mBuilder = new ToolbarRippleDrawable.Builder(mToolbar.getContext(), mRippleStyle);

        return mBuilder.build();
    }

    private ActionMenuView getMenuView() {
        if (mMenuView == null) {
            for (int i = 0; i < mToolbar.getChildCount(); i++) {
                View child = mToolbar.getChildAt(i);
                if (child instanceof ActionMenuView) {
                    mMenuView = (ActionMenuView) child;
                    break;
                }
            }
        }

        return mMenuView;
    }

    private void onGlobalLayout() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
            mToolbar.getViewTreeObserver().removeOnGlobalLayoutListener(mOnGlobalLayoutListener);
        else
            mToolbar.getViewTreeObserver().removeGlobalOnLayoutListener(mOnGlobalLayoutListener);

        ActionMenuView menuView = getMenuView();
        for (int i = 0, count = menuView == null ? 0 : menuView.getChildCount(); i < count; i++) {
            View child = menuView.getChildAt(i);
            if (mRippleStyle != 0) {
                if (child.getBackground() == null || !(child.getBackground() instanceof ToolbarRippleDrawable))
                    ViewUtil.setBackground(child, getBackground());
            }
        }

        if (mGroupChanged) {
            animateIn();
            mGroupChanged = false;
        }
    }

    private void animateOut() {
        ActionMenuView menuView = getMenuView();
        int count = menuView == null ? 0 : menuView.getChildCount();
        Animation slowestAnimation = null;
        mAnimations.clear();
        mAnimations.ensureCapacity(count);

        for (int i = 0; i < count; i++) {
            View child = menuView.getChildAt(i);
            Animation anim = mAnimator.getOutAnimation(child, i);
            mAnimations.add(anim);
            if (anim != null)
                if (slowestAnimation == null || slowestAnimation.getStartOffset()
                        + slowestAnimation.getDuration() < anim.getStartOffset() + anim.getDuration())
                    slowestAnimation = anim;
        }

        if (slowestAnimation == null)
            mOutAnimationEndListener.onAnimationEnd(null);
        else {
            slowestAnimation.setAnimationListener(mOutAnimationEndListener);

            for (int i = 0; i < count; i++) {
                Animation anim = mAnimations.get(i);
                if (anim != null)
                    menuView.getChildAt(i).startAnimation(anim);
            }
        }

        mAnimations.clear();
    }

    private void animateIn() {
        ActionMenuView menuView = getMenuView();

        for (int i = 0, count = menuView == null ? 0 : menuView.getChildCount(); i < count; i++) {
            View child = menuView.getChildAt(i);
            Animation anim = mAnimator.getInAnimation(child, i);
            if (anim != null)
                child.startAnimation(anim);
        }
    }

    /**
     * Interface definition for creating animation of menu item view when switch group.
     */
    public interface Animator {

        /**
         * Get the animation of the menu item view will be removed.
         * @param v The menu item view.
         * @param position The position of item.
         * @return
         */
        public Animation getOutAnimation(View v, int position);

        /**
         * Get the animation of the menu item view will be added.
         * @param v The menu item view.
         * @param position The position of item.
         * @return
         */
        public Animation getInAnimation(View v, int position);
    }

    private static class SimpleAnimator implements Animator {
        private int mAnimationIn;
        private int mAnimationOut;

        public SimpleAnimator(int animIn, int animOut) {
            mAnimationIn = animIn;
            mAnimationOut = animOut;
        }

        @Override
        public Animation getOutAnimation(View v, int position) {
            return mAnimationOut == 0 ? null : AnimationUtils.loadAnimation(v.getContext(), mAnimationOut);
        }

        @Override
        public Animation getInAnimation(View v, int position) {
            return mAnimationIn == 0 ? null : AnimationUtils.loadAnimation(v.getContext(), mAnimationIn);
        }
    }

    /**
     * Abstract class to manage the state of navigation icon.
     */
    public static abstract class NavigationManager {

        protected NavigationDrawerDrawable mNavigationIcon;
        protected Toolbar mToolbar;

        protected boolean mNavigationVisible = true;

        protected long mAnimationDuration;
        private long mAnimTime;
        private List<Object> mAnimations = new ArrayList<>();

        public NavigationManager(NavigationDrawerDrawable navigationIcon, Toolbar toolbar) {
            mToolbar = toolbar;
            mNavigationIcon = navigationIcon;
            mToolbar.setNavigationIcon(mNavigationVisible ? mNavigationIcon : null);
            mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onNavigationClick();
                }
            });
            mAnimationDuration = toolbar.getResources().getInteger(android.R.integer.config_shortAnimTime);
        }

        /**
         * Check if current state of navigation icon is back state or not.
         * @return
         */
        public abstract boolean isBackState();

        /**
         * Hangle event click navigation icon. Subclass should override this function.
         */
        public abstract void onNavigationClick();

        /**
         * Notify the current state of navigation icon is invalid. It should update the state immediately without showing animation.
         */
        public void notifyStateInvalidated() {
            mNavigationIcon.switchIconState(
                    isBackState() ? NavigationDrawerDrawable.STATE_ARROW : NavigationDrawerDrawable.STATE_DRAWER,
                    false);
        }

        /**
         * Notify the current state of navigation icon is changed. It should update the state with animation.
         */
        public void notifyStateChanged() {
            mNavigationIcon.switchIconState(
                    isBackState() ? NavigationDrawerDrawable.STATE_ARROW : NavigationDrawerDrawable.STATE_DRAWER,
                    mNavigationVisible);
        }

        /**
         * Notify the progress of animation between 2 states changed. Use this function to sync the progress with another animation.
         * @param isBackState the current state (the end state of animation) is back state or not.
         * @param progress the current progress of animation.
         */
        public void notifyStateProgressChanged(boolean isBackState, float progress) {
            mNavigationIcon.setIconState(
                    isBackState ? NavigationDrawerDrawable.STATE_ARROW : NavigationDrawerDrawable.STATE_DRAWER,
                    progress);
        }

        public boolean isNavigationVisible() {
            return mNavigationVisible;
        }

        public void setNavigationVisible(boolean visible, boolean animation) {
            if (mNavigationVisible != visible) {
                mNavigationVisible = visible;
                long time = SystemClock.uptimeMillis();

                if (!animation) {
                    mToolbar.setNavigationIcon(mNavigationVisible ? mNavigationIcon : null);
                    mAnimTime = time;
                    if (!mNavigationVisible)
                        mNavigationIcon.cancel();
                } else {
                    if (mNavigationVisible)
                        animateNavigationIn(time);
                    else
                        animateNavigationOut(time);
                }

            }
        }

        protected Interpolator getInterpolator(boolean in) {
            return new DecelerateInterpolator();
        }

        private void cancelAllAnimations() {
            for (Object obj : mAnimations) {
                if (obj instanceof Animation)
                    ((Animation) obj).cancel();
                else if (obj instanceof ValueAnimatorCompat)
                    ((ValueAnimatorCompat) obj).cancel();
            }

            mAnimations.clear();
        }

        private void animateNavigationOut(long time) {
            mAnimTime = time;
            cancelAllAnimations();
            mToolbar.setNavigationIcon(null);
            doOnPreDraw(mToolbar, new AnimRunnable(time) {
                @Override
                void doWork() {
                    final ViewData viewData = new ViewData(mToolbar);
                    mToolbar.setNavigationIcon(mNavigationIcon);
                    doOnPreDraw(mToolbar, new AnimRunnable(mTime) {
                        @Override
                        void doWork() {
                            boolean first = true;
                            for (int i = 0, count = mToolbar.getChildCount(); i < count; i++) {
                                View child = mToolbar.getChildAt(i);
                                if (!(child instanceof ActionMenuView)) {
                                    int nextLeft = viewData.getLeft(child);
                                    if (nextLeft < 0)
                                        nextLeft = -child.getLeft() - child.getWidth();

                                    if (first) {
                                        animateViewOut(child, nextLeft, new Runnable() {
                                            @Override
                                            public void run() {
                                                mToolbar.setNavigationIcon(null);
                                                mNavigationIcon.cancel();
                                            }
                                        });
                                        first = false;
                                    } else
                                        animateViewOut(child, nextLeft, null);
                                }
                            }

                            if (first)
                                mToolbar.setNavigationIcon(null);
                        }
                    });
                }
            });
        }

        private void animateViewOut(final View view, final int nextLeft, final Runnable doOnEndRunnable) {
            final Interpolator interpolator = getInterpolator(false);
            final int prevLeft = view.getLeft();

            ValueAnimatorCompat animator = AnimatorCompatHelper.emptyValueAnimator();
            animator.setDuration(mAnimationDuration);
            animator.addUpdateListener(new AnimatorUpdateListenerCompat() {
                @Override
                public void onAnimationUpdate(ValueAnimatorCompat animation) {
                    float factor = interpolator.getInterpolation(animation.getAnimatedFraction());
                    float left = prevLeft + (nextLeft - prevLeft) * factor;
                    view.offsetLeftAndRight((int) (left - view.getLeft()));

                    if (animation.getAnimatedFraction() == 1f) {
                        if (doOnEndRunnable != null)
                            doOnEndRunnable.run();
                    }
                }
            });

            animator.addListener(new AnimatorListenerCompat() {
                @Override
                public void onAnimationStart(ValueAnimatorCompat animation) {
                }

                @Override
                public void onAnimationEnd(ValueAnimatorCompat animation) {
                    mAnimations.remove(animation);
                }

                @Override
                public void onAnimationCancel(ValueAnimatorCompat animation) {
                }

                @Override
                public void onAnimationRepeat(ValueAnimatorCompat animation) {
                }
            });

            animator.start();
            mAnimations.add(animator);
        }

        private void animateNavigationIn(long time) {
            mAnimTime = time;
            cancelAllAnimations();
            mToolbar.setNavigationIcon(null);
            doOnPreDraw(mToolbar, new AnimRunnable(time) {
                @Override
                void doWork() {
                    final ViewData viewData = new ViewData(mToolbar);
                    mToolbar.setNavigationIcon(mNavigationIcon);
                    doOnPreDraw(mToolbar, new AnimRunnable(mTime) {
                        @Override
                        void doWork() {
                            for (int i = 0, count = mToolbar.getChildCount(); i < count; i++) {
                                View child = mToolbar.getChildAt(i);
                                if (!(child instanceof ActionMenuView)) {
                                    int prevLeft = viewData.getLeft(child);
                                    if (prevLeft < 0)
                                        prevLeft = -child.getLeft() - child.getWidth();

                                    animateViewIn(child, prevLeft);
                                }
                            }
                        }
                    });
                }
            });
        }

        private void animateViewIn(View view, int prevLeft) {
            TranslateAnimation anim = new TranslateAnimation(TranslateAnimation.ABSOLUTE, prevLeft - view.getLeft(),
                    TranslateAnimation.ABSOLUTE, 0, TranslateAnimation.ABSOLUTE, 0, TranslateAnimation.ABSOLUTE, 0);

            anim.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    mAnimations.remove(animation);
                }

                @Override
                public void onAnimationRepeat(Animation animation) {
                }
            });
            anim.setInterpolator(getInterpolator(true));
            anim.setDuration(mAnimationDuration);
            view.startAnimation(anim);
            mAnimations.add(anim);
        }

        private void doOnPreDraw(final View v, final Runnable runnable) {
            v.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    runnable.run();
                    v.getViewTreeObserver().removeOnPreDrawListener(this);
                    return false;
                }
            });
        }

        static class ViewData {

            List<View> views;
            List<Integer> lefts;

            public ViewData(Toolbar toolbar) {
                int count = toolbar.getChildCount();
                views = new ArrayList<>(count);
                lefts = new ArrayList<>(count);

                for (int i = 0; i < count; i++) {
                    View child = toolbar.getChildAt(i);
                    if (!(child instanceof ActionMenuView)) {
                        views.add(child);
                        lefts.add(child.getLeft());
                    }
                }
            }

            public int getLeft(View view) {
                for (int i = 0, size = views.size(); i < size; i++)
                    if (views.get(i) == view)
                        return lefts.get(i);

                return -1;
            }

        }

        abstract class AnimRunnable implements Runnable {

            long mTime;

            public AnimRunnable(long time) {
                mTime = time;
            }

            @Override
            public void run() {
                if (mTime == mAnimTime)
                    doWork();
            }

            abstract void doWork();
        }

    }

    /**
     * A Base Navigation Manager that handle navigation state between fragment changing and navigation drawer.
     * If you want to handle state in another case, you should override isBackState(),  shouldSyncDrawerSlidingProgress(), and call notify notifyStateChanged() if need.
      */
    public static class BaseNavigationManager extends NavigationManager {
        protected DrawerLayout mDrawerLayout;
        protected FragmentManager mFragmentManager;

        /**
         *
         * @param styleId the resourceId of navigation icon style.
         * @param drawerLayout can be null if you don't need to handle navigation state when open/close navigation drawer.
         */
        public BaseNavigationManager(int styleId, FragmentManager fragmentManager, Toolbar toolbar,
                DrawerLayout drawerLayout) {
            super(new NavigationDrawerDrawable.Builder(toolbar.getContext(), styleId).build(), toolbar);
            mDrawerLayout = drawerLayout;
            mFragmentManager = fragmentManager;

            if (mDrawerLayout != null)
                mDrawerLayout.setDrawerListener(new DrawerLayout.DrawerListener() {

                    @Override
                    public void onDrawerSlide(View drawerView, float slideOffset) {
                        BaseNavigationManager.this.onDrawerSlide(drawerView, slideOffset);
                    }

                    @Override
                    public void onDrawerOpened(View drawerView) {
                        BaseNavigationManager.this.onDrawerOpened(drawerView);
                    }

                    @Override
                    public void onDrawerClosed(View drawerView) {
                        BaseNavigationManager.this.onDrawerClosed(drawerView);
                    }

                    @Override
                    public void onDrawerStateChanged(int newState) {
                        BaseNavigationManager.this.onDrawerStateChanged(newState);
                    }

                });

            mFragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
                @Override
                public void onBackStackChanged() {
                    onFragmentChanged();
                }
            });
        }

        @Override
        public boolean isBackState() {
            return mFragmentManager.getBackStackEntryCount() > 1
                    || (mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START));
        }

        @Override
        public void onNavigationClick() {
        }

        /**
         * Check if should sync progress of drawer sliding animation with navigation state changing animation.
         */
        protected boolean shouldSyncDrawerSlidingProgress() {
            if (mFragmentManager.getBackStackEntryCount() > 1)
                return false;

            return true;
        }

        protected void onFragmentChanged() {
            notifyStateChanged();
        }

        /**
         * Handling onDrawerSlide event of DrawerLayout. It'll sync progress of drawer sliding animation with navigation state changing animation if needed.
         * If you also want to handle this event, make sure to call super method.
         */
        protected void onDrawerSlide(View drawerView, float slideOffset) {
            if (!shouldSyncDrawerSlidingProgress()) {
                notifyStateInvalidated();
            } else {
                if (mDrawerLayout.isDrawerOpen(GravityCompat.START))
                    notifyStateProgressChanged(false, 1f - slideOffset);
                else
                    notifyStateProgressChanged(true, slideOffset);
            }
        }

        protected void onDrawerOpened(View drawerView) {
        }

        protected void onDrawerClosed(View drawerView) {
        }

        protected void onDrawerStateChanged(int newState) {
        }

    }

    /**
     * A Manager class extend from {@link BaseNavigationManager} class and add theme supporting.
     */
    public static class ThemableNavigationManager extends BaseNavigationManager
            implements ThemeManager.OnThemeChangedListener {

        private int mStyleId;
        private int mCurrentStyle;

        /**
         *
         * @param styleId the styleId of navigation icon.
         * @param drawerLayout can be null if you don't need to handle navigation state when open/close navigation drawer.
         */
        public ThemableNavigationManager(int styleId, FragmentManager fragmentManager, Toolbar toolbar,
                DrawerLayout drawerLayout) {
            super(ThemeManager.getInstance().getCurrentStyle(styleId), fragmentManager, toolbar, drawerLayout);
            mStyleId = styleId;
            mCurrentStyle = ThemeManager.getInstance().getCurrentStyle(styleId);
            ThemeManager.getInstance().registerOnThemeChangedListener(this);
        }

        @Override
        public void onThemeChanged(@Nullable ThemeManager.OnThemeChangedEvent event) {
            int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
            if (mCurrentStyle != style) {
                mCurrentStyle = style;
                NavigationDrawerDrawable drawable = new NavigationDrawerDrawable.Builder(mToolbar.getContext(),
                        mCurrentStyle).build();
                drawable.switchIconState(mNavigationIcon.getIconState(), false);
                mNavigationIcon = drawable;
                mToolbar.setNavigationIcon(mNavigationVisible ? mNavigationIcon : null);
            }
        }

    }
}