1 /* 2 * Copyright (C) 2014 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.focus; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.graphics.Canvas; 22 import android.graphics.Paint; 23 import android.graphics.Point; 24 import android.graphics.RectF; 25 import android.graphics.Region; 26 import android.util.AttributeSet; 27 import android.view.View; 28 29 import com.android.camera.debug.Log; 30 import com.android.camera.debug.Log.Tag; 31 import com.android.camera.ui.motion.AnimationClock.SystemTimeClock; 32 import com.android.camera.ui.motion.DynamicAnimator; 33 import com.android.camera.ui.motion.Invalidator; 34 import com.android.camera.ui.motion.LinearScale; 35 import com.android.camera2.R; 36 37 import javax.annotation.Nullable; 38 39 /** 40 * Custom view for running the focus ring animations. 41 */ 42 public class FocusRingView extends View implements Invalidator, FocusRing { 43 private static final Tag TAG = new Tag("FocusRingView"); 44 private static final float FADE_IN_DURATION_MILLIS = 1000f; 45 private static final float FADE_OUT_DURATION_MILLIS = 250f; 46 47 private final AutoFocusRing mAutoFocusRing; 48 private final ManualFocusRing mManualFocusRing; 49 private final DynamicAnimator mAnimator; 50 private final LinearScale mRatioScale; 51 private final float mDefaultRadiusPx; 52 53 private FocusRingRenderer currentFocusAnimation; 54 private boolean isFirstDraw; 55 private float mLastRadiusPx; 56 57 @Nullable 58 private RectF mPreviewSize; 59 60 public FocusRingView(Context context, AttributeSet attrs) { 61 super(context, attrs); 62 63 Resources res = getResources(); 64 Paint paint = makePaint(res, R.color.focus_color); 65 66 float focusCircleMinSize = res.getDimensionPixelSize(R.dimen.focus_circle_min_size); 67 float focusCircleMaxSize = res.getDimensionPixelSize(R.dimen.focus_circle_max_size); 68 mDefaultRadiusPx = res.getDimensionPixelSize(R.dimen.focus_circle_initial_size); 69 70 mRatioScale = new LinearScale(0, 1, focusCircleMinSize, focusCircleMaxSize); 71 mAnimator = new DynamicAnimator(this, new SystemTimeClock()); 72 73 mAutoFocusRing = new AutoFocusRing(mAnimator, paint, 74 FADE_IN_DURATION_MILLIS, 75 FADE_OUT_DURATION_MILLIS); 76 mManualFocusRing = new ManualFocusRing(mAnimator, paint, 77 FADE_OUT_DURATION_MILLIS); 78 79 mAnimator.animations.add(mAutoFocusRing); 80 mAnimator.animations.add(mManualFocusRing); 81 82 isFirstDraw = true; 83 mLastRadiusPx = mDefaultRadiusPx; 84 } 85 86 @Override 87 public boolean isPassiveFocusRunning() { 88 return mAutoFocusRing.isActive(); 89 } 90 91 @Override 92 public boolean isActiveFocusRunning() { 93 return mManualFocusRing.isActive(); 94 } 95 96 @Override 97 public void startPassiveFocus() { 98 mAnimator.invalidate(); 99 long tMs = mAnimator.getTimeMillis(); 100 101 if (mManualFocusRing.isActive() && !mManualFocusRing.isExiting()) { 102 mManualFocusRing.stop(tMs); 103 } 104 105 mAutoFocusRing.start(tMs, mLastRadiusPx, mLastRadiusPx); 106 currentFocusAnimation = mAutoFocusRing; 107 } 108 109 @Override 110 public void startActiveFocus() { 111 mAnimator.invalidate(); 112 long tMs = mAnimator.getTimeMillis(); 113 114 if (mAutoFocusRing.isActive() && !mAutoFocusRing.isExiting()) { 115 mAutoFocusRing.stop(tMs); 116 } 117 118 mManualFocusRing.start(tMs, 0.0f, mLastRadiusPx); 119 currentFocusAnimation = mManualFocusRing; 120 } 121 122 @Override 123 public void stopFocusAnimations() { 124 long tMs = mAnimator.getTimeMillis(); 125 if (mManualFocusRing.isActive() && !mManualFocusRing.isExiting() 126 && !mManualFocusRing.isEntering()) { 127 mManualFocusRing.exit(tMs); 128 } 129 130 if (mAutoFocusRing.isActive() && !mAutoFocusRing.isExiting()) { 131 mAutoFocusRing.exit(tMs); 132 } 133 } 134 135 @Override 136 public void setFocusLocation(float viewX, float viewY) { 137 mAutoFocusRing.setCenterX((int) viewX); 138 mAutoFocusRing.setCenterY((int) viewY); 139 mManualFocusRing.setCenterX((int) viewX); 140 mManualFocusRing.setCenterY((int) viewY); 141 } 142 143 @Override 144 public void centerFocusLocation() { 145 Point center = computeCenter(); 146 mAutoFocusRing.setCenterX(center.x); 147 mAutoFocusRing.setCenterY(center.y); 148 mManualFocusRing.setCenterX(center.x); 149 mManualFocusRing.setCenterY(center.y); 150 } 151 152 @Override 153 public void setRadiusRatio(float ratio) { 154 setRadius(mRatioScale.scale(mRatioScale.clamp(ratio))); 155 } 156 157 @Override 158 public void configurePreviewDimensions(RectF previewArea) { 159 mPreviewSize = previewArea; 160 mLastRadiusPx = mDefaultRadiusPx; 161 162 if (!isFirstDraw) { 163 centerAutofocusRing(); 164 } 165 } 166 167 @Override 168 protected void onDraw(Canvas canvas) { 169 if (isFirstDraw) { 170 isFirstDraw = false; 171 centerAutofocusRing(); 172 } 173 174 if (mPreviewSize != null) { 175 canvas.clipRect(mPreviewSize, Region.Op.REPLACE); 176 } 177 178 mAnimator.draw(canvas); 179 } 180 181 private void setRadius(float radiusPx) { 182 long tMs = mAnimator.getTimeMillis(); 183 // Some devices return zero for invalid or "unknown" diopter values. 184 if (currentFocusAnimation != null && radiusPx > 0.1f) { 185 currentFocusAnimation.setRadius(tMs, radiusPx); 186 mLastRadiusPx = radiusPx; 187 } 188 } 189 190 private void centerAutofocusRing() { 191 Point center = computeCenter(); 192 mAutoFocusRing.setCenterX(center.x); 193 mAutoFocusRing.setCenterY(center.y); 194 } 195 196 private Point computeCenter() { 197 if (mPreviewSize != null && (mPreviewSize.width() * mPreviewSize.height() > 0.01f)) { 198 Log.i(TAG, "Computing center via preview size."); 199 return new Point((int) mPreviewSize.centerX(), (int) mPreviewSize.centerY()); 200 } 201 Log.i(TAG, "Computing center via view bounds."); 202 return new Point(getWidth() / 2, getHeight() / 2); 203 } 204 205 private Paint makePaint(Resources res, int color) { 206 Paint paint = new Paint(); 207 paint.setAntiAlias(true); 208 paint.setColor(res.getColor(color)); 209 paint.setStyle(Paint.Style.STROKE); 210 paint.setStrokeCap(Paint.Cap.ROUND); 211 paint.setStrokeWidth(res.getDimension(R.dimen.focus_circle_stroke)); 212 return paint; 213 } 214 } 215