11package com.penumbraos.bridge_settings
22
3- import android.annotation.SuppressLint
43import android.content.Context
4+ import android.content.SharedPreferences
55import android.media.AudioManager
66import android.os.Handler
77import android.os.Looper
@@ -16,17 +16,12 @@ import kotlinx.coroutines.flow.StateFlow
1616import kotlinx.coroutines.flow.asStateFlow
1717import kotlinx.coroutines.launch
1818import kotlinx.serialization.json.Json
19- import kotlinx.serialization.json.JsonElement
20- import kotlinx.serialization.json.JsonPrimitive
21- import kotlinx.serialization.json.booleanOrNull
22- import kotlinx.serialization.json.doubleOrNull
23- import kotlinx.serialization.json.intOrNull
24- import kotlinx.serialization.json.jsonPrimitive
25- import java.io.File
2619import java.util.concurrent.ConcurrentHashMap
2720
2821private const val TAG = " SettingsRegistry"
2922
23+ private const val SYSTEM_SETTINGS_APP_ID = " penumbra_system"
24+
3025data class ActionResult (
3126 val success : Boolean ,
3227 val message : String? = null ,
@@ -49,9 +44,13 @@ interface SettingsActionProvider {
4944 fun getActionDefinitions (): Map <String , LocalActionDefinition >
5045}
5146
52- class SettingsRegistry (private val context : Context , val shellClient : ShellClient ) {
47+ class SettingsRegistry (
48+ private val context : Context ,
49+ private val sharedPreferences : SharedPreferences ,
50+ shellClient : ShellClient
51+ ) {
5352 private val appSettings = ConcurrentHashMap <String , MutableMap <String , AppSettingsCategory >>()
54- private val systemSettings = ConcurrentHashMap <String , Any >()
53+ private val systemSettings = ConcurrentHashMap <String , Any ? >()
5554 private val actionProviders = ConcurrentHashMap <String , SettingsActionProvider >()
5655
5756 // Execution state tracking
@@ -102,9 +101,6 @@ class SettingsRegistry(private val context: Context, val shellClient: ShellClien
102101 }
103102 }
104103
105- // Store saved app settings values until apps register their schemas
106- private val savedAppSettingsValues =
107- ConcurrentHashMap <String , ConcurrentHashMap <String , Map <String , JsonElement >>>()
108104
109105 private val humaneDisplayController = HumaneDisplayController (shellClient)
110106 private val temperatureController = TemperatureController (shellClient)
@@ -113,11 +109,9 @@ class SettingsRegistry(private val context: Context, val shellClient: ShellClien
113109 // Reference to web server for broadcasting (set by SettingsService)
114110 private var webServer: SettingsWebServer ? = null
115111
116- private val _settingsFlow = MutableStateFlow <Map <String , Map <String , Any >>>(emptyMap())
117- val settingsFlow: StateFlow <Map <String , Map <String , Any >>> = _settingsFlow .asStateFlow()
112+ private val _settingsFlow = MutableStateFlow <Map <String , Map <String , Any ? >>>(emptyMap())
113+ val settingsFlow: StateFlow <Map <String , Map <String , Any ? >>> = _settingsFlow .asStateFlow()
118114
119- @SuppressLint(" SdCardPath" )
120- private val settingsFile = File (" /sdcard/penumbra/etc/settings.json" )
121115 private val json = Json {
122116 prettyPrint = true
123117 ignoreUnknownKeys = true
@@ -142,30 +136,16 @@ class SettingsRegistry(private val context: Context, val shellClient: ShellClien
142136
143137 private fun loadSavedSettings () {
144138 try {
145- if (settingsFile.exists()) {
146- val persistedData =
147- json.decodeFromString<PersistedSettings >(settingsFile.readText())
148-
149- // Load non-Android system settings
150- persistedData.systemSettings.forEach { (key, value) ->
139+ sharedPreferences.all.filter { it.key.startsWith(SYSTEM_SETTINGS_APP_ID ) }
140+ .forEach { (key, value) ->
151141 if (! isAndroidSystemSetting(key)) {
152- systemSettings[key] = value // Keep as string
153- }
154- }
155-
156- // Load app settings values (without schemas - will be merged when apps register)
157- persistedData.appSettings.forEach { (appId, categories) ->
158- val appSavedValues =
159- savedAppSettingsValues.getOrPut(appId) { ConcurrentHashMap () }
160- categories.forEach { (category, settingValues) ->
161- appSavedValues[category] = settingValues
142+ systemSettings[key] = value
162143 }
163144 }
164145
165- Log .i(TAG , " Loaded settings from ${settingsFile.absolutePath} " )
166- }
146+ Log .i(TAG , " Loaded settings from SharedPreferences" )
167147 } catch (e: Exception ) {
168- Log .w(TAG , " Failed to load settings from file " , e)
148+ Log .w(TAG , " Failed to load settings from SharedPreferences " , e)
169149 }
170150 }
171151
@@ -288,26 +268,22 @@ class SettingsRegistry(private val context: Context, val shellClient: ShellClien
288268 category : String ,
289269 definitions : Map <String , SettingDefinition >
290270 ) {
271+ if (appId.startsWith(SYSTEM_SETTINGS_APP_ID )) {
272+ throw IllegalArgumentException (" Cannot register system settings" )
273+ }
274+
291275 Log .i(TAG , " Registering settings for app: $appId , category: $category " )
292276
293277 val appCategories = appSettings.getOrPut(appId) { mutableMapOf () }
294278 val settingsCategory = AppSettingsCategory (appId, category, definitions)
295279
296- // Initialize with default values
297280 definitions.forEach { (key, definition) ->
298- settingsCategory.values[key] = definition.defaultValue
299- }
300-
301- // Merge in any previously saved values for this app/category
302- savedAppSettingsValues[appId]?.get(category)?.forEach { (key, jsonValue) ->
303- if (definitions.containsKey(key)) {
304- // Convert JsonElement back to proper type
305- val convertedValue = jsonValue.jsonPrimitive.let { primitive ->
306- primitive.booleanOrNull ? : primitive.intOrNull ? : primitive.doubleOrNull
307- ? : primitive.content
308- }
309- settingsCategory.values[key] = convertedValue
310- Log .d(TAG , " Restored saved value for $appId .$category .$key = $convertedValue " )
281+ val fullKey = " $appId .$category .$key "
282+ val savedValue = sharedPreferences.all[fullKey]
283+ if (savedValue != null ) {
284+ settingsCategory.values[key] = savedValue
285+ } else {
286+ settingsCategory.values[key] = definition.defaultValue
311287 }
312288 }
313289
@@ -336,8 +312,8 @@ class SettingsRegistry(private val context: Context, val shellClient: ShellClien
336312
337313 settingsCategory.values[key] = value
338314
339- // Save app settings to file
340- saveSettings( )
315+ // Save this specific app setting immediately
316+ saveAppSetting(appId, category, key, value )
341317
342318 updateSettingsFlow()
343319 Log .i(TAG , " Updated app setting: $appId .$category .$key = $value " )
@@ -366,7 +342,7 @@ class SettingsRegistry(private val context: Context, val shellClient: ShellClien
366342 if (success) {
367343 if (! isAndroidSystemSetting(key)) {
368344 systemSettings[key] = value
369- saveSettings( )
345+ saveSystemSetting(key, value )
370346 }
371347
372348 updateSettingsFlow()
@@ -381,12 +357,12 @@ class SettingsRegistry(private val context: Context, val shellClient: ShellClien
381357 return systemSettings[key]
382358 }
383359
384- fun getAllSystemSettings (): Map <String , Any > {
360+ fun getAllSystemSettings (): Map <String , Any ? > {
385361 return systemSettings.toMap()
386362 }
387363
388- fun getAllSettings (): Map <String , Map <String , Any >> {
389- val result = mutableMapOf<String , Map <String , Any >>()
364+ fun getAllSettings (): Map <String , Map <String , Any ? >> {
365+ val result = mutableMapOf<String , Map <String , Any ? >>()
390366
391367 // Add system settings
392368 result[" system" ] = systemSettings.toMap()
@@ -444,44 +420,40 @@ class SettingsRegistry(private val context: Context, val shellClient: ShellClien
444420 }
445421 }
446422
447- private fun saveSettings () {
423+ private fun SharedPreferences.Editor.putValue (key : String , value : Any ) {
424+ when (value) {
425+ is Boolean -> putBoolean(key, value)
426+ is Int -> putInt(key, value)
427+ is Long -> putLong(key, value)
428+ is Float -> putFloat(key, value)
429+ is String -> putString(key, value)
430+ else -> putString(key, value.toString())
431+ }
432+ }
433+
434+ private fun saveSystemSetting (key : String , value : Any ) {
435+ val fullKey = " $SYSTEM_SETTINGS_APP_ID .$key "
448436 try {
449- // Create directory if it doesn't exist
450- settingsFile.parentFile?.mkdirs()
451-
452- // Collect non-Android system settings for persistence
453- val systemSettingsToSave = systemSettings
454- .filterKeys { ! isAndroidSystemSetting(it) }
455- .mapValues { it.value.toString() }
456-
457- // Serialize app settings
458- val appSettingsToSave = mutableMapOf<String , Map <String , Map <String , JsonElement >>>()
459- appSettings.forEach { (appId, categories) ->
460- val categoriesMap = mutableMapOf<String , Map <String , JsonElement >>()
461- categories.forEach { (category, categoryData) ->
462- val valuesMap = mutableMapOf<String , JsonElement >()
463- categoryData.values.forEach { (key, value) ->
464- valuesMap[key] = when (value) {
465- is Boolean -> JsonPrimitive (value)
466- is Number -> JsonPrimitive (value)
467- is String -> JsonPrimitive (value)
468- else -> JsonPrimitive (value.toString())
469- }
470- }
471- categoriesMap[category] = valuesMap
472- }
473- appSettingsToSave[appId] = categoriesMap
437+ sharedPreferences.edit().apply {
438+ putValue(fullKey, value)
439+ apply ()
474440 }
441+ Log .d(TAG , " Saved system setting: $fullKey = $value " )
442+ } catch (e: Exception ) {
443+ Log .e(TAG , " Failed to save system setting $fullKey " , e)
444+ }
445+ }
475446
476- val persistedData = PersistedSettings (
477- systemSettings = systemSettingsToSave,
478- appSettings = appSettingsToSave
479- )
480-
481- settingsFile.writeText(json.encodeToString(persistedData))
482- Log .d(TAG , " Settings saved to ${settingsFile.absolutePath} " )
447+ private fun saveAppSetting (appId : String , category : String , key : String , value : Any ) {
448+ val fullKey = " $appId .$category .$key "
449+ try {
450+ sharedPreferences.edit().apply {
451+ putValue(fullKey, value)
452+ apply ()
453+ }
454+ Log .d(TAG , " Saved app setting: $fullKey = $value " )
483455 } catch (e: Exception ) {
484- Log .e(TAG , " Failed to save settings to file " , e)
456+ Log .e(TAG , " Failed to save app setting $fullKey " , e)
485457 }
486458 }
487459
0 commit comments