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 1980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolbimport android.animation.Animator; 2080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolbimport android.animation.Animator.AnimatorListener; 2180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolbimport android.animation.AnimatorListenerAdapter; 2280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolbimport android.animation.ValueAnimator; 2380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolbimport android.animation.ValueAnimator.AnimatorUpdateListener; 24376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.content.Context; 25376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.content.res.Resources; 26376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Canvas; 27565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolbimport android.graphics.Paint; 28565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolbimport android.graphics.Path; 29376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Point; 30376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.PointF; 31565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolbimport android.graphics.RectF; 322a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolbimport android.graphics.drawable.Drawable; 33376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.util.AttributeSet; 34376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.view.MotionEvent; 352a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolbimport android.view.SoundEffectConstants; 36376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.view.View; 374be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reckimport android.view.ViewGroup; 38376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.widget.FrameLayout; 39376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 4080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolbimport com.android.browser.R; 4180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 42376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.ArrayList; 43376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.List; 44376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 45376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbpublic class PieMenu extends FrameLayout { 46376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 470860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private static final int MAX_LEVELS = 5; 4880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private static final long ANIMATION = 80; 49376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 50376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public interface PieController { 51376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 52376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * called before menu opens to customize menu 53376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * returns if pie state has been changed 54376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 55376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public boolean onOpen(); 5680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb public void stopEditingUrl(); 5780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 58376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 590860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb 601acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb /** 611acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb * A view like object that lives off of the pie menu 621acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb */ 631acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb public interface PieView { 641acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb 65eb95db48b01b3db935601f25bd1a2358674b76daMichael Kolb public interface OnLayoutListener { 66eb95db48b01b3db935601f25bd1a2358674b76daMichael Kolb public void onLayout(int ax, int ay, boolean left); 67eb95db48b01b3db935601f25bd1a2358674b76daMichael Kolb } 68eb95db48b01b3db935601f25bd1a2358674b76daMichael Kolb 69eb95db48b01b3db935601f25bd1a2358674b76daMichael Kolb public void setLayoutListener(OnLayoutListener l); 70eb95db48b01b3db935601f25bd1a2358674b76daMichael Kolb 71a4befac241ec69791fd30e11355ed3dc10d7fb37Michael Kolb public void layout(int anchorX, int anchorY, boolean onleft, float angle, 72a4befac241ec69791fd30e11355ed3dc10d7fb37Michael Kolb int parentHeight); 731acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb 741acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb public void draw(Canvas c); 751acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb 761acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb public boolean onTouchEvent(MotionEvent evt); 771acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb 781acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 791acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb 80376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private Point mCenter; 81376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int mRadius; 82376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int mRadiusInc; 83376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int mSlop; 84565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private int mTouchOffset; 8580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private Path mPath; 86376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 87376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private boolean mOpen; 88376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private PieController mController; 89376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 900860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private List<PieItem> mItems; 910860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private int mLevels; 920860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private int[] mCounts; 931acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb private PieView mPieView = null; 942a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 9580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb // sub menus 9680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private List<PieItem> mCurrentItems; 9780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private PieItem mOpenItem; 9880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 990860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private Drawable mBackground; 100565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private Paint mNormalPaint; 101565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private Paint mSelectedPaint; 10280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private Paint mSubPaint; 1032a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 1040860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb // touch handling 10580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private PieItem mCurrentItem; 1062a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 107716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb private boolean mUseBackground; 10880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private boolean mAnimating; 109716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb 110376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 111376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param context 112376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param attrs 113376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param defStyle 114376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 115376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public PieMenu(Context context, AttributeSet attrs, int defStyle) { 116376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb super(context, attrs, defStyle); 117376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb init(context); 118376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 119376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 120376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 121376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param context 122376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param attrs 123376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 124376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public PieMenu(Context context, AttributeSet attrs) { 125376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb super(context, attrs); 126376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb init(context); 127376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 128376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 129376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 130376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param context 131376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 132376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public PieMenu(Context context) { 133376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb super(context); 134376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb init(context); 135376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 136376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 137376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void init(Context ctx) { 1380860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mItems = new ArrayList<PieItem>(); 1390860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mLevels = 0; 1400860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mCounts = new int[MAX_LEVELS]; 141376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb Resources res = ctx.getResources(); 1420860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mRadius = (int) res.getDimension(R.dimen.qc_radius_start); 1430860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mRadiusInc = (int) res.getDimension(R.dimen.qc_radius_increment); 144376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mSlop = (int) res.getDimension(R.dimen.qc_slop); 145565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mTouchOffset = (int) res.getDimension(R.dimen.qc_touch_offset); 146376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mOpen = false; 147376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setWillNotDraw(false); 148376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setDrawingCacheEnabled(false); 149376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter = new Point(0,0); 1500860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mBackground = res.getDrawable(R.drawable.qc_background_normal); 151565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mNormalPaint = new Paint(); 152565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mNormalPaint.setColor(res.getColor(R.color.qc_normal)); 153565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mNormalPaint.setAntiAlias(true); 154565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mSelectedPaint = new Paint(); 155565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mSelectedPaint.setColor(res.getColor(R.color.qc_selected)); 156565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mSelectedPaint.setAntiAlias(true); 15780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mSubPaint = new Paint(); 15880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mSubPaint.setAntiAlias(true); 15980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mSubPaint.setColor(res.getColor(R.color.qc_sub)); 160376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 161376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 162376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void setController(PieController ctl) { 163376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mController = ctl; 164376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 165376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 166716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb public void setUseBackground(boolean useBackground) { 167716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb mUseBackground = useBackground; 168716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb } 169716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb 1700860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb public void addItem(PieItem item) { 171376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // add the item to the pie itself 1720860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mItems.add(item); 1730860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb int l = item.getLevel(); 1740860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mLevels = Math.max(mLevels, l); 1750860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mCounts[l]++; 176376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 177376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 1780860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb public void removeItem(PieItem item) { 1790860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mItems.remove(item); 180376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 181376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 182376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void clearItems() { 1830860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mItems.clear(); 184376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 185376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 1860860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private boolean onTheLeft() { 1870860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb return mCenter.x < mSlop; 1880860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb } 189376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 1900860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb /** 1910860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb * guaranteed has center set 1920860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb * @param show 1930860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb */ 1940860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private void show(boolean show) { 195376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mOpen = show; 196376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 197ee8ede1146cefb85d0b9e7f1fc796fcc8808629aMichael Kolb // ensure clean state 198ee8ede1146cefb85d0b9e7f1fc796fcc8808629aMichael Kolb mAnimating = false; 199ee8ede1146cefb85d0b9e7f1fc796fcc8808629aMichael Kolb mCurrentItem = null; 200ee8ede1146cefb85d0b9e7f1fc796fcc8808629aMichael Kolb mOpenItem = null; 201ee8ede1146cefb85d0b9e7f1fc796fcc8808629aMichael Kolb mPieView = null; 20280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mController.stopEditingUrl(); 20380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mCurrentItems = mItems; 204ee8ede1146cefb85d0b9e7f1fc796fcc8808629aMichael Kolb for (PieItem item : mCurrentItems) { 205ee8ede1146cefb85d0b9e7f1fc796fcc8808629aMichael Kolb item.setSelected(false); 206ee8ede1146cefb85d0b9e7f1fc796fcc8808629aMichael Kolb } 207376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mController != null) { 208376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb boolean changed = mController.onOpen(); 209376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 2100860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb layoutPie(); 21180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb animateOpen(); 212376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 213376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb invalidate(); 214376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 215376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 21680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private void animateOpen() { 21780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb ValueAnimator anim = ValueAnimator.ofFloat(0, 1); 21880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb anim.addUpdateListener(new AnimatorUpdateListener() { 21980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb @Override 22080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb public void onAnimationUpdate(ValueAnimator animation) { 22180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb for (PieItem item : mCurrentItems) { 22280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb item.setAnimationAngle((1 - animation.getAnimatedFraction()) * (- item.getStart())); 22380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 22480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb invalidate(); 22580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 22680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 22780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb }); 22880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb anim.setDuration(2*ANIMATION); 22980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb anim.start(); 23080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 23180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 232376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void setCenter(int x, int y) { 233376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (x < mSlop) { 234376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter.x = 0; 235376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else { 236376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter.x = getWidth(); 237376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 238376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter.y = y; 239376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 240376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 2410860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private void layoutPie() { 242565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb float emptyangle = (float) Math.PI / 16; 243565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int rgap = 2; 244565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int inner = mRadius + rgap; 245565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int outer = mRadius + mRadiusInc - rgap; 246565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int gap = 1; 2470860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb for (int i = 0; i < mLevels; i++) { 2480860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb int level = i + 1; 249565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb float sweep = (float) (Math.PI - 2 * emptyangle) / mCounts[level]; 250565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb float angle = emptyangle + sweep / 2; 25180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mPath = makeSlice(getDegrees(0) - gap, getDegrees(sweep) + gap, outer, inner, mCenter); 25280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb for (PieItem item : mCurrentItems) { 2530860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (item.getLevel() == level) { 2540860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb View view = item.getView(); 25580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (view != null) { 25680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb view.measure(view.getLayoutParams().width, 25780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb view.getLayoutParams().height); 25880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb int w = view.getMeasuredWidth(); 25980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb int h = view.getMeasuredHeight(); 26080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb int r = inner + (outer - inner) * 2 / 3; 26180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb int x = (int) (r * Math.sin(angle)); 26280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb int y = mCenter.y - (int) (r * Math.cos(angle)) - h / 2; 26380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (onTheLeft()) { 26480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb x = mCenter.x + x - w / 2; 26580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } else { 26680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb x = mCenter.x - x - w / 2; 26780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 26880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb view.layout(x, y, x + w, y + h); 2690860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb } 2700860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb float itemstart = angle - sweep / 2; 27180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb item.setGeometry(itemstart, sweep, inner, outer); 2720860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb angle += sweep; 273376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 274376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 2750860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb inner += mRadiusInc; 2760860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb outer += mRadiusInc; 277376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 278376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 279376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 280565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb 281565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb /** 282565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb * converts a 283565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb * 284565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb * @param angle from 0..PI to Android degrees (clockwise starting at 3 285565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb * o'clock) 286565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb * @return skia angle 287565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb */ 288565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private float getDegrees(double angle) { 289565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb return (float) (270 - 180 * angle / Math.PI); 290565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb } 291565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb 2920860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb @Override 2930860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb protected void onDraw(Canvas canvas) { 2940860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (mOpen) { 295565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int state; 296716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb if (mUseBackground) { 297716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb int w = mBackground.getIntrinsicWidth(); 298716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb int h = mBackground.getIntrinsicHeight(); 299716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb int left = mCenter.x - w; 300716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb int top = mCenter.y - h / 2; 301716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb mBackground.setBounds(left, top, left + w, top + h); 302716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb state = canvas.save(); 303716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb if (onTheLeft()) { 304716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb canvas.scale(-1, 1); 305716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb } 306716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb mBackground.draw(canvas); 307716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb canvas.restoreToCount(state); 308376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 30980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb // draw base menu 31080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb PieItem last = mCurrentItem; 31180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (mOpenItem != null) { 31280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb last = mOpenItem; 31380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 31480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb for (PieItem item : mCurrentItems) { 31580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (item != last) { 31680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb drawItem(canvas, item); 317565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb } 31880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 31980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (last != null) { 32080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb drawItem(canvas, last); 321376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 3221acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb if (mPieView != null) { 3231acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb mPieView.draw(canvas); 3241acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 325376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 3262a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb } 3272a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 3280860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private void drawItem(Canvas canvas, PieItem item) { 32980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (item.getView() != null) { 33080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb Paint p = item.isSelected() ? mSelectedPaint : mNormalPaint; 33180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (!mItems.contains(item)) { 33280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb p = item.isSelected() ? mSelectedPaint : mSubPaint; 33380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 33480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb int state = canvas.save(); 33580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (onTheLeft()) { 33680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb canvas.scale(-1, 1); 33780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 33880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb float r = getDegrees(item.getStartAngle()) - 270; // degrees(0) 33980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb canvas.rotate(r, mCenter.x, mCenter.y); 34080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb canvas.drawPath(mPath, p); 34180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb canvas.restoreToCount(state); 34280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb // draw the item view 34380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb View view = item.getView(); 34480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb state = canvas.save(); 34580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb canvas.translate(view.getX(), view.getY()); 34680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb view.draw(canvas); 34780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb canvas.restoreToCount(state); 34880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 349565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb } 350565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb 351565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private Path makeSlice(float start, float end, int outer, int inner, Point center) { 352565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb RectF bb = 353565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb new RectF(center.x - outer, center.y - outer, center.x + outer, 354565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb center.y + outer); 355565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb RectF bbi = 356565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb new RectF(center.x - inner, center.y - inner, center.x + inner, 357565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb center.y + inner); 358565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb Path path = new Path(); 359565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb path.arcTo(bb, start, end - start, true); 360565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb path.arcTo(bbi, end, start - end); 361565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb path.close(); 362565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb return path; 363565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb } 364565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb 365376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // touch handling for pie 366376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 367376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb @Override 368376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public boolean onTouchEvent(MotionEvent evt) { 369376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float x = evt.getX(); 370376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float y = evt.getY(); 371376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int action = evt.getActionMasked(); 372376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (MotionEvent.ACTION_DOWN == action) { 373376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if ((x > getWidth() - mSlop) || (x < mSlop)) { 374376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setCenter((int) x, (int) y); 375376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb show(true); 376376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return true; 377376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 378376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (MotionEvent.ACTION_UP == action) { 379376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 3801acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb boolean handled = false; 3811acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb if (mPieView != null) { 3821acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb handled = mPieView.onTouchEvent(evt); 3831acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 3840860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb PieItem item = mCurrentItem; 38580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (!mAnimating) { 38680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb deselect(); 38780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 388376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb show(false); 389f6504f863a407c9b4615ac14de9b623775f65f69Michael Kolb if (!handled && (item != null) && (item.getView() != null)) { 390f6504f863a407c9b4615ac14de9b623775f65f69Michael Kolb if ((item == mOpenItem) || !mAnimating) { 391f6504f863a407c9b4615ac14de9b623775f65f69Michael Kolb item.getView().performClick(); 392f6504f863a407c9b4615ac14de9b623775f65f69Michael Kolb } 3930860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb } 394376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return true; 395376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 396376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (MotionEvent.ACTION_CANCEL == action) { 397376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 398376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb show(false); 399376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 40080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (!mAnimating) { 40180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb deselect(); 40280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb invalidate(); 40380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 404376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return false; 405376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (MotionEvent.ACTION_MOVE == action) { 40680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (mAnimating) return false; 4070860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb boolean handled = false; 408bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb PointF polar = getPolar(x, y); 4090860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb int maxr = mRadius + mLevels * mRadiusInc + 50; 4101acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb if (mPieView != null) { 4111acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb handled = mPieView.onTouchEvent(evt); 4121acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 4131acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb if (handled) { 4141acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb invalidate(); 4151acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb return false; 4161acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 41780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (polar.y < mRadius) { 41880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (mOpenItem != null) { 41980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb closeSub(); 42080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } else if (!mAnimating) { 42180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb deselect(); 42280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb invalidate(); 42380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 42480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb return false; 42580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 4260860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (polar.y > maxr) { 427bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb deselect(); 4280860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb show(false); 4294be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck evt.setAction(MotionEvent.ACTION_DOWN); 4304be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck if (getParent() != null) { 4314be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck ((ViewGroup) getParent()).dispatchTouchEvent(evt); 4324be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck } 433bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb return false; 434bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb } 4350860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb PieItem item = findItem(polar); 43680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (item == null) { 43780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } else if (mCurrentItem != item) { 4380860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb onEnter(item); 43980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if ((item != null) && item.isPieView() && (item.getView() != null)) { 4401acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb int cx = item.getView().getLeft() + (onTheLeft() 4411acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb ? item.getView().getWidth() : 0); 4421acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb int cy = item.getView().getTop(); 4431acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb mPieView = item.getPieView(); 444565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb layoutPieView(mPieView, cx, cy, 445565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb (item.getStartAngle() + item.getSweep()) / 2); 4461acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 447376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb invalidate(); 448376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 449376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 450376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // always re-dispatch event 451376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return false; 452376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 453376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 454565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private void layoutPieView(PieView pv, int x, int y, float angle) { 455a4befac241ec69791fd30e11355ed3dc10d7fb37Michael Kolb pv.layout(x, y, onTheLeft(), angle, getHeight()); 4561acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 4571acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb 458376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 459376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * enter a slice for a view 460376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * updates model only 4610860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb * @param item 462376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 4630860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private void onEnter(PieItem item) { 464376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // deselect 4650860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (mCurrentItem != null) { 4660860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mCurrentItem.setSelected(false); 467376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 4680860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (item != null) { 469376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // clear up stack 4702a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb playSoundEffect(SoundEffectConstants.CLICK); 4710860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb item.setSelected(true); 4721acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb mPieView = null; 47380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mCurrentItem = item; 47480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if ((mCurrentItem != mOpenItem) && mCurrentItem.hasItems()) { 47580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb openSub(mCurrentItem); 47680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mOpenItem = item; 47780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 47880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } else { 47980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mCurrentItem = null; 480376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 48180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 48280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 48380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 48480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private void animateOut(final PieItem fixed, AnimatorListener listener) { 48580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if ((mCurrentItems == null) || (fixed == null)) return; 48680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb final float target = fixed.getStartAngle(); 48780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb ValueAnimator anim = ValueAnimator.ofFloat(0, 1); 48880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb anim.addUpdateListener(new AnimatorUpdateListener() { 48980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb @Override 49080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb public void onAnimationUpdate(ValueAnimator animation) { 49180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb for (PieItem item : mCurrentItems) { 49280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (item != fixed) { 49380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb item.setAnimationAngle(animation.getAnimatedFraction() 49480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb * (target - item.getStart())); 49580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 49680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 49780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb invalidate(); 49880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 49980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb }); 50080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb anim.setDuration(ANIMATION); 50180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb anim.addListener(listener); 50280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb anim.start(); 50380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 50480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 50580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private void animateIn(final PieItem fixed, AnimatorListener listener) { 50680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if ((mCurrentItems == null) || (fixed == null)) return; 50780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb final float target = fixed.getStartAngle(); 50880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb ValueAnimator anim = ValueAnimator.ofFloat(0, 1); 50980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb anim.addUpdateListener(new AnimatorUpdateListener() { 51080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb @Override 51180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb public void onAnimationUpdate(ValueAnimator animation) { 51280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb for (PieItem item : mCurrentItems) { 51380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (item != fixed) { 51480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb item.setAnimationAngle((1 - animation.getAnimatedFraction()) 51580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb * (target - item.getStart())); 51680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 51780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 51880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb invalidate(); 51980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 52080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 52180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 52280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb }); 52380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb anim.setDuration(ANIMATION); 52480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb anim.addListener(listener); 52580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb anim.start(); 52680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 52780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 52880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private void openSub(final PieItem item) { 52980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mAnimating = true; 53080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb animateOut(item, new AnimatorListenerAdapter() { 53180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb public void onAnimationEnd(Animator a) { 53280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb for (PieItem item : mCurrentItems) { 53380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb item.setAnimationAngle(0); 53480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 53580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mCurrentItems = new ArrayList<PieItem>(mItems.size()); 53680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb int i = 0, j = 0; 53780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb while (i < mItems.size()) { 53880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (mItems.get(i) == item) { 53980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mCurrentItems.add(item); 54080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } else { 54180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mCurrentItems.add(item.getItems().get(j++)); 54280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 54380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb i++; 54480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 54580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb layoutPie(); 54680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb animateIn(item, new AnimatorListenerAdapter() { 54780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb public void onAnimationEnd(Animator a) { 54880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb for (PieItem item : mCurrentItems) { 54980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb item.setAnimationAngle(0); 55080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 55180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mAnimating = false; 55280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 55380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb }); 55480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 55580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb }); 55680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 55780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 55880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private void closeSub() { 55980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mAnimating = true; 56080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (mCurrentItem != null) { 56180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mCurrentItem.setSelected(false); 56280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 56380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb animateOut(mOpenItem, new AnimatorListenerAdapter() { 56480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb public void onAnimationEnd(Animator a) { 56580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb for (PieItem item : mCurrentItems) { 56680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb item.setAnimationAngle(0); 56780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 56880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mCurrentItems = mItems; 56980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mPieView = null; 57080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb animateIn(mOpenItem, new AnimatorListenerAdapter() { 57180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb public void onAnimationEnd(Animator a) { 57280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb for (PieItem item : mCurrentItems) { 57380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb item.setAnimationAngle(0); 57480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 57580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mAnimating = false; 57680f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mOpenItem = null; 57780f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mCurrentItem = null; 57880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 57980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb }); 58080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 58180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb }); 582376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 583376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 584376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void deselect() { 5850860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (mCurrentItem != null) { 5860860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mCurrentItem.setSelected(false); 587376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 58880f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (mOpenItem != null) { 58980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mOpenItem = null; 59080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb mCurrentItems = mItems; 59180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 5920860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mCurrentItem = null; 5931acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb mPieView = null; 594376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 595376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 596bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb private PointF getPolar(float x, float y) { 597bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb PointF res = new PointF(); 598376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // get angle and radius from x/y 599bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.x = (float) Math.PI / 2; 600376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb x = mCenter.x - x; 601376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mCenter.x < mSlop) { 602376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb x = -x; 603376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 604376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb y = mCenter.y - y; 605bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.y = (float) Math.sqrt(x * x + y * y); 606376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (y > 0) { 607bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.x = (float) Math.asin(x / res.y); 608376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (y < 0) { 609bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.x = (float) (Math.PI - Math.asin(x / res.y )); 610376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 611bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb return res; 612bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb } 613bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb 614bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb /** 615bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb * 616bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb * @param polar x: angle, y: dist 6170860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb * @return the item at angle/dist or null 618bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb */ 6190860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private PieItem findItem(PointF polar) { 620376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // find the matching item: 62180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb for (PieItem item : mCurrentItems) { 62280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb if (inside(polar, mTouchOffset, item)) { 6230860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb return item; 624376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 625376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 626376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return null; 627376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 628376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 62980f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb private boolean inside(PointF polar, float offset, PieItem item) { 63080f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb return (item.getInnerRadius() - offset < polar.y) 63180f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb && (item.getOuterRadius() - offset > polar.y) 63280f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb && (item.getStartAngle() < polar.x) 63380f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb && (item.getStartAngle() + item.getSweep() > polar.x); 63480f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb } 63580f750852edf43c6b09e5633f2c6ebdb2ed94a5bMichael Kolb 636376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb} 637