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