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;
27da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Changimport android.graphics.Path;
2880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Liimport android.graphics.RectF;
29ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wangimport android.os.Handler;
30a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Liimport android.os.SystemClock;
316f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport android.util.AttributeSet;
32dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Liimport android.view.MotionEvent;
336f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport android.view.View;
34ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wangimport android.widget.ImageView;
356f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
366f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li/**
37ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang * A view that contains camera setting indicators in two levels. The first-level
38ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang * indicators including the zoom, camera picker, flash and second-level control.
39ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang * The second-level indicators are the merely for the camera settings.
406f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li */
41ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wangpublic class IndicatorControlWheel extends IndicatorControl implements
42ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        View.OnClickListener {
43ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    public static final int HIGHLIGHT_WIDTH = 4;
44ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang
45ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    private static final String TAG = "IndicatorControlWheel";
46ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int HIGHLIGHT_DEGREES = 30;
47ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final double HIGHLIGHT_RADIANS = Math.toRadians(HIGHLIGHT_DEGREES);
48ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
49ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    // The following angles are based in the zero degree on the right. Here we
5091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang    // have the CameraPicker, ZoomControl and the Settings icons in the
5191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang    // first-level. For consistency, we treat the zoom control as one of the
5291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang    // indicator buttons but it needs additional efforts for rotation animation.
5391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang    // For second-level indicators, the indicators are located evenly between start
5491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang    // and end angle. In addition, these indicators for the second-level hidden
5591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang    // in the same wheel with larger angle values are visible after rotation.
5691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang    private static final int FIRST_LEVEL_START_DEGREES = 74;
5791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang    private static final int FIRST_LEVEL_END_DEGREES = 286;
58ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int FIRST_LEVEL_SECTOR_DEGREES = 45;
59ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int SECOND_LEVEL_START_DEGREES = 60;
60ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int SECOND_LEVEL_END_DEGREES = 300;
6191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang    private static final int MAX_ZOOM_CONTROL_DEGREES = 264;
62ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int CLOSE_ICON_DEFAULT_DEGREES = 315;
63ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
64ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private static final int ANIMATION_TIME = 300; // milliseconds
65bed7386688cd7810cba23732ab71f370fe3222ffChung-yih Wang
66e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan    // The width of the edges on both sides of the wheel, which has less alpha.
67e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan    private static final float EDGE_STROKE_WIDTH = 6f;
68a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private static final int TIME_LAPSE_ARC_WIDTH = 6;
69e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan
70e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan    private final int HIGHLIGHT_COLOR;
71da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang    private final int HIGHLIGHT_FAN_COLOR;
72a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private final int TIME_LAPSE_ARC_COLOR;
73e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan
7480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    // The center of the shutter button.
75f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li    private int mCenterX, mCenterY;
7680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    // The width of the wheel stroke.
77ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    private int mStrokeWidth;
78f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li    private double mShutterButtonRadius;
7980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    private double mWheelRadius;
80ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    private double mChildRadians[];
8180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    private Paint mBackgroundPaint;
8280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    private RectF mBackgroundRect;
83e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li    // The index of the child that is being pressed. -1 means no child is being
84e5fe56fec047dec5ccb12c2001bacfd9455352daWu-cheng Li    // pressed.
85039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li    private int mPressedIndex = -1;
86dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li
87a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    // Time lapse recording variables.
88a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private int mTimeLapseInterval;  // in ms
89a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private long mRecordingStartTime = 0;
90a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    private long mNumberOfFrames = 0;
91a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
92ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    // Remember the last event for event cancelling if out of bound.
93ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    private MotionEvent mLastMotionEvent;
94ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang
95ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private ImageView mSecondLevelIcon;
96ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private ImageView mCloseIcon;
97ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
98ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    // Variables for animation.
99ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private long mAnimationStartTime;
100ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private boolean mInAnimation = false;
101ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private Handler mHandler = new Handler();
102ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private final Runnable mRunnable = new Runnable() {
103ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        public void run() {
104ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            requestLayout();
105ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
106ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    };
107ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
108ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    // Variables for level control.
109ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private int mCurrentLevel = 0;
110ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private int mSecondLevelStartIndex = -1;
111ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private double mStartVisibleRadians[] = new double[2];
112ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private double mEndVisibleRadians[] = new double[2];
113ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private double mSectorRadians[] = new double[2];
114ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private double mTouchSectorRadians[] = new double[2];
115ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
11691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang    private ZoomControlWheel mZoomControl;
11791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang    private boolean mInitialized;
118ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
119ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    public IndicatorControlWheel(Context context, AttributeSet attrs) {
12080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        super(context, attrs);
121e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan        Resources resources = context.getResources();
122e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan        HIGHLIGHT_COLOR = resources.getColor(R.color.review_control_pressed_color);
123da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang        HIGHLIGHT_FAN_COLOR = resources.getColor(R.color.review_control_pressed_fan_color);
124a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        TIME_LAPSE_ARC_COLOR = resources.getColor(R.color.time_lapse_arc);
125bed7386688cd7810cba23732ab71f370fe3222ffChung-yih Wang
12680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        setWillNotDraw(false);
12780715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li
12880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mBackgroundPaint = new Paint();
12980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mBackgroundPaint.setStyle(Paint.Style.STROKE);
13080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mBackgroundPaint.setAntiAlias(true);
13180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li
13280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        mBackgroundRect = new RectF();
133ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    }
134ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
135ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private int getChildCountByLevel(int level) {
136ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // Get current child count by level.
137ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if (level == 1) {
138ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            return (getChildCount() - mSecondLevelStartIndex);
139ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        } else {
140ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            return mSecondLevelStartIndex;
141ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
142ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    }
143ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
144a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang    private void changeIndicatorsLevel() {
145ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mPressedIndex = -1;
146ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        dismissSettingPopup();
147ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mInAnimation = true;
148ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mAnimationStartTime = SystemClock.uptimeMillis();
149ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        requestLayout();
15080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    }
15180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li
152a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang    @Override
153a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang    public void onClick(View view) {
154a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang        changeIndicatorsLevel();
155a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang    }
156a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang
15795788ace8ad3630312f2f94544690c448c935f9eChung-yih Wang    public void initialize(Context context, PreferenceGroup group,
158b7d0a0704e437a1892fbf2a6e5138cf36df9fde8Chung-yih Wang            boolean isZoomSupported, String[] keys, String[] otherSettingKeys) {
159ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        mShutterButtonRadius = IndicatorControlWheelContainer.SHUTTER_BUTTON_RADIUS;
160ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        mStrokeWidth = Util.dpToPixel(IndicatorControlWheelContainer.STROKE_WIDTH);
161ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        mWheelRadius = mShutterButtonRadius + mStrokeWidth * 0.5;
162ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang
163ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        setPreferenceGroup(group);
164ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang
16591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        // Add the ZoomControl if supported.
166ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        if (isZoomSupported) {
16791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            mZoomControl = (ZoomControlWheel) findViewById(R.id.zoom_control);
16891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            mZoomControl.setVisibility(View.VISIBLE);
169ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        }
1700935e09bb12a1886a801713bbd78bbc20bf4ea4eChih-yu Huang
171ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang        // Add CameraPicker.
172ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        initializeCameraPicker();
1730935e09bb12a1886a801713bbd78bbc20bf4ea4eChih-yu Huang
174ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // Add second-level Indicator Icon.
175ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mSecondLevelIcon = addImageButton(context, R.drawable.ic_settings_holo_light, true);
176ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mSecondLevelStartIndex = getChildCount();
177ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
178ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // Add second-level buttons.
179ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mCloseIcon = addImageButton(context, R.drawable.btn_wheel_close_settings, false);
180ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        addControls(keys, otherSettingKeys);
181ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
182ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // The angle(in radians) of each icon for touch events.
183ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        mChildRadians = new double[getChildCount()];
184ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        presetFirstLevelChildRadians();
185ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        presetSecondLevelChildRadians();
18691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        mInitialized = true;
187ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    }
188ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
189ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private ImageView addImageButton(Context context, int resourceId, boolean rotatable) {
190b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang        ImageView view;
191b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang        if (rotatable) {
192b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang            view = new RotateImageView(context);
193b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang        } else {
194c12e12365fc3b0b3b4718eced8f3dc6a776c0851Angus Kong            view = new TwoStateImageView(context);
195b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang        }
196ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        view.setImageResource(resourceId);
197ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        view.setOnClickListener(this);
198ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        addView(view);
199ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        return view;
200ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    }
201ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang
202ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    private int getTouchIndicatorIndex(double delta) {
203ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // The delta is the angle of touch point in radians.
204ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if (mInAnimation) return -1;
205ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int count = getChildCountByLevel(mCurrentLevel);
206ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        if (count == 0) return -1;
207ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        int sectors = count - 1;
20867bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang        int startIndex = (mCurrentLevel == 0) ? 0 : mSecondLevelStartIndex;
20967bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang        int endIndex;
21067bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang        if (mCurrentLevel == 0) {
21167bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang            // Skip the first component if it is zoom control, as we will
21267bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang            // deal with it specifically.
21367bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang            if (mZoomControl != null) startIndex++;
21467bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang            endIndex = mSecondLevelStartIndex - 1;
21567bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang        } else {
21667bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang            endIndex = getChildCount() - 1;
21767bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang        }
218ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        // Check which indicator is touched.
21967bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang        double halfTouchSectorRadians = mTouchSectorRadians[mCurrentLevel];
22067bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang        if ((delta >= (mChildRadians[startIndex] - halfTouchSectorRadians)) &&
22167bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang                (delta <= (mChildRadians[endIndex] + halfTouchSectorRadians))) {
22291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            int index = 0;
22367bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang            if (mCurrentLevel == 1) {
22467bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang                index = (int) ((delta - mChildRadians[startIndex])
22591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                        / mSectorRadians[mCurrentLevel]);
22691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                // greater than the center of ending indicator
22791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                if (index > sectors) return (startIndex + sectors);
22891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                // less than the center of starting indicator
22991d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                if (index < 0) return startIndex;
23091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            }
231ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            if (delta <= (mChildRadians[startIndex + index]
23267bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang                    + halfTouchSectorRadians)) {
233ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                return (startIndex + index);
234ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            }
235ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            if (delta >= (mChildRadians[startIndex + index + 1]
23667bed66e355d37dd43a01f1d6f532d45dab18765Chung-yih Wang                    - halfTouchSectorRadians)) {
237ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                return (startIndex + index + 1);
238ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            }
23991d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang
24091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            // It must be for zoom control if the touch event is in the visible
24191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            // range and not for other indicator buttons.
24291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            if ((mCurrentLevel == 0) && (mZoomControl != null)) return 0;
243ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        }
244ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        return -1;
245ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    }
246ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang
247ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    private void injectMotionEvent(int viewIndex, MotionEvent event, int action) {
248ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        View v = getChildAt(viewIndex);
249ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        event.setAction(action);
250ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        v.dispatchTouchEvent(event);
25195788ace8ad3630312f2f94544690c448c935f9eChung-yih Wang    }
25295788ace8ad3630312f2f94544690c448c935f9eChung-yih Wang
25395788ace8ad3630312f2f94544690c448c935f9eChung-yih Wang    @Override
254039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li    public boolean dispatchTouchEvent(MotionEvent event) {
255039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        if (!onFilterTouchEventForSecurity(event)) return false;
256ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        mLastMotionEvent = event;
257f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li        int action = event.getAction();
258039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li
25916ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        double dx = event.getX() - mCenterX;
26016ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        double dy = mCenterY - event.getY();
26116ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li        double radius = Math.sqrt(dx * dx + dy * dy);
26216ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li
263ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        // Ignore the event if too far from the shutter button.
264ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if ((radius <= (mWheelRadius + mStrokeWidth)) && (radius > mShutterButtonRadius)) {
265f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li            double delta = Math.atan2(dy, dx);
266f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li            if (delta < 0) delta += Math.PI * 2;
267ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            int index = getTouchIndicatorIndex(delta);
26891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            // Check if the touch event is for zoom control.
26991d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            if ((mZoomControl != null) && (index == 0)) {
27091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                mZoomControl.dispatchTouchEvent(event);
27191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            }
272ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            // Move over from one indicator to another.
273ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            if ((index != mPressedIndex) || (action == MotionEvent.ACTION_DOWN)) {
274ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                if (mPressedIndex != -1) {
275ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                    injectMotionEvent(mPressedIndex, event, MotionEvent.ACTION_CANCEL);
276ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                } else {
277ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                    // Cancel the popup if it is different from the selected.
278ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                    if (getSelectedIndicatorIndex() != index) dismissSettingPopup();
279ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                }
280ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                if ((index != -1) && (action == MotionEvent.ACTION_MOVE)) {
28191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                    if (mCurrentLevel != 0) {
28291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                        injectMotionEvent(index, event, MotionEvent.ACTION_DOWN);
28391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                    }
284f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li                }
285dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li            }
286ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            if ((index != -1) && (action != MotionEvent.ACTION_MOVE)) {
28791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                getChildAt(index).dispatchTouchEvent(event);
28891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            }
28991d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            // Do not highlight the CameraPicker or Settings icon if we
29091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            // touch from the zoom control to one of them.
29191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            if ((mCurrentLevel == 0) && (index != 0)
29291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                    && (action == MotionEvent.ACTION_MOVE)) {
29391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                return true;
294ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            }
295ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            // Once the button is up, reset the press index.
296ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            mPressedIndex = (action == MotionEvent.ACTION_UP) ? -1 : index;
297ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            invalidate();
298ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            return true;
299dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li        }
300039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        // The event is not on any of the child.
301ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        onTouchOutBound();
302dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li        return false;
303dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li    }
3046f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
305ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private void rotateWheel() {
306ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int totalDegrees = CLOSE_ICON_DEFAULT_DEGREES - SECOND_LEVEL_START_DEGREES;
307ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int startAngle = ((mCurrentLevel == 0) ? CLOSE_ICON_DEFAULT_DEGREES
308ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                                               : SECOND_LEVEL_START_DEGREES);
309ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if (mCurrentLevel == 0) totalDegrees = -totalDegrees;
310ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
311ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int elapsedTime = (int) (SystemClock.uptimeMillis() - mAnimationStartTime);
312ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if (elapsedTime >= ANIMATION_TIME) {
313ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            elapsedTime = ANIMATION_TIME;
314ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            mCurrentLevel = (mCurrentLevel == 0) ? 1 : 0;
315ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            mInAnimation = false;
316ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
317ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
318ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int expectedAngle = startAngle + (totalDegrees * elapsedTime / ANIMATION_TIME);
319ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        double increment = Math.toRadians(expectedAngle)
320ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                - mChildRadians[mSecondLevelStartIndex];
321ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        for (int i = 0 ; i < getChildCount(); ++i) mChildRadians[i] += increment;
32291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        // We also need to rotate the zoom control wheel as well.
32391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        if (mZoomControl != null) {
32491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            mZoomControl.rotate(mChildRadians[0]
32591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                    - Math.toRadians(MAX_ZOOM_CONTROL_DEGREES));
32691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        }
32791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang    }
328ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
32980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    @Override
3306f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li    protected void onLayout(
3316f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            boolean changed, int left, int top, int right, int bottom) {
33291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        if (!mInitialized) return;
333ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        if (mInAnimation) {
334ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            rotateWheel();
335ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            mHandler.post(mRunnable);
336ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
337ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        mCenterX = right - left - Util.dpToPixel(
338ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                IndicatorControlWheelContainer.FULL_WHEEL_RADIUS);
33962776cd28a40b4c6ada4b4aa38683f45572ae266Wu-cheng Li        mCenterY = (bottom - top) / 2;
3406f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
341ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // Layout the indicators based on the current level.
342ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // The icons are spreaded on the left side of the shutter button.
343ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        for (int i = 0; i < getChildCount(); ++i) {
3446f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li            View view = getChildAt(i);
345f060a6491f31e2e4b545ac6d71995074b969c7a2Chung-yih Wang            // We still need to show the disabled indicators in the second level.
346ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            double radian = mChildRadians[i];
347ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            double startVisibleRadians = mInAnimation
348ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                    ? mStartVisibleRadians[1]
349ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                    : mStartVisibleRadians[mCurrentLevel];
350ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            double endVisibleRadians = mInAnimation
351ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                    ? mEndVisibleRadians[1]
352ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                    : mEndVisibleRadians[mCurrentLevel];
353dffce0bd29d78b0168ed71f4d5aa2bd7c8a6efa3Chung-yih Wang            if ((!view.isEnabled() && (mCurrentLevel == 0))
354dffce0bd29d78b0168ed71f4d5aa2bd7c8a6efa3Chung-yih Wang                    || (radian < (startVisibleRadians - HIGHLIGHT_RADIANS / 2))
355dffce0bd29d78b0168ed71f4d5aa2bd7c8a6efa3Chung-yih Wang                    || (radian > (endVisibleRadians + HIGHLIGHT_RADIANS / 2))) {
356ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                view.setVisibility(View.GONE);
357ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                continue;
358ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            }
359ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            view.setVisibility(View.VISIBLE);
36080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            int x = mCenterX + (int)(mWheelRadius * Math.cos(radian));
36180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            int y = mCenterY - (int)(mWheelRadius * Math.sin(radian));
36280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            int width = view.getMeasuredWidth();
36380715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li            int height = view.getMeasuredHeight();
36491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            if (view == mZoomControl) {
36591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                // ZoomControlWheel matches the size of its parent view.
36691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                view.layout(0, 0, right - left, bottom - top);
36791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            } else {
36891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                view.layout(x - width / 2, y - height / 2, x + width / 2,
36991d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang                        y + height / 2);
37091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            }
371f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li        }
3726f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li    }
3736f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li
374ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private void presetFirstLevelChildRadians() {
37591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        // Set the visible range in the first-level indicator wheel.
37691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        mStartVisibleRadians[0] = Math.toRadians(FIRST_LEVEL_START_DEGREES);
37791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        mTouchSectorRadians[0] = HIGHLIGHT_RADIANS;
37891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        mEndVisibleRadians[0] = Math.toRadians(FIRST_LEVEL_END_DEGREES);
379ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
38091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        // Set the angle of each component in the first-level indicator wheel.
381ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int startIndex = 0;
38291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        if (mZoomControl != null) {
38391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            mChildRadians[startIndex++] = Math.toRadians(MAX_ZOOM_CONTROL_DEGREES);
384ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
38591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        if (mCameraPicker != null) {
38691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang            mChildRadians[startIndex++] = Math.toRadians(FIRST_LEVEL_START_DEGREES);
38791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        }
38891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        mChildRadians[startIndex++] = Math.toRadians(FIRST_LEVEL_END_DEGREES);
389ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    }
390ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
391ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    private void presetSecondLevelChildRadians() {
392ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int count = getChildCountByLevel(1);
393f09f58c11f3e6b5670cfb1317bb2d7cf1e412d7cChung-yih Wang        int sectors = (count <= 1) ? 1 : (count - 1);
394ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        double sectorDegrees =
395ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                ((SECOND_LEVEL_END_DEGREES - SECOND_LEVEL_START_DEGREES) / sectors);
396ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mSectorRadians[1] = Math.toRadians(sectorDegrees);
397ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
398ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        double degrees = CLOSE_ICON_DEFAULT_DEGREES;
399ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mStartVisibleRadians[1] = Math.toRadians(SECOND_LEVEL_START_DEGREES);
400ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
401ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        int startIndex = mSecondLevelStartIndex;
402ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        for (int i = 0; i < count; i++) {
403ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            mChildRadians[startIndex + i] = Math.toRadians(degrees);
404ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            degrees += sectorDegrees;
405ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
406ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
407ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        // The radians for the touch sector of an indicator.
408ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mTouchSectorRadians[1] =
409ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang                Math.min(HIGHLIGHT_RADIANS, Math.toRadians(sectorDegrees));
410ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
411ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mEndVisibleRadians[1] = Math.toRadians(SECOND_LEVEL_END_DEGREES);
412ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    }
413ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
414a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    public void startTimeLapseAnimation(int timeLapseInterval, long startTime) {
415a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        mTimeLapseInterval = timeLapseInterval;
416a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        mRecordingStartTime = startTime;
417a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        mNumberOfFrames = 0;
418a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        invalidate();
419a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    }
420a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
421a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    public void stopTimeLapseAnimation() {
422a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        mTimeLapseInterval = 0;
423a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        invalidate();
424a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li    }
425a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
426039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li    private int getSelectedIndicatorIndex() {
427039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        for (int i = 0; i < mIndicators.size(); i++) {
428039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            AbstractIndicatorButton b = mIndicators.get(i);
429039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            if (b.getPopupWindow() != null) {
430039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li                return indexOfChild(b);
431039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li            }
432039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        }
433ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        if (mPressedIndex != -1) {
434ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            View v = getChildAt(mPressedIndex);
435ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            if (!(v instanceof AbstractIndicatorButton) && v.isEnabled()) {
436ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                return mPressedIndex;
437ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            }
438ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        }
439039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        return -1;
440039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li    }
441039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li
44280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    @Override
44380715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    protected void onDraw(Canvas canvas) {
444039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        int selectedIndex = getSelectedIndicatorIndex();
445039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li
446039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li        // Draw the highlight arc if an indicator is selected or being pressed.
44791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        // And skip the zoom control which index is zero.
44891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        if (selectedIndex >= 1) {
449ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang            int degree = (int) Math.toDegrees(mChildRadians[selectedIndex]);
450da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            float innerR = (float) mShutterButtonRadius;
451da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            float outerR = (float) (mShutterButtonRadius + mStrokeWidth +
452da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang                    EDGE_STROKE_WIDTH * 0.5);
453da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang
454da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            // Construct the path of the fan-shaped semi-transparent area.
455da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            Path fanPath = new Path();
456da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            mBackgroundRect.set(mCenterX - innerR, mCenterY - innerR,
457da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang                    mCenterX + innerR, mCenterY + innerR);
458da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            fanPath.arcTo(mBackgroundRect, -degree + HIGHLIGHT_DEGREES / 2,
459da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang                    -HIGHLIGHT_DEGREES);
460da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            mBackgroundRect.set(mCenterX - outerR, mCenterY - outerR,
461da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang                    mCenterX + outerR, mCenterY + outerR);
462da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            fanPath.arcTo(mBackgroundRect, -degree - HIGHLIGHT_DEGREES / 2,
463da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang                    HIGHLIGHT_DEGREES);
464da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            fanPath.close();
465da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang
46619d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan            mBackgroundPaint.setStrokeWidth(HIGHLIGHT_WIDTH);
467da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            mBackgroundPaint.setStrokeCap(Paint.Cap.SQUARE);
468da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            mBackgroundPaint.setStyle(Paint.Style.FILL_AND_STROKE);
469da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            mBackgroundPaint.setColor(HIGHLIGHT_FAN_COLOR);
470da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            canvas.drawPath(fanPath, mBackgroundPaint);
471da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang
472da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            // Draw the highlight edge
473da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang            mBackgroundPaint.setStyle(Paint.Style.STROKE);
47419d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan            mBackgroundPaint.setColor(HIGHLIGHT_COLOR);
475ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            canvas.drawArc(mBackgroundRect, -degree - HIGHLIGHT_DEGREES / 2,
476ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang                    HIGHLIGHT_DEGREES, false, mBackgroundPaint);
47719d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan        }
47819d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan
479a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        // Draw arc shaped indicator in time lapse recording.
480a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        if (mTimeLapseInterval != 0) {
481a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            // Setup rectangle and paint.
482a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundRect.set((float)(mCenterX - mShutterButtonRadius),
483a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li                    (float)(mCenterY - mShutterButtonRadius),
484a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li                    (float)(mCenterX + mShutterButtonRadius),
485a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li                    (float)(mCenterY + mShutterButtonRadius));
486a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundRect.inset(3f, 3f);
487a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundPaint.setStrokeWidth(TIME_LAPSE_ARC_WIDTH);
488a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
489a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            mBackgroundPaint.setColor(TIME_LAPSE_ARC_COLOR);
490a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
491a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            // Compute the start angle and sweep angle.
492a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            long timeDelta = SystemClock.uptimeMillis() - mRecordingStartTime;
493a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            long numberOfFrames = timeDelta / mTimeLapseInterval;
494248232e937007384328592e0c269b42cf03c6899Wu-cheng Li            float sweepAngle;
495a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            if (numberOfFrames > mNumberOfFrames) {
496248232e937007384328592e0c269b42cf03c6899Wu-cheng Li                // The arc just acrosses 0 degree. Draw a full circle so it
497248232e937007384328592e0c269b42cf03c6899Wu-cheng Li                // looks better.
498248232e937007384328592e0c269b42cf03c6899Wu-cheng Li                sweepAngle = 360;
499a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li                mNumberOfFrames = numberOfFrames;
500a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            } else {
501248232e937007384328592e0c269b42cf03c6899Wu-cheng Li                sweepAngle = timeDelta % mTimeLapseInterval * 360f / mTimeLapseInterval;
502a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li            }
503a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
504248232e937007384328592e0c269b42cf03c6899Wu-cheng Li            canvas.drawArc(mBackgroundRect, 0, sweepAngle, false, mBackgroundPaint);
505248232e937007384328592e0c269b42cf03c6899Wu-cheng Li            invalidate();
506a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li        }
507a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li
50880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li        super.onDraw(canvas);
50980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li    }
510ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang
511ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    @Override
512ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    public void setEnabled(boolean enabled) {
513ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        super.setEnabled(enabled);
51491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        if (!mInitialized) return;
515b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang        if (mCurrentMode == MODE_VIDEO) {
516b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang            mSecondLevelIcon.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
517b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang            mCloseIcon.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
518b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang            requestLayout();
519ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        } else {
520b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang            // We also disable the zoom button during snapshot.
5210bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li            enableZoom(enabled);
522ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        }
523ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mSecondLevelIcon.setEnabled(enabled);
524ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        mCloseIcon.setEnabled(enabled);
525ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang    }
526ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang
5270bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li    public void enableZoom(boolean enabled) {
52891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang        if (mZoomControl != null) mZoomControl.setEnabled(enabled);
5290bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li    }
5300bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li
531ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    public void onTouchOutBound() {
532ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang        dismissSettingPopup();
533ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        if (mPressedIndex != -1) {
534ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            injectMotionEvent(mPressedIndex, mLastMotionEvent, MotionEvent.ACTION_CANCEL);
535ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            mPressedIndex = -1;
536ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang            invalidate();
537ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang        }
538ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang    }
539a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang
540a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang    public void dismissSecondLevelIndicator() {
541a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang        if (mCurrentLevel == 1) {
542a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang            changeIndicatorsLevel();
543a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang        }
544a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang    }
5456f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li}
546