PieMenu.java revision 2a56ecaf153d788a1acebc54b670347a1a58d693
1376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb/* 2376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * Copyright (C) 2010 The Android Open Source Project 3376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * 4376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License"); 5376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * you may not use this file except in compliance with the License. 6376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * You may obtain a copy of the License at 7376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * 8376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * http://www.apache.org/licenses/LICENSE-2.0 9376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * 10376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * Unless required by applicable law or agreed to in writing, software 11376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS, 12376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * See the License for the specific language governing permissions and 14376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * limitations under the License. 15376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 16376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 17376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbpackage com.android.browser.view; 18376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 19376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport com.android.browser.R; 20376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 21376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.content.Context; 22376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.content.res.Resources; 232a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolbimport android.graphics.Bitmap; 242a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolbimport android.graphics.BitmapShader; 25376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Canvas; 262a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolbimport android.graphics.Matrix; 27376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Paint; 28376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Path; 29376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Point; 30376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.PointF; 31376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Rect; 32376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.RectF; 332a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolbimport android.graphics.Shader; 342a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolbimport android.graphics.drawable.Drawable; 35376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.util.AttributeSet; 36376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.view.MotionEvent; 372a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolbimport android.view.SoundEffectConstants; 38376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.view.View; 394be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reckimport android.view.ViewGroup; 40376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.widget.FrameLayout; 41376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 42376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.ArrayList; 43376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.HashMap; 44376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.List; 45376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.Map; 46376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 47376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbpublic class PieMenu extends FrameLayout { 48376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 49376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private static final int RADIUS_GAP = 10; 50376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 51376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public interface PieController { 52376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 53376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * called before menu opens to customize menu 54376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * returns if pie state has been changed 55376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 56376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public boolean onOpen(); 57376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 58376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private Point mCenter; 59376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int mRadius; 60376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int mRadiusInc; 61376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int mSlop; 62376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 63376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private boolean mOpen; 64376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private Paint mPaint; 65376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private Paint mSelectedPaint; 66376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private PieController mController; 67376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 68376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private Map<View, List<View>> mMenu; 69376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private List<View> mStack; 70376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 71376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private boolean mDirty; 72376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 732a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb private Drawable mActiveDrawable; 742a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb private Drawable mInactiveDrawable; 752a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb private final Paint mActiveShaderPaint = new Paint(); 762a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb private final Paint mInactiveShaderPaint = new Paint(); 772a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb private final Matrix mActiveMatrix = new Matrix(); 782a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb private final Matrix mInactiveMatrix = new Matrix(); 792a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 802a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb private BitmapShader mActiveShader; 812a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb private BitmapShader mInactiveShader; 822a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 832a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 84376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 85376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param context 86376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param attrs 87376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param defStyle 88376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 89376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public PieMenu(Context context, AttributeSet attrs, int defStyle) { 90376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb super(context, attrs, defStyle); 91376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb init(context); 92376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 93376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 94376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 95376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param context 96376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param attrs 97376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 98376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public PieMenu(Context context, AttributeSet attrs) { 99376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb super(context, attrs); 100376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb init(context); 101376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 102376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 103376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 104376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param context 105376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 106376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public PieMenu(Context context) { 107376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb super(context); 108376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb init(context); 109376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 110376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 111376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void init(Context ctx) { 112376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb this.setTag(new MenuTag(0)); 113376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mStack = new ArrayList<View>(); 114376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mStack.add(this); 115376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb Resources res = ctx.getResources(); 116376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mRadius = (int) res.getDimension(R.dimen.qc_radius); 117376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mRadiusInc = (int) res.getDimension(R.dimen.qc_radius_inc); 118376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mSlop = (int) res.getDimension(R.dimen.qc_slop); 119376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mPaint = new Paint(); 120376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mPaint.setAntiAlias(true); 121376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mPaint.setColor(res.getColor(R.color.qc_slice_normal)); 122376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mSelectedPaint = new Paint(); 123376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mSelectedPaint.setAntiAlias(true); 124376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mSelectedPaint.setColor(res.getColor(R.color.qc_slice_active)); 125376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mOpen = false; 126376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mMenu = new HashMap<View, List<View>>(); 127376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setWillNotDraw(false); 128376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setDrawingCacheEnabled(false); 129376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter = new Point(0,0); 130376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mDirty = true; 1312a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mActiveShaderPaint.setStyle(Paint.Style.FILL); 1322a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mActiveShaderPaint.setAntiAlias(true); 1332a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 1342a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mInactiveShaderPaint.setStyle(Paint.Style.FILL); 1352a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mInactiveShaderPaint.setAntiAlias(true); 1362a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mActiveDrawable = res.getDrawable(R.drawable.qc_background_selected); 1372a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mInactiveDrawable = res.getDrawable(R.drawable.qc_background_normal); 1382a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 1392a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb Bitmap activeTexture = getDrawableAsBitmap(mActiveDrawable, 1402a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mActiveDrawable.getIntrinsicWidth(), 1412a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mActiveDrawable.getIntrinsicHeight()); 1422a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb Bitmap inactiveTexture = getDrawableAsBitmap(mInactiveDrawable, 1432a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mInactiveDrawable.getIntrinsicWidth(), 1442a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mInactiveDrawable.getIntrinsicHeight()); 1452a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 1462a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mActiveShader = new BitmapShader(activeTexture, 1472a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 1482a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mActiveShaderPaint.setShader(mActiveShader); 1492a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 1502a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mInactiveShader = new BitmapShader(inactiveTexture, 1512a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 1522a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb mInactiveShaderPaint.setShader(mInactiveShader); 1532a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 1542a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb } 1552a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 1562a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb private static Bitmap getDrawableAsBitmap(Drawable drawable, int width, int height) { 1572a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 1582a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb Canvas c = new Canvas(b); 1592a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb drawable.setBounds(0, 0, width, height); 1602a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb drawable.draw(c); 1612a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb return b; 162376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 163376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 164376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void setController(PieController ctl) { 165376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mController = ctl; 166376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 167376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 168376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void setRadius(int r) { 169376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mRadius = r; 170376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb requestLayout(); 171376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 172376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 173376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void setRadiusIncrement(int ri) { 174376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mRadiusInc = ri; 175376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb requestLayout(); 176376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 177376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 178376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 179376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * add a menu item to another item as a submenu 180376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param item 181376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param parent 182376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 183376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void addItem(View item, View parent) { 184376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> subs = mMenu.get(parent); 185376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (subs == null) { 186376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb subs = new ArrayList<View>(); 187376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mMenu.put(parent, subs); 188376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 189376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb subs.add(item); 190376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb MenuTag tag = new MenuTag(((MenuTag) parent.getTag()).level + 1); 191376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb item.setTag(tag); 192376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 193376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 194376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void addItem(View view) { 195376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // add the item to the pie itself 196376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb addItem(view, this); 197376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 198376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 199376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void removeItem(View view) { 200376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> subs = mMenu.get(view); 201376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mMenu.remove(view); 202376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb for (View p : mMenu.keySet()) { 203376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> sl = mMenu.get(p); 204376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (sl != null) { 205376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb sl.remove(view); 206376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 207376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 208376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 209376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 210376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void clearItems(View parent) { 211376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> subs = mMenu.remove(parent); 212376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (subs != null) { 213376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb for (View sub: subs) { 214376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb clearItems(sub); 215376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 216376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 217376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 218376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 219376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void clearItems() { 220376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mMenu.clear(); 221376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 222376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 223376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 224376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void show(boolean show) { 225376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mOpen = show; 226376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 227376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mController != null) { 228376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb boolean changed = mController.onOpen(); 229376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 230376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mDirty = true; 231376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 232376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (!show) { 233376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // hide sub items 234376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mStack.clear(); 235376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mStack.add(this); 236376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 237376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb invalidate(); 238376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 239376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 240376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void setCenter(int x, int y) { 241376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (x < mSlop) { 242376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter.x = 0; 243376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else { 244376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter.x = getWidth(); 245376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 246376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter.y = y; 247376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 248376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 249376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private boolean onTheLeft() { 250376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return mCenter.x < mSlop; 251376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 252376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 253376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb @Override 254376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb protected void onDraw(Canvas canvas) { 255376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 256376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int radius = mRadius; 257376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // start in the center for 0 level menu 258376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float anchor = (float) Math.PI / 2; 259376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb PointF angles = new PointF(); 260376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int state = canvas.save(); 261376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (onTheLeft()) { 262376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // left handed 263376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb canvas.scale(-1, 1); 264376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 265376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb for (View parent : mStack) { 266376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> subs = mMenu.get(parent); 267376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (subs != null) { 268376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setGeometry(anchor, subs.size(), angles); 269376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 270376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb anchor = drawSlices(canvas, subs, radius, angles.x, angles.y); 271376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb radius += mRadiusInc + RADIUS_GAP; 272376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 273376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb canvas.restoreToCount(state); 274376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mDirty = false; 275376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 276376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 277376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 278376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 279376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * draw the set of slices 280376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param canvas 281376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param items 282376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param radius 283376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param start 284376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param sweep 285376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @return the angle of the selected slice 286376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 287376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private float drawSlices(Canvas canvas, List<View> items, int radius, 288376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float start, float sweep) { 289376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float angle = start + sweep / 2; 290376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // gap between slices in degrees 291376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float gap = 1f; 292376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float newanchor = 0f; 293376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb for (View item : items) { 294376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mDirty) { 295376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb item.measure(item.getLayoutParams().width, 296376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb item.getLayoutParams().height); 297376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int w = item.getMeasuredWidth(); 298376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int h = item.getMeasuredHeight(); 299376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int x = (int) (radius * Math.sin(angle)); 300376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int y = mCenter.y - (int) (radius * Math.cos(angle)) - h / 2; 301376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (onTheLeft()) { 302376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb x = mCenter.x + x - w / 2; 303376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else { 304376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb x = mCenter.x - x - w / 2; 305376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 306376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb item.layout(x, y, x + w, y + h); 307376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 308376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float itemstart = angle - sweep / 2; 309376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int inner = radius - mRadiusInc / 2; 310376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int outer = radius + mRadiusInc / 2; 311376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb Path slice = makeSlice(getDegrees(itemstart) - gap, 312376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb getDegrees(itemstart + sweep) + gap, 313376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb outer, inner, mCenter); 314376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb MenuTag tag = (MenuTag) item.getTag(); 315376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb tag.start = itemstart; 316376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb tag.sweep = sweep; 317376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb tag.inner = inner; 318376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb tag.outer = outer; 319376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int state = canvas.save(); 3202a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb int[] topLeft = new int[2]; 3212a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb getLocationInWindow(topLeft); 3222a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb topLeft[0] = mCenter.x - outer; 3232a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb topLeft[1] = mCenter.y - outer; 3242a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb Paint paint = item.isPressed() ? mActiveShaderPaint : mInactiveShaderPaint; 3252a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb drawClipped(canvas, paint, slice, topLeft, item.isPressed()); 3262a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb canvas.restoreToCount(state); 3272a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb state = canvas.save(); 328376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (onTheLeft()) { 329376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb canvas.scale(-1, 1); 330376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 331376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb canvas.translate(item.getX(), item.getY()); 332376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb item.draw(canvas); 333376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb canvas.restoreToCount(state); 334376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mStack.contains(item)) { 335376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // item is anchor for sub menu 336376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb newanchor = angle; 337376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 338376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb angle += sweep; 339376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 340376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return newanchor; 341376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 342376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 3432a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb private void drawClipped(Canvas canvas, Paint paint, Path clipPath, int[] pos, 3442a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb boolean selected) { 3452a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb // TODO: We should change the matrix/shader only when needed 3462a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb final Matrix matrix = selected ? mActiveMatrix : mInactiveMatrix; 3472a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb matrix.setTranslate(pos[0], pos[1]); 3482a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb (selected ? mActiveShader : mInactiveShader).setLocalMatrix(matrix); 3492a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb canvas.drawPath(clipPath, paint); 3502a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb } 3512a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 3522a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 353376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 354376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * converts a 355376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param angle from 0..PI to Android degrees (clockwise starting at 3 o'clock) 356376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @return skia angle 357376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 358376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private float getDegrees(double angle) { 359376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return (float) (270 - 180 * angle / Math.PI); 360376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 361376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 362376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private Path makeSlice(float startangle, float endangle, int outerradius, 363376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int innerradius, Point center) { 364376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb RectF bb = new RectF(center.x - outerradius, center.y - outerradius, 365376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb center.x + outerradius, center.y + outerradius); 366376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb RectF bbi = new RectF(center.x - innerradius, center.y - innerradius, 367376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb center.x + innerradius, center.y + innerradius); 368376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb Path path = new Path(); 369376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb path.arcTo(bb, startangle, endangle - startangle, true); 370376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb path.arcTo(bbi, endangle, startangle - endangle); 371376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb path.close(); 372376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return path; 373376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 374376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 375376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 376376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * all angles are 0 .. MATH.PI where 0 points up, and rotate counterclockwise 377376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * set the startangle and slice sweep in result 378376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param anchorangle : angle at which the menu is anchored 379376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param nslices 380376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param result : x : start, y : sweep 381376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 382376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void setGeometry(float anchorangle, int nslices, PointF result) { 383376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float span = (float) Math.min(anchorangle, Math.PI - anchorangle); 384376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float sweep = 2 * span / (nslices + 1); 385376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb result.x = anchorangle - span + sweep / 2; 386376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb result.y = sweep; 387376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 388376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 389376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // touch handling for pie 390376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 391376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb View mCurrentView; 392376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb Rect mHitRect = new Rect(); 393376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 394376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb @Override 395376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public boolean onTouchEvent(MotionEvent evt) { 396376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float x = evt.getX(); 397376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float y = evt.getY(); 398376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int action = evt.getActionMasked(); 399376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int edges = evt.getEdgeFlags(); 400376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (MotionEvent.ACTION_DOWN == action) { 401376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if ((x > getWidth() - mSlop) || (x < mSlop)) { 402376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setCenter((int) x, (int) y); 403376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb show(true); 404376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return true; 405376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 406376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (MotionEvent.ACTION_UP == action) { 407376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 408376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb View v = mCurrentView; 409376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb deselect(); 410376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (v != null) { 411376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb v.performClick(); 412376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 413376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb show(false); 414376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return true; 415376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 416376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (MotionEvent.ACTION_CANCEL == action) { 417376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 418376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb show(false); 419376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 420376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb deselect(); 421376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return false; 422376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (MotionEvent.ACTION_MOVE == action) { 423bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb PointF polar = getPolar(x, y); 424bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb if (polar.y > mRadius + 2 * mRadiusInc) { 425bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb show(false); 426bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb deselect(); 4274be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck evt.setAction(MotionEvent.ACTION_DOWN); 4284be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck if (getParent() != null) { 4294be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck ((ViewGroup) getParent()).dispatchTouchEvent(evt); 4304be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck } 431bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb return false; 432bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb } 433bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb View v = findView(polar); 434376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mCurrentView != v) { 435376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb onEnter(v); 436376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb invalidate(); 437376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 438376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 439376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // always re-dispatch event 440376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return false; 441376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 442376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 443376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 444376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * enter a slice for a view 445376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * updates model only 446376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param view 447376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 448376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void onEnter(View view) { 449376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // deselect 450376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mCurrentView != null) { 451376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (getLevel(mCurrentView) >= getLevel(view)) { 452376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCurrentView.setPressed(false); 453376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 454376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 455376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (view != null) { 456376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // clear up stack 4572a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb playSoundEffect(SoundEffectConstants.CLICK); 458376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb MenuTag tag = (MenuTag) view.getTag(); 459376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int i = mStack.size() - 1; 460376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb while (i > 0) { 461376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb View v = mStack.get(i); 462376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (((MenuTag) v.getTag()).level >= tag.level) { 463376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb v.setPressed(false); 464376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mStack.remove(i); 465376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else { 466376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb break; 467376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 468376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb i--; 469376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 470376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> items = mMenu.get(view); 471376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (items != null) { 472376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mStack.add(view); 473376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mDirty = true; 474376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 475376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb view.setPressed(true); 476376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 477376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCurrentView = view; 478376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 479376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 480376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void deselect() { 481376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mCurrentView != null) { 482376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCurrentView.setPressed(false); 483376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 484376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCurrentView = null; 485376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 486376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 487376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int getLevel(View v) { 488376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (v == null) return -1; 489376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return ((MenuTag) v.getTag()).level; 490376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 491376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 492bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb private PointF getPolar(float x, float y) { 493bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb PointF res = new PointF(); 494376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // get angle and radius from x/y 495bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.x = (float) Math.PI / 2; 496376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb x = mCenter.x - x; 497376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mCenter.x < mSlop) { 498376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb x = -x; 499376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 500376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb y = mCenter.y - y; 501bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.y = (float) Math.sqrt(x * x + y * y); 502376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (y > 0) { 503bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.x = (float) Math.asin(x / res.y); 504376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (y < 0) { 505bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.x = (float) (Math.PI - Math.asin(x / res.y )); 506376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 507bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb return res; 508bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb } 509bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb 510bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb /** 511bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb * 512bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb * @param polar x: angle, y: dist 513bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb * @return 514bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb */ 515bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb private View findView(PointF polar) { 516376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // find the matching item: 517376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb for (View parent : mStack) { 518376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> subs = mMenu.get(parent); 519376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (subs != null) { 520376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb for (View item : subs) { 521376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb MenuTag tag = (MenuTag) item.getTag(); 522bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb if ((tag.inner < polar.y) 523bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb && (tag.outer > polar.y) 524bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb && (tag.start < polar.x) 525bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb && (tag.start + tag.sweep > polar.x)) { 526376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return item; 527376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 528376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 529376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 530376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 531376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return null; 532376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 533376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 534376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb class MenuTag { 535376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 536376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int level; 537376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float start; 538376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float sweep; 539376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int inner; 540376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int outer; 541376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 542376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public MenuTag(int l) { 543376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb level = l; 544376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 545376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 546376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 547376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 548376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb} 549