Home | History | Annotate | Download | only in drawable
      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 android.graphics.drawable;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorSet;
     21 import android.animation.ObjectAnimator;
     22 import android.animation.TimeInterpolator;
     23 import android.graphics.Canvas;
     24 import android.graphics.CanvasProperty;
     25 import android.graphics.Paint;
     26 import android.graphics.Rect;
     27 import android.util.FloatProperty;
     28 import android.view.DisplayListCanvas;
     29 import android.view.RenderNodeAnimator;
     30 import android.view.animation.LinearInterpolator;
     31 
     32 /**
     33  * Draws a ripple background.
     34  */
     35 class RippleBackground extends RippleComponent {
     36 
     37     private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
     38 
     39     private static final int OPACITY_ENTER_DURATION = 600;
     40     private static final int OPACITY_ENTER_DURATION_FAST = 120;
     41     private static final int OPACITY_EXIT_DURATION = 480;
     42 
     43     // Hardware rendering properties.
     44     private CanvasProperty<Paint> mPropPaint;
     45     private CanvasProperty<Float> mPropRadius;
     46     private CanvasProperty<Float> mPropX;
     47     private CanvasProperty<Float> mPropY;
     48 
     49     // Software rendering properties.
     50     private float mOpacity = 0;
     51 
     52     /** Whether this ripple is bounded. */
     53     private boolean mIsBounded;
     54 
     55     public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded,
     56             boolean forceSoftware) {
     57         super(owner, bounds, forceSoftware);
     58 
     59         mIsBounded = isBounded;
     60     }
     61 
     62     public boolean isVisible() {
     63         return mOpacity > 0 || isHardwareAnimating();
     64     }
     65 
     66     @Override
     67     protected boolean drawSoftware(Canvas c, Paint p) {
     68         boolean hasContent = false;
     69 
     70         final int origAlpha = p.getAlpha();
     71         final int alpha = (int) (origAlpha * mOpacity + 0.5f);
     72         if (alpha > 0) {
     73             p.setAlpha(alpha);
     74             c.drawCircle(0, 0, mTargetRadius, p);
     75             p.setAlpha(origAlpha);
     76             hasContent = true;
     77         }
     78 
     79         return hasContent;
     80     }
     81 
     82     @Override
     83     protected boolean drawHardware(DisplayListCanvas c) {
     84         c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
     85         return true;
     86     }
     87 
     88     @Override
     89     protected Animator createSoftwareEnter(boolean fast) {
     90         // Linear enter based on current opacity.
     91         final int maxDuration = fast ? OPACITY_ENTER_DURATION_FAST : OPACITY_ENTER_DURATION;
     92         final int duration = (int) ((1 - mOpacity) * maxDuration);
     93 
     94         final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
     95         opacity.setAutoCancel(true);
     96         opacity.setDuration(duration);
     97         opacity.setInterpolator(LINEAR_INTERPOLATOR);
     98 
     99         return opacity;
    100     }
    101 
    102     @Override
    103     protected Animator createSoftwareExit() {
    104         final AnimatorSet set = new AnimatorSet();
    105 
    106         // Linear exit after enter is completed.
    107         final ObjectAnimator exit = ObjectAnimator.ofFloat(this, RippleBackground.OPACITY, 0);
    108         exit.setInterpolator(LINEAR_INTERPOLATOR);
    109         exit.setDuration(OPACITY_EXIT_DURATION);
    110         exit.setAutoCancel(true);
    111 
    112         final AnimatorSet.Builder builder = set.play(exit);
    113 
    114         // Linear "fast" enter based on current opacity.
    115         final int fastEnterDuration = mIsBounded ?
    116                 (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0;
    117         if (fastEnterDuration > 0) {
    118             final ObjectAnimator enter = ObjectAnimator.ofFloat(this, RippleBackground.OPACITY, 1);
    119             enter.setInterpolator(LINEAR_INTERPOLATOR);
    120             enter.setDuration(fastEnterDuration);
    121             enter.setAutoCancel(true);
    122 
    123             builder.after(enter);
    124         }
    125 
    126         return set;
    127     }
    128 
    129     @Override
    130     protected RenderNodeAnimatorSet createHardwareExit(Paint p) {
    131         final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet();
    132 
    133         final int targetAlpha = p.getAlpha();
    134         final int currentAlpha = (int) (mOpacity * targetAlpha + 0.5f);
    135         p.setAlpha(currentAlpha);
    136 
    137         mPropPaint = CanvasProperty.createPaint(p);
    138         mPropRadius = CanvasProperty.createFloat(mTargetRadius);
    139         mPropX = CanvasProperty.createFloat(0);
    140         mPropY = CanvasProperty.createFloat(0);
    141 
    142         final int fastEnterDuration = mIsBounded ?
    143                 (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0;
    144 
    145         // Linear exit after enter is completed.
    146         final RenderNodeAnimator exit = new RenderNodeAnimator(
    147                 mPropPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
    148         exit.setInterpolator(LINEAR_INTERPOLATOR);
    149         exit.setDuration(OPACITY_EXIT_DURATION);
    150         if (fastEnterDuration > 0) {
    151             exit.setStartDelay(fastEnterDuration);
    152             exit.setStartValue(targetAlpha);
    153         }
    154         set.add(exit);
    155 
    156         // Linear "fast" enter based on current opacity.
    157         if (fastEnterDuration > 0) {
    158             final RenderNodeAnimator enter = new RenderNodeAnimator(
    159                     mPropPaint, RenderNodeAnimator.PAINT_ALPHA, targetAlpha);
    160             enter.setInterpolator(LINEAR_INTERPOLATOR);
    161             enter.setDuration(fastEnterDuration);
    162             set.add(enter);
    163         }
    164 
    165         return set;
    166     }
    167 
    168     @Override
    169     protected void jumpValuesToExit() {
    170         mOpacity = 0;
    171     }
    172 
    173     private static abstract class BackgroundProperty extends FloatProperty<RippleBackground> {
    174         public BackgroundProperty(String name) {
    175             super(name);
    176         }
    177     }
    178 
    179     private static final BackgroundProperty OPACITY = new BackgroundProperty("opacity") {
    180         @Override
    181         public void setValue(RippleBackground object, float value) {
    182             object.mOpacity = value;
    183             object.invalidateSelf();
    184         }
    185 
    186         @Override
    187         public Float get(RippleBackground object) {
    188             return object.mOpacity;
    189         }
    190     };
    191 }
    192