IndicatorControlWheel.java revision a18e1141ed70a052d257a641c9a8b6f152a81d95
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
1995788ace8ad3630312f2f94544690c448c935f9eChung-yih Wangimport com.android.camera.PreferenceGroup;
206f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport com.android.camera.R;
2116ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Liimport com.android.camera.Util;
226f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
236f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport android.content.Context;
24e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyanimport android.content.res.Resources;
2580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Liimport android.graphics.Canvas;
2680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Liimport android.graphics.Paint;
2780715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Liimport android.graphics.RectF;
28ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wangimport android.os.Handler;
29a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Liimport android.os.SystemClock;
306f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport android.util.AttributeSet;
31dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Liimport android.view.MotionEvent;
326f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport android.view.View;
33ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wangimport android.widget.ImageView;
346f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
356f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li/**
36ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang * A view that contains camera setting indicators in two levels. The first-level
37ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang * indicators including the zoom, camera picker, flash and second-level control.
38ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang * The second-level indicators are the merely for the camera settings.
396f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li */
40ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wangpublic class IndicatorControlWheel extends IndicatorControl implements
41ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        View.OnClickListener {
42ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    public static final int HIGHLIGHT_WIDTH = 4;
43ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang
44ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    private static final String TAG = "IndicatorControlWheel";
45ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int HIGHLIGHT_DEGREES = 30;
46ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final double HIGHLIGHT_RADIANS = Math.toRadians(HIGHLIGHT_DEGREES);
47ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
48ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    // The following angles are based in the zero degree on the right. Here we
49ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    // have the fix 45 degrees for each sector in the first-level as we have to
50ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    // align the zoom button exactly at degree 180. For second-level indicators,
51ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    // the indicators located evenly between start and end angle. In addition,
52ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    // these indicators for the second-level hidden in the same wheel with
53ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    // larger angle values are visible after rotation.
54ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int FIRST_LEVEL_END_DEGREES = 270;
55ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int FIRST_LEVEL_SECTOR_DEGREES = 45;
56ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int SECOND_LEVEL_START_DEGREES = 60;
57ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int SECOND_LEVEL_END_DEGREES = 300;
58ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int CLOSE_ICON_DEFAULT_DEGREES = 315;
59ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang    private static final int ZOOM_ICON_DEFAULT_DEGREES = 180;
60ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
61ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int ANIMATION_TIME = 300; // milliseconds
62bed7386688cd7810cba23732ab71f370fe3222ffChung-yih Wang
63e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan    // The width of the edges on both sides of the wheel, which has less alpha.
64e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan    private static final float EDGE_STROKE_WIDTH = 6f;
65a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private static final int TIME_LAPSE_ARC_WIDTH = 6;
66e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan
67e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan    private final int HIGHLIGHT_COLOR;
68a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private final int TIME_LAPSE_ARC_COLOR;
69e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan
7080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    // The center of the shutter button.
71f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li    private int mCenterX, mCenterY;
7280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    // The width of the wheel stroke.
73ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    private int mStrokeWidth;
74f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li    private double mShutterButtonRadius;
7580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    private double mWheelRadius;
76ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    private double mChildRadians[];
7780715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    private Paint mBackgroundPaint;
7880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    private RectF mBackgroundRect;
79e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li    // The index of the child that is being pressed. -1 means no child is being
80e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li    // pressed.
81039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li    private int mPressedIndex = -1;
82dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li
83a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    // Time lapse recording variables.
84a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private int mTimeLapseInterval;  // in ms
85a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private long mRecordingStartTime = 0;
86a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private long mNumberOfFrames = 0;
87a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
88ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    // Remember the last event for event cancelling if out of bound.
89ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    private MotionEvent mLastMotionEvent;
90ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang
91ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private ImageView mSecondLevelIcon;
92ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private ImageView mCloseIcon;
93ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
94ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    // Variables for animation.
95ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private long mAnimationStartTime;
96ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private boolean mInAnimation = false;
97ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private Handler mHandler = new Handler();
98ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private final Runnable mRunnable = new Runnable() {
99ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        public void run() {
100ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            requestLayout();
101ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
102ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    };
103ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
104ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    // Variables for level control.
105ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private int mCurrentLevel = 0;
106ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private int mSecondLevelStartIndex = -1;
107ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private double mStartVisibleRadians[] = new double[2];
108ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private double mEndVisibleRadians[] = new double[2];
109ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private double mSectorRadians[] = new double[2];
110ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private double mTouchSectorRadians[] = new double[2];
111ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
112b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang    private ImageView mZoomIcon;
113ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
114ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    public IndicatorControlWheel(Context context, AttributeSet attrs) {
11580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        super(context, attrs);
116e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan        Resources resources = context.getResources();
117e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan        HIGHLIGHT_COLOR = resources.getColor(R.color.review_control_pressed_color);
118a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        TIME_LAPSE_ARC_COLOR = resources.getColor(R.color.time_lapse_arc);
119bed7386688cd7810cba23732ab71f370fe3222ffChung-yih Wang
12080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        setWillNotDraw(false);
12180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li
12280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mBackgroundPaint = new Paint();
12380715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mBackgroundPaint.setStyle(Paint.Style.STROKE);
12480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mBackgroundPaint.setAntiAlias(true);
12580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li
12680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mBackgroundRect = new RectF();
127ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    }
128ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
129ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private int getChildCountByLevel(int level) {
130ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // Get current child count by level.
131ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if (level == 1) {
132ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            return (getChildCount() - mSecondLevelStartIndex);
133ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        } else {
134ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            return mSecondLevelStartIndex;
135ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
136ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    }
137ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
138a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang    private void changeIndicatorsLevel() {
139ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mPressedIndex = -1;
140ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        dismissSettingPopup();
141ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mInAnimation = true;
142ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mAnimationStartTime = SystemClock.uptimeMillis();
143ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        requestLayout();
14480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    }
14580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li
146a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang    @Override
147a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang    public void onClick(View view) {
148a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang        if (view == mZoomIcon) return;
149a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang        changeIndicatorsLevel();
150a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang    }
151a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang
15295788ace8ad3630312f2f94544690c448c935f9eChung-yih Wang    public void initialize(Context context, PreferenceGroup group,
153b7d0a0704e437a1892fbf2a6e5138cf36df9fde8Chung-yih Wang            boolean isZoomSupported, String[] keys, String[] otherSettingKeys) {
154ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        mShutterButtonRadius = IndicatorControlWheelContainer.SHUTTER_BUTTON_RADIUS;
155ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        mStrokeWidth = Util.dpToPixel(IndicatorControlWheelContainer.STROKE_WIDTH);
156ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        mWheelRadius = mShutterButtonRadius + mStrokeWidth * 0.5;
157ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang
158ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        setPreferenceGroup(group);
159ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang
160ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        // Add Zoom Icon.
161ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        if (isZoomSupported) {
162ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang            mZoomIcon = (ImageView) addImageButton(context, R.drawable.ic_zoom_holo_light, false);
163ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        }
1640935e09bb12a1886a801713bbd78bbc20bf4ea4eChih-yu Huang
165ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        // Add CameraPicker.
166ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        initializeCameraPicker();
1670935e09bb12a1886a801713bbd78bbc20bf4ea4eChih-yu Huang
168ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // Add second-level Indicator Icon.
169ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mSecondLevelIcon = addImageButton(context, R.drawable.ic_settings_holo_light, true);
170ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mSecondLevelStartIndex = getChildCount();
171ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
172ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // Add second-level buttons.
173ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mCloseIcon = addImageButton(context, R.drawable.btn_wheel_close_settings, false);
174ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        addControls(keys, otherSettingKeys);
175ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
176ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // The angle(in radians) of each icon for touch events.
177ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        mChildRadians = new double[getChildCount()];
178ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        presetFirstLevelChildRadians();
179ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        presetSecondLevelChildRadians();
180ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    }
181ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
182ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private ImageView addImageButton(Context context, int resourceId, boolean rotatable) {
183b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang        ImageView view;
184b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang        if (rotatable) {
185b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang            view = new RotateImageView(context);
186b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang        } else {
187b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang            view = new ColorFilterImageView(context);
188b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang        }
189ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        view.setImageResource(resourceId);
190ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        view.setOnClickListener(this);
191ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        addView(view);
192ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        return view;
193ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    }
194ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang
195ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    private int getTouchIndicatorIndex(double delta) {
196ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // The delta is the angle of touch point in radians.
197ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if (mInAnimation) return -1;
198ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int count = getChildCountByLevel(mCurrentLevel);
199ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        if (count == 0) return -1;
200ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int startIndex = (mCurrentLevel == 0) ? 0 : mSecondLevelStartIndex;
201ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        int sectors = count - 1;
202ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        // Check which indicator is touched.
203ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if ((delta >= (mStartVisibleRadians[mCurrentLevel] - HIGHLIGHT_RADIANS / 2)) &&
204ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            (delta <= (mEndVisibleRadians[mCurrentLevel] + HIGHLIGHT_RADIANS / 2))) {
205ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            int index = (int) ((delta - mStartVisibleRadians[mCurrentLevel])
206ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                    / mSectorRadians[mCurrentLevel]);
207ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
208ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            // greater than the center of ending indicator
209ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            if (index > sectors) return (startIndex + sectors);
210ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            // less than the center of starting indicator
211ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            if (index < 0) return startIndex;
212ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
213ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            if (delta <= (mChildRadians[startIndex + index]
214ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                    + mTouchSectorRadians[mCurrentLevel] / 2)) {
215ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                return (startIndex + index);
216ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            }
217ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            if (delta >= (mChildRadians[startIndex + index + 1]
218ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                    - mTouchSectorRadians[mCurrentLevel] / 2)) {
219ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                return (startIndex + index + 1);
220ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            }
221ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        }
222ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        return -1;
223ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    }
224ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang
225ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    private void injectMotionEvent(int viewIndex, MotionEvent event, int action) {
226ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        View v = getChildAt(viewIndex);
227ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        event.setAction(action);
228ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        v.dispatchTouchEvent(event);
22995788ace8ad3630312f2f94544690c448c935f9eChung-yih Wang    }
23095788ace8ad3630312f2f94544690c448c935f9eChung-yih Wang
23195788ace8ad3630312f2f94544690c448c935f9eChung-yih Wang    @Override
232039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li    public boolean dispatchTouchEvent(MotionEvent event) {
233039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        if (!onFilterTouchEventForSecurity(event)) return false;
234ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        mLastMotionEvent = event;
235f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li        int action = event.getAction();
236039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li
23716ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        double dx = event.getX() - mCenterX;
23816ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        double dy = mCenterY - event.getY();
23916ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        double radius = Math.sqrt(dx * dx + dy * dy);
24016ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li
241ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        // Ignore the event if too far from the shutter button.
242ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if ((radius <= (mWheelRadius + mStrokeWidth)) && (radius > mShutterButtonRadius)) {
243f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li            double delta = Math.atan2(dy, dx);
244f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li            if (delta < 0) delta += Math.PI * 2;
245ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            int index = getTouchIndicatorIndex(delta);
246ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            // Move over from one indicator to another.
247ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            if ((index != mPressedIndex) || (action == MotionEvent.ACTION_DOWN)) {
248ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                if (mPressedIndex != -1) {
249ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                    injectMotionEvent(mPressedIndex, event, MotionEvent.ACTION_CANCEL);
250ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                } else {
251ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                    // Cancel the popup if it is different from the selected.
252ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                    if (getSelectedIndicatorIndex() != index) dismissSettingPopup();
253ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                }
254ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                if ((index != -1) && (action == MotionEvent.ACTION_MOVE)) {
255ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                    injectMotionEvent(index, event, MotionEvent.ACTION_DOWN);
256f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li                }
257dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li            }
258ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            if ((index != -1) && (action != MotionEvent.ACTION_MOVE)) {
259ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang                View view = getChildAt(index);
260ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang                // Switch to zoom control only if a touch down event is received.
2610bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li                if ((view == mZoomIcon) && (action == MotionEvent.ACTION_DOWN)
2620bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li                        && mZoomIcon.isEnabled()) {
263ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang                    mPressedIndex = -1;
264ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang                    mOnIndicatorEventListener.onIndicatorEvent(
265ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang                            OnIndicatorEventListener.EVENT_ENTER_ZOOM_CONTROL);
266ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang                    return true;
267ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang                } else {
268ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang                    getChildAt(index).dispatchTouchEvent(event);
269ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang                }
270ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            }
271ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            // Once the button is up, reset the press index.
272ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            mPressedIndex = (action == MotionEvent.ACTION_UP) ? -1 : index;
273ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            invalidate();
274ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            return true;
275dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li        }
276039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        // The event is not on any of the child.
277ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        onTouchOutBound();
278dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li        return false;
279dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li    }
2806f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
281ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private void rotateWheel() {
282ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int totalDegrees = CLOSE_ICON_DEFAULT_DEGREES - SECOND_LEVEL_START_DEGREES;
283ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int startAngle = ((mCurrentLevel == 0) ? CLOSE_ICON_DEFAULT_DEGREES
284ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                                               : SECOND_LEVEL_START_DEGREES);
285ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if (mCurrentLevel == 0) totalDegrees = -totalDegrees;
286ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
287ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int elapsedTime = (int) (SystemClock.uptimeMillis() - mAnimationStartTime);
288ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if (elapsedTime >= ANIMATION_TIME) {
289ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            elapsedTime = ANIMATION_TIME;
290ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            mCurrentLevel = (mCurrentLevel == 0) ? 1 : 0;
291ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            mInAnimation = false;
292ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
293ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
294ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int expectedAngle = startAngle + (totalDegrees * elapsedTime / ANIMATION_TIME);
295ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        double increment = Math.toRadians(expectedAngle)
296ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                - mChildRadians[mSecondLevelStartIndex];
297ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        for (int i = 0 ; i < getChildCount(); ++i) mChildRadians[i] += increment;
298ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        requestLayout();
299ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang   }
300ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
30180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    @Override
3026f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li    protected void onLayout(
3036f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            boolean changed, int left, int top, int right, int bottom) {
304ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if (mInAnimation) {
305ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            rotateWheel();
306ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            mHandler.post(mRunnable);
307ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
308ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        mCenterX = right - left - Util.dpToPixel(
309ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                IndicatorControlWheelContainer.FULL_WHEEL_RADIUS);
31062776cd28a40b4c6ada4b4aa38683f45572ae266Wu-cheng Li        mCenterY = (bottom - top) / 2;
3116f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
312ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // Layout the indicators based on the current level.
313ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // The icons are spreaded on the left side of the shutter button.
314ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        for (int i = 0; i < getChildCount(); ++i) {
3156f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            View view = getChildAt(i);
316b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang            if (!view.isEnabled()) continue;
317ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            double radian = mChildRadians[i];
318ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            double startVisibleRadians = mInAnimation
319ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                    ? mStartVisibleRadians[1]
320ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                    : mStartVisibleRadians[mCurrentLevel];
321ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            double endVisibleRadians = mInAnimation
322ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                    ? mEndVisibleRadians[1]
323ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                    : mEndVisibleRadians[mCurrentLevel];
324ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            if ((radian < (startVisibleRadians - HIGHLIGHT_RADIANS / 2)) ||
325ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                    (radian > (endVisibleRadians + HIGHLIGHT_RADIANS / 2))) {
326ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                view.setVisibility(View.GONE);
327ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                continue;
328ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            }
329ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            view.setVisibility(View.VISIBLE);
33080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            int x = mCenterX + (int)(mWheelRadius * Math.cos(radian));
33180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            int y = mCenterY - (int)(mWheelRadius * Math.sin(radian));
33280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            int width = view.getMeasuredWidth();
33380715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            int height = view.getMeasuredHeight();
3346f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            view.layout(x - width / 2, y - height / 2, x + width / 2,
3356f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li                    y + height / 2);
336f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li        }
3376f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li    }
3386f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
339ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private void presetFirstLevelChildRadians() {
340ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int count = getChildCountByLevel(0);
341ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int sectors = (count <= 1) ? 0 : (count - 1);
342ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        double sectorDegrees = FIRST_LEVEL_SECTOR_DEGREES;
343ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mSectorRadians[0] = Math.toRadians(sectorDegrees);
344ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        int zoomIndex = indexOfChild(mZoomIcon);
345ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        double degrees;
346ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang
347ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        // Make sure the zoom button is located at 180 degrees. If there are
348ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        // more buttons than we could show in the visible angle from 90 degrees
349ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        // to 270 degrees, the modification of FIRST_LEVEL_SECTOR_DEGREES is
350ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        // required then.
351ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        if (zoomIndex >= 0) {
352ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang            degrees = ZOOM_ICON_DEFAULT_DEGREES - (zoomIndex * sectorDegrees);
353ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        }  else {
354ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang            degrees = FIRST_LEVEL_END_DEGREES - (sectors * sectorDegrees);
355ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        }
356ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mStartVisibleRadians[0] = Math.toRadians(degrees);
357ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
358ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int startIndex = 0;
359ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        for (int i = 0; i < count; i++) {
360ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            mChildRadians[startIndex + i] = Math.toRadians(degrees);
361ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            degrees += sectorDegrees;
362ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
363ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
364ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // The radians for the touch sector of an indicator.
365ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mTouchSectorRadians[0] = HIGHLIGHT_RADIANS;
366ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mEndVisibleRadians[0] = Math.toRadians(FIRST_LEVEL_END_DEGREES);
367ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    }
368ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
369ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private void presetSecondLevelChildRadians() {
370ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int count = getChildCountByLevel(1);
371f09f58c11f3e6b5670cfb1317bb2d7cf1e412d7cChung-yih Wang        int sectors = (count <= 1) ? 1 : (count - 1);
372ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        double sectorDegrees =
373ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                ((SECOND_LEVEL_END_DEGREES - SECOND_LEVEL_START_DEGREES) / sectors);
374ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mSectorRadians[1] = Math.toRadians(sectorDegrees);
375ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
376ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        double degrees = CLOSE_ICON_DEFAULT_DEGREES;
377ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mStartVisibleRadians[1] = Math.toRadians(SECOND_LEVEL_START_DEGREES);
378ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
379ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int startIndex = mSecondLevelStartIndex;
380ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        for (int i = 0; i < count; i++) {
381ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            mChildRadians[startIndex + i] = Math.toRadians(degrees);
382ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            degrees += sectorDegrees;
383ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
384ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
385ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // The radians for the touch sector of an indicator.
386ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mTouchSectorRadians[1] =
387ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                Math.min(HIGHLIGHT_RADIANS, Math.toRadians(sectorDegrees));
388ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
389ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mEndVisibleRadians[1] = Math.toRadians(SECOND_LEVEL_END_DEGREES);
390ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    }
391ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
392a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    public void startTimeLapseAnimation(int timeLapseInterval, long startTime) {
393a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        mTimeLapseInterval = timeLapseInterval;
394a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        mRecordingStartTime = startTime;
395a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        mNumberOfFrames = 0;
396a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        invalidate();
397a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    }
398a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
399a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    public void stopTimeLapseAnimation() {
400a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        mTimeLapseInterval = 0;
401a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        invalidate();
402a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    }
403a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
404039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li    private int getSelectedIndicatorIndex() {
405039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        for (int i = 0; i < mIndicators.size(); i++) {
406039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            AbstractIndicatorButton b = mIndicators.get(i);
407039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            if (b.getPopupWindow() != null) {
408039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                return indexOfChild(b);
409039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            }
410039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        }
411ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        if (mPressedIndex != -1) {
412ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            View v = getChildAt(mPressedIndex);
413ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            if (!(v instanceof AbstractIndicatorButton) && v.isEnabled()) {
414ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                return mPressedIndex;
415ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            }
416ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        }
417039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        return -1;
418039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li    }
419039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li
42080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    @Override
42180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    protected void onDraw(Canvas canvas) {
42219d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan        // Draw highlight.
42374c5164d19fa213c6904639e6e6b0d9e542cbf66Hung-ying Tyan        float delta = mStrokeWidth * 0.5f;
42474c5164d19fa213c6904639e6e6b0d9e542cbf66Hung-ying Tyan        float radius = (float) (mWheelRadius + mStrokeWidth * 0.5 + EDGE_STROKE_WIDTH);
425039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        mBackgroundRect.set(mCenterX - radius, mCenterY - radius, mCenterX + radius,
426039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                 mCenterY + radius);
427039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li
428039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        int selectedIndex = getSelectedIndicatorIndex();
429039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li
430039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        // Draw the highlight arc if an indicator is selected or being pressed.
431ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang       if (selectedIndex >= 0) {
432ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            int degree = (int) Math.toDegrees(mChildRadians[selectedIndex]);
43319d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan            mBackgroundPaint.setStrokeWidth(HIGHLIGHT_WIDTH);
43419d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan            mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
43519d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan            mBackgroundPaint.setColor(HIGHLIGHT_COLOR);
436ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            canvas.drawArc(mBackgroundRect, -degree - HIGHLIGHT_DEGREES / 2,
437ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                    HIGHLIGHT_DEGREES, false, mBackgroundPaint);
43819d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan        }
43919d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan
440a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        // Draw arc shaped indicator in time lapse recording.
441a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        if (mTimeLapseInterval != 0) {
442a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            // Setup rectangle and paint.
443a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundRect.set((float)(mCenterX - mShutterButtonRadius),
444a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li                    (float)(mCenterY - mShutterButtonRadius),
445a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li                    (float)(mCenterX + mShutterButtonRadius),
446a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li                    (float)(mCenterY + mShutterButtonRadius));
447a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundRect.inset(3f, 3f);
448a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundPaint.setStrokeWidth(TIME_LAPSE_ARC_WIDTH);
449a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
450a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundPaint.setColor(TIME_LAPSE_ARC_COLOR);
451a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
452a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            // Compute the start angle and sweep angle.
453a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            long timeDelta = SystemClock.uptimeMillis() - mRecordingStartTime;
454a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            long numberOfFrames = timeDelta / mTimeLapseInterval;
455248232e937007384328592e0c269b42cf03c6899Wu-cheng Li            float sweepAngle;
456a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            if (numberOfFrames > mNumberOfFrames) {
457248232e937007384328592e0c269b42cf03c6899Wu-cheng Li                // The arc just acrosses 0 degree. Draw a full circle so it
458248232e937007384328592e0c269b42cf03c6899Wu-cheng Li                // looks better.
459248232e937007384328592e0c269b42cf03c6899Wu-cheng Li                sweepAngle = 360;
460a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li                mNumberOfFrames = numberOfFrames;
461a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            } else {
462248232e937007384328592e0c269b42cf03c6899Wu-cheng Li                sweepAngle = timeDelta % mTimeLapseInterval * 360f / mTimeLapseInterval;
463a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            }
464a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
465248232e937007384328592e0c269b42cf03c6899Wu-cheng Li            canvas.drawArc(mBackgroundRect, 0, sweepAngle, false, mBackgroundPaint);
466248232e937007384328592e0c269b42cf03c6899Wu-cheng Li            invalidate();
467a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        }
468a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
46980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        super.onDraw(canvas);
47080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    }
471ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang
472ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    @Override
473ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    public void setEnabled(boolean enabled) {
474ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        super.setEnabled(enabled);
475b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang        if (mCurrentMode == MODE_VIDEO) {
476b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang            mSecondLevelIcon.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
477b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang            mCloseIcon.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
478b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang            requestLayout();
479ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        } else {
480b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang            // We also disable the zoom button during snapshot.
4810bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li            enableZoom(enabled);
482ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
483ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mSecondLevelIcon.setEnabled(enabled);
484ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mCloseIcon.setEnabled(enabled);
485ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    }
486ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
4870bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li    public void enableZoom(boolean enabled) {
4880bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li        if (mZoomIcon != null)  mZoomIcon.setEnabled(enabled);
4890bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li    }
4900bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li
491ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    public void onTouchOutBound() {
492ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        dismissSettingPopup();
493ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        if (mPressedIndex != -1) {
494ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            injectMotionEvent(mPressedIndex, mLastMotionEvent, MotionEvent.ACTION_CANCEL);
495ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            mPressedIndex = -1;
496ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            invalidate();
497ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        }
498ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    }
499a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang
500a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang    public void dismissSecondLevelIndicator() {
501a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang        if (mCurrentLevel == 1) {
502a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang            changeIndicatorsLevel();
503a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang        }
504a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang    }
5056f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li}
506