PieRenderer.java revision 7feb060e24b2b5fed416d075facc6e2c8fce23e9
11373b98135b2854578111dd535e24d39c8853f14Michael Kolb/*
21373b98135b2854578111dd535e24d39c8853f14Michael Kolb * Copyright (C) 2012 The Android Open Source Project
31373b98135b2854578111dd535e24d39c8853f14Michael Kolb *
41373b98135b2854578111dd535e24d39c8853f14Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License");
51373b98135b2854578111dd535e24d39c8853f14Michael Kolb * you may not use this file except in compliance with the License.
61373b98135b2854578111dd535e24d39c8853f14Michael Kolb * You may obtain a copy of the License at
71373b98135b2854578111dd535e24d39c8853f14Michael Kolb *
81373b98135b2854578111dd535e24d39c8853f14Michael Kolb *      http://www.apache.org/licenses/LICENSE-2.0
91373b98135b2854578111dd535e24d39c8853f14Michael Kolb *
101373b98135b2854578111dd535e24d39c8853f14Michael Kolb * Unless required by applicable law or agreed to in writing, software
111373b98135b2854578111dd535e24d39c8853f14Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS,
121373b98135b2854578111dd535e24d39c8853f14Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131373b98135b2854578111dd535e24d39c8853f14Michael Kolb * See the License for the specific language governing permissions and
141373b98135b2854578111dd535e24d39c8853f14Michael Kolb * limitations under the License.
151373b98135b2854578111dd535e24d39c8853f14Michael Kolb */
161373b98135b2854578111dd535e24d39c8853f14Michael Kolb
171373b98135b2854578111dd535e24d39c8853f14Michael Kolbpackage com.android.camera.ui;
181373b98135b2854578111dd535e24d39c8853f14Michael Kolb
191a9a41b66187eac7ff59f22a50de0d9d9403e4ceMichael Kolbimport android.animation.Animator;
201a9a41b66187eac7ff59f22a50de0d9d9403e4ceMichael Kolbimport android.animation.AnimatorListenerAdapter;
211373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.content.Context;
221373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.content.res.Resources;
231373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.Canvas;
241373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.Color;
251373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.Paint;
261373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.Path;
271373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.Point;
281373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.PointF;
291373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.RectF;
301373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.os.Handler;
311373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.os.Message;
321373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.view.MotionEvent;
331373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.view.View;
34e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolbimport android.view.ViewConfiguration;
35c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolbimport android.view.animation.Animation;
36bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolbimport android.view.animation.Animation.AnimationListener;
37bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolbimport android.view.animation.LinearInterpolator;
38c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolbimport android.view.animation.Transformation;
391373b98135b2854578111dd535e24d39c8853f14Michael Kolb
401373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport com.android.camera.R;
411a9a41b66187eac7ff59f22a50de0d9d9403e4ceMichael Kolbimport com.android.gallery3d.common.ApiHelper;
421373b98135b2854578111dd535e24d39c8853f14Michael Kolb
431373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport java.util.ArrayList;
441373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport java.util.List;
451373b98135b2854578111dd535e24d39c8853f14Michael Kolb
468042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolbpublic class PieRenderer extends OverlayRenderer
478042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        implements FocusIndicator {
481373b98135b2854578111dd535e24d39c8853f14Michael Kolb
491373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private static final String TAG = "CAM Pie";
501373b98135b2854578111dd535e24d39c8853f14Michael Kolb
518042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    // Sometimes continuous autofocus starts and stops several times quickly.
528042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    // These states are used to make sure the animation is run for at least some
538042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    // time.
548042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mState;
558042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private ScaleAnimation mAnimation = new ScaleAnimation();
568042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int STATE_IDLE = 0;
578042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int STATE_FOCUSING = 1;
588042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int STATE_FINISHING = 2;
598042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int STATE_PIE = 3;
608042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
618042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private Runnable mDisappear = new Disappear();
628042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private Animation.AnimationListener mEndAction = new EndAction();
63673da081181c72352d57c9bf97badb2929370685Michael Kolb    private static final int SCALING_UP_TIME = 600;
64673da081181c72352d57c9bf97badb2929370685Michael Kolb    private static final int SCALING_DOWN_TIME = 100;
658042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int DISAPPEAR_TIMEOUT = 200;
668042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int DIAL_HORIZONTAL = 157;
678042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
68e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb    private static final long PIE_FADE_IN_DURATION = 200;
69673da081181c72352d57c9bf97badb2929370685Michael Kolb    private static final long FOCUS_TAP_TIMEOUT = 500;
70e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb    private static final long PIE_SELECT_FADE_DURATION = 300;
711373b98135b2854578111dd535e24d39c8853f14Michael Kolb
721373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private static final int MSG_OPEN = 2;
731373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private static final int MSG_CLOSE = 3;
74e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb    private static final int MSG_FOCUS_TAP = 4;
751373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private static final float PIE_SWEEP = (float)(Math.PI * 2 / 3);
761373b98135b2854578111dd535e24d39c8853f14Michael Kolb    // geometry
771373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private Point mCenter;
781373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private int mRadius;
791373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private int mRadiusInc;
808042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
811373b98135b2854578111dd535e24d39c8853f14Michael Kolb    // the detection if touch is inside a slice is offset
821373b98135b2854578111dd535e24d39c8853f14Michael Kolb    // inbounds by this amount to allow the selection to show before the
831373b98135b2854578111dd535e24d39c8853f14Michael Kolb    // finger covers it
841373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private int mTouchOffset;
851373b98135b2854578111dd535e24d39c8853f14Michael Kolb
861373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private List<PieItem> mItems;
871373b98135b2854578111dd535e24d39c8853f14Michael Kolb
881373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private PieItem mOpenItem;
891373b98135b2854578111dd535e24d39c8853f14Michael Kolb
901373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private Paint mSelectedPaint;
911373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private Paint mSubPaint;
921373b98135b2854578111dd535e24d39c8853f14Michael Kolb
931373b98135b2854578111dd535e24d39c8853f14Michael Kolb    // touch handling
941373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private PieItem mCurrentItem;
951373b98135b2854578111dd535e24d39c8853f14Michael Kolb
968042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private Paint mFocusPaint;
97673da081181c72352d57c9bf97badb2929370685Michael Kolb    private int mSuccessColor;
98673da081181c72352d57c9bf97badb2929370685Michael Kolb    private int mFailColor;
998042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mCircleSize;
1008042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mFocusX;
1018042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mFocusY;
1028042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mCenterX;
1038042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mCenterY;
1048042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
1058042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mDialAngle;
1068042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private RectF mCircle;
1078042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private RectF mDial;
1088042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private Point mPoint1;
1098042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private Point mPoint2;
1108042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mStartAnimationAngle;
1118042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private boolean mFocused;
1128042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mInnerOffset;
1138042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mOuterStroke;
1148042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mInnerStroke;
115a0dd52298b55357a711663180677efa39d35e5abMichael Kolb    private boolean mFocusFromTap;
11655f0d13040df45c75704df9d633b444cb36f2465Michael Kolb    private boolean mTapMode;
117fe44832abc2e9fb937ac5bd9c65e103397992e8cMichael Kolb    private boolean mBlockFocus;
118e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb    private int mTouchSlopSquared;
119e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb    private Point mDown;
120e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb    private boolean mOpening;
121bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb    private LinearAnimation mXFade;
1227feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb    private LinearAnimation mFadeIn;
1238042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
1241373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private Handler mHandler = new Handler() {
1251373b98135b2854578111dd535e24d39c8853f14Michael Kolb        public void handleMessage(Message msg) {
1261373b98135b2854578111dd535e24d39c8853f14Michael Kolb            switch(msg.what) {
1271373b98135b2854578111dd535e24d39c8853f14Michael Kolb            case MSG_OPEN:
12863e79531bd35bd5bc1fc11cbc1f93acb6b9c7b0bMichael Kolb                if (mListener != null) {
1291373b98135b2854578111dd535e24d39c8853f14Michael Kolb                    mListener.onPieOpened(mCenter.x, mCenter.y);
1301373b98135b2854578111dd535e24d39c8853f14Michael Kolb                }
1311373b98135b2854578111dd535e24d39c8853f14Michael Kolb                break;
1321373b98135b2854578111dd535e24d39c8853f14Michael Kolb            case MSG_CLOSE:
13363e79531bd35bd5bc1fc11cbc1f93acb6b9c7b0bMichael Kolb                if (mListener != null) {
1341373b98135b2854578111dd535e24d39c8853f14Michael Kolb                    mListener.onPieClosed();
1351373b98135b2854578111dd535e24d39c8853f14Michael Kolb                }
1361373b98135b2854578111dd535e24d39c8853f14Michael Kolb                break;
137a0dd52298b55357a711663180677efa39d35e5abMichael Kolb            case MSG_FOCUS_TAP:
138a0dd52298b55357a711663180677efa39d35e5abMichael Kolb                // reset flag
13955f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                mTapMode = false;
14055f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                if (mState == STATE_PIE) {
14155f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                    show(false);
14255f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                }
143a0dd52298b55357a711663180677efa39d35e5abMichael Kolb                break;
1441373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
1451373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
1461373b98135b2854578111dd535e24d39c8853f14Michael Kolb    };
1471373b98135b2854578111dd535e24d39c8853f14Michael Kolb
1481373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private PieListener mListener;
1491373b98135b2854578111dd535e24d39c8853f14Michael Kolb
1501373b98135b2854578111dd535e24d39c8853f14Michael Kolb    static public interface PieListener {
1511373b98135b2854578111dd535e24d39c8853f14Michael Kolb        public void onPieOpened(int centerX, int centerY);
1521373b98135b2854578111dd535e24d39c8853f14Michael Kolb        public void onPieClosed();
1531373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
1541373b98135b2854578111dd535e24d39c8853f14Michael Kolb
1551373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public void setPieListener(PieListener pl) {
1561373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mListener = pl;
1571373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
1581373b98135b2854578111dd535e24d39c8853f14Michael Kolb
1591373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public PieRenderer(Context context) {
1601373b98135b2854578111dd535e24d39c8853f14Michael Kolb        init(context);
1611373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
162e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb
1631373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void init(Context ctx) {
164ad639dff251233032734106afe4fe51a52be3721Michael Kolb        setVisible(false);
1651373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mItems = new ArrayList<PieItem>();
1661373b98135b2854578111dd535e24d39c8853f14Michael Kolb        Resources res = ctx.getResources();
167a521253223897b099995e0af08982b4236ce5861Michael Kolb        mRadius = (int) res.getDimensionPixelSize(R.dimen.pie_radius_start);
168eede360bc53b9eb7192b7117447622d6d7a5d220Michael Kolb        mCircleSize = mRadius - res.getDimensionPixelSize(R.dimen.focus_radius_offset);
169a521253223897b099995e0af08982b4236ce5861Michael Kolb        mRadiusInc =  (int) res.getDimensionPixelSize(R.dimen.pie_radius_increment);
170a521253223897b099995e0af08982b4236ce5861Michael Kolb        mTouchOffset = (int) res.getDimensionPixelSize(R.dimen.pie_touch_offset);
1711373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mCenter = new Point(0,0);
1721373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mSelectedPaint = new Paint();
173673da081181c72352d57c9bf97badb2929370685Michael Kolb        mSelectedPaint.setColor(Color.argb(255, 51, 181, 229));
1741373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mSelectedPaint.setAntiAlias(true);
1751373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mSubPaint = new Paint();
1761373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mSubPaint.setAntiAlias(true);
177673da081181c72352d57c9bf97badb2929370685Michael Kolb        mSubPaint.setColor(Color.argb(200, 250, 230, 128));
1788042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusPaint = new Paint();
1798042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusPaint.setAntiAlias(true);
1808042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusPaint.setColor(Color.WHITE);
1818042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusPaint.setStyle(Paint.Style.STROKE);
182673da081181c72352d57c9bf97badb2929370685Michael Kolb        mSuccessColor = Color.GREEN;
183673da081181c72352d57c9bf97badb2929370685Michael Kolb        mFailColor = Color.RED;
1848042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCircle = new RectF();
1858042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDial = new RectF();
1868042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mPoint1 = new Point();
1878042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mPoint2 = new Point();
1888042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mInnerOffset = res.getDimensionPixelSize(R.dimen.focus_inner_offset);
1898042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mOuterStroke = res.getDimensionPixelSize(R.dimen.focus_outer_stroke);
1908042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mInnerStroke = res.getDimensionPixelSize(R.dimen.focus_inner_stroke);
191a0dd52298b55357a711663180677efa39d35e5abMichael Kolb        mState = STATE_IDLE;
192fe44832abc2e9fb937ac5bd9c65e103397992e8cMichael Kolb        mBlockFocus = false;
193e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb        mTouchSlopSquared = ViewConfiguration.get(ctx).getScaledTouchSlop();
194e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb        mTouchSlopSquared = mTouchSlopSquared * mTouchSlopSquared;
195e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb        mDown = new Point();
196a0dd52298b55357a711663180677efa39d35e5abMichael Kolb    }
1978042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
198673da081181c72352d57c9bf97badb2929370685Michael Kolb    public boolean showsItems() {
19955f0d13040df45c75704df9d633b444cb36f2465Michael Kolb        return mTapMode;
200673da081181c72352d57c9bf97badb2929370685Michael Kolb    }
201673da081181c72352d57c9bf97badb2929370685Michael Kolb
2021373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public void addItem(PieItem item) {
2031373b98135b2854578111dd535e24d39c8853f14Michael Kolb        // add the item to the pie itself
2041373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mItems.add(item);
2051373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
2061373b98135b2854578111dd535e24d39c8853f14Michael Kolb
2071373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public void removeItem(PieItem item) {
2081373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mItems.remove(item);
2091373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
2101373b98135b2854578111dd535e24d39c8853f14Michael Kolb
2111373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public void clearItems() {
2121373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mItems.clear();
2131373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
2141373b98135b2854578111dd535e24d39c8853f14Michael Kolb
215a5e23d9eb2bc96e7da4c021df2e11c1c43d379cdMichael Kolb    public void showInCenter() {
216d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb        if ((mState == STATE_PIE) && isVisible()) {
217d560a87ef69c81e0b313898f0d673c57d89ff740Michael Kolb            mTapMode = false;
218d560a87ef69c81e0b313898f0d673c57d89ff740Michael Kolb            show(false);
219d560a87ef69c81e0b313898f0d673c57d89ff740Michael Kolb        } else {
220d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb            if (mState != STATE_IDLE) {
221d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb                mHandler.removeMessages(MSG_FOCUS_TAP);
222d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb                cancelFocus();
223d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb            }
224d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb            mState = STATE_PIE;
225d560a87ef69c81e0b313898f0d673c57d89ff740Michael Kolb            setCenter(mCenterX, mCenterY);
226d560a87ef69c81e0b313898f0d673c57d89ff740Michael Kolb            mTapMode = true;
227d560a87ef69c81e0b313898f0d673c57d89ff740Michael Kolb            show(true);
228d560a87ef69c81e0b313898f0d673c57d89ff740Michael Kolb        }
229a5e23d9eb2bc96e7da4c021df2e11c1c43d379cdMichael Kolb    }
230a5e23d9eb2bc96e7da4c021df2e11c1c43d379cdMichael Kolb
23169853ec0cefd7efae87db0f81947f8789c42249dMichael Kolb    public void hide() {
23269853ec0cefd7efae87db0f81947f8789c42249dMichael Kolb        show(false);
23369853ec0cefd7efae87db0f81947f8789c42249dMichael Kolb    }
23469853ec0cefd7efae87db0f81947f8789c42249dMichael Kolb
2351373b98135b2854578111dd535e24d39c8853f14Michael Kolb    /**
2361373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * guaranteed has center set
2371373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * @param show
2381373b98135b2854578111dd535e24d39c8853f14Michael Kolb     */
2391373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void show(boolean show) {
2401373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (show) {
2418042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mState = STATE_PIE;
2421373b98135b2854578111dd535e24d39c8853f14Michael Kolb            // ensure clean state
2431373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mCurrentItem = null;
2441373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mOpenItem = null;
2451373b98135b2854578111dd535e24d39c8853f14Michael Kolb            for (PieItem item : mItems) {
2461373b98135b2854578111dd535e24d39c8853f14Michael Kolb                item.setSelected(false);
2471373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
2481373b98135b2854578111dd535e24d39c8853f14Michael Kolb            layoutPie();
2491a9a41b66187eac7ff59f22a50de0d9d9403e4ceMichael Kolb            fadeIn();
250b8c5a15af9e8f1adb1849072a6f5a1830887b43eMichael Kolb        } else {
251b8c5a15af9e8f1adb1849072a6f5a1830887b43eMichael Kolb            mState = STATE_IDLE;
252fd187ac6cfd6129882ae0bcea1034f4d18fcdff8Michael Kolb            mTapMode = false;
253bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            if (mXFade != null) {
254bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                mXFade.cancel();
255bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            }
2561373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
257ad639dff251233032734106afe4fe51a52be3721Michael Kolb        setVisible(show);
258ad639dff251233032734106afe4fe51a52be3721Michael Kolb        mHandler.sendEmptyMessage(show ? MSG_OPEN : MSG_CLOSE);
2594d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb    }
2604d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb
2611a9a41b66187eac7ff59f22a50de0d9d9403e4ceMichael Kolb    private void fadeIn() {
2627feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        mFadeIn = new LinearAnimation(0, 1);
2637feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        mFadeIn.setDuration(PIE_FADE_IN_DURATION);
2647feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        mFadeIn.setAnimationListener(new AnimationListener() {
2657feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            @Override
2667feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            public void onAnimationStart(Animation animation) {
2677feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            }
2687feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb
2697feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            @Override
2707feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            public void onAnimationEnd(Animation animation) {
2717feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                mFadeIn = null;
2727feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            }
2737feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb
2747feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            @Override
2757feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            public void onAnimationRepeat(Animation animation) {
2767feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            }
2777feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        });
2787feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        mFadeIn.startNow();
2797feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        mOverlay.startAnimation(mFadeIn);
2801a9a41b66187eac7ff59f22a50de0d9d9403e4ceMichael Kolb    }
2811a9a41b66187eac7ff59f22a50de0d9d9403e4ceMichael Kolb
282c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb    public void setCenter(int x, int y) {
2831373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mCenter.x = x;
2841373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mCenter.y = y;
2858042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        // when using the pie menu, align the focus ring
2868042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        alignFocus(x, y);
2878042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
2888042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
2898042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private void setupPie(int x, int y) {
2908042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        // when using the focus ring, align pie items
2918042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCenter.x = x;
2928042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCenter.y = y;
2938042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCurrentItem = null;
2948042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mOpenItem = null;
2958042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        for (PieItem item : mItems) {
2968042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            item.setSelected(false);
2978042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
2988042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        layoutPie();
2991373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
3001373b98135b2854578111dd535e24d39c8853f14Michael Kolb
3011373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void layoutPie() {
3021373b98135b2854578111dd535e24d39c8853f14Michael Kolb        int rgap = 2;
3031373b98135b2854578111dd535e24d39c8853f14Michael Kolb        int inner = mRadius + rgap;
3041373b98135b2854578111dd535e24d39c8853f14Michael Kolb        int outer = mRadius + mRadiusInc - rgap;
3051373b98135b2854578111dd535e24d39c8853f14Michael Kolb        int gap = 1;
3061373b98135b2854578111dd535e24d39c8853f14Michael Kolb        layoutItems(mItems, (float) (Math.PI / 2), inner, outer, gap);
3071373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
3081373b98135b2854578111dd535e24d39c8853f14Michael Kolb
3094d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb    private void layoutItems(List<PieItem> items, float centerAngle, int inner,
3104d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            int outer, int gap) {
3111373b98135b2854578111dd535e24d39c8853f14Michael Kolb        float emptyangle = PIE_SWEEP / 16;
3121373b98135b2854578111dd535e24d39c8853f14Michael Kolb        float sweep = (float) (PIE_SWEEP - 2 * emptyangle) / items.size();
3131373b98135b2854578111dd535e24d39c8853f14Michael Kolb        float angle = centerAngle - PIE_SWEEP / 2 + emptyangle + sweep / 2;
314b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb        // check if we have custom geometry
315b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb        // first item we find triggers custom sweep for all
316b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb        // this allows us to re-use the path
317b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb        for (PieItem item : items) {
318b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb            if (item.getCenter() >= 0) {
319b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb                sweep = item.getSweep();
320b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb                break;
321b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb            }
322b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb        }
3234d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb        Path path = makeSlice(getDegrees(0) - gap, getDegrees(sweep) + gap,
3244d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                outer, inner, mCenter);
3251373b98135b2854578111dd535e24d39c8853f14Michael Kolb        for (PieItem item : items) {
3261373b98135b2854578111dd535e24d39c8853f14Michael Kolb            // shared between items
3271373b98135b2854578111dd535e24d39c8853f14Michael Kolb            item.setPath(path);
3281373b98135b2854578111dd535e24d39c8853f14Michael Kolb            View view = item.getView();
329b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb            if (item.getCenter() >= 0) {
330b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb                angle = item.getCenter();
331b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb            }
3321373b98135b2854578111dd535e24d39c8853f14Michael Kolb            if (view != null) {
3331373b98135b2854578111dd535e24d39c8853f14Michael Kolb                view.measure(view.getLayoutParams().width,
3341373b98135b2854578111dd535e24d39c8853f14Michael Kolb                        view.getLayoutParams().height);
3351373b98135b2854578111dd535e24d39c8853f14Michael Kolb                int w = view.getMeasuredWidth();
3361373b98135b2854578111dd535e24d39c8853f14Michael Kolb                int h = view.getMeasuredHeight();
3371373b98135b2854578111dd535e24d39c8853f14Michael Kolb                // move views to outer border
3381373b98135b2854578111dd535e24d39c8853f14Michael Kolb                int r = inner + (outer - inner) * 2 / 3;
3391373b98135b2854578111dd535e24d39c8853f14Michael Kolb                int x = (int) (r * Math.cos(angle));
3401373b98135b2854578111dd535e24d39c8853f14Michael Kolb                int y = mCenter.y - (int) (r * Math.sin(angle)) - h / 2;
3411373b98135b2854578111dd535e24d39c8853f14Michael Kolb                x = mCenter.x + x - w / 2;
3421373b98135b2854578111dd535e24d39c8853f14Michael Kolb                view.layout(x, y, x + w, y + h);
3431373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
3441373b98135b2854578111dd535e24d39c8853f14Michael Kolb            float itemstart = angle - sweep / 2;
3451373b98135b2854578111dd535e24d39c8853f14Michael Kolb            item.setGeometry(itemstart, sweep, inner, outer);
3461373b98135b2854578111dd535e24d39c8853f14Michael Kolb            if (item.hasItems()) {
3474d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                layoutItems(item.getItems(), angle, inner,
3484d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                        outer + mRadiusInc / 2, gap);
3491373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
3501373b98135b2854578111dd535e24d39c8853f14Michael Kolb            angle += sweep;
3511373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
3524d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb    }
3531373b98135b2854578111dd535e24d39c8853f14Michael Kolb
3541373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private Path makeSlice(float start, float end, int outer, int inner, Point center) {
3551373b98135b2854578111dd535e24d39c8853f14Michael Kolb        RectF bb =
3561373b98135b2854578111dd535e24d39c8853f14Michael Kolb                new RectF(center.x - outer, center.y - outer, center.x + outer,
3571373b98135b2854578111dd535e24d39c8853f14Michael Kolb                        center.y + outer);
3581373b98135b2854578111dd535e24d39c8853f14Michael Kolb        RectF bbi =
3591373b98135b2854578111dd535e24d39c8853f14Michael Kolb                new RectF(center.x - inner, center.y - inner, center.x + inner,
3601373b98135b2854578111dd535e24d39c8853f14Michael Kolb                        center.y + inner);
3611373b98135b2854578111dd535e24d39c8853f14Michael Kolb        Path path = new Path();
3621373b98135b2854578111dd535e24d39c8853f14Michael Kolb        path.arcTo(bb, start, end - start, true);
3631373b98135b2854578111dd535e24d39c8853f14Michael Kolb        path.arcTo(bbi, end, start - end);
3641373b98135b2854578111dd535e24d39c8853f14Michael Kolb        path.close();
3651373b98135b2854578111dd535e24d39c8853f14Michael Kolb        return path;
3661373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
3671373b98135b2854578111dd535e24d39c8853f14Michael Kolb
3681373b98135b2854578111dd535e24d39c8853f14Michael Kolb    /**
3691373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * converts a
370b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb     * @param angle from 0..PI to Android degrees (clockwise starting at 3 o'clock)
3711373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * @return skia angle
3721373b98135b2854578111dd535e24d39c8853f14Michael Kolb     */
3731373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private float getDegrees(double angle) {
3741373b98135b2854578111dd535e24d39c8853f14Michael Kolb        return (float) (360 - 180 * angle / Math.PI);
3751373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
3761373b98135b2854578111dd535e24d39c8853f14Michael Kolb
3772b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb    private void startFadeOut() {
3782b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb        if (ApiHelper.HAS_VIEW_PROPERTY_ANIMATOR) {
3792b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb            mOverlay.animate().alpha(0).setListener(new AnimatorListenerAdapter() {
3802b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                @Override
3812b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                public void onAnimationEnd(Animator animation) {
3822b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                    deselect();
3832b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                    show(false);
3842b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                    mOverlay.setAlpha(1);
3852b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                    super.onAnimationEnd(animation);
3862b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                }
387e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb            }).setDuration(PIE_SELECT_FADE_DURATION);
3882b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb        } else {
3892b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb            deselect();
3902b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb            show(false);
3912b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb        }
3922b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb    }
3932b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb
3941373b98135b2854578111dd535e24d39c8853f14Michael Kolb    @Override
3951373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public void onDraw(Canvas canvas) {
3968042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        drawFocus(canvas);
3978042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        if (mState == STATE_FINISHING) return;
3987feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        float alpha = 1;
3997feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        if (mXFade != null) {
4007feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            alpha = mXFade.getValue();
4017feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        } else if (mFadeIn != null) {
4027feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            alpha = mFadeIn.getValue();
4037feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        }
404bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        if ((mOpenItem == null) || (mXFade != null)) {
405ad639dff251233032734106afe4fe51a52be3721Michael Kolb            // draw base menu
406ad639dff251233032734106afe4fe51a52be3721Michael Kolb            for (PieItem item : mItems) {
407bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                drawItem(canvas, item, alpha);
408ad639dff251233032734106afe4fe51a52be3721Michael Kolb            }
409bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        }
410bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        if (mOpenItem != null) {
411ad639dff251233032734106afe4fe51a52be3721Michael Kolb            for (PieItem inner : mOpenItem.getItems()) {
412bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                drawItem(canvas, inner, (mXFade != null) ? (1 - 0.5f * alpha) : 1);
4131373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
4141373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
4151373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
4161373b98135b2854578111dd535e24d39c8853f14Michael Kolb
417bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb    private void drawItem(Canvas canvas, PieItem item, float alpha) {
418d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb        if ((mState == STATE_PIE) && (item.getView() != null)) {
419d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb            if (item.getPath() != null) {
4207feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                int state = -1;
4217feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                if (item.isSelected()) {
4227feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                    Paint p = mSelectedPaint;
4237feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                    state = canvas.save();
4247feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                    float r = getDegrees(item.getStartAngle());
4257feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                    canvas.rotate(r, mCenter.x, mCenter.y);
4267feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                    canvas.drawPath(item.getPath(), p);
4277feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                    canvas.restoreToCount(state);
4287feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                }
429d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb                // draw the item view
430d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb                View view = item.getView();
431d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb                state = canvas.save();
4327feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                if (mFadeIn != null) {
4337feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                    float sf = 0.9f + alpha * 0.1f;
4347feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                    canvas.scale(sf, sf, mCenter.x, mCenter.y);
4357feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                }
436d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb                canvas.translate(view.getX(), view.getY());
4377feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                if (alpha < 1) {
4387feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                    canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), (int)(255 * alpha), 0);
4397feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                }
440d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb                view.draw(canvas);
4417feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                if (alpha < 1) {
4427feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                    canvas.restore();
4437feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb                }
444d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb                canvas.restoreToCount(state);
4458042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            }
4461373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
4471373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
4481373b98135b2854578111dd535e24d39c8853f14Michael Kolb
4491373b98135b2854578111dd535e24d39c8853f14Michael Kolb    @Override
4501373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public boolean onTouchEvent(MotionEvent evt) {
4511373b98135b2854578111dd535e24d39c8853f14Michael Kolb        float x = evt.getX();
4521373b98135b2854578111dd535e24d39c8853f14Michael Kolb        float y = evt.getY();
4531373b98135b2854578111dd535e24d39c8853f14Michael Kolb        int action = evt.getActionMasked();
45455f0d13040df45c75704df9d633b444cb36f2465Michael Kolb        PointF polar = getPolar(x, y, !(mTapMode));
4551373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (MotionEvent.ACTION_DOWN == action) {
456e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb            mDown.x = (int) evt.getX();
457e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb            mDown.y = (int) evt.getY();
458e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb            mOpening = false;
45955f0d13040df45c75704df9d633b444cb36f2465Michael Kolb            if (mTapMode) {
46055f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                PieItem item = findItem(polar);
46155f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                if ((item != null) && (mCurrentItem != item)) {
46255f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                    mHandler.removeMessages(MSG_FOCUS_TAP);
46355f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                    mState = STATE_PIE;
46455f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                    onEnter(item);
46555f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                }
46655f0d13040df45c75704df9d633b444cb36f2465Michael Kolb            } else {
46755f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                setCenter((int) x, (int) y);
46855f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                show(true);
46955f0d13040df45c75704df9d633b444cb36f2465Michael Kolb            }
4701373b98135b2854578111dd535e24d39c8853f14Michael Kolb            return true;
4711373b98135b2854578111dd535e24d39c8853f14Michael Kolb        } else if (MotionEvent.ACTION_UP == action) {
4722b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb            if (isVisible()) {
4732b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                PieItem item = mCurrentItem;
4742b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                if (mTapMode) {
4752b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                    item = findItem(polar);
476e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                    if (item != null && mOpening) {
477e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                        mOpening = false;
478e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                        return true;
479e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                    }
4802b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                }
48155f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                if (item == null) {
48255f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                    mTapMode = false;
4832b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                    show(false);
484e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                } else if (!mOpening
485e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                        && !item.hasItems() && item.getView() != null) {
48663e79531bd35bd5bc1fc11cbc1f93acb6b9c7b0bMichael Kolb                    item.getView().performClick();
4872b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                    startFadeOut();
4882b2e0831c0494ea45fe2d57869a2dc09cefae19eMichael Kolb                    mTapMode = false;
4891373b98135b2854578111dd535e24d39c8853f14Michael Kolb                }
4901373b98135b2854578111dd535e24d39c8853f14Michael Kolb                return true;
4911373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
4921373b98135b2854578111dd535e24d39c8853f14Michael Kolb        } else if (MotionEvent.ACTION_CANCEL == action) {
49355f0d13040df45c75704df9d633b444cb36f2465Michael Kolb            if (isVisible() || mTapMode) {
4941373b98135b2854578111dd535e24d39c8853f14Michael Kolb                show(false);
4951373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
49663e79531bd35bd5bc1fc11cbc1f93acb6b9c7b0bMichael Kolb            deselect();
4971373b98135b2854578111dd535e24d39c8853f14Michael Kolb            return false;
4981373b98135b2854578111dd535e24d39c8853f14Michael Kolb        } else if (MotionEvent.ACTION_MOVE == action) {
4994d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            if (polar.y < mRadius) {
5004d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                if (mOpenItem != null) {
5014d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                    mOpenItem = null;
50263e79531bd35bd5bc1fc11cbc1f93acb6b9c7b0bMichael Kolb                } else {
5031373b98135b2854578111dd535e24d39c8853f14Michael Kolb                    deselect();
5041373b98135b2854578111dd535e24d39c8853f14Michael Kolb                }
5054d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                return false;
5064d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            }
5074d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            PieItem item = findItem(polar);
508e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb            boolean moved = hasMoved(evt);
509e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb            if ((item != null) && (mCurrentItem != item) && (!mOpening || moved)) {
510e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                // only select if we didn't just open or have moved past slop
511e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                mOpening = false;
512e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                if (moved) {
513e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                    // switch back to swipe mode
514e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                    mTapMode = false;
515e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                }
5164d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                onEnter(item);
5171373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
5181373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
5191373b98135b2854578111dd535e24d39c8853f14Michael Kolb        return false;
5201373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
5211373b98135b2854578111dd535e24d39c8853f14Michael Kolb
522e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb    private boolean hasMoved(MotionEvent e) {
523e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb        return mTouchSlopSquared < (e.getX() - mDown.x) * (e.getX() - mDown.x)
524e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                + (e.getY() - mDown.y) * (e.getY() - mDown.y);
525e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb    }
526e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb
5271373b98135b2854578111dd535e24d39c8853f14Michael Kolb    /**
5281373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * enter a slice for a view
5291373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * updates model only
5301373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * @param item
5311373b98135b2854578111dd535e24d39c8853f14Michael Kolb     */
5321373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void onEnter(PieItem item) {
5331373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (mCurrentItem != null) {
5341373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mCurrentItem.setSelected(false);
5351373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
5362d3af28f91481d7f3887cb0bb9c5d06375baf787Michael Kolb        if (item != null && item.isEnabled()) {
5371373b98135b2854578111dd535e24d39c8853f14Michael Kolb            item.setSelected(true);
5381373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mCurrentItem = item;
5391373b98135b2854578111dd535e24d39c8853f14Michael Kolb            if ((mCurrentItem != mOpenItem) && mCurrentItem.hasItems()) {
540e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb                openCurrentItem();
5411373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
5421373b98135b2854578111dd535e24d39c8853f14Michael Kolb        } else {
5431373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mCurrentItem = null;
5441373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
5451373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
5461373b98135b2854578111dd535e24d39c8853f14Michael Kolb
5471373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void deselect() {
5481373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (mCurrentItem != null) {
5491373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mCurrentItem.setSelected(false);
5501373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
5511373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (mOpenItem != null) {
5521373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mOpenItem = null;
5531373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
5541373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mCurrentItem = null;
5551373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
5561373b98135b2854578111dd535e24d39c8853f14Michael Kolb
5571373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void openCurrentItem() {
5581373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if ((mCurrentItem != null) && mCurrentItem.hasItems()) {
55955f0d13040df45c75704df9d633b444cb36f2465Michael Kolb            mCurrentItem.setSelected(false);
5601373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mOpenItem = mCurrentItem;
561e3cf61f8f7d27348fb7d6c7f86fedd1fadecb48eMichael Kolb            mOpening = true;
562bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            mXFade = new LinearAnimation(1, 0);
563bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            mXFade.setDuration(200);
564bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            mXFade.setAnimationListener(new AnimationListener() {
565bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                @Override
566bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                public void onAnimationStart(Animation animation) {
567bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                }
568bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb
569bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                @Override
570bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                public void onAnimationEnd(Animation animation) {
571bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                    mXFade = null;
572bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                }
573bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb
574bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                @Override
575bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                public void onAnimationRepeat(Animation animation) {
576bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb                }
577bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            });
578bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            mXFade.startNow();
579bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            mOverlay.startAnimation(mXFade);
5801373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
5811373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
5821373b98135b2854578111dd535e24d39c8853f14Michael Kolb
583673da081181c72352d57c9bf97badb2929370685Michael Kolb    private PointF getPolar(float x, float y, boolean useOffset) {
5841373b98135b2854578111dd535e24d39c8853f14Michael Kolb        PointF res = new PointF();
5851373b98135b2854578111dd535e24d39c8853f14Michael Kolb        // get angle and radius from x/y
5861373b98135b2854578111dd535e24d39c8853f14Michael Kolb        res.x = (float) Math.PI / 2;
5871373b98135b2854578111dd535e24d39c8853f14Michael Kolb        x = x - mCenter.x;
5881373b98135b2854578111dd535e24d39c8853f14Michael Kolb        y = mCenter.y - y;
589a521253223897b099995e0af08982b4236ce5861Michael Kolb        res.y = (float) Math.sqrt(x * x + y * y);
5901373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (x != 0) {
5911373b98135b2854578111dd535e24d39c8853f14Michael Kolb            res.x = (float) Math.atan2(y,  x);
592b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb            if (res.x < 0) {
593b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb                res.x = (float) (2 * Math.PI + res.x);
594b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb            }
5951373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
596673da081181c72352d57c9bf97badb2929370685Michael Kolb        res.y = res.y + (useOffset ? mTouchOffset : 0);
5971373b98135b2854578111dd535e24d39c8853f14Michael Kolb        return res;
5981373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
5991373b98135b2854578111dd535e24d39c8853f14Michael Kolb
6001373b98135b2854578111dd535e24d39c8853f14Michael Kolb    /**
6011373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * @param polar x: angle, y: dist
6021373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * @return the item at angle/dist or null
6031373b98135b2854578111dd535e24d39c8853f14Michael Kolb     */
6041373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private PieItem findItem(PointF polar) {
6051373b98135b2854578111dd535e24d39c8853f14Michael Kolb        // find the matching item:
6061373b98135b2854578111dd535e24d39c8853f14Michael Kolb        List<PieItem> items = (mOpenItem != null) ? mOpenItem.getItems() : mItems;
6071373b98135b2854578111dd535e24d39c8853f14Michael Kolb        for (PieItem item : items) {
608a521253223897b099995e0af08982b4236ce5861Michael Kolb            if (inside(polar, item)) {
6091373b98135b2854578111dd535e24d39c8853f14Michael Kolb                return item;
6101373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
6111373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
6121373b98135b2854578111dd535e24d39c8853f14Michael Kolb        return null;
6131373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
6141373b98135b2854578111dd535e24d39c8853f14Michael Kolb
615a521253223897b099995e0af08982b4236ce5861Michael Kolb    private boolean inside(PointF polar, PieItem item) {
616a521253223897b099995e0af08982b4236ce5861Michael Kolb        return (item.getInnerRadius() < polar.y)
61755f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                && (item.getStartAngle() < polar.x)
61855f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                && (item.getStartAngle() + item.getSweep() > polar.x)
61955f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                && (!mTapMode || (item.getOuterRadius() > polar.y));
6201373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
6211373b98135b2854578111dd535e24d39c8853f14Michael Kolb
6221373b98135b2854578111dd535e24d39c8853f14Michael Kolb    @Override
6231373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public boolean handlesTouch() {
6241373b98135b2854578111dd535e24d39c8853f14Michael Kolb        return true;
6251373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
6261373b98135b2854578111dd535e24d39c8853f14Michael Kolb
6278042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    // focus specific code
6288042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
629fe44832abc2e9fb937ac5bd9c65e103397992e8cMichael Kolb    public void setBlockFocus(boolean blocked) {
630fe44832abc2e9fb937ac5bd9c65e103397992e8cMichael Kolb        mBlockFocus = blocked;
631fe44832abc2e9fb937ac5bd9c65e103397992e8cMichael Kolb        if (blocked) {
632fe44832abc2e9fb937ac5bd9c65e103397992e8cMichael Kolb            clear();
633fe44832abc2e9fb937ac5bd9c65e103397992e8cMichael Kolb        }
634fe44832abc2e9fb937ac5bd9c65e103397992e8cMichael Kolb    }
635fe44832abc2e9fb937ac5bd9c65e103397992e8cMichael Kolb
63655f0d13040df45c75704df9d633b444cb36f2465Michael Kolb    public void setFocus(int x, int y, boolean startImmediately) {
637a0dd52298b55357a711663180677efa39d35e5abMichael Kolb        mFocusFromTap = true;
63855f0d13040df45c75704df9d633b444cb36f2465Michael Kolb        mTapMode = true;
6396f54ad9e32b77db645f31c45b768af221e80e207Michael Kolb        mFocusX = x;
6406f54ad9e32b77db645f31c45b768af221e80e207Michael Kolb        mFocusY = y;
6418042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        setCircle(mFocusX, mFocusY);
6428042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        setupPie(mFocusX, mFocusY);
64355f0d13040df45c75704df9d633b444cb36f2465Michael Kolb        if (startImmediately) {
64455f0d13040df45c75704df9d633b444cb36f2465Michael Kolb            // cameras that don't support focus still need to show menu
64555f0d13040df45c75704df9d633b444cb36f2465Michael Kolb            setVisible(true);
64655f0d13040df45c75704df9d633b444cb36f2465Michael Kolb            mState = STATE_PIE;
64755f0d13040df45c75704df9d633b444cb36f2465Michael Kolb            mHandler.removeMessages(MSG_FOCUS_TAP);
64855f0d13040df45c75704df9d633b444cb36f2465Michael Kolb            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_FOCUS_TAP),
64955f0d13040df45c75704df9d633b444cb36f2465Michael Kolb                    FOCUS_TAP_TIMEOUT);
65055f0d13040df45c75704df9d633b444cb36f2465Michael Kolb            update();
65155f0d13040df45c75704df9d633b444cb36f2465Michael Kolb        }
6528042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6538042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6548042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void alignFocus(int x, int y) {
6558042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mOverlay.removeCallbacks(mDisappear);
6568042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.cancel();
6578042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.reset();
6588042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusX = x;
6598042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusY = y;
6608042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDialAngle = DIAL_HORIZONTAL;
6618042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        setCircle(x, y);
6628042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocused = false;
6638042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6648042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6658042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public int getSize() {
6668042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        return 2 * mCircleSize;
6678042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6688042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6698042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int getRandomRange() {
670673da081181c72352d57c9bf97badb2929370685Michael Kolb        return (int)(-60 + 120 * Math.random());
6718042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6728042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6731373b98135b2854578111dd535e24d39c8853f14Michael Kolb    @Override
6741373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public void layout(int l, int t, int r, int b) {
6751373b98135b2854578111dd535e24d39c8853f14Michael Kolb        super.layout(l, t, r, b);
6768042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCenterX = (r - l) / 2;
6778042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCenterY = (b - t) / 2;
6788042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusX = mCenterX;
6798042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusY = mCenterY;
6808042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        setCircle(mFocusX, mFocusY);
68116f9b35fce4a46d3e6f72962670f2e28d291793cMichael Kolb        if (isVisible() && mState == STATE_PIE) {
68216f9b35fce4a46d3e6f72962670f2e28d291793cMichael Kolb            setCenter(mCenterX, mCenterY);
68316f9b35fce4a46d3e6f72962670f2e28d291793cMichael Kolb            layoutPie();
68416f9b35fce4a46d3e6f72962670f2e28d291793cMichael Kolb        }
6851373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
6861373b98135b2854578111dd535e24d39c8853f14Michael Kolb
6878042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private void setCircle(int cx, int cy) {
6888042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCircle.set(cx - mCircleSize, cy - mCircleSize,
6898042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                cx + mCircleSize, cy + mCircleSize);
6908042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDial.set(cx - mCircleSize + mInnerOffset, cy - mCircleSize + mInnerOffset,
6918042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                cx + mCircleSize - mInnerOffset, cy + mCircleSize - mInnerOffset);
6928042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6938042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6948042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void drawFocus(Canvas canvas) {
695fe44832abc2e9fb937ac5bd9c65e103397992e8cMichael Kolb        if (mBlockFocus) return;
6968042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusPaint.setStrokeWidth(mOuterStroke);
6977feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        if (mState == STATE_PIE && mFadeIn != null) {
6987feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            canvas.save();
6997feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            float sf = 0.9f + mFadeIn.getValue() * 0.1f;
7007feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            canvas.scale(sf, sf, mCenterX, mCenterY);
7017feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        }
7028042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        canvas.drawCircle((float) mFocusX, (float) mFocusY, (float) mCircleSize, mFocusPaint);
7037feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        if (mState == STATE_PIE && mFadeIn != null) {
7047feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb            canvas.restore();
7057feb060e24b2b5fed416d075facc6e2c8fce23e9Michael Kolb        }
706d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb        if (mState == STATE_PIE) return;
707673da081181c72352d57c9bf97badb2929370685Michael Kolb        int color = mFocusPaint.getColor();
708673da081181c72352d57c9bf97badb2929370685Michael Kolb        if (mState == STATE_FINISHING) {
709673da081181c72352d57c9bf97badb2929370685Michael Kolb            mFocusPaint.setColor(mFocused ? mSuccessColor : mFailColor);
710673da081181c72352d57c9bf97badb2929370685Michael Kolb        }
711673da081181c72352d57c9bf97badb2929370685Michael Kolb        mFocusPaint.setStrokeWidth(mInnerStroke);
712673da081181c72352d57c9bf97badb2929370685Michael Kolb        drawLine(canvas, mDialAngle, mFocusPaint);
713673da081181c72352d57c9bf97badb2929370685Michael Kolb        drawLine(canvas, mDialAngle + 45, mFocusPaint);
714673da081181c72352d57c9bf97badb2929370685Michael Kolb        drawLine(canvas, mDialAngle + 180, mFocusPaint);
715673da081181c72352d57c9bf97badb2929370685Michael Kolb        drawLine(canvas, mDialAngle + 225, mFocusPaint);
7161de083f82907552ec6a92eaece0dc396e260d49eChris Craik        canvas.save();
7171de083f82907552ec6a92eaece0dc396e260d49eChris Craik        // rotate the arc instead of its offset to better use framework's shape caching
7181de083f82907552ec6a92eaece0dc396e260d49eChris Craik        canvas.rotate(mDialAngle, mFocusX, mFocusY);
7191de083f82907552ec6a92eaece0dc396e260d49eChris Craik        canvas.drawArc(mDial, 0, 45, false, mFocusPaint);
7201de083f82907552ec6a92eaece0dc396e260d49eChris Craik        canvas.drawArc(mDial, 180, 45, false, mFocusPaint);
7211de083f82907552ec6a92eaece0dc396e260d49eChris Craik        canvas.restore();
722673da081181c72352d57c9bf97badb2929370685Michael Kolb        mFocusPaint.setColor(color);
7238042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
7248042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
7258042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private void drawLine(Canvas canvas, int angle, Paint p) {
7268042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        convertCart(angle, mCircleSize - mInnerOffset, mPoint1);
7278042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        convertCart(angle, mCircleSize - mInnerOffset + mInnerOffset / 3, mPoint2);
7288042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        canvas.drawLine(mPoint1.x + mFocusX, mPoint1.y + mFocusY,
7298042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                mPoint2.x + mFocusX, mPoint2.y + mFocusY, p);
7308042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
7318042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
7328042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static void convertCart(int angle, int radius, Point out) {
7338042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        double a = 2 * Math.PI * (angle % 360) / 360;
7348042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        out.x = (int) (radius * Math.cos(a) + 0.5);
7358042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        out.y = (int) (radius * Math.sin(a) + 0.5);
7368042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
7378042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
7388042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    @Override
7398042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void showStart() {
7408042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        if (mState == STATE_IDLE) {
741a0dd52298b55357a711663180677efa39d35e5abMichael Kolb            if (mFocusFromTap) {
742a0dd52298b55357a711663180677efa39d35e5abMichael Kolb                mHandler.removeMessages(MSG_FOCUS_TAP);
743a0dd52298b55357a711663180677efa39d35e5abMichael Kolb                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_FOCUS_TAP),
744a0dd52298b55357a711663180677efa39d35e5abMichael Kolb                        FOCUS_TAP_TIMEOUT);
745a0dd52298b55357a711663180677efa39d35e5abMichael Kolb            }
746673da081181c72352d57c9bf97badb2929370685Michael Kolb            mStartAnimationAngle = 67;
7478042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            int range = getRandomRange();
748673da081181c72352d57c9bf97badb2929370685Michael Kolb            startAnimation(SCALING_UP_TIME,
749673da081181c72352d57c9bf97badb2929370685Michael Kolb                    false, mStartAnimationAngle, mStartAnimationAngle + range);
7508042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mState = STATE_FOCUSING;
7518042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
7528042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
7538042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
7548042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    @Override
7558042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void showSuccess(boolean timeout) {
7568042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        if (mState == STATE_FOCUSING) {
757673da081181c72352d57c9bf97badb2929370685Michael Kolb            startAnimation(SCALING_DOWN_TIME,
7588042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                    timeout, mStartAnimationAngle);
7598042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mState = STATE_FINISHING;
7608042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocused = true;
7618042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
7628042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
7638042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
7648042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    @Override
7658042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void showFail(boolean timeout) {
7668042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        if (mState == STATE_FOCUSING) {
767673da081181c72352d57c9bf97badb2929370685Michael Kolb            startAnimation(SCALING_DOWN_TIME,
7688042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                    timeout, mStartAnimationAngle);
7698042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mState = STATE_FINISHING;
7708042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocused = false;
7718042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
7728042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
7738042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
774d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb    private void cancelFocus() {
775d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb        if (mAnimation != null) {
776d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb            mAnimation.cancel();
777d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb        }
778d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb        mOverlay.removeCallbacks(mDisappear);
779d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb        mFocused = false;
780d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb        mFocusFromTap = false;
781d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb    }
782d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb
7838042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    @Override
7848042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void clear() {
785b986d1e51d776706cb3603a9c386772da6d7cf48Michael Kolb        if (mState == STATE_PIE) return;
7868042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.cancel();
7878042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocused = false;
788a0dd52298b55357a711663180677efa39d35e5abMichael Kolb        mFocusFromTap = false;
7898042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mOverlay.removeCallbacks(mDisappear);
7908042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDisappear.run();
7918042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
7928042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
793673da081181c72352d57c9bf97badb2929370685Michael Kolb    private void startAnimation(long duration, boolean timeout,
7948042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            float toScale) {
795673da081181c72352d57c9bf97badb2929370685Michael Kolb        startAnimation(duration, timeout, mDialAngle,
7968042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                toScale);
7978042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
7988042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
799673da081181c72352d57c9bf97badb2929370685Michael Kolb    private void startAnimation(long duration, boolean timeout,
8008042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            float fromScale, float toScale) {
8018042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        setVisible(true);
8028042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.cancel();
8038042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.reset();
8048042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.setDuration(duration);
8058042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.setScale(fromScale, toScale);
8068042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.setAnimationListener(timeout ? mEndAction : null);
8078042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mOverlay.startAnimation(mAnimation);
8088042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        update();
8098042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
8108042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
8118042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private class EndAction implements Animation.AnimationListener {
8128042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        @Override
8138042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        public void onAnimationEnd(Animation animation) {
8148042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            // Keep the focus indicator for some time.
8158042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mOverlay.postDelayed(mDisappear, DISAPPEAR_TIMEOUT);
8168042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
8178042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
8188042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        @Override
8198042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        public void onAnimationRepeat(Animation animation) {
8208042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
8218042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
8228042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        @Override
8238042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        public void onAnimationStart(Animation animation) {
8248042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
8258042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
8268042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
8278042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private class Disappear implements Runnable {
8288042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        @Override
8298042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        public void run() {
830d028bb06a5ec6c23c82195f099ac7b24983ac4a4Michael Kolb            if (mState == STATE_PIE) return;
8318042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            setVisible(false);
8328042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocusX = mCenterX;
8338042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocusY = mCenterY;
8348042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mState = STATE_IDLE;
8358042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            setCircle(mFocusX, mFocusY);
8368042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            setupPie(mFocusX, mFocusY);
8378042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocused = false;
8388042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
8398042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
8408042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
8418042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private class ScaleAnimation extends Animation {
8428042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        private float mFrom = 1f;
8438042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        private float mTo = 1f;
8448042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
8458042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        public ScaleAnimation() {
8468042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            setFillAfter(true);
8478042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
8488042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
8498042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        public void setScale(float from, float to) {
8508042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFrom = from;
8518042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mTo = to;
8528042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
8538042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
854c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        @Override
855c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        protected void applyTransformation(float interpolatedTime, Transformation t) {
8568042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mDialAngle = (int)(mFrom + (mTo - mFrom) * interpolatedTime);
857c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        }
858c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb    }
859c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb
860bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb
861bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb    private class LinearAnimation extends Animation {
862bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        private float mFrom;
863bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        private float mTo;
864bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        private float mValue;
865bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb
866bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        public LinearAnimation(float from, float to) {
867bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            setFillAfter(true);
868bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            setInterpolator(new LinearInterpolator());
869bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            mFrom = from;
870bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            mTo = to;
871bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        }
872bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb
873bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        public float getValue() {
874bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            return mValue;
875bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        }
876bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb
877bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        @Override
878bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        protected void applyTransformation(float interpolatedTime, Transformation t) {
879bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb            mValue = (mFrom + (mTo - mFrom) * interpolatedTime);
880bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb        }
881bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb    }
882bd592c2f2dce4fd6a580cc8bb2fb29c449748801Michael Kolb
8831373b98135b2854578111dd535e24d39c8853f14Michael Kolb}
884