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