1 /*
2  * Copyright (C) 2016 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.systemui.statusbar;
18 
19 import android.view.View;
20 
21 import com.android.systemui.Interpolators;
22 import com.android.systemui.R;
23 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
24 
25 /**
26  * A helper to fade views in and out.
27  */
28 public class CrossFadeHelper {
29     public static final long ANIMATION_DURATION_LENGTH = 210;
30 
fadeOut(final View view, final Runnable endRunnable)31     public static void fadeOut(final View view, final Runnable endRunnable) {
32         fadeOut(view, ANIMATION_DURATION_LENGTH, 0, endRunnable);
33     }
34 
fadeOut(final View view, long duration, int delay, final Runnable endRunnable)35     public static void fadeOut(final View view, long duration, int delay,
36             final Runnable endRunnable) {
37         view.animate().cancel();
38         view.animate()
39                 .alpha(0f)
40                 .setDuration(duration)
41                 .setInterpolator(Interpolators.ALPHA_OUT)
42                 .setStartDelay(delay)
43                 .withEndAction(new Runnable() {
44                     @Override
45                     public void run() {
46                         if (endRunnable != null) {
47                             endRunnable.run();
48                         }
49                         view.setVisibility(View.INVISIBLE);
50                     }
51                 });
52         if (view.hasOverlappingRendering()) {
53             view.animate().withLayer();
54         }
55     }
56 
fadeOut(View view, float fadeOutAmount)57     public static void fadeOut(View view, float fadeOutAmount) {
58         fadeOut(view, fadeOutAmount, true /* remap */);
59     }
60 
61     /**
62      * Fade out a view by a given progress amount
63      * @param view the view to fade out
64      * @param fadeOutAmount how much the view is faded out. 0 means not at all and 1 means fully
65      *                      faded out
66      * @param remap whether the fade amount should be remapped to the shorter duration
67      * {@link #ANIMATION_DURATION_LENGTH} from the normal fade duration
68      * {@link StackStateAnimator#ANIMATION_DURATION_STANDARD} in order to have a faster fading.
69      *
70      * @see #fadeIn(View, float, boolean)
71      */
fadeOut(View view, float fadeOutAmount, boolean remap)72     public static void fadeOut(View view, float fadeOutAmount, boolean remap) {
73         view.animate().cancel();
74         if (fadeOutAmount == 1.0f) {
75             view.setVisibility(View.INVISIBLE);
76         } else if (view.getVisibility() == View.INVISIBLE) {
77             view.setVisibility(View.VISIBLE);
78         }
79         if (remap) {
80             fadeOutAmount = mapToFadeDuration(fadeOutAmount);
81         }
82         float alpha = Interpolators.ALPHA_OUT.getInterpolation(1.0f - fadeOutAmount);
83         view.setAlpha(alpha);
84         updateLayerType(view, alpha);
85     }
86 
mapToFadeDuration(float fadeOutAmount)87     private static float mapToFadeDuration(float fadeOutAmount) {
88         // Assuming a linear interpolator, we can easily map it to our new duration
89         float endPoint = (float) ANIMATION_DURATION_LENGTH
90                 / (float) StackStateAnimator.ANIMATION_DURATION_STANDARD;
91         return Math.min(fadeOutAmount / endPoint, 1.0f);
92     }
93 
updateLayerType(View view, float alpha)94     private static void updateLayerType(View view, float alpha) {
95         if (view.hasOverlappingRendering() && alpha > 0.0f && alpha < 1.0f) {
96             if (view.getLayerType() != View.LAYER_TYPE_HARDWARE) {
97                 view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
98                 view.setTag(R.id.cross_fade_layer_type_changed_tag, true);
99             }
100         } else if (view.getLayerType() == View.LAYER_TYPE_HARDWARE
101                 && view.getTag(R.id.cross_fade_layer_type_changed_tag) != null) {
102             if (view.getTag(R.id.cross_fade_layer_type_changed_tag) != null) {
103                 view.setLayerType(View.LAYER_TYPE_NONE, null);
104             }
105         }
106     }
107 
fadeIn(final View view)108     public static void fadeIn(final View view) {
109         fadeIn(view, ANIMATION_DURATION_LENGTH, 0);
110     }
111 
fadeIn(final View view, long duration, int delay)112     public static void fadeIn(final View view, long duration, int delay) {
113         view.animate().cancel();
114         if (view.getVisibility() == View.INVISIBLE) {
115             view.setAlpha(0.0f);
116             view.setVisibility(View.VISIBLE);
117         }
118         view.animate()
119                 .alpha(1f)
120                 .setDuration(duration)
121                 .setStartDelay(delay)
122                 .setInterpolator(Interpolators.ALPHA_IN)
123                 .withEndAction(null);
124         if (view.hasOverlappingRendering() && view.getLayerType() != View.LAYER_TYPE_HARDWARE) {
125             view.animate().withLayer();
126         }
127     }
128 
fadeIn(View view, float fadeInAmount)129     public static void fadeIn(View view, float fadeInAmount) {
130         fadeIn(view, fadeInAmount, true /* remap */);
131     }
132 
133     /**
134      * Fade in a view by a given progress amount
135      * @param view the view to fade in
136      * @param fadeInAmount how much the view is faded in. 0 means not at all and 1 means fully
137      *                     faded in.
138      * @param remap whether the fade amount should be remapped to the shorter duration
139      * {@link #ANIMATION_DURATION_LENGTH} from the normal fade duration
140      * {@link StackStateAnimator#ANIMATION_DURATION_STANDARD} in order to have a faster fading.
141      *
142      * @see #fadeOut(View, float, boolean)
143      */
fadeIn(View view, float fadeInAmount, boolean remap)144     public static void fadeIn(View view, float fadeInAmount, boolean remap) {
145         view.animate().cancel();
146         if (view.getVisibility() == View.INVISIBLE) {
147             view.setVisibility(View.VISIBLE);
148         }
149         if (remap) {
150             fadeInAmount = mapToFadeDuration(fadeInAmount);
151         }
152         float alpha = Interpolators.ALPHA_IN.getInterpolation(fadeInAmount);
153         view.setAlpha(alpha);
154         updateLayerType(view, alpha);
155     }
156 }
157