1ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang/* 2ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * Copyright (C) 2011 The Android Open Source Project 3ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * 4ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * you may not use this file except in compliance with the License. 6ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * You may obtain a copy of the License at 7ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * 8ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * http://www.apache.org/licenses/LICENSE-2.0 9ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * 10ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * Unless required by applicable law or agreed to in writing, software 11ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * distributed under the License is distributed on an "AS IS" BASIS, 12ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * See the License for the specific language governing permissions and 14ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang * limitations under the License. 15ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang */ 16ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 17ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wangpackage com.android.camera.ui; 18ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 19ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wangimport com.android.camera.R; 20ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wangimport com.android.camera.Util; 21ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 22ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wangimport android.content.Context; 23ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wangimport android.content.res.Resources; 24ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wangimport android.graphics.Canvas; 25ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wangimport android.graphics.Paint; 26ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wangimport android.graphics.RectF; 27ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wangimport android.util.AttributeSet; 28ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wangimport android.view.MotionEvent; 29ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wangimport android.view.View; 30ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 31ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang/** 3291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang * A view that contains camera zoom control and its layout. In addition to the 3391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang * zoom control, the method {@link #rotate} is added for rotation animation 3491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang * which is called in switching between first-level and second-level indicators. 35ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang */ 36ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wangpublic class ZoomControlWheel extends ZoomControl { 37ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private static final String TAG = "ZoomControlWheel"; 38ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private static final int HIGHLIGHT_WIDTH = 4; 39ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private static final int HIGHLIGHT_DEGREES = 30; 40ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private static final int TRAIL_WIDTH = 2; 4191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang private static final int ZOOM_IN_ICON_DEGREES = 96; 4291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang private static final int ZOOM_OUT_ICON_DEGREES = 264; 43ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private static final int MAX_SLIDER_ANGLE = 44ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang ZOOM_OUT_ICON_DEGREES - (HIGHLIGHT_DEGREES / 2); 45ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private static final int MIN_SLIDER_ANGLE = 46ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang ZOOM_IN_ICON_DEGREES + (HIGHLIGHT_DEGREES / 2); 4791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang private static final int DEFAULT_SLIDER_POSITION = MAX_SLIDER_ANGLE; 48ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private static final float EDGE_STROKE_WIDTH = 6f; 49ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private static final double BUFFER_RADIANS = Math.toRadians(HIGHLIGHT_DEGREES / 2); 5091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang private static final double SLIDER_RANGE = 5191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang Math.toRadians(MAX_SLIDER_ANGLE - MIN_SLIDER_ANGLE); 5291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang private double mSliderRadians = DEFAULT_SLIDER_POSITION; 53ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 54ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private final int HIGHLIGHT_COLOR; 55ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private final int TRAIL_COLOR; 56ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 57ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang // The center of the shutter button. 58ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private int mCenterX, mCenterY; 59ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang // The width of the wheel stroke. 60ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private int mStrokeWidth; 61ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private double mShutterButtonRadius; 62ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private double mWheelRadius; 63ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private Paint mBackgroundPaint; 64ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private RectF mBackgroundRect; 65ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 6691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang private double mRotateAngle; 6791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang 68ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang public ZoomControlWheel(Context context, AttributeSet attrs) { 69ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang super(context, attrs); 70ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang setWillNotDraw(false); 71ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 72ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mBackgroundPaint = new Paint(); 73ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mBackgroundPaint.setStyle(Paint.Style.STROKE); 74ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mBackgroundPaint.setAntiAlias(true); 75ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 76ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mBackgroundRect = new RectF(); 77ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang Resources resources = context.getResources(); 78ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang HIGHLIGHT_COLOR = resources.getColor(R.color.review_control_pressed_color); 79ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang TRAIL_COLOR = resources.getColor(R.color.icon_disabled_color); 80ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 81ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mShutterButtonRadius = IndicatorControlWheelContainer.SHUTTER_BUTTON_RADIUS; 82ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mStrokeWidth = Util.dpToPixel(IndicatorControlWheelContainer.STROKE_WIDTH); 83ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mWheelRadius = mShutterButtonRadius + mStrokeWidth * 0.5; 84ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang } 85ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 86ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang @Override 87ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang public boolean dispatchTouchEvent(MotionEvent event) { 8891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (!onFilterTouchEventForSecurity(event) || !isEnabled()) return false; 89ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang int action = event.getAction(); 90ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 91ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang double dx = event.getX() - mCenterX; 92ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang double dy = mCenterY - event.getY(); 93ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang double radius = Math.sqrt(dx * dx + dy * dy); 94ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang // Ignore the event if too far from the shutter button. 9591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang double angle = Math.atan2(dy, dx); 9691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if (angle < 0) angle += (2 * Math.PI); 9791d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang mSliderRadians = getSliderDrawAngle(angle); 9891d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang 99ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang // We assume the slider button is pressed all the time when the 100ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang // zoom control is active. So we take care of the following events 101ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang // only. 102ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang switch (action) { 103ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang case MotionEvent.ACTION_OUTSIDE: 104ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang case MotionEvent.ACTION_UP: 105ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang case MotionEvent.ACTION_CANCEL: 106ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang closeZoomControl(); 107ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang break; 108b7d0a0704e437a1892fbf2a6e5138cf36df9fde8Chung-yih Wang case MotionEvent.ACTION_MOVE: 10991d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang performZoom((Math.toRadians(MAX_SLIDER_ANGLE) 11091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang - mSliderRadians) / SLIDER_RANGE); 111ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang requestLayout(); 112ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang } 113ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang return true; 114ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang } 115ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 116ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang @Override 117ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang public void startZoomControl() { 118ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang super.startZoomControl(); 119ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mSliderRadians = Math.toRadians(DEFAULT_SLIDER_POSITION); 120ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang } 121ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 122ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private void layoutIcon(View view, double radian) { 12391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // Rotate the wheel with the angle when the wheel is rotating or 12491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang // the indicator control is in the second-level. 12591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang radian += mRotateAngle; 126ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang int x = mCenterX + (int)(mWheelRadius * Math.cos(radian)); 127ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang int y = mCenterY - (int)(mWheelRadius * Math.sin(radian)); 128ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang int width = view.getMeasuredWidth(); 129ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang int height = view.getMeasuredHeight(); 130ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang view.layout(x - width / 2, y - height / 2, x + width / 2, 131ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang y + height / 2); 132ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang } 133ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 13491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang private double getSliderDrawAngle(double sliderAngle) { 135ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang if (sliderAngle > Math.toRadians(MAX_SLIDER_ANGLE)) { 136ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang return Math.toRadians(MAX_SLIDER_ANGLE); 137ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang } else if (sliderAngle < Math.toRadians(MIN_SLIDER_ANGLE)) { 138ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang return Math.toRadians(MIN_SLIDER_ANGLE); 139ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang } 140ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang return sliderAngle; 141ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang } 142ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 143ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang @Override 144ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang protected void onLayout( 145ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang boolean changed, int left, int top, int right, int bottom) { 146ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mCenterX = right - left - Util.dpToPixel( 147ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang IndicatorControlWheelContainer.FULL_WHEEL_RADIUS); 148ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mCenterY = (bottom - top) / 2; 149ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang layoutIcon(mZoomIn, Math.toRadians(ZOOM_IN_ICON_DEGREES)); 150ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang layoutIcon(mZoomOut, Math.toRadians(ZOOM_OUT_ICON_DEGREES)); 15191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang layoutIcon(mZoomSlider, getSliderDrawAngle(mSliderRadians)); 152ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang } 153ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 154ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private double getZoomIndexAngle() { 155f09f58c11f3e6b5670cfb1317bb2d7cf1e412d7cChung-yih Wang if (mZoomMax == 0) return Math.PI; 156ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang return Math.toRadians(MAX_SLIDER_ANGLE - 157ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang (MAX_SLIDER_ANGLE - MIN_SLIDER_ANGLE) * mZoomIndex / mZoomMax); 158ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang } 159ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 160ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang private void drawArc(Canvas canvas, int startAngle, int sweepAngle, 161ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang double radius, int color, int width) { 162ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mBackgroundRect.set((float) (mCenterX - radius), (float) (mCenterY - radius), 163ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang (float) (mCenterX + radius), (float) (mCenterY + radius)); 164ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mBackgroundPaint.setStrokeWidth(width); 165ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND); 166ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mBackgroundPaint.setColor(color); 167ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang canvas.drawArc(mBackgroundRect, startAngle, sweepAngle, false, mBackgroundPaint); 168ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang } 169ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang 170ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang @Override 171ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang protected void onDraw(Canvas canvas) { 172ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang // Draw the slider trail. 17391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang int startAngle = -MAX_SLIDER_ANGLE - (int) Math.toDegrees(mRotateAngle); 17491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang int radians = (MAX_SLIDER_ANGLE - MIN_SLIDER_ANGLE); 17591d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang if ((startAngle + radians) > 0) radians = -startAngle; 17691d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang drawArc(canvas, startAngle, radians, 177ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang mWheelRadius, TRAIL_COLOR, TRAIL_WIDTH); 178ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang super.onDraw(canvas); 179ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang } 18091d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang 18191d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang public void rotate(double angle) { 18291d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang mRotateAngle = angle; 18391d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang requestLayout(); 18491d70ce513ffee99b216f88a92650e1b63575289Chung-yih Wang } 185ab2ffa88872149978823c3184d0af162d3cdca13Chung-yih Wang} 186