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 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     public void clear() {
    135         // Face indicator is displayed during preview. Do not clear the
    136         // drawable.
    137         mFaces = null;
    138         invalidate();
    139     }
    140 
    141     public void pause() {
    142         mPause = true;
    143     }
    144 
    145     public void resume() {
    146         mPause = false;
    147     }
    148 
    149     public void setBlockDraw(boolean block) {
    150         mBlocked = block;
    151     }
    152 
    153     @Override
    154     protected void onDraw(Canvas canvas) {
    155         if (!mBlocked && (mFaces != null) && (mFaces.length > 0)) {
    156             int rw, rh;
    157             rw = (int) mPreviewArea.width();
    158             rh = (int) mPreviewArea.height();
    159             // Prepare the matrix.
    160             if (((rh > rw) && ((mDisplayOrientation == 0) || (mDisplayOrientation == 180)))
    161                     || ((rw > rh) && ((mDisplayOrientation == 90) || (mDisplayOrientation == 270)))) {
    162                 int temp = rw;
    163                 rw = rh;
    164                 rh = temp;
    165             }
    166             CameraUtil.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, rw, rh);
    167             // Focus indicator is directional. Rotate the matrix and the canvas
    168             // so it looks correctly in all orientations.
    169             canvas.save();
    170             mMatrix.postRotate(mOrientation); // postRotate is clockwise
    171             canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas)
    172             for (int i = 0; i < mFaces.length; i++) {
    173                 // Filter out false positives.
    174                 if (mFaces[i].score < 50) continue;
    175 
    176                 // Transform the coordinates.
    177                 mRect.set(mFaces[i].rect);
    178                 if (LOGV) {
    179                     CameraUtil.dumpRect(mRect, "Original rect");
    180                 }
    181                 mMatrix.mapRect(mRect);
    182                 if (LOGV) {
    183                     CameraUtil.dumpRect(mRect, "Transformed rect");
    184                 }
    185                 mPaint.setColor(mColor);
    186                 mRect.offset(mPreviewArea.left, mPreviewArea.top);
    187                 canvas.drawRect(mRect, mPaint);
    188             }
    189             canvas.restore();
    190         }
    191         super.onDraw(canvas);
    192     }
    193 
    194     @Override
    195     public void onPreviewAreaChanged(RectF previewArea) {
    196         mPreviewArea.set(previewArea);
    197     }
    198 }
    199