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