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