18872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb/*
28872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * Copyright (C) 2011 The Android Open Source Project
38872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb *
48872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License");
58872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * you may not use this file except in compliance with the License.
68872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * You may obtain a copy of the License at
78872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb *
88872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb *      http://www.apache.org/licenses/LICENSE-2.0
98872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb *
108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * Unless required by applicable law or agreed to in writing, software
118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS,
128872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * See the License for the specific language governing permissions and
148872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * limitations under the License.
158872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb */
168872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
178872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbpackage com.android.camera.ui;
188872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
198872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.content.Context;
208872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.content.res.Resources;
218872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.graphics.Canvas;
228872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.graphics.Matrix;
238872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.graphics.Paint;
248872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.graphics.Paint.Style;
258872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.graphics.RectF;
268872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.hardware.Camera.Face;
278872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.os.Handler;
288872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.os.Message;
298872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.util.AttributeSet;
308872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.view.View;
318872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
322bca210e5fc8a77685775ffb403096167b017dceAngus Kongimport com.android.camera.debug.Log;
33638e6f06c877d90b907f66ea9c22b3c6b73c7384Sascha Haeberlingimport com.android.camera.util.CameraUtil;
348e963a5a6016d246184ed65906f9d103e92b17e2Sascha Haeberlingimport com.android.camera2.R;
358872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
36753bb8aa56fff425fe16b93be368b9b236e4751fDoris Liupublic class FaceView extends View
3701d56038a53487a9b7989bd17c732b8919da64f1Paul Rohde    implements Rotatable, PreviewStatusListener.PreviewAreaChangedListener {
382bca210e5fc8a77685775ffb403096167b017dceAngus Kong    private static final Log.Tag TAG = new Log.Tag("FaceView");
398872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private final boolean LOGV = false;
408872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // The value for android.hardware.Camera.setDisplayOrientation.
418872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mDisplayOrientation;
428872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // The orientation compensation for the face indicator to make it look
438872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // correctly in all device orientations. Ex: if the value is 90, the
448872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // indicator should be rotated 90 degrees counter-clockwise.
458872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mOrientation;
468872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private boolean mMirror;
478872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private boolean mPause;
488872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Matrix mMatrix = new Matrix();
498872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private RectF mRect = new RectF();
508872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // As face detection can be flaky, we add a layer of filtering on top of it
518872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // to avoid rapid changes in state (eg, flickering between has faces and
528872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    // not having faces)
538872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Face[] mFaces;
548872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Face[] mPendingFaces;
558872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private int mColor;
568872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Paint mPaint;
578872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private volatile boolean mBlocked;
588872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
598872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final int MSG_SWITCH_FACES = 1;
608872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private static final int SWITCH_DELAY = 70;
618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private boolean mStateSwitchPending = false;
628872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private Handler mHandler = new Handler() {
638872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        @Override
648872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        public void handleMessage(Message msg) {
658872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            switch (msg.what) {
668872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            case MSG_SWITCH_FACES:
678872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                mStateSwitchPending = false;
688872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                mFaces = mPendingFaces;
698872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                invalidate();
708872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                break;
718872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
728872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    };
7459401045bf479b2617c981075aec92190ec59aebDoris Liu    private final RectF mPreviewArea = new RectF();
758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public FaceView(Context context, AttributeSet attrs) {
778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        super(context, attrs);
788872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        Resources res = getResources();
79e2f5fdda4b19da650a7616853990624969d0f384Andy Huibers        mColor = res.getColor(R.color.face_detect_start);
808872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mPaint = new Paint();
818872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mPaint.setAntiAlias(true);
828872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mPaint.setStyle(Style.STROKE);
838872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mPaint.setStrokeWidth(res.getDimension(R.dimen.face_circle_stroke));
848872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
858872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
868872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void setFaces(Face[] faces) {
872bca210e5fc8a77685775ffb403096167b017dceAngus Kong        if (LOGV) {
882bca210e5fc8a77685775ffb403096167b017dceAngus Kong            Log.v(TAG, "Num of faces=" + faces.length);
892bca210e5fc8a77685775ffb403096167b017dceAngus Kong        }
908872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mPause) return;
918872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mFaces != null) {
928872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if ((faces.length > 0 && mFaces.length == 0)
938872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    || (faces.length == 0 && mFaces.length > 0)) {
948872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                mPendingFaces = faces;
958872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                if (!mStateSwitchPending) {
968872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    mStateSwitchPending = true;
978872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    mHandler.sendEmptyMessageDelayed(MSG_SWITCH_FACES, SWITCH_DELAY);
988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                }
998872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                return;
1008872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
1018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
1028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mStateSwitchPending) {
1038872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mStateSwitchPending = false;
1048872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mHandler.removeMessages(MSG_SWITCH_FACES);
1058872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
1068872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFaces = faces;
1078872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        invalidate();
1088872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1098872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void setDisplayOrientation(int orientation) {
1118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mDisplayOrientation = orientation;
1122bca210e5fc8a77685775ffb403096167b017dceAngus Kong        if (LOGV) {
1132bca210e5fc8a77685775ffb403096167b017dceAngus Kong            Log.v(TAG, "mDisplayOrientation=" + orientation);
1142bca210e5fc8a77685775ffb403096167b017dceAngus Kong        }
1158872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1168872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1178872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    @Override
1188872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void setOrientation(int orientation, boolean animation) {
1198872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mOrientation = orientation;
1208872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        invalidate();
1218872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1228872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1238872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void setMirror(boolean mirror) {
1248872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mMirror = mirror;
1252bca210e5fc8a77685775ffb403096167b017dceAngus Kong        if (LOGV) {
1262bca210e5fc8a77685775ffb403096167b017dceAngus Kong            Log.v(TAG, "mMirror=" + mirror);
1272bca210e5fc8a77685775ffb403096167b017dceAngus Kong        }
1288872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1298872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1308872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public boolean faceExists() {
1318872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        return (mFaces != null && mFaces.length > 0);
1328872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1338872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1348872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void clear() {
1358872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        // Face indicator is displayed during preview. Do not clear the
1368872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        // drawable.
1378872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mFaces = null;
1388872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        invalidate();
1398872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1408872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1418872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void pause() {
1428872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mPause = true;
1438872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1448872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1458872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void resume() {
1468872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mPause = false;
1478872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1488872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1498872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void setBlockDraw(boolean block) {
1508872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mBlocked = block;
1518872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1528872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1538872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    @Override
1548872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    protected void onDraw(Canvas canvas) {
1558872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (!mBlocked && (mFaces != null) && (mFaces.length > 0)) {
156753bb8aa56fff425fe16b93be368b9b236e4751fDoris Liu            int rw, rh;
15759401045bf479b2617c981075aec92190ec59aebDoris Liu            rw = (int) mPreviewArea.width();
15859401045bf479b2617c981075aec92190ec59aebDoris Liu            rh = (int) mPreviewArea.height();
1598872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            // Prepare the matrix.
1608872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if (((rh > rw) && ((mDisplayOrientation == 0) || (mDisplayOrientation == 180)))
1618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    || ((rw > rh) && ((mDisplayOrientation == 90) || (mDisplayOrientation == 270)))) {
1628872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                int temp = rw;
1638872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                rw = rh;
1648872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                rh = temp;
1658872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
166b50b5cbfbc0a67db6fc43373363b10381c9c61a3Angus Kong            CameraUtil.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, rw, rh);
1678872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            // Focus indicator is directional. Rotate the matrix and the canvas
1688872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            // so it looks correctly in all orientations.
1698872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            canvas.save();
1708872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mMatrix.postRotate(mOrientation); // postRotate is clockwise
1718872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas)
1728872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            for (int i = 0; i < mFaces.length; i++) {
1738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // Filter out false positives.
1748872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                if (mFaces[i].score < 50) continue;
1758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // Transform the coordinates.
1778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                mRect.set(mFaces[i].rect);
1782bca210e5fc8a77685775ffb403096167b017dceAngus Kong                if (LOGV) {
1792bca210e5fc8a77685775ffb403096167b017dceAngus Kong                    CameraUtil.dumpRect(mRect, "Original rect");
1802bca210e5fc8a77685775ffb403096167b017dceAngus Kong                }
1818872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                mMatrix.mapRect(mRect);
1822bca210e5fc8a77685775ffb403096167b017dceAngus Kong                if (LOGV) {
1832bca210e5fc8a77685775ffb403096167b017dceAngus Kong                    CameraUtil.dumpRect(mRect, "Transformed rect");
1842bca210e5fc8a77685775ffb403096167b017dceAngus Kong                }
1858872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                mPaint.setColor(mColor);
18659401045bf479b2617c981075aec92190ec59aebDoris Liu                mRect.offset(mPreviewArea.left, mPreviewArea.top);
18762b4c3c0da3abd397b3790820359927784c50bc2Andy Huibers                canvas.drawRect(mRect, mPaint);
1888872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
1898872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            canvas.restore();
1908872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
1918872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        super.onDraw(canvas);
1928872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
19359401045bf479b2617c981075aec92190ec59aebDoris Liu
19459401045bf479b2617c981075aec92190ec59aebDoris Liu    @Override
1952bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong    public void onPreviewAreaChanged(RectF previewArea) {
19659401045bf479b2617c981075aec92190ec59aebDoris Liu        mPreviewArea.set(previewArea);
19759401045bf479b2617c981075aec92190ec59aebDoris Liu    }
1988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb}
199