IndicatorControlWheel.java revision 91d70ce513ffee99b216f88a92650e1b63575289
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; 20791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang int startIndex = 0; 208ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang int sectors = count - 1; 209ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang // Check which indicator is touched. 210ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang if ((delta >= (mStartVisibleRadians[mCurrentLevel] - HIGHLIGHT_RADIANS / 2)) && 211ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang (delta <= (mEndVisibleRadians[mCurrentLevel] + HIGHLIGHT_RADIANS / 2))) { 21291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang int index = 0; 21391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (mCurrentLevel == 0) { 21491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // Skip the first component if it is zoom control, as we will 21591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // deal with it specifically. 21691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (mZoomControl != null) startIndex++; 21791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang } else { 21891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang startIndex = mSecondLevelStartIndex; 21991d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang index = (int) ((delta - mStartVisibleRadians[mCurrentLevel]) 22091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang / mSectorRadians[mCurrentLevel]); 22191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // greater than the center of ending indicator 22291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (index > sectors) return (startIndex + sectors); 22391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // less than the center of starting indicator 22491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (index < 0) return startIndex; 22591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang } 226ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang 227ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang if (delta <= (mChildRadians[startIndex + index] 228ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang + mTouchSectorRadians[mCurrentLevel] / 2)) { 229ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang return (startIndex + index); 230ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang } 231ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang if (delta >= (mChildRadians[startIndex + index + 1] 232ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang - mTouchSectorRadians[mCurrentLevel] / 2)) { 233ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang return (startIndex + index + 1); 234ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang } 23591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang 23691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // It must be for zoom control if the touch event is in the visible 23791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // range and not for other indicator buttons. 23891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if ((mCurrentLevel == 0) && (mZoomControl != null)) return 0; 239ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang } 240ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang return -1; 241ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang } 242ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang 243ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang private void injectMotionEvent(int viewIndex, MotionEvent event, int action) { 244ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang View v = getChildAt(viewIndex); 245ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang event.setAction(action); 246ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang v.dispatchTouchEvent(event); 24795788ace8ad3630312f2f94544690c448c935f9eChung-yih Wang } 24895788ace8ad3630312f2f94544690c448c935f9eChung-yih Wang 24995788ace8ad3630312f2f94544690c448c935f9eChung-yih Wang @Override 250039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li public boolean dispatchTouchEvent(MotionEvent event) { 251039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (!onFilterTouchEventForSecurity(event)) return false; 252ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang mLastMotionEvent = event; 253f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li int action = event.getAction(); 254039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li 25516ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li double dx = event.getX() - mCenterX; 25616ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li double dy = mCenterY - event.getY(); 25716ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li double radius = Math.sqrt(dx * dx + dy * dy); 25816ca94d73bfe07f280e381595709b56c2681b2bcWu-cheng Li 259ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang // Ignore the event if too far from the shutter button. 260ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang if ((radius <= (mWheelRadius + mStrokeWidth)) && (radius > mShutterButtonRadius)) { 261f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li double delta = Math.atan2(dy, dx); 262f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li if (delta < 0) delta += Math.PI * 2; 263ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang int index = getTouchIndicatorIndex(delta); 26491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // Check if the touch event is for zoom control. 26591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if ((mZoomControl != null) && (index == 0)) { 26691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang mZoomControl.dispatchTouchEvent(event); 26791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang } 268ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang // Move over from one indicator to another. 269ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang if ((index != mPressedIndex) || (action == MotionEvent.ACTION_DOWN)) { 270ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang if (mPressedIndex != -1) { 271ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang injectMotionEvent(mPressedIndex, event, MotionEvent.ACTION_CANCEL); 272ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang } else { 273ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang // Cancel the popup if it is different from the selected. 274ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang if (getSelectedIndicatorIndex() != index) dismissSettingPopup(); 275ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang } 276ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang if ((index != -1) && (action == MotionEvent.ACTION_MOVE)) { 27791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (mCurrentLevel != 0) { 27891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang injectMotionEvent(index, event, MotionEvent.ACTION_DOWN); 27991d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang } 280f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li } 281dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li } 282ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang if ((index != -1) && (action != MotionEvent.ACTION_MOVE)) { 28391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang getChildAt(index).dispatchTouchEvent(event); 28491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang } 28591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // Do not highlight the CameraPicker or Settings icon if we 28691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // touch from the zoom control to one of them. 28791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if ((mCurrentLevel == 0) && (index != 0) 28891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang && (action == MotionEvent.ACTION_MOVE)) { 28991d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang return true; 290ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang } 291ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang // Once the button is up, reset the press index. 292ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang mPressedIndex = (action == MotionEvent.ACTION_UP) ? -1 : index; 293ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang invalidate(); 294ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang return true; 295dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li } 296039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // The event is not on any of the child. 297ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang onTouchOutBound(); 298dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li return false; 299dfee7f813adb8de209472d24553b860d2515ea0aWu-cheng Li } 3006f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 301ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang private void rotateWheel() { 302ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang int totalDegrees = CLOSE_ICON_DEFAULT_DEGREES - SECOND_LEVEL_START_DEGREES; 303ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang int startAngle = ((mCurrentLevel == 0) ? CLOSE_ICON_DEFAULT_DEGREES 304ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang : SECOND_LEVEL_START_DEGREES); 305ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang if (mCurrentLevel == 0) totalDegrees = -totalDegrees; 306ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang 307ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang int elapsedTime = (int) (SystemClock.uptimeMillis() - mAnimationStartTime); 308ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang if (elapsedTime >= ANIMATION_TIME) { 309ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang elapsedTime = ANIMATION_TIME; 310ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang mCurrentLevel = (mCurrentLevel == 0) ? 1 : 0; 311ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang mInAnimation = false; 312ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang } 313ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang 314ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang int expectedAngle = startAngle + (totalDegrees * elapsedTime / ANIMATION_TIME); 315ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang double increment = Math.toRadians(expectedAngle) 316ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang - mChildRadians[mSecondLevelStartIndex]; 317ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang for (int i = 0 ; i < getChildCount(); ++i) mChildRadians[i] += increment; 31891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // We also need to rotate the zoom control wheel as well. 31991d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (mZoomControl != null) { 32091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang mZoomControl.rotate(mChildRadians[0] 32191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang - Math.toRadians(MAX_ZOOM_CONTROL_DEGREES)); 32291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang } 32391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang } 324ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang 32580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li @Override 3266f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li protected void onLayout( 3276f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li boolean changed, int left, int top, int right, int bottom) { 32891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (!mInitialized) return; 329ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang if (mInAnimation) { 330ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang rotateWheel(); 331ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang mHandler.post(mRunnable); 332ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang } 333ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang mCenterX = right - left - Util.dpToPixel( 334ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang IndicatorControlWheelContainer.FULL_WHEEL_RADIUS); 33562776cd28a40b4c6ada4b4aa38683f45572ae266Wu-cheng Li mCenterY = (bottom - top) / 2; 3366f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 337ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang // Layout the indicators based on the current level. 338ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang // The icons are spreaded on the left side of the shutter button. 339ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang for (int i = 0; i < getChildCount(); ++i) { 3406f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li View view = getChildAt(i); 341f060a6491f31e2e4b545ac6d71995074b969c7a2Chung-yih Wang // We still need to show the disabled indicators in the second level. 342ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang double radian = mChildRadians[i]; 343ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang double startVisibleRadians = mInAnimation 344ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang ? mStartVisibleRadians[1] 345ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang : mStartVisibleRadians[mCurrentLevel]; 346ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang double endVisibleRadians = mInAnimation 347ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang ? mEndVisibleRadians[1] 348ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang : mEndVisibleRadians[mCurrentLevel]; 349dffce0bd29d78b0168ed71f4d5aa2bd7c8a6efa3Chung-yih Wang if ((!view.isEnabled() && (mCurrentLevel == 0)) 350dffce0bd29d78b0168ed71f4d5aa2bd7c8a6efa3Chung-yih Wang || (radian < (startVisibleRadians - HIGHLIGHT_RADIANS / 2)) 351dffce0bd29d78b0168ed71f4d5aa2bd7c8a6efa3Chung-yih Wang || (radian > (endVisibleRadians + HIGHLIGHT_RADIANS / 2))) { 352ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang view.setVisibility(View.GONE); 353ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang continue; 354ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang } 355ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang view.setVisibility(View.VISIBLE); 35680715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li int x = mCenterX + (int)(mWheelRadius * Math.cos(radian)); 35780715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li int y = mCenterY - (int)(mWheelRadius * Math.sin(radian)); 35880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li int width = view.getMeasuredWidth(); 35980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li int height = view.getMeasuredHeight(); 36091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (view == mZoomControl) { 36191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // ZoomControlWheel matches the size of its parent view. 36291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang view.layout(0, 0, right - left, bottom - top); 36391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang } else { 36491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang view.layout(x - width / 2, y - height / 2, x + width / 2, 36591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang y + height / 2); 36691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang } 367f5bffc476b8293b30bb500bed2ab7914bb4ca411Wu-cheng Li } 3686f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li } 3696f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li 370ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang private void presetFirstLevelChildRadians() { 37191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // Set the visible range in the first-level indicator wheel. 37291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang mStartVisibleRadians[0] = Math.toRadians(FIRST_LEVEL_START_DEGREES); 37391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang mTouchSectorRadians[0] = HIGHLIGHT_RADIANS; 37491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang mEndVisibleRadians[0] = Math.toRadians(FIRST_LEVEL_END_DEGREES); 375ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang 37691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // Set the angle of each component in the first-level indicator wheel. 377ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang int startIndex = 0; 37891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (mZoomControl != null) { 37991d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang mChildRadians[startIndex++] = Math.toRadians(MAX_ZOOM_CONTROL_DEGREES); 380ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang } 38191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (mCameraPicker != null) { 38291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang mChildRadians[startIndex++] = Math.toRadians(FIRST_LEVEL_START_DEGREES); 38391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang } 38491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang mChildRadians[startIndex++] = Math.toRadians(FIRST_LEVEL_END_DEGREES); 385ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang } 386ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang 387ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang private void presetSecondLevelChildRadians() { 388ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang int count = getChildCountByLevel(1); 389f09f58c11f3e6b5670cfb1317bb2d7cf1e412d7cChung-yih Wang int sectors = (count <= 1) ? 1 : (count - 1); 390ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang double sectorDegrees = 391ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang ((SECOND_LEVEL_END_DEGREES - SECOND_LEVEL_START_DEGREES) / sectors); 392ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang mSectorRadians[1] = Math.toRadians(sectorDegrees); 393ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang 394ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang double degrees = CLOSE_ICON_DEFAULT_DEGREES; 395ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang mStartVisibleRadians[1] = Math.toRadians(SECOND_LEVEL_START_DEGREES); 396ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang 397ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang int startIndex = mSecondLevelStartIndex; 398ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang for (int i = 0; i < count; i++) { 399ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang mChildRadians[startIndex + i] = Math.toRadians(degrees); 400ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang degrees += sectorDegrees; 401ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang } 402ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang 403ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang // The radians for the touch sector of an indicator. 404ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang mTouchSectorRadians[1] = 405ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang Math.min(HIGHLIGHT_RADIANS, Math.toRadians(sectorDegrees)); 406ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang 407ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang mEndVisibleRadians[1] = Math.toRadians(SECOND_LEVEL_END_DEGREES); 408ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang } 409ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang 410a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li public void startTimeLapseAnimation(int timeLapseInterval, long startTime) { 411a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mTimeLapseInterval = timeLapseInterval; 412a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mRecordingStartTime = startTime; 413a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mNumberOfFrames = 0; 414a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li invalidate(); 415a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li } 416a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li 417a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li public void stopTimeLapseAnimation() { 418a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mTimeLapseInterval = 0; 419a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li invalidate(); 420a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li } 421a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li 422039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li private int getSelectedIndicatorIndex() { 423039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li for (int i = 0; i < mIndicators.size(); i++) { 424039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li AbstractIndicatorButton b = mIndicators.get(i); 425039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li if (b.getPopupWindow() != null) { 426039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li return indexOfChild(b); 427039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } 428039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } 429ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang if (mPressedIndex != -1) { 430ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang View v = getChildAt(mPressedIndex); 431ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang if (!(v instanceof AbstractIndicatorButton) && v.isEnabled()) { 432ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang return mPressedIndex; 433ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang } 434ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang } 435039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li return -1; 436039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li } 437039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li 43880715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li @Override 43980715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li protected void onDraw(Canvas canvas) { 440039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li int selectedIndex = getSelectedIndicatorIndex(); 441039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li 442039f1dc1fd89b5baeed8be05f3f21770cf75d4b3Wu-cheng Li // Draw the highlight arc if an indicator is selected or being pressed. 44391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // And skip the zoom control which index is zero. 44491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (selectedIndex >= 1) { 445ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang int degree = (int) Math.toDegrees(mChildRadians[selectedIndex]); 446da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang float innerR = (float) mShutterButtonRadius; 447da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang float outerR = (float) (mShutterButtonRadius + mStrokeWidth + 448da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang EDGE_STROKE_WIDTH * 0.5); 449da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang 450da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang // Construct the path of the fan-shaped semi-transparent area. 451da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang Path fanPath = new Path(); 452da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang mBackgroundRect.set(mCenterX - innerR, mCenterY - innerR, 453da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang mCenterX + innerR, mCenterY + innerR); 454da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang fanPath.arcTo(mBackgroundRect, -degree + HIGHLIGHT_DEGREES / 2, 455da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang -HIGHLIGHT_DEGREES); 456da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang mBackgroundRect.set(mCenterX - outerR, mCenterY - outerR, 457da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang mCenterX + outerR, mCenterY + outerR); 458da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang fanPath.arcTo(mBackgroundRect, -degree - HIGHLIGHT_DEGREES / 2, 459da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang HIGHLIGHT_DEGREES); 460da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang fanPath.close(); 461da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang 46219d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan mBackgroundPaint.setStrokeWidth(HIGHLIGHT_WIDTH); 463da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang mBackgroundPaint.setStrokeCap(Paint.Cap.SQUARE); 464da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang mBackgroundPaint.setStyle(Paint.Style.FILL_AND_STROKE); 465da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang mBackgroundPaint.setColor(HIGHLIGHT_FAN_COLOR); 466da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang canvas.drawPath(fanPath, mBackgroundPaint); 467da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang 468da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang // Draw the highlight edge 469da705aa1c8cbaaba105d9bc7799aef95c416a459Chih-Chung Chang mBackgroundPaint.setStyle(Paint.Style.STROKE); 47019d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan mBackgroundPaint.setColor(HIGHLIGHT_COLOR); 471ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang canvas.drawArc(mBackgroundRect, -degree - HIGHLIGHT_DEGREES / 2, 472ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang HIGHLIGHT_DEGREES, false, mBackgroundPaint); 47319d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan } 47419d029b83d65fb2e124f08aa8b98054974fc3acdHung-ying Tyan 475a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li // Draw arc shaped indicator in time lapse recording. 476a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li if (mTimeLapseInterval != 0) { 477a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li // Setup rectangle and paint. 478a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mBackgroundRect.set((float)(mCenterX - mShutterButtonRadius), 479a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li (float)(mCenterY - mShutterButtonRadius), 480a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li (float)(mCenterX + mShutterButtonRadius), 481a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li (float)(mCenterY + mShutterButtonRadius)); 482a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mBackgroundRect.inset(3f, 3f); 483a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mBackgroundPaint.setStrokeWidth(TIME_LAPSE_ARC_WIDTH); 484a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND); 485a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mBackgroundPaint.setColor(TIME_LAPSE_ARC_COLOR); 486a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li 487a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li // Compute the start angle and sweep angle. 488a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li long timeDelta = SystemClock.uptimeMillis() - mRecordingStartTime; 489a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li long numberOfFrames = timeDelta / mTimeLapseInterval; 490248232e937007384328592e0c269b42cf03c6899Wu-cheng Li float sweepAngle; 491a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li if (numberOfFrames > mNumberOfFrames) { 492248232e937007384328592e0c269b42cf03c6899Wu-cheng Li // The arc just acrosses 0 degree. Draw a full circle so it 493248232e937007384328592e0c269b42cf03c6899Wu-cheng Li // looks better. 494248232e937007384328592e0c269b42cf03c6899Wu-cheng Li sweepAngle = 360; 495a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li mNumberOfFrames = numberOfFrames; 496a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li } else { 497248232e937007384328592e0c269b42cf03c6899Wu-cheng Li sweepAngle = timeDelta % mTimeLapseInterval * 360f / mTimeLapseInterval; 498a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li } 499a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li 500248232e937007384328592e0c269b42cf03c6899Wu-cheng Li canvas.drawArc(mBackgroundRect, 0, sweepAngle, false, mBackgroundPaint); 501248232e937007384328592e0c269b42cf03c6899Wu-cheng Li invalidate(); 502a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li } 503a18b115b025fe0c3b046348953946c8db07e12ceWu-cheng Li 50480715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li super.onDraw(canvas); 50580715aae46e5a7a9aa378002301ba9d76f093776Wu-cheng Li } 506ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang 507ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang @Override 508ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang public void setEnabled(boolean enabled) { 509ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang super.setEnabled(enabled); 51091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (!mInitialized) return; 511b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang if (mCurrentMode == MODE_VIDEO) { 512b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang mSecondLevelIcon.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE); 513b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang mCloseIcon.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE); 514b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang requestLayout(); 515ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang } else { 516b1ce29490ec61847602ece16f3745417787d3d8fChung-yih Wang // We also disable the zoom button during snapshot. 5170bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li enableZoom(enabled); 518ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang } 519ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang mSecondLevelIcon.setEnabled(enabled); 520ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang mCloseIcon.setEnabled(enabled); 521ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang } 522ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang 5230bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li public void enableZoom(boolean enabled) { 52491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (mZoomControl != null) mZoomControl.setEnabled(enabled); 5250bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li } 5260bb4487fcc7601118111d4545c31dae04651b456Wu-cheng Li 527ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang public void onTouchOutBound() { 528ed6373cd34393dfdda8d18a03705c83bfff1f535Chung-yih Wang dismissSettingPopup(); 529ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang if (mPressedIndex != -1) { 530ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang injectMotionEvent(mPressedIndex, mLastMotionEvent, MotionEvent.ACTION_CANCEL); 531ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang mPressedIndex = -1; 532ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang invalidate(); 533ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang } 534ff45331706e2fe361b4ff736d90b0ad4af0e7be9Chung-yih Wang } 535a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang 536a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang public void dismissSecondLevelIndicator() { 537a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang if (mCurrentLevel == 1) { 538a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang changeIndicatorsLevel(); 539a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang } 540a18e1141ed70a052d257a641c9a8b6f152a81d95Chung-yih Wang } 5416f2283fbf049ad1f9870a86474af295604d95600Wu-cheng Li} 542