aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/cyanogenmod/filemanager/ui/widgets/ActionBarDrawerToggle.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/cyanogenmod/filemanager/ui/widgets/ActionBarDrawerToggle.java')
-rw-r--r--src/com/cyanogenmod/filemanager/ui/widgets/ActionBarDrawerToggle.java554
1 files changed, 554 insertions, 0 deletions
diff --git a/src/com/cyanogenmod/filemanager/ui/widgets/ActionBarDrawerToggle.java b/src/com/cyanogenmod/filemanager/ui/widgets/ActionBarDrawerToggle.java
new file mode 100644
index 00000000..4ef9e483
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/ui/widgets/ActionBarDrawerToggle.java
@@ -0,0 +1,554 @@
+/*
+ * Copyright (C) 2013 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.
+ *
+ *
+ * (modified from android.support.v4.app)
+ */
+
+package com.cyanogenmod.filemanager.ui.widgets;
+
+import java.lang.reflect.Method;
+
+import android.R;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LevelListDrawable;
+import android.os.Build;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+/**
+ * This class provides a handy way to tie together the functionality of
+ * {@link DrawerLayout} and the framework <code>ActionBar</code> to implement
+ * the recommended design for navigation drawers.
+ *
+ * <p>
+ * To use <code>ActionBarDrawerToggle</code>, create one in your Activity and
+ * call through to the following methods corresponding to your Activity
+ * callbacks:
+ * </p>
+ *
+ * <ul>
+ * <li>
+ * {@link Activity#onConfigurationChanged(android.content.res.Configuration)
+ * onConfigurationChanged}</li>
+ * <li>{@link Activity#onOptionsItemSelected(android.view.MenuItem)
+ * onOptionsItemSelected}</li>
+ * </ul>
+ *
+ * <p>
+ * Call {@link #syncState()} from your <code>Activity</code>'s
+ * {@link Activity#onPostCreate(android.os.Bundle) onPostCreate} to synchronize
+ * the indicator with the state of the linked DrawerLayout after
+ * <code>onRestoreInstanceState</code> has occurred.
+ * </p>
+ *
+ * <p>
+ * <code>ActionBarDrawerToggle</code> can be used directly as a
+ * {@link DrawerLayout.DrawerListener}, or if you are already providing your own
+ * listener, call through to each of the listener methods from your own.
+ * </p>
+ */
+public class ActionBarDrawerToggle implements DrawerLayout.DrawerListener {
+ private static final String TAG = "ActionBarDrawerToggle";
+
+ /**
+ * 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.
+ */
+ Delegate getDrawerToggleDelegate();
+ }
+
+ public interface Delegate {
+ /**
+ * @return Up indicator drawable as defined in the Activity's theme, or
+ * null if one is not defined.
+ */
+ Drawable getThemeUpIndicator();
+
+ /**
+ * 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, int contentDescRes);
+
+ /**
+ * Set the Action Bar's up indicator content description.
+ *
+ * @param contentDescRes
+ * - Content description to set
+ */
+ void setActionBarDescription(int contentDescRes);
+ }
+
+ private static final int[] THEME_ATTRS = new int[] { R.attr.homeAsUpIndicator };
+
+ private static class ActionBarDrawerToggleImpl {
+ public static Drawable getThemeUpIndicator(Activity activity) {
+ final TypedArray a = activity.obtainStyledAttributes(THEME_ATTRS);
+ final Drawable result = a.getDrawable(0);
+ a.recycle();
+ return result;
+ }
+
+ public static Object setActionBarUpIndicator(Object info,
+ Activity activity, Drawable drawable, int contentDescRes) {
+ if (info == null) {
+ info = new SetIndicatorInfo(activity);
+ }
+
+ final ActionBar actionBar = activity.getActionBar();
+ actionBar.setHomeAsUpIndicator(drawable);
+ actionBar.setHomeActionContentDescription(contentDescRes);
+
+ return info;
+ }
+
+ public static Object setActionBarDescription(Object info,
+ Activity activity, int contentDescRes) {
+ if (info == null) {
+ info = new SetIndicatorInfo(activity);
+ }
+
+ final ActionBar actionBar = activity.getActionBar();
+ actionBar.setHomeActionContentDescription(contentDescRes);
+
+ return info;
+ }
+ }
+
+ private static class SetIndicatorInfo {
+ public Method setHomeAsUpIndicator;
+ public Method setHomeActionContentDescription;
+ public ImageView upIndicatorView;
+
+ SetIndicatorInfo(Activity activity) {
+ try {
+ setHomeAsUpIndicator = ActionBar.class.getDeclaredMethod(
+ "setHomeAsUpIndicator", Drawable.class);
+ setHomeActionContentDescription = ActionBar.class
+ .getDeclaredMethod("setHomeActionContentDescription",
+ Integer.TYPE);
+
+ // If we got the method we won't need the stuff below.
+ return;
+ } catch (NoSuchMethodException e) {
+ // Oh well. We'll use the other mechanism below instead.
+ }
+
+ final View home = activity.findViewById(android.R.id.home);
+ if (home == null) {
+ // Action bar doesn't have a known configuration, an OEM messed
+ // with things.
+ return;
+ }
+
+ final ViewGroup parent = (ViewGroup) home.getParent();
+ final int childCount = parent.getChildCount();
+ if (childCount != 2) {
+ // No idea which one will be the right one, an OEM messed with
+ // things.
+ return;
+ }
+
+ final View first = parent.getChildAt(0);
+ final View second = parent.getChildAt(1);
+ final View up = first.getId() == android.R.id.home ? second : first;
+
+ if (up instanceof ImageView) {
+ // Jackpot! (Probably...)
+ upIndicatorView = (ImageView) up;
+ }
+ }
+ }
+
+ private static final ActionBarDrawerToggleImpl IMPL = new ActionBarDrawerToggleImpl();
+
+ /** Fraction of its total width by which to offset the toggle drawable. */
+ private static final float TOGGLE_DRAWABLE_OFFSET = 1 / 3f;
+
+ // android.R.id.home as defined by public API in v11
+ private static final int ID_HOME = 0x0102002c;
+
+ private final Activity mActivity;
+ private final Delegate mActivityImpl;
+ private final DrawerLayout mDrawerLayout;
+ private boolean mDrawerIndicatorEnabled = true;
+
+ private Drawable mThemeImage;
+ private Drawable mDrawerImage;
+ private SlideDrawable mSlider;
+ private int mDrawerImageResource;
+ private final int mOpenDrawerContentDescRes;
+ private final int mCloseDrawerContentDescRes;
+
+ private Object mSetIndicatorInfo;
+
+ /**
+ * Construct a new ActionBarDrawerToggle.
+ *
+ * <p>
+ * The given {@link Activity} will be linked to the specified
+ * {@link DrawerLayout}. The provided drawer indicator drawable will animate
+ * slightly off-screen as the drawer is opened, indicating that in the open
+ * state the drawer will move off-screen when pressed and in the closed
+ * state the drawer will move on-screen when pressed.
+ * </p>
+ *
+ * <p>
+ * String resources must be provided to describe the open/close drawer
+ * actions for accessibility services.
+ * </p>
+ *
+ * @param activity
+ * The Activity hosting the drawer
+ * @param drawerLayout
+ * The DrawerLayout to link to the given Activity's ActionBar
+ * @param drawerImageRes
+ * A Drawable resource to use as the drawer indicator
+ * @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,
+ int drawerImageRes, int openDrawerContentDescRes,
+ int closeDrawerContentDescRes) {
+ mActivity = activity;
+
+ // Allow the Activity to provide an impl
+ if (activity instanceof DelegateProvider) {
+ mActivityImpl = ((DelegateProvider) activity)
+ .getDrawerToggleDelegate();
+ } else {
+ mActivityImpl = null;
+ }
+
+ mDrawerLayout = drawerLayout;
+ mDrawerImageResource = drawerImageRes;
+ mOpenDrawerContentDescRes = openDrawerContentDescRes;
+ mCloseDrawerContentDescRes = closeDrawerContentDescRes;
+
+ mThemeImage = getThemeUpIndicator();
+ mDrawerImage = activity.getResources().getDrawable(drawerImageRes);
+ mSlider = new SlideDrawable(mDrawerImage);
+ mSlider.setOffset(TOGGLE_DRAWABLE_OFFSET);
+ }
+
+ /**
+ * Synchronize the state of the drawer indicator/affordance with the linked
+ * DrawerLayout.
+ *
+ * <p>
+ * This should be called from your <code>Activity</code>'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.)
+ * </p>
+ */
+ public void syncState() {
+ if (mDrawerLayout.isDrawerOpen(Gravity.START)) {
+ mSlider.setPosition(1);
+ } else {
+ mSlider.setPosition(0);
+ }
+
+ if (mDrawerIndicatorEnabled) {
+ setActionBarUpIndicator(
+ mSlider,
+ mDrawerLayout.isDrawerOpen(Gravity.START) ? mCloseDrawerContentDescRes
+ : mOpenDrawerContentDescRes);
+ }
+ }
+
+ /**
+ * Enable or disable the drawer indicator. The indicator defaults to
+ * enabled.
+ *
+ * <p>
+ * When the indicator is disabled, the <code>ActionBar</code> will revert to
+ * displaying the home-as-up indicator provided by the <code>Activity</code>
+ * 's theme in the <code>android.R.attr.homeAsUpIndicator</code> attribute
+ * instead of the animated drawer glyph.
+ * </p>
+ *
+ * @param enable
+ * true to enable, false to disable
+ */
+ public void setDrawerIndicatorEnabled(boolean enable) {
+ if (enable != mDrawerIndicatorEnabled) {
+ if (enable) {
+ setActionBarUpIndicator(
+ mSlider,
+ mDrawerLayout.isDrawerOpen(Gravity.START) ? mCloseDrawerContentDescRes
+ : mOpenDrawerContentDescRes);
+ } else {
+ setActionBarUpIndicator(mThemeImage, 0);
+ }
+ mDrawerIndicatorEnabled = enable;
+ }
+ }
+
+ /**
+ * @return true if the enhanced drawer indicator is enabled, false otherwise
+ * @see #setDrawerIndicatorEnabled(boolean)
+ */
+ public boolean isDrawerIndicatorEnabled() {
+ return mDrawerIndicatorEnabled;
+ }
+
+ /**
+ * This method replaces the drawer image resource with a new one.
+ *
+ * @param newDrawerImageRes
+ * The new resource id
+ */
+ public void setDrawerImageResource(int newDrawerImageRes) {
+ mDrawerImageResource = newDrawerImageRes;
+ mDrawerImage = mActivity.getResources().getDrawable(
+ mDrawerImageResource);
+ mSlider = new SlideDrawable(mDrawerImage);
+ mSlider.setOffset(TOGGLE_DRAWABLE_OFFSET);
+ syncState();
+ }
+
+ /**
+ * This method should always be called by your <code>Activity</code>'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
+ mThemeImage = getThemeUpIndicator();
+ mDrawerImage = mActivity.getResources().getDrawable(
+ mDrawerImageResource);
+ syncState();
+ }
+
+ /**
+ * This method should be called by your <code>Activity</code>'s
+ * {@link Activity#onOptionsItemSelected(android.view.MenuItem)
+ * onOptionsItemSelected} method. If it returns true, your
+ * <code>onOptionsItemSelected</code> 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() == ID_HOME
+ && mDrawerIndicatorEnabled) {
+ if (mDrawerLayout.isDrawerVisible(Gravity.START)) {
+ mDrawerLayout.closeDrawer(Gravity.START);
+ } else {
+ mDrawerLayout.openDrawer(Gravity.START);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@link DrawerLayout.DrawerListener} callback method. If you do not use
+ * your ActionBarDrawerToggle instance directly as your DrawerLayout's
+ * listener, you should call through to this method from your own listener
+ * object.
+ *
+ * @param drawerView
+ * The child view that was moved
+ * @param slideOffset
+ * The new offset of this drawer within its range, from 0-1
+ */
+ @Override
+ public void onDrawerSlide(View drawerView, float slideOffset) {
+ float glyphOffset = mSlider.getPosition();
+ if (slideOffset > 0.5f) {
+ glyphOffset = Math.max(glyphOffset,
+ Math.max(0.f, slideOffset - 0.5f) * 2);
+ } else {
+ glyphOffset = Math.min(glyphOffset, slideOffset * 2);
+ }
+ mSlider.setPosition(glyphOffset);
+ }
+
+ /**
+ * {@link DrawerLayout.DrawerListener} callback method. If you do not use
+ * your ActionBarDrawerToggle instance directly as your DrawerLayout's
+ * listener, you should call through to this method from your own listener
+ * object.
+ *
+ * @param drawerView
+ * Drawer view that is now open
+ */
+ @Override
+ public void onDrawerOpened(View drawerView) {
+ mSlider.setPosition(1);
+ if (mDrawerIndicatorEnabled) {
+ setActionBarDescription(mCloseDrawerContentDescRes);
+ }
+ }
+
+ /**
+ * {@link DrawerLayout.DrawerListener} callback method. If you do not use
+ * your ActionBarDrawerToggle instance directly as your DrawerLayout's
+ * listener, you should call through to this method from your own listener
+ * object.
+ *
+ * @param drawerView
+ * Drawer view that is now closed
+ */
+ @Override
+ public void onDrawerClosed(View drawerView) {
+ mSlider.setPosition(0);
+ if (mDrawerIndicatorEnabled) {
+ setActionBarDescription(mOpenDrawerContentDescRes);
+ }
+ }
+
+ /**
+ * {@link DrawerLayout.DrawerListener} callback method. If you do not use
+ * your ActionBarDrawerToggle instance directly as your DrawerLayout's
+ * listener, you should call through to this method from your own listener
+ * object.
+ *
+ * @param newState
+ * The new drawer motion state
+ */
+ @Override
+ public void onDrawerStateChanged(int newState) {
+ }
+
+ Drawable getThemeUpIndicator() {
+ if (mActivityImpl != null) {
+ return mActivityImpl.getThemeUpIndicator();
+ }
+ return IMPL.getThemeUpIndicator(mActivity);
+ }
+
+ void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
+ if (mActivityImpl != null) {
+ mActivityImpl.setActionBarUpIndicator(upDrawable, contentDescRes);
+ return;
+ }
+ mSetIndicatorInfo = IMPL.setActionBarUpIndicator(mSetIndicatorInfo,
+ mActivity, upDrawable, contentDescRes);
+ }
+
+ void setActionBarDescription(int contentDescRes) {
+ if (mActivityImpl != null) {
+ mActivityImpl.setActionBarDescription(contentDescRes);
+ return;
+ }
+ mSetIndicatorInfo = IMPL.setActionBarDescription(mSetIndicatorInfo,
+ mActivity, contentDescRes);
+ }
+
+ private class SlideDrawable extends LevelListDrawable implements
+ Drawable.Callback {
+ private final boolean mHasMirroring = Build.VERSION.SDK_INT > 18;
+ private final Rect mTmpRect = new Rect();
+
+ private float mPosition;
+ private float mOffset;
+
+ private SlideDrawable(Drawable wrapped) {
+ super();
+
+ if (wrapped.isAutoMirrored()) {
+ this.setAutoMirrored(true);
+ }
+
+ addLevel(0, 0, wrapped);
+ }
+
+ /**
+ * Sets the current position along the offset.
+ *
+ * @param position
+ * a value between 0 and 1
+ */
+ public void setPosition(float position) {
+ mPosition = position;
+ invalidateSelf();
+ }
+
+ public float getPosition() {
+ return mPosition;
+ }
+
+ /**
+ * Specifies the maximum offset when the position is at 1.
+ *
+ * @param offset
+ * maximum offset as a fraction of the drawable width,
+ * positive to shift left or negative to shift right.
+ * @see #setPosition(float)
+ */
+ public void setOffset(float offset) {
+ mOffset = offset;
+ invalidateSelf();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ copyBounds(mTmpRect);
+ canvas.save();
+
+ // Layout direction must be obtained from the activity.
+ final boolean isLayoutRTL = mActivity.getWindow().getDecorView()
+ .getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ final int flipRtl = isLayoutRTL ? -1 : 1;
+ final int width = mTmpRect.width();
+ canvas.translate(-mOffset * width * mPosition * flipRtl, 0);
+
+ // Force auto-mirroring if it's not supported by the platform.
+ if (isLayoutRTL && !mHasMirroring) {
+ canvas.translate(width, 0);
+ canvas.scale(-1, 1);
+ }
+
+ super.draw(canvas);
+ canvas.restore();
+ }
+ }
+}