FaceView.java revision 8dbb2a65ef1b0404c169e8fada6c0d0f154dd08f
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.camera.ui; 18 19import android.annotation.TargetApi; 20import android.content.Context; 21import android.content.res.Resources; 22import android.graphics.Canvas; 23import android.graphics.Color; 24import android.graphics.Matrix; 25import android.graphics.Paint; 26import android.graphics.Paint.Style; 27import android.graphics.RectF; 28import android.hardware.Camera.Face; 29import android.os.Handler; 30import android.os.Message; 31import android.util.AttributeSet; 32import android.util.Log; 33import android.view.View; 34 35import com.android.camera.R; 36import com.android.camera.Util; 37import com.android.gallery3d.common.ApiHelper; 38 39@TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) 40public class FaceView extends View implements FocusIndicator, Rotatable { 41 private static final String TAG = "FaceView"; 42 private final boolean LOGV = false; 43 // The value for android.hardware.Camera.setDisplayOrientation. 44 private int mDisplayOrientation; 45 // The orientation compensation for the face indicator to make it look 46 // correctly in all device orientations. Ex: if the value is 90, the 47 // indicator should be rotated 90 degrees counter-clockwise. 48 private int mOrientation; 49 private boolean mMirror; 50 private boolean mPause; 51 private Matrix mMatrix = new Matrix(); 52 private RectF mRect = new RectF(); 53 // As face detection can be flaky, we add a layer of filtering on top of it 54 // to avoid rapid changes in state (eg, flickering between has faces and 55 // not having faces) 56 private Face[] mFaces; 57 private Face[] mPendingFaces; 58 private int mColor; 59 private final int mFocusingColor; 60 private final int mFocusedColor; 61 private final int mFailColor; 62 private Paint mPaint; 63 private volatile boolean mBlocked; 64 65 private static final int MSG_SWITCH_FACES = 1; 66 private static final int SWITCH_DELAY = 70; 67 private boolean mStateSwitchPending = false; 68 private Handler mHandler = new Handler() { 69 @Override 70 public void handleMessage(Message msg) { 71 switch (msg.what) { 72 case MSG_SWITCH_FACES: 73 mStateSwitchPending = false; 74 mFaces = mPendingFaces; 75 invalidate(); 76 break; 77 } 78 } 79 }; 80 81 public FaceView(Context context, AttributeSet attrs) { 82 super(context, attrs); 83 Resources res = getResources(); 84 mFocusingColor = res.getColor(R.color.face_detect_start); 85 mFocusedColor = res.getColor(R.color.face_detect_success); 86 mFailColor = res.getColor(R.color.face_detect_fail); 87 mColor = mFocusingColor; 88 mPaint = new Paint(); 89 mPaint.setAntiAlias(true); 90 mPaint.setStyle(Style.STROKE); 91 mPaint.setStrokeWidth(res.getDimension(R.dimen.focus_outer_stroke)); 92 } 93 94 public void setFaces(Face[] faces) { 95 if (LOGV) Log.v(TAG, "Num of faces=" + faces.length); 96 if (mPause) return; 97 if (mFaces != null) { 98 if ((faces.length > 0 && mFaces.length == 0) 99 || (faces.length == 0 && mFaces.length > 0)) { 100 mPendingFaces = faces; 101 if (!mStateSwitchPending) { 102 mStateSwitchPending = true; 103 mHandler.sendEmptyMessageDelayed(MSG_SWITCH_FACES, SWITCH_DELAY); 104 } 105 return; 106 } 107 } 108 if (mStateSwitchPending) { 109 mStateSwitchPending = false; 110 mHandler.removeMessages(MSG_SWITCH_FACES); 111 } 112 mFaces = faces; 113 invalidate(); 114 } 115 116 public void setDisplayOrientation(int orientation) { 117 mDisplayOrientation = orientation; 118 if (LOGV) Log.v(TAG, "mDisplayOrientation=" + orientation); 119 } 120 121 @Override 122 public void setOrientation(int orientation, boolean animation) { 123 mOrientation = orientation; 124 invalidate(); 125 } 126 127 public void setMirror(boolean mirror) { 128 mMirror = mirror; 129 if (LOGV) Log.v(TAG, "mMirror=" + mirror); 130 } 131 132 public boolean faceExists() { 133 return (mFaces != null && mFaces.length > 0); 134 } 135 136 @Override 137 public void showStart() { 138 mColor = mFocusingColor; 139 invalidate(); 140 } 141 142 // Ignore the parameter. No autofocus animation for face detection. 143 @Override 144 public void showSuccess(boolean timeout) { 145 mColor = mFocusedColor; 146 invalidate(); 147 } 148 149 // Ignore the parameter. No autofocus animation for face detection. 150 @Override 151 public void showFail(boolean timeout) { 152 mColor = mFailColor; 153 invalidate(); 154 } 155 156 @Override 157 public void clear() { 158 // Face indicator is displayed during preview. Do not clear the 159 // drawable. 160 mColor = mFocusingColor; 161 mFaces = null; 162 invalidate(); 163 } 164 165 public void pause() { 166 mPause = true; 167 } 168 169 public void resume() { 170 mPause = false; 171 } 172 173 public void setBlockDraw(boolean block) { 174 mBlocked = block; 175 } 176 177 @Override 178 protected void onDraw(Canvas canvas) { 179 if (!mBlocked && (mFaces != null) && (mFaces.length > 0)) { 180 // Prepare the matrix. 181 Util.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, getWidth(), getHeight()); 182 183 // Focus indicator is directional. Rotate the matrix and the canvas 184 // so it looks correctly in all orientations. 185 canvas.save(); 186 mMatrix.postRotate(mOrientation); // postRotate is clockwise 187 canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas) 188 for (int i = 0; i < mFaces.length; i++) { 189 // Filter out false positives. 190 if (mFaces[i].score < 50) continue; 191 192 // Transform the coordinates. 193 mRect.set(mFaces[i].rect); 194 if (LOGV) Util.dumpRect(mRect, "Original rect"); 195 mMatrix.mapRect(mRect); 196 if (LOGV) Util.dumpRect(mRect, "Transformed rect"); 197 mPaint.setColor(mColor); 198 canvas.drawOval(mRect, mPaint); 199 } 200 canvas.restore(); 201 } 202 super.onDraw(canvas); 203 } 204} 205