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