Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions samples/CameraAccessAndroid/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ dependencies {
implementation(libs.datastore.preferences)
implementation(libs.gson)
implementation(libs.lifecycle.process)
implementation(libs.security.crypto)
androidTestImplementation(libs.androidx.ui.test.junit4)
androidTestImplementation(libs.androidx.test.uiautomator)
androidTestImplementation(libs.androidx.test.rules)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,69 @@ package com.meta.wearable.dat.externalsampleapps.cameraaccess.settings

import android.content.Context
import android.content.SharedPreferences
import android.util.Log
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import com.meta.wearable.dat.externalsampleapps.cameraaccess.Secrets

object SettingsManager {
private const val TAG = "SettingsManager"
private const val PREFS_NAME = "visionclaw_settings"
private const val SECURE_PREFS_NAME = "visionclaw_secure"

// Non-sensitive settings (host, port, toggles)
private lateinit var prefs: SharedPreferences
// Secrets (API keys, tokens) — encrypted at rest
private lateinit var securePrefs: SharedPreferences

fun init(context: Context) {
prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)

val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
securePrefs = EncryptedSharedPreferences.create(
context,
SECURE_PREFS_NAME,
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

migrateSecretsFromPlainPrefs()
}

/** One-time migration: move secrets from plain SharedPreferences to EncryptedSharedPreferences. */
private fun migrateSecretsFromPlainPrefs() {
if (prefs.getBoolean("secrets_migrated", false)) return

val secretKeys = listOf("geminiAPIKey", "openClawHookToken", "openClawGatewayToken")
for (key in secretKeys) {
val value = prefs.getString(key, null)
if (value != null) {
securePrefs.edit().putString(key, value).apply()
prefs.edit().remove(key).apply()
Log.d(TAG, "Migrated $key to encrypted storage")
}
}

prefs.edit().putBoolean("secrets_migrated", true).apply()
}

// Secrets — stored in EncryptedSharedPreferences
var geminiAPIKey: String
get() = prefs.getString("geminiAPIKey", null) ?: Secrets.geminiAPIKey
set(value) = prefs.edit().putString("geminiAPIKey", value).apply()
get() = securePrefs.getString("geminiAPIKey", null) ?: Secrets.geminiAPIKey
set(value) = securePrefs.edit().putString("geminiAPIKey", value).apply()

var openClawHookToken: String
get() = securePrefs.getString("openClawHookToken", null) ?: Secrets.openClawHookToken
set(value) = securePrefs.edit().putString("openClawHookToken", value).apply()

var openClawGatewayToken: String
get() = securePrefs.getString("openClawGatewayToken", null) ?: Secrets.openClawGatewayToken
set(value) = securePrefs.edit().putString("openClawGatewayToken", value).apply()

// Non-sensitive settings — plain SharedPreferences
var geminiSystemPrompt: String
get() = prefs.getString("geminiSystemPrompt", null) ?: DEFAULT_SYSTEM_PROMPT
set(value) = prefs.edit().putString("geminiSystemPrompt", value).apply()
Expand All @@ -32,14 +80,6 @@ object SettingsManager {
}
set(value) = prefs.edit().putInt("openClawPort", value).apply()

var openClawHookToken: String
get() = prefs.getString("openClawHookToken", null) ?: Secrets.openClawHookToken
set(value) = prefs.edit().putString("openClawHookToken", value).apply()

var openClawGatewayToken: String
get() = prefs.getString("openClawGatewayToken", null) ?: Secrets.openClawGatewayToken
set(value) = prefs.edit().putString("openClawGatewayToken", value).apply()

var webrtcSignalingURL: String
get() = prefs.getString("webrtcSignalingURL", null) ?: Secrets.webrtcSignalingURL
set(value) = prefs.edit().putString("webrtcSignalingURL", value).apply()
Expand All @@ -53,6 +93,7 @@ object SettingsManager {
set(value) = prefs.edit().putBoolean("proactiveNotificationsEnabled", value).apply()

fun resetAll() {
securePrefs.edit().clear().apply()
prefs.edit().clear().apply()
}

Expand Down
2 changes: 2 additions & 0 deletions samples/CameraAccessAndroid/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ camerax = "1.4.1"
datastore = "1.1.1"
gson = "2.11.0"
lifecycleProcess = "2.8.7"
securityCrypto = "1.1.0-alpha06"

[libraries]
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
Expand All @@ -41,6 +42,7 @@ camerax-view = { group = "androidx.camera", name = "camera-view", version.ref =
datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" }
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
lifecycle-process = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "lifecycleProcess" }
security-crypto = { group = "androidx.security", name = "security-crypto", version.ref = "securityCrypto" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Expand Down