PieRenderer.java revision c605826581f2ef1640828af82dbf26a70d4c7c78
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 441373b98135b2854578111dd535e24d39c8853f14Michael Kolbpublic class PieRenderer extends OverlayRenderer { 451373b98135b2854578111dd535e24d39c8853f14Michael Kolb 461373b98135b2854578111dd535e24d39c8853f14Michael Kolb private static final String TAG = "CAM Pie"; 471373b98135b2854578111dd535e24d39c8853f14Michael Kolb 484d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb private static final long PIE_OPEN_DELAY = 200; 491373b98135b2854578111dd535e24d39c8853f14Michael Kolb 501373b98135b2854578111dd535e24d39c8853f14Michael Kolb private static final int MSG_OPEN = 2; 511373b98135b2854578111dd535e24d39c8853f14Michael Kolb private static final int MSG_CLOSE = 3; 521373b98135b2854578111dd535e24d39c8853f14Michael Kolb private static final int MSG_SUBMENU = 4; 531373b98135b2854578111dd535e24d39c8853f14Michael Kolb private static final float PIE_SWEEP = (float)(Math.PI * 2 / 3); 541373b98135b2854578111dd535e24d39c8853f14Michael Kolb // geometry 551373b98135b2854578111dd535e24d39c8853f14Michael Kolb private Point mCenter; 561373b98135b2854578111dd535e24d39c8853f14Michael Kolb private int mRadius; 571373b98135b2854578111dd535e24d39c8853f14Michael Kolb private int mRadiusInc; 581373b98135b2854578111dd535e24d39c8853f14Michael Kolb private int mSlop; 591373b98135b2854578111dd535e24d39c8853f14Michael Kolb // the detection if touch is inside a slice is offset 601373b98135b2854578111dd535e24d39c8853f14Michael Kolb // inbounds by this amount to allow the selection to show before the 611373b98135b2854578111dd535e24d39c8853f14Michael Kolb // finger covers it 621373b98135b2854578111dd535e24d39c8853f14Michael Kolb private int mTouchOffset; 631373b98135b2854578111dd535e24d39c8853f14Michael Kolb 641373b98135b2854578111dd535e24d39c8853f14Michael Kolb private List<PieItem> mItems; 651373b98135b2854578111dd535e24d39c8853f14Michael Kolb 661373b98135b2854578111dd535e24d39c8853f14Michael Kolb private PieItem mOpenItem; 671373b98135b2854578111dd535e24d39c8853f14Michael Kolb 681373b98135b2854578111dd535e24d39c8853f14Michael Kolb private Paint mNormalPaint; 691373b98135b2854578111dd535e24d39c8853f14Michael Kolb private Paint mSelectedPaint; 701373b98135b2854578111dd535e24d39c8853f14Michael Kolb private Paint mSubPaint; 711373b98135b2854578111dd535e24d39c8853f14Michael Kolb 721373b98135b2854578111dd535e24d39c8853f14Michael Kolb // touch handling 731373b98135b2854578111dd535e24d39c8853f14Michael Kolb private PieItem mCurrentItem; 741373b98135b2854578111dd535e24d39c8853f14Michael Kolb 751373b98135b2854578111dd535e24d39c8853f14Michael Kolb private boolean mAnimating; 76c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb private float mAlpha; 771373b98135b2854578111dd535e24d39c8853f14Michael Kolb 781373b98135b2854578111dd535e24d39c8853f14Michael Kolb private Handler mHandler = new Handler() { 791373b98135b2854578111dd535e24d39c8853f14Michael Kolb public void handleMessage(Message msg) { 801373b98135b2854578111dd535e24d39c8853f14Michael Kolb switch(msg.what) { 811373b98135b2854578111dd535e24d39c8853f14Michael Kolb case MSG_OPEN: 82c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb if (mListener != null && !mAnimating) { 831373b98135b2854578111dd535e24d39c8853f14Michael Kolb mListener.onPieOpened(mCenter.x, mCenter.y); 841373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 851373b98135b2854578111dd535e24d39c8853f14Michael Kolb break; 861373b98135b2854578111dd535e24d39c8853f14Michael Kolb case MSG_CLOSE: 87c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb if (mListener != null && !mAnimating) { 881373b98135b2854578111dd535e24d39c8853f14Michael Kolb mListener.onPieClosed(); 891373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 901373b98135b2854578111dd535e24d39c8853f14Michael Kolb break; 911373b98135b2854578111dd535e24d39c8853f14Michael Kolb case MSG_SUBMENU: 921373b98135b2854578111dd535e24d39c8853f14Michael Kolb openCurrentItem(); 931373b98135b2854578111dd535e24d39c8853f14Michael Kolb break; 941373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 951373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 961373b98135b2854578111dd535e24d39c8853f14Michael Kolb }; 971373b98135b2854578111dd535e24d39c8853f14Michael Kolb 981373b98135b2854578111dd535e24d39c8853f14Michael Kolb private PieListener mListener; 991373b98135b2854578111dd535e24d39c8853f14Michael Kolb 1001373b98135b2854578111dd535e24d39c8853f14Michael Kolb static public interface PieListener { 1011373b98135b2854578111dd535e24d39c8853f14Michael Kolb public void onPieOpened(int centerX, int centerY); 1021373b98135b2854578111dd535e24d39c8853f14Michael Kolb public void onPieClosed(); 1031373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 1041373b98135b2854578111dd535e24d39c8853f14Michael Kolb 1051373b98135b2854578111dd535e24d39c8853f14Michael Kolb public void setPieListener(PieListener pl) { 1061373b98135b2854578111dd535e24d39c8853f14Michael Kolb mListener = pl; 1071373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 1081373b98135b2854578111dd535e24d39c8853f14Michael Kolb 1091373b98135b2854578111dd535e24d39c8853f14Michael Kolb public PieRenderer(Context context) { 1101373b98135b2854578111dd535e24d39c8853f14Michael Kolb init(context); 1111373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 1121373b98135b2854578111dd535e24d39c8853f14Michael Kolb private void init(Context ctx) { 113ad639dff251233032734106afe4fe51a52be3721Michael Kolb setVisible(false); 1141373b98135b2854578111dd535e24d39c8853f14Michael Kolb mItems = new ArrayList<PieItem>(); 1151373b98135b2854578111dd535e24d39c8853f14Michael Kolb Resources res = ctx.getResources(); 116a521253223897b099995e0af08982b4236ce5861Michael Kolb mRadius = (int) res.getDimensionPixelSize(R.dimen.pie_radius_start); 117a521253223897b099995e0af08982b4236ce5861Michael Kolb mRadiusInc = (int) res.getDimensionPixelSize(R.dimen.pie_radius_increment); 118a521253223897b099995e0af08982b4236ce5861Michael Kolb mSlop = (int) res.getDimensionPixelSize(R.dimen.pie_touch_slop); 119a521253223897b099995e0af08982b4236ce5861Michael Kolb mTouchOffset = (int) res.getDimensionPixelSize(R.dimen.pie_touch_offset); 1201373b98135b2854578111dd535e24d39c8853f14Michael Kolb mCenter = new Point(0,0); 1211373b98135b2854578111dd535e24d39c8853f14Michael Kolb mNormalPaint = new Paint(); 1221373b98135b2854578111dd535e24d39c8853f14Michael Kolb mNormalPaint.setColor(Color.argb(0, 0, 0, 0)); 1231373b98135b2854578111dd535e24d39c8853f14Michael Kolb mNormalPaint.setAntiAlias(true); 1241373b98135b2854578111dd535e24d39c8853f14Michael Kolb mSelectedPaint = new Paint(); 1251373b98135b2854578111dd535e24d39c8853f14Michael Kolb mSelectedPaint.setColor(Color.argb(128, 0, 0, 0)); //res.getColor(R.color.qc_selected)); 1261373b98135b2854578111dd535e24d39c8853f14Michael Kolb mSelectedPaint.setAntiAlias(true); 1271373b98135b2854578111dd535e24d39c8853f14Michael Kolb mSubPaint = new Paint(); 1281373b98135b2854578111dd535e24d39c8853f14Michael Kolb mSubPaint.setAntiAlias(true); 1291373b98135b2854578111dd535e24d39c8853f14Michael Kolb mSubPaint.setColor(Color.argb(200, 250, 230, 128)); //res.getColor(R.color.qc_sub)); 1301373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 1311373b98135b2854578111dd535e24d39c8853f14Michael Kolb 1321373b98135b2854578111dd535e24d39c8853f14Michael Kolb public void addItem(PieItem item) { 1331373b98135b2854578111dd535e24d39c8853f14Michael Kolb // add the item to the pie itself 1341373b98135b2854578111dd535e24d39c8853f14Michael Kolb mItems.add(item); 1351373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 1361373b98135b2854578111dd535e24d39c8853f14Michael Kolb 1371373b98135b2854578111dd535e24d39c8853f14Michael Kolb public void removeItem(PieItem item) { 1381373b98135b2854578111dd535e24d39c8853f14Michael Kolb mItems.remove(item); 1391373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 1401373b98135b2854578111dd535e24d39c8853f14Michael Kolb 1411373b98135b2854578111dd535e24d39c8853f14Michael Kolb public void clearItems() { 1421373b98135b2854578111dd535e24d39c8853f14Michael Kolb mItems.clear(); 1431373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 1441373b98135b2854578111dd535e24d39c8853f14Michael Kolb 145c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb public void fade() { 146c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb Animation anim = new AlphaAnimation(); 147c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb anim.setFillAfter(true); 148c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb anim.setAnimationListener(new AnimationListener() { 149c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb @Override 150c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb public void onAnimationStart(Animation animation) { 151c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb mAnimating = true; 152c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb update(); 153c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb } 154c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb @Override 155c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb public void onAnimationEnd(Animation animation) { 156c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb show(false); 157c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb mAlpha = 0f; 158c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb mAnimating = false; 159c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb setViewAlpha(mOverlay, 1); 160c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb } 161c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb @Override 162c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb public void onAnimationRepeat(Animation animation) { 163c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb } 164c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb }); 165c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb anim.reset(); 166c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb anim.setDuration(500); 167c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb show(true); 168c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb mOverlay.startAnimation(anim); 169c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb } 170c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb 1711373b98135b2854578111dd535e24d39c8853f14Michael Kolb /** 1721373b98135b2854578111dd535e24d39c8853f14Michael Kolb * guaranteed has center set 1731373b98135b2854578111dd535e24d39c8853f14Michael Kolb * @param show 1741373b98135b2854578111dd535e24d39c8853f14Michael Kolb */ 1751373b98135b2854578111dd535e24d39c8853f14Michael Kolb private void show(boolean show) { 1761373b98135b2854578111dd535e24d39c8853f14Michael Kolb if (show) { 1771373b98135b2854578111dd535e24d39c8853f14Michael Kolb // ensure clean state 1781373b98135b2854578111dd535e24d39c8853f14Michael Kolb mAnimating = false; 1791373b98135b2854578111dd535e24d39c8853f14Michael Kolb mCurrentItem = null; 1801373b98135b2854578111dd535e24d39c8853f14Michael Kolb mOpenItem = null; 1811373b98135b2854578111dd535e24d39c8853f14Michael Kolb for (PieItem item : mItems) { 1821373b98135b2854578111dd535e24d39c8853f14Michael Kolb item.setSelected(false); 1831373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 1841373b98135b2854578111dd535e24d39c8853f14Michael Kolb layoutPie(); 1851373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 186ad639dff251233032734106afe4fe51a52be3721Michael Kolb setVisible(show); 187ad639dff251233032734106afe4fe51a52be3721Michael Kolb mHandler.sendEmptyMessage(show ? MSG_OPEN : MSG_CLOSE); 1884d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb } 1894d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb 190c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb public void setCenter(int x, int y) { 1911373b98135b2854578111dd535e24d39c8853f14Michael Kolb mCenter.x = x; 1921373b98135b2854578111dd535e24d39c8853f14Michael Kolb mCenter.y = y; 1931373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 1941373b98135b2854578111dd535e24d39c8853f14Michael Kolb 1951373b98135b2854578111dd535e24d39c8853f14Michael Kolb private void layoutPie() { 1961373b98135b2854578111dd535e24d39c8853f14Michael Kolb int rgap = 2; 1971373b98135b2854578111dd535e24d39c8853f14Michael Kolb int inner = mRadius + rgap; 1981373b98135b2854578111dd535e24d39c8853f14Michael Kolb int outer = mRadius + mRadiusInc - rgap; 1991373b98135b2854578111dd535e24d39c8853f14Michael Kolb int gap = 1; 2001373b98135b2854578111dd535e24d39c8853f14Michael Kolb layoutItems(mItems, (float) (Math.PI / 2), inner, outer, gap); 2011373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 2021373b98135b2854578111dd535e24d39c8853f14Michael Kolb 2034d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb private void layoutItems(List<PieItem> items, float centerAngle, int inner, 2044d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb int outer, int gap) { 2051373b98135b2854578111dd535e24d39c8853f14Michael Kolb float emptyangle = PIE_SWEEP / 16; 2061373b98135b2854578111dd535e24d39c8853f14Michael Kolb float sweep = (float) (PIE_SWEEP - 2 * emptyangle) / items.size(); 2071373b98135b2854578111dd535e24d39c8853f14Michael Kolb float angle = centerAngle - PIE_SWEEP / 2 + emptyangle + sweep / 2; 208b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb // check if we have custom geometry 209b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb // first item we find triggers custom sweep for all 210b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb // this allows us to re-use the path 211b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb for (PieItem item : items) { 212b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb if (item.getCenter() >= 0) { 213b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb sweep = item.getSweep(); 214b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb break; 215b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb } 216b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb } 2174d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb Path path = makeSlice(getDegrees(0) - gap, getDegrees(sweep) + gap, 2184d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb outer, inner, mCenter); 2191373b98135b2854578111dd535e24d39c8853f14Michael Kolb for (PieItem item : items) { 2201373b98135b2854578111dd535e24d39c8853f14Michael Kolb // shared between items 2211373b98135b2854578111dd535e24d39c8853f14Michael Kolb item.setPath(path); 2221373b98135b2854578111dd535e24d39c8853f14Michael Kolb View view = item.getView(); 223b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb if (item.getCenter() >= 0) { 224b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb angle = item.getCenter(); 225b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb } 2261373b98135b2854578111dd535e24d39c8853f14Michael Kolb if (view != null) { 2271373b98135b2854578111dd535e24d39c8853f14Michael Kolb view.measure(view.getLayoutParams().width, 2281373b98135b2854578111dd535e24d39c8853f14Michael Kolb view.getLayoutParams().height); 2291373b98135b2854578111dd535e24d39c8853f14Michael Kolb int w = view.getMeasuredWidth(); 2301373b98135b2854578111dd535e24d39c8853f14Michael Kolb int h = view.getMeasuredHeight(); 2311373b98135b2854578111dd535e24d39c8853f14Michael Kolb // move views to outer border 2321373b98135b2854578111dd535e24d39c8853f14Michael Kolb int r = inner + (outer - inner) * 2 / 3; 2331373b98135b2854578111dd535e24d39c8853f14Michael Kolb int x = (int) (r * Math.cos(angle)); 2341373b98135b2854578111dd535e24d39c8853f14Michael Kolb int y = mCenter.y - (int) (r * Math.sin(angle)) - h / 2; 2351373b98135b2854578111dd535e24d39c8853f14Michael Kolb x = mCenter.x + x - w / 2; 2361373b98135b2854578111dd535e24d39c8853f14Michael Kolb view.layout(x, y, x + w, y + h); 2371373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 2381373b98135b2854578111dd535e24d39c8853f14Michael Kolb float itemstart = angle - sweep / 2; 2391373b98135b2854578111dd535e24d39c8853f14Michael Kolb item.setGeometry(itemstart, sweep, inner, outer); 2401373b98135b2854578111dd535e24d39c8853f14Michael Kolb if (item.hasItems()) { 2414d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb layoutItems(item.getItems(), angle, inner, 2424d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb outer + mRadiusInc / 2, gap); 2431373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 2441373b98135b2854578111dd535e24d39c8853f14Michael Kolb angle += sweep; 2451373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 2464d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb } 2471373b98135b2854578111dd535e24d39c8853f14Michael Kolb 2481373b98135b2854578111dd535e24d39c8853f14Michael Kolb private Path makeSlice(float start, float end, int outer, int inner, Point center) { 2491373b98135b2854578111dd535e24d39c8853f14Michael Kolb outer = inner + (outer - inner) * 2 / 3; 2501373b98135b2854578111dd535e24d39c8853f14Michael Kolb RectF bb = 2511373b98135b2854578111dd535e24d39c8853f14Michael Kolb new RectF(center.x - outer, center.y - outer, center.x + outer, 2521373b98135b2854578111dd535e24d39c8853f14Michael Kolb center.y + outer); 2531373b98135b2854578111dd535e24d39c8853f14Michael Kolb RectF bbi = 2541373b98135b2854578111dd535e24d39c8853f14Michael Kolb new RectF(center.x - inner, center.y - inner, center.x + inner, 2551373b98135b2854578111dd535e24d39c8853f14Michael Kolb center.y + inner); 2561373b98135b2854578111dd535e24d39c8853f14Michael Kolb Path path = new Path(); 2571373b98135b2854578111dd535e24d39c8853f14Michael Kolb path.arcTo(bb, start, end - start, true); 2581373b98135b2854578111dd535e24d39c8853f14Michael Kolb path.arcTo(bbi, end, start - end); 2591373b98135b2854578111dd535e24d39c8853f14Michael Kolb path.close(); 2601373b98135b2854578111dd535e24d39c8853f14Michael Kolb return path; 2611373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 2621373b98135b2854578111dd535e24d39c8853f14Michael Kolb 2631373b98135b2854578111dd535e24d39c8853f14Michael Kolb /** 2641373b98135b2854578111dd535e24d39c8853f14Michael Kolb * converts a 265b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb * @param angle from 0..PI to Android degrees (clockwise starting at 3 o'clock) 2661373b98135b2854578111dd535e24d39c8853f14Michael Kolb * @return skia angle 2671373b98135b2854578111dd535e24d39c8853f14Michael Kolb */ 2681373b98135b2854578111dd535e24d39c8853f14Michael Kolb private float getDegrees(double angle) { 2691373b98135b2854578111dd535e24d39c8853f14Michael Kolb return (float) (360 - 180 * angle / Math.PI); 2701373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 2711373b98135b2854578111dd535e24d39c8853f14Michael Kolb 2721373b98135b2854578111dd535e24d39c8853f14Michael Kolb @Override 2731373b98135b2854578111dd535e24d39c8853f14Michael Kolb public void onDraw(Canvas canvas) { 274c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb if (mAnimating) { 275c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb setViewAlpha(mOverlay, mAlpha); 276c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb } 277ad639dff251233032734106afe4fe51a52be3721Michael Kolb if (mOpenItem == null) { 278ad639dff251233032734106afe4fe51a52be3721Michael Kolb // draw base menu 279ad639dff251233032734106afe4fe51a52be3721Michael Kolb for (PieItem item : mItems) { 280ad639dff251233032734106afe4fe51a52be3721Michael Kolb drawItem(canvas, item); 281ad639dff251233032734106afe4fe51a52be3721Michael Kolb } 282ad639dff251233032734106afe4fe51a52be3721Michael Kolb } else { 283ad639dff251233032734106afe4fe51a52be3721Michael Kolb for (PieItem inner : mOpenItem.getItems()) { 284ad639dff251233032734106afe4fe51a52be3721Michael Kolb drawItem(canvas, inner); 2851373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 2861373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 2871373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 2881373b98135b2854578111dd535e24d39c8853f14Michael Kolb 2891373b98135b2854578111dd535e24d39c8853f14Michael Kolb private void drawItem(Canvas canvas, PieItem item) { 2901373b98135b2854578111dd535e24d39c8853f14Michael Kolb if (item.getView() != null) { 2911373b98135b2854578111dd535e24d39c8853f14Michael Kolb Paint p = item.isSelected() ? mSelectedPaint : mNormalPaint; 2921373b98135b2854578111dd535e24d39c8853f14Michael Kolb int state = canvas.save(); 293b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb float r = getDegrees(item.getStartAngle()); 2941373b98135b2854578111dd535e24d39c8853f14Michael Kolb canvas.rotate(r, mCenter.x, mCenter.y); 2951373b98135b2854578111dd535e24d39c8853f14Michael Kolb canvas.drawPath(item.getPath(), p); 2961373b98135b2854578111dd535e24d39c8853f14Michael Kolb canvas.restoreToCount(state); 2971373b98135b2854578111dd535e24d39c8853f14Michael Kolb // draw the item view 2981373b98135b2854578111dd535e24d39c8853f14Michael Kolb View view = item.getView(); 2991373b98135b2854578111dd535e24d39c8853f14Michael Kolb state = canvas.save(); 3001373b98135b2854578111dd535e24d39c8853f14Michael Kolb canvas.translate(view.getX(), view.getY()); 3011373b98135b2854578111dd535e24d39c8853f14Michael Kolb view.draw(canvas); 3021373b98135b2854578111dd535e24d39c8853f14Michael Kolb canvas.restoreToCount(state); 3031373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3041373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3051373b98135b2854578111dd535e24d39c8853f14Michael Kolb 306c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) 307c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb private void setViewAlpha(View v, float alpha) { 308c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb if (ApiHelper.HAS_VIEW_TRANSFORM_PROPERTIES) { 309c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb v.setAlpha(alpha); 310c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb } 311c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb } 312c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb 3131373b98135b2854578111dd535e24d39c8853f14Michael Kolb // touch handling for pie 3141373b98135b2854578111dd535e24d39c8853f14Michael Kolb 3151373b98135b2854578111dd535e24d39c8853f14Michael Kolb @Override 3161373b98135b2854578111dd535e24d39c8853f14Michael Kolb public boolean onTouchEvent(MotionEvent evt) { 3171373b98135b2854578111dd535e24d39c8853f14Michael Kolb float x = evt.getX(); 3181373b98135b2854578111dd535e24d39c8853f14Michael Kolb float y = evt.getY(); 3191373b98135b2854578111dd535e24d39c8853f14Michael Kolb int action = evt.getActionMasked(); 3201373b98135b2854578111dd535e24d39c8853f14Michael Kolb if (MotionEvent.ACTION_DOWN == action) { 3211373b98135b2854578111dd535e24d39c8853f14Michael Kolb setCenter((int) x, (int) y); 3224d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb show(true); 3231373b98135b2854578111dd535e24d39c8853f14Michael Kolb return true; 3241373b98135b2854578111dd535e24d39c8853f14Michael Kolb } else if (MotionEvent.ACTION_UP == action) { 325ad639dff251233032734106afe4fe51a52be3721Michael Kolb if (isVisible()) { 3261373b98135b2854578111dd535e24d39c8853f14Michael Kolb PieItem item = mCurrentItem; 3271373b98135b2854578111dd535e24d39c8853f14Michael Kolb if (!mAnimating) { 3281373b98135b2854578111dd535e24d39c8853f14Michael Kolb deselect(); 3291373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3301373b98135b2854578111dd535e24d39c8853f14Michael Kolb show(false); 3311373b98135b2854578111dd535e24d39c8853f14Michael Kolb if ((item != null) && (item.getView() != null)) { 3321373b98135b2854578111dd535e24d39c8853f14Michael Kolb if ((item == mOpenItem) || !mAnimating) { 3331373b98135b2854578111dd535e24d39c8853f14Michael Kolb item.getView().performClick(); 3341373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3351373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3361373b98135b2854578111dd535e24d39c8853f14Michael Kolb return true; 3371373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3381373b98135b2854578111dd535e24d39c8853f14Michael Kolb } else if (MotionEvent.ACTION_CANCEL == action) { 339ad639dff251233032734106afe4fe51a52be3721Michael Kolb if (isVisible()) { 3401373b98135b2854578111dd535e24d39c8853f14Michael Kolb show(false); 3411373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3421373b98135b2854578111dd535e24d39c8853f14Michael Kolb if (!mAnimating) { 3431373b98135b2854578111dd535e24d39c8853f14Michael Kolb deselect(); 3441373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3451373b98135b2854578111dd535e24d39c8853f14Michael Kolb return false; 3461373b98135b2854578111dd535e24d39c8853f14Michael Kolb } else if (MotionEvent.ACTION_MOVE == action) { 3471373b98135b2854578111dd535e24d39c8853f14Michael Kolb if (mAnimating) return false; 3484d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb PointF polar = getPolar(x, y); 3494d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb int maxr = mRadius + mRadiusInc + 50; 3504d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb if (polar.y < mRadius) { 3514d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb if (mOpenItem != null) { 3524d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb mOpenItem = null; 3534d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb } else if (!mAnimating) { 3541373b98135b2854578111dd535e24d39c8853f14Michael Kolb deselect(); 3551373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3564d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb return false; 3574d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb } 3584d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb if (polar.y > maxr) { 3594d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb deselect(); 3604d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb show(false); 3614d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb evt.setAction(MotionEvent.ACTION_DOWN); 3624d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb return false; 3634d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb } 3644d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb PieItem item = findItem(polar); 3654d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb if (item == null) { 3664d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb } else if (mCurrentItem != item) { 3674d628bcbaf4e6f2330dc9619f2129770863666dcMichael Kolb onEnter(item); 3681373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3691373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3701373b98135b2854578111dd535e24d39c8853f14Michael Kolb return false; 3711373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3721373b98135b2854578111dd535e24d39c8853f14Michael Kolb 3731373b98135b2854578111dd535e24d39c8853f14Michael Kolb /** 3741373b98135b2854578111dd535e24d39c8853f14Michael Kolb * enter a slice for a view 3751373b98135b2854578111dd535e24d39c8853f14Michael Kolb * updates model only 3761373b98135b2854578111dd535e24d39c8853f14Michael Kolb * @param item 3771373b98135b2854578111dd535e24d39c8853f14Michael Kolb */ 3781373b98135b2854578111dd535e24d39c8853f14Michael Kolb private void onEnter(PieItem item) { 3791373b98135b2854578111dd535e24d39c8853f14Michael Kolb if (mCurrentItem != null) { 3801373b98135b2854578111dd535e24d39c8853f14Michael Kolb mCurrentItem.setSelected(false); 3811373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3822d3af28f91481d7f3887cb0bb9c5d06375baf787Michael Kolb if (item != null && item.isEnabled()) { 3831373b98135b2854578111dd535e24d39c8853f14Michael Kolb item.setSelected(true); 3841373b98135b2854578111dd535e24d39c8853f14Michael Kolb mCurrentItem = item; 3851373b98135b2854578111dd535e24d39c8853f14Michael Kolb if ((mCurrentItem != mOpenItem) && mCurrentItem.hasItems()) { 3861373b98135b2854578111dd535e24d39c8853f14Michael Kolb mHandler.sendEmptyMessageDelayed(MSG_SUBMENU, PIE_OPEN_DELAY); 3871373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3881373b98135b2854578111dd535e24d39c8853f14Michael Kolb } else { 3891373b98135b2854578111dd535e24d39c8853f14Michael Kolb mCurrentItem = null; 3901373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3911373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3921373b98135b2854578111dd535e24d39c8853f14Michael Kolb 3931373b98135b2854578111dd535e24d39c8853f14Michael Kolb private void deselect() { 3941373b98135b2854578111dd535e24d39c8853f14Michael Kolb if (mCurrentItem != null) { 3951373b98135b2854578111dd535e24d39c8853f14Michael Kolb mCurrentItem.setSelected(false); 3961373b98135b2854578111dd535e24d39c8853f14Michael Kolb mHandler.removeMessages(MSG_SUBMENU); 3971373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 3981373b98135b2854578111dd535e24d39c8853f14Michael Kolb if (mOpenItem != null) { 3991373b98135b2854578111dd535e24d39c8853f14Michael Kolb mOpenItem = null; 4001373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 4011373b98135b2854578111dd535e24d39c8853f14Michael Kolb mCurrentItem = null; 4021373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 4031373b98135b2854578111dd535e24d39c8853f14Michael Kolb 4041373b98135b2854578111dd535e24d39c8853f14Michael Kolb private void openCurrentItem() { 4051373b98135b2854578111dd535e24d39c8853f14Michael Kolb if ((mCurrentItem != null) && mCurrentItem.hasItems()) { 4061373b98135b2854578111dd535e24d39c8853f14Michael Kolb mOpenItem = mCurrentItem; 4071373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 4081373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 4091373b98135b2854578111dd535e24d39c8853f14Michael Kolb 4101373b98135b2854578111dd535e24d39c8853f14Michael Kolb private PointF getPolar(float x, float y) { 4111373b98135b2854578111dd535e24d39c8853f14Michael Kolb PointF res = new PointF(); 4121373b98135b2854578111dd535e24d39c8853f14Michael Kolb // get angle and radius from x/y 4131373b98135b2854578111dd535e24d39c8853f14Michael Kolb res.x = (float) Math.PI / 2; 4141373b98135b2854578111dd535e24d39c8853f14Michael Kolb x = x - mCenter.x; 4151373b98135b2854578111dd535e24d39c8853f14Michael Kolb y = mCenter.y - y; 416a521253223897b099995e0af08982b4236ce5861Michael Kolb res.y = (float) Math.sqrt(x * x + y * y); 4171373b98135b2854578111dd535e24d39c8853f14Michael Kolb if (x != 0) { 4181373b98135b2854578111dd535e24d39c8853f14Michael Kolb res.x = (float) Math.atan2(y, x); 419b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb if (res.x < 0) { 420b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb res.x = (float) (2 * Math.PI + res.x); 421b5b59b88683823d70faac7b3454519d53c51180bMichael Kolb } 4221373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 423a521253223897b099995e0af08982b4236ce5861Michael Kolb res.y = res.y + mTouchOffset; 4241373b98135b2854578111dd535e24d39c8853f14Michael Kolb return res; 4251373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 4261373b98135b2854578111dd535e24d39c8853f14Michael Kolb 4271373b98135b2854578111dd535e24d39c8853f14Michael Kolb /** 4281373b98135b2854578111dd535e24d39c8853f14Michael Kolb * @param polar x: angle, y: dist 4291373b98135b2854578111dd535e24d39c8853f14Michael Kolb * @return the item at angle/dist or null 4301373b98135b2854578111dd535e24d39c8853f14Michael Kolb */ 4311373b98135b2854578111dd535e24d39c8853f14Michael Kolb private PieItem findItem(PointF polar) { 4321373b98135b2854578111dd535e24d39c8853f14Michael Kolb // find the matching item: 4331373b98135b2854578111dd535e24d39c8853f14Michael Kolb List<PieItem> items = (mOpenItem != null) ? mOpenItem.getItems() : mItems; 4341373b98135b2854578111dd535e24d39c8853f14Michael Kolb for (PieItem item : items) { 435a521253223897b099995e0af08982b4236ce5861Michael Kolb if (inside(polar, item)) { 4361373b98135b2854578111dd535e24d39c8853f14Michael Kolb return item; 4371373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 4381373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 4391373b98135b2854578111dd535e24d39c8853f14Michael Kolb return null; 4401373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 4411373b98135b2854578111dd535e24d39c8853f14Michael Kolb 442a521253223897b099995e0af08982b4236ce5861Michael Kolb private boolean inside(PointF polar, PieItem item) { 443a521253223897b099995e0af08982b4236ce5861Michael Kolb return (item.getInnerRadius() < polar.y) 444a521253223897b099995e0af08982b4236ce5861Michael Kolb && (item.getOuterRadius() > polar.y) 4451373b98135b2854578111dd535e24d39c8853f14Michael Kolb && (item.getStartAngle() < polar.x) 4461373b98135b2854578111dd535e24d39c8853f14Michael Kolb && (item.getStartAngle() + item.getSweep() > polar.x); 4471373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 4481373b98135b2854578111dd535e24d39c8853f14Michael Kolb 4491373b98135b2854578111dd535e24d39c8853f14Michael Kolb @Override 4501373b98135b2854578111dd535e24d39c8853f14Michael Kolb public boolean handlesTouch() { 4511373b98135b2854578111dd535e24d39c8853f14Michael Kolb return true; 4521373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 4531373b98135b2854578111dd535e24d39c8853f14Michael Kolb 4541373b98135b2854578111dd535e24d39c8853f14Michael Kolb @Override 4551373b98135b2854578111dd535e24d39c8853f14Michael Kolb public void layout(int l, int t, int r, int b) { 4561373b98135b2854578111dd535e24d39c8853f14Michael Kolb super.layout(l, t, r, b); 4571373b98135b2854578111dd535e24d39c8853f14Michael Kolb } 4581373b98135b2854578111dd535e24d39c8853f14Michael Kolb 459c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb private class AlphaAnimation extends Animation { 460c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb @Override 461c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb protected void applyTransformation(float interpolatedTime, Transformation t) { 462c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb mAlpha = 1 - interpolatedTime; 463c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb } 464c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb } 465c605826581f2ef1640828af82dbf26a70d4c7c78Michael Kolb 4661373b98135b2854578111dd535e24d39c8853f14Michael Kolb} 467