Home | History | Annotate | Download | only in focus
      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