();
125 |
126 | static final class TabInfo {
127 |
128 | private final Class> mClss;
129 | private final Bundle mArgs;
130 |
131 | TabInfo(Class> aClass, Bundle args) {
132 | mClss = aClass;
133 | mArgs = args;
134 | }
135 | }
136 |
137 | public PagerAdapter(FragmentActivity activity) {
138 | super(activity.getSupportFragmentManager());
139 | mContext = activity;
140 | }
141 |
142 | @Override
143 | public int getCount() {
144 | return mTabs.size();
145 | }
146 |
147 | @Override
148 | public Fragment getItem(int position) {
149 | TabInfo info = mTabs.get(position);
150 | return Fragment.instantiate(mContext, info.mClss.getName(),
151 | info.mArgs);
152 | }
153 |
154 | public void addTab(Class> clss, Bundle args) {
155 | TabInfo info = new TabInfo(clss, args);
156 | mTabs.add(info);
157 | notifyDataSetChanged();
158 | }
159 |
160 | }
161 |
162 | public static class TextViewFragment extends Fragment {
163 |
164 | @Override
165 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
166 | Bundle savedInstanceState) {
167 | FrameLayout frameLayout = new FrameLayout(getActivity());
168 | frameLayout.setLayoutParams(new FrameLayout.LayoutParams(
169 | FrameLayout.LayoutParams.MATCH_PARENT,
170 | FrameLayout.LayoutParams.MATCH_PARENT, Gravity.CENTER));
171 |
172 | TextView tv = new TextView(getActivity());
173 | tv.setText("This is an example of a Fragment in a View Pager");
174 | frameLayout.addView(tv);
175 | return frameLayout;
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/menudrawer-samples/src/net/simonvt/menudrawer/samples/WindowSample.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer.samples;
2 |
3 | import net.simonvt.menudrawer.MenuDrawer;
4 | import net.simonvt.menudrawer.Position;
5 |
6 | import android.app.Activity;
7 | import android.os.Build;
8 | import android.os.Bundle;
9 | import android.view.MenuItem;
10 | import android.view.View;
11 | import android.widget.TextView;
12 |
13 | public class WindowSample extends Activity implements View.OnClickListener {
14 |
15 | private static final String STATE_MENUDRAWER = "net.simonvt.menudrawer.samples.WindowSample.menuDrawer";
16 | private static final String STATE_ACTIVE_VIEW_ID = "net.simonvt.menudrawer.samples.WindowSample.activeViewId";
17 |
18 | private MenuDrawer mMenuDrawer;
19 | private TextView mContentTextView;
20 |
21 | private int mActiveViewId;
22 |
23 | @Override
24 | public void onCreate(Bundle inState) {
25 | super.onCreate(inState);
26 | if (inState != null) {
27 | mActiveViewId = inState.getInt(STATE_ACTIVE_VIEW_ID);
28 | }
29 |
30 | mMenuDrawer = MenuDrawer.attach(this, MenuDrawer.Type.BEHIND, Position.LEFT, MenuDrawer.MENU_DRAG_WINDOW);
31 | mMenuDrawer.setContentView(R.layout.activity_windowsample);
32 | mMenuDrawer.setMenuView(R.layout.menu_scrollview);
33 |
34 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
35 | getActionBar().setDisplayHomeAsUpEnabled(true);
36 | }
37 |
38 | mContentTextView = (TextView) findViewById(R.id.contentText);
39 |
40 | findViewById(R.id.item1).setOnClickListener(this);
41 | findViewById(R.id.item2).setOnClickListener(this);
42 | findViewById(R.id.item3).setOnClickListener(this);
43 | findViewById(R.id.item4).setOnClickListener(this);
44 | findViewById(R.id.item5).setOnClickListener(this);
45 | findViewById(R.id.item6).setOnClickListener(this);
46 |
47 | TextView activeView = (TextView) findViewById(mActiveViewId);
48 | if (activeView != null) {
49 | mMenuDrawer.setActiveView(activeView);
50 | mContentTextView.setText("Active item: " + activeView.getText());
51 | }
52 |
53 | // This will animate the drawer open and closed until the user manually drags it. Usually this would only be
54 | // called on first launch.
55 | mMenuDrawer.peekDrawer();
56 | }
57 |
58 | @Override
59 | protected void onRestoreInstanceState(Bundle inState) {
60 | super.onRestoreInstanceState(inState);
61 | mMenuDrawer.restoreState(inState.getParcelable(STATE_MENUDRAWER));
62 | }
63 |
64 | @Override
65 | protected void onSaveInstanceState(Bundle outState) {
66 | super.onSaveInstanceState(outState);
67 | outState.putParcelable(STATE_MENUDRAWER, mMenuDrawer.saveState());
68 | outState.putInt(STATE_ACTIVE_VIEW_ID, mActiveViewId);
69 | }
70 |
71 | @Override
72 | public boolean onOptionsItemSelected(MenuItem item) {
73 | switch (item.getItemId()) {
74 | case android.R.id.home:
75 | mMenuDrawer.toggleMenu();
76 | return true;
77 | }
78 |
79 | return super.onOptionsItemSelected(item);
80 | }
81 |
82 | @Override
83 | public void onBackPressed() {
84 | final int drawerState = mMenuDrawer.getDrawerState();
85 | if (drawerState == MenuDrawer.STATE_OPEN || drawerState == MenuDrawer.STATE_OPENING) {
86 | mMenuDrawer.closeMenu();
87 | return;
88 | }
89 |
90 | super.onBackPressed();
91 | }
92 |
93 | @Override
94 | public void onClick(View v) {
95 | mMenuDrawer.setActiveView(v);
96 | mContentTextView.setText("Active item: " + ((TextView) v).getText());
97 | mMenuDrawer.closeMenu();
98 | mActiveViewId = v.getId();
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/menudrawer/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/menudrawer/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'android-library'
2 | apply plugin: 'maven'
3 | apply plugin: 'signing'
4 | apply plugin: 'checkstyle'
5 |
6 | android {
7 | sourceSets {
8 | main {
9 | manifest.srcFile 'AndroidManifest.xml'
10 | java.srcDirs = ['src']
11 | res.srcDirs = ['res']
12 | }
13 | }
14 |
15 | compileSdkVersion parent.ext.compileSdkVersion
16 | buildToolsVersion parent.ext.buildToolsVersion
17 |
18 | defaultConfig {
19 | minSdkVersion parent.ext.minSdkVersion
20 | targetSdkVersion parent.ext.targetSdkVersion
21 | }
22 | }
23 |
24 | checkstyle {
25 | configFile project.file('../checkstyle.xml')
26 | showViolations true
27 | }
28 |
29 | android.libraryVariants.all { variant ->
30 | def name = variant.buildType.name
31 |
32 | def checkstyle = project.tasks.create "checkstyle${name.capitalize()}", Checkstyle
33 | checkstyle.dependsOn variant.javaCompile
34 | checkstyle.source variant.javaCompile.source
35 | checkstyle.classpath = project.fileTree(variant.javaCompile.destinationDir)
36 | checkstyle.exclude('**/BuildConfig.java')
37 | checkstyle.exclude('**/R.java')
38 | project.tasks.getByName("check").dependsOn checkstyle
39 | }
40 |
41 | apply from: 'https://raw.github.com/SimonVT/gradle-mvn-push/0.11/gradle-mvn-push.gradle'
42 |
--------------------------------------------------------------------------------
/menudrawer/gradle.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2014 Simon Vig Therkildsen
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | POM_NAME=MenuDrawer
18 | POM_ARTIFACT_ID=menudrawer
19 | POM_PACKAGING=aar
--------------------------------------------------------------------------------
/menudrawer/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/menudrawer/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #FF555555
5 |
6 |
7 |
--------------------------------------------------------------------------------
/menudrawer/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/menudrawer/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Close drawer
5 |
6 | Open drawer
7 |
8 |
9 |
--------------------------------------------------------------------------------
/menudrawer/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/BuildLayerFrameLayout.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.util.AttributeSet;
6 | import android.widget.FrameLayout;
7 |
8 | /**
9 | * FrameLayout which caches the hardware layer if available.
10 | *
11 | * If it's not posted twice the layer either wont be built on start, or it'll be built twice.
12 | */
13 | class BuildLayerFrameLayout extends FrameLayout {
14 |
15 | private boolean mChanged;
16 |
17 | private boolean mHardwareLayersEnabled = true;
18 |
19 | private boolean mAttached;
20 |
21 | private boolean mFirst = true;
22 |
23 | public BuildLayerFrameLayout(Context context) {
24 | super(context);
25 | if (MenuDrawer.USE_TRANSLATIONS) {
26 | setLayerType(LAYER_TYPE_HARDWARE, null);
27 | }
28 | }
29 |
30 | public BuildLayerFrameLayout(Context context, AttributeSet attrs) {
31 | super(context, attrs);
32 | if (MenuDrawer.USE_TRANSLATIONS) {
33 | setLayerType(LAYER_TYPE_HARDWARE, null);
34 | }
35 | }
36 |
37 | public BuildLayerFrameLayout(Context context, AttributeSet attrs, int defStyle) {
38 | super(context, attrs, defStyle);
39 | if (MenuDrawer.USE_TRANSLATIONS) {
40 | setLayerType(LAYER_TYPE_HARDWARE, null);
41 | }
42 | }
43 |
44 | void setHardwareLayersEnabled(boolean enabled) {
45 | mHardwareLayersEnabled = enabled;
46 | }
47 |
48 | @Override
49 | protected void onAttachedToWindow() {
50 | super.onAttachedToWindow();
51 | mAttached = true;
52 | }
53 |
54 | @Override
55 | protected void onDetachedFromWindow() {
56 | super.onDetachedFromWindow();
57 | mAttached = false;
58 | }
59 |
60 | @Override
61 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
62 | super.onSizeChanged(w, h, oldw, oldh);
63 |
64 | if (MenuDrawer.USE_TRANSLATIONS && mHardwareLayersEnabled) {
65 | post(new Runnable() {
66 | @Override
67 | public void run() {
68 | mChanged = true;
69 | invalidate();
70 | }
71 | });
72 | }
73 | }
74 |
75 | @Override
76 | protected void dispatchDraw(Canvas canvas) {
77 | super.dispatchDraw(canvas);
78 |
79 | if (mChanged && MenuDrawer.USE_TRANSLATIONS) {
80 | post(new Runnable() {
81 | @Override
82 | public void run() {
83 | if (mAttached) {
84 | final int layerType = getLayerType();
85 | // If it's already a hardware layer, it'll be built anyway.
86 | if (layerType != LAYER_TYPE_HARDWARE || mFirst) {
87 | mFirst = false;
88 | setLayerType(LAYER_TYPE_HARDWARE, null);
89 | buildLayer();
90 | setLayerType(LAYER_TYPE_NONE, null);
91 | }
92 | }
93 | }
94 | });
95 |
96 | mChanged = false;
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/ColorDrawable.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2008 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.simonvt.menudrawer;
18 |
19 | import android.content.res.Resources;
20 | import android.graphics.Canvas;
21 | import android.graphics.ColorFilter;
22 | import android.graphics.Paint;
23 | import android.graphics.PixelFormat;
24 | import android.graphics.drawable.Drawable;
25 |
26 | /**
27 | * A specialized Drawable that fills the Canvas with a specified color.
28 | * Note that a ColorDrawable ignores the ColorFilter.
29 | *
30 | * It can be defined in an XML file with the <color>
element.
31 | *
32 | * @attr ref android.R.styleable#ColorDrawable_color
33 | */
34 | class ColorDrawable extends Drawable {
35 |
36 | private ColorState mState;
37 | private final Paint mPaint = new Paint();
38 |
39 | /** Creates a new black ColorDrawable. */
40 | public ColorDrawable() {
41 | this(null);
42 | }
43 |
44 | /**
45 | * Creates a new ColorDrawable with the specified color.
46 | *
47 | * @param color The color to draw.
48 | */
49 | public ColorDrawable(int color) {
50 | this(null);
51 | setColor(color);
52 | }
53 |
54 | private ColorDrawable(ColorState state) {
55 | mState = new ColorState(state);
56 | }
57 |
58 | @Override
59 | public int getChangingConfigurations() {
60 | return super.getChangingConfigurations() | mState.mChangingConfigurations;
61 | }
62 |
63 | @Override
64 | public void draw(Canvas canvas) {
65 | if ((mState.mUseColor >>> 24) != 0) {
66 | mPaint.setColor(mState.mUseColor);
67 | canvas.drawRect(getBounds(), mPaint);
68 | }
69 | }
70 |
71 | /**
72 | * Gets the drawable's color value.
73 | *
74 | * @return int The color to draw.
75 | */
76 | public int getColor() {
77 | return mState.mUseColor;
78 | }
79 |
80 | /**
81 | * Sets the drawable's color value. This action will clobber the results of prior calls to
82 | * {@link #setAlpha(int)} on this object, which side-affected the underlying color.
83 | *
84 | * @param color The color to draw.
85 | */
86 | public void setColor(int color) {
87 | if (mState.mBaseColor != color || mState.mUseColor != color) {
88 | invalidateSelf();
89 | mState.mBaseColor = mState.mUseColor = color;
90 | }
91 | }
92 |
93 | /**
94 | * Returns the alpha value of this drawable's color.
95 | *
96 | * @return A value between 0 and 255.
97 | */
98 | public int getAlpha() {
99 | return mState.mUseColor >>> 24;
100 | }
101 |
102 | /**
103 | * Sets the color's alpha value.
104 | *
105 | * @param alpha The alpha value to set, between 0 and 255.
106 | */
107 | public void setAlpha(int alpha) {
108 | alpha += alpha >> 7; // make it 0..256
109 | int baseAlpha = mState.mBaseColor >>> 24;
110 | int useAlpha = baseAlpha * alpha >> 8;
111 | int oldUseColor = mState.mUseColor;
112 | mState.mUseColor = (mState.mBaseColor << 8 >>> 8) | (useAlpha << 24);
113 | if (oldUseColor != mState.mUseColor) {
114 | invalidateSelf();
115 | }
116 | }
117 |
118 | /**
119 | * Setting a color filter on a ColorDrawable has no effect.
120 | *
121 | * @param colorFilter Ignore.
122 | */
123 | public void setColorFilter(ColorFilter colorFilter) {
124 | }
125 |
126 | public int getOpacity() {
127 | switch (mState.mUseColor >>> 24) {
128 | case 255:
129 | return PixelFormat.OPAQUE;
130 | case 0:
131 | return PixelFormat.TRANSPARENT;
132 | }
133 | return PixelFormat.TRANSLUCENT;
134 | }
135 |
136 | @Override
137 | public ConstantState getConstantState() {
138 | mState.mChangingConfigurations = getChangingConfigurations();
139 | return mState;
140 | }
141 |
142 | static final class ColorState extends ConstantState {
143 |
144 | int mBaseColor; // base color, independent of setAlpha()
145 | int mUseColor; // basecolor modulated by setAlpha()
146 | int mChangingConfigurations;
147 |
148 | ColorState(ColorState state) {
149 | if (state != null) {
150 | mBaseColor = state.mBaseColor;
151 | mUseColor = state.mUseColor;
152 | }
153 | }
154 |
155 | @Override
156 | public Drawable newDrawable() {
157 | return new ColorDrawable(this);
158 | }
159 |
160 | @Override
161 | public Drawable newDrawable(Resources res) {
162 | return new ColorDrawable(this);
163 | }
164 |
165 | @Override
166 | public int getChangingConfigurations() {
167 | return mChangingConfigurations;
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/FloatScroller.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2006 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.simonvt.menudrawer;
18 |
19 | import android.view.animation.AnimationUtils;
20 | import android.view.animation.Interpolator;
21 |
22 | /**
23 | * This class encapsulates scrolling. The duration of the scroll
24 | * can be passed in the constructor and specifies the maximum time that
25 | * the scrolling animation should take. Past this time, the scrolling is
26 | * automatically moved to its final stage and computeScrollOffset()
27 | * will always return false to indicate that scrolling is over.
28 | */
29 | class FloatScroller {
30 |
31 | private float mStart;
32 | private float mFinal;
33 |
34 | private float mCurr;
35 | private long mStartTime;
36 | private int mDuration;
37 | private float mDurationReciprocal;
38 | private float mDeltaX;
39 | private boolean mFinished;
40 | private Interpolator mInterpolator;
41 |
42 | /**
43 | * Create a Scroller with the specified interpolator. If the interpolator is
44 | * null, the default (viscous) interpolator will be used. Specify whether or
45 | * not to support progressive "flywheel" behavior in flinging.
46 | */
47 | public FloatScroller(Interpolator interpolator) {
48 | mFinished = true;
49 | mInterpolator = interpolator;
50 | }
51 |
52 | /**
53 | * Returns whether the scroller has finished scrolling.
54 | *
55 | * @return True if the scroller has finished scrolling, false otherwise.
56 | */
57 | public final boolean isFinished() {
58 | return mFinished;
59 | }
60 |
61 | /**
62 | * Force the finished field to a particular value.
63 | *
64 | * @param finished The new finished value.
65 | */
66 | public final void forceFinished(boolean finished) {
67 | mFinished = finished;
68 | }
69 |
70 | /**
71 | * Returns how long the scroll event will take, in milliseconds.
72 | *
73 | * @return The duration of the scroll in milliseconds.
74 | */
75 | public final int getDuration() {
76 | return mDuration;
77 | }
78 |
79 | /**
80 | * Returns the current offset in the scroll.
81 | *
82 | * @return The new offset as an absolute distance from the origin.
83 | */
84 | public final float getCurr() {
85 | return mCurr;
86 | }
87 |
88 | /**
89 | * Returns the start offset in the scroll.
90 | *
91 | * @return The start offset as an absolute distance from the origin.
92 | */
93 | public final float getStart() {
94 | return mStart;
95 | }
96 |
97 | /**
98 | * Returns where the scroll will end. Valid only for "fling" scrolls.
99 | *
100 | * @return The final offset as an absolute distance from the origin.
101 | */
102 | public final float getFinal() {
103 | return mFinal;
104 | }
105 |
106 | public boolean computeScrollOffset() {
107 | if (mFinished) {
108 | return false;
109 | }
110 |
111 | int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
112 |
113 | if (timePassed < mDuration) {
114 | float x = timePassed * mDurationReciprocal;
115 | x = mInterpolator.getInterpolation(x);
116 | mCurr = mStart + x * mDeltaX;
117 |
118 | } else {
119 | mCurr = mFinal;
120 | mFinished = true;
121 | }
122 | return true;
123 | }
124 |
125 | public void startScroll(float start, float delta, int duration) {
126 | mFinished = false;
127 | mDuration = duration;
128 | mStartTime = AnimationUtils.currentAnimationTimeMillis();
129 | mStart = start;
130 | mFinal = start + delta;
131 | mDeltaX = delta;
132 | mDurationReciprocal = 1.0f / (float) mDuration;
133 | }
134 |
135 | /**
136 | * Stops the animation. Contrary to {@link #forceFinished(boolean)},
137 | * aborting the animating cause the scroller to move to the final x and y
138 | * position
139 | *
140 | * @see #forceFinished(boolean)
141 | */
142 | public void abortAnimation() {
143 | mCurr = mFinal;
144 | mFinished = true;
145 | }
146 |
147 | /**
148 | * Extend the scroll animation. This allows a running animation to scroll
149 | * further and longer, when used with {@link #setFinal(float)}.
150 | *
151 | * @param extend Additional time to scroll in milliseconds.
152 | * @see #setFinal(float)
153 | */
154 | public void extendDuration(int extend) {
155 | int passed = timePassed();
156 | mDuration = passed + extend;
157 | mDurationReciprocal = 1.0f / mDuration;
158 | mFinished = false;
159 | }
160 |
161 | /**
162 | * Returns the time elapsed since the beginning of the scrolling.
163 | *
164 | * @return The elapsed time in milliseconds.
165 | */
166 | public int timePassed() {
167 | return (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
168 | }
169 |
170 | public void setFinal(float newVal) {
171 | mFinal = newVal;
172 | mDeltaX = mFinal - mStart;
173 | mFinished = false;
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/NoClickThroughFrameLayout.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.MotionEvent;
6 |
7 | /**
8 | * FrameLayout which doesn't let touch events propagate to views positioned behind it in the view hierarchy.
9 | */
10 | class NoClickThroughFrameLayout extends BuildLayerFrameLayout {
11 |
12 | public NoClickThroughFrameLayout(Context context) {
13 | super(context);
14 | }
15 |
16 | public NoClickThroughFrameLayout(Context context, AttributeSet attrs) {
17 | super(context, attrs);
18 | }
19 |
20 | public NoClickThroughFrameLayout(Context context, AttributeSet attrs, int defStyle) {
21 | super(context, attrs, defStyle);
22 | }
23 |
24 | @Override
25 | public boolean onTouchEvent(MotionEvent event) {
26 | return true;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/PeekInterpolator.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer;
2 |
3 | import android.view.animation.Interpolator;
4 |
5 | class PeekInterpolator implements Interpolator {
6 |
7 | private static final String TAG = "PeekInterpolator";
8 |
9 | private static final SinusoidalInterpolator SINUSOIDAL_INTERPOLATOR = new SinusoidalInterpolator();
10 |
11 | @Override
12 | public float getInterpolation(float input) {
13 | float result;
14 |
15 | if (input < 1.f / 3.f) {
16 | result = SINUSOIDAL_INTERPOLATOR.getInterpolation(input * 3);
17 |
18 | } else if (input > 2.f / 3.f) {
19 | final float val = ((input + 1.f / 3.f) - 1.f) * 3.f;
20 | result = 1.f - SINUSOIDAL_INTERPOLATOR.getInterpolation(val);
21 |
22 | } else {
23 | result = 1.f;
24 | }
25 |
26 | return result;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/Position.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer;
2 |
3 | import android.util.SparseArray;
4 |
5 | /**
6 | * Enums used for positioning the drawer.
7 | */
8 | public enum Position {
9 | // Positions the drawer to the left of the content.
10 | LEFT(0),
11 |
12 | // Positions the drawer above the content.
13 | TOP(1),
14 |
15 | // Positions the drawer to the right of the content.
16 | RIGHT(2),
17 |
18 | // Positions the drawer below the content.
19 | BOTTOM(3),
20 |
21 | /**
22 | * Position the drawer at the start edge. This will position the drawer to the {@link #LEFT} with LTR languages and
23 | * {@link #RIGHT} with RTL languages.
24 | */
25 | START(4),
26 |
27 | /**
28 | * Position the drawer at the end edge. This will position the drawer to the {@link #RIGHT} with LTR languages and
29 | * {@link #LEFT} with RTL languages.
30 | */
31 | END(5);
32 |
33 | final int mValue;
34 |
35 | Position(int value) {
36 | mValue = value;
37 | }
38 |
39 | private static final SparseArray STRING_MAPPING = new SparseArray();
40 |
41 | static {
42 | for (Position via : Position.values()) {
43 | STRING_MAPPING.put(via.mValue, via);
44 | }
45 | }
46 |
47 | public static Position fromValue(int value) {
48 | return STRING_MAPPING.get(value);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/Scroller.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2006 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.simonvt.menudrawer;
18 |
19 | import android.content.Context;
20 | import android.hardware.SensorManager;
21 | import android.os.Build;
22 | import android.util.FloatMath;
23 | import android.view.ViewConfiguration;
24 | import android.view.animation.AnimationUtils;
25 | import android.view.animation.Interpolator;
26 |
27 |
28 | /**
29 | * This class encapsulates scrolling. The duration of the scroll
30 | * can be passed in the constructor and specifies the maximum time that
31 | * the scrolling animation should take. Past this time, the scrolling is
32 | * automatically moved to its final stage and computeScrollOffset()
33 | * will always return false to indicate that scrolling is over.
34 | */
35 | class Scroller {
36 | private int mMode;
37 |
38 | private int mStartX;
39 | private int mStartY;
40 | private int mFinalX;
41 | private int mFinalY;
42 |
43 | private int mMinX;
44 | private int mMaxX;
45 | private int mMinY;
46 | private int mMaxY;
47 |
48 | private int mCurrX;
49 | private int mCurrY;
50 | private long mStartTime;
51 | private int mDuration;
52 | private float mDurationReciprocal;
53 | private float mDeltaX;
54 | private float mDeltaY;
55 | private boolean mFinished;
56 | private Interpolator mInterpolator;
57 | private boolean mFlywheel;
58 |
59 | private float mVelocity;
60 |
61 | private static final int DEFAULT_DURATION = 250;
62 | private static final int SCROLL_MODE = 0;
63 | private static final int FLING_MODE = 1;
64 |
65 | private static final float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9));
66 | private static final float ALPHA = 800; // pixels / seconds
67 | private static final float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance)
68 | private static final float END_TENSION = 1.0f - START_TENSION;
69 | private static final int NB_SAMPLES = 100;
70 | private static final float[] SPLINE = new float[NB_SAMPLES + 1];
71 |
72 | private float mDeceleration;
73 | private final float mPpi;
74 |
75 | static {
76 | float xMin = 0.0f;
77 | for (int i = 0; i <= NB_SAMPLES; i++) {
78 | final float t = (float) i / NB_SAMPLES;
79 | float xMax = 1.0f;
80 | float x, tx, coef;
81 | while (true) {
82 | x = xMin + (xMax - xMin) / 2.0f;
83 | coef = 3.0f * x * (1.0f - x);
84 | tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x;
85 | if (Math.abs(tx - t) < 1E-5) break;
86 | if (tx > t) xMax = x;
87 | else xMin = x;
88 | }
89 | final float d = coef + x * x * x;
90 | SPLINE[i] = d;
91 | }
92 | SPLINE[NB_SAMPLES] = 1.0f;
93 |
94 | // This controls the viscous fluid effect (how much of it)
95 | sViscousFluidScale = 8.0f;
96 | // must be set to 1.0 (used in viscousFluid())
97 | sViscousFluidNormalize = 1.0f;
98 | sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
99 | }
100 |
101 | private static float sViscousFluidScale;
102 | private static float sViscousFluidNormalize;
103 |
104 | /**
105 | * Create a Scroller with the default duration and interpolator.
106 | */
107 | public Scroller(Context context) {
108 | this(context, null);
109 | }
110 |
111 | /**
112 | * Create a Scroller with the specified interpolator. If the interpolator is
113 | * null, the default (viscous) interpolator will be used. "Flywheel" behavior will
114 | * be in effect for apps targeting Honeycomb or newer.
115 | */
116 | public Scroller(Context context, Interpolator interpolator) {
117 | this(context, interpolator,
118 | context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
119 | }
120 |
121 | /**
122 | * Create a Scroller with the specified interpolator. If the interpolator is
123 | * null, the default (viscous) interpolator will be used. Specify whether or
124 | * not to support progressive "flywheel" behavior in flinging.
125 | */
126 | public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
127 | mFinished = true;
128 | mInterpolator = interpolator;
129 | mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
130 | mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
131 | mFlywheel = flywheel;
132 | }
133 |
134 | /**
135 | * The amount of friction applied to flings. The default value
136 | * is {@link android.view.ViewConfiguration#getScrollFriction}.
137 | *
138 | * @param friction A scalar dimension-less value representing the coefficient of
139 | * friction.
140 | */
141 | public final void setFriction(float friction) {
142 | mDeceleration = computeDeceleration(friction);
143 | }
144 |
145 | private float computeDeceleration(float friction) {
146 | return SensorManager.GRAVITY_EARTH // g (m/s^2)
147 | * 39.37f // inch/meter
148 | * mPpi // pixels per inch
149 | * friction;
150 | }
151 |
152 | /**
153 | *
154 | * Returns whether the scroller has finished scrolling.
155 | *
156 | * @return True if the scroller has finished scrolling, false otherwise.
157 | */
158 | public final boolean isFinished() {
159 | return mFinished;
160 | }
161 |
162 | /**
163 | * Force the finished field to a particular value.
164 | *
165 | * @param finished The new finished value.
166 | */
167 | public final void forceFinished(boolean finished) {
168 | mFinished = finished;
169 | }
170 |
171 | /**
172 | * Returns how long the scroll event will take, in milliseconds.
173 | *
174 | * @return The duration of the scroll in milliseconds.
175 | */
176 | public final int getDuration() {
177 | return mDuration;
178 | }
179 |
180 | /**
181 | * Returns the current X offset in the scroll.
182 | *
183 | * @return The new X offset as an absolute distance from the origin.
184 | */
185 | public final int getCurrX() {
186 | return mCurrX;
187 | }
188 |
189 | /**
190 | * Returns the current Y offset in the scroll.
191 | *
192 | * @return The new Y offset as an absolute distance from the origin.
193 | */
194 | public final int getCurrY() {
195 | return mCurrY;
196 | }
197 |
198 | /**
199 | * Returns the current velocity.
200 | *
201 | * @return The original velocity less the deceleration. Result may be
202 | * negative.
203 | */
204 | public float getCurrVelocity() {
205 | return mVelocity - mDeceleration * timePassed() / 2000.0f;
206 | }
207 |
208 | /**
209 | * Returns the start X offset in the scroll.
210 | *
211 | * @return The start X offset as an absolute distance from the origin.
212 | */
213 | public final int getStartX() {
214 | return mStartX;
215 | }
216 |
217 | /**
218 | * Returns the start Y offset in the scroll.
219 | *
220 | * @return The start Y offset as an absolute distance from the origin.
221 | */
222 | public final int getStartY() {
223 | return mStartY;
224 | }
225 |
226 | /**
227 | * Returns where the scroll will end. Valid only for "fling" scrolls.
228 | *
229 | * @return The final X offset as an absolute distance from the origin.
230 | */
231 | public final int getFinalX() {
232 | return mFinalX;
233 | }
234 |
235 | /**
236 | * Returns where the scroll will end. Valid only for "fling" scrolls.
237 | *
238 | * @return The final Y offset as an absolute distance from the origin.
239 | */
240 | public final int getFinalY() {
241 | return mFinalY;
242 | }
243 |
244 | /**
245 | * Call this when you want to know the new location. If it returns true,
246 | * the animation is not yet finished. loc will be altered to provide the
247 | * new location.
248 | */
249 | public boolean computeScrollOffset() {
250 | if (mFinished) {
251 | return false;
252 | }
253 |
254 | int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
255 |
256 | if (timePassed < mDuration) {
257 | switch (mMode) {
258 | case SCROLL_MODE:
259 | float x = timePassed * mDurationReciprocal;
260 |
261 | if (mInterpolator == null)
262 | x = viscousFluid(x);
263 | else
264 | x = mInterpolator.getInterpolation(x);
265 |
266 | mCurrX = mStartX + Math.round(x * mDeltaX);
267 | mCurrY = mStartY + Math.round(x * mDeltaY);
268 | break;
269 | case FLING_MODE:
270 | final float t = (float) timePassed / mDuration;
271 | final int index = (int) (NB_SAMPLES * t);
272 | final float tInf = (float) index / NB_SAMPLES;
273 | final float tSup = (float) (index + 1) / NB_SAMPLES;
274 | final float dInf = SPLINE[index];
275 | final float dSup = SPLINE[index + 1];
276 | final float distanceCoef = dInf + (t - tInf) / (tSup - tInf) * (dSup - dInf);
277 |
278 | mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
279 | // Pin to mMinX <= mCurrX <= mMaxX
280 | mCurrX = Math.min(mCurrX, mMaxX);
281 | mCurrX = Math.max(mCurrX, mMinX);
282 |
283 | mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
284 | // Pin to mMinY <= mCurrY <= mMaxY
285 | mCurrY = Math.min(mCurrY, mMaxY);
286 | mCurrY = Math.max(mCurrY, mMinY);
287 |
288 | if (mCurrX == mFinalX && mCurrY == mFinalY) {
289 | mFinished = true;
290 | }
291 |
292 | break;
293 | }
294 | } else {
295 | mCurrX = mFinalX;
296 | mCurrY = mFinalY;
297 | mFinished = true;
298 | }
299 | return true;
300 | }
301 |
302 | /**
303 | * Start scrolling by providing a starting point and the distance to travel.
304 | * The scroll will use the default value of 250 milliseconds for the
305 | * duration.
306 | *
307 | * @param startX Starting horizontal scroll offset in pixels. Positive
308 | * numbers will scroll the content to the left.
309 | * @param startY Starting vertical scroll offset in pixels. Positive numbers
310 | * will scroll the content up.
311 | * @param dx Horizontal distance to travel. Positive numbers will scroll the
312 | * content to the left.
313 | * @param dy Vertical distance to travel. Positive numbers will scroll the
314 | * content up.
315 | */
316 | public void startScroll(int startX, int startY, int dx, int dy) {
317 | startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
318 | }
319 |
320 | /**
321 | * Start scrolling by providing a starting point and the distance to travel.
322 | *
323 | * @param startX Starting horizontal scroll offset in pixels. Positive
324 | * numbers will scroll the content to the left.
325 | * @param startY Starting vertical scroll offset in pixels. Positive numbers
326 | * will scroll the content up.
327 | * @param dx Horizontal distance to travel. Positive numbers will scroll the
328 | * content to the left.
329 | * @param dy Vertical distance to travel. Positive numbers will scroll the
330 | * content up.
331 | * @param duration Duration of the scroll in milliseconds.
332 | */
333 | public void startScroll(int startX, int startY, int dx, int dy, int duration) {
334 | mMode = SCROLL_MODE;
335 | mFinished = false;
336 | mDuration = duration;
337 | mStartTime = AnimationUtils.currentAnimationTimeMillis();
338 | mStartX = startX;
339 | mStartY = startY;
340 | mFinalX = startX + dx;
341 | mFinalY = startY + dy;
342 | mDeltaX = dx;
343 | mDeltaY = dy;
344 | mDurationReciprocal = 1.0f / (float) mDuration;
345 | }
346 |
347 | /**
348 | * Start scrolling based on a fling gesture. The distance travelled will
349 | * depend on the initial velocity of the fling.
350 | *
351 | * @param startX Starting point of the scroll (X)
352 | * @param startY Starting point of the scroll (Y)
353 | * @param velocityX Initial velocity of the fling (X) measured in pixels per
354 | * second.
355 | * @param velocityY Initial velocity of the fling (Y) measured in pixels per
356 | * second
357 | * @param minX Minimum X value. The scroller will not scroll past this
358 | * point.
359 | * @param maxX Maximum X value. The scroller will not scroll past this
360 | * point.
361 | * @param minY Minimum Y value. The scroller will not scroll past this
362 | * point.
363 | * @param maxY Maximum Y value. The scroller will not scroll past this
364 | * point.
365 | */
366 | public void fling(int startX, int startY, int velocityX, int velocityY,
367 | int minX, int maxX, int minY, int maxY) {
368 | // Continue a scroll or fling in progress
369 | if (mFlywheel && !mFinished) {
370 | float oldVel = getCurrVelocity();
371 |
372 | float dx = (float) (mFinalX - mStartX);
373 | float dy = (float) (mFinalY - mStartY);
374 | float hyp = FloatMath.sqrt(dx * dx + dy * dy);
375 |
376 | float ndx = dx / hyp;
377 | float ndy = dy / hyp;
378 |
379 | float oldVelocityX = ndx * oldVel;
380 | float oldVelocityY = ndy * oldVel;
381 | if (Math.signum(velocityX) == Math.signum(oldVelocityX)
382 | && Math.signum(velocityY) == Math.signum(oldVelocityY)) {
383 | velocityX += oldVelocityX;
384 | velocityY += oldVelocityY;
385 | }
386 | }
387 |
388 | mMode = FLING_MODE;
389 | mFinished = false;
390 |
391 | float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);
392 |
393 | mVelocity = velocity;
394 | final double l = Math.log(START_TENSION * velocity / ALPHA);
395 | mDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0)));
396 | mStartTime = AnimationUtils.currentAnimationTimeMillis();
397 | mStartX = startX;
398 | mStartY = startY;
399 |
400 | float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
401 | float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
402 |
403 | int totalDistance =
404 | (int) (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));
405 |
406 | mMinX = minX;
407 | mMaxX = maxX;
408 | mMinY = minY;
409 | mMaxY = maxY;
410 |
411 | mFinalX = startX + Math.round(totalDistance * coeffX);
412 | // Pin to mMinX <= mFinalX <= mMaxX
413 | mFinalX = Math.min(mFinalX, mMaxX);
414 | mFinalX = Math.max(mFinalX, mMinX);
415 |
416 | mFinalY = startY + Math.round(totalDistance * coeffY);
417 | // Pin to mMinY <= mFinalY <= mMaxY
418 | mFinalY = Math.min(mFinalY, mMaxY);
419 | mFinalY = Math.max(mFinalY, mMinY);
420 | }
421 |
422 | static float viscousFluid(float x) {
423 | x *= sViscousFluidScale;
424 | if (x < 1.0f) {
425 | x -= (1.0f - (float) Math.exp(-x));
426 | } else {
427 | float start = 0.36787944117f; // 1/e == exp(-1)
428 | x = 1.0f - (float) Math.exp(1.0f - x);
429 | x = start + x * (1.0f - start);
430 | }
431 | x *= sViscousFluidNormalize;
432 | return x;
433 | }
434 |
435 | /**
436 | * Stops the animation. Contrary to {@link #forceFinished(boolean)},
437 | * aborting the animating cause the scroller to move to the final x and y
438 | * position
439 | *
440 | * @see #forceFinished(boolean)
441 | */
442 | public void abortAnimation() {
443 | mCurrX = mFinalX;
444 | mCurrY = mFinalY;
445 | mFinished = true;
446 | }
447 |
448 | /**
449 | * Extend the scroll animation. This allows a running animation to scroll
450 | * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
451 | *
452 | * @param extend Additional time to scroll in milliseconds.
453 | * @see #setFinalX(int)
454 | * @see #setFinalY(int)
455 | */
456 | public void extendDuration(int extend) {
457 | int passed = timePassed();
458 | mDuration = passed + extend;
459 | mDurationReciprocal = 1.0f / mDuration;
460 | mFinished = false;
461 | }
462 |
463 | /**
464 | * Returns the time elapsed since the beginning of the scrolling.
465 | *
466 | * @return The elapsed time in milliseconds.
467 | */
468 | public int timePassed() {
469 | return (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
470 | }
471 |
472 | /**
473 | * Sets the final position (X) for this scroller.
474 | *
475 | * @param newX The new X offset as an absolute distance from the origin.
476 | * @see #extendDuration(int)
477 | * @see #setFinalY(int)
478 | */
479 | public void setFinalX(int newX) {
480 | mFinalX = newX;
481 | mDeltaX = mFinalX - mStartX;
482 | mFinished = false;
483 | }
484 |
485 | /**
486 | * Sets the final position (Y) for this scroller.
487 | *
488 | * @param newY The new Y offset as an absolute distance from the origin.
489 | * @see #extendDuration(int)
490 | * @see #setFinalX(int)
491 | */
492 | public void setFinalY(int newY) {
493 | mFinalY = newY;
494 | mDeltaY = mFinalY - mStartY;
495 | mFinished = false;
496 | }
497 |
498 | /**
499 | * @hide
500 | */
501 | public boolean isScrollingInDirection(float xvel, float yvel) {
502 | return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX)
503 | && Math.signum(yvel) == Math.signum(mFinalY - mStartY);
504 | }
505 | }
506 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/SinusoidalInterpolator.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer;
2 |
3 | import android.view.animation.Interpolator;
4 |
5 | /**
6 | * Interpolator which, when drawn from 0 to 1, looks like half a sine-wave. Used for smoother opening/closing when
7 | * peeking at the drawer.
8 | */
9 | class SinusoidalInterpolator implements Interpolator {
10 |
11 | @Override
12 | public float getInterpolation(float input) {
13 | return (float) (0.5f + 0.5f * Math.sin(input * Math.PI - Math.PI / 2.f));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/SlideDrawable.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.ColorFilter;
5 | import android.graphics.PorterDuff;
6 | import android.graphics.Rect;
7 | import android.graphics.Region;
8 | import android.graphics.drawable.Drawable;
9 |
10 | public class SlideDrawable extends Drawable implements Drawable.Callback {
11 |
12 | private Drawable mWrapped;
13 | private float mOffset;
14 |
15 | private final Rect mTmpRect = new Rect();
16 |
17 | private boolean mIsRtl;
18 |
19 | public SlideDrawable(Drawable wrapped) {
20 | mWrapped = wrapped;
21 | }
22 |
23 | public void setOffset(float offset) {
24 | mOffset = offset;
25 | invalidateSelf();
26 | }
27 |
28 | public float getOffset() {
29 | return mOffset;
30 | }
31 |
32 | void setIsRtl(boolean isRtl) {
33 | mIsRtl = isRtl;
34 | invalidateSelf();
35 | }
36 |
37 | @Override
38 | public void draw(Canvas canvas) {
39 | mWrapped.copyBounds(mTmpRect);
40 | canvas.save();
41 | if (mIsRtl) {
42 | canvas.translate(1.f / 3 * mTmpRect.width() * mOffset, 0);
43 | } else {
44 | canvas.translate(1.f / 3 * mTmpRect.width() * -mOffset, 0);
45 | }
46 | mWrapped.draw(canvas);
47 | canvas.restore();
48 | }
49 |
50 | @Override
51 | public void setChangingConfigurations(int configs) {
52 | mWrapped.setChangingConfigurations(configs);
53 | }
54 |
55 | @Override
56 | public int getChangingConfigurations() {
57 | return mWrapped.getChangingConfigurations();
58 | }
59 |
60 | @Override
61 | public void setDither(boolean dither) {
62 | mWrapped.setDither(dither);
63 | }
64 |
65 | @Override
66 | public void setFilterBitmap(boolean filter) {
67 | mWrapped.setFilterBitmap(filter);
68 | }
69 |
70 | @Override
71 | public void setAlpha(int alpha) {
72 | mWrapped.setAlpha(alpha);
73 | }
74 |
75 | @Override
76 | public void setColorFilter(ColorFilter cf) {
77 | mWrapped.setColorFilter(cf);
78 | }
79 |
80 | @Override
81 | public void setColorFilter(int color, PorterDuff.Mode mode) {
82 | mWrapped.setColorFilter(color, mode);
83 | }
84 |
85 | @Override
86 | public void clearColorFilter() {
87 | mWrapped.clearColorFilter();
88 | }
89 |
90 | @Override
91 | public boolean isStateful() {
92 | return mWrapped.isStateful();
93 | }
94 |
95 | @Override
96 | public boolean setState(int[] stateSet) {
97 | return mWrapped.setState(stateSet);
98 | }
99 |
100 | @Override
101 | public int[] getState() {
102 | return mWrapped.getState();
103 | }
104 |
105 | @Override
106 | public Drawable getCurrent() {
107 | return mWrapped.getCurrent();
108 | }
109 |
110 | @Override
111 | public boolean setVisible(boolean visible, boolean restart) {
112 | return super.setVisible(visible, restart);
113 | }
114 |
115 | @Override
116 | public int getOpacity() {
117 | return mWrapped.getOpacity();
118 | }
119 |
120 | @Override
121 | public Region getTransparentRegion() {
122 | return mWrapped.getTransparentRegion();
123 | }
124 |
125 | @Override
126 | protected boolean onStateChange(int[] state) {
127 | mWrapped.setState(state);
128 | return super.onStateChange(state);
129 | }
130 |
131 | @Override
132 | protected void onBoundsChange(Rect bounds) {
133 | super.onBoundsChange(bounds);
134 | mWrapped.setBounds(bounds);
135 | }
136 |
137 | @Override
138 | public int getIntrinsicWidth() {
139 | return mWrapped.getIntrinsicWidth();
140 | }
141 |
142 | @Override
143 | public int getIntrinsicHeight() {
144 | return mWrapped.getIntrinsicHeight();
145 | }
146 |
147 | @Override
148 | public int getMinimumWidth() {
149 | return mWrapped.getMinimumWidth();
150 | }
151 |
152 | @Override
153 | public int getMinimumHeight() {
154 | return mWrapped.getMinimumHeight();
155 | }
156 |
157 | @Override
158 | public boolean getPadding(Rect padding) {
159 | return mWrapped.getPadding(padding);
160 | }
161 |
162 | @Override
163 | public ConstantState getConstantState() {
164 | return super.getConstantState();
165 | }
166 |
167 | @Override
168 | public void invalidateDrawable(Drawable who) {
169 | if (who == mWrapped) {
170 | invalidateSelf();
171 | }
172 | }
173 |
174 | @Override
175 | public void scheduleDrawable(Drawable who, Runnable what, long when) {
176 | if (who == mWrapped) {
177 | scheduleSelf(what, when);
178 | }
179 | }
180 |
181 | @Override
182 | public void unscheduleDrawable(Drawable who, Runnable what) {
183 | if (who == mWrapped) {
184 | unscheduleSelf(what);
185 | }
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/SmoothInterpolator.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer;
2 |
3 | import android.view.animation.Interpolator;
4 |
5 | class SmoothInterpolator implements Interpolator {
6 |
7 | @Override
8 | public float getInterpolation(float t) {
9 | t -= 1.0f;
10 | return t * t * t * t * t + 1.0f;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/StaticDrawer.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Canvas;
6 | import android.graphics.drawable.Drawable;
7 | import android.util.AttributeSet;
8 |
9 | public class StaticDrawer extends MenuDrawer {
10 |
11 | public StaticDrawer(Context context) {
12 | super(context);
13 | }
14 |
15 | public StaticDrawer(Context context, AttributeSet attrs) {
16 | super(context, attrs);
17 | }
18 |
19 | public StaticDrawer(Context context, AttributeSet attrs, int defStyle) {
20 | super(context, attrs, defStyle);
21 | }
22 |
23 | @Override
24 | protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {
25 | super.initDrawer(context, attrs, defStyle);
26 | super.addView(mMenuContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
27 | super.addView(mContentContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
28 |
29 | mIsStatic = true;
30 | }
31 |
32 | @Override
33 | protected void drawOverlay(Canvas canvas) {
34 | // NO-OP
35 | }
36 |
37 | @Override
38 | protected void onOffsetPixelsChanged(int offsetPixels) {
39 | // NO-OP
40 | }
41 |
42 | @Override
43 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
44 | final int width = r - l;
45 | final int height = b - t;
46 |
47 | switch (getPosition()) {
48 | case LEFT:
49 | mMenuContainer.layout(0, 0, mMenuSize, height);
50 | mContentContainer.layout(mMenuSize, 0, width, height);
51 | break;
52 |
53 | case RIGHT:
54 | mMenuContainer.layout(width - mMenuSize, 0, width, height);
55 | mContentContainer.layout(0, 0, width - mMenuSize, height);
56 | break;
57 |
58 | case TOP:
59 | mMenuContainer.layout(0, 0, width, mMenuSize);
60 | mContentContainer.layout(0, mMenuSize, width, height);
61 | break;
62 |
63 | case BOTTOM:
64 | mMenuContainer.layout(0, height - mMenuSize, width, height);
65 | mContentContainer.layout(0, 0, width, height - mMenuSize);
66 | break;
67 | }
68 | }
69 |
70 | @Override
71 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
72 | final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
73 | final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
74 |
75 | if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
76 | throw new IllegalStateException("Must measure with an exact size");
77 | }
78 |
79 | final int width = MeasureSpec.getSize(widthMeasureSpec);
80 | final int height = MeasureSpec.getSize(heightMeasureSpec);
81 |
82 | switch (getPosition()) {
83 | case LEFT:
84 | case RIGHT: {
85 | final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
86 |
87 | final int menuWidth = mMenuSize;
88 | final int menuWidthMeasureSpec = MeasureSpec.makeMeasureSpec(menuWidth, MeasureSpec.EXACTLY);
89 |
90 | final int contentWidth = width - menuWidth;
91 | final int contentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY);
92 |
93 | mContentContainer.measure(contentWidthMeasureSpec, childHeightMeasureSpec);
94 | mMenuContainer.measure(menuWidthMeasureSpec, childHeightMeasureSpec);
95 | break;
96 | }
97 |
98 | case TOP:
99 | case BOTTOM: {
100 | final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
101 |
102 | final int menuHeight = mMenuSize;
103 | final int menuHeightMeasureSpec = MeasureSpec.makeMeasureSpec(menuHeight, MeasureSpec.EXACTLY);
104 |
105 | final int contentHeight = height - menuHeight;
106 | final int contentHeightMeasureSpec = MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY);
107 |
108 | mContentContainer.measure(childWidthMeasureSpec, contentHeightMeasureSpec);
109 | mMenuContainer.measure(childWidthMeasureSpec, menuHeightMeasureSpec);
110 | break;
111 | }
112 | }
113 |
114 | setMeasuredDimension(width, height);
115 | }
116 |
117 | @Override
118 | public void toggleMenu(boolean animate) {
119 | // NO-OP
120 | }
121 |
122 | @Override
123 | public void openMenu(boolean animate) {
124 | // NO-OP
125 | }
126 |
127 | @Override
128 | public void closeMenu(boolean animate) {
129 | // NO-OP
130 | }
131 |
132 | @Override
133 | public boolean isMenuVisible() {
134 | return true;
135 | }
136 |
137 | @Override
138 | public void setMenuSize(int size) {
139 | mMenuSize = size;
140 | requestLayout();
141 | invalidate();
142 | }
143 |
144 | @Override
145 | public void setOffsetMenuEnabled(boolean offsetMenu) {
146 | // NO-OP
147 | }
148 |
149 | @Override
150 | public boolean getOffsetMenuEnabled() {
151 | return false;
152 | }
153 |
154 | @Override
155 | public void peekDrawer() {
156 | // NO-OP
157 | }
158 |
159 | @Override
160 | public void peekDrawer(long delay) {
161 | // NO-OP
162 | }
163 |
164 | @Override
165 | public void peekDrawer(long startDelay, long delay) {
166 | // NO-OP
167 | }
168 |
169 | @Override
170 | public void setHardwareLayerEnabled(boolean enabled) {
171 | // NO-OP
172 | }
173 |
174 | @Override
175 | public int getTouchMode() {
176 | return TOUCH_MODE_NONE;
177 | }
178 |
179 | @Override
180 | public void setTouchMode(int mode) {
181 | // NO-OP
182 | }
183 |
184 | @Override
185 | public void setTouchBezelSize(int size) {
186 | // NO-OP
187 | }
188 |
189 | @Override
190 | public int getTouchBezelSize() {
191 | return 0;
192 | }
193 |
194 | @Override
195 | public void setSlideDrawable(int drawableRes) {
196 | // NO-OP
197 | }
198 |
199 | @Override
200 | public void setSlideDrawable(Drawable drawable) {
201 | // NO-OP
202 | }
203 |
204 | @Override
205 | public void setupUpIndicator(Activity activity) {
206 | // NO-OP
207 | }
208 |
209 | @Override
210 | public void setDrawerIndicatorEnabled(boolean enabled) {
211 | // NO-OP
212 | }
213 |
214 | @Override
215 | public boolean isDrawerIndicatorEnabled() {
216 | return false;
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/ViewHelper.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer;
2 |
3 | import android.os.Build;
4 | import android.view.View;
5 |
6 | final class ViewHelper {
7 |
8 | private ViewHelper() {
9 | }
10 |
11 | public static int getLeft(View v) {
12 | if (MenuDrawer.USE_TRANSLATIONS) {
13 | return (int) (v.getLeft() + v.getTranslationX());
14 | }
15 |
16 | return v.getLeft();
17 | }
18 |
19 | public static int getTop(View v) {
20 | if (MenuDrawer.USE_TRANSLATIONS) {
21 | return (int) (v.getTop() + v.getTranslationY());
22 | }
23 |
24 | return v.getTop();
25 | }
26 |
27 | public static int getRight(View v) {
28 | if (MenuDrawer.USE_TRANSLATIONS) {
29 | return (int) (v.getRight() + v.getTranslationX());
30 | }
31 |
32 | return v.getRight();
33 | }
34 |
35 | public static int getBottom(View v) {
36 | if (MenuDrawer.USE_TRANSLATIONS) {
37 | return (int) (v.getBottom() + v.getTranslationY());
38 | }
39 |
40 | return v.getBottom();
41 | }
42 |
43 | public static int getLayoutDirection(View v) {
44 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
45 | return v.getLayoutDirection();
46 | }
47 |
48 | return View.LAYOUT_DIRECTION_LTR;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/compat/ActionBarHelper.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer.compat;
2 |
3 | import android.app.Activity;
4 | import android.graphics.drawable.Drawable;
5 | import android.os.Build;
6 | import android.util.Log;
7 |
8 | import java.lang.reflect.Method;
9 |
10 | public final class ActionBarHelper {
11 |
12 | private static final String TAG = "ActionBarHelper";
13 |
14 | static final boolean DEBUG = false;
15 |
16 | private Activity mActivity;
17 |
18 | private Object mIndicatorInfo;
19 |
20 | private boolean mUsesCompat;
21 |
22 | public ActionBarHelper(Activity activity) {
23 | mActivity = activity;
24 |
25 | try {
26 | Class clazz = activity.getClass();
27 | Method m = clazz.getMethod("getSupportActionBar");
28 | mUsesCompat = true;
29 | } catch (NoSuchMethodException e) {
30 | if (DEBUG) {
31 | Log.e(TAG,
32 | "Activity " + activity.getClass().getSimpleName() + " does not use a compatibility action bar",
33 | e);
34 | }
35 | }
36 |
37 | mIndicatorInfo = getIndicatorInfo();
38 | }
39 |
40 | private Object getIndicatorInfo() {
41 | if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
42 | return ActionBarHelperCompat.getIndicatorInfo(mActivity);
43 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
44 | return ActionBarHelperNative.getIndicatorInfo(mActivity);
45 | }
46 |
47 | return null;
48 | }
49 |
50 | public void setActionBarUpIndicator(Drawable drawable, int contentDesc) {
51 | if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
52 | ActionBarHelperCompat.setActionBarUpIndicator(mIndicatorInfo, mActivity, drawable, contentDesc);
53 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
54 | ActionBarHelperNative.setActionBarUpIndicator(mIndicatorInfo, mActivity, drawable, contentDesc);
55 | }
56 | }
57 |
58 | public void setActionBarDescription(int contentDesc) {
59 | if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
60 | ActionBarHelperCompat.setActionBarDescription(mIndicatorInfo, mActivity, contentDesc);
61 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
62 | ActionBarHelperNative.setActionBarDescription(mIndicatorInfo, mActivity, contentDesc);
63 | }
64 | }
65 |
66 | public Drawable getThemeUpIndicator() {
67 | if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
68 | return ActionBarHelperCompat.getThemeUpIndicator(mIndicatorInfo);
69 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
70 | return ActionBarHelperNative.getThemeUpIndicator(mIndicatorInfo, mActivity);
71 | }
72 |
73 | return null;
74 | }
75 |
76 | public void setDisplayShowHomeAsUpEnabled(boolean enabled) {
77 | if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
78 | ActionBarHelperCompat.setDisplayHomeAsUpEnabled(mIndicatorInfo, enabled);
79 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
80 | ActionBarHelperNative.setDisplayHomeAsUpEnabled(mActivity, enabled);
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/compat/ActionBarHelperCompat.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer.compat;
2 |
3 | import android.app.Activity;
4 | import android.graphics.drawable.Drawable;
5 | import android.util.Log;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import java.lang.reflect.Method;
11 |
12 | final class ActionBarHelperCompat {
13 |
14 | private static final String TAG = "ActionBarHelperCompat";
15 |
16 | private ActionBarHelperCompat() {
17 | }
18 |
19 | public static void setActionBarUpIndicator(Object info, Activity activity, Drawable drawable, int contentDescRes) {
20 | final SetIndicatorInfo sii = (SetIndicatorInfo) info;
21 | if (sii.mUpIndicatorView != null) {
22 | sii.mUpIndicatorView.setImageDrawable(drawable);
23 | final String contentDescription = contentDescRes == 0 ? null : activity.getString(contentDescRes);
24 | sii.mUpIndicatorView.setContentDescription(contentDescription);
25 | }
26 | }
27 |
28 | public static void setActionBarDescription(Object info, Activity activity, int contentDescRes) {
29 | final SetIndicatorInfo sii = (SetIndicatorInfo) info;
30 | if (sii.mUpIndicatorView != null) {
31 | final String contentDescription = contentDescRes == 0 ? null : activity.getString(contentDescRes);
32 | sii.mUpIndicatorView.setContentDescription(contentDescription);
33 | }
34 | }
35 |
36 | public static Drawable getThemeUpIndicator(Object info) {
37 | final SetIndicatorInfo sii = (SetIndicatorInfo) info;
38 | if (sii.mUpIndicatorView != null) {
39 | return sii.mUpIndicatorView.getDrawable();
40 | }
41 | return null;
42 | }
43 |
44 | public static Object getIndicatorInfo(Activity activity) {
45 | return new SetIndicatorInfo(activity);
46 | }
47 |
48 | public static void setDisplayHomeAsUpEnabled(Object info, boolean enabled) {
49 | final SetIndicatorInfo sii = (SetIndicatorInfo) info;
50 | if (sii.mHomeAsUpEnabled != null) {
51 | try {
52 | sii.mHomeAsUpEnabled.invoke(sii.mActionBar, enabled);
53 | } catch (Throwable t) {
54 | if (ActionBarHelper.DEBUG) {
55 | Log.e(TAG, "Unable to call setHomeAsUpEnabled", t);
56 | }
57 | }
58 | }
59 | }
60 |
61 | private static class SetIndicatorInfo {
62 |
63 | public ImageView mUpIndicatorView;
64 | public Object mActionBar;
65 | public Method mHomeAsUpEnabled;
66 |
67 | SetIndicatorInfo(Activity activity) {
68 | try {
69 | String appPackage = activity.getPackageName();
70 |
71 | try {
72 | // Attempt to find ActionBarSherlock up indicator
73 | final int homeId = activity.getResources().getIdentifier("abs__home", "id", appPackage);
74 | View v = activity.findViewById(homeId);
75 | ViewGroup parent = (ViewGroup) v.getParent();
76 | final int upId = activity.getResources().getIdentifier("abs__up", "id", appPackage);
77 | mUpIndicatorView = (ImageView) parent.findViewById(upId);
78 | } catch (Throwable t) {
79 | if (ActionBarHelper.DEBUG) {
80 | Log.e(TAG, "ABS action bar not found", t);
81 | }
82 | }
83 |
84 | if (mUpIndicatorView == null) {
85 | // Attempt to find AppCompat up indicator
86 | final int homeId = activity.getResources().getIdentifier("home", "id", appPackage);
87 | View v = activity.findViewById(homeId);
88 | ViewGroup parent = (ViewGroup) v.getParent();
89 | final int upId = activity.getResources().getIdentifier("up", "id", appPackage);
90 | mUpIndicatorView = (ImageView) parent.findViewById(upId);
91 | }
92 |
93 | Class supportActivity = activity.getClass();
94 | Method getActionBar = supportActivity.getMethod("getSupportActionBar");
95 |
96 | mActionBar = getActionBar.invoke(activity, null);
97 | Class supportActionBar = mActionBar.getClass();
98 | mHomeAsUpEnabled = supportActionBar.getMethod("setDisplayHomeAsUpEnabled", Boolean.TYPE);
99 |
100 | } catch (Throwable t) {
101 | if (ActionBarHelper.DEBUG) {
102 | Log.e(TAG, "Unable to init SetIndicatorInfo for ABS", t);
103 | }
104 | }
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/menudrawer/src/net/simonvt/menudrawer/compat/ActionBarHelperNative.java:
--------------------------------------------------------------------------------
1 | package net.simonvt.menudrawer.compat;
2 |
3 | import android.app.ActionBar;
4 | import android.app.Activity;
5 | import android.content.res.TypedArray;
6 | import android.graphics.drawable.Drawable;
7 | import android.util.Log;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.ImageView;
11 |
12 | import java.lang.reflect.Method;
13 |
14 | final class ActionBarHelperNative {
15 |
16 | private static final String TAG = "ActionBarHelperNative";
17 |
18 | private ActionBarHelperNative() {
19 | }
20 |
21 | private static final int[] THEME_ATTRS = new int[] {
22 | android.R.attr.homeAsUpIndicator
23 | };
24 |
25 | public static void setActionBarUpIndicator(Object info, Activity activity, Drawable drawable, int contentDescRes) {
26 |
27 | final SetIndicatorInfo sii = (SetIndicatorInfo) info;
28 | if (sii.setHomeAsUpIndicator != null) {
29 | try {
30 | final ActionBar actionBar = activity.getActionBar();
31 | sii.setHomeAsUpIndicator.invoke(actionBar, drawable);
32 | sii.setHomeActionContentDescription.invoke(actionBar, contentDescRes);
33 | } catch (Throwable t) {
34 | if (ActionBarHelper.DEBUG) Log.e(TAG, "Couldn't set home-as-up indicator via JB-MR2 API", t);
35 | }
36 | } else if (sii.upIndicatorView != null) {
37 | sii.upIndicatorView.setImageDrawable(drawable);
38 | } else {
39 | if (ActionBarHelper.DEBUG) Log.e(TAG, "Couldn't set home-as-up indicator");
40 | }
41 | }
42 |
43 | public static void setActionBarDescription(Object info, Activity activity, int contentDescRes) {
44 | final SetIndicatorInfo sii = (SetIndicatorInfo) info;
45 | if (sii.setHomeAsUpIndicator != null) {
46 | try {
47 | final ActionBar actionBar = activity.getActionBar();
48 | sii.setHomeActionContentDescription.invoke(actionBar, contentDescRes);
49 | } catch (Throwable t) {
50 | if (ActionBarHelper.DEBUG) Log.e(TAG, "Couldn't set content description via JB-MR2 API", t);
51 | }
52 | }
53 | }
54 |
55 | public static Drawable getThemeUpIndicator(Object info, Activity activity) {
56 | final TypedArray a = activity.obtainStyledAttributes(THEME_ATTRS);
57 | final Drawable result = a.getDrawable(0);
58 | a.recycle();
59 | return result;
60 | }
61 |
62 | public static Object getIndicatorInfo(Activity activity) {
63 | return new SetIndicatorInfo(activity);
64 | }
65 |
66 | public static void setDisplayHomeAsUpEnabled(Activity activity, boolean b) {
67 | ActionBar actionBar = activity.getActionBar();
68 | if (actionBar != null) {
69 | actionBar.setDisplayHomeAsUpEnabled(b);
70 | }
71 | }
72 |
73 | private static class SetIndicatorInfo {
74 |
75 | public Method setHomeAsUpIndicator;
76 | public Method setHomeActionContentDescription;
77 | public ImageView upIndicatorView;
78 |
79 | SetIndicatorInfo(Activity activity) {
80 | try {
81 | setHomeAsUpIndicator = ActionBar.class.getDeclaredMethod("setHomeAsUpIndicator", Drawable.class);
82 | setHomeActionContentDescription = ActionBar.class.getDeclaredMethod(
83 | "setHomeActionContentDescription", Integer.TYPE);
84 |
85 | // If we got the method we won't need the stuff below.
86 | return;
87 | } catch (Throwable t) {
88 | // Oh well. We'll use the other mechanism below instead.
89 | }
90 |
91 | final View home = activity.findViewById(android.R.id.home);
92 | if (home == null) {
93 | // Action bar doesn't have a known configuration, an OEM messed with things.
94 | return;
95 | }
96 |
97 | final ViewGroup parent = (ViewGroup) home.getParent();
98 | final int childCount = parent.getChildCount();
99 | if (childCount != 2) {
100 | // No idea which one will be the right one, an OEM messed with things.
101 | return;
102 | }
103 |
104 | final View first = parent.getChildAt(0);
105 | final View second = parent.getChildAt(1);
106 | final View up = first.getId() == android.R.id.home ? second : first;
107 |
108 | if (up instanceof ImageView) {
109 | // Jackpot! (Probably...)
110 | upIndicatorView = (ImageView) up;
111 | }
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'menudrawer', 'menudrawer-samples'
2 |
--------------------------------------------------------------------------------