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