1 /* 2 * Copyright (C) 2013 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.animation.ValueAnimator; 20 import android.content.Context; 21 import android.content.res.Resources; 22 import android.graphics.Canvas; 23 import android.graphics.Paint; 24 import android.graphics.Rect; 25 import android.graphics.drawable.Drawable; 26 import android.util.AttributeSet; 27 import android.view.View; 28 29 import com.android.camera.FocusOverlayManager; 30 import com.android.camera.debug.DebugPropertyHelper; 31 import com.android.camera.debug.Log; 32 import com.android.camera2.R; 33 34 /** 35 * Displays a focus indicator. 36 */ 37 public class FocusOverlay extends View implements FocusOverlayManager.FocusUI { 38 private static final Log.Tag TAG = new Log.Tag("FocusOverlay"); 39 40 /** System Properties switch to enable debugging focus UI. */ 41 private static final boolean CAPTURE_DEBUG_UI = DebugPropertyHelper.showCaptureDebugUI(); 42 43 private final static int FOCUS_DURATION_MS = 500; 44 private final static int FOCUS_INDICATOR_ROTATION_DEGREES = 50; 45 46 private final Drawable mFocusIndicator; 47 private Drawable mFocusOuterRing; 48 private final Rect mBounds = new Rect(); 49 private final ValueAnimator mFocusAnimation = new ValueAnimator(); 50 51 private Paint mDebugSolidPaint; 52 private Paint mDebugCornersPaint; 53 private Paint mDebugTextPaint; 54 private int mDebugStartColor; 55 private int mDebugSuccessColor; 56 private int mDebugFailColor; 57 private Rect mFocusDebugSolidRect; 58 private Rect mFocusDebugCornersRect; 59 private boolean mIsPassiveScan; 60 private String mDebugMessage; 61 62 private int mPositionX; 63 private int mPositionY; 64 private int mAngle; 65 private final int mFocusIndicatorSize; 66 private boolean mShowIndicator; 67 private final int mFocusOuterRingSize; 68 69 public FocusOverlay(Context context, AttributeSet attrs) { 70 super(context, attrs); 71 mFocusIndicator = getResources().getDrawable(R.drawable.focus_ring_touch_inner); 72 mFocusIndicatorSize = getResources().getDimensionPixelSize(R.dimen.focus_inner_ring_size); 73 mFocusOuterRing = getResources().getDrawable(R.drawable.focus_ring_touch_outer); 74 mFocusOuterRingSize = getResources().getDimensionPixelSize(R.dimen.focus_outer_ring_size); 75 76 if (CAPTURE_DEBUG_UI) { 77 Resources res = getResources(); 78 mDebugStartColor = res.getColor(R.color.focus_debug); 79 mDebugSuccessColor = res.getColor(R.color.focus_debug_success); 80 mDebugFailColor = res.getColor(R.color.focus_debug_fail); 81 mDebugTextPaint= new Paint(); 82 mDebugTextPaint.setColor(res.getColor(R.color.focus_debug_text)); 83 mDebugTextPaint.setStyle(Paint.Style.FILL); 84 mDebugSolidPaint = new Paint(); 85 mDebugSolidPaint.setColor(res.getColor(R.color.focus_debug)); 86 mDebugSolidPaint.setAntiAlias(true); 87 mDebugSolidPaint.setStyle(Paint.Style.STROKE); 88 mDebugSolidPaint.setStrokeWidth(res.getDimension(R.dimen.focus_debug_stroke)); 89 mDebugCornersPaint = new Paint(mDebugSolidPaint); 90 mDebugCornersPaint.setColor(res.getColor(R.color.focus_debug)); 91 mFocusDebugSolidRect = new Rect(); 92 mFocusDebugCornersRect = new Rect(); 93 } 94 } 95 96 @Override 97 public boolean hasFaces() { 98 // TODO: Add face detection support. 99 return false; 100 } 101 102 @Override 103 public void clearFocus() { 104 mShowIndicator = false; 105 if (CAPTURE_DEBUG_UI) { 106 setVisibility(INVISIBLE); 107 } 108 } 109 110 @Override 111 public void setFocusPosition(int x, int y, boolean isPassiveScan) { 112 setFocusPosition(x, y, isPassiveScan, 0, 0); 113 } 114 115 @Override 116 public void setFocusPosition(int x, int y, boolean isPassiveScan, int aFsize, int aEsize) { 117 mIsPassiveScan = isPassiveScan; 118 mPositionX = x; 119 mPositionY = y; 120 mBounds.set(x - mFocusIndicatorSize / 2, y - mFocusIndicatorSize / 2, 121 x + mFocusIndicatorSize / 2, y + mFocusIndicatorSize / 2); 122 mFocusIndicator.setBounds(mBounds); 123 mFocusOuterRing.setBounds(x - mFocusOuterRingSize / 2, y - mFocusOuterRingSize / 2, 124 x + mFocusOuterRingSize / 2, y + mFocusOuterRingSize / 2); 125 126 if (CAPTURE_DEBUG_UI) { 127 mFocusOuterRing.setBounds(0, 0, 0, 0); 128 if (isPassiveScan) { 129 // Use AE rect only. 130 mFocusDebugSolidRect.setEmpty(); 131 int avg = (aFsize + aEsize) / 2; 132 mFocusDebugCornersRect.set(x - avg / 2, y - avg / 2, x + avg / 2, y + avg / 2); 133 } else { 134 mFocusDebugSolidRect.set(x - aFsize / 2, y - aFsize / 2, x + aFsize / 2, 135 y + aFsize / 2); 136 // If AE region is different size than AF region and active scan. 137 if (aFsize != aEsize) { 138 mFocusDebugCornersRect.set(x - aEsize / 2, y - aEsize / 2, x + aEsize / 2, 139 y + aEsize / 2); 140 } else { 141 mFocusDebugCornersRect.setEmpty(); 142 } 143 } 144 mDebugSolidPaint.setColor(mDebugStartColor); 145 mDebugCornersPaint.setColor(mDebugStartColor); 146 } 147 148 if (getVisibility() != VISIBLE) { 149 setVisibility(VISIBLE); 150 } 151 invalidate(); 152 } 153 154 /** 155 * This is called in: 156 * <ul> 157 * <li>API1 non-CAF after autoFocus().</li> 158 * <li>API1 CAF mode for onAutoFocusMoving(true).</li> 159 * <li>API2 for transition to ACTIVE_SCANNING or PASSIVE_SCANNING.</li> 160 * <ul> 161 * TODO after PhotoModule/GcamModule deprecation: Do not use this for CAF. 162 */ 163 @Override 164 public void onFocusStarted() { 165 mShowIndicator = true; 166 mFocusAnimation.setIntValues(0, FOCUS_INDICATOR_ROTATION_DEGREES); 167 mFocusAnimation.setDuration(FOCUS_DURATION_MS); 168 mFocusAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 169 @Override 170 public void onAnimationUpdate(ValueAnimator animation) { 171 mAngle = (Integer) animation.getAnimatedValue(); 172 invalidate(); 173 } 174 }); 175 mFocusAnimation.start(); 176 if (CAPTURE_DEBUG_UI) { 177 mDebugMessage = null; 178 } 179 } 180 181 /** 182 * This is called in: 183 * <ul> 184 * <li>API1 non-CAF for onAutoFocus(true).</li> 185 * <li>API2 non-CAF for transition to FOCUSED_LOCKED.</li> 186 * <li>API1 CAF mode for onAutoFocusMoving(false).</li> 187 * <ul> 188 * TODO after PhotoModule/GcamModule deprecation: Do not use this for CAF. 189 */ 190 @Override 191 public void onFocusSucceeded() { 192 mFocusAnimation.cancel(); 193 mShowIndicator = false; 194 if (CAPTURE_DEBUG_UI && !mIsPassiveScan) { 195 mDebugSolidPaint.setColor(mDebugSuccessColor); 196 } 197 invalidate(); 198 } 199 200 /** 201 * This is called in: 202 * <ul> 203 * <li>API1 non-CAF for onAutoFocus(false).</li> 204 * <li>API2 non-CAF for transition to NOT_FOCUSED_LOCKED.</li> 205 * <ul> 206 */ 207 @Override 208 public void onFocusFailed() { 209 mFocusAnimation.cancel(); 210 mShowIndicator = false; 211 if (CAPTURE_DEBUG_UI && !mIsPassiveScan) { 212 mDebugSolidPaint.setColor(mDebugFailColor); 213 } 214 invalidate(); 215 } 216 217 /** 218 * This is called in: 219 * API2 for CAF state changes to PASSIVE_FOCUSED or PASSIVE_UNFOCUSED. 220 */ 221 @Override 222 public void setPassiveFocusSuccess(boolean success) { 223 mFocusAnimation.cancel(); 224 mShowIndicator = false; 225 if (CAPTURE_DEBUG_UI) { 226 mDebugCornersPaint.setColor(success ? mDebugSuccessColor : mDebugFailColor); 227 } 228 invalidate(); 229 } 230 231 @Override 232 public void showDebugMessage(String message) { 233 if (CAPTURE_DEBUG_UI) { 234 mDebugMessage = message; 235 } 236 } 237 238 @Override 239 public void pauseFaceDetection() { 240 // TODO: Add face detection support. 241 } 242 243 @Override 244 public void resumeFaceDetection() { 245 // TODO: Add face detection support. 246 } 247 248 @Override 249 public void onDraw(Canvas canvas) { 250 super.onDraw(canvas); 251 252 if (mShowIndicator) { 253 mFocusOuterRing.draw(canvas); 254 canvas.save(); 255 canvas.rotate(mAngle, mPositionX, mPositionY); 256 mFocusIndicator.draw(canvas); 257 canvas.restore(); 258 } 259 if (CAPTURE_DEBUG_UI) { 260 canvas.drawRect(mFocusDebugSolidRect, mDebugSolidPaint); 261 float delta = 0.1f * mFocusDebugCornersRect.width(); 262 float left = mFocusDebugCornersRect.left; 263 float top = mFocusDebugCornersRect.top; 264 float right = mFocusDebugCornersRect.right; 265 float bot = mFocusDebugCornersRect.bottom; 266 267 canvas.drawLines(new float[]{left, top + delta, left, top, left, top, left + delta, top}, mDebugCornersPaint); 268 canvas.drawLines(new float[]{right, top + delta, right, top, right, top, right - delta, top}, mDebugCornersPaint); 269 canvas.drawLines(new float[]{left, bot - delta, left, bot, left, bot, left + delta, bot}, mDebugCornersPaint); 270 canvas.drawLines(new float[]{right, bot - delta, right, bot, right, bot, right - delta, bot}, mDebugCornersPaint); 271 272 if (mDebugMessage != null) { 273 mDebugTextPaint.setTextSize(40); 274 canvas.drawText(mDebugMessage, left - 4, bot + 44, mDebugTextPaint); 275 } 276 } 277 } 278 } 279