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