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