/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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 android.support.v7.app;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.graphics.drawable.DrawerArrowDrawable;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
/**
* This class provides a handy way to tie together the functionality of
* {@link android.support.v4.widget.DrawerLayout} and the framework ActionBar
to
* implement the recommended design for navigation drawers.
*
*
To use ActionBarDrawerToggle
, create one in your Activity and call through
* to the following methods corresponding to your Activity callbacks:
Call {@link #syncState()} from your Activity
's
* {@link android.app.Activity#onPostCreate(android.os.Bundle) onPostCreate} to synchronize the
* indicator with the state of the linked DrawerLayout after onRestoreInstanceState
* has occurred.
ActionBarDrawerToggle
can be used directly as a
* {@link android.support.v4.widget.DrawerLayout.DrawerListener}, or if you are already providing
* your own listener, call through to each of the listener methods from your own.
* You can customize the the animated toggle by defining the * {@link android.support.v7.appcompat.R.styleable#DrawerArrowToggle drawerArrowStyle} in your * ActionBar theme. */ public class ActionBarDrawerToggle implements DrawerLayout.DrawerListener { /** * Allows an implementing Activity to return an {@link ActionBarDrawerToggle.Delegate} to use * with ActionBarDrawerToggle. */ public interface DelegateProvider { /** * @return Delegate to use for ActionBarDrawableToggles, or null if the Activity * does not wish to override the default behavior. */ @Nullable Delegate getDrawerToggleDelegate(); } public interface Delegate { /** * Set the Action Bar's up indicator drawable and content description. * * @param upDrawable - Drawable to set as up indicator * @param contentDescRes - Content description to set */ void setActionBarUpIndicator(Drawable upDrawable, @StringRes int contentDescRes); /** * Set the Action Bar's up indicator content description. * * @param contentDescRes - Content description to set */ void setActionBarDescription(@StringRes int contentDescRes); /** * Returns the drawable to be set as up button when DrawerToggle is disabled */ Drawable getThemeUpIndicator(); /** * Returns the context of ActionBar */ Context getActionBarThemedContext(); /** * Returns whether navigation icon is visible or not. * Used to print warning messages in case developer forgets to set displayHomeAsUp to true */ boolean isNavigationVisible(); } private final Delegate mActivityImpl; private final DrawerLayout mDrawerLayout; private DrawerArrowDrawable mSlider; private Drawable mHomeAsUpIndicator; boolean mDrawerIndicatorEnabled = true; private boolean mHasCustomUpIndicator; private final int mOpenDrawerContentDescRes; private final int mCloseDrawerContentDescRes; // used in toolbar mode when DrawerToggle is disabled View.OnClickListener mToolbarNavigationClickListener; // If developer does not set displayHomeAsUp, DrawerToggle won't show up. // DrawerToggle logs a warning if this case is detected private boolean mWarnedForDisplayHomeAsUp = false; /** * Construct a new ActionBarDrawerToggle. * *
The given {@link Activity} will be linked to the specified {@link DrawerLayout} and * its Actionbar's Up button will be set to a custom drawable. *
This drawable shows a Hamburger icon when drawer is closed and an arrow when drawer * is open. It animates between these two states as the drawer opens.
* *String resources must be provided to describe the open/close drawer actions for * accessibility services.
* * @param activity The Activity hosting the drawer. Should have an ActionBar. * @param drawerLayout The DrawerLayout to link to the given Activity's ActionBar * @param openDrawerContentDescRes A String resource to describe the "open drawer" action * for accessibility * @param closeDrawerContentDescRes A String resource to describe the "close drawer" action * for accessibility */ public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, @StringRes int openDrawerContentDescRes, @StringRes int closeDrawerContentDescRes) { this(activity, null, drawerLayout, null, openDrawerContentDescRes, closeDrawerContentDescRes); } /** * Construct a new ActionBarDrawerToggle with a Toolbar. ** The given {@link Activity} will be linked to the specified {@link DrawerLayout} and * the Toolbar's navigation icon will be set to a custom drawable. Using this constructor * will set Toolbar's navigation click listener to toggle the drawer when it is clicked. *
* This drawable shows a Hamburger icon when drawer is closed and an arrow when drawer * is open. It animates between these two states as the drawer opens. *
* String resources must be provided to describe the open/close drawer actions for * accessibility services. *
* Please use {@link #ActionBarDrawerToggle(Activity, DrawerLayout, int, int)} if you are * setting the Toolbar as the ActionBar of your activity. * * @param activity The Activity hosting the drawer. * @param toolbar The toolbar to use if you have an independent Toolbar. * @param drawerLayout The DrawerLayout to link to the given Activity's ActionBar * @param openDrawerContentDescRes A String resource to describe the "open drawer" action * for accessibility * @param closeDrawerContentDescRes A String resource to describe the "close drawer" action * for accessibility */ public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, Toolbar toolbar, @StringRes int openDrawerContentDescRes, @StringRes int closeDrawerContentDescRes) { this(activity, toolbar, drawerLayout, null, openDrawerContentDescRes, closeDrawerContentDescRes); } /** * In the future, we can make this constructor public if we want to let developers customize * the * animation. */ ActionBarDrawerToggle(Activity activity, Toolbar toolbar, DrawerLayout drawerLayout, DrawerArrowDrawable slider, @StringRes int openDrawerContentDescRes, @StringRes int closeDrawerContentDescRes) { if (toolbar != null) { mActivityImpl = new ToolbarCompatDelegate(toolbar); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mDrawerIndicatorEnabled) { toggle(); } else if (mToolbarNavigationClickListener != null) { mToolbarNavigationClickListener.onClick(v); } } }); } else if (activity instanceof DelegateProvider) { // Allow the Activity to provide an impl mActivityImpl = ((DelegateProvider) activity).getDrawerToggleDelegate(); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { mActivityImpl = new JellybeanMr2Delegate(activity); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { mActivityImpl = new HoneycombDelegate(activity); } else { mActivityImpl = new DummyDelegate(activity); } mDrawerLayout = drawerLayout; mOpenDrawerContentDescRes = openDrawerContentDescRes; mCloseDrawerContentDescRes = closeDrawerContentDescRes; if (slider == null) { mSlider = new DrawerArrowDrawable(mActivityImpl.getActionBarThemedContext()); } else { mSlider = slider; } mHomeAsUpIndicator = getThemeUpIndicator(); } /** * Synchronize the state of the drawer indicator/affordance with the linked DrawerLayout. * *
This should be called from your Activity
's
* {@link Activity#onPostCreate(android.os.Bundle) onPostCreate} method to synchronize after
* the DrawerLayout's instance state has been restored, and any other time when the state
* may have diverged in such a way that the ActionBarDrawerToggle was not notified.
* (For example, if you stop forwarding appropriate drawer events for a period of time.)
Activity
's
* {@link Activity#onConfigurationChanged(android.content.res.Configuration)
* onConfigurationChanged}
* method.
*
* @param newConfig The new configuration
*/
public void onConfigurationChanged(Configuration newConfig) {
// Reload drawables that can change with configuration
if (!mHasCustomUpIndicator) {
mHomeAsUpIndicator = getThemeUpIndicator();
}
syncState();
}
/**
* This method should be called by your Activity
's
* {@link Activity#onOptionsItemSelected(android.view.MenuItem) onOptionsItemSelected} method.
* If it returns true, your onOptionsItemSelected
method should return true and
* skip further processing.
*
* @param item the MenuItem instance representing the selected menu item
* @return true if the event was handled and further processing should not occur
*/
public boolean onOptionsItemSelected(MenuItem item) {
if (item != null && item.getItemId() == android.R.id.home && mDrawerIndicatorEnabled) {
toggle();
return true;
}
return false;
}
void toggle() {
int drawerLockMode = mDrawerLayout.getDrawerLockMode(GravityCompat.START);
if (mDrawerLayout.isDrawerVisible(GravityCompat.START)
&& (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_OPEN)) {
mDrawerLayout.closeDrawer(GravityCompat.START);
} else if (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {
mDrawerLayout.openDrawer(GravityCompat.START);
}
}
/**
* Set the up indicator to display when the drawer indicator is not
* enabled.
*
* If you pass null
to this method, the default drawable from
* the theme will be used.
*
* @param indicator A drawable to use for the up indicator, or null to use
* the theme's default
* @see #setDrawerIndicatorEnabled(boolean)
*/
public void setHomeAsUpIndicator(Drawable indicator) {
if (indicator == null) {
mHomeAsUpIndicator = getThemeUpIndicator();
mHasCustomUpIndicator = false;
} else {
mHomeAsUpIndicator = indicator;
mHasCustomUpIndicator = true;
}
if (!mDrawerIndicatorEnabled) {
setActionBarUpIndicator(mHomeAsUpIndicator, 0);
}
}
/**
* Set the up indicator to display when the drawer indicator is not
* enabled.
*
* If you pass 0 to this method, the default drawable from the theme will * be used. * * @param resId Resource ID of a drawable to use for the up indicator, or 0 * to use the theme's default * @see #setDrawerIndicatorEnabled(boolean) */ public void setHomeAsUpIndicator(int resId) { Drawable indicator = null; if (resId != 0) { indicator = mDrawerLayout.getResources().getDrawable(resId); } setHomeAsUpIndicator(indicator); } /** * @return true if the enhanced drawer indicator is enabled, false otherwise * @see #setDrawerIndicatorEnabled(boolean) */ public boolean isDrawerIndicatorEnabled() { return mDrawerIndicatorEnabled; } /** * Enable or disable the drawer indicator. The indicator defaults to enabled. * *
When the indicator is disabled, the ActionBar
will revert to displaying
* the home-as-up indicator provided by the Activity
's theme in the
* android.R.attr.homeAsUpIndicator
attribute instead of the animated
* drawer glyph.