IndicatorControlWheel.java revision bed7386688cd7810cba23732ab71f370fe3222ff
16f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li/* 26f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * Copyright (C) 2010 The Android Open Source Project 36f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * 46f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * Licensed under the Apache License, Version 2.0 (the "License"); 56f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * you may not use this file except in compliance with the License. 66f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * You may obtain a copy of the License at 76f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * 86f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * http://www.apache.org/licenses/LICENSE-2.0 96f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * 106f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * Unless required by applicable law or agreed to in writing, software 116f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * distributed under the License is distributed on an "AS IS" BASIS, 126f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * See the License for the specific language governing permissions and 146f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li * limitations under the License. 156f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li */ 166f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 176f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Lipackage com.android.camera.ui; 186f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 196f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport com.android.camera.R; 206f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 216f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport android.content.Context; 22e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyanimport android.content.res.Resources; 2380715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Liimport android.graphics.Canvas; 2480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Liimport android.graphics.Paint; 2580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Liimport android.graphics.RectF; 26a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Liimport android.os.SystemClock; 276f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport android.util.AttributeSet; 28dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Liimport android.view.MotionEvent; 296f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Liimport android.view.View; 306f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 317341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Liimport java.util.ArrayList; 326f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 336f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li/** 3480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li * A view that contains shutter button and camera setting indicators. The 3580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li * indicators are spreaded around the shutter button. The first child is always 36c5e37cbb96b8284203560c51dd5f41c705a21f58Wu-cheng Li * the shutter button. 376f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li */ 38bed7386688cd7810cba23732ab71f370fe3222ffChung-yih Wangpublic class IndicatorWheel extends IndicatorControl { 39f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li private static final String TAG = "IndicatorWheel"; 40bed7386688cd7810cba23732ab71f370fe3222ffChung-yih Wang 41e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan // The width of the edges on both sides of the wheel, which has less alpha. 42e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan private static final float EDGE_STROKE_WIDTH = 6f; 43e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan private static final int HIGHLIGHT_WIDTH = 4; 44e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan private static final int HIGHLIGHT_DEGREE = 30; 45a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li private static final int TIME_LAPSE_ARC_WIDTH = 6; 46e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan 47e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan private final int HIGHLIGHT_COLOR; 48a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li private final int TIME_LAPSE_ARC_COLOR; 49e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan 5080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li // The center of the shutter button. 51f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li private int mCenterX, mCenterY; 5280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li // The width of the wheel stroke. 5341ac285a1c97b1df78246456effb80de51033323Hung-ying Tyan private int mStrokeWidth = 60; 5480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li private View mShutterButton; 55f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li private double mShutterButtonRadius; 5680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li private double mWheelRadius; 57f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li private double mSectorInitialRadians[]; 5880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li private Paint mBackgroundPaint; 5980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li private RectF mBackgroundRect; 60039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // The index of the indicator that is being pressed. This starts from 0. 61039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // -1 means no indicator is being pressed. 62039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li private int mPressedIndex = -1; 63dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li 64a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li // Time lapse recording variables. 65a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li private int mTimeLapseInterval; // in ms 66a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li private long mRecordingStartTime = 0; 67a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li private long mNumberOfFrames = 0; 68a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li 6980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li public IndicatorWheel(Context context, AttributeSet attrs) { 7080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li super(context, attrs); 71e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan Resources resources = context.getResources(); 72e50d2553aeb9e55504697e2a72759e8f46ee2992Hung-ying Tyan HIGHLIGHT_COLOR = resources.getColor(R.color.review_control_pressed_color); 73a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li TIME_LAPSE_ARC_COLOR = resources.getColor(R.color.time_lapse_arc); 74bed7386688cd7810cba23732ab71f370fe3222ffChung-yih Wang 7580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li setWillNotDraw(false); 7680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li 7780715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li mBackgroundPaint = new Paint(); 7880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li mBackgroundPaint.setStyle(Paint.Style.STROKE); 7980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li mBackgroundPaint.setAntiAlias(true); 8080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li 8180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li mBackgroundRect = new RectF(); 8280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li } 8380715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li 84bed7386688cd7810cba23732ab71f370fe3222ffChung-yih Wang private boolean isInsideShutterButton(MotionEvent ev) { 85039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li float x = ev.getX(); 86039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li float y = ev.getY(); 87039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li float shutterButtonX = mShutterButton.getX(); 88039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li float shutterButtonY = mShutterButton.getY(); 89039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (x >= shutterButtonX && y >= shutterButtonY 90039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li && (x < shutterButtonX + mShutterButton.getWidth()) 91039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li && (y < shutterButtonY + mShutterButton.getHeight())) { 92039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li return true; 934504e5b41a0f647ef3e16b1134e130fe3678aebfWu-cheng Li } 944504e5b41a0f647ef3e16b1134e130fe3678aebfWu-cheng Li return false; 954504e5b41a0f647ef3e16b1134e130fe3678aebfWu-cheng Li } 964504e5b41a0f647ef3e16b1134e130fe3678aebfWu-cheng Li 97dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li @Override 98039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li public boolean dispatchTouchEvent(MotionEvent event) { 99039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (!onFilterTouchEventForSecurity(event)) return false; 1001c62e1b83c2fdda82c726bf712324444136fdc5dHung-ying Tyan 101f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li int action = event.getAction(); 102039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li 103039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // Check if the event should be dispatched to the shutter button. 104039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (isInsideShutterButton(event)) { 105039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP) { 106039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li return mShutterButton.dispatchTouchEvent(event); 107039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } 1087341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Li return false; 1097341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Li } 110039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (!isEnabled()) return false; 111f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li 1127341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Li double dx = event.getX() - mCenterX; 1137341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Li double dy = mCenterY - event.getY(); 1147341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Li double radius = Math.sqrt(dx * dx + dy * dy); 1157341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Li // Ignore the event if it's too near to the shutter button or too far 1167341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Li // from the shutter button. 1177341b9b5d8194c4ab7140977a8c5218e6d83d516Wu-cheng Li if (radius >= mShutterButtonRadius && radius <= mWheelRadius + mStrokeWidth) { 118f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li double delta = Math.atan2(dy, dx); 119f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li if (delta < 0) delta += Math.PI * 2; 120f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li // Check which sector is pressed. 121f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li if (delta > mSectorInitialRadians[0]) { 122039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li for (int i = 0; i < getChildCount() - 1; i++) { 123f5e80a8a5670798d52afe08e0c15aa4448d33316Wu-cheng Li if (delta < mSectorInitialRadians[i + 1]) { 124039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li View child = getChildAt(i + 1); 125039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (action == MotionEvent.ACTION_DOWN) { 126039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (child instanceof AbstractIndicatorButton) { 127039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li AbstractIndicatorButton b = (AbstractIndicatorButton) child; 128039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // If the same setting is pressed when the popup is open, 129039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // do not dismiss it because it will be handled in the child. 130039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (b.getPopupWindow() == null) { 131039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li dismissSettingPopup(); 132fba0439b62f19a2a19ff6559c1ae055e910400dbWu-cheng Li } 133039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } else { 134039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // Zoom button or back/front camera switch is pressed. 135039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li dismissSettingPopup(); 136823d57389b616091827059ca6401f675e416a53bWu-cheng Li } 137039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li child.dispatchTouchEvent(event); 138039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li invalidate(); 139039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li mPressedIndex = i; 140039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } else if (action == MotionEvent.ACTION_UP) { 141039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li child.dispatchTouchEvent(event); 142039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li invalidate(); 143039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li mPressedIndex = -1; 144039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } else if (action == MotionEvent.ACTION_MOVE) { 145039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // Dispatch the event if the location across a sector. 146039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (mPressedIndex != -1 && i != mPressedIndex) { 147039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li dismissSettingPopup(); 148039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // Cancel the previous one. 149039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li View cancelChild = getChildAt(mPressedIndex + 1); 150039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li event.setAction(MotionEvent.ACTION_CANCEL); 151039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li cancelChild.dispatchTouchEvent(event); 152039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // Send down to the current one. 153039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li event.setAction(MotionEvent.ACTION_DOWN); 154039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li child.dispatchTouchEvent(event); 155039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li event.setAction(action); // Set the action back 156039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li invalidate(); 1574504e5b41a0f647ef3e16b1134e130fe3678aebfWu-cheng Li } 158039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // The children do not care about ACTION_MOVE. Besides, the press 159039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // state will be wrong because of View.pointInView. 160039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li mPressedIndex = i; 1614504e5b41a0f647ef3e16b1134e130fe3678aebfWu-cheng Li } 162f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li return true; 163f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li } 164f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li } 165dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li } 166dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li } 167039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // The event is not on any of the child. 1684504e5b41a0f647ef3e16b1134e130fe3678aebfWu-cheng Li dismissSettingPopup(); 169039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (mPressedIndex != -1) { 170039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li View cancelChild = getChildAt(mPressedIndex + 1); 171039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li event.setAction(MotionEvent.ACTION_CANCEL); 172039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li cancelChild.dispatchTouchEvent(event); 173039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li mPressedIndex = -1; 174039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } 175039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li invalidate(); 176dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li return false; 177dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li } 1786f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 17980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li @Override 18080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li protected void onFinishInflate() { 18180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li super.onFinishInflate(); 18280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li // The first view is shutter button. 18380715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li mShutterButton = getChildAt(0); 184248232e937007384328592e0c269b42cf03c6899Wu-cheng Li invalidate(); 1856f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li } 1866f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 1876f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li @Override 1886f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li protected void onMeasure(int widthSpec, int heightSpec) { 1896f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li // Measure all children. 19080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li int childCount = getChildCount(); 1916f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 19280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li for (int i = 0; i < childCount; i++) { 1936f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li getChildAt(i).measure(freeSpec, freeSpec); 1946f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li } 1956f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 1966f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li // Measure myself. 197039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li int desiredWidth = mShutterButton.getMeasuredWidth() * 3; 19841ac285a1c97b1df78246456effb80de51033323Hung-ying Tyan int desiredHeight = (int)(mShutterButton.getMeasuredHeight() * 4.5) + 2; 1996f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li int widthMode = MeasureSpec.getMode(widthSpec); 2006f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li int heightMode = MeasureSpec.getMode(heightSpec); 2016f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li int measuredWidth, measuredHeight; 2026f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li if (widthMode == MeasureSpec.UNSPECIFIED) { 2036f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li measuredWidth = desiredWidth; 2046f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li } else if (widthMode == MeasureSpec.AT_MOST) { 2056f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li measuredWidth = Math.min(desiredWidth, MeasureSpec.getSize(widthSpec)); 2066f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li } else { // MeasureSpec.EXACTLY 2076f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li measuredWidth = MeasureSpec.getSize(widthSpec); 2086f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li } 2096f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li if (heightMode == MeasureSpec.UNSPECIFIED) { 2106f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li measuredHeight = desiredHeight; 2116f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li } else if (heightMode == MeasureSpec.AT_MOST) { 2126f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li measuredHeight = Math.min(desiredHeight, MeasureSpec.getSize(heightSpec)); 2136f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li } else { // MeasureSpec.EXACTLY 2146f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li measuredHeight = MeasureSpec.getSize(heightSpec); 2156f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li } 2166f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li setMeasuredDimension(measuredWidth, measuredHeight); 2176f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li } 2186f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 2196f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li @Override 2206f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li protected void onLayout( 2216f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li boolean changed, int left, int top, int right, int bottom) { 2226f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li int count = getChildCount(); 2236f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li if (count == 0) return; 2246f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 2256f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li // Layout the shutter button. 22680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li int shutterButtonWidth = mShutterButton.getMeasuredWidth(); 22741ac285a1c97b1df78246456effb80de51033323Hung-ying Tyan mShutterButtonRadius = shutterButtonWidth / 2.0; 22880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li int shutterButtonHeight = mShutterButton.getMeasuredHeight(); 22974c5164d19fa213c6904639e6e6b0d9e542cbf66Hung-ying Tyan mStrokeWidth = (int) (mShutterButtonRadius * 1.05); 2309bd6c4687e40d86d5de95c3998eae7b079170d42Wu-cheng Li int innerRadius = (int) (mShutterButtonRadius + mStrokeWidth * 0.84); 23147335563ade2c03ee37621b49a16d5c6905e3531Wu-cheng Li // 64 is the requirement by UI design. The distance between the center 23247335563ade2c03ee37621b49a16d5c6905e3531Wu-cheng Li // and the border is 64 pixels. This has to be consistent with the 23347335563ade2c03ee37621b49a16d5c6905e3531Wu-cheng Li // background. 23447335563ade2c03ee37621b49a16d5c6905e3531Wu-cheng Li mCenterX = right - left - 64; 23562776cd28a40b4c6ada4b4aa38683f45572ae266Wu-cheng Li mCenterY = (bottom - top) / 2; 23680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li mShutterButton.layout(mCenterX - shutterButtonWidth / 2, 23780715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li mCenterY - shutterButtonHeight / 2, 23880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li mCenterX + shutterButtonWidth / 2, 23980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li mCenterY + shutterButtonHeight / 2); 2406f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 2416f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li // Layout the settings. The icons are spreaded on the left side of the 2426f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li // shutter button. So the angle starts from 90 to 270 degrees. 2436f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li if (count == 1) return; 24474c5164d19fa213c6904639e6e6b0d9e542cbf66Hung-ying Tyan mWheelRadius = innerRadius + mStrokeWidth * 0.5; 24519d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan double intervalDegrees = (count == 2) ? 90.0 : 180.0 / (count - 2); 2466f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li double initialDegrees = 90.0; 2476f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li int index = 0; 248f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li for (int i = 0; i < count; i++) { 2496f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li View view = getChildAt(i); 25080715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li if (view == mShutterButton) continue; 2516f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li double degree = initialDegrees + intervalDegrees * index; 252f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li double radian = Math.toRadians(degree); 25380715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li int x = mCenterX + (int)(mWheelRadius * Math.cos(radian)); 25480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li int y = mCenterY - (int)(mWheelRadius * Math.sin(radian)); 25580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li int width = view.getMeasuredWidth(); 25680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li int height = view.getMeasuredHeight(); 2576f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li view.layout(x - width / 2, y - height / 2, x + width / 2, 2586f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li y + height / 2); 2596f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li index++; 2606f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li } 2616f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 262f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li // Store the radian intervals for each icon. 263f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li mSectorInitialRadians = new double[count]; 264f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li mSectorInitialRadians[0] = Math.toRadians( 265f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li initialDegrees - intervalDegrees / 2.0); 266f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li for (int i = 1; i < count; i++) { 267f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li mSectorInitialRadians[i] = mSectorInitialRadians[i - 1] 268f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li + Math.toRadians(intervalDegrees); 269f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li } 2706f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li } 2716f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 272a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li public void startTimeLapseAnimation(int timeLapseInterval, long startTime) { 273a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mTimeLapseInterval = timeLapseInterval; 274a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mRecordingStartTime = startTime; 275a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mNumberOfFrames = 0; 276a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li invalidate(); 277a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li } 278a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li 279a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li public void stopTimeLapseAnimation() { 280a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mTimeLapseInterval = 0; 281a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li invalidate(); 282a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li } 283a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li 284039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li private int getSelectedIndicatorIndex() { 285039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li for (int i = 0; i < mIndicators.size(); i++) { 286039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li AbstractIndicatorButton b = mIndicators.get(i); 287039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (b.getPopupWindow() != null) { 288039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li return indexOfChild(b); 289039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } 290039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } 291039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li return -1; 292039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } 293039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li 29480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li @Override 29580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li protected void onDraw(Canvas canvas) { 29619d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan // Draw highlight. 29774c5164d19fa213c6904639e6e6b0d9e542cbf66Hung-ying Tyan float delta = mStrokeWidth * 0.5f; 29874c5164d19fa213c6904639e6e6b0d9e542cbf66Hung-ying Tyan float radius = (float) (mWheelRadius + mStrokeWidth * 0.5 + EDGE_STROKE_WIDTH); 299039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li mBackgroundRect.set(mCenterX - radius, mCenterY - radius, mCenterX + radius, 300039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li mCenterY + radius); 301039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li 302039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li int selectedIndex = getSelectedIndicatorIndex(); 303039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li 304039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // Draw the highlight arc if an indicator is selected or being pressed. 305039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (selectedIndex >= 0 || mPressedIndex >= 0) { 30619d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan int count = getChildCount(); 30719d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan float initialDegrees = 90.0f; 30819d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan float intervalDegrees = (count <= 2) ? 0.0f : 180.0f / (count - 2); 309039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li float degree; 310039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (selectedIndex >= 0) { 311039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li degree = initialDegrees + intervalDegrees * (selectedIndex - 1); 312039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } else { 313039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li degree = initialDegrees + intervalDegrees * mPressedIndex; 314039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } 31519d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan mBackgroundPaint.setStrokeWidth(HIGHLIGHT_WIDTH); 31619d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND); 31719d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan mBackgroundPaint.setColor(HIGHLIGHT_COLOR); 31819d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan canvas.drawArc(mBackgroundRect, -degree - HIGHLIGHT_DEGREE / 2, 31919d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan HIGHLIGHT_DEGREE, false, mBackgroundPaint); 32019d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan } 32119d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan 322a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li // Draw arc shaped indicator in time lapse recording. 323a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li if (mTimeLapseInterval != 0) { 324a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li // Setup rectangle and paint. 325a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mBackgroundRect.set((float)(mCenterX - mShutterButtonRadius), 326a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li (float)(mCenterY - mShutterButtonRadius), 327a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li (float)(mCenterX + mShutterButtonRadius), 328a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li (float)(mCenterY + mShutterButtonRadius)); 329a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mBackgroundRect.inset(3f, 3f); 330a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mBackgroundPaint.setStrokeWidth(TIME_LAPSE_ARC_WIDTH); 331a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND); 332a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mBackgroundPaint.setColor(TIME_LAPSE_ARC_COLOR); 333a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li 334a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li // Compute the start angle and sweep angle. 335a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li long timeDelta = SystemClock.uptimeMillis() - mRecordingStartTime; 336a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li long numberOfFrames = timeDelta / mTimeLapseInterval; 337248232e937007384328592e0c269b42cf03c6899Wu-cheng Li float sweepAngle; 338a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li if (numberOfFrames > mNumberOfFrames) { 339248232e937007384328592e0c269b42cf03c6899Wu-cheng Li // The arc just acrosses 0 degree. Draw a full circle so it 340248232e937007384328592e0c269b42cf03c6899Wu-cheng Li // looks better. 341248232e937007384328592e0c269b42cf03c6899Wu-cheng Li sweepAngle = 360; 342a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mNumberOfFrames = numberOfFrames; 343a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li } else { 344248232e937007384328592e0c269b42cf03c6899Wu-cheng Li sweepAngle = timeDelta % mTimeLapseInterval * 360f / mTimeLapseInterval; 345a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li } 346a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li 347248232e937007384328592e0c269b42cf03c6899Wu-cheng Li canvas.drawArc(mBackgroundRect, 0, sweepAngle, false, mBackgroundPaint); 348248232e937007384328592e0c269b42cf03c6899Wu-cheng Li invalidate(); 349a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li } 350a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li 35180715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li super.onDraw(canvas); 35280715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li } 3536f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li} 354