FaceView.java revision 630d255bdeeb1a13a71b0ecda0a38d6546c351e2
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;
6341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
64630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private static final int MSG_SWITCH_FACES = 1;
65630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private static final int SWITCH_DELAY = 70;
66630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private boolean mStateSwitchPending = false;
67630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private Handler mHandler = new Handler() {
68630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        @Override
69630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        public void handleMessage(Message msg) {
70630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            switch (msg.what) {
71630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            case MSG_SWITCH_FACES:
72630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                mStateSwitchPending = false;
73630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                mFaces = mPendingFaces;
74630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                invalidate();
75630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                break;
76630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            }
77630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        }
78630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    };
79630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck
8041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public FaceView(Context context, AttributeSet attrs) {
8141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        super(context, attrs);
82918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        Resources res = getResources();
83918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mFocusingColor = res.getColor(R.color.face_detect_start);
84918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mFocusedColor = res.getColor(R.color.face_detect_success);
85918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mFailColor = res.getColor(R.color.face_detect_fail);
86918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFocusingColor;
87918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mPaint = new Paint();
88918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mPaint.setAntiAlias(true);
89918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mPaint.setStyle(Style.STROKE);
90918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mPaint.setStrokeWidth(res.getDimension(R.dimen.focus_outer_stroke));
9141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
9241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
9341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public void setFaces(Face[] faces) {
9441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        if (LOGV) Log.v(TAG, "Num of faces=" + faces.length);
95d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li        if (mPause) return;
96630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        if (mFaces != null) {
97630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            if ((faces.length > 0 && mFaces.length == 0)
98630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                    || (faces.length == 0 && mFaces.length > 0)) {
99630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                mPendingFaces = faces;
100630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                if (!mStateSwitchPending) {
101630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                    mStateSwitchPending = true;
102630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                    mHandler.sendEmptyMessageDelayed(MSG_SWITCH_FACES, SWITCH_DELAY);
103630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                }
104630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                return;
105630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            }
106630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        }
107630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        if (mStateSwitchPending) {
108630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            mStateSwitchPending = false;
109630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            mHandler.removeMessages(MSG_SWITCH_FACES);
110630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        }
11141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        mFaces = faces;
11241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        invalidate();
11341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
11441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
11541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public void setDisplayOrientation(int orientation) {
11641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        mDisplayOrientation = orientation;
11741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        if (LOGV) Log.v(TAG, "mDisplayOrientation=" + orientation);
11841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
11941ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
1202865863f6a1887bda17e0a58a6a0f3aaac237cb6Angus Kong    @Override
1212865863f6a1887bda17e0a58a6a0f3aaac237cb6Angus Kong    public void setOrientation(int orientation, boolean animation) {
122c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li        mOrientation = orientation;
123c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li        invalidate();
124c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    }
125c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li
12641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public void setMirror(boolean mirror) {
12741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        mMirror = mirror;
12841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        if (LOGV) Log.v(TAG, "mMirror=" + mirror);
12941ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
13041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
13141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public boolean faceExists() {
13241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        return (mFaces != null && mFaces.length > 0);
13341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
13441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
13520b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    @Override
13620b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    public void showStart() {
137918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFocusingColor;
13820b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        invalidate();
13920b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    }
14020b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li
141e75e33cd3de39f1961d3fac8e7a51d126260de45Wu-cheng Li    // Ignore the parameter. No autofocus animation for face detection.
14220b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    @Override
143e75e33cd3de39f1961d3fac8e7a51d126260de45Wu-cheng Li    public void showSuccess(boolean timeout) {
144918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFocusedColor;
14520b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        invalidate();
14620b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    }
14720b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li
148e75e33cd3de39f1961d3fac8e7a51d126260de45Wu-cheng Li    // Ignore the parameter. No autofocus animation for face detection.
14920b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    @Override
150e75e33cd3de39f1961d3fac8e7a51d126260de45Wu-cheng Li    public void showFail(boolean timeout) {
151918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFailColor;
15220b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        invalidate();
15320b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    }
15420b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li
15520b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    @Override
15620b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    public void clear() {
15720b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        // Face indicator is displayed during preview. Do not clear the
15820b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        // drawable.
159918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFocusingColor;
16041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        mFaces = null;
16141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        invalidate();
16241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
16341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
164d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    public void pause() {
165d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li        mPause = true;
166d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    }
167d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li
168d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    public void resume() {
169d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li        mPause = false;
170d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    }
171d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li
17241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    @Override
17341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    protected void onDraw(Canvas canvas) {
17441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        if (mFaces != null && mFaces.length > 0) {
17541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li            // Prepare the matrix.
176048edf2ef22607b85bc4b062e2af71804f5b3530Chih-yu Huang            Util.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, getWidth(), getHeight());
17741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
178c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            // Focus indicator is directional. Rotate the matrix and the canvas
179c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            // so it looks correctly in all orientations.
180c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            canvas.save();
181c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            mMatrix.postRotate(mOrientation); // postRotate is clockwise
182c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas)
18341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li            for (int i = 0; i < mFaces.length; i++) {
18459585c89a3f85ea92c06426e5f6cc6038f91e308Wu-cheng Li                // Filter out false positives.
18559585c89a3f85ea92c06426e5f6cc6038f91e308Wu-cheng Li                if (mFaces[i].score < 50) continue;
18659585c89a3f85ea92c06426e5f6cc6038f91e308Wu-cheng Li
18741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li                // Transform the coordinates.
18841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li                mRect.set(mFaces[i].rect);
189048edf2ef22607b85bc4b062e2af71804f5b3530Chih-yu Huang                if (LOGV) Util.dumpRect(mRect, "Original rect");
19041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li                mMatrix.mapRect(mRect);
191048edf2ef22607b85bc4b062e2af71804f5b3530Chih-yu Huang                if (LOGV) Util.dumpRect(mRect, "Transformed rect");
192918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb                mPaint.setColor(mColor);
193918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb                canvas.drawOval(mRect, mPaint);
19441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li            }
195c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            canvas.restore();
19641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        }
19741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        super.onDraw(canvas);
19841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
19941ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li}
200