blob: a207d575912dc607302900539c6504ff5611e3ac [file] [log] [blame]
Fengjiang Liea7cb492024-03-18 11:36:26 -07001/*
2 * Copyright (C) 2024 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
17package com.android.launcher3
18
19import android.view.View
20import android.view.ViewGroup
21import android.view.ViewParent
22
23object UtilitiesKt {
24
25 /**
26 * Modify [ViewGroup]'s attribute with type [T]. The overridden attribute is saved by calling
27 * [View.setTag] and can be later restored by [View.getTag].
28 *
29 * @param <T> type of [ViewGroup] attribute. For example, [T] is [Boolean] if modifying
30 * [ViewGroup.setClipChildren]
31 */
32 abstract class ViewGroupAttrModifier<T>(
33 private val targetAttrValue: T,
34 private val tagKey: Int
35 ) {
36 /**
37 * If [targetAttrValue] is different from existing view attribute returned from
38 * [getAttribute], this method will save existing attribute by calling [ViewGroup.setTag].
39 * Then call [setAttribute] to set attribute with [targetAttrValue].
40 */
41 fun saveAndChangeAttribute(viewGroup: ViewGroup) {
42 val oldAttrValue = getAttribute(viewGroup)
43 if (oldAttrValue !== targetAttrValue) {
44 viewGroup.setTag(tagKey, oldAttrValue)
45 setAttribute(viewGroup, targetAttrValue)
46 }
47 }
48
49 /** Restore saved attribute in [saveAndChangeAttribute] by calling [ViewGroup.getTag]. */
50 @Suppress("UNCHECKED_CAST")
51 fun restoreAttribute(viewGroup: ViewGroup) {
52 val oldAttrValue: T = viewGroup.getTag(tagKey) as T ?: return
53 setAttribute(viewGroup, oldAttrValue)
54 viewGroup.setTag(tagKey, null)
55 }
56
57 /** Subclass will override this method to decide how to get [ViewGroup] attribute. */
58 abstract fun getAttribute(viewGroup: ViewGroup): T
59
60 /** Subclass will override this method to decide how to set [ViewGroup] attribute. */
61 abstract fun setAttribute(viewGroup: ViewGroup, attr: T)
62 }
63
64 /** [ViewGroupAttrModifier] to call [ViewGroup.setClipChildren] to false. */
65 @JvmField
66 val CLIP_CHILDREN_FALSE_MODIFIER: ViewGroupAttrModifier<Boolean> =
67 object : ViewGroupAttrModifier<Boolean>(false, R.id.saved_clip_children_tag_id) {
68 override fun getAttribute(viewGroup: ViewGroup): Boolean {
69 return viewGroup.clipChildren
70 }
71
72 override fun setAttribute(viewGroup: ViewGroup, clipChildren: Boolean) {
73 viewGroup.clipChildren = clipChildren
74 }
75 }
76
77 /** [ViewGroupAttrModifier] to call [ViewGroup.setClipToPadding] to false. */
78 @JvmField
79 val CLIP_TO_PADDING_FALSE_MODIFIER: ViewGroupAttrModifier<Boolean> =
80 object : ViewGroupAttrModifier<Boolean>(false, R.id.saved_clip_to_padding_tag_id) {
81 override fun getAttribute(viewGroup: ViewGroup): Boolean {
82 return viewGroup.clipToPadding
83 }
84
85 override fun setAttribute(viewGroup: ViewGroup, clipToPadding: Boolean) {
86 viewGroup.clipToPadding = clipToPadding
87 }
88 }
89
90 /**
91 * Recursively call [ViewGroupAttrModifier.saveAndChangeAttribute] from [View] to its parent
92 * (direct or indirect) inclusive.
93 *
94 * [ViewGroupAttrModifier.saveAndChangeAttribute] will save the existing attribute value on each
95 * view with [View.setTag], which can be restored in [restoreAttributesOnViewTree].
96 *
97 * Note that if parent is null or not a parent of the view, this method will be applied all the
98 * way to root view.
99 *
100 * @param v child view
101 * @param parent direct or indirect parent of child view
102 * @param modifiers list of [ViewGroupAttrModifier] to modify view attribute
103 */
104 @JvmStatic
105 fun modifyAttributesOnViewTree(
106 v: View?,
107 parent: ViewParent?,
108 vararg modifiers: ViewGroupAttrModifier<*>
109 ) {
110 if (v == null) {
111 return
112 }
113 if (v is ViewGroup) {
114 for (modifier in modifiers) {
115 modifier.saveAndChangeAttribute(v)
116 }
117 }
118 if (v === parent) {
119 return
120 }
121 if (v.parent is View) {
122 modifyAttributesOnViewTree(v.parent as View, parent, *modifiers)
123 }
124 }
125
126 /**
127 * Recursively call [ViewGroupAttrModifier.restoreAttribute]} to restore view attributes
128 * previously saved in [ViewGroupAttrModifier.saveAndChangeAttribute] on view to its parent
129 * (direct or indirect) inclusive.
130 *
131 * Note that if parent is null or not a parent of the view, this method will be applied all the
132 * way to root view.
133 *
134 * @param v child view
135 * @param parent direct or indirect parent of child view
136 * @param modifiers list of [ViewGroupAttrModifier] to restore view attributes
137 */
138 @JvmStatic
139 fun restoreAttributesOnViewTree(
140 v: View?,
141 parent: ViewParent?,
142 vararg modifiers: ViewGroupAttrModifier<*>
143 ) {
144 if (v == null) {
145 return
146 }
147 if (v is ViewGroup) {
148 for (modifier in modifiers) {
149 modifier.restoreAttribute(v)
150 }
151 }
152 if (v === parent) {
153 return
154 }
155 if (v.parent is View) {
156 restoreAttributesOnViewTree(v.parent as View, parent, *modifiers)
157 }
158 }
159}