1 /*
2 * Copyright 2021 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 androidx.glance.action
18
19 import androidx.datastore.preferences.core.Preferences
20 import java.util.Collections
21
22 /**
23 * Action parameters, used to pass information to an [Action].
24 *
25 * Construct action parameters using [actionParametersOf] or [mutableActionParametersOf], with typed
26 * key-value pairs. The [Key] class enforces typing of the values inserted.
27 */
28 abstract class ActionParameters internal constructor() {
29
30 /**
31 * Key for [ActionParameters] values. Type T is the type of the associated value. T must be a
32 * primitive or Parcelable type. The [Key.name] must be unique, keys with the same name are
33 * considered equal.
34 */
35 class Key<T : Any>(val name: String) {
36 /**
37 * Infix function to create a Parameters.Pair.
38 *
39 * @param value the value this key should point to
40 */
tonull41 infix fun to(value: T): Pair<T> = Pair(this, value)
42
43 override fun equals(other: Any?): Boolean = other is Key<*> && name == other.name
44
45 override fun hashCode(): Int = name.hashCode()
46
47 override fun toString(): String = name
48 }
49
50 /**
51 * Key Value pairs for Parameters. Type T is the type of the value. Pairs must have unique keys,
52 * or they will be considered equal.
53 *
54 * Create this using the infix function [to].
55 */
56 class Pair<T : Any> internal constructor(internal val key: Key<T>, internal val value: T) {
57 override fun equals(other: Any?): Boolean =
58 other is Pair<*> && key == other.key && value == other.value
59
60 override fun hashCode(): Int = key.hashCode() + value.hashCode()
61
62 override fun toString(): String = "(${key.name}, $value)"
63 }
64
65 /**
66 * Returns true if the Parameters set contains the given Key.
67 *
68 * @param key the key to check for
69 */
containsnull70 abstract operator fun <T : Any> contains(key: Key<T>): Boolean
71
72 /**
73 * Get a parameter with a key. If the key is not set, returns null.
74 *
75 * @param T the type of the parameter
76 * @param key the key for the parameter
77 * @throws ClassCastException if there is something stored with the same name as [key] but it
78 * cannot be cast to T
79 */
80 abstract operator fun <T : Any> get(key: Key<T>): T?
81
82 /**
83 * Get a parameter with a key. If the key is not set, returns the provided default value.
84 *
85 * @param T the type of the parameter
86 * @param key the key for the parameter
87 * @param defaultValue the default value to return if key is missing
88 * @throws ClassCastException if there is something stored with the same name as [key] but it
89 * cannot be cast to T
90 */
91 abstract fun <T : Any> getOrDefault(key: Key<T>, defaultValue: @UnsafeVariance T): T
92
93 /**
94 * Retrieves a map of all key value pairs. The map is unmodifiable, and attempts to mutate it
95 * will throw runtime exceptions.
96 *
97 * @return a map of all parameters in this Parameters
98 */
99 abstract fun asMap(): Map<Key<out Any>, Any>
100
101 /** Returns whether there are any keys stored in the parameters. */
102 abstract fun isEmpty(): Boolean
103 }
104
105 /**
106 * Mutable version of [ActionParameters]. Allows for editing the underlying data, and adding or
107 * removing key-value pairs.
108 */
109 class MutableActionParameters
110 internal constructor(internal val map: MutableMap<Key<out Any>, Any> = mutableMapOf()) :
111 ActionParameters() {
112
113 override operator fun <T : Any> contains(key: Key<T>): Boolean = map.containsKey(key)
114
115 @Suppress("UNCHECKED_CAST")
116 override operator fun <T : Any> get(key: Key<T>): T? = map[key] as T?
117
118 override fun <T : Any> getOrDefault(key: Key<T>, defaultValue: T): T = get(key) ?: defaultValue
119
120 override fun asMap(): Map<Key<out Any>, Any> = Collections.unmodifiableMap(map)
121
122 /**
123 * Sets a key value pair in MutableParameters. If the value is null, the key is removed from the
124 * parameters.
125 *
126 * @param key the parameter to set
127 * @param value the value to assign to this parameter
128 * @return the previous value associated with the key, or null if the key was not present
129 */
130 operator fun <T : Any> set(key: Key<T>, value: T?): T? {
131 val mapValue = get(key)
132 when (value) {
133 null -> remove(key)
134 else -> map[key] = value
135 }
136 return mapValue
137 }
138
139 /**
140 * Removes an item from this MutableParameters.
141 *
142 * @param key the parameter to remove
143 * @return the original value of the parameter
144 */
145 @Suppress("UNCHECKED_CAST") fun <T : Any> remove(key: Key<T>) = map.remove(key) as T?
146
147 /** Removes all parameters from this MutableParameters. */
148 fun clear() = map.clear()
149
150 override fun equals(other: Any?): Boolean = other is MutableActionParameters && map == other.map
151
152 override fun hashCode(): Int = map.hashCode()
153
154 override fun toString(): String = map.toString()
155
156 override fun isEmpty(): Boolean = map.isEmpty()
157 }
158
159 /**
160 * Returns a new read-only Parameters, from the specified contents. The key element in the given
161 * pairs will point to the corresponding value element. All values must be either of primitive or
162 * Parcelable types.
163 *
164 * If multiple pairs have the same key, the resulting map will contain only the value from the last
165 * of those pairs.
166 */
actionParametersOfnull167 fun actionParametersOf(vararg pairs: ActionParameters.Pair<out Any>): ActionParameters =
168 mutableActionParametersOf(*pairs)
169
170 /**
171 * Returns a new MutableParameters, from the specified contents. The key element in the given pairs
172 * will point to the corresponding value element. All values must be either of primitive or
173 * Parcelable types.
174 *
175 * If multiple pairs have the same key, the resulting Parameters will contain only the value from
176 * the last of those pairs.
177 */
178 fun mutableActionParametersOf(
179 vararg pairs: ActionParameters.Pair<out Any>
180 ): MutableActionParameters =
181 MutableActionParameters(mutableMapOf(*pairs.map { it.key to it.value }.toTypedArray()))
182
183 /**
184 * Gets a mutable copy of Parameters, containing all key value pairs. This can be used to edit the
185 * parameter data without creating a new object.
186 *
187 * This is similar to [Map.toMutableMap].
188 *
189 * @return a MutableParameters with all the same parameters as this Parameters
190 */
ActionParametersnull191 fun ActionParameters.toMutableParameters(): MutableActionParameters =
192 MutableActionParameters(asMap().toMutableMap())
193
194 /**
195 * Gets a read-only copy of Parameters, containing all key value pairs.
196 *
197 * This is similar to [Map.toMap].
198 *
199 * @return a copy of this Parameters
200 */
201 fun ActionParameters.toParameters(): ActionParameters = toMutableParameters()
202
203 /** Creates an action key from a preferences key. */
204 fun <T : Any> Preferences.Key<T>.toParametersKey(): ActionParameters.Key<T> =
205 ActionParameters.Key(name)
206