xref: /aosp_15_r20/frameworks/base/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright 2023 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.compose.animation.scene
18 
19 import androidx.annotation.VisibleForTesting
20 import androidx.compose.runtime.Stable
21 
22 /**
23  * A base class to create unique keys, associated to an [identity] that is used to check the
24  * equality of two key instances.
25  */
26 @Stable
27 sealed class Key(val debugName: String, val identity: Any) {
equalsnull28     override fun equals(other: Any?): Boolean {
29         if (this === other) return true
30         if (this.javaClass != other?.javaClass) return false
31         return identity == (other as? Key)?.identity
32     }
33 
hashCodenull34     override fun hashCode(): Int {
35         return identity.hashCode()
36     }
37 
toStringnull38     override fun toString(): String {
39         return "Key(debugName=$debugName)"
40     }
41 }
42 
43 /** The key for a content (scene or overlay). */
44 sealed class ContentKey(debugName: String, identity: Any) : Key(debugName, identity) {
45     @VisibleForTesting
46     // TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can
47     // access internal members.
48     abstract val testTag: String
49 }
50 
51 /** Key for a scene. */
52 class SceneKey(debugName: String, identity: Any = Object()) : ContentKey(debugName, identity) {
53     override val testTag: String = "scene:$debugName"
54 
55     /** The unique [ElementKey] identifying this scene's root element. */
56     val rootElementKey = ElementKey(debugName, identity)
57 
toStringnull58     override fun toString(): String {
59         return "SceneKey(debugName=$debugName)"
60     }
61 }
62 
63 /** Key for an overlay. */
64 class OverlayKey(debugName: String, identity: Any = Object()) : ContentKey(debugName, identity) {
65     override val testTag: String = "overlay:$debugName"
66 
toStringnull67     override fun toString(): String {
68         return "OverlayKey(debugName=$debugName)"
69     }
70 }
71 
72 /** Key for an element. */
73 open class ElementKey(
74     debugName: String,
75     identity: Any = Object(),
76 
77     /**
78      * The [ElementContentPicker] to use when deciding in which scene we should draw shared Elements
79      * or compose MovableElements.
80      */
81     open val contentPicker: ElementContentPicker = DefaultElementContentPicker,
82 
83     /**
84      * Whether we should place all copies of this element when it is shared.
85      *
86      * This should usually be false, but it can be useful when sharing a container that has a
87      * different content in different scenes/overlays. That way the container will have the same
88      * size and position in all scenes/overlays but all different contents will be placed and
89      * visible on screen.
90      */
91     val placeAllCopies: Boolean = false,
92 ) : Key(debugName, identity), ElementMatcher {
93     @VisibleForTesting
94     // TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can
95     // access internal members.
96     val testTag: String = "element:$debugName"
97 
matchesnull98     override fun matches(key: ElementKey, content: ContentKey): Boolean {
99         return key == this
100     }
101 
toStringnull102     override fun toString(): String {
103         return "ElementKey(debugName=$debugName)"
104     }
105 
106     companion object {
107         /** Matches any element whose [key identity][ElementKey.identity] matches [predicate]. */
withIdentitynull108         fun withIdentity(predicate: (Any) -> Boolean): ElementMatcher {
109             return object : ElementMatcher {
110                 override fun matches(key: ElementKey, content: ContentKey): Boolean {
111                     return predicate(key.identity)
112                 }
113             }
114         }
115     }
116 }
117 
118 /** Key for a movable element. */
119 class MovableElementKey(
120     debugName: String,
121 
122     /**
123      * The [StaticElementContentPicker] to use when deciding in which scene we should draw shared
124      * Elements or compose MovableElements.
125      *
126      * @see DefaultElementContentPicker
127      * @see MovableElementContentPicker
128      */
129     override val contentPicker: StaticElementContentPicker,
130     identity: Any = Object(),
131 ) : ElementKey(debugName, identity, contentPicker) {
132     constructor(
133         debugName: String,
134 
135         /** The exhaustive list of contents (scenes or overlays) that can contain this element. */
136         contents: Set<ContentKey>,
137         identity: Any = Object(),
138     ) : this(debugName, MovableElementContentPicker(contents), identity)
139 
toStringnull140     override fun toString(): String {
141         return "MovableElementKey(debugName=$debugName)"
142     }
143 }
144 
145 /** Key for a shared value of an element. */
146 class ValueKey(debugName: String, identity: Any = Object()) : Key(debugName, identity) {
toStringnull147     override fun toString(): String {
148         return "ValueKey(debugName=$debugName)"
149     }
150 }
151 
152 /**
153  * Key for a transition. This can be used to specify which transition spec should be used when
154  * starting the transition between two scenes.
155  */
156 class TransitionKey(debugName: String, identity: Any = Object()) : Key(debugName, identity) {
toStringnull157     override fun toString(): String {
158         return "TransitionKey(debugName=$debugName)"
159     }
160 
161     companion object {
162         /**
163          * A special transition key indicating that the associated transition should be used for
164          * Predictive Back gestures.
165          *
166          * Use this key when defining a transition that you want to be specifically triggered when
167          * the user performs a Predictive Back gesture.
168          */
169         val PredictiveBack = TransitionKey("PredictiveBack")
170     }
171 }
172