Skip to content

Commit

Permalink
Fix non unique items ids crashes - Use SecureRandom when batch updati…
Browse files Browse the repository at this point in the history
…ng lists
  • Loading branch information
lolo-io committed Apr 1, 2024
1 parent 0497600 commit 09dce9d
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 49 deletions.
46 changes: 25 additions & 21 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ android {
minSdk = 23
targetSdk = 34
versionCode = versionCodeCI ?: 19
versionName = "1.4.1"
versionName = "1.4.2"
vectorDrawables.useSupportLibrary = true
}

Expand All @@ -50,6 +50,10 @@ android {
}
}

ksp {
arg("room.schemaLocation", "$projectDir/schemas")
}

buildTypes {
getByName("debug") {
applicationIdSuffix = ".debug"
Expand Down Expand Up @@ -85,39 +89,39 @@ dependencies {

// android
implementation(libs.androidx.core.splashscreen)
implementation (libs.androidx.preference.ktx)
implementation (libs.androidx.lifecycle.extensions)
implementation (libs.androidx.legacy.support.v4)
implementation (libs.androidx.appcompat)
implementation(libs.androidx.preference.ktx)
implementation(libs.androidx.lifecycle.extensions)
implementation(libs.androidx.legacy.support.v4)
implementation(libs.androidx.appcompat)

// android - design
implementation (libs.constraint.layout)
implementation (libs.androidx.recyclerview)
implementation (libs.flexbox)
implementation (libs.material)
implementation (libs.androidx.swiperefreshlayout)
implementation(libs.constraint.layout)
implementation(libs.androidx.recyclerview)
implementation(libs.flexbox)
implementation(libs.material)
implementation(libs.androidx.swiperefreshlayout)

// kotlin
implementation (libs.kotlinx.coroutines.core)
implementation (libs.kotlin.stdlib.jdk7)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlin.stdlib.jdk7)

// firebase
implementation (libs.firebase.crashlytics)
implementation(libs.firebase.crashlytics)

// koin di
implementation (libs.koin.android)
implementation (libs.koin.androidx.navigation)
implementation(libs.koin.android)
implementation(libs.koin.androidx.navigation)

// room
implementation (libs.androidx.room.runtime)
implementation (libs.androidx.room.ktx)
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)

// json
implementation (libs.gson)
implementation(libs.gson)

// other libs
implementation (libs.whatsnew)
implementation (libs.storage)
implementation (libs.advrecyclerview)
implementation(libs.whatsnew)
implementation(libs.storage)
implementation(libs.advrecyclerview)
}
102 changes: 102 additions & 0 deletions app/schemas/com.lolo.io.onelist.core.database.OneListDatabase/1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "ddf75a2d5d7163d83c62a03b588eb6b4",
"entities": [
{
"tableName": "itemList",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`title` TEXT NOT NULL, `position` INTEGER NOT NULL, `items` TEXT NOT NULL, `uri` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
"fields": [
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "position",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "items",
"columnName": "items",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "uri",
"columnName": "uri",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "item",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`title` TEXT NOT NULL, `comment` TEXT NOT NULL, `done` INTEGER NOT NULL, `commentDisplayed` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
"fields": [
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "comment",
"columnName": "comment",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "done",
"columnName": "done",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "commentDisplayed",
"columnName": "commentDisplayed",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ddf75a2d5d7163d83c62a03b588eb6b4')"
]
}
}
102 changes: 102 additions & 0 deletions app/schemas/com.lolo.io.onelist.core.database.OneListDatabase/2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "ddf75a2d5d7163d83c62a03b588eb6b4",
"entities": [
{
"tableName": "itemList",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`title` TEXT NOT NULL, `position` INTEGER NOT NULL, `items` TEXT NOT NULL, `uri` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
"fields": [
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "position",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "items",
"columnName": "items",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "uri",
"columnName": "uri",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "item",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`title` TEXT NOT NULL, `comment` TEXT NOT NULL, `done` INTEGER NOT NULL, `commentDisplayed` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
"fields": [
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "comment",
"columnName": "comment",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "done",
"columnName": "done",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "commentDisplayed",
"columnName": "commentDisplayed",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ddf75a2d5d7163d83c62a03b588eb6b4')"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,21 @@ package com.lolo.io.onelist.core.data.migration

import android.content.Context
import android.content.SharedPreferences
import android.health.connect.datatypes.units.Length
import android.util.Log
import android.widget.Toast
import androidx.fragment.app.FragmentActivity
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.lolo.io.onelist.App
import com.lolo.io.onelist.core.data.reporitory.OneListRepository
import com.lolo.io.onelist.core.data.shared_preferences.SharedPreferencesHelper
import com.lolo.io.onelist.core.database.dao.ItemListDao
import com.lolo.io.onelist.core.model.ItemList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.lang.ref.WeakReference
import java.security.SecureRandom
import kotlin.math.abs


class UpdateHelper(
private val preferences: SharedPreferencesHelper,
Expand All @@ -35,12 +33,42 @@ class UpdateHelper(
private val oldDefaultPathPref = "defaultPath"
private val oldThemePref: String = "theme"

fun hasToMigratePrefs(activity: FragmentActivity): Boolean {

fun applyMigrationsIfNecessary(activity: FragmentActivity, then: () -> Unit) {
if (hasToMigratePrefs(activity)) {
applyUpdatePatches(activity, then)
}
fixItemsWithSameIdsIfFond(then)

}


private fun fixItemsWithSameIdsIfFond(then: () -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
val allLists = repository.getAllLists().first().lists
val secureRandom = SecureRandom()
val ids = allLists.flatMap { it.items }.map { it.id }
val distinctIds = ids.distinct()
if (ids.size > distinctIds.size) {
allLists.forEach {
it.items.forEach {
it.id = secureRandom.nextLong()
}
}

repository.saveAllLists(allLists)

then()
}
}
}

private fun hasToMigratePrefs(activity: FragmentActivity): Boolean {
val activityPreferences = activity.getPreferences(Context.MODE_PRIVATE)
return activityPreferences.getString(oldVersionPref, null) != null
}

fun applyUpdatePatches(activity: FragmentActivity, then: () -> Unit) {
private fun applyUpdatePatches(activity: FragmentActivity, then: () -> Unit) {
val activityPreferences = activity.getPreferences(Context.MODE_PRIVATE)
val editor = activityPreferences.edit()

Expand All @@ -55,9 +83,14 @@ class UpdateHelper(
preferences.theme = activityPreferences.getString(oldThemePref, "auto") ?: "auto"
editor.putString(oldThemePref, null)

val secureRandom = SecureRandom()

val oldLists = getAllLists(activityPreferences)
CoroutineScope(Dispatchers.IO).launch {
oldLists.forEach {
it.items.forEach {
it.id = abs(secureRandom.nextLong())
}
repository.createList(it.copy(id = 0))
}
then()
Expand Down
Loading

0 comments on commit 09dce9d

Please sign in to comment.