1 /*
2  * Copyright (C) 2013 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 android.animation.ValueAnimator;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.graphics.Canvas;
23 import android.graphics.Paint;
24 import android.graphics.Rect;
25 import android.graphics.drawable.Drawable;
26 import android.util.AttributeSet;
27 import android.view.View;
28 
29 import com.android.camera.FocusOverlayManager;
30 import com.android.camera.debug.DebugPropertyHelper;
31 import com.android.camera.debug.Log;
32 import com.android.camera2.R;
33 
34 /**
35  * Displays a focus indicator.
36  */
37 public class FocusOverlay extends View implements FocusOverlayManager.FocusUI {
38     private static final Log.Tag TAG = new Log.Tag("FocusOverlay");
39 
40     /** System Properties switch to enable debugging focus UI. */
41     private static final boolean CAPTURE_DEBUG_UI = DebugPropertyHelper.showCaptureDebugUI();
42 
43     private final static int FOCUS_DURATION_MS = 500;
44     private final static int FOCUS_INDICATOR_ROTATION_DEGREES = 50;
45 
46     private final Drawable mFocusIndicator;
47     private Drawable mFocusOuterRing;
48     private final Rect mBounds = new Rect();
49     private final ValueAnimator mFocusAnimation = new ValueAnimator();
50 
51     private Paint mDebugSolidPaint;
52     private Paint mDebugCornersPaint;
53     private Paint mDebugTextPaint;
54     private int mDebugStartColor;
55     private int mDebugSuccessColor;
56     private int mDebugFailColor;
57     private Rect mFocusDebugSolidRect;
58     private Rect mFocusDebugCornersRect;
59     private boolean mIsPassiveScan;
60     private String mDebugMessage;
61 
62     private int mPositionX;
63     private int mPositionY;
64     private int mAngle;
65     private final int mFocusIndicatorSize;
66     private boolean mShowIndicator;
67     private final int mFocusOuterRingSize;
68 
FocusOverlay(Context context, AttributeSet attrs)69     public FocusOverlay(Context context, AttributeSet attrs) {
70         super(context, attrs);
71         mFocusIndicator = getResources().getDrawable(R.drawable.focus_ring_touch_inner);
72         mFocusIndicatorSize = getResources().getDimensionPixelSize(R.dimen.focus_inner_ring_size);
73         mFocusOuterRing = getResources().getDrawable(R.drawable.focus_ring_touch_outer);
74         mFocusOuterRingSize = getResources().getDimensionPixelSize(R.dimen.focus_outer_ring_size);
75 
76         if (CAPTURE_DEBUG_UI) {
77             Resources res = getResources();
78             mDebugStartColor = res.getColor(R.color.focus_debug);
79             mDebugSuccessColor = res.getColor(R.color.focus_debug_success);
80             mDebugFailColor = res.getColor(R.color.focus_debug_fail);
81             mDebugTextPaint= new Paint();
82             mDebugTextPaint.setColor(res.getColor(R.color.focus_debug_text));
83             mDebugTextPaint.setStyle(Paint.Style.FILL);
84             mDebugSolidPaint = new Paint();
85             mDebugSolidPaint.setColor(res.getColor(R.color.focus_debug));
86             mDebugSolidPaint.setAntiAlias(true);
87             mDebugSolidPaint.setStyle(Paint.Style.STROKE);
88             mDebugSolidPaint.setStrokeWidth(res.getDimension(R.dimen.focus_debug_stroke));
89             mDebugCornersPaint = new Paint(mDebugSolidPaint);
90             mDebugCornersPaint.setColor(res.getColor(R.color.focus_debug));
91             mFocusDebugSolidRect = new Rect();
92             mFocusDebugCornersRect = new Rect();
93         }
94     }
95 
96     @Override
hasFaces()97     public boolean hasFaces() {
98         // TODO: Add face detection support.
99         return false;
100     }
101 
102     @Override
clearFocus()103     public void clearFocus() {
104         mShowIndicator = false;
105         if (CAPTURE_DEBUG_UI) {
106             setVisibility(INVISIBLE);
107         }
108     }
109 
110     @Override
setFocusPosition(int x, int y, boolean isPassiveScan)111     public void setFocusPosition(int x, int y, boolean isPassiveScan) {
112         setFocusPosition(x, y, isPassiveScan, 0, 0);
113     }
114 
115     @Override
setFocusPosition(int x, int y, boolean isPassiveScan, int aFsize, int aEsize)116     public void setFocusPosition(int x, int y, boolean isPassiveScan, int aFsize, int aEsize) {
117         mIsPassiveScan = isPassiveScan;
118         mPositionX = x;
119         mPositionY = y;
120         mBounds.set(x - mFocusIndicatorSize / 2, y - mFocusIndicatorSize / 2,
121                 x + mFocusIndicatorSize / 2, y + mFocusIndicatorSize / 2);
122         mFocusIndicator.setBounds(mBounds);
123         mFocusOuterRing.setBounds(x - mFocusOuterRingSize / 2, y - mFocusOuterRingSize / 2,
124                 x + mFocusOuterRingSize / 2, y + mFocusOuterRingSize / 2);
125 
126         if (CAPTURE_DEBUG_UI) {
127             mFocusOuterRing.setBounds(0, 0, 0, 0);
128             if (isPassiveScan) {
129                 // Use AE rect only.
130                 mFocusDebugSolidRect.setEmpty();
131                 int avg = (aFsize + aEsize) / 2;
132                 mFocusDebugCornersRect.set(x - avg / 2, y - avg / 2, x + avg / 2, y + avg / 2);
133             } else {
134                 mFocusDebugSolidRect.set(x - aFsize / 2, y - aFsize / 2, x + aFsize / 2,
135                         y + aFsize / 2);
136                 // If AE region is different size than AF region and active scan.
137                 if (aFsize != aEsize) {
138                     mFocusDebugCornersRect.set(x - aEsize / 2, y - aEsize / 2, x + aEsize / 2,
139                             y + aEsize / 2);
140                 } else {
141                     mFocusDebugCornersRect.setEmpty();
142                 }
143             }
144             mDebugSolidPaint.setColor(mDebugStartColor);
145             mDebugCornersPaint.setColor(mDebugStartColor);
146         }
147 
148         if (getVisibility() != VISIBLE) {
149             setVisibility(VISIBLE);
150         }
151         invalidate();
152     }
153 
154     /**
155      * This is called in:
156      * <ul>
157      * <li>API1 non-CAF after autoFocus().</li>
158      * <li>API1 CAF mode for onAutoFocusMoving(true).</li>
159      * <li>API2 for transition to ACTIVE_SCANNING or PASSIVE_SCANNING.</li>
160      * <ul>
161      * TODO after PhotoModule/GcamModule deprecation: Do not use this for CAF.
162      */
163     @Override
onFocusStarted()164     public void onFocusStarted() {
165         mShowIndicator = true;
166         mFocusAnimation.setIntValues(0, FOCUS_INDICATOR_ROTATION_DEGREES);
167         mFocusAnimation.setDuration(FOCUS_DURATION_MS);
168         mFocusAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
169             @Override
170             public void onAnimationUpdate(ValueAnimator animation) {
171                 mAngle = (Integer) animation.getAnimatedValue();
172                 invalidate();
173             }
174         });
175         mFocusAnimation.start();
176         if (CAPTURE_DEBUG_UI) {
177             mDebugMessage = null;
178         }
179     }
180 
181     /**
182      * This is called in:
183      * <ul>
184      * <li>API1 non-CAF for onAutoFocus(true).</li>
185      * <li>API2 non-CAF for transition to FOCUSED_LOCKED.</li>
186      * <li>API1 CAF mode for onAutoFocusMoving(false).</li>
187      * <ul>
188      * TODO after PhotoModule/GcamModule deprecation: Do not use this for CAF.
189      */
190     @Override
onFocusSucceeded()191     public void onFocusSucceeded() {
192         mFocusAnimation.cancel();
193         mShowIndicator = false;
194         if (CAPTURE_DEBUG_UI && !mIsPassiveScan) {
195             mDebugSolidPaint.setColor(mDebugSuccessColor);
196         }
197         invalidate();
198     }
199 
200     /**
201      * This is called in:
202      * <ul>
203      * <li>API1 non-CAF for onAutoFocus(false).</li>
204      * <li>API2 non-CAF for transition to NOT_FOCUSED_LOCKED.</li>
205      * <ul>
206      */
207     @Override
onFocusFailed()208     public void onFocusFailed() {
209         mFocusAnimation.cancel();
210         mShowIndicator = false;
211         if (CAPTURE_DEBUG_UI && !mIsPassiveScan) {
212             mDebugSolidPaint.setColor(mDebugFailColor);
213         }
214         invalidate();
215     }
216 
217     /**
218      * This is called in:
219      * API2 for CAF state changes to PASSIVE_FOCUSED or PASSIVE_UNFOCUSED.
220      */
221     @Override
setPassiveFocusSuccess(boolean success)222     public void setPassiveFocusSuccess(boolean success) {
223         mFocusAnimation.cancel();
224         mShowIndicator = false;
225         if (CAPTURE_DEBUG_UI) {
226             mDebugCornersPaint.setColor(success ? mDebugSuccessColor : mDebugFailColor);
227         }
228         invalidate();
229     }
230 
231     @Override
showDebugMessage(String message)232     public void showDebugMessage(String message) {
233         if (CAPTURE_DEBUG_UI) {
234             mDebugMessage = message;
235         }
236     }
237 
238     @Override
pauseFaceDetection()239     public void pauseFaceDetection() {
240         // TODO: Add face detection support.
241     }
242 
243     @Override
resumeFaceDetection()244     public void resumeFaceDetection() {
245         // TODO: Add face detection support.
246     }
247 
248     @Override
onDraw(Canvas canvas)249     public void onDraw(Canvas canvas) {
250         super.onDraw(canvas);
251 
252         if (mShowIndicator) {
253             mFocusOuterRing.draw(canvas);
254             canvas.save();
255             canvas.rotate(mAngle, mPositionX, mPositionY);
256             mFocusIndicator.draw(canvas);
257             canvas.restore();
258         }
259         if (CAPTURE_DEBUG_UI) {
260             canvas.drawRect(mFocusDebugSolidRect, mDebugSolidPaint);
261             float delta = 0.1f * mFocusDebugCornersRect.width();
262             float left = mFocusDebugCornersRect.left;
263             float top = mFocusDebugCornersRect.top;
264             float right = mFocusDebugCornersRect.right;
265             float bot = mFocusDebugCornersRect.bottom;
266 
267             canvas.drawLines(new float[]{left, top + delta, left, top, left, top, left + delta, top}, mDebugCornersPaint);
268             canvas.drawLines(new float[]{right, top + delta, right, top, right, top, right - delta, top}, mDebugCornersPaint);
269             canvas.drawLines(new float[]{left, bot - delta, left, bot, left, bot, left + delta, bot}, mDebugCornersPaint);
270             canvas.drawLines(new float[]{right, bot - delta, right, bot, right, bot, right - delta, bot}, mDebugCornersPaint);
271 
272             if (mDebugMessage != null) {
273                 mDebugTextPaint.setTextSize(40);
274                 canvas.drawText(mDebugMessage, left - 4, bot + 44, mDebugTextPaint);
275             }
276         }
277     }
278 }
279