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