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