FaceView.java revision 630d255bdeeb1a13a71b0ecda0a38d6546c351e2
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 64 private static final int MSG_SWITCH_FACES = 1; 65 private static final int SWITCH_DELAY = 70; 66 private boolean mStateSwitchPending = false; 67 private Handler mHandler = new Handler() { 68 @Override 69 public void handleMessage(Message msg) { 70 switch (msg.what) { 71 case MSG_SWITCH_FACES: 72 mStateSwitchPending = false; 73 mFaces = mPendingFaces; 74 invalidate(); 75 break; 76 } 77 } 78 }; 79 80 public FaceView(Context context, AttributeSet attrs) { 81 super(context, attrs); 82 Resources res = getResources(); 83 mFocusingColor = res.getColor(R.color.face_detect_start); 84 mFocusedColor = res.getColor(R.color.face_detect_success); 85 mFailColor = res.getColor(R.color.face_detect_fail); 86 mColor = mFocusingColor; 87 mPaint = new Paint(); 88 mPaint.setAntiAlias(true); 89 mPaint.setStyle(Style.STROKE); 90 mPaint.setStrokeWidth(res.getDimension(R.dimen.focus_outer_stroke)); 91 } 92 93 public void setFaces(Face[] faces) { 94 if (LOGV) Log.v(TAG, "Num of faces=" + faces.length); 95 if (mPause) return; 96 if (mFaces != null) { 97 if ((faces.length > 0 && mFaces.length == 0) 98 || (faces.length == 0 && mFaces.length > 0)) { 99 mPendingFaces = faces; 100 if (!mStateSwitchPending) { 101 mStateSwitchPending = true; 102 mHandler.sendEmptyMessageDelayed(MSG_SWITCH_FACES, SWITCH_DELAY); 103 } 104 return; 105 } 106 } 107 if (mStateSwitchPending) { 108 mStateSwitchPending = false; 109 mHandler.removeMessages(MSG_SWITCH_FACES); 110 } 111 mFaces = faces; 112 invalidate(); 113 } 114 115 public void setDisplayOrientation(int orientation) { 116 mDisplayOrientation = orientation; 117 if (LOGV) Log.v(TAG, "mDisplayOrientation=" + orientation); 118 } 119 120 @Override 121 public void setOrientation(int orientation, boolean animation) { 122 mOrientation = orientation; 123 invalidate(); 124 } 125 126 public void setMirror(boolean mirror) { 127 mMirror = mirror; 128 if (LOGV) Log.v(TAG, "mMirror=" + mirror); 129 } 130 131 public boolean faceExists() { 132 return (mFaces != null && mFaces.length > 0); 133 } 134 135 @Override 136 public void showStart() { 137 mColor = mFocusingColor; 138 invalidate(); 139 } 140 141 // Ignore the parameter. No autofocus animation for face detection. 142 @Override 143 public void showSuccess(boolean timeout) { 144 mColor = mFocusedColor; 145 invalidate(); 146 } 147 148 // Ignore the parameter. No autofocus animation for face detection. 149 @Override 150 public void showFail(boolean timeout) { 151 mColor = mFailColor; 152 invalidate(); 153 } 154 155 @Override 156 public void clear() { 157 // Face indicator is displayed during preview. Do not clear the 158 // drawable. 159 mColor = mFocusingColor; 160 mFaces = null; 161 invalidate(); 162 } 163 164 public void pause() { 165 mPause = true; 166 } 167 168 public void resume() { 169 mPause = false; 170 } 171 172 @Override 173 protected void onDraw(Canvas canvas) { 174 if (mFaces != null && mFaces.length > 0) { 175 // Prepare the matrix. 176 Util.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, getWidth(), getHeight()); 177 178 // Focus indicator is directional. Rotate the matrix and the canvas 179 // so it looks correctly in all orientations. 180 canvas.save(); 181 mMatrix.postRotate(mOrientation); // postRotate is clockwise 182 canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas) 183 for (int i = 0; i < mFaces.length; i++) { 184 // Filter out false positives. 185 if (mFaces[i].score < 50) continue; 186 187 // Transform the coordinates. 188 mRect.set(mFaces[i].rect); 189 if (LOGV) Util.dumpRect(mRect, "Original rect"); 190 mMatrix.mapRect(mRect); 191 if (LOGV) Util.dumpRect(mRect, "Transformed rect"); 192 mPaint.setColor(mColor); 193 canvas.drawOval(mRect, mPaint); 194 } 195 canvas.restore(); 196 } 197 super.onDraw(canvas); 198 } 199} 200