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.R;
     37 import com.android.camera.Util;
     38 import com.android.gallery3d.common.ApiHelper;
     39 
     40 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
     41 public class FaceView extends View implements FocusIndicator, Rotatable {
     42     private static final String TAG = "CAM FaceView";
     43     private final boolean LOGV = false;
     44     // The value for android.hardware.Camera.setDisplayOrientation.
     45     private int mDisplayOrientation;
     46     // The orientation compensation for the face indicator to make it look
     47     // correctly in all device orientations. Ex: if the value is 90, the
     48     // indicator should be rotated 90 degrees counter-clockwise.
     49     private int mOrientation;
     50     private boolean mMirror;
     51     private boolean mPause;
     52     private Matrix mMatrix = new Matrix();
     53     private RectF mRect = new RectF();
     54     // As face detection can be flaky, we add a layer of filtering on top of it
     55     // to avoid rapid changes in state (eg, flickering between has faces and
     56     // not having faces)
     57     private Face[] mFaces;
     58     private Face[] mPendingFaces;
     59     private int mColor;
     60     private final int mFocusingColor;
     61     private final int mFocusedColor;
     62     private final int mFailColor;
     63     private Paint mPaint;
     64     private volatile boolean mBlocked;
     65 
     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     public void setFaces(Face[] faces) {
     96         if (LOGV) Log.v(TAG, "Num of faces=" + faces.length);
     97         if (mPause) return;
     98         if (mFaces != null) {
     99             if ((faces.length > 0 && mFaces.length == 0)
    100                     || (faces.length == 0 && mFaces.length > 0)) {
    101                 mPendingFaces = faces;
    102                 if (!mStateSwitchPending) {
    103                     mStateSwitchPending = true;
    104                     mHandler.sendEmptyMessageDelayed(MSG_SWITCH_FACES, SWITCH_DELAY);
    105                 }
    106                 return;
    107             }
    108         }
    109         if (mStateSwitchPending) {
    110             mStateSwitchPending = false;
    111             mHandler.removeMessages(MSG_SWITCH_FACES);
    112         }
    113         mFaces = faces;
    114         invalidate();
    115     }
    116 
    117     public void setDisplayOrientation(int orientation) {
    118         mDisplayOrientation = orientation;
    119         if (LOGV) Log.v(TAG, "mDisplayOrientation=" + orientation);
    120     }
    121 
    122     @Override
    123     public void setOrientation(int orientation, boolean animation) {
    124         mOrientation = orientation;
    125         invalidate();
    126     }
    127 
    128     public void setMirror(boolean mirror) {
    129         mMirror = mirror;
    130         if (LOGV) Log.v(TAG, "mMirror=" + mirror);
    131     }
    132 
    133     public boolean faceExists() {
    134         return (mFaces != null && mFaces.length > 0);
    135     }
    136 
    137     @Override
    138     public void showStart() {
    139         mColor = mFocusingColor;
    140         invalidate();
    141     }
    142 
    143     // Ignore the parameter. No autofocus animation for face detection.
    144     @Override
    145     public void showSuccess(boolean timeout) {
    146         mColor = mFocusedColor;
    147         invalidate();
    148     }
    149 
    150     // Ignore the parameter. No autofocus animation for face detection.
    151     @Override
    152     public void showFail(boolean timeout) {
    153         mColor = mFailColor;
    154         invalidate();
    155     }
    156 
    157     @Override
    158     public void clear() {
    159         // Face indicator is displayed during preview. Do not clear the
    160         // drawable.
    161         mColor = mFocusingColor;
    162         mFaces = null;
    163         invalidate();
    164     }
    165 
    166     public void pause() {
    167         mPause = true;
    168     }
    169 
    170     public void resume() {
    171         mPause = false;
    172     }
    173 
    174     public void setBlockDraw(boolean block) {
    175         mBlocked = block;
    176     }
    177 
    178     @Override
    179     protected void onDraw(Canvas canvas) {
    180         if (!mBlocked && (mFaces != null) && (mFaces.length > 0)) {
    181             final CameraScreenNail sn = ((CameraActivity) getContext()).getCameraScreenNail();
    182             int rw = sn.getUncroppedRenderWidth();
    183             int rh = sn.getUncroppedRenderHeight();
    184             // Prepare the matrix.
    185             if (((rh > rw) && ((mDisplayOrientation == 0) || (mDisplayOrientation == 180)))
    186                     || ((rw > rh) && ((mDisplayOrientation == 90) || (mDisplayOrientation == 270)))) {
    187                 int temp = rw;
    188                 rw = rh;
    189                 rh = temp;
    190             }
    191             Util.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, rw, rh);
    192             int dx = (getWidth() - rw) / 2;
    193             int dy = (getHeight() - rh) / 2;
    194 
    195             // Focus indicator is directional. Rotate the matrix and the canvas
    196             // so it looks correctly in all orientations.
    197             canvas.save();
    198             mMatrix.postRotate(mOrientation); // postRotate is clockwise
    199             canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas)
    200             for (int i = 0; i < mFaces.length; i++) {
    201                 // Filter out false positives.
    202                 if (mFaces[i].score < 50) continue;
    203 
    204                 // Transform the coordinates.
    205                 mRect.set(mFaces[i].rect);
    206                 if (LOGV) Util.dumpRect(mRect, "Original rect");
    207                 mMatrix.mapRect(mRect);
    208                 if (LOGV) Util.dumpRect(mRect, "Transformed rect");
    209                 mPaint.setColor(mColor);
    210                 mRect.offset(dx, dy);
    211                 canvas.drawOval(mRect, mPaint);
    212             }
    213             canvas.restore();
    214         }
    215         super.onDraw(canvas);
    216     }
    217 }
    218