IndicatorControlWheel.java revision e5fe56fec047dec5ccb12c2001bacfd9455352da
16f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li/*
26f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * Copyright (C) 2010 The Android Open Source Project
36f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li *
46f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * Licensed under the Apache License, Version 2.0 (the "License");
56f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * you may not use this file except in compliance with the License.
66f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * You may obtain a copy of the License at
76f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li *
86f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li *      http://www.apache.org/licenses/LICENSE-2.0
96f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li *
106f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * Unless required by applicable law or agreed to in writing, software
116f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * distributed under the License is distributed on an "AS IS" BASIS,
126f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * See the License for the specific language governing permissions and
146f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * limitations under the License.
156f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li */
166f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
176f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Lipackage com.android.camera.ui;
186f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
196f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport com.android.camera.R;
2016ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Liimport com.android.camera.Util;
216f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
226f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport android.content.Context;
23e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyanimport android.content.res.Resources;
2480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Liimport android.graphics.Canvas;
2580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Liimport android.graphics.Paint;
2680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Liimport android.graphics.RectF;
27a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Liimport android.os.SystemClock;
286f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport android.util.AttributeSet;
29dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Liimport android.view.MotionEvent;
306f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport android.view.View;
316f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
326f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li/**
3380715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li * A view that contains shutter button and camera setting indicators. The
3480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li * indicators are spreaded around the shutter button. The first child is always
35c5e37cbb96b8284203560c51dd5f41c705a21f58Wu-cheng Li * the shutter button.
366f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li */
37bed7386688cd7810cba23732ab71f370fe3222ffChung-yih Wangpublic class IndicatorWheel extends IndicatorControl {
38f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li    private static final String TAG = "IndicatorWheel";
39bed7386688cd7810cba23732ab71f370fe3222ffChung-yih Wang
40e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan    // The width of the edges on both sides of the wheel, which has less alpha.
41e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan    private static final float EDGE_STROKE_WIDTH = 6f;
42e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan    private static final int HIGHLIGHT_WIDTH = 4;
43e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan    private static final int HIGHLIGHT_DEGREE = 30;
44a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private static final int TIME_LAPSE_ARC_WIDTH = 6;
45e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan
46e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan    private final int HIGHLIGHT_COLOR;
47a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private final int TIME_LAPSE_ARC_COLOR;
48e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan
4980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    // The center of the shutter button.
50f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li    private int mCenterX, mCenterY;
5180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    // The width of the wheel stroke.
5241ac285a1c97b1df78246456effb80de51033323Hung-ying Tyan    private int mStrokeWidth = 60;
5380715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    private View mShutterButton;
54f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li    private double mShutterButtonRadius;
5580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    private double mWheelRadius;
56f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li    private double mSectorInitialRadians[];
5780715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    private Paint mBackgroundPaint;
5880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    private RectF mBackgroundRect;
59e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li    // The index of the child that is being pressed. -1 means no child is being
60e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li    // pressed.
61039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li    private int mPressedIndex = -1;
62dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li
63a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    // Time lapse recording variables.
64a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private int mTimeLapseInterval;  // in ms
65a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private long mRecordingStartTime = 0;
66a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private long mNumberOfFrames = 0;
67a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
6880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    public IndicatorWheel(Context context, AttributeSet attrs) {
6980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        super(context, attrs);
70e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan        Resources resources = context.getResources();
71e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan        HIGHLIGHT_COLOR = resources.getColor(R.color.review_control_pressed_color);
72a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        TIME_LAPSE_ARC_COLOR = resources.getColor(R.color.time_lapse_arc);
73bed7386688cd7810cba23732ab71f370fe3222ffChung-yih Wang
7480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        setWillNotDraw(false);
7580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li
7680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mBackgroundPaint = new Paint();
7780715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mBackgroundPaint.setStyle(Paint.Style.STROKE);
7880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mBackgroundPaint.setAntiAlias(true);
7980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li
8080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mBackgroundRect = new RectF();
8180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    }
8280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li
83dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li    @Override
84039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li    public boolean dispatchTouchEvent(MotionEvent event) {
85039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        if (!onFilterTouchEventForSecurity(event)) return false;
861c62e1b83c2fdda82c726bf712324444136fdc5dHung-ying Tyan
87f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li        int action = event.getAction();
88039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li
8916ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        double dx = event.getX() - mCenterX;
9016ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        double dy = mCenterY - event.getY();
9116ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        double radius = Math.sqrt(dx * dx + dy * dy);
9216ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li
93039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        // Check if the event should be dispatched to the shutter button.
9416ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        if (radius <= mShutterButtonRadius) {
95039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP) {
96e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li                mPressedIndex = (action == MotionEvent.ACTION_DOWN) ? 0 : -1;
97039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                return mShutterButton.dispatchTouchEvent(event);
98039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            }
997341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Li            return false;
1007341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Li        }
101f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li
102e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li        // Send cancel to the shutter button if it was pressed.
103e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li        if (mPressedIndex == 0) {
104e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li            event.setAction(MotionEvent.ACTION_CANCEL);
105e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li            mShutterButton.dispatchTouchEvent(event);
106e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li            mPressedIndex = -1;
107e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li            return true;
108e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li        }
109e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li
1107341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Li        // Ignore the event if it's too near to the shutter button or too far
1117341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Li        // from the shutter button.
11216ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        if (radius <= mWheelRadius + mStrokeWidth) {
113f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li            double delta = Math.atan2(dy, dx);
114f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li            if (delta < 0) delta += Math.PI * 2;
115f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li            // Check which sector is pressed.
116f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li            if (delta > mSectorInitialRadians[0]) {
117e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li                for (int i = 1; i < getChildCount(); i++) {
118e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li                    if (delta < mSectorInitialRadians[i]) {
119e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li                        View child = getChildAt(i);
120039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                        if (action == MotionEvent.ACTION_DOWN) {
121039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                            if (child instanceof AbstractIndicatorButton) {
122039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                                AbstractIndicatorButton b = (AbstractIndicatorButton) child;
123039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                                // If the same setting is pressed when the popup is open,
124039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                                // do not dismiss it because it will be handled in the child.
125039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                                if (b.getPopupWindow() == null) {
126039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                                    dismissSettingPopup();
127fba0439b62f19a2a19ff6559c1ae055e910400dbWu-cheng Li                                }
128039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                            } else {
129039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                                // Zoom button or back/front camera switch is pressed.
130039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                                dismissSettingPopup();
131823d57389b616091827059ca6401f675e416a53bWu-cheng Li                            }
132c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                            if (child.dispatchTouchEvent(event)) {
133c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                                mPressedIndex = i;
134c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                            }
135039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                            invalidate();
136039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                        } else if (action == MotionEvent.ACTION_UP) {
137039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                            child.dispatchTouchEvent(event);
138039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                            invalidate();
139039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                            mPressedIndex = -1;
140039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                        } else if (action == MotionEvent.ACTION_MOVE) {
141039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                            // Dispatch the event if the location across a sector.
142c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                            if (i != mPressedIndex) {
143039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                                dismissSettingPopup();
144039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                                // Cancel the previous one.
145c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                                if (mPressedIndex != -1) {
146e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li                                    View cancelChild = getChildAt(mPressedIndex);
147c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                                    event.setAction(MotionEvent.ACTION_CANCEL);
148c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                                    cancelChild.dispatchTouchEvent(event);
149c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                                    mPressedIndex = -1;
150c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                                }
151039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                                // Send down to the current one.
152039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                                event.setAction(MotionEvent.ACTION_DOWN);
153c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                                if (child.dispatchTouchEvent(event)) {
154c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                                    mPressedIndex = i;
155c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                                }
156039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                                invalidate();
1574504e5b41a0f647ef3e16b1134e130fe3678aebfWu-cheng Li                            }
158c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                            // The children do not care about ACTION_MOVE.
159c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                            // Besides, the press state will be wrong
160c6bf6547259e75a4fc0e3ac61ab589f456031837Wu-cheng Li                            // because of View.pointInView.
1614504e5b41a0f647ef3e16b1134e130fe3678aebfWu-cheng Li                        }
162f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li                        return true;
163f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li                    }
164f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li                }
165dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li            }
166dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li        }
167039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        // The event is not on any of the child.
1684504e5b41a0f647ef3e16b1134e130fe3678aebfWu-cheng Li        dismissSettingPopup();
169039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        if (mPressedIndex != -1) {
170e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li            View cancelChild = getChildAt(mPressedIndex);
171039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            event.setAction(MotionEvent.ACTION_CANCEL);
172039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            cancelChild.dispatchTouchEvent(event);
173039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            mPressedIndex = -1;
174039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        }
175039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        invalidate();
176dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li        return false;
177dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li    }
1786f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
17980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    @Override
18080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    protected void onFinishInflate() {
18180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        super.onFinishInflate();
18280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        // The first view is shutter button.
18380715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mShutterButton = getChildAt(0);
184248232e937007384328592e0c269b42cf03c6899Wu-cheng Li        invalidate();
1856f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li    }
1866f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
1876f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li    @Override
1886f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li    protected void onMeasure(int widthSpec, int heightSpec) {
1896f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        // Measure all children.
19080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        int childCount = getChildCount();
1916f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
19280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        for (int i = 0; i < childCount; i++) {
1936f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            getChildAt(i).measure(freeSpec, freeSpec);
1946f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        }
1956f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
19616ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        // Measure myself. Add some buffer for highlight arc.
19716ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        int desiredWidth = mShutterButton.getMeasuredWidth() + HIGHLIGHT_WIDTH * 4;
19816ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        int desiredHeight = mShutterButton.getMeasuredHeight() + HIGHLIGHT_WIDTH * 4;
1996f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        int widthMode = MeasureSpec.getMode(widthSpec);
2006f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        int heightMode = MeasureSpec.getMode(heightSpec);
2016f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        int measuredWidth, measuredHeight;
2026f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        if (widthMode == MeasureSpec.UNSPECIFIED) {
2036f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            measuredWidth = desiredWidth;
2046f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        } else if (widthMode == MeasureSpec.AT_MOST) {
2056f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            measuredWidth = Math.min(desiredWidth, MeasureSpec.getSize(widthSpec));
2066f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        } else {  // MeasureSpec.EXACTLY
2076f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            measuredWidth = MeasureSpec.getSize(widthSpec);
2086f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        }
2096f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        if (heightMode == MeasureSpec.UNSPECIFIED) {
2106f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            measuredHeight = desiredHeight;
2116f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        } else if (heightMode == MeasureSpec.AT_MOST) {
2126f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            measuredHeight = Math.min(desiredHeight, MeasureSpec.getSize(heightSpec));
2136f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        } else {  // MeasureSpec.EXACTLY
2146f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            measuredHeight = MeasureSpec.getSize(heightSpec);
2156f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        }
2166f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        setMeasuredDimension(measuredWidth, measuredHeight);
2176f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li    }
2186f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
2196f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li    @Override
2206f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li    protected void onLayout(
2216f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            boolean changed, int left, int top, int right, int bottom) {
2226f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        int count = getChildCount();
2236f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        if (count == 0) return;
2246f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
2256f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        // Layout the shutter button.
22680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        int shutterButtonWidth = mShutterButton.getMeasuredWidth();
22780715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        int shutterButtonHeight = mShutterButton.getMeasuredHeight();
22816ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        // These numbers have to sync with the background of the shutter button.
22916ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        mShutterButtonRadius = Util.dpToPixel(74);
23016ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        mStrokeWidth = Util.dpToPixel(87);
23116ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        mCenterX = right - left - Util.dpToPixel(93);
23262776cd28a40b4c6ada4b4aa38683f45572ae266Wu-cheng Li        mCenterY = (bottom - top) / 2;
23316ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        mShutterButton.layout(right - left - shutterButtonWidth,
23480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li                mCenterY - shutterButtonHeight / 2,
23516ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li                right - left,
23616ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li                mCenterY + shutterButtonHeight - shutterButtonHeight / 2);
2376f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
2386f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        // Layout the settings. The icons are spreaded on the left side of the
2396f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        // shutter button. So the angle starts from 90 to 270 degrees.
2406f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        if (count == 1) return;
24116ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        mWheelRadius = mShutterButtonRadius + mStrokeWidth * 0.5;
24219d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan        double intervalDegrees = (count == 2) ? 90.0 : 180.0 / (count - 2);
2436f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        double initialDegrees = 90.0;
2446f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        int index = 0;
245f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li        for (int i = 0; i < count; i++) {
2466f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            View view = getChildAt(i);
24780715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            if (view == mShutterButton) continue;
2486f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            double degree = initialDegrees + intervalDegrees * index;
249f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li            double radian = Math.toRadians(degree);
25080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            int x = mCenterX + (int)(mWheelRadius * Math.cos(radian));
25180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            int y = mCenterY - (int)(mWheelRadius * Math.sin(radian));
25280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            int width = view.getMeasuredWidth();
25380715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            int height = view.getMeasuredHeight();
2546f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            view.layout(x - width / 2, y - height / 2, x + width / 2,
2556f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li                    y + height / 2);
2566f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            index++;
2576f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li        }
2586f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
259f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li        // Store the radian intervals for each icon.
260f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li        mSectorInitialRadians = new double[count];
261f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li        mSectorInitialRadians[0] = Math.toRadians(
262f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li                initialDegrees - intervalDegrees / 2.0);
263f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li        for (int i = 1; i < count; i++) {
264f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li            mSectorInitialRadians[i] = mSectorInitialRadians[i - 1]
265f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li                    + Math.toRadians(intervalDegrees);
266f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li        }
2676f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li    }
2686f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
269a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    public void startTimeLapseAnimation(int timeLapseInterval, long startTime) {
270a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        mTimeLapseInterval = timeLapseInterval;
271a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        mRecordingStartTime = startTime;
272a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        mNumberOfFrames = 0;
273a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        invalidate();
274a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    }
275a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
276a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    public void stopTimeLapseAnimation() {
277a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        mTimeLapseInterval = 0;
278a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        invalidate();
279a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    }
280a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
281039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li    private int getSelectedIndicatorIndex() {
282039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        for (int i = 0; i < mIndicators.size(); i++) {
283039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            AbstractIndicatorButton b = mIndicators.get(i);
284039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            if (b.getPopupWindow() != null) {
285039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                return indexOfChild(b);
286039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            }
287039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        }
288039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        return -1;
289039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li    }
290039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li
29180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    @Override
29280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    protected void onDraw(Canvas canvas) {
29319d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan        // Draw highlight.
29474c5164d19fa213c6904639e6e6b0d9e542cbf66Hung-ying Tyan        float delta = mStrokeWidth * 0.5f;
29574c5164d19fa213c6904639e6e6b0d9e542cbf66Hung-ying Tyan        float radius = (float) (mWheelRadius + mStrokeWidth * 0.5 + EDGE_STROKE_WIDTH);
296039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        mBackgroundRect.set(mCenterX - radius, mCenterY - radius, mCenterX + radius,
297039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                 mCenterY + radius);
298039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li
299039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        int selectedIndex = getSelectedIndicatorIndex();
300039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li
301039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        // Draw the highlight arc if an indicator is selected or being pressed.
302e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li        if (selectedIndex >= 0 || mPressedIndex > 0) {
30319d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan            int count = getChildCount();
30419d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan            float initialDegrees = 90.0f;
30519d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan            float intervalDegrees = (count <= 2) ? 0.0f : 180.0f / (count - 2);
306039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            float degree;
307039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            if (selectedIndex >= 0) {
308039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                degree = initialDegrees + intervalDegrees * (selectedIndex - 1);
309039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            } else {
310e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li                degree = initialDegrees + intervalDegrees * (mPressedIndex - 1);
311039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            }
31219d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan            mBackgroundPaint.setStrokeWidth(HIGHLIGHT_WIDTH);
31319d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan            mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
31419d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan            mBackgroundPaint.setColor(HIGHLIGHT_COLOR);
31519d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan            canvas.drawArc(mBackgroundRect, -degree - HIGHLIGHT_DEGREE / 2,
31619d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan                    HIGHLIGHT_DEGREE, false, mBackgroundPaint);
31719d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan        }
31819d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan
319a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        // Draw arc shaped indicator in time lapse recording.
320a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        if (mTimeLapseInterval != 0) {
321a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            // Setup rectangle and paint.
322a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundRect.set((float)(mCenterX - mShutterButtonRadius),
323a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li                    (float)(mCenterY - mShutterButtonRadius),
324a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li                    (float)(mCenterX + mShutterButtonRadius),
325a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li                    (float)(mCenterY + mShutterButtonRadius));
326a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundRect.inset(3f, 3f);
327a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundPaint.setStrokeWidth(TIME_LAPSE_ARC_WIDTH);
328a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
329a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundPaint.setColor(TIME_LAPSE_ARC_COLOR);
330a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
331a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            // Compute the start angle and sweep angle.
332a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            long timeDelta = SystemClock.uptimeMillis() - mRecordingStartTime;
333a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            long numberOfFrames = timeDelta / mTimeLapseInterval;
334248232e937007384328592e0c269b42cf03c6899Wu-cheng Li            float sweepAngle;
335a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            if (numberOfFrames > mNumberOfFrames) {
336248232e937007384328592e0c269b42cf03c6899Wu-cheng Li                // The arc just acrosses 0 degree. Draw a full circle so it
337248232e937007384328592e0c269b42cf03c6899Wu-cheng Li                // looks better.
338248232e937007384328592e0c269b42cf03c6899Wu-cheng Li                sweepAngle = 360;
339a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li                mNumberOfFrames = numberOfFrames;
340a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            } else {
341248232e937007384328592e0c269b42cf03c6899Wu-cheng Li                sweepAngle = timeDelta % mTimeLapseInterval * 360f / mTimeLapseInterval;
342a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            }
343a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
344248232e937007384328592e0c269b42cf03c6899Wu-cheng Li            canvas.drawArc(mBackgroundRect, 0, sweepAngle, false, mBackgroundPaint);
345248232e937007384328592e0c269b42cf03c6899Wu-cheng Li            invalidate();
346a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        }
347a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
34880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        super.onDraw(canvas);
34980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    }
3506f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li}
351