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 /** Key for a scene. */
44 class SceneKey(
45     debugName: String,
46     identity: Any = Object(),
47 ) : Key(debugName, identity) {
48     @VisibleForTesting
49     // TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can
50     // access internal members.
51     val testTag: String = "scene:$debugName"
52 
53     /** The unique [ElementKey] identifying this scene's root element. */
54     val rootElementKey = ElementKey(debugName, identity)
55 
toStringnull56     override fun toString(): String {
57         return "SceneKey(debugName=$debugName)"
58     }
59 }
60 
61 /** Key for an element. */
62 class ElementKey(
63     debugName: String,
64     identity: Any = Object(),
65 
66     /**
67      * The [ElementScenePicker] to use when deciding in which scene we should draw shared Elements
68      * or compose MovableElements.
69      */
70     val scenePicker: ElementScenePicker = DefaultElementScenePicker,
71 ) : Key(debugName, identity), ElementMatcher {
72     @VisibleForTesting
73     // TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can
74     // access internal members.
75     val testTag: String = "element:$debugName"
76 
matchesnull77     override fun matches(key: ElementKey, scene: SceneKey): Boolean {
78         return key == this
79     }
80 
toStringnull81     override fun toString(): String {
82         return "ElementKey(debugName=$debugName)"
83     }
84 
85     companion object {
86         /** Matches any element whose [key identity][ElementKey.identity] matches [predicate]. */
withIdentitynull87         fun withIdentity(predicate: (Any) -> Boolean): ElementMatcher {
88             return object : ElementMatcher {
89                 override fun matches(key: ElementKey, scene: SceneKey): Boolean {
90                     return predicate(key.identity)
91                 }
92             }
93         }
94     }
95 }
96 
97 /** Key for a shared value of an element. */
98 class ValueKey(debugName: String, identity: Any = Object()) : Key(debugName, identity) {
toStringnull99     override fun toString(): String {
100         return "ValueKey(debugName=$debugName)"
101     }
102 }
103 
104 /**
105  * Key for a transition. This can be used to specify which transition spec should be used when
106  * starting the transition between two scenes.
107  */
108 class TransitionKey(debugName: String, identity: Any = Object()) : Key(debugName, identity) {
toStringnull109     override fun toString(): String {
110         return "TransitionKey(debugName=$debugName)"
111     }
112 }
113