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.animation.Animator; 20 import android.view.View; 21 import android.view.ViewPropertyAnimator; 22 23 import androidx.annotation.Nullable; 24 25 import com.android.app.animation.Interpolators; 26 import com.android.systemui.res.R; 27 import com.android.systemui.statusbar.notification.stack.StackStateAnimator; 28 29 /** 30 * A helper to fade views in and out. 31 */ 32 public class CrossFadeHelper { 33 public static final long ANIMATION_DURATION_LENGTH = 210; 34 fadeOut(final View view)35 public static void fadeOut(final View view) { 36 fadeOut(view, (Runnable) null); 37 } 38 fadeOut(final View view, final Runnable endRunnable)39 public static void fadeOut(final View view, final Runnable endRunnable) { 40 fadeOut(view, ANIMATION_DURATION_LENGTH, 0, endRunnable); 41 } 42 fadeOut(final View view, final Animator.AnimatorListener listener)43 public static void fadeOut(final View view, final Animator.AnimatorListener listener) { 44 fadeOut(view, ANIMATION_DURATION_LENGTH, 0, listener); 45 } 46 fadeOut(final View view, long duration, int delay)47 public static void fadeOut(final View view, long duration, int delay) { 48 fadeOut(view, duration, delay, (Runnable) null); 49 } 50 51 /** 52 * Perform a fade-out animation, invoking {@code endRunnable} when the animation ends. It will 53 * not be invoked if the animation is cancelled. 54 * 55 * @deprecated Use {@link #fadeOut(View, long, int, Animator.AnimatorListener)} instead. 56 */ 57 @Deprecated fadeOut(final View view, long duration, int delay, @Nullable final Runnable endRunnable)58 public static void fadeOut(final View view, long duration, int delay, 59 @Nullable final Runnable endRunnable) { 60 view.animate().cancel(); 61 view.animate() 62 .alpha(0f) 63 .setDuration(duration) 64 .setInterpolator(Interpolators.ALPHA_OUT) 65 .setStartDelay(delay) 66 .withEndAction(() -> { 67 if (endRunnable != null) { 68 endRunnable.run(); 69 } 70 if (view.getVisibility() != View.GONE) { 71 view.setVisibility(View.INVISIBLE); 72 } 73 }); 74 if (view.hasOverlappingRendering()) { 75 view.animate().withLayer(); 76 } 77 } 78 fadeOut(final View view, long duration, int delay, @Nullable final Animator.AnimatorListener listener)79 public static void fadeOut(final View view, long duration, int delay, 80 @Nullable final Animator.AnimatorListener listener) { 81 view.animate().cancel(); 82 ViewPropertyAnimator animator = view.animate() 83 .alpha(0f) 84 .setDuration(duration) 85 .setInterpolator(Interpolators.ALPHA_OUT) 86 .setStartDelay(delay) 87 .withEndAction(() -> { 88 if (view.getVisibility() != View.GONE) { 89 view.setVisibility(View.INVISIBLE); 90 } 91 }); 92 if (listener != null) { 93 animator.setListener(listener); 94 } 95 if (view.hasOverlappingRendering()) { 96 view.animate().withLayer(); 97 } 98 } 99 fadeOut(View view, float fadeOutAmount)100 public static void fadeOut(View view, float fadeOutAmount) { 101 fadeOut(view, fadeOutAmount, true /* remap */); 102 } 103 104 /** 105 * Fade out a view by a given progress amount 106 * @param view the view to fade out 107 * @param fadeOutAmount how much the view is faded out. 0 means not at all and 1 means fully 108 * faded out 109 * @param remap whether the fade amount should be remapped to the shorter duration 110 * {@link #ANIMATION_DURATION_LENGTH} from the normal fade duration 111 * {@link StackStateAnimator#ANIMATION_DURATION_STANDARD} in order to have a faster fading. 112 * 113 * @see #fadeIn(View, float, boolean) 114 */ fadeOut(View view, float fadeOutAmount, boolean remap)115 public static void fadeOut(View view, float fadeOutAmount, boolean remap) { 116 view.animate().cancel(); 117 if (fadeOutAmount == 1.0f && view.getVisibility() != View.GONE) { 118 view.setVisibility(View.INVISIBLE); 119 } else if (view.getVisibility() == View.INVISIBLE) { 120 view.setVisibility(View.VISIBLE); 121 } 122 if (remap) { 123 fadeOutAmount = mapToFadeDuration(fadeOutAmount); 124 } 125 float alpha = Interpolators.ALPHA_OUT.getInterpolation(1.0f - fadeOutAmount); 126 view.setAlpha(alpha); 127 updateLayerType(view, alpha); 128 } 129 mapToFadeDuration(float fadeOutAmount)130 private static float mapToFadeDuration(float fadeOutAmount) { 131 // Assuming a linear interpolator, we can easily map it to our new duration 132 float endPoint = (float) ANIMATION_DURATION_LENGTH 133 / (float) StackStateAnimator.ANIMATION_DURATION_STANDARD; 134 return Math.min(fadeOutAmount / endPoint, 1.0f); 135 } 136 updateLayerType(View view, float alpha)137 private static void updateLayerType(View view, float alpha) { 138 if (view.hasOverlappingRendering() && alpha > 0.0f && alpha < 1.0f) { 139 if (view.getLayerType() != View.LAYER_TYPE_HARDWARE) { 140 view.setLayerType(View.LAYER_TYPE_HARDWARE, null); 141 view.setTag(R.id.cross_fade_layer_type_changed_tag, true); 142 } 143 } else if (view.getLayerType() == View.LAYER_TYPE_HARDWARE 144 && view.getTag(R.id.cross_fade_layer_type_changed_tag) != null) { 145 view.setLayerType(View.LAYER_TYPE_NONE, null); 146 } 147 } 148 fadeIn(final View view)149 public static void fadeIn(final View view) { 150 fadeIn(view, ANIMATION_DURATION_LENGTH, 0); 151 } 152 fadeIn(final View view, Runnable endRunnable)153 public static void fadeIn(final View view, Runnable endRunnable) { 154 fadeIn(view, ANIMATION_DURATION_LENGTH, /* delay= */ 0, endRunnable); 155 } 156 fadeIn(final View view, Animator.AnimatorListener listener)157 public static void fadeIn(final View view, Animator.AnimatorListener listener) { 158 fadeIn(view, ANIMATION_DURATION_LENGTH, /* delay= */ 0, listener); 159 } 160 fadeIn(final View view, long duration, int delay)161 public static void fadeIn(final View view, long duration, int delay) { 162 fadeIn(view, duration, delay, /* endRunnable= */ (Runnable) null); 163 } 164 165 /** 166 * Perform a fade-in animation, invoking {@code endRunnable} when the animation ends. It will 167 * not be invoked if the animation is cancelled. 168 * 169 * @deprecated Use {@link #fadeIn(View, long, int, Animator.AnimatorListener)} instead. 170 */ 171 @Deprecated fadeIn(final View view, long duration, int delay, @Nullable Runnable endRunnable)172 public static void fadeIn(final View view, long duration, int delay, 173 @Nullable Runnable endRunnable) { 174 view.animate().cancel(); 175 if (view.getVisibility() == View.INVISIBLE) { 176 view.setAlpha(0.0f); 177 view.setVisibility(View.VISIBLE); 178 } 179 view.animate() 180 .alpha(1f) 181 .setDuration(duration) 182 .setStartDelay(delay) 183 .setInterpolator(Interpolators.ALPHA_IN) 184 .withEndAction(endRunnable); 185 if (view.hasOverlappingRendering() && view.getLayerType() != View.LAYER_TYPE_HARDWARE) { 186 view.animate().withLayer(); 187 } 188 } 189 fadeIn(final View view, long duration, int delay, @Nullable Animator.AnimatorListener listener)190 public static void fadeIn(final View view, long duration, int delay, 191 @Nullable Animator.AnimatorListener listener) { 192 view.animate().cancel(); 193 if (view.getVisibility() == View.INVISIBLE) { 194 view.setAlpha(0.0f); 195 view.setVisibility(View.VISIBLE); 196 } 197 ViewPropertyAnimator animator = view.animate() 198 .alpha(1f) 199 .setDuration(duration) 200 .setStartDelay(delay) 201 .setInterpolator(Interpolators.ALPHA_IN); 202 if (listener != null) { 203 animator.setListener(listener); 204 } 205 if (view.hasOverlappingRendering() && view.getLayerType() != View.LAYER_TYPE_HARDWARE) { 206 view.animate().withLayer(); 207 } 208 } 209 fadeIn(View view, float fadeInAmount)210 public static void fadeIn(View view, float fadeInAmount) { 211 fadeIn(view, fadeInAmount, false /* remap */); 212 } 213 214 /** 215 * Fade in a view by a given progress amount 216 * @param view the view to fade in 217 * @param fadeInAmount how much the view is faded in. 0 means not at all and 1 means fully 218 * faded in. 219 * @param remap whether the fade amount should be remapped to the shorter duration 220 * {@link #ANIMATION_DURATION_LENGTH} from the normal fade duration 221 * {@link StackStateAnimator#ANIMATION_DURATION_STANDARD} in order to have a faster fading. 222 * 223 * @see #fadeOut(View, float, boolean) 224 */ fadeIn(View view, float fadeInAmount, boolean remap)225 public static void fadeIn(View view, float fadeInAmount, boolean remap) { 226 view.animate().cancel(); 227 if (view.getVisibility() == View.INVISIBLE) { 228 view.setVisibility(View.VISIBLE); 229 } 230 if (remap) { 231 fadeInAmount = mapToFadeDuration(fadeInAmount); 232 } 233 float alpha = Interpolators.ALPHA_IN.getInterpolation(fadeInAmount); 234 view.setAlpha(alpha); 235 updateLayerType(view, alpha); 236 } 237 } 238