1 /*
2  * Copyright (C) 2014 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.focus;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.graphics.Canvas;
22 import android.graphics.Paint;
23 import android.graphics.Point;
24 import android.graphics.RectF;
25 import android.graphics.Region;
26 import android.util.AttributeSet;
27 import android.view.View;
28 
29 import com.android.camera.debug.Log;
30 import com.android.camera.debug.Log.Tag;
31 import com.android.camera.ui.motion.AnimationClock.SystemTimeClock;
32 import com.android.camera.ui.motion.DynamicAnimator;
33 import com.android.camera.ui.motion.Invalidator;
34 import com.android.camera.ui.motion.LinearScale;
35 import com.android.camera2.R;
36 
37 import javax.annotation.Nullable;
38 
39 /**
40  * Custom view for running the focus ring animations.
41  */
42 public class FocusRingView extends View implements Invalidator, FocusRing {
43     private static final Tag TAG = new Tag("FocusRingView");
44     private static final float FADE_IN_DURATION_MILLIS = 1000f;
45     private static final float FADE_OUT_DURATION_MILLIS = 250f;
46 
47     private final AutoFocusRing mAutoFocusRing;
48     private final ManualFocusRing mManualFocusRing;
49     private final DynamicAnimator mAnimator;
50     private final LinearScale mRatioScale;
51     private final float mDefaultRadiusPx;
52 
53     private FocusRingRenderer currentFocusAnimation;
54     private boolean isFirstDraw;
55     private float mLastRadiusPx;
56 
57     @Nullable
58     private RectF mPreviewSize;
59 
FocusRingView(Context context, AttributeSet attrs)60     public FocusRingView(Context context, AttributeSet attrs) {
61         super(context, attrs);
62 
63         Resources res = getResources();
64         Paint paint = makePaint(res, R.color.focus_color);
65 
66         float focusCircleMinSize = res.getDimensionPixelSize(R.dimen.focus_circle_min_size);
67         float focusCircleMaxSize = res.getDimensionPixelSize(R.dimen.focus_circle_max_size);
68         mDefaultRadiusPx = res.getDimensionPixelSize(R.dimen.focus_circle_initial_size);
69 
70         mRatioScale = new LinearScale(0, 1, focusCircleMinSize, focusCircleMaxSize);
71         mAnimator = new DynamicAnimator(this, new SystemTimeClock());
72 
73         mAutoFocusRing = new AutoFocusRing(mAnimator, paint,
74               FADE_IN_DURATION_MILLIS,
75               FADE_OUT_DURATION_MILLIS);
76         mManualFocusRing = new ManualFocusRing(mAnimator, paint,
77               FADE_OUT_DURATION_MILLIS);
78 
79         mAnimator.animations.add(mAutoFocusRing);
80         mAnimator.animations.add(mManualFocusRing);
81 
82         isFirstDraw = true;
83         mLastRadiusPx = mDefaultRadiusPx;
84     }
85 
86     @Override
isPassiveFocusRunning()87     public boolean isPassiveFocusRunning() {
88         return mAutoFocusRing.isActive();
89     }
90 
91     @Override
isActiveFocusRunning()92     public boolean isActiveFocusRunning() {
93         return mManualFocusRing.isActive();
94     }
95 
96     @Override
startPassiveFocus()97     public void startPassiveFocus() {
98         mAnimator.invalidate();
99         long tMs = mAnimator.getTimeMillis();
100 
101         if (mManualFocusRing.isActive() && !mManualFocusRing.isExiting()) {
102             mManualFocusRing.stop(tMs);
103         }
104 
105         mAutoFocusRing.start(tMs, mLastRadiusPx, mLastRadiusPx);
106         currentFocusAnimation = mAutoFocusRing;
107     }
108 
109     @Override
startActiveFocus()110     public void startActiveFocus() {
111         mAnimator.invalidate();
112         long tMs = mAnimator.getTimeMillis();
113 
114         if (mAutoFocusRing.isActive() && !mAutoFocusRing.isExiting()) {
115             mAutoFocusRing.stop(tMs);
116         }
117 
118         mManualFocusRing.start(tMs, 0.0f, mLastRadiusPx);
119         currentFocusAnimation = mManualFocusRing;
120     }
121 
122     @Override
stopFocusAnimations()123     public void stopFocusAnimations() {
124         long tMs = mAnimator.getTimeMillis();
125         if (mManualFocusRing.isActive() && !mManualFocusRing.isExiting()
126               && !mManualFocusRing.isEntering()) {
127             mManualFocusRing.exit(tMs);
128         }
129 
130         if (mAutoFocusRing.isActive() && !mAutoFocusRing.isExiting()) {
131             mAutoFocusRing.exit(tMs);
132         }
133     }
134 
135     @Override
setFocusLocation(float viewX, float viewY)136     public void setFocusLocation(float viewX, float viewY) {
137         mAutoFocusRing.setCenterX((int) viewX);
138         mAutoFocusRing.setCenterY((int) viewY);
139         mManualFocusRing.setCenterX((int) viewX);
140         mManualFocusRing.setCenterY((int) viewY);
141     }
142 
143     @Override
centerFocusLocation()144     public void centerFocusLocation() {
145         Point center = computeCenter();
146         mAutoFocusRing.setCenterX(center.x);
147         mAutoFocusRing.setCenterY(center.y);
148         mManualFocusRing.setCenterX(center.x);
149         mManualFocusRing.setCenterY(center.y);
150     }
151 
152     @Override
setRadiusRatio(float ratio)153     public void setRadiusRatio(float ratio) {
154         setRadius(mRatioScale.scale(mRatioScale.clamp(ratio)));
155     }
156 
157     @Override
configurePreviewDimensions(RectF previewArea)158     public void configurePreviewDimensions(RectF previewArea) {
159         mPreviewSize = previewArea;
160         mLastRadiusPx = mDefaultRadiusPx;
161 
162         if (!isFirstDraw) {
163             centerAutofocusRing();
164         }
165     }
166 
167     @Override
onDraw(Canvas canvas)168     protected void onDraw(Canvas canvas) {
169         if (isFirstDraw) {
170             isFirstDraw = false;
171             centerAutofocusRing();
172         }
173 
174         if (mPreviewSize != null) {
175             canvas.clipRect(mPreviewSize, Region.Op.REPLACE);
176         }
177 
178         mAnimator.draw(canvas);
179     }
180 
setRadius(float radiusPx)181     private void setRadius(float radiusPx) {
182         long tMs = mAnimator.getTimeMillis();
183         // Some devices return zero for invalid or "unknown" diopter values.
184         if (currentFocusAnimation != null && radiusPx > 0.1f) {
185             currentFocusAnimation.setRadius(tMs, radiusPx);
186             mLastRadiusPx = radiusPx;
187         }
188     }
189 
centerAutofocusRing()190     private void centerAutofocusRing() {
191         Point center = computeCenter();
192         mAutoFocusRing.setCenterX(center.x);
193         mAutoFocusRing.setCenterY(center.y);
194     }
195 
computeCenter()196     private Point computeCenter() {
197         if (mPreviewSize != null && (mPreviewSize.width() * mPreviewSize.height() > 0.01f)) {
198             Log.i(TAG, "Computing center via preview size.");
199             return new Point((int) mPreviewSize.centerX(), (int) mPreviewSize.centerY());
200         }
201         Log.i(TAG, "Computing center via view bounds.");
202         return new Point(getWidth() / 2, getHeight() / 2);
203     }
204 
makePaint(Resources res, int color)205     private Paint makePaint(Resources res, int color) {
206         Paint paint = new Paint();
207         paint.setAntiAlias(true);
208         paint.setColor(res.getColor(color));
209         paint.setStyle(Paint.Style.STROKE);
210         paint.setStrokeCap(Paint.Cap.ROUND);
211         paint.setStrokeWidth(res.getDimension(R.dimen.focus_circle_stroke));
212         return paint;
213     }
214 }
215