18872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb/*
28872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * Copyright (C) 2012 The Android Open Source Project
38872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb *
48872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License");
58872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * you may not use this file except in compliance with the License.
68872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * You may obtain a copy of the License at
78872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb *
88872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb *      http://www.apache.org/licenses/LICENSE-2.0
98872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb *
108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * Unless required by applicable law or agreed to in writing, software
118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS,
128872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * See the License for the specific language governing permissions and
148872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * limitations under the License.
158872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb */
168872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
178872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbpackage com.android.camera.ui;
188872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
191b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liuimport android.animation.Animator;
201b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liuimport android.animation.Animator.AnimatorListener;
211b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liuimport android.animation.ValueAnimator;
228872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.content.Context;
238872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.content.res.Resources;
248872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.graphics.Canvas;
258872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.graphics.Color;
268872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.graphics.Paint;
278872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.graphics.Path;
288872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.graphics.Point;
298872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.graphics.PointF;
308872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.graphics.RectF;
318872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.os.Handler;
328872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.os.Message;
330f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolbimport android.util.FloatMath;
348872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.view.MotionEvent;
358872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.view.ViewConfiguration;
368872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.view.animation.Animation;
378872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.view.animation.Transformation;
388872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
3910f4ba0198904a3d7679771ce845c847d940226fMichael Kolbimport com.android.camera.drawable.TextDrawable;
4054d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberlingimport com.android.camera.ui.ProgressRenderer.VisibilityListener;
418e963a5a6016d246184ed65906f9d103e92b17e2Sascha Haeberlingimport com.android.camera2.R;
428872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
4354d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberlingimport java.util.ArrayList;
4454d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberlingimport java.util.List;
4554d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling
46e0ecc64979a29e5bbdd1084712b231070b1d57bfSascha Haeberling/**
47e0ecc64979a29e5bbdd1084712b231070b1d57bfSascha Haeberling * An overlay renderer that is used to display focus state and progress state.
48e0ecc64979a29e5bbdd1084712b231070b1d57bfSascha Haeberling */
498872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbpublic class PieRenderer extends OverlayRenderer
508872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        implements FocusIndicator {
518872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
52e0ecc64979a29e5bbdd1084712b231070b1d57bfSascha Haeberling    private static final String TAG = "PieRenderer";
538872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
548872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // Sometimes continuous autofocus starts and stops several times quickly.
558872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // These states are used to make sure the animation is run for at least some
568872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // time.
578872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private volatile int mState;
588872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private ScaleAnimation mAnimation = new ScaleAnimation();
598872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final int STATE_IDLE = 0;
608872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final int STATE_FOCUSING = 1;
618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final int STATE_FINISHING = 2;
628872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final int STATE_PIE = 8;
638872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
643bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private static final float MATH_PI_2 = (float)(Math.PI / 2);
653bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
668872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Runnable mDisappear = new Disappear();
678872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Animation.AnimationListener mEndAction = new EndAction();
688872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final int SCALING_UP_TIME = 600;
698872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final int SCALING_DOWN_TIME = 100;
708872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final int DISAPPEAR_TIMEOUT = 200;
718872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final int DIAL_HORIZONTAL = 157;
72ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb    // fade out timings
73ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb    private static final int PIE_FADE_OUT_DURATION = 600;
748872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final long PIE_FADE_IN_DURATION = 200;
768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final long PIE_XFADE_DURATION = 200;
778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final long PIE_SELECT_FADE_DURATION = 300;
783bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private static final long PIE_OPEN_SUB_DELAY = 400;
79ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb    private static final long PIE_SLICE_DURATION = 80;
808872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
818872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final int MSG_OPEN = 0;
828872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final int MSG_CLOSE = 1;
833bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private static final int MSG_OPENSUBMENU = 2;
843daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb
853daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    protected static float CENTER = (float) Math.PI / 2;
86f465110e4b8ed787589e052e5ac746c588c5ac8fMichael Kolb    protected static float RAD24 = (float)(24 * Math.PI / 180);
873daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    protected static final float SWEEP_SLICE = 0.14f;
883daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    protected static final float SWEEP_ARC = 0.23f;
893daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb
908872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // geometry
918872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mRadius;
923daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    private int mRadiusInc;
938872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
948872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // the detection if touch is inside a slice is offset
958872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // inbounds by this amount to allow the selection to show before the
968872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // finger covers it
978872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mTouchOffset;
988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
993bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private List<PieItem> mOpen;
1008872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Paint mSelectedPaint;
1028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Paint mSubPaint;
1033bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private Paint mMenuArcPaint;
1048872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1058872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // touch handling
1068872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private PieItem mCurrentItem;
1078872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1088872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Paint mFocusPaint;
1098872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mSuccessColor;
1108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mFailColor;
1118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mCircleSize;
1128872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mFocusX;
1138872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mFocusY;
1148872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mCenterX;
1158872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mCenterY;
1163daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    private int mArcCenterY;
1173daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    private int mSliceCenterY;
1183bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private int mPieCenterX;
1193bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private int mPieCenterY;
1203daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    private int mSliceRadius;
1213bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private int mArcRadius;
1223bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private int mArcOffset;
1238872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1248872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mDialAngle;
1258872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private RectF mCircle;
1268872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private RectF mDial;
1278872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Point mPoint1;
1288872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Point mPoint2;
1298872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mStartAnimationAngle;
1308872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private boolean mFocused;
1318872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mInnerOffset;
1328872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mOuterStroke;
1338872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mInnerStroke;
1348872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private boolean mTapMode;
1358872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private boolean mBlockFocus;
1368872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mTouchSlopSquared;
1378872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Point mDown;
1388872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private boolean mOpening;
1391b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu    private ValueAnimator mXFade;
1401b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu    private ValueAnimator mFadeIn;
1411b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu    private ValueAnimator mFadeOut;
1421b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu    private ValueAnimator mSlice;
1438872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private volatile boolean mFocusCancelled;
1443daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    private PointF mPolar = new PointF();
14510f4ba0198904a3d7679771ce845c847d940226fMichael Kolb    private TextDrawable mLabel;
1460f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb    private int mDeadZone;
1470f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb    private int mAngleZone;
1480f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb    private float mCenterAngle;
1493daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb
150e0ecc64979a29e5bbdd1084712b231070b1d57bfSascha Haeberling    private ProgressRenderer mProgressRenderer;
1518872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1528872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Handler mHandler = new Handler() {
1538872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        public void handleMessage(Message msg) {
1548872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            switch(msg.what) {
1558872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            case MSG_OPEN:
1568872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                if (mListener != null) {
1573bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                    mListener.onPieOpened(mPieCenterX, mPieCenterY);
1588872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                }
1598872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                break;
1608872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            case MSG_CLOSE:
1618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                if (mListener != null) {
1628872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    mListener.onPieClosed();
1638872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                }
1648872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                break;
1653bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            case MSG_OPENSUBMENU:
1663bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                onEnterOpen();
1673bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                break;
1688872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
1693bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
1708872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
1718872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    };
1728872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private PieListener mListener;
1748872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    static public interface PieListener {
1768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        public void onPieOpened(int centerX, int centerY);
1778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        public void onPieClosed();
1788872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1798872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1808872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void setPieListener(PieListener pl) {
1818872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mListener = pl;
1828872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1838872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1848872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public PieRenderer(Context context) {
1858872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        init(context);
1868872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1878872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1888872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void init(Context ctx) {
1898872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        setVisible(false);
1903bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mOpen = new ArrayList<PieItem>();
1913bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mOpen.add(new PieItem(null, 0));
1928872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        Resources res = ctx.getResources();
1938872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mRadius = (int) res.getDimensionPixelSize(R.dimen.pie_radius_start);
1943daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        mRadiusInc = (int) res.getDimensionPixelSize(R.dimen.pie_radius_increment);
1958872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mCircleSize = mRadius - res.getDimensionPixelSize(R.dimen.focus_radius_offset);
1968872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mTouchOffset = (int) res.getDimensionPixelSize(R.dimen.pie_touch_offset);
1978872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mSelectedPaint = new Paint();
1988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mSelectedPaint.setColor(Color.argb(255, 51, 181, 229));
1998872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mSelectedPaint.setAntiAlias(true);
2008872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mSubPaint = new Paint();
2018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mSubPaint.setAntiAlias(true);
2028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mSubPaint.setColor(Color.argb(200, 250, 230, 128));
2038872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFocusPaint = new Paint();
2048872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFocusPaint.setAntiAlias(true);
2058872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFocusPaint.setColor(Color.WHITE);
2068872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFocusPaint.setStyle(Paint.Style.STROKE);
2078872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mSuccessColor = Color.GREEN;
2088872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFailColor = Color.RED;
2098872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mCircle = new RectF();
2108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mDial = new RectF();
2118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mPoint1 = new Point();
2128872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mPoint2 = new Point();
2138872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mInnerOffset = res.getDimensionPixelSize(R.dimen.focus_inner_offset);
2148872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mOuterStroke = res.getDimensionPixelSize(R.dimen.focus_outer_stroke);
2158872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mInnerStroke = res.getDimensionPixelSize(R.dimen.focus_inner_stroke);
2168872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mState = STATE_IDLE;
2178872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mBlockFocus = false;
2188872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mTouchSlopSquared = ViewConfiguration.get(ctx).getScaledTouchSlop();
2198872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mTouchSlopSquared = mTouchSlopSquared * mTouchSlopSquared;
2208872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mDown = new Point();
2213bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mMenuArcPaint = new Paint();
2223bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mMenuArcPaint.setAntiAlias(true);
2233bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mMenuArcPaint.setColor(Color.argb(140, 255, 255, 255));
2243bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mMenuArcPaint.setStrokeWidth(10);
2253bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mMenuArcPaint.setStyle(Paint.Style.STROKE);
2263daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        mSliceRadius = res.getDimensionPixelSize(R.dimen.pie_item_radius);
2273bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mArcRadius = res.getDimensionPixelSize(R.dimen.pie_arc_radius);
2283bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mArcOffset = res.getDimensionPixelSize(R.dimen.pie_arc_offset);
22910f4ba0198904a3d7679771ce845c847d940226fMichael Kolb        mLabel = new TextDrawable(res);
230f465110e4b8ed787589e052e5ac746c588c5ac8fMichael Kolb        mLabel.setDropShadow(true);
2310f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb        mDeadZone = res.getDimensionPixelSize(R.dimen.pie_deadzone_width);
2320f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb        mAngleZone = res.getDimensionPixelSize(R.dimen.pie_anglezone_width);
233e0ecc64979a29e5bbdd1084712b231070b1d57bfSascha Haeberling        mProgressRenderer = new ProgressRenderer(ctx);
2343bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
2353bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
2363bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private PieItem getRoot() {
2373bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        return mOpen.get(0);
2388872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
2398872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
2408872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public boolean showsItems() {
2418872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        return mTapMode;
2428872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
2438872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
2448872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void addItem(PieItem item) {
2458872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        // add the item to the pie itself
2463bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        getRoot().addItem(item);
2478872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
2488872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
2498872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void clearItems() {
2503bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        getRoot().clearItems();
2518872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
2528872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
2538872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void showInCenter() {
2548872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if ((mState == STATE_PIE) && isVisible()) {
2558872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mTapMode = false;
2568872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            show(false);
2578872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        } else {
2588872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if (mState != STATE_IDLE) {
2598872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                cancelFocus();
2608872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
2618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mState = STATE_PIE;
2623bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            resetPieCenter();
2633bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            setCenter(mPieCenterX, mPieCenterY);
2648872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mTapMode = true;
2658872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            show(true);
2668872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
2678872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
2688872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
2698872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void hide() {
2708872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        show(false);
2718872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
2728872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
2738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    /**
2748872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * guaranteed has center set
2758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * @param show
2768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     */
2778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void show(boolean show) {
2788872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (show) {
2790233bade51dfcce5fa1f49b5c2e200a719feab22Michael Kolb            if (mXFade != null) {
2800233bade51dfcce5fa1f49b5c2e200a719feab22Michael Kolb                mXFade.cancel();
2810233bade51dfcce5fa1f49b5c2e200a719feab22Michael Kolb            }
2828872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mState = STATE_PIE;
2838872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            // ensure clean state
2848872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mCurrentItem = null;
2853bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            PieItem root = getRoot();
2863bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            for (PieItem openItem : mOpen) {
2873bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                if (openItem.hasItems()) {
2883bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                    for (PieItem item : openItem.getItems()) {
2893bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                        item.setSelected(false);
2903bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                    }
2913bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                }
2928872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
29310f4ba0198904a3d7679771ce845c847d940226fMichael Kolb            mLabel.setText("");
2943bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            mOpen.clear();
2953bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            mOpen.add(root);
2968872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            layoutPie();
2978872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            fadeIn();
2988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        } else {
2998872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mState = STATE_IDLE;
3008872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mTapMode = false;
3018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if (mXFade != null) {
3028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                mXFade.cancel();
3038872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
3049e012f853e3eb5c9d36c84351961f588eef4788fMichael Kolb            if (mLabel != null) {
3059e012f853e3eb5c9d36c84351961f588eef4788fMichael Kolb                mLabel.setText("");
3069e012f853e3eb5c9d36c84351961f588eef4788fMichael Kolb            }
3078872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
3088872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        setVisible(show);
3098872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mHandler.sendEmptyMessage(show ? MSG_OPEN : MSG_CLOSE);
3108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
3118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
312fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu    public boolean isOpen() {
313fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu        return mState == STATE_PIE && isVisible();
314fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu    }
315fb57df15a87f95ce8b2f861e54d960602912e03cDoris Liu
316e0ecc64979a29e5bbdd1084712b231070b1d57bfSascha Haeberling    public void setProgress(int percent) {
317e0ecc64979a29e5bbdd1084712b231070b1d57bfSascha Haeberling        mProgressRenderer.setProgress(percent);
318e0ecc64979a29e5bbdd1084712b231070b1d57bfSascha Haeberling    }
319e0ecc64979a29e5bbdd1084712b231070b1d57bfSascha Haeberling
3208872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void fadeIn() {
3211b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu        mFadeIn = new ValueAnimator();
3221b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu        mFadeIn.setFloatValues(0f, 1f);
3238872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFadeIn.setDuration(PIE_FADE_IN_DURATION);
3241b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu        // linear interpolation
3251b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu        mFadeIn.setInterpolator(null);
3261b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu        mFadeIn.addListener(new AnimatorListener() {
3278872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            @Override
3281b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            public void onAnimationStart(Animator animation) {
3298872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
3308872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
3318872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            @Override
3321b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            public void onAnimationEnd(Animator animation) {
3338872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                mFadeIn = null;
3348872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
3358872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
3368872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            @Override
3371b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            public void onAnimationRepeat(Animator animation) {
3381b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            }
3391b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu
3401b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            @Override
3411b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            public void onAnimationCancel(Animator arg0) {
3428872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
3438872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        });
3441b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu        mFadeIn.start();
3458872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
3468872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
3478872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void setCenter(int x, int y) {
3483bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mPieCenterX = x;
3493bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mPieCenterY = y;
3500f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb        mSliceCenterY = y + mSliceRadius - mArcOffset;
3513daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        mArcCenterY = y - mArcOffset + mArcRadius;
3523bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
3533bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
3543bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    @Override
3553bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    public void layout(int l, int t, int r, int b) {
3563bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        super.layout(l, t, r, b);
3573bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mCenterX = (r - l) / 2;
3583bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mCenterY = (b - t) / 2;
3593bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
3603bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mFocusX = mCenterX;
3613bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mFocusY = mCenterY;
3623bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        resetPieCenter();
3633bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        setCircle(mFocusX, mFocusY);
3643bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        if (isVisible() && mState == STATE_PIE) {
3653bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            setCenter(mPieCenterX, mPieCenterY);
3663bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            layoutPie();
3673bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        }
3683bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
3693bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
3703bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private void resetPieCenter() {
3713bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mPieCenterX = mCenterX;
372f465110e4b8ed787589e052e5ac746c588c5ac8fMichael Kolb        mPieCenterY = (int) (getHeight() - 2.5f * mDeadZone);
3738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
3748872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
3758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void layoutPie() {
3760f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb        mCenterAngle = getCenterAngle();
3773daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        layoutItems(0, getRoot().getItems());
37860e938e0caed26441e0ab784344fd161954dc8abMichael Kolb        layoutLabel(getLevel());
37910f4ba0198904a3d7679771ce845c847d940226fMichael Kolb    }
38010f4ba0198904a3d7679771ce845c847d940226fMichael Kolb
38110f4ba0198904a3d7679771ce845c847d940226fMichael Kolb    private void layoutLabel(int level) {
3820f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb        int x = mPieCenterX - (int) (FloatMath.sin(mCenterAngle - CENTER)
3830f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb                * (mArcRadius + (level + 2) * mRadiusInc));
38410f4ba0198904a3d7679771ce845c847d940226fMichael Kolb        int y = mArcCenterY - mArcRadius - (level + 2) * mRadiusInc;
38510f4ba0198904a3d7679771ce845c847d940226fMichael Kolb        int w = mLabel.getIntrinsicWidth();
38610f4ba0198904a3d7679771ce845c847d940226fMichael Kolb        int h = mLabel.getIntrinsicHeight();
38710f4ba0198904a3d7679771ce845c847d940226fMichael Kolb        mLabel.setBounds(x - w/2, y - h/2, x + w/2, y + h/2);
3883daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    }
3893daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb
3903daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    private void layoutItems(int level, List<PieItem> items) {
3913daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        int extend = 1;
3923daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        Path path = makeSlice(getDegrees(0) + extend, getDegrees(SWEEP_ARC) - extend,
3933daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                mArcRadius, mArcRadius + mRadiusInc + mRadiusInc / 4,
3943daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                mPieCenterX, mArcCenterY - level * mRadiusInc);
395b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb        final int count = items.size();
396b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb        int pos = 0;
3978872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        for (PieItem item : items) {
3988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            // shared between items
3998872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            item.setPath(path);
400b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            float angle = getArcCenter(item, pos, count);
4018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            int w = item.getIntrinsicWidth();
4028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            int h = item.getIntrinsicHeight();
4038872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            // move views to outer border
4043daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb            int r = mArcRadius + mRadiusInc * 2 / 3;
4058872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            int x = (int) (r * Math.cos(angle));
4063daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb            int y = mArcCenterY - (level * mRadiusInc) - (int) (r * Math.sin(angle)) - h / 2;
4073daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb            x = mPieCenterX + x - w / 2;
4088872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            item.setBounds(x, y, x + w, y + h);
4093daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb            item.setLevel(level);
4108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if (item.hasItems()) {
4113daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                layoutItems(level + 1, item.getItems());
4128872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
413b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            pos++;
4148872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
4158872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
4168872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
4173daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    private Path makeSlice(float start, float end, int inner, int outer, int cx, int cy) {
4188872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        RectF bb =
4193daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                new RectF(cx - outer, cy - outer, cx + outer,
4203daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                        cy + outer);
4218872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        RectF bbi =
4223daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                new RectF(cx - inner, cy - inner, cx + inner,
4233daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                        cy + inner);
4248872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        Path path = new Path();
4258872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        path.arcTo(bb, start, end - start, true);
4268872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        path.arcTo(bbi, end, start - end);
4278872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        path.close();
4288872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        return path;
4298872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
4308872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
431b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb    private float getArcCenter(PieItem item, int pos, int count) {
432b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb        return getCenter(pos, count, SWEEP_ARC);
4333daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    }
4343daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb
435b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb    private float getSliceCenter(PieItem item, int pos, int count) {
4360f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb        float center = (getCenterAngle() - CENTER) * 0.5f + CENTER;
437b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb        return center + (count - 1) * SWEEP_SLICE / 2f
438b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb                - pos * SWEEP_SLICE;
4393daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    }
4403daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb
4413daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    private float getCenter(int pos, int count, float sweep) {
4420f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb        return mCenterAngle + (count - 1) * sweep / 2f - pos * sweep;
4430f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb    }
4440f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb
4450f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb    private float getCenterAngle() {
4460f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb        float center = CENTER;
4470f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb        if (mPieCenterX < mDeadZone + mAngleZone) {
448f465110e4b8ed787589e052e5ac746c588c5ac8fMichael Kolb            center = CENTER - (mAngleZone - mPieCenterX + mDeadZone) * RAD24
4490f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb                    / (float) mAngleZone;
4500f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb        } else if (mPieCenterX > getWidth() - mDeadZone - mAngleZone) {
451f465110e4b8ed787589e052e5ac746c588c5ac8fMichael Kolb            center = CENTER + (mPieCenterX - (getWidth() - mDeadZone - mAngleZone)) * RAD24
4520f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb                    / (float) mAngleZone;
4530f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb        }
4540f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb        return center;
4553daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    }
4563daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb
4578872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    /**
4588872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * converts a
4598872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * @param angle from 0..PI to Android degrees (clockwise starting at 3 o'clock)
4608872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * @return skia angle
4618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     */
4628872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private float getDegrees(double angle) {
4638872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        return (float) (360 - 180 * angle / Math.PI);
4648872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
4658872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
466ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb    private void startFadeOut(final PieItem item) {
467ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb        if (mFadeIn != null) {
468ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb            mFadeIn.cancel();
469ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb        }
470ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb        if (mXFade != null) {
471ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb            mXFade.cancel();
4728872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
4731b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu        mFadeOut = new ValueAnimator();
4741b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu        mFadeOut.setFloatValues(1f, 0f);
475ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb        mFadeOut.setDuration(PIE_FADE_OUT_DURATION);
4761b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu        mFadeOut.addListener(new AnimatorListener() {
477ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb            @Override
4781b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            public void onAnimationStart(Animator animator) {
479ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb            }
480ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb
481ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb            @Override
4821b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            public void onAnimationEnd(Animator animator) {
483ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                item.performClick();
484ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                mFadeOut = null;
485ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                deselect();
486ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                show(false);
487ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                mOverlay.setAlpha(1);
488ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb            }
489ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb
490ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb            @Override
4911b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            public void onAnimationRepeat(Animator animator) {
4921b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            }
4931b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu
4941b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            @Override
4951b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            public void onAnimationCancel(Animator animator) {
496ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb            }
4971b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu
498ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb        });
4991b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu        mFadeOut.start();
5008872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
5018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
5023bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    // root does not count
5033bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private boolean hasOpenItem() {
5043bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        return mOpen.size() > 1;
5053bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
5063bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
5073bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    // pop an item of the open item stack
5083bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private PieItem closeOpenItem() {
5093bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        PieItem item = getOpenItem();
5103bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        mOpen.remove(mOpen.size() -1);
5113bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        return item;
5123bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
5133bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
5143bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private PieItem getOpenItem() {
5153bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        return mOpen.get(mOpen.size() - 1);
5163bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
5173bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
5183bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    // return the children either the root or parent of the current open item
5193bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private PieItem getParent() {
5203bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        return mOpen.get(Math.max(0, mOpen.size() - 2));
5213bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
5223bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
5233bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private int getLevel() {
5243bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        return mOpen.size() - 1;
5253bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
5263bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
5278872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    @Override
5288872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void onDraw(Canvas canvas) {
529e0ecc64979a29e5bbdd1084712b231070b1d57bfSascha Haeberling        mProgressRenderer.onDraw(canvas, mFocusX, mFocusY);
530e0ecc64979a29e5bbdd1084712b231070b1d57bfSascha Haeberling
5318872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        float alpha = 1;
5328872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mXFade != null) {
5331b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            alpha = (Float) mXFade.getAnimatedValue();
5348872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        } else if (mFadeIn != null) {
5351b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            alpha = (Float) mFadeIn.getAnimatedValue();
536ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb        } else if (mFadeOut != null) {
5371b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            alpha = (Float) mFadeOut.getAnimatedValue();
5388872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
5398872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        int state = canvas.save();
5408872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mFadeIn != null) {
5418872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            float sf = 0.9f + alpha * 0.1f;
5423bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            canvas.scale(sf, sf, mPieCenterX, mPieCenterY);
5433bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        }
5443bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        if (mState != STATE_PIE) {
5453bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            drawFocus(canvas);
5468872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
5478872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mState == STATE_FINISHING) {
5488872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            canvas.restoreToCount(state);
5498872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            return;
5508872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
5519e012f853e3eb5c9d36c84351961f588eef4788fMichael Kolb        if (mState != STATE_PIE) return;
5523bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        if (!hasOpenItem() || (mXFade != null)) {
5538872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            // draw base menu
5543daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb            drawArc(canvas, getLevel(), getParent());
555b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            List<PieItem> items = getParent().getItems();
556b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            final int count = items.size();
557b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            int pos = 0;
5583bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            for (PieItem item : getParent().getItems()) {
559b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb                drawItem(Math.max(0, mOpen.size() - 2), pos, count, canvas, item, alpha);
560b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb                pos++;
5618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
56210f4ba0198904a3d7679771ce845c847d940226fMichael Kolb            mLabel.draw(canvas);
5638872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
5643bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        if (hasOpenItem()) {
5653bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            int level = getLevel();
5663daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb            drawArc(canvas, level, getOpenItem());
567b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            List<PieItem> items = getOpenItem().getItems();
568b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            final int count = items.size();
569b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            int pos = 0;
570b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            for (PieItem inner : items) {
571ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                if (mFadeOut != null) {
572b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb                    drawItem(level, pos, count, canvas, inner, alpha);
573ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                } else {
574b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb                    drawItem(level, pos, count, canvas, inner, (mXFade != null) ? (1 - 0.5f * alpha) : 1);
575ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                }
576b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb                pos++;
5778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
57810f4ba0198904a3d7679771ce845c847d940226fMichael Kolb            mLabel.draw(canvas);
5798872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
5808872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        canvas.restoreToCount(state);
5818872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
5828872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
5833daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    private void drawArc(Canvas canvas, int level, PieItem item) {
5843bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        // arc
5853bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        if (mState == STATE_PIE) {
586b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            final int count = item.getItems().size();
587b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            float start = mCenterAngle + (count * SWEEP_ARC / 2f);
588b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            float end =  mCenterAngle - (count * SWEEP_ARC / 2f);
5893daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb            int cy = mArcCenterY - level * mRadiusInc;
5903daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb            canvas.drawArc(new RectF(mPieCenterX - mArcRadius, cy - mArcRadius,
5913daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                    mPieCenterX + mArcRadius, cy + mArcRadius),
5923daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                    getDegrees(end), getDegrees(start) - getDegrees(end), false, mMenuArcPaint);
5933bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        }
5943bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
5953daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb
596b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb    private void drawItem(int level, int pos, int count, Canvas canvas, PieItem item, float alpha) {
5978872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mState == STATE_PIE) {
5988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if (item.getPath() != null) {
5993daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                int y = mArcCenterY - level * mRadiusInc;
6008872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                if (item.isSelected()) {
6018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    Paint p = mSelectedPaint;
6028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    int state = canvas.save();
603ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                    float angle = 0;
604ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                    if (mSlice != null) {
6051b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                        angle = (Float) mSlice.getAnimatedValue();
606ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                    } else {
607ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                        angle = getArcCenter(item, pos, count) - SWEEP_ARC / 2f;
608ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                    }
6093daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                    angle = getDegrees(angle);
6103daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                    canvas.rotate(angle, mPieCenterX, y);
611ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                    if (mFadeOut != null) {
612ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                        p.setAlpha((int)(255 * alpha));
613ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                    }
6148872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    canvas.drawPath(item.getPath(), p);
615ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                    if (mFadeOut != null) {
616ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                        p.setAlpha(255);
617ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                    }
6188872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    canvas.restoreToCount(state);
6198872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                }
620ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                if (mFadeOut == null) {
621ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                    alpha = alpha * (item.isEnabled() ? 1 : 0.3f);
622ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                    // draw the item view
623ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                    item.setAlpha(alpha);
624ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                }
6258872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                item.draw(canvas);
6268872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
6278872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
6288872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
6298872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
6308872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    @Override
6318872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public boolean onTouchEvent(MotionEvent evt) {
6328872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        float x = evt.getX();
6338872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        float y = evt.getY();
6348872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        int action = evt.getActionMasked();
6353daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        getPolar(x, y, !mTapMode, mPolar);
6368872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (MotionEvent.ACTION_DOWN == action) {
6370f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb            if ((x < mDeadZone) || (x > getWidth() - mDeadZone)) {
6380f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb                return false;
6390f4a5ae4fae89cc7cde711f6aad8d7f571132ca9Michael Kolb            }
6408872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mDown.x = (int) evt.getX();
6418872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mDown.y = (int) evt.getY();
6428872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mOpening = false;
6438872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if (mTapMode) {
6443daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                PieItem item = findItem(mPolar);
6458872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                if ((item != null) && (mCurrentItem != item)) {
6468872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    mState = STATE_PIE;
6478872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    onEnter(item);
6488872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                }
6498872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            } else {
6508872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                setCenter((int) x, (int) y);
6518872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                show(true);
6528872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
6538872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            return true;
6548872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        } else if (MotionEvent.ACTION_UP == action) {
6558872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if (isVisible()) {
6568872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                PieItem item = mCurrentItem;
6578872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                if (mTapMode) {
6583daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                    item = findItem(mPolar);
6593bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                    if (mOpening) {
6608872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                        mOpening = false;
6618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                        return true;
6628872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    }
6638872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                }
6648872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                if (item == null) {
6658872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    mTapMode = false;
6668872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    show(false);
6673bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                } else if (!mOpening && !item.hasItems()) {
6683bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                        startFadeOut(item);
6693bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                        mTapMode = false;
6703bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                } else {
6713bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                    mTapMode = true;
6728872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                }
6738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                return true;
6748872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
6758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        } else if (MotionEvent.ACTION_CANCEL == action) {
6768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if (isVisible() || mTapMode) {
6778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                show(false);
6788872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
6798872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            deselect();
6803bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            mHandler.removeMessages(MSG_OPENSUBMENU);
6818872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            return false;
6828872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        } else if (MotionEvent.ACTION_MOVE == action) {
6833daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb            if (pulledToCenter(mPolar)) {
6843bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                mHandler.removeMessages(MSG_OPENSUBMENU);
6853bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                if (hasOpenItem()) {
6863bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                    if (mCurrentItem != null) {
6873bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                        mCurrentItem.setSelected(false);
6883bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                    }
6893bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                    closeOpenItem();
6903bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                    mCurrentItem = null;
6918872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                } else {
6928872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    deselect();
6938872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                }
69410f4ba0198904a3d7679771ce845c847d940226fMichael Kolb                mLabel.setText("");
6958872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                return false;
6968872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
6973daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb            PieItem item = findItem(mPolar);
6988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            boolean moved = hasMoved(evt);
6998872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if ((item != null) && (mCurrentItem != item) && (!mOpening || moved)) {
7003bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                mHandler.removeMessages(MSG_OPENSUBMENU);
7018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // only select if we didn't just open or have moved past slop
7028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                if (moved) {
7038872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    // switch back to swipe mode
7048872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    mTapMode = false;
7058872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                }
7063bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                onEnterSelect(item);
7073bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                mHandler.sendEmptyMessageDelayed(MSG_OPENSUBMENU, PIE_OPEN_SUB_DELAY);
7088872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
7098872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
7108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        return false;
7118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
7128872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
71354d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling    @Override
71454d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling    public boolean isVisible() {
71554d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling        return super.isVisible() || mProgressRenderer.isVisible();
71654d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling    }
71754d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling
7183bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private boolean pulledToCenter(PointF polarCoords) {
7193daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        return polarCoords.y < mArcRadius - mRadiusInc;
7203bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
7213bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
722b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb    private boolean inside(PointF polar, PieItem item, int pos, int count) {
723b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb        float start = getSliceCenter(item, pos, count) - SWEEP_SLICE / 2f;
7243daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        boolean res =  (mArcRadius < polar.y)
7253daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                && (start < polar.x)
7263daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                && (start + SWEEP_SLICE > polar.x)
7273daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb                && (!mTapMode || (mArcRadius + mRadiusInc > polar.y));
7283daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        return res;
7293daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    }
7303daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb
7313daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb    private void getPolar(float x, float y, boolean useOffset, PointF res) {
7323bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        // get angle and radius from x/y
7333bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        res.x = (float) Math.PI / 2;
7343daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        x = x - mPieCenterX;
7353daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        float y1 = mSliceCenterY - getLevel() * mRadiusInc - y;
7363daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        float y2 = mArcCenterY - getLevel() * mRadiusInc - y;
7373daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb        res.y = (float) Math.sqrt(x * x + y2 * y2);
7383bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        if (x != 0) {
7393daa3518c3bfa1a2c1da37c56e1a34e9a0cac259Michael Kolb            res.x = (float) Math.atan2(y1,  x);
7403bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            if (res.x < 0) {
7413bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                res.x = (float) (2 * Math.PI + res.x);
7423bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            }
7433bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        }
7443bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        res.y = res.y + (useOffset ? mTouchOffset : 0);
7453bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
7463bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
7478872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private boolean hasMoved(MotionEvent e) {
7488872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        return mTouchSlopSquared < (e.getX() - mDown.x) * (e.getX() - mDown.x)
7498872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                + (e.getY() - mDown.y) * (e.getY() - mDown.y);
7508872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
7518872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
7523bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private void onEnterSelect(PieItem item) {
7533bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        if (mCurrentItem != null) {
7543bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            mCurrentItem.setSelected(false);
7553bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        }
7563bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        if (item != null && item.isEnabled()) {
757ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb            moveSelection(mCurrentItem, item);
7583bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            item.setSelected(true);
7593bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            mCurrentItem = item;
76010f4ba0198904a3d7679771ce845c847d940226fMichael Kolb            mLabel.setText(mCurrentItem.getLabel());
76110f4ba0198904a3d7679771ce845c847d940226fMichael Kolb            layoutLabel(getLevel());
7623bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        } else {
7633bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            mCurrentItem = null;
7643bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        }
7653bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
7663bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
7673bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    private void onEnterOpen() {
768dc7d1ac9c9fd4ba9f4c689e17a4a248a27463fbeMichael Kolb        if ((mCurrentItem != null) && (mCurrentItem != getOpenItem()) && mCurrentItem.hasItems()) {
7693bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            openCurrentItem();
7703bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        }
7713bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb    }
7723bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb
7738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    /**
7748872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * enter a slice for a view
7758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * updates model only
7768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * @param item
7778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     */
7788872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void onEnter(PieItem item) {
7798872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mCurrentItem != null) {
7808872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mCurrentItem.setSelected(false);
7818872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
7828872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (item != null && item.isEnabled()) {
7838872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            item.setSelected(true);
7848872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mCurrentItem = item;
785712cc2f5add2fb78fbb723ecbca5f576a91fb54fMichael Kolb            mLabel.setText(mCurrentItem.getLabel());
7863bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            if ((mCurrentItem != getOpenItem()) && mCurrentItem.hasItems()) {
7878872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                openCurrentItem();
78810f4ba0198904a3d7679771ce845c847d940226fMichael Kolb                layoutLabel(getLevel());
7898872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
7908872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        } else {
7918872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mCurrentItem = null;
7928872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
7938872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
7948872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
7958872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void deselect() {
7968872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mCurrentItem != null) {
7978872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mCurrentItem.setSelected(false);
7988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
7993bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        if (hasOpenItem()) {
8003bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            PieItem item = closeOpenItem();
8013bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            onEnter(item);
8023bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        } else {
8033bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            mCurrentItem = null;
8048872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
8058872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
8068872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
807ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb    private int getItemPos(PieItem target) {
808ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb        List<PieItem> items = getOpenItem().getItems();
809ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb        return items.indexOf(target);
810ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb    }
811ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb
812ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb    private int getCurrentCount() {
813ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb        return getOpenItem().getItems().size();
814ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb    }
815ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb
816ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb    private void moveSelection(PieItem from, PieItem to) {
817ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb        final int count = getCurrentCount();
818ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb        final int fromPos = getItemPos(from);
819ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb        final int toPos = getItemPos(to);
820ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb        if (fromPos != -1 && toPos != -1) {
821ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb            float startAngle = getArcCenter(from, getItemPos(from), count)
822ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                    - SWEEP_ARC / 2f;
823ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb            float endAngle = getArcCenter(to, getItemPos(to), count)
824ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                    - SWEEP_ARC / 2f;
8251b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            mSlice = new ValueAnimator();
8261b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            mSlice.setFloatValues(startAngle, endAngle);
8271b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            // linear interpolater
8281b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            mSlice.setInterpolator(null);
829ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb            mSlice.setDuration(PIE_SLICE_DURATION);
8301b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            mSlice.addListener(new AnimatorListener() {
831ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                @Override
8321b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                public void onAnimationEnd(Animator arg0) {
833ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                    mSlice = null;
834ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                }
835ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb
836ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                @Override
8371b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                public void onAnimationRepeat(Animator arg0) {
8381b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                }
8391b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu
8401b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                @Override
8411b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                public void onAnimationStart(Animator arg0) {
842ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                }
843ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb
844ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                @Override
8451b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                public void onAnimationCancel(Animator arg0) {
846ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb                }
847ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb            });
8481b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            mSlice.start();
849ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb        }
850ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb    }
851ad2a745d44fdf5acdd98d3bd8212ca51f45c29e7Michael Kolb
8528872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void openCurrentItem() {
8538872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if ((mCurrentItem != null) && mCurrentItem.hasItems()) {
8543bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            mOpen.add(mCurrentItem);
855f465110e4b8ed787589e052e5ac746c588c5ac8fMichael Kolb            layoutLabel(getLevel());
8568872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mOpening = true;
857ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb            if (mFadeIn != null) {
858ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb                mFadeIn.cancel();
859ebcf6fed531d00cfb6900a1fb5d83c60aa7f9693Michael Kolb            }
8601b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            mXFade = new ValueAnimator();
8611b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            mXFade.setFloatValues(1f, 0f);
8628872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mXFade.setDuration(PIE_XFADE_DURATION);
8631b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            // Linear interpolation
8641b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            mXFade.setInterpolator(null);
8653bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb            final PieItem ci = mCurrentItem;
8661b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            mXFade.addListener(new AnimatorListener() {
8678872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                @Override
8681b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                public void onAnimationStart(Animator animation) {
8698872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                }
8708872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
8718872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                @Override
8721b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                public void onAnimationEnd(Animator animation) {
8738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    mXFade = null;
8743bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                    ci.setSelected(false);
8753bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb                    mOpening = false;
8768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                }
8778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
8788872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                @Override
8791b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                public void onAnimationRepeat(Animator animation) {
8801b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                }
8811b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu
8821b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                @Override
8831b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu                public void onAnimationCancel(Animator arg0) {
8848872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                }
8858872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            });
8861b9362e03c56e98a27879a15b9d133f1e50fc4d4Doris Liu            mXFade.start();
8878872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
8888872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
8898872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
8908872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    /**
8918872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * @param polar x: angle, y: dist
8928872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * @return the item at angle/dist or null
8938872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     */
8948872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private PieItem findItem(PointF polar) {
8958872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        // find the matching item:
8963bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0Michael Kolb        List<PieItem> items = getOpenItem().getItems();
897b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb        final int count = items.size();
898b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb        int pos = 0;
8998872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        for (PieItem item : items) {
900b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            if (inside(polar, item, pos, count)) {
9018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                return item;
9028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
903b7c49994231f31f5cf4de4a66eec7a85a3bd9551Michael Kolb            pos++;
9048872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
9058872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        return null;
9068872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
9078872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9088872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9098872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    @Override
9108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public boolean handlesTouch() {
9118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        return true;
9128872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
9138872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9148872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // focus specific code
9158872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9168872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void setBlockFocus(boolean blocked) {
9178872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mBlockFocus = blocked;
9188872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (blocked) {
9198872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            clear();
9208872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
9218872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
9228872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9238872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void setFocus(int x, int y) {
924d9b229be6fa942920317fc5e031e803e408adc1dIgor Murashkin        mOverlay.removeCallbacks(mDisappear);
9258872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFocusX = x;
9268872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFocusY = y;
9278872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        setCircle(mFocusX, mFocusY);
9288872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
9298872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9308872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public int getSize() {
9318872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        return 2 * mCircleSize;
9328872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
9338872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9348872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int getRandomRange() {
9358872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        return (int)(-60 + 120 * Math.random());
9368872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
9378872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9388872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void setCircle(int cx, int cy) {
9398872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mCircle.set(cx - mCircleSize, cy - mCircleSize,
9408872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                cx + mCircleSize, cy + mCircleSize);
9418872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mDial.set(cx - mCircleSize + mInnerOffset, cy - mCircleSize + mInnerOffset,
9428872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                cx + mCircleSize - mInnerOffset, cy + mCircleSize - mInnerOffset);
9438872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
9448872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9458872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void drawFocus(Canvas canvas) {
9468872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mBlockFocus) return;
9478872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFocusPaint.setStrokeWidth(mOuterStroke);
9488872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        canvas.drawCircle((float) mFocusX, (float) mFocusY, (float) mCircleSize, mFocusPaint);
9498872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mState == STATE_PIE) return;
9508872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        int color = mFocusPaint.getColor();
9518872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mState == STATE_FINISHING) {
9528872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mFocusPaint.setColor(mFocused ? mSuccessColor : mFailColor);
9538872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
9548872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFocusPaint.setStrokeWidth(mInnerStroke);
9558872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        drawLine(canvas, mDialAngle, mFocusPaint);
9568872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        drawLine(canvas, mDialAngle + 45, mFocusPaint);
9578872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        drawLine(canvas, mDialAngle + 180, mFocusPaint);
9588872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        drawLine(canvas, mDialAngle + 225, mFocusPaint);
9598872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        canvas.save();
9608872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        // rotate the arc instead of its offset to better use framework's shape caching
9618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        canvas.rotate(mDialAngle, mFocusX, mFocusY);
9628872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        canvas.drawArc(mDial, 0, 45, false, mFocusPaint);
9638872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        canvas.drawArc(mDial, 180, 45, false, mFocusPaint);
9648872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        canvas.restore();
9658872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFocusPaint.setColor(color);
9668872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
9678872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9688872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void drawLine(Canvas canvas, int angle, Paint p) {
9698872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        convertCart(angle, mCircleSize - mInnerOffset, mPoint1);
9708872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        convertCart(angle, mCircleSize - mInnerOffset + mInnerOffset / 3, mPoint2);
9718872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        canvas.drawLine(mPoint1.x + mFocusX, mPoint1.y + mFocusY,
9728872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                mPoint2.x + mFocusX, mPoint2.y + mFocusY, p);
9738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
9748872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static void convertCart(int angle, int radius, Point out) {
9768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        double a = 2 * Math.PI * (angle % 360) / 360;
9778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        out.x = (int) (radius * Math.cos(a) + 0.5);
9788872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        out.y = (int) (radius * Math.sin(a) + 0.5);
9798872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
9808872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9818872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    @Override
9828872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void showStart() {
9838872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mState == STATE_PIE) return;
9848872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        cancelFocus();
9858872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mStartAnimationAngle = 67;
9868872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        int range = getRandomRange();
9878872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        startAnimation(SCALING_UP_TIME,
9888872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                false, mStartAnimationAngle, mStartAnimationAngle + range);
9898872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mState = STATE_FOCUSING;
9908872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
9918872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
9928872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    @Override
9938872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void showSuccess(boolean timeout) {
9948872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mState == STATE_FOCUSING) {
9958872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            startAnimation(SCALING_DOWN_TIME,
9968872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    timeout, mStartAnimationAngle);
9978872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mState = STATE_FINISHING;
9988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mFocused = true;
9998872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
10008872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
10018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
10028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    @Override
10038872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void showFail(boolean timeout) {
10048872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mState == STATE_FOCUSING) {
10058872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            startAnimation(SCALING_DOWN_TIME,
10068872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    timeout, mStartAnimationAngle);
10078872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mState = STATE_FINISHING;
10088872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mFocused = false;
10098872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
10108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
10118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
10128872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void cancelFocus() {
10138872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFocusCancelled = true;
10148872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mOverlay.removeCallbacks(mDisappear);
1015e3de7223ee3fe56e097d68f50c5a59ae61370d65Michael Kolb        if (mAnimation != null && !mAnimation.hasEnded()) {
10168872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mAnimation.cancel();
10178872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
10188872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFocusCancelled = false;
10198872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFocused = false;
10208872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mState = STATE_IDLE;
10218872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
10228872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
102354d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling    public void clear(boolean waitUntilProgressIsHidden) {
102454d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling        if (mState == STATE_PIE)
102554d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling            return;
102654d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling        cancelFocus();
102754d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling
102854d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling        if (waitUntilProgressIsHidden) {
102954d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling            mProgressRenderer.setVisibilityListener(new VisibilityListener() {
103054d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling                @Override
103154d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling                public void onHidden() {
103254d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling                    mOverlay.post(mDisappear);
103354d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling                }
103454d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling            });
1035b7dcf9f20708376dfd14f9db6b41addc2f92e5c6Sascha Haeberling        } else {
1036b7dcf9f20708376dfd14f9db6b41addc2f92e5c6Sascha Haeberling            mOverlay.post(mDisappear);
1037b7dcf9f20708376dfd14f9db6b41addc2f92e5c6Sascha Haeberling            mProgressRenderer.setVisibilityListener(null);
103854d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling        }
103954d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling    }
104054d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling
10418872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    @Override
10428872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void clear() {
104354d24c3f82e979b7851e2c0f8b2a4dc69122a7c3Sascha Haeberling        clear(false);
10448872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
10458872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
10468872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void startAnimation(long duration, boolean timeout,
10478872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            float toScale) {
10488872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        startAnimation(duration, timeout, mDialAngle,
10498872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                toScale);
10508872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
10518872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
10528872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void startAnimation(long duration, boolean timeout,
10538872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            float fromScale, float toScale) {
10548872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        setVisible(true);
10558872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mAnimation.reset();
10568872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mAnimation.setDuration(duration);
10578872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mAnimation.setScale(fromScale, toScale);
10588872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mAnimation.setAnimationListener(timeout ? mEndAction : null);
10598872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mOverlay.startAnimation(mAnimation);
10608872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        update();
10618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
10628872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
10638872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private class EndAction implements Animation.AnimationListener {
10648872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        @Override
10658872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        public void onAnimationEnd(Animation animation) {
10668872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            // Keep the focus indicator for some time.
10678872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if (!mFocusCancelled) {
10688872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                mOverlay.postDelayed(mDisappear, DISAPPEAR_TIMEOUT);
10698872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
10708872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
10718872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
10728872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        @Override
10738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        public void onAnimationRepeat(Animation animation) {
10748872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
10758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
10768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        @Override
10778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        public void onAnimationStart(Animation animation) {
10788872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
10798872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
10808872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
10818872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private class Disappear implements Runnable {
10828872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        @Override
10838872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        public void run() {
10848872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if (mState == STATE_PIE) return;
10858872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            setVisible(false);
10868872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mFocusX = mCenterX;
10878872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mFocusY = mCenterY;
10888872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mState = STATE_IDLE;
10898872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            setCircle(mFocusX, mFocusY);
10908872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mFocused = false;
10918872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
10928872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
10938872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
10948872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private class ScaleAnimation extends Animation {
10958872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        private float mFrom = 1f;
10968872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        private float mTo = 1f;
10978872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
10988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        public ScaleAnimation() {
10998872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            setFillAfter(true);
11008872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
11018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
11028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        public void setScale(float from, float to) {
11038872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mFrom = from;
11048872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mTo = to;
11058872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
11068872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
11078872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        @Override
11088872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        protected void applyTransformation(float interpolatedTime, Transformation t) {
11098872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mDialAngle = (int)(mFrom + (mTo - mFrom) * interpolatedTime);
11108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
11118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
11128872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
11138872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb}
1114