1 /*
2  * Copyright 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 @file:JvmName("ViewTree")
18 
19 package androidx.core.viewtree
20 
21 import android.view.View
22 import android.view.ViewParent
23 
24 /**
25  * Assigns the disjoint parent of the given `view` to `parent`.
26  *
27  * A disjoint parent acts as an alternative to [View.getParent] and is used to link two disjoint
28  * view hierarchies together. ViewOverlays, popups, and dialogs are all examples of when two view
29  * hierarchies are disjoint but related. In all of these cases, there is either a new window or new
30  * root view that has no parent of its own but conceptually is owned or otherwise related to another
31  * specific view in the disjoint hierarchy.
32  *
33  * A disjoint parent is only used when a view has no parent of its own, so it is important to set
34  * this tag only once at the root of the tree that is disconnected from its parent. AndroidX
35  * automatically sets disjoint parents for hierarchies it creates in certain use cases, like for
36  * view overlays (used in transitions).
37  *
38  * Setting the disjoint parent allows other components to resolve information from the disjoint
39  * parent. AndroidX will use disjoint parents to resolve values stored in the view tree, like
40  * ViewModel store owners, lifecycle owners, and more. They are not used or set by the platform.
41  * Other View-based libraries should consider setting this property to allow these and similar
42  * lookups to occur between disconnected view hierarchies. Opting in is not mandatory, and therefore
43  * disjoint parents are not guaranteed to be a comprehensive source of view ownership in all
44  * situations.
45  *
46  * To prevent a leak, a view must not outlive its disjoint parent. Additionally, you should avoid
47  * accidentally creating a cycle in the parent and disjoint parent hierarchy. A view can be
48  * re-parented to a different disjoint parent by calling this method again in the future, and can
49  * have its disjoint parent cleared by setting it to `null`.
50  *
51  * @param parent The disjoint parent to set on `view`
52  * @receiver The view to set the disjoint parent of
53  * @see [getParentOrViewTreeDisjointParent]
54  */
setViewTreeDisjointParentnull55 public fun View.setViewTreeDisjointParent(parent: ViewParent?) {
56     setTag(R.id.view_tree_disjoint_parent, parent)
57 }
58 
59 /**
60  * Looks up a disjoint parent previously set by [setViewTreeDisjointParent].
61  *
62  * @return The [View.getParent] or the disjoint parent of the given `view`. If present, the parent
63  *   view is always preferred by this method over the disjoint parent. If the view has neither a
64  *   parent nor a disjoint parent, `null` is returned.
65  * @receiver The view to get the parent or disjoint parent from
66  */
getParentOrViewTreeDisjointParentnull67 public fun View.getParentOrViewTreeDisjointParent(): ViewParent? {
68     val parent = parent
69     if (parent != null) return parent
70 
71     val djParent = getTag(R.id.view_tree_disjoint_parent)
72     return if ((djParent is ViewParent)) djParent else null
73 }
74