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