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