Loading app/build.gradle.kts +2 −1 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ plugins { alias(libs.plugins.kotlin.android) alias(libs.plugins.compose.compiler) alias(libs.plugins.detekt.plugin) alias(libs.plugins.kotlin.serialization) } android { Loading Loading @@ -75,7 +76,7 @@ dependencies { implementation(libs.elib) // Utilities implementation(libs.gson) implementation(libs.kotlinx.serialization.json) implementation(libs.material) // Tests Loading app/src/main/java/foundation/e/findmydevice/data/PasswordCheckResult.kt 0 → 100644 +9 −0 Original line number Diff line number Diff line package foundation.e.findmydevice.data import kotlinx.serialization.Serializable @Serializable data class PasswordCheckResult( val first: Long, val second: Boolean ) app/src/main/java/foundation/e/findmydevice/storage/PersistentStorage.kt +19 −17 Original line number Diff line number Diff line Loading @@ -2,8 +2,10 @@ package foundation.e.findmydevice.storage import android.content.Context import android.content.SharedPreferences import com.google.gson.Gson import com.google.gson.reflect.TypeToken import foundation.e.findmydevice.data.PasswordCheckResult import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import androidx.core.content.edit /** * PersistentStorage Loading @@ -22,17 +24,16 @@ class PersistentStorage (context: Context) { private val sharedPreferences: SharedPreferences = context.getSharedPreferences(PREFERENCE_STORE, Context.MODE_PRIVATE) private val gson: Gson = Gson() private val json = Json { ignoreUnknownKeys = true } fun clear() { sharedPreferences.edit().clear().apply() sharedPreferences.edit { clear() } } // region Password fun savePassword(password: String) { with(sharedPreferences.edit()) { sharedPreferences.edit { putString(PASSWORD_KEY, password) apply() } } Loading @@ -43,9 +44,8 @@ class PersistentStorage (context: Context) { // region Status fun saveStatus(status: Boolean) { with(sharedPreferences.edit()) { sharedPreferences.edit { putBoolean(STATUS_KEY, status) apply() } } Loading @@ -58,20 +58,22 @@ class PersistentStorage (context: Context) { // Using Pair<Long,Boolean> structure to save password test results in a list fun getCheckedPasswordResultHistory(): List<Pair<Long, Boolean>> { val json = sharedPreferences.getString(DATE_BOOLEAN_LIST_KEY, null) return if (json != null) { val type = object : TypeToken<List<Pair<Long, Boolean>>>() {}.type gson.fromJson(json, type) } else { emptyList() } val stored = sharedPreferences.getString(DATE_BOOLEAN_LIST_KEY, null) ?: return emptyList() return runCatching { json.decodeFromString<List<PasswordCheckResult>>(stored) .map { result -> Pair(result.first, result.second) } }.getOrDefault(emptyList()) } fun addCheckedPasswordResult(value: Boolean) { val currentList = getCheckedPasswordResultHistory().toMutableList() currentList.add(Pair(System.currentTimeMillis(), value)) val json = gson.toJson(currentList) sharedPreferences.edit().putString(DATE_BOOLEAN_LIST_KEY, json).apply() val serialized = json.encodeToString( currentList.map { (timestamp, result) -> PasswordCheckResult(first = timestamp, second = result) } ) sharedPreferences.edit { putString(DATE_BOOLEAN_LIST_KEY, serialized) } } //endregion Loading gradle/libs.versions.toml +3 −2 Original line number Diff line number Diff line Loading @@ -5,9 +5,9 @@ composeBom = "2025.12.01" coreKtx = "1.17.0" detekt = "1.23.8" elib = "0.0.1-alpha11" gson = "2.13.2" junitVersion = "1.3.0" kotlin = "2.3.0" serialization = "1.9.0" material = "1.13.0" mockitoCore = "5.21.0" robolectric = "4.16" Loading @@ -25,7 +25,7 @@ androidx-ui = { group = "androidx.compose.ui", name = "ui" } androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" } elib = { module = "foundation.e:elib", version.ref = "elib" } gson = { module = "com.google.code.gson:gson", version.ref = "gson" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoCore" } robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } Loading @@ -35,3 +35,4 @@ android-application = { id = "com.android.application", version.ref = "agp" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } detekt-plugin = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } Loading
app/build.gradle.kts +2 −1 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ plugins { alias(libs.plugins.kotlin.android) alias(libs.plugins.compose.compiler) alias(libs.plugins.detekt.plugin) alias(libs.plugins.kotlin.serialization) } android { Loading Loading @@ -75,7 +76,7 @@ dependencies { implementation(libs.elib) // Utilities implementation(libs.gson) implementation(libs.kotlinx.serialization.json) implementation(libs.material) // Tests Loading
app/src/main/java/foundation/e/findmydevice/data/PasswordCheckResult.kt 0 → 100644 +9 −0 Original line number Diff line number Diff line package foundation.e.findmydevice.data import kotlinx.serialization.Serializable @Serializable data class PasswordCheckResult( val first: Long, val second: Boolean )
app/src/main/java/foundation/e/findmydevice/storage/PersistentStorage.kt +19 −17 Original line number Diff line number Diff line Loading @@ -2,8 +2,10 @@ package foundation.e.findmydevice.storage import android.content.Context import android.content.SharedPreferences import com.google.gson.Gson import com.google.gson.reflect.TypeToken import foundation.e.findmydevice.data.PasswordCheckResult import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import androidx.core.content.edit /** * PersistentStorage Loading @@ -22,17 +24,16 @@ class PersistentStorage (context: Context) { private val sharedPreferences: SharedPreferences = context.getSharedPreferences(PREFERENCE_STORE, Context.MODE_PRIVATE) private val gson: Gson = Gson() private val json = Json { ignoreUnknownKeys = true } fun clear() { sharedPreferences.edit().clear().apply() sharedPreferences.edit { clear() } } // region Password fun savePassword(password: String) { with(sharedPreferences.edit()) { sharedPreferences.edit { putString(PASSWORD_KEY, password) apply() } } Loading @@ -43,9 +44,8 @@ class PersistentStorage (context: Context) { // region Status fun saveStatus(status: Boolean) { with(sharedPreferences.edit()) { sharedPreferences.edit { putBoolean(STATUS_KEY, status) apply() } } Loading @@ -58,20 +58,22 @@ class PersistentStorage (context: Context) { // Using Pair<Long,Boolean> structure to save password test results in a list fun getCheckedPasswordResultHistory(): List<Pair<Long, Boolean>> { val json = sharedPreferences.getString(DATE_BOOLEAN_LIST_KEY, null) return if (json != null) { val type = object : TypeToken<List<Pair<Long, Boolean>>>() {}.type gson.fromJson(json, type) } else { emptyList() } val stored = sharedPreferences.getString(DATE_BOOLEAN_LIST_KEY, null) ?: return emptyList() return runCatching { json.decodeFromString<List<PasswordCheckResult>>(stored) .map { result -> Pair(result.first, result.second) } }.getOrDefault(emptyList()) } fun addCheckedPasswordResult(value: Boolean) { val currentList = getCheckedPasswordResultHistory().toMutableList() currentList.add(Pair(System.currentTimeMillis(), value)) val json = gson.toJson(currentList) sharedPreferences.edit().putString(DATE_BOOLEAN_LIST_KEY, json).apply() val serialized = json.encodeToString( currentList.map { (timestamp, result) -> PasswordCheckResult(first = timestamp, second = result) } ) sharedPreferences.edit { putString(DATE_BOOLEAN_LIST_KEY, serialized) } } //endregion Loading
gradle/libs.versions.toml +3 −2 Original line number Diff line number Diff line Loading @@ -5,9 +5,9 @@ composeBom = "2025.12.01" coreKtx = "1.17.0" detekt = "1.23.8" elib = "0.0.1-alpha11" gson = "2.13.2" junitVersion = "1.3.0" kotlin = "2.3.0" serialization = "1.9.0" material = "1.13.0" mockitoCore = "5.21.0" robolectric = "4.16" Loading @@ -25,7 +25,7 @@ androidx-ui = { group = "androidx.compose.ui", name = "ui" } androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" } elib = { module = "foundation.e:elib", version.ref = "elib" } gson = { module = "com.google.code.gson:gson", version.ref = "gson" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoCore" } robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } Loading @@ -35,3 +35,4 @@ android-application = { id = "com.android.application", version.ref = "agp" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } detekt-plugin = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }