PieRenderer.java revision 8042e7045f3e11dd49d40eebe603eb5fe12d711a
11373b98135b2854578111dd535e24d39c8853f14Michael Kolb/*
21373b98135b2854578111dd535e24d39c8853f14Michael Kolb * Copyright (C) 2012 The Android Open Source Project
31373b98135b2854578111dd535e24d39c8853f14Michael Kolb *
41373b98135b2854578111dd535e24d39c8853f14Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License");
51373b98135b2854578111dd535e24d39c8853f14Michael Kolb * you may not use this file except in compliance with the License.
61373b98135b2854578111dd535e24d39c8853f14Michael Kolb * You may obtain a copy of the License at
71373b98135b2854578111dd535e24d39c8853f14Michael Kolb *
81373b98135b2854578111dd535e24d39c8853f14Michael Kolb *      http://www.apache.org/licenses/LICENSE-2.0
91373b98135b2854578111dd535e24d39c8853f14Michael Kolb *
101373b98135b2854578111dd535e24d39c8853f14Michael Kolb * Unless required by applicable law or agreed to in writing, software
111373b98135b2854578111dd535e24d39c8853f14Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS,
121373b98135b2854578111dd535e24d39c8853f14Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131373b98135b2854578111dd535e24d39c8853f14Michael Kolb * See the License for the specific language governing permissions and
141373b98135b2854578111dd535e24d39c8853f14Michael Kolb * limitations under the License.
151373b98135b2854578111dd535e24d39c8853f14Michael Kolb */
161373b98135b2854578111dd535e24d39c8853f14Michael Kolb
171373b98135b2854578111dd535e24d39c8853f14Michael Kolbpackage com.android.camera.ui;
181373b98135b2854578111dd535e24d39c8853f14Michael Kolb
19c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolbimport android.annotation.TargetApi;
201373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.content.Context;
211373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.content.res.Resources;
221373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.Canvas;
231373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.Color;
241373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.Paint;
251373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.Path;
261373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.Point;
271373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.PointF;
281373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.graphics.RectF;
291373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.os.Handler;
301373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.os.Message;
31c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolbimport android.util.Log;
321373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.view.MotionEvent;
331373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport android.view.View;
34c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolbimport android.view.animation.Animation;
35c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolbimport android.view.animation.Animation.AnimationListener;
36c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolbimport android.view.animation.Transformation;
371373b98135b2854578111dd535e24d39c8853f14Michael Kolb
381373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport com.android.camera.R;
39c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolbimport com.android.gallery3d.common.ApiHelper;
401373b98135b2854578111dd535e24d39c8853f14Michael Kolb
411373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport java.util.ArrayList;
421373b98135b2854578111dd535e24d39c8853f14Michael Kolbimport java.util.List;
431373b98135b2854578111dd535e24d39c8853f14Michael Kolb
448042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolbpublic class PieRenderer extends OverlayRenderer
458042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        implements FocusIndicator {
461373b98135b2854578111dd535e24d39c8853f14Michael Kolb
471373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private static final String TAG = "CAM Pie";
481373b98135b2854578111dd535e24d39c8853f14Michael Kolb
498042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    // Sometimes continuous autofocus starts and stops several times quickly.
508042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    // These states are used to make sure the animation is run for at least some
518042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    // time.
528042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mState;
538042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private ScaleAnimation mAnimation = new ScaleAnimation();
548042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int STATE_IDLE = 0;
558042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int STATE_FOCUSING = 1;
568042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int STATE_FINISHING = 2;
578042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int STATE_PIE = 3;
588042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
598042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private Runnable mDisappear = new Disappear();
608042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private Animation.AnimationListener mEndAction = new EndAction();
618042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int SCALING_UP_TIME = 1000;
628042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int SCALING_DOWN_TIME = 200;
638042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int DISAPPEAR_TIMEOUT = 200;
648042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static final int DIAL_HORIZONTAL = 157;
658042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
664d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb    private static final long PIE_OPEN_DELAY = 200;
671373b98135b2854578111dd535e24d39c8853f14Michael Kolb
681373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private static final int MSG_OPEN = 2;
691373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private static final int MSG_CLOSE = 3;
701373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private static final int MSG_SUBMENU = 4;
711373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private static final float PIE_SWEEP = (float)(Math.PI * 2 / 3);
721373b98135b2854578111dd535e24d39c8853f14Michael Kolb    // geometry
731373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private Point mCenter;
741373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private int mRadius;
751373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private int mRadiusInc;
761373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private int mSlop;
778042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
781373b98135b2854578111dd535e24d39c8853f14Michael Kolb    // the detection if touch is inside a slice is offset
791373b98135b2854578111dd535e24d39c8853f14Michael Kolb    // inbounds by this amount to allow the selection to show before the
801373b98135b2854578111dd535e24d39c8853f14Michael Kolb    // finger covers it
811373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private int mTouchOffset;
821373b98135b2854578111dd535e24d39c8853f14Michael Kolb
831373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private List<PieItem> mItems;
841373b98135b2854578111dd535e24d39c8853f14Michael Kolb
851373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private PieItem mOpenItem;
861373b98135b2854578111dd535e24d39c8853f14Michael Kolb
871373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private Paint mNormalPaint;
881373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private Paint mSelectedPaint;
891373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private Paint mSubPaint;
901373b98135b2854578111dd535e24d39c8853f14Michael Kolb
911373b98135b2854578111dd535e24d39c8853f14Michael Kolb    // touch handling
921373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private PieItem mCurrentItem;
931373b98135b2854578111dd535e24d39c8853f14Michael Kolb
941373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private boolean mAnimating;
95c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb    private float mAlpha;
961373b98135b2854578111dd535e24d39c8853f14Michael Kolb
978042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private Paint mFocusPaint;
988042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private Paint mSuccessPaint;
998042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private Paint mDotPaint;
1008042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mCircleSize;
1018042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mDotRadius;
1028042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mFocusX;
1038042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mFocusY;
1048042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mCenterX;
1058042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mCenterY;
1068042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
1078042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mDialAngle;
1088042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private RectF mCircle;
1098042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private RectF mDial;
1108042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private Point mPoint1;
1118042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private Point mPoint2;
1128042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mStartAnimationAngle;
1138042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private boolean mFocused;
1148042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mInnerOffset;
1158042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mOuterStroke;
1168042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int mInnerStroke;
1178042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private boolean mFirstTime = true;
1188042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
1198042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
1201373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private Handler mHandler = new Handler() {
1211373b98135b2854578111dd535e24d39c8853f14Michael Kolb        public void handleMessage(Message msg) {
1221373b98135b2854578111dd535e24d39c8853f14Michael Kolb            switch(msg.what) {
1231373b98135b2854578111dd535e24d39c8853f14Michael Kolb            case MSG_OPEN:
124c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb                if (mListener != null && !mAnimating) {
1251373b98135b2854578111dd535e24d39c8853f14Michael Kolb                    mListener.onPieOpened(mCenter.x, mCenter.y);
1261373b98135b2854578111dd535e24d39c8853f14Michael Kolb                }
1271373b98135b2854578111dd535e24d39c8853f14Michael Kolb                break;
1281373b98135b2854578111dd535e24d39c8853f14Michael Kolb            case MSG_CLOSE:
129c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb                if (mListener != null && !mAnimating) {
1301373b98135b2854578111dd535e24d39c8853f14Michael Kolb                    mListener.onPieClosed();
1311373b98135b2854578111dd535e24d39c8853f14Michael Kolb                }
1321373b98135b2854578111dd535e24d39c8853f14Michael Kolb                break;
1331373b98135b2854578111dd535e24d39c8853f14Michael Kolb            case MSG_SUBMENU:
1341373b98135b2854578111dd535e24d39c8853f14Michael Kolb                openCurrentItem();
1351373b98135b2854578111dd535e24d39c8853f14Michael Kolb                break;
1361373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
1371373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
1381373b98135b2854578111dd535e24d39c8853f14Michael Kolb    };
1391373b98135b2854578111dd535e24d39c8853f14Michael Kolb
1401373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private PieListener mListener;
1411373b98135b2854578111dd535e24d39c8853f14Michael Kolb
1421373b98135b2854578111dd535e24d39c8853f14Michael Kolb    static public interface PieListener {
1431373b98135b2854578111dd535e24d39c8853f14Michael Kolb        public void onPieOpened(int centerX, int centerY);
1441373b98135b2854578111dd535e24d39c8853f14Michael Kolb        public void onPieClosed();
1451373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
1461373b98135b2854578111dd535e24d39c8853f14Michael Kolb
1471373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public void setPieListener(PieListener pl) {
1481373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mListener = pl;
1491373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
1501373b98135b2854578111dd535e24d39c8853f14Michael Kolb
1511373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public PieRenderer(Context context) {
1521373b98135b2854578111dd535e24d39c8853f14Michael Kolb        init(context);
1531373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
1541373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void init(Context ctx) {
155ad639dff251233032734106afe4fe51a52be3721Michael Kolb        setVisible(false);
1561373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mItems = new ArrayList<PieItem>();
1571373b98135b2854578111dd535e24d39c8853f14Michael Kolb        Resources res = ctx.getResources();
158a521253223897b099995e0af08982b4236ce5861Michael Kolb        mRadius = (int) res.getDimensionPixelSize(R.dimen.pie_radius_start);
159a521253223897b099995e0af08982b4236ce5861Michael Kolb        mRadiusInc =  (int) res.getDimensionPixelSize(R.dimen.pie_radius_increment);
160a521253223897b099995e0af08982b4236ce5861Michael Kolb        mSlop = (int) res.getDimensionPixelSize(R.dimen.pie_touch_slop);
161a521253223897b099995e0af08982b4236ce5861Michael Kolb        mTouchOffset = (int) res.getDimensionPixelSize(R.dimen.pie_touch_offset);
1621373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mCenter = new Point(0,0);
1631373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mNormalPaint = new Paint();
1641373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mNormalPaint.setColor(Color.argb(0, 0, 0, 0));
1651373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mNormalPaint.setAntiAlias(true);
1661373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mSelectedPaint = new Paint();
1671373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mSelectedPaint.setColor(Color.argb(128, 0, 0, 0)); //res.getColor(R.color.qc_selected));
1681373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mSelectedPaint.setAntiAlias(true);
1691373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mSubPaint = new Paint();
1701373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mSubPaint.setAntiAlias(true);
1711373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mSubPaint.setColor(Color.argb(200, 250, 230, 128)); //res.getColor(R.color.qc_sub));
1728042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusPaint = new Paint();
1738042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusPaint.setAntiAlias(true);
1748042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusPaint.setColor(Color.WHITE);
1758042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusPaint.setStyle(Paint.Style.STROKE);
1768042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mSuccessPaint = new Paint(mFocusPaint);
1778042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mSuccessPaint.setColor(Color.GREEN);
1788042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDotPaint = new Paint();
1798042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDotPaint.setAntiAlias(true);
1808042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDotPaint.setColor(Color.argb(80, 255, 255, 255));
1818042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDotPaint.setStyle(Paint.Style.FILL);
1828042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCircle = new RectF();
1838042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDial = new RectF();
1848042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mPoint1 = new Point();
1858042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mPoint2 = new Point();
1868042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mInnerOffset = res.getDimensionPixelSize(R.dimen.focus_inner_offset);
1878042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mOuterStroke = res.getDimensionPixelSize(R.dimen.focus_outer_stroke);
1888042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mInnerStroke = res.getDimensionPixelSize(R.dimen.focus_inner_stroke);
1898042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDotRadius = res.getDimensionPixelSize(R.dimen.focus_dot_radius);
1908042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        setVisible(false);
1918042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
1921373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
1931373b98135b2854578111dd535e24d39c8853f14Michael Kolb
1941373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public void addItem(PieItem item) {
1951373b98135b2854578111dd535e24d39c8853f14Michael Kolb        // add the item to the pie itself
1961373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mItems.add(item);
1971373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
1981373b98135b2854578111dd535e24d39c8853f14Michael Kolb
1991373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public void removeItem(PieItem item) {
2001373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mItems.remove(item);
2011373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
2021373b98135b2854578111dd535e24d39c8853f14Michael Kolb
2031373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public void clearItems() {
2041373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mItems.clear();
2051373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
2061373b98135b2854578111dd535e24d39c8853f14Michael Kolb
207c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb    public void fade() {
2088042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFirstTime = false;
2098042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        setCenter(mCenterX, mCenterY);
210c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        Animation anim = new AlphaAnimation();
211c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        anim.setFillAfter(true);
212c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        anim.setAnimationListener(new AnimationListener() {
213c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb            @Override
214c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb            public void onAnimationStart(Animation animation) {
215c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb                mAnimating = true;
216c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb                update();
217c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb            }
218c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb            @Override
219c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb            public void onAnimationEnd(Animation animation) {
220c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb                show(false);
221c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb                mAlpha = 0f;
222c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb                mAnimating = false;
223c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb                setViewAlpha(mOverlay, 1);
224c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb            }
225c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb            @Override
226c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb            public void onAnimationRepeat(Animation animation) {
227c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb            }
228c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        });
229c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        anim.reset();
2308042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        anim.setDuration(1000);
231c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        show(true);
232c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        mOverlay.startAnimation(anim);
233c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb    }
234c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb
2351373b98135b2854578111dd535e24d39c8853f14Michael Kolb    /**
2361373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * guaranteed has center set
2371373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * @param show
2381373b98135b2854578111dd535e24d39c8853f14Michael Kolb     */
2391373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void show(boolean show) {
2401373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (show) {
2418042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mState = STATE_PIE;
2421373b98135b2854578111dd535e24d39c8853f14Michael Kolb            // ensure clean state
2431373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mAnimating = false;
2441373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mCurrentItem = null;
2451373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mOpenItem = null;
2461373b98135b2854578111dd535e24d39c8853f14Michael Kolb            for (PieItem item : mItems) {
2471373b98135b2854578111dd535e24d39c8853f14Michael Kolb                item.setSelected(false);
2481373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
2491373b98135b2854578111dd535e24d39c8853f14Michael Kolb            layoutPie();
2501373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
251ad639dff251233032734106afe4fe51a52be3721Michael Kolb        setVisible(show);
252ad639dff251233032734106afe4fe51a52be3721Michael Kolb        mHandler.sendEmptyMessage(show ? MSG_OPEN : MSG_CLOSE);
2534d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb    }
2544d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb
255c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb    public void setCenter(int x, int y) {
2561373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mCenter.x = x;
2571373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mCenter.y = y;
2588042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        // when using the pie menu, align the focus ring
2598042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        alignFocus(x, y);
2608042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
2618042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
2628042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private void setupPie(int x, int y) {
2638042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        // when using the focus ring, align pie items
2648042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCenter.x = x;
2658042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCenter.y = y;
2668042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimating = false;
2678042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCurrentItem = null;
2688042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mOpenItem = null;
2698042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        for (PieItem item : mItems) {
2708042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            item.setSelected(false);
2718042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
2728042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        layoutPie();
2731373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
2741373b98135b2854578111dd535e24d39c8853f14Michael Kolb
2751373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void layoutPie() {
2761373b98135b2854578111dd535e24d39c8853f14Michael Kolb        int rgap = 2;
2771373b98135b2854578111dd535e24d39c8853f14Michael Kolb        int inner = mRadius + rgap;
2781373b98135b2854578111dd535e24d39c8853f14Michael Kolb        int outer = mRadius + mRadiusInc - rgap;
2791373b98135b2854578111dd535e24d39c8853f14Michael Kolb        int gap = 1;
2801373b98135b2854578111dd535e24d39c8853f14Michael Kolb        layoutItems(mItems, (float) (Math.PI / 2), inner, outer, gap);
2811373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
2821373b98135b2854578111dd535e24d39c8853f14Michael Kolb
2834d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb    private void layoutItems(List<PieItem> items, float centerAngle, int inner,
2844d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            int outer, int gap) {
2851373b98135b2854578111dd535e24d39c8853f14Michael Kolb        float emptyangle = PIE_SWEEP / 16;
2861373b98135b2854578111dd535e24d39c8853f14Michael Kolb        float sweep = (float) (PIE_SWEEP - 2 * emptyangle) / items.size();
2871373b98135b2854578111dd535e24d39c8853f14Michael Kolb        float angle = centerAngle - PIE_SWEEP / 2 + emptyangle + sweep / 2;
288b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb        // check if we have custom geometry
289b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb        // first item we find triggers custom sweep for all
290b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb        // this allows us to re-use the path
291b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb        for (PieItem item : items) {
292b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb            if (item.getCenter() >= 0) {
293b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb                sweep = item.getSweep();
294b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb                break;
295b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb            }
296b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb        }
2974d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb        Path path = makeSlice(getDegrees(0) - gap, getDegrees(sweep) + gap,
2984d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                outer, inner, mCenter);
2991373b98135b2854578111dd535e24d39c8853f14Michael Kolb        for (PieItem item : items) {
3001373b98135b2854578111dd535e24d39c8853f14Michael Kolb            // shared between items
3011373b98135b2854578111dd535e24d39c8853f14Michael Kolb            item.setPath(path);
3021373b98135b2854578111dd535e24d39c8853f14Michael Kolb            View view = item.getView();
303b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb            if (item.getCenter() >= 0) {
304b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb                angle = item.getCenter();
305b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb            }
3061373b98135b2854578111dd535e24d39c8853f14Michael Kolb            if (view != null) {
3071373b98135b2854578111dd535e24d39c8853f14Michael Kolb                view.measure(view.getLayoutParams().width,
3081373b98135b2854578111dd535e24d39c8853f14Michael Kolb                        view.getLayoutParams().height);
3091373b98135b2854578111dd535e24d39c8853f14Michael Kolb                int w = view.getMeasuredWidth();
3101373b98135b2854578111dd535e24d39c8853f14Michael Kolb                int h = view.getMeasuredHeight();
3111373b98135b2854578111dd535e24d39c8853f14Michael Kolb                // move views to outer border
3121373b98135b2854578111dd535e24d39c8853f14Michael Kolb                int r = inner + (outer - inner) * 2 / 3;
3131373b98135b2854578111dd535e24d39c8853f14Michael Kolb                int x = (int) (r * Math.cos(angle));
3141373b98135b2854578111dd535e24d39c8853f14Michael Kolb                int y = mCenter.y - (int) (r * Math.sin(angle)) - h / 2;
3151373b98135b2854578111dd535e24d39c8853f14Michael Kolb                x = mCenter.x + x - w / 2;
3161373b98135b2854578111dd535e24d39c8853f14Michael Kolb                view.layout(x, y, x + w, y + h);
3171373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
3181373b98135b2854578111dd535e24d39c8853f14Michael Kolb            float itemstart = angle - sweep / 2;
3191373b98135b2854578111dd535e24d39c8853f14Michael Kolb            item.setGeometry(itemstart, sweep, inner, outer);
3201373b98135b2854578111dd535e24d39c8853f14Michael Kolb            if (item.hasItems()) {
3214d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                layoutItems(item.getItems(), angle, inner,
3224d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                        outer + mRadiusInc / 2, gap);
3231373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
3241373b98135b2854578111dd535e24d39c8853f14Michael Kolb            angle += sweep;
3251373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
3264d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb    }
3271373b98135b2854578111dd535e24d39c8853f14Michael Kolb
3281373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private Path makeSlice(float start, float end, int outer, int inner, Point center) {
3291373b98135b2854578111dd535e24d39c8853f14Michael Kolb        outer = inner + (outer - inner) * 2 / 3;
3301373b98135b2854578111dd535e24d39c8853f14Michael Kolb        RectF bb =
3311373b98135b2854578111dd535e24d39c8853f14Michael Kolb                new RectF(center.x - outer, center.y - outer, center.x + outer,
3321373b98135b2854578111dd535e24d39c8853f14Michael Kolb                        center.y + outer);
3331373b98135b2854578111dd535e24d39c8853f14Michael Kolb        RectF bbi =
3341373b98135b2854578111dd535e24d39c8853f14Michael Kolb                new RectF(center.x - inner, center.y - inner, center.x + inner,
3351373b98135b2854578111dd535e24d39c8853f14Michael Kolb                        center.y + inner);
3361373b98135b2854578111dd535e24d39c8853f14Michael Kolb        Path path = new Path();
3371373b98135b2854578111dd535e24d39c8853f14Michael Kolb        path.arcTo(bb, start, end - start, true);
3381373b98135b2854578111dd535e24d39c8853f14Michael Kolb        path.arcTo(bbi, end, start - end);
3391373b98135b2854578111dd535e24d39c8853f14Michael Kolb        path.close();
3401373b98135b2854578111dd535e24d39c8853f14Michael Kolb        return path;
3411373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
3421373b98135b2854578111dd535e24d39c8853f14Michael Kolb
3431373b98135b2854578111dd535e24d39c8853f14Michael Kolb    /**
3441373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * converts a
345b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb     * @param angle from 0..PI to Android degrees (clockwise starting at 3 o'clock)
3461373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * @return skia angle
3471373b98135b2854578111dd535e24d39c8853f14Michael Kolb     */
3481373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private float getDegrees(double angle) {
3491373b98135b2854578111dd535e24d39c8853f14Michael Kolb        return (float) (360 - 180 * angle / Math.PI);
3501373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
3511373b98135b2854578111dd535e24d39c8853f14Michael Kolb
3521373b98135b2854578111dd535e24d39c8853f14Michael Kolb    @Override
3531373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public void onDraw(Canvas canvas) {
3548042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        drawFocus(canvas);
3558042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        if (mState == STATE_FINISHING) return;
356c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        if (mAnimating) {
357c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb            setViewAlpha(mOverlay, mAlpha);
358c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        }
359ad639dff251233032734106afe4fe51a52be3721Michael Kolb        if (mOpenItem == null) {
360ad639dff251233032734106afe4fe51a52be3721Michael Kolb            // draw base menu
361ad639dff251233032734106afe4fe51a52be3721Michael Kolb            for (PieItem item : mItems) {
362ad639dff251233032734106afe4fe51a52be3721Michael Kolb                drawItem(canvas, item);
363ad639dff251233032734106afe4fe51a52be3721Michael Kolb            }
364ad639dff251233032734106afe4fe51a52be3721Michael Kolb        } else {
365ad639dff251233032734106afe4fe51a52be3721Michael Kolb            for (PieItem inner : mOpenItem.getItems()) {
366ad639dff251233032734106afe4fe51a52be3721Michael Kolb                drawItem(canvas, inner);
3671373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
3681373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
3691373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
3701373b98135b2854578111dd535e24d39c8853f14Michael Kolb
3711373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void drawItem(Canvas canvas, PieItem item) {
3721373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (item.getView() != null) {
3738042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            if (mState == STATE_FOCUSING) {
3748042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                View view = item.getView();
3758042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                canvas.drawCircle(view.getLeft() + view.getWidth() / 2,
3768042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                        view.getTop() + view.getHeight() / 2,
3778042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                        mDotRadius, mDotPaint);
3788042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            } else {
3798042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                Paint p = item.isSelected() ? mSelectedPaint : mNormalPaint;
3808042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                int state = canvas.save();
3818042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                float r = getDegrees(item.getStartAngle());
3828042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                canvas.rotate(r, mCenter.x, mCenter.y);
3838042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                canvas.drawPath(item.getPath(), p);
3848042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                canvas.restoreToCount(state);
3858042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                // draw the item view
3868042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                View view = item.getView();
3878042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                state = canvas.save();
3888042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                canvas.translate(view.getX(), view.getY());
3898042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                view.draw(canvas);
3908042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                canvas.restoreToCount(state);
3918042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            }
3921373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
3931373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
3941373b98135b2854578111dd535e24d39c8853f14Michael Kolb
395c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb    @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
396c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb    private void setViewAlpha(View v, float alpha) {
397c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        if (ApiHelper.HAS_VIEW_TRANSFORM_PROPERTIES) {
398c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb            v.setAlpha(alpha);
399c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        }
400c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb    }
401c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb
4021373b98135b2854578111dd535e24d39c8853f14Michael Kolb    // touch handling for pie
4031373b98135b2854578111dd535e24d39c8853f14Michael Kolb
4041373b98135b2854578111dd535e24d39c8853f14Michael Kolb    @Override
4051373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public boolean onTouchEvent(MotionEvent evt) {
4061373b98135b2854578111dd535e24d39c8853f14Michael Kolb        float x = evt.getX();
4071373b98135b2854578111dd535e24d39c8853f14Michael Kolb        float y = evt.getY();
4081373b98135b2854578111dd535e24d39c8853f14Michael Kolb        int action = evt.getActionMasked();
4091373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (MotionEvent.ACTION_DOWN == action) {
4101373b98135b2854578111dd535e24d39c8853f14Michael Kolb            setCenter((int) x, (int) y);
4114d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            show(true);
4121373b98135b2854578111dd535e24d39c8853f14Michael Kolb            return true;
4131373b98135b2854578111dd535e24d39c8853f14Michael Kolb        } else if (MotionEvent.ACTION_UP == action) {
414ad639dff251233032734106afe4fe51a52be3721Michael Kolb            if (isVisible()) {
4151373b98135b2854578111dd535e24d39c8853f14Michael Kolb                PieItem item = mCurrentItem;
4161373b98135b2854578111dd535e24d39c8853f14Michael Kolb                if (!mAnimating) {
4171373b98135b2854578111dd535e24d39c8853f14Michael Kolb                    deselect();
4181373b98135b2854578111dd535e24d39c8853f14Michael Kolb                }
4191373b98135b2854578111dd535e24d39c8853f14Michael Kolb                show(false);
4201373b98135b2854578111dd535e24d39c8853f14Michael Kolb                if ((item != null) && (item.getView() != null)) {
4211373b98135b2854578111dd535e24d39c8853f14Michael Kolb                    if ((item == mOpenItem) || !mAnimating) {
4221373b98135b2854578111dd535e24d39c8853f14Michael Kolb                        item.getView().performClick();
4231373b98135b2854578111dd535e24d39c8853f14Michael Kolb                    }
4241373b98135b2854578111dd535e24d39c8853f14Michael Kolb                }
4251373b98135b2854578111dd535e24d39c8853f14Michael Kolb                return true;
4261373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
4271373b98135b2854578111dd535e24d39c8853f14Michael Kolb        } else if (MotionEvent.ACTION_CANCEL == action) {
428ad639dff251233032734106afe4fe51a52be3721Michael Kolb            if (isVisible()) {
4291373b98135b2854578111dd535e24d39c8853f14Michael Kolb                show(false);
4301373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
4311373b98135b2854578111dd535e24d39c8853f14Michael Kolb            if (!mAnimating) {
4321373b98135b2854578111dd535e24d39c8853f14Michael Kolb                deselect();
4331373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
4341373b98135b2854578111dd535e24d39c8853f14Michael Kolb            return false;
4351373b98135b2854578111dd535e24d39c8853f14Michael Kolb        } else if (MotionEvent.ACTION_MOVE == action) {
4361373b98135b2854578111dd535e24d39c8853f14Michael Kolb            if (mAnimating) return false;
4374d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            PointF polar = getPolar(x, y);
4384d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            int maxr = mRadius + mRadiusInc + 50;
4394d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            if (polar.y < mRadius) {
4404d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                if (mOpenItem != null) {
4414d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                    mOpenItem = null;
4424d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                } else if (!mAnimating) {
4431373b98135b2854578111dd535e24d39c8853f14Michael Kolb                    deselect();
4441373b98135b2854578111dd535e24d39c8853f14Michael Kolb                }
4454d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                return false;
4464d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            }
4474d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            if (polar.y > maxr) {
4484d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                deselect();
4494d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                show(false);
4504d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                evt.setAction(MotionEvent.ACTION_DOWN);
4514d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                return false;
4524d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            }
4534d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            PieItem item = findItem(polar);
4544d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            if (item == null) {
4554d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb            } else if (mCurrentItem != item) {
4564d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb                onEnter(item);
4571373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
4581373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
4591373b98135b2854578111dd535e24d39c8853f14Michael Kolb        return false;
4601373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
4611373b98135b2854578111dd535e24d39c8853f14Michael Kolb
4621373b98135b2854578111dd535e24d39c8853f14Michael Kolb    /**
4631373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * enter a slice for a view
4641373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * updates model only
4651373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * @param item
4661373b98135b2854578111dd535e24d39c8853f14Michael Kolb     */
4671373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void onEnter(PieItem item) {
4681373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (mCurrentItem != null) {
4691373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mCurrentItem.setSelected(false);
4701373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
4712d3af28f91481d7f3887cb0bb9c5d06375baf787Michael Kolb        if (item != null && item.isEnabled()) {
4721373b98135b2854578111dd535e24d39c8853f14Michael Kolb            item.setSelected(true);
4731373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mCurrentItem = item;
4741373b98135b2854578111dd535e24d39c8853f14Michael Kolb            if ((mCurrentItem != mOpenItem) && mCurrentItem.hasItems()) {
4751373b98135b2854578111dd535e24d39c8853f14Michael Kolb                mHandler.sendEmptyMessageDelayed(MSG_SUBMENU, PIE_OPEN_DELAY);
4761373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
4771373b98135b2854578111dd535e24d39c8853f14Michael Kolb        } else {
4781373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mCurrentItem = null;
4791373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
4801373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
4811373b98135b2854578111dd535e24d39c8853f14Michael Kolb
4821373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void deselect() {
4831373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (mCurrentItem != null) {
4841373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mCurrentItem.setSelected(false);
4851373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mHandler.removeMessages(MSG_SUBMENU);
4861373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
4871373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (mOpenItem != null) {
4881373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mOpenItem = null;
4891373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
4901373b98135b2854578111dd535e24d39c8853f14Michael Kolb        mCurrentItem = null;
4911373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
4921373b98135b2854578111dd535e24d39c8853f14Michael Kolb
4931373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private void openCurrentItem() {
4941373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if ((mCurrentItem != null) && mCurrentItem.hasItems()) {
4951373b98135b2854578111dd535e24d39c8853f14Michael Kolb            mOpenItem = mCurrentItem;
4961373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
4971373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
4981373b98135b2854578111dd535e24d39c8853f14Michael Kolb
4991373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private PointF getPolar(float x, float y) {
5001373b98135b2854578111dd535e24d39c8853f14Michael Kolb        PointF res = new PointF();
5011373b98135b2854578111dd535e24d39c8853f14Michael Kolb        // get angle and radius from x/y
5021373b98135b2854578111dd535e24d39c8853f14Michael Kolb        res.x = (float) Math.PI / 2;
5031373b98135b2854578111dd535e24d39c8853f14Michael Kolb        x = x - mCenter.x;
5041373b98135b2854578111dd535e24d39c8853f14Michael Kolb        y = mCenter.y - y;
505a521253223897b099995e0af08982b4236ce5861Michael Kolb        res.y = (float) Math.sqrt(x * x + y * y);
5061373b98135b2854578111dd535e24d39c8853f14Michael Kolb        if (x != 0) {
5071373b98135b2854578111dd535e24d39c8853f14Michael Kolb            res.x = (float) Math.atan2(y,  x);
508b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb            if (res.x < 0) {
509b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb                res.x = (float) (2 * Math.PI + res.x);
510b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb            }
5111373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
512a521253223897b099995e0af08982b4236ce5861Michael Kolb        res.y = res.y + mTouchOffset;
5131373b98135b2854578111dd535e24d39c8853f14Michael Kolb        return res;
5141373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
5151373b98135b2854578111dd535e24d39c8853f14Michael Kolb
5161373b98135b2854578111dd535e24d39c8853f14Michael Kolb    /**
5171373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * @param polar x: angle, y: dist
5181373b98135b2854578111dd535e24d39c8853f14Michael Kolb     * @return the item at angle/dist or null
5191373b98135b2854578111dd535e24d39c8853f14Michael Kolb     */
5201373b98135b2854578111dd535e24d39c8853f14Michael Kolb    private PieItem findItem(PointF polar) {
5211373b98135b2854578111dd535e24d39c8853f14Michael Kolb        // find the matching item:
5221373b98135b2854578111dd535e24d39c8853f14Michael Kolb        List<PieItem> items = (mOpenItem != null) ? mOpenItem.getItems() : mItems;
5231373b98135b2854578111dd535e24d39c8853f14Michael Kolb        for (PieItem item : items) {
524a521253223897b099995e0af08982b4236ce5861Michael Kolb            if (inside(polar, item)) {
5251373b98135b2854578111dd535e24d39c8853f14Michael Kolb                return item;
5261373b98135b2854578111dd535e24d39c8853f14Michael Kolb            }
5271373b98135b2854578111dd535e24d39c8853f14Michael Kolb        }
5281373b98135b2854578111dd535e24d39c8853f14Michael Kolb        return null;
5291373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
5301373b98135b2854578111dd535e24d39c8853f14Michael Kolb
531a521253223897b099995e0af08982b4236ce5861Michael Kolb    private boolean inside(PointF polar, PieItem item) {
532a521253223897b099995e0af08982b4236ce5861Michael Kolb        return (item.getInnerRadius() < polar.y)
533a521253223897b099995e0af08982b4236ce5861Michael Kolb        && (item.getOuterRadius() > polar.y)
5341373b98135b2854578111dd535e24d39c8853f14Michael Kolb        && (item.getStartAngle() < polar.x)
5351373b98135b2854578111dd535e24d39c8853f14Michael Kolb        && (item.getStartAngle() + item.getSweep() > polar.x);
5361373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
5371373b98135b2854578111dd535e24d39c8853f14Michael Kolb
5381373b98135b2854578111dd535e24d39c8853f14Michael Kolb    @Override
5391373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public boolean handlesTouch() {
5401373b98135b2854578111dd535e24d39c8853f14Michael Kolb        return true;
5411373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
5421373b98135b2854578111dd535e24d39c8853f14Michael Kolb
5438042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private class AlphaAnimation extends Animation {
5448042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        @Override
5458042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        protected void applyTransformation(float interpolatedTime, Transformation t) {
5468042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mAlpha = 1 - interpolatedTime;
5478042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
5488042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
5498042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
5508042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    // focus specific code
5518042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
5528042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void setFocus(int x, int y) {
5538042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        switch(mOverlay.getOrientation()) {
5548042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        case 0:
5558042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocusX = x;
5568042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocusY = y;
5578042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            break;
5588042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        case 180:
5598042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocusX = getWidth() - x;
5608042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocusY = getHeight() - y;
5618042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            break;
5628042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        case 90:
5638042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocusX = getWidth() - y;
5648042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocusY = x;
5658042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            break;
5668042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        case 270:
5678042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocusX = y ;
5688042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocusY = getHeight() - x;
5698042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            break;
5708042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
5718042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        setCircle(mFocusX, mFocusY);
5728042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        setupPie(mFocusX, mFocusY);
5738042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
5748042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
5758042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void alignFocus(int x, int y) {
5768042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mOverlay.removeCallbacks(mDisappear);
5778042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.cancel();
5788042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.reset();
5798042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusX = x;
5808042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusY = y;
5818042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDialAngle = DIAL_HORIZONTAL;
5828042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        setCircle(x, y);
5838042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocused = false;
5848042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
5858042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
5868042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public int getSize() {
5878042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        return 2 * mCircleSize;
5888042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
5898042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
5908042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int getRandomAngle() {
5918042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        return (int)(90 * Math.random());
5928042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
5938042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
5948042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private int getRandomRange() {
5958042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        return (int)(120 * Math.random());
5968042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
5978042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
5981373b98135b2854578111dd535e24d39c8853f14Michael Kolb    @Override
5991373b98135b2854578111dd535e24d39c8853f14Michael Kolb    public void layout(int l, int t, int r, int b) {
6001373b98135b2854578111dd535e24d39c8853f14Michael Kolb        super.layout(l, t, r, b);
6018042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCircleSize = Math.min(200, Math.min(getWidth(), getHeight()) / 5);
6028042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCenterX = (r - l) / 2;
6038042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCenterY = (b - t) / 2;
6048042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusX = mCenterX;
6058042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusY = mCenterY;
6068042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        setCircle(mFocusX, mFocusY);
6078042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        if (mFirstTime) {
6088042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            fade();
6098042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
6101373b98135b2854578111dd535e24d39c8853f14Michael Kolb    }
6111373b98135b2854578111dd535e24d39c8853f14Michael Kolb
6128042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private void setCircle(int cx, int cy) {
6138042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mCircle.set(cx - mCircleSize, cy - mCircleSize,
6148042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                cx + mCircleSize, cy + mCircleSize);
6158042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDial.set(cx - mCircleSize + mInnerOffset, cy - mCircleSize + mInnerOffset,
6168042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                cx + mCircleSize - mInnerOffset, cy + mCircleSize - mInnerOffset);
6178042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6188042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6198042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void drawFocus(Canvas canvas) {
6208042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocusPaint.setStrokeWidth(mOuterStroke);
6218042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        canvas.drawCircle((float) mFocusX, (float) mFocusY, (float) mCircleSize, mFocusPaint);
6228042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        Paint inner = (mFocused ? mSuccessPaint : mFocusPaint);
6238042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        inner.setStrokeWidth(mInnerStroke);
6248042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        canvas.drawArc(mDial, mDialAngle, 45, false, inner);
6258042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        canvas.drawArc(mDial, mDialAngle + 180, 45, false, inner);
6268042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        drawLine(canvas, mDialAngle, inner);
6278042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        drawLine(canvas, mDialAngle + 45, inner);
6288042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        drawLine(canvas, mDialAngle + 180, inner);
6298042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        drawLine(canvas, mDialAngle + 225, inner);
6308042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6318042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6328042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private void drawLine(Canvas canvas, int angle, Paint p) {
6338042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        convertCart(angle, mCircleSize - mInnerOffset, mPoint1);
6348042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        convertCart(angle, mCircleSize - mInnerOffset + mInnerOffset / 3, mPoint2);
6358042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        canvas.drawLine(mPoint1.x + mFocusX, mPoint1.y + mFocusY,
6368042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                mPoint2.x + mFocusX, mPoint2.y + mFocusY, p);
6378042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6388042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6398042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private static void convertCart(int angle, int radius, Point out) {
6408042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        double a = 2 * Math.PI * (angle % 360) / 360;
6418042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        out.x = (int) (radius * Math.cos(a) + 0.5);
6428042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        out.y = (int) (radius * Math.sin(a) + 0.5);
6438042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6448042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6458042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    @Override
6468042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void showStart() {
6478042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        if (mState == STATE_IDLE) {
6488042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            int angle = getRandomAngle();
6498042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            int range = getRandomRange();
6508042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            startAnimation(R.drawable.ic_focus_focusing, SCALING_UP_TIME,
6518042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                    false, angle, angle + range);
6528042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mState = STATE_FOCUSING;
6538042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mStartAnimationAngle = angle;
6548042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
6558042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6568042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6578042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    @Override
6588042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void showSuccess(boolean timeout) {
6598042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        if (mState == STATE_FOCUSING) {
6608042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            startAnimation(R.drawable.ic_focus_focused, SCALING_DOWN_TIME,
6618042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                    timeout, mStartAnimationAngle);
6628042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mState = STATE_FINISHING;
6638042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocused = true;
6648042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
6658042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6668042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6678042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    @Override
6688042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void showFail(boolean timeout) {
6698042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        if (mState == STATE_FOCUSING) {
6708042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            startAnimation(R.drawable.ic_focus_failed, SCALING_DOWN_TIME,
6718042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                    timeout, mStartAnimationAngle);
6728042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mState = STATE_FINISHING;
6738042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocused = false;
6748042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
6758042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6768042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6778042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    @Override
6788042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    public void clear() {
6798042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.cancel();
6808042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mFocused = false;
6818042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mOverlay.removeCallbacks(mDisappear);
6828042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mDisappear.run();
6838042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6848042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6858042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private void startAnimation(int resid, long duration, boolean timeout,
6868042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            float toScale) {
6878042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        startAnimation(resid, duration, timeout, mDialAngle,
6888042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb                toScale);
6898042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
6908042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
6918042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private void startAnimation(int resid, long duration, boolean timeout,
6928042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            float fromScale, float toScale) {
6938042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        setVisible(true);
6948042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.cancel();
6958042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.reset();
6968042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.setDuration(duration);
6978042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.setScale(fromScale, toScale);
6988042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mAnimation.setAnimationListener(timeout ? mEndAction : null);
6998042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        mOverlay.startAnimation(mAnimation);
7008042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        update();
7018042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
7028042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
7038042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private class EndAction implements Animation.AnimationListener {
7048042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        @Override
7058042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        public void onAnimationEnd(Animation animation) {
7068042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            // Keep the focus indicator for some time.
7078042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mOverlay.postDelayed(mDisappear, DISAPPEAR_TIMEOUT);
7088042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
7098042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
7108042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        @Override
7118042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        public void onAnimationRepeat(Animation animation) {
7128042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
7138042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
7148042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        @Override
7158042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        public void onAnimationStart(Animation animation) {
7168042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
7178042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
7188042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
7198042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private class Disappear implements Runnable {
7208042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        @Override
7218042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        public void run() {
7228042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            setVisible(false);
7238042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocusX = mCenterX;
7248042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocusY = mCenterY;
7258042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mState = STATE_IDLE;
7268042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            setCircle(mFocusX, mFocusY);
7278042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            setupPie(mFocusX, mFocusY);
7288042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFocused = false;
7298042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
7308042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    }
7318042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
7328042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb    private class ScaleAnimation extends Animation {
7338042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        private float mFrom = 1f;
7348042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        private float mTo = 1f;
7358042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
7368042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        public ScaleAnimation() {
7378042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            setFillAfter(true);
7388042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
7398042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
7408042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        public void setScale(float from, float to) {
7418042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mFrom = from;
7428042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mTo = to;
7438042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb        }
7448042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb
745c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        @Override
746c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        protected void applyTransformation(float interpolatedTime, Transformation t) {
7478042e7045f3e11dd49d40eebe603eb5fe12d711aMichael Kolb            mDialAngle = (int)(mFrom + (mTo - mFrom) * interpolatedTime);
748c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb        }
749c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb    }
750c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb
7511373b98135b2854578111dd535e24d39c8853f14Michael Kolb}
752