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