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