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