1 /* <lambda>null2 * Copyright (C) 2019 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.notification 18 19 import android.animation.Animator 20 import android.animation.AnimatorListenerAdapter 21 import android.animation.ValueAnimator 22 import android.view.View 23 import android.view.ViewGroup 24 import com.android.systemui.Interpolators 25 import com.android.systemui.R 26 27 /** 28 * Class to help with fading of view groups without fading one subview 29 */ 30 class ViewGroupFadeHelper { 31 companion object { 32 private val visibilityIncluder = { 33 view: View -> view.visibility == View.VISIBLE 34 } 35 36 /** 37 * Fade out all views of a root except a single child. This will iterate over all children 38 * of the view and make sure that the animation works smoothly. 39 * @param root the view root to fade the children away 40 * @param excludedView which view should remain 41 * @param duration the duration of the animation 42 */ 43 @JvmStatic 44 fun fadeOutAllChildrenExcept(root: ViewGroup, excludedView: View, duration: Long, 45 endRunnable: Runnable?) { 46 // starting from the view going up, we are adding the siblings of the child to the set 47 // of views that need to be faded. 48 val viewsToFadeOut = gatherViews(root, excludedView, visibilityIncluder) 49 50 // Applying the right layertypes for the animation 51 for (viewToFade in viewsToFadeOut) { 52 if (viewToFade.hasOverlappingRendering 53 && viewToFade.layerType == View.LAYER_TYPE_NONE) { 54 viewToFade.setLayerType(View.LAYER_TYPE_HARDWARE, null) 55 viewToFade.setTag(R.id.view_group_fade_helper_hardware_layer, true) 56 } 57 } 58 59 val animator = ValueAnimator.ofFloat(1.0f, 0.0f).apply { 60 this.duration = duration 61 interpolator = Interpolators.ALPHA_OUT 62 addUpdateListener { animation -> 63 val previousSetAlpha = root.getTag( 64 R.id.view_group_fade_helper_previous_value_tag) as Float? 65 val newAlpha = animation.animatedValue as Float 66 for (viewToFade in viewsToFadeOut) { 67 if (viewToFade.alpha != previousSetAlpha) { 68 // A value was set that wasn't set from our view, let's store it and restore 69 // it at the end 70 viewToFade.setTag(R.id.view_group_fade_helper_restore_tag, viewToFade.alpha) 71 } 72 viewToFade.alpha = newAlpha 73 } 74 root.setTag(R.id.view_group_fade_helper_previous_value_tag, newAlpha) 75 } 76 addListener(object : AnimatorListenerAdapter() { 77 override fun onAnimationEnd(animation: Animator?) { 78 endRunnable?.run() 79 } 80 }) 81 start() 82 } 83 root.setTag(R.id.view_group_fade_helper_modified_views, viewsToFadeOut) 84 root.setTag(R.id.view_group_fade_helper_animator, animator) 85 } 86 87 private fun gatherViews(root: ViewGroup, excludedView: View, 88 shouldInclude: (View) -> Boolean): MutableSet<View> { 89 val viewsToFadeOut = mutableSetOf<View>() 90 var parent = excludedView.parent as ViewGroup? 91 var viewContainingExcludedView = excludedView; 92 while (parent != null) { 93 for (i in 0 until parent.childCount) { 94 val child = parent.getChildAt(i) 95 if (shouldInclude.invoke(child) && viewContainingExcludedView != child) { 96 viewsToFadeOut.add(child) 97 } 98 } 99 if (parent == root) { 100 break; 101 } 102 viewContainingExcludedView = parent 103 parent = parent.parent as ViewGroup? 104 } 105 return viewsToFadeOut 106 } 107 108 /** 109 * Reset all view alphas for views previously transformed away. 110 */ 111 @JvmStatic 112 fun reset(root: ViewGroup) { 113 @Suppress("UNCHECKED_CAST") 114 val modifiedViews = root.getTag(R.id.view_group_fade_helper_modified_views) 115 as MutableSet<View>? 116 val animator = root.getTag(R.id.view_group_fade_helper_animator) as Animator? 117 if (modifiedViews == null || animator == null) { 118 // nothing to restore 119 return 120 } 121 animator.cancel() 122 val lastSetValue = root.getTag( 123 R.id.view_group_fade_helper_previous_value_tag) as Float? 124 for (viewToFade in modifiedViews) { 125 val restoreAlpha = viewToFade.getTag( 126 R.id.view_group_fade_helper_restore_tag) as Float? 127 if (restoreAlpha == null) { 128 continue 129 } 130 if (lastSetValue == viewToFade.alpha) { 131 // it was modified after the transition! 132 viewToFade.alpha = restoreAlpha 133 } 134 val needsLayerReset = viewToFade.getTag( 135 R.id.view_group_fade_helper_hardware_layer) as Boolean? 136 if (needsLayerReset == true) { 137 viewToFade.setLayerType(View.LAYER_TYPE_NONE, null) 138 viewToFade.setTag(R.id.view_group_fade_helper_hardware_layer, null) 139 } 140 viewToFade.setTag(R.id.view_group_fade_helper_restore_tag, null) 141 } 142 root.setTag(R.id.view_group_fade_helper_modified_views, null) 143 root.setTag(R.id.view_group_fade_helper_previous_value_tag, null) 144 root.setTag(R.id.view_group_fade_helper_animator, null) 145 } 146 } 147 } 148