1 /* 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 17 package com.android.launcher3 18 19 import android.view.View 20 import android.view.ViewGroup 21 import android.view.ViewParent 22 23 object 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 */ saveAndChangeAttributenull41 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") restoreAttributenull51 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. */ getAttributenull58 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) { getAttributenull81 override fun getAttribute(viewGroup: ViewGroup): Boolean { 82 return viewGroup.clipToPadding 83 } 84 setAttributenull85 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 modifyAttributesOnViewTreenull105 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 restoreAttributesOnViewTreenull139 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 } 160