1 /* 2 * Copyright (C) 2020 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 package com.android.systemui.util.settings 17 18 import android.content.ContentResolver 19 import android.database.ContentObserver 20 import android.net.Uri 21 import android.provider.Settings.SettingNotFoundException 22 import com.android.app.tracing.TraceUtils.trace 23 24 /** 25 * Used to interact with mainly with Settings.Global, but can also be used for Settings.System and 26 * Settings.Secure. To use the per-user System and Secure settings, [UserSettingsProxy] must be used 27 * instead. 28 * 29 * This interface can be implemented to give instance method (instead of static method) versions of 30 * Settings.Global. It can be injected into class constructors and then faked or mocked as needed in 31 * tests. 32 * 33 * You can ask for [GlobalSettings] to be injected as needed. 34 * 35 * This class also provides [.registerContentObserver] methods, normally found on [ContentResolver] 36 * instances, unifying setting related actions in one place. 37 */ 38 interface SettingsProxy { 39 /** Returns the [ContentResolver] this instance was constructed with. */ getContentResolvernull40 fun getContentResolver(): ContentResolver 41 42 /** 43 * Construct the content URI for a particular name/value pair, useful for monitoring changes 44 * with a ContentObserver. 45 * 46 * @param name to look up in the table 47 * @return the corresponding content URI, or null if not present 48 */ 49 fun getUriFor(name: String): Uri 50 51 /** 52 * Convenience wrapper around [ContentResolver.registerContentObserver].' 53 * 54 * Implicitly calls [getUriFor] on the passed in name. 55 */ 56 fun registerContentObserverSync(name: String, settingsObserver: ContentObserver) { 57 registerContentObserverSync(getUriFor(name), settingsObserver) 58 } 59 60 /** Convenience wrapper around [ContentResolver.registerContentObserver].' */ registerContentObserverSyncnull61 fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) = 62 registerContentObserverSync(uri, false, settingsObserver) 63 64 /** 65 * Convenience wrapper around [ContentResolver.registerContentObserver].' 66 * 67 * Implicitly calls [getUriFor] on the passed in name. 68 */ 69 fun registerContentObserverSync( 70 name: String, 71 notifyForDescendants: Boolean, 72 settingsObserver: ContentObserver 73 ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver) 74 75 /** Convenience wrapper around [ContentResolver.registerContentObserver].' */ 76 fun registerContentObserverSync( 77 uri: Uri, 78 notifyForDescendants: Boolean, 79 settingsObserver: ContentObserver 80 ) { 81 trace({ "SP#registerObserver#[$uri]" }) { 82 getContentResolver() 83 .registerContentObserver(uri, notifyForDescendants, settingsObserver) 84 } 85 } 86 87 /** See [ContentResolver.unregisterContentObserver]. */ unregisterContentObserverSyncnull88 fun unregisterContentObserverSync(settingsObserver: ContentObserver) { 89 trace({ "SP#unregisterObserver" }) { 90 getContentResolver().unregisterContentObserver(settingsObserver) 91 } 92 } 93 94 /** 95 * Look up a name in the database. 96 * 97 * @param name to look up in the table 98 * @return the corresponding value, or null if not present 99 */ getStringnull100 fun getString(name: String): String 101 102 /** 103 * Store a name/value pair into the database. 104 * 105 * @param name to store 106 * @param value to associate with the name 107 * @return true if the value was set, false on database errors 108 */ 109 fun putString(name: String, value: String): Boolean 110 111 /** 112 * Store a name/value pair into the database. 113 * 114 * The method takes an optional tag to associate with the setting which can be used to clear 115 * only settings made by your package and associated with this tag by passing the tag to 116 * [ ][.resetToDefaults]. Anyone can override the current tag. Also if another package changes 117 * the setting then the tag will be set to the one specified in the set call which can be null. 118 * Also any of the settings setters that do not take a tag as an argument effectively clears the 119 * tag. 120 * 121 * For example, if you set settings A and B with tags T1 and T2 and another app changes setting 122 * A (potentially to the same value), it can assign to it a tag T3 (note that now the package 123 * that changed the setting is not yours). Now if you reset your changes for T1 and T2 only 124 * setting B will be reset and A not (as it was changed by another package) but since A did not 125 * change you are in the desired initial state. Now if the other app changes the value of A 126 * (assuming you registered an observer in the beginning) you would detect that the setting was 127 * changed by another app and handle this appropriately (ignore, set back to some value, etc). 128 * 129 * Also the method takes an argument whether to make the value the default for this setting. If 130 * the system already specified a default value, then the one passed in here will **not** be set 131 * as the default. 132 * 133 * @param name to store. 134 * @param value to associate with the name. 135 * @param tag to associate with the setting. 136 * @param makeDefault whether to make the value the default one. 137 * @return true if the value was set, false on database errors. 138 * @see .resetToDefaults 139 */ 140 fun putString(name: String, value: String, tag: String, makeDefault: Boolean): Boolean 141 142 /** 143 * Convenience function for retrieving a single secure settings value as an integer. Note that 144 * internally setting values are always stored as strings; this function converts the string to 145 * an integer for you. The default value will be returned if the setting is not defined or not 146 * an integer. 147 * 148 * @param name The name of the setting to retrieve. 149 * @param def Value to return if the setting is not defined. 150 * @return The setting's current value, or 'def' if it is not defined or not a valid integer. 151 */ 152 fun getInt(name: String, def: Int): Int { 153 val v = getString(name) 154 return try { 155 v.toInt() 156 } catch (e: NumberFormatException) { 157 def 158 } 159 } 160 161 /** 162 * Convenience function for retrieving a single secure settings value as an integer. Note that 163 * internally setting values are always stored as strings; this function converts the string to 164 * an integer for you. 165 * 166 * This version does not take a default value. If the setting has not been set, or the string 167 * value is not a number, it throws [Settings.SettingNotFoundException]. 168 * 169 * @param name The name of the setting to retrieve. 170 * @return The setting's current value. 171 * @throws Settings.SettingNotFoundException Thrown if a setting by the given name can't be 172 * found or the setting value is not an integer. 173 */ 174 @Throws(SettingNotFoundException::class) getIntnull175 fun getInt(name: String): Int { 176 val v = getString(name) 177 return try { 178 v.toInt() 179 } catch (e: NumberFormatException) { 180 throw SettingNotFoundException(name) 181 } 182 } 183 184 /** 185 * Convenience function for updating a single settings value as an integer. This will either 186 * create a new entry in the table if the given name does not exist, or modify the value of the 187 * existing row with that name. Note that internally setting values are always stored as 188 * strings, so this function converts the given value to a string before storing it. 189 * 190 * @param name The name of the setting to modify. 191 * @param value The new value for the setting. 192 * @return true if the value was set, false on database errors 193 */ putIntnull194 fun putInt(name: String, value: Int): Boolean { 195 return putString(name, value.toString()) 196 } 197 198 /** 199 * Convenience function for retrieving a single secure settings value as a boolean. Note that 200 * internally setting values are always stored as strings; this function converts the string to 201 * a boolean for you. The default value will be returned if the setting is not defined or not a 202 * boolean. 203 * 204 * @param name The name of the setting to retrieve. 205 * @param def Value to return if the setting is not defined. 206 * @return The setting's current value, or 'def' if it is not defined or not a valid boolean. 207 */ getBoolnull208 fun getBool(name: String, def: Boolean): Boolean { 209 return getInt(name, if (def) 1 else 0) != 0 210 } 211 212 /** 213 * Convenience function for retrieving a single secure settings value as a boolean. Note that 214 * internally setting values are always stored as strings; this function converts the string to 215 * a boolean for you. 216 * 217 * This version does not take a default value. If the setting has not been set, or the string 218 * value is not a number, it throws [Settings.SettingNotFoundException]. 219 * 220 * @param name The name of the setting to retrieve. 221 * @return The setting's current value. 222 * @throws Settings.SettingNotFoundException Thrown if a setting by the given name can't be 223 * found or the setting value is not a boolean. 224 */ 225 @Throws(SettingNotFoundException::class) getBoolnull226 fun getBool(name: String): Boolean { 227 return getInt(name) != 0 228 } 229 230 /** 231 * Convenience function for updating a single settings value as a boolean. This will either 232 * create a new entry in the table if the given name does not exist, or modify the value of the 233 * existing row with that name. Note that internally setting values are always stored as 234 * strings, so this function converts the given value to a string before storing it. 235 * 236 * @param name The name of the setting to modify. 237 * @param value The new value for the setting. 238 * @return true if the value was set, false on database errors 239 */ putBoolnull240 fun putBool(name: String, value: Boolean): Boolean { 241 return putInt(name, if (value) 1 else 0) 242 } 243 244 /** 245 * Convenience function for retrieving a single secure settings value as a `long`. Note that 246 * internally setting values are always stored as strings; this function converts the string to 247 * a `long` for you. The default value will be returned if the setting is not defined or not a 248 * `long`. 249 * 250 * @param name The name of the setting to retrieve. 251 * @param def Value to return if the setting is not defined. 252 * @return The setting's current value, or 'def' if it is not defined or not a valid `long`. 253 */ getLongnull254 fun getLong(name: String, def: Long): Long { 255 val valString = getString(name) 256 return parseLongOrUseDefault(valString, def) 257 } 258 259 /** 260 * Convenience function for retrieving a single secure settings value as a `long`. Note that 261 * internally setting values are always stored as strings; this function converts the string to 262 * a `long` for you. 263 * 264 * This version does not take a default value. If the setting has not been set, or the string 265 * value is not a number, it throws [Settings.SettingNotFoundException]. 266 * 267 * @param name The name of the setting to retrieve. 268 * @return The setting's current value. 269 * @throws Settings.SettingNotFoundException Thrown if a setting by the given name can't be 270 * found or the setting value is not an integer. 271 */ 272 @Throws(SettingNotFoundException::class) getLongnull273 fun getLong(name: String): Long { 274 val valString = getString(name) 275 return parseLongOrThrow(name, valString) 276 } 277 278 /** 279 * Convenience function for updating a secure settings value as a long integer. This will either 280 * create a new entry in the table if the given name does not exist, or modify the value of the 281 * existing row with that name. Note that internally setting values are always stored as 282 * strings, so this function converts the given value to a string before storing it. 283 * 284 * @param name The name of the setting to modify. 285 * @param value The new value for the setting. 286 * @return true if the value was set, false on database errors 287 */ putLongnull288 fun putLong(name: String, value: Long): Boolean { 289 return putString(name, value.toString()) 290 } 291 292 /** 293 * Convenience function for retrieving a single secure settings value as a floating point 294 * number. Note that internally setting values are always stored as strings; this function 295 * converts the string to an float for you. The default value will be returned if the setting is 296 * not defined or not a valid float. 297 * 298 * @param name The name of the setting to retrieve. 299 * @param def Value to return if the setting is not defined. 300 * @return The setting's current value, or 'def' if it is not defined or not a valid float. 301 */ getFloatnull302 fun getFloat(name: String, def: Float): Float { 303 val v = getString(name) 304 return parseFloat(v, def) 305 } 306 307 /** 308 * Convenience function for retrieving a single secure settings value as a float. Note that 309 * internally setting values are always stored as strings; this function converts the string to 310 * a float for you. 311 * 312 * This version does not take a default value. If the setting has not been set, or the string 313 * value is not a number, it throws [Settings.SettingNotFoundException]. 314 * 315 * @param name The name of the setting to retrieve. 316 * @return The setting's current value. 317 * @throws Settings.SettingNotFoundException Thrown if a setting by the given name can't be 318 * found or the setting value is not a float. 319 */ 320 @Throws(SettingNotFoundException::class) getFloatnull321 fun getFloat(name: String): Float { 322 val v = getString(name) 323 return parseFloatOrThrow(name, v) 324 } 325 326 /** 327 * Convenience function for updating a single settings value as a floating point number. This 328 * will either create a new entry in the table if the given name does not exist, or modify the 329 * value of the existing row with that name. Note that internally setting values are always 330 * stored as strings, so this function converts the given value to a string before storing it. 331 * 332 * @param name The name of the setting to modify. 333 * @param value The new value for the setting. 334 * @return true if the value was set, false on database errors 335 */ putFloatnull336 fun putFloat(name: String, value: Float): Boolean { 337 return putString(name, value.toString()) 338 } 339 340 companion object { 341 /** Convert a string to a long, or uses a default if the string is malformed or null */ 342 @JvmStatic parseLongOrUseDefaultnull343 fun parseLongOrUseDefault(valString: String, def: Long): Long { 344 val value: Long 345 value = 346 try { 347 valString.toLong() 348 } catch (e: NumberFormatException) { 349 def 350 } 351 return value 352 } 353 354 /** Convert a string to a long, or throws an exception if the string is malformed or null */ 355 @JvmStatic 356 @Throws(SettingNotFoundException::class) parseLongOrThrownull357 fun parseLongOrThrow(name: String, valString: String?): Long { 358 if (valString == null) { 359 throw SettingNotFoundException(name) 360 } 361 return try { 362 valString.toLong() 363 } catch (e: NumberFormatException) { 364 throw SettingNotFoundException(name) 365 } 366 } 367 368 /** Convert a string to a float, or uses a default if the string is malformed or null */ 369 @JvmStatic parseFloatnull370 fun parseFloat(v: String?, def: Float): Float { 371 return try { 372 v?.toFloat() ?: def 373 } catch (e: NumberFormatException) { 374 def 375 } 376 } 377 378 /** 379 * Convert a string to a float, or throws an exception if the string is malformed or null 380 */ 381 @JvmStatic 382 @Throws(SettingNotFoundException::class) parseFloatOrThrownull383 fun parseFloatOrThrow(name: String, v: String?): Float { 384 if (v == null) { 385 throw SettingNotFoundException(name) 386 } 387 return try { 388 v.toFloat() 389 } catch (e: NumberFormatException) { 390 throw SettingNotFoundException(name) 391 } 392 } 393 } 394 } 395