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.ObjectAnimator;
20 import android.animation.TimeInterpolator;
21 import android.graphics.Canvas;
22 import android.graphics.Paint;
23 import android.graphics.Rect;
24 import android.util.FloatProperty;
25 import android.view.animation.LinearInterpolator;
26 
27 /**
28  * Draws a ripple background.
29  */
30 class RippleBackground extends RippleComponent {
31 
32     private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
33 
34     private static final int OPACITY_DURATION = 80;
35 
36     private ObjectAnimator mAnimator;
37 
38     private float mOpacity = 0;
39 
40     /** Whether this ripple is bounded. */
41     private boolean mIsBounded;
42 
43     private boolean mFocused = false;
44     private boolean mHovered = false;
45 
RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded)46     public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded) {
47         super(owner, bounds);
48 
49         mIsBounded = isBounded;
50     }
51 
isVisible()52     public boolean isVisible() {
53         return mOpacity > 0;
54     }
55 
draw(Canvas c, Paint p)56     public void draw(Canvas c, Paint p) {
57         final int origAlpha = p.getAlpha();
58         final int alpha = Math.min((int) (origAlpha * mOpacity + 0.5f), 255);
59         if (alpha > 0) {
60             p.setAlpha(alpha);
61             c.drawCircle(0, 0, mTargetRadius, p);
62             p.setAlpha(origAlpha);
63         }
64     }
65 
setState(boolean focused, boolean hovered, boolean pressed)66     public void setState(boolean focused, boolean hovered, boolean pressed) {
67         if (!mFocused) {
68             focused = focused && !pressed;
69         }
70         if (!mHovered) {
71             hovered = hovered && !pressed;
72         }
73         if (mHovered != hovered || mFocused != focused) {
74             mHovered = hovered;
75             mFocused = focused;
76             onStateChanged();
77         }
78     }
79 
onStateChanged()80     private void onStateChanged() {
81         // Hover             = .2 * alpha
82         // Focus             = .6 * alpha
83         // Focused + Hovered = .6 * alpha
84         float newOpacity = mFocused ? .6f : mHovered ? .2f : 0f;
85         if (mAnimator != null) {
86             mAnimator.cancel();
87             mAnimator = null;
88         }
89         mAnimator = ObjectAnimator.ofFloat(this, OPACITY, newOpacity);
90         mAnimator.setDuration(OPACITY_DURATION);
91         mAnimator.setInterpolator(LINEAR_INTERPOLATOR);
92         mAnimator.start();
93     }
94 
jumpToFinal()95     public void jumpToFinal() {
96         if (mAnimator != null) {
97             mAnimator.end();
98             mAnimator = null;
99         }
100     }
101 
102     private static abstract class BackgroundProperty extends FloatProperty<RippleBackground> {
BackgroundProperty(String name)103         public BackgroundProperty(String name) {
104             super(name);
105         }
106     }
107 
108     private static final BackgroundProperty OPACITY = new BackgroundProperty("opacity") {
109         @Override
110         public void setValue(RippleBackground object, float value) {
111             object.mOpacity = value;
112             object.invalidateSelf();
113         }
114 
115         @Override
116         public Float get(RippleBackground object) {
117             return object.mOpacity;
118         }
119     };
120 }
121