• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.annotation.TargetApi;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.graphics.Canvas;
23 import android.graphics.Matrix;
24 import android.graphics.Paint;
25 import android.graphics.Paint.Style;
26 import android.graphics.RectF;
27 import android.hardware.Camera.Face;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.util.AttributeSet;
31 import android.util.Log;
32 import android.view.View;
33 
34 import com.android.camera.CameraActivity;
35 import com.android.camera.CameraScreenNail;
36 import com.android.camera.R;
37 import com.android.camera.Util;
38 import com.android.gallery3d.common.ApiHelper;
39 
40 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
41 public class FaceView extends View implements FocusIndicator, Rotatable {
42     private static final String TAG = "CAM FaceView";
43     private final boolean LOGV = false;
44     // The value for android.hardware.Camera.setDisplayOrientation.
45     private int mDisplayOrientation;
46     // The orientation compensation for the face indicator to make it look
47     // correctly in all device orientations. Ex: if the value is 90, the
48     // indicator should be rotated 90 degrees counter-clockwise.
49     private int mOrientation;
50     private boolean mMirror;
51     private boolean mPause;
52     private Matrix mMatrix = new Matrix();
53     private RectF mRect = new RectF();
54     // As face detection can be flaky, we add a layer of filtering on top of it
55     // to avoid rapid changes in state (eg, flickering between has faces and
56     // not having faces)
57     private Face[] mFaces;
58     private Face[] mPendingFaces;
59     private int mColor;
60     private final int mFocusingColor;
61     private final int mFocusedColor;
62     private final int mFailColor;
63     private Paint mPaint;
64     private volatile boolean mBlocked;
65 
66     private static final int MSG_SWITCH_FACES = 1;
67     private static final int SWITCH_DELAY = 70;
68     private boolean mStateSwitchPending = false;
69     private Handler mHandler = new Handler() {
70         @Override
71         public void handleMessage(Message msg) {
72             switch (msg.what) {
73             case MSG_SWITCH_FACES:
74                 mStateSwitchPending = false;
75                 mFaces = mPendingFaces;
76                 invalidate();
77                 break;
78             }
79         }
80     };
81 
FaceView(Context context, AttributeSet attrs)82     public FaceView(Context context, AttributeSet attrs) {
83         super(context, attrs);
84         Resources res = getResources();
85         mFocusingColor = res.getColor(R.color.face_detect_start);
86         mFocusedColor = res.getColor(R.color.face_detect_success);
87         mFailColor = res.getColor(R.color.face_detect_fail);
88         mColor = mFocusingColor;
89         mPaint = new Paint();
90         mPaint.setAntiAlias(true);
91         mPaint.setStyle(Style.STROKE);
92         mPaint.setStrokeWidth(res.getDimension(R.dimen.face_circle_stroke));
93     }
94 
setFaces(Face[] faces)95     public void setFaces(Face[] faces) {
96         if (LOGV) Log.v(TAG, "Num of faces=" + faces.length);
97         if (mPause) return;
98         if (mFaces != null) {
99             if ((faces.length > 0 && mFaces.length == 0)
100                     || (faces.length == 0 && mFaces.length > 0)) {
101                 mPendingFaces = faces;
102                 if (!mStateSwitchPending) {
103                     mStateSwitchPending = true;
104                     mHandler.sendEmptyMessageDelayed(MSG_SWITCH_FACES, SWITCH_DELAY);
105                 }
106                 return;
107             }
108         }
109         if (mStateSwitchPending) {
110             mStateSwitchPending = false;
111             mHandler.removeMessages(MSG_SWITCH_FACES);
112         }
113         mFaces = faces;
114         invalidate();
115     }
116 
setDisplayOrientation(int orientation)117     public void setDisplayOrientation(int orientation) {
118         mDisplayOrientation = orientation;
119         if (LOGV) Log.v(TAG, "mDisplayOrientation=" + orientation);
120     }
121 
122     @Override
setOrientation(int orientation, boolean animation)123     public void setOrientation(int orientation, boolean animation) {
124         mOrientation = orientation;
125         invalidate();
126     }
127 
setMirror(boolean mirror)128     public void setMirror(boolean mirror) {
129         mMirror = mirror;
130         if (LOGV) Log.v(TAG, "mMirror=" + mirror);
131     }
132 
faceExists()133     public boolean faceExists() {
134         return (mFaces != null && mFaces.length > 0);
135     }
136 
137     @Override
showStart()138     public void showStart() {
139         mColor = mFocusingColor;
140         invalidate();
141     }
142 
143     // Ignore the parameter. No autofocus animation for face detection.
144     @Override
showSuccess(boolean timeout)145     public void showSuccess(boolean timeout) {
146         mColor = mFocusedColor;
147         invalidate();
148     }
149 
150     // Ignore the parameter. No autofocus animation for face detection.
151     @Override
showFail(boolean timeout)152     public void showFail(boolean timeout) {
153         mColor = mFailColor;
154         invalidate();
155     }
156 
157     @Override
clear()158     public void clear() {
159         // Face indicator is displayed during preview. Do not clear the
160         // drawable.
161         mColor = mFocusingColor;
162         mFaces = null;
163         invalidate();
164     }
165 
pause()166     public void pause() {
167         mPause = true;
168     }
169 
resume()170     public void resume() {
171         mPause = false;
172     }
173 
setBlockDraw(boolean block)174     public void setBlockDraw(boolean block) {
175         mBlocked = block;
176     }
177 
178     @Override
onDraw(Canvas canvas)179     protected void onDraw(Canvas canvas) {
180         if (!mBlocked && (mFaces != null) && (mFaces.length > 0)) {
181             final CameraScreenNail sn = ((CameraActivity) getContext()).getCameraScreenNail();
182             int rw = sn.getUncroppedRenderWidth();
183             int rh = sn.getUncroppedRenderHeight();
184             // Prepare the matrix.
185             if (((rh > rw) && ((mDisplayOrientation == 0) || (mDisplayOrientation == 180)))
186                     || ((rw > rh) && ((mDisplayOrientation == 90) || (mDisplayOrientation == 270)))) {
187                 int temp = rw;
188                 rw = rh;
189                 rh = temp;
190             }
191             Util.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, rw, rh);
192             int dx = (getWidth() - rw) / 2;
193             int dy = (getHeight() - rh) / 2;
194 
195             // Focus indicator is directional. Rotate the matrix and the canvas
196             // so it looks correctly in all orientations.
197             canvas.save();
198             mMatrix.postRotate(mOrientation); // postRotate is clockwise
199             canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas)
200             for (int i = 0; i < mFaces.length; i++) {
201                 // Filter out false positives.
202                 if (mFaces[i].score < 50) continue;
203 
204                 // Transform the coordinates.
205                 mRect.set(mFaces[i].rect);
206                 if (LOGV) Util.dumpRect(mRect, "Original rect");
207                 mMatrix.mapRect(mRect);
208                 if (LOGV) Util.dumpRect(mRect, "Transformed rect");
209                 mPaint.setColor(mColor);
210                 mRect.offset(dx, dy);
211                 canvas.drawOval(mRect, mPaint);
212             }
213             canvas.restore();
214         }
215         super.onDraw(canvas);
216     }
217 }
218