FaceView.java revision 8dbb2a65ef1b0404c169e8fada6c0d0f154dd08f
141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li/*
241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * Copyright (C) 2011 The Android Open Source Project
341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li *
441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * Licensed under the Apache License, Version 2.0 (the "License");
541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * you may not use this file except in compliance with the License.
641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * You may obtain a copy of the License at
741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li *
841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li *      http://www.apache.org/licenses/LICENSE-2.0
941ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li *
1041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * Unless required by applicable law or agreed to in writing, software
1141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * distributed under the License is distributed on an "AS IS" BASIS,
1241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * See the License for the specific language governing permissions and
1441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * limitations under the License.
1541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li */
1641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
1741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Lipackage com.android.camera.ui;
1841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
19c347dd2cccb6cbad8caa8404eae1c0ae17204156Ahbong Changimport android.annotation.TargetApi;
2041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.content.Context;
21918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolbimport android.content.res.Resources;
2241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.graphics.Canvas;
23630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reckimport android.graphics.Color;
2441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.graphics.Matrix;
25918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolbimport android.graphics.Paint;
26918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolbimport android.graphics.Paint.Style;
2741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.graphics.RectF;
2841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.hardware.Camera.Face;
29630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reckimport android.os.Handler;
30630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reckimport android.os.Message;
3141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.util.AttributeSet;
3241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.util.Log;
3341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.view.View;
3441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
35892781804513066b68b4a2fa6f846e862dac252aOwen Linimport com.android.camera.R;
36892781804513066b68b4a2fa6f846e862dac252aOwen Linimport com.android.camera.Util;
37c347dd2cccb6cbad8caa8404eae1c0ae17204156Ahbong Changimport com.android.gallery3d.common.ApiHelper;
38892781804513066b68b4a2fa6f846e862dac252aOwen Lin
39c347dd2cccb6cbad8caa8404eae1c0ae17204156Ahbong Chang@TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
403ebe49d8f1b4defb7cfd4850a14e795aada2ebd1Chih-yu Huangpublic class FaceView extends View implements FocusIndicator, Rotatable {
4164f9e54b860d158f8317bce7d4e8b6af5cbcfbabPin Ting    private static final String TAG = "FaceView";
4241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    private final boolean LOGV = false;
43c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    // The value for android.hardware.Camera.setDisplayOrientation.
4441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    private int mDisplayOrientation;
45c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    // The orientation compensation for the face indicator to make it look
46c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    // correctly in all device orientations. Ex: if the value is 90, the
47c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    // indicator should be rotated 90 degrees counter-clockwise.
48c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    private int mOrientation;
4941ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    private boolean mMirror;
50d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    private boolean mPause;
5141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    private Matrix mMatrix = new Matrix();
5241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    private RectF mRect = new RectF();
53630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    // As face detection can be flaky, we add a layer of filtering on top of it
54630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    // to avoid rapid changes in state (eg, flickering between has faces and
55630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    // not having faces)
5641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    private Face[] mFaces;
57630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private Face[] mPendingFaces;
58918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb    private int mColor;
59918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb    private final int mFocusingColor;
60918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb    private final int mFocusedColor;
61918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb    private final int mFailColor;
62918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb    private Paint mPaint;
638dbb2a65ef1b0404c169e8fada6c0d0f154dd08fMichael Kolb    private volatile boolean mBlocked;
6441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
65630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private static final int MSG_SWITCH_FACES = 1;
66630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private static final int SWITCH_DELAY = 70;
67630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private boolean mStateSwitchPending = false;
68630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private Handler mHandler = new Handler() {
69630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        @Override
70630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        public void handleMessage(Message msg) {
71630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            switch (msg.what) {
72630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            case MSG_SWITCH_FACES:
73630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                mStateSwitchPending = false;
74630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                mFaces = mPendingFaces;
75630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                invalidate();
76630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                break;
77630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            }
78630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        }
79630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    };
80630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck
8141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public FaceView(Context context, AttributeSet attrs) {
8241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        super(context, attrs);
83918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        Resources res = getResources();
84918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mFocusingColor = res.getColor(R.color.face_detect_start);
85918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mFocusedColor = res.getColor(R.color.face_detect_success);
86918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mFailColor = res.getColor(R.color.face_detect_fail);
87918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFocusingColor;
88918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mPaint = new Paint();
89918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mPaint.setAntiAlias(true);
90918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mPaint.setStyle(Style.STROKE);
91918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mPaint.setStrokeWidth(res.getDimension(R.dimen.focus_outer_stroke));
9241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
9341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
9441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public void setFaces(Face[] faces) {
9541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        if (LOGV) Log.v(TAG, "Num of faces=" + faces.length);
96d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li        if (mPause) return;
97630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        if (mFaces != null) {
98630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            if ((faces.length > 0 && mFaces.length == 0)
99630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                    || (faces.length == 0 && mFaces.length > 0)) {
100630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                mPendingFaces = faces;
101630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                if (!mStateSwitchPending) {
102630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                    mStateSwitchPending = true;
103630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                    mHandler.sendEmptyMessageDelayed(MSG_SWITCH_FACES, SWITCH_DELAY);
104630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                }
105630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                return;
106630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            }
107630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        }
108630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        if (mStateSwitchPending) {
109630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            mStateSwitchPending = false;
110630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            mHandler.removeMessages(MSG_SWITCH_FACES);
111630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        }
11241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        mFaces = faces;
11341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        invalidate();
11441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
11541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
11641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public void setDisplayOrientation(int orientation) {
11741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        mDisplayOrientation = orientation;
11841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        if (LOGV) Log.v(TAG, "mDisplayOrientation=" + orientation);
11941ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
12041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
1212865863f6a1887bda17e0a58a6a0f3aaac237cb6Angus Kong    @Override
1222865863f6a1887bda17e0a58a6a0f3aaac237cb6Angus Kong    public void setOrientation(int orientation, boolean animation) {
123c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li        mOrientation = orientation;
124c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li        invalidate();
125c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    }
126c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li
12741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public void setMirror(boolean mirror) {
12841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        mMirror = mirror;
12941ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        if (LOGV) Log.v(TAG, "mMirror=" + mirror);
13041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
13141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
13241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public boolean faceExists() {
13341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        return (mFaces != null && mFaces.length > 0);
13441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
13541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
13620b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    @Override
13720b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    public void showStart() {
138918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFocusingColor;
13920b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        invalidate();
14020b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    }
14120b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li
142e75e33cd3de39f1961d3fac8e7a51d126260de45Wu-cheng Li    // Ignore the parameter. No autofocus animation for face detection.
14320b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    @Override
144e75e33cd3de39f1961d3fac8e7a51d126260de45Wu-cheng Li    public void showSuccess(boolean timeout) {
145918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFocusedColor;
14620b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        invalidate();
14720b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    }
14820b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li
149e75e33cd3de39f1961d3fac8e7a51d126260de45Wu-cheng Li    // Ignore the parameter. No autofocus animation for face detection.
15020b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    @Override
151e75e33cd3de39f1961d3fac8e7a51d126260de45Wu-cheng Li    public void showFail(boolean timeout) {
152918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFailColor;
15320b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        invalidate();
15420b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    }
15520b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li
15620b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    @Override
15720b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    public void clear() {
15820b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        // Face indicator is displayed during preview. Do not clear the
15920b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        // drawable.
160918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFocusingColor;
16141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        mFaces = null;
16241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        invalidate();
16341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
16441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
165d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    public void pause() {
166d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li        mPause = true;
167d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    }
168d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li
169d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    public void resume() {
170d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li        mPause = false;
171d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    }
172d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li
1738dbb2a65ef1b0404c169e8fada6c0d0f154dd08fMichael Kolb    public void setBlockDraw(boolean block) {
1748dbb2a65ef1b0404c169e8fada6c0d0f154dd08fMichael Kolb        mBlocked = block;
1758dbb2a65ef1b0404c169e8fada6c0d0f154dd08fMichael Kolb    }
1768dbb2a65ef1b0404c169e8fada6c0d0f154dd08fMichael Kolb
17741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    @Override
17841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    protected void onDraw(Canvas canvas) {
1798dbb2a65ef1b0404c169e8fada6c0d0f154dd08fMichael Kolb        if (!mBlocked && (mFaces != null) && (mFaces.length > 0)) {
18041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li            // Prepare the matrix.
181048edf2ef22607b85bc4b062e2af71804f5b3530Chih-yu Huang            Util.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, getWidth(), getHeight());
18241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
183c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            // Focus indicator is directional. Rotate the matrix and the canvas
184c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            // so it looks correctly in all orientations.
185c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            canvas.save();
186c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            mMatrix.postRotate(mOrientation); // postRotate is clockwise
187c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas)
18841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li            for (int i = 0; i < mFaces.length; i++) {
18959585c89a3f85ea92c06426e5f6cc6038f91e308Wu-cheng Li                // Filter out false positives.
19059585c89a3f85ea92c06426e5f6cc6038f91e308Wu-cheng Li                if (mFaces[i].score < 50) continue;
19159585c89a3f85ea92c06426e5f6cc6038f91e308Wu-cheng Li
19241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li                // Transform the coordinates.
19341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li                mRect.set(mFaces[i].rect);
194048edf2ef22607b85bc4b062e2af71804f5b3530Chih-yu Huang                if (LOGV) Util.dumpRect(mRect, "Original rect");
19541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li                mMatrix.mapRect(mRect);
196048edf2ef22607b85bc4b062e2af71804f5b3530Chih-yu Huang                if (LOGV) Util.dumpRect(mRect, "Transformed rect");
197918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb                mPaint.setColor(mColor);
198918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb                canvas.drawOval(mRect, mPaint);
19941ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li            }
200c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            canvas.restore();
20141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        }
20241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        super.onDraw(canvas);
20341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
20441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li}
205