Home | History | Annotate | Download | only in ui
      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 
     17 package com.android.camera.ui;
     18 
     19 import android.content.Context;
     20 import android.content.res.Resources;
     21 import android.graphics.Canvas;
     22 import android.graphics.Matrix;
     23 import android.graphics.Paint;
     24 import android.graphics.Paint.Style;
     25 import android.graphics.RectF;
     26 import android.hardware.Camera.Face;
     27 import android.os.Handler;
     28 import android.os.Message;
     29 import android.util.AttributeSet;
     30 import android.view.View;
     31 
     32 import com.android.camera.debug.Log;
     33 import com.android.camera.util.CameraUtil;
     34 import com.android.camera2.R;
     35 
     36 public class FaceView extends View
     37     implements FocusIndicator, Rotatable, PreviewStatusListener.PreviewAreaChangedListener {
     38     private static final Log.Tag TAG = new Log.Tag("FaceView");
     39     private final boolean LOGV = false;
     40     // The value for android.hardware.Camera.setDisplayOrientation.
     41     private int mDisplayOrientation;
     42     // The orientation compensation for the face indicator to make it look
     43     // correctly in all device orientations. Ex: if the value is 90, the
     44     // indicator should be rotated 90 degrees counter-clockwise.
     45     private int mOrientation;
     46     private boolean mMirror;
     47     private boolean mPause;
     48     private Matrix mMatrix = new Matrix();
     49     private RectF mRect = new RectF();
     50     // As face detection can be flaky, we add a layer of filtering on top of it
     51     // to avoid rapid changes in state (eg, flickering between has faces and
     52     // not having faces)
     53     private Face[] mFaces;
     54     private Face[] mPendingFaces;
     55     private int mColor;
     56     private Paint mPaint;
     57     private volatile boolean mBlocked;
     58 
     59     private static final int MSG_SWITCH_FACES = 1;
     60     private static final int SWITCH_DELAY = 70;
     61     private boolean mStateSwitchPending = false;
     62     private Handler mHandler = new Handler() {
     63         @Override
     64         public void handleMessage(Message msg) {
     65             switch (msg.what) {
     66             case MSG_SWITCH_FACES:
     67                 mStateSwitchPending = false;
     68                 mFaces = mPendingFaces;
     69                 invalidate();
     70                 break;
     71             }
     72         }
     73     };
     74     private final RectF mPreviewArea = new RectF();
     75 
     76     public FaceView(Context context, AttributeSet attrs) {
     77         super(context, attrs);
     78         Resources res = getResources();
     79         mColor = res.getColor(R.color.face_detect_start);
     80         mPaint = new Paint();
     81         mPaint.setAntiAlias(true);
     82         mPaint.setStyle(Style.STROKE);
     83         mPaint.setStrokeWidth(res.getDimension(R.dimen.face_circle_stroke));
     84     }
     85 
     86     public void setFaces(Face[] faces) {
     87         if (LOGV) {
     88             Log.v(TAG, "Num of faces=" + faces.length);
     89         }
     90         if (mPause) return;
     91         if (mFaces != null) {
     92             if ((faces.length > 0 && mFaces.length == 0)
     93                     || (faces.length == 0 && mFaces.length > 0)) {
     94                 mPendingFaces = faces;
     95                 if (!mStateSwitchPending) {
     96                     mStateSwitchPending = true;
     97                     mHandler.sendEmptyMessageDelayed(MSG_SWITCH_FACES, SWITCH_DELAY);
     98                 }
     99                 return;
    100             }
    101         }
    102         if (mStateSwitchPending) {
    103             mStateSwitchPending = false;
    104             mHandler.removeMessages(MSG_SWITCH_FACES);
    105         }
    106         mFaces = faces;
    107         invalidate();
    108     }
    109 
    110     public void setDisplayOrientation(int orientation) {
    111         mDisplayOrientation = orientation;
    112         if (LOGV) {
    113             Log.v(TAG, "mDisplayOrientation=" + orientation);
    114         }
    115     }
    116 
    117     @Override
    118     public void setOrientation(int orientation, boolean animation) {
    119         mOrientation = orientation;
    120         invalidate();
    121     }
    122 
    123     public void setMirror(boolean mirror) {
    124         mMirror = mirror;
    125         if (LOGV) {
    126             Log.v(TAG, "mMirror=" + mirror);
    127         }
    128     }
    129 
    130     public boolean faceExists() {
    131         return (mFaces != null && mFaces.length > 0);
    132     }
    133 
    134     @Override
    135     public void showStart() {
    136         invalidate();
    137     }
    138 
    139     // Ignore the parameter. No autofocus animation for face detection.
    140     @Override
    141     public void showSuccess(boolean timeout) {
    142         invalidate();
    143     }
    144 
    145     // Ignore the parameter. No autofocus animation for face detection.
    146     @Override
    147     public void showFail(boolean timeout) {
    148         invalidate();
    149     }
    150 
    151     @Override
    152     public void clear() {
    153         // Face indicator is displayed during preview. Do not clear the
    154         // drawable.
    155         mFaces = null;
    156         invalidate();
    157     }
    158 
    159     public void pause() {
    160         mPause = true;
    161     }
    162 
    163     public void resume() {
    164         mPause = false;
    165     }
    166 
    167     public void setBlockDraw(boolean block) {
    168         mBlocked = block;
    169     }
    170 
    171     @Override
    172     protected void onDraw(Canvas canvas) {
    173         if (!mBlocked && (mFaces != null) && (mFaces.length > 0)) {
    174             int rw, rh;
    175             rw = (int) mPreviewArea.width();
    176             rh = (int) mPreviewArea.height();
    177             // Prepare the matrix.
    178             if (((rh > rw) && ((mDisplayOrientation == 0) || (mDisplayOrientation == 180)))
    179                     || ((rw > rh) && ((mDisplayOrientation == 90) || (mDisplayOrientation == 270)))) {
    180                 int temp = rw;
    181                 rw = rh;
    182                 rh = temp;
    183             }
    184             CameraUtil.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, rw, rh);
    185             // Focus indicator is directional. Rotate the matrix and the canvas
    186             // so it looks correctly in all orientations.
    187             canvas.save();
    188             mMatrix.postRotate(mOrientation); // postRotate is clockwise
    189             canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas)
    190             for (int i = 0; i < mFaces.length; i++) {
    191                 // Filter out false positives.
    192                 if (mFaces[i].score < 50) continue;
    193 
    194                 // Transform the coordinates.
    195                 mRect.set(mFaces[i].rect);
    196                 if (LOGV) {
    197                     CameraUtil.dumpRect(mRect, "Original rect");
    198                 }
    199                 mMatrix.mapRect(mRect);
    200                 if (LOGV) {
    201                     CameraUtil.dumpRect(mRect, "Transformed rect");
    202                 }
    203                 mPaint.setColor(mColor);
    204                 mRect.offset(mPreviewArea.left, mPreviewArea.top);
    205                 canvas.drawRect(mRect, mPaint);
    206             }
    207             canvas.restore();
    208         }
    209         super.onDraw(canvas);
    210     }
    211 
    212     @Override
    213     public void onPreviewAreaChanged(RectF previewArea) {
    214         mPreviewArea.set(previewArea);
    215     }
    216 }
    217