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