diff --git a/app/schemas/com.greenart7c3.nostrsigner.database.AppDatabase/15.json b/app/schemas/com.greenart7c3.nostrsigner.database.AppDatabase/15.json index bc1f9a9e..8821d388 100644 --- a/app/schemas/com.greenart7c3.nostrsigner.database.AppDatabase/15.json +++ b/app/schemas/com.greenart7c3.nostrsigner.database.AppDatabase/15.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 15, - "identityHash": "3690c8720be33c75a319014268cf1afe", + "identityHash": "92867f818b27cf76fafbe2ba5c6cd100", "entities": [ { "tableName": "application", @@ -214,7 +214,7 @@ ] }, { - "tableName": "history2", + "tableName": "history", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `pkKey` TEXT NOT NULL, `type` TEXT NOT NULL, `kind` INTEGER, `time` INTEGER NOT NULL, `accepted` INTEGER NOT NULL)", "fields": [ { @@ -261,22 +261,22 @@ }, "indices": [ { - "name": "history_by_pk_key2", + "name": "history_by_pk_key", "unique": false, "columnNames": [ "pkKey" ], "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `history_by_pk_key2` ON `${TABLE_NAME}` (`pkKey`)" + "createSql": "CREATE INDEX IF NOT EXISTS `history_by_pk_key` ON `${TABLE_NAME}` (`pkKey`)" }, { - "name": "history_by_id2", + "name": "history_by_id", "unique": false, "columnNames": [ "id" ], "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `history_by_id2` ON `${TABLE_NAME}` (`id`)" + "createSql": "CREATE INDEX IF NOT EXISTS `history_by_id` ON `${TABLE_NAME}` (`id`)" }, { "name": "history_by_time", @@ -302,7 +302,7 @@ ], "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, '3690c8720be33c75a319014268cf1afe')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '92867f818b27cf76fafbe2ba5c6cd100')" ] } } \ No newline at end of file diff --git a/app/schemas/com.greenart7c3.nostrsigner.database.AppDatabase/16.json b/app/schemas/com.greenart7c3.nostrsigner.database.AppDatabase/16.json new file mode 100644 index 00000000..ed502e92 --- /dev/null +++ b/app/schemas/com.greenart7c3.nostrsigner.database.AppDatabase/16.json @@ -0,0 +1,222 @@ +{ + "formatVersion": 1, + "database": { + "version": 16, + "identityHash": "e6b7bce07714a5336bb9200ed1760153", + "entities": [ + { + "tableName": "application", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `name` TEXT NOT NULL, `relays` TEXT NOT NULL, `url` TEXT NOT NULL, `icon` TEXT NOT NULL, `description` TEXT NOT NULL, `pubKey` TEXT NOT NULL, `isConnected` INTEGER NOT NULL, `secret` TEXT NOT NULL, `useSecret` INTEGER NOT NULL, `signPolicy` INTEGER NOT NULL, `closeApplication` INTEGER NOT NULL, `deleteAfter` INTEGER NOT NULL, `lastUsed` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "relays", + "columnName": "relays", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pubKey", + "columnName": "pubKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isConnected", + "columnName": "isConnected", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "secret", + "columnName": "secret", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "useSecret", + "columnName": "useSecret", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "signPolicy", + "columnName": "signPolicy", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "closeApplication", + "columnName": "closeApplication", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleteAfter", + "columnName": "deleteAfter", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastUsed", + "columnName": "lastUsed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "key" + ] + }, + "indices": [ + { + "name": "index_key", + "unique": true, + "columnNames": [ + "key" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_key` ON `${TABLE_NAME}` (`key`)" + }, + { + "name": "index_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_name` ON `${TABLE_NAME}` (`name`)" + } + ] + }, + { + "tableName": "applicationPermission", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER, `pkKey` TEXT NOT NULL, `type` TEXT NOT NULL, `kind` INTEGER, `acceptable` INTEGER NOT NULL, `rememberType` INTEGER NOT NULL, `acceptUntil` INTEGER NOT NULL, `rejectUntil` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`pkKey`) REFERENCES `application`(`key`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER" + }, + { + "fieldPath": "pkKey", + "columnName": "pkKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "kind", + "columnName": "kind", + "affinity": "INTEGER" + }, + { + "fieldPath": "acceptable", + "columnName": "acceptable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "rememberType", + "columnName": "rememberType", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "acceptUntil", + "columnName": "acceptUntil", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "rejectUntil", + "columnName": "rejectUntil", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "permissions_by_pk_key", + "unique": false, + "columnNames": [ + "pkKey" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `permissions_by_pk_key` ON `${TABLE_NAME}` (`pkKey`)" + }, + { + "name": "permissions_unique", + "unique": true, + "columnNames": [ + "pkKey", + "type", + "kind" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `permissions_unique` ON `${TABLE_NAME}` (`pkKey`, `type`, `kind`)" + } + ], + "foreignKeys": [ + { + "table": "application", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "pkKey" + ], + "referencedColumns": [ + "key" + ] + } + ] + } + ], + "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, 'e6b7bce07714a5336bb9200ed1760153')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.greenart7c3.nostrsigner.database.HistoryDatabase/1.json b/app/schemas/com.greenart7c3.nostrsigner.database.HistoryDatabase/1.json new file mode 100644 index 00000000..e8ac33e6 --- /dev/null +++ b/app/schemas/com.greenart7c3.nostrsigner.database.HistoryDatabase/1.json @@ -0,0 +1,99 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "29d5da9510c1dd78f8bdf4641d6cdf33", + "entities": [ + { + "tableName": "history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `pkKey` TEXT NOT NULL, `type` TEXT NOT NULL, `kind` INTEGER, `time` INTEGER NOT NULL, `accepted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pkKey", + "columnName": "pkKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "kind", + "columnName": "kind", + "affinity": "INTEGER" + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accepted", + "columnName": "accepted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "history_by_pk_key", + "unique": false, + "columnNames": [ + "pkKey" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `history_by_pk_key` ON `${TABLE_NAME}` (`pkKey`)" + }, + { + "name": "history_by_id", + "unique": false, + "columnNames": [ + "id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `history_by_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "history_by_time", + "unique": false, + "columnNames": [ + "time" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `history_by_time` ON `${TABLE_NAME}` (`time`)" + }, + { + "name": "history_by_key_and_time", + "unique": false, + "columnNames": [ + "pkKey", + "time" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `history_by_key_and_time` ON `${TABLE_NAME}` (`pkKey`, `time`)" + } + ] + } + ], + "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, '29d5da9510c1dd78f8bdf4641d6cdf33')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt b/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt index fb91e429..6c4f07bb 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt @@ -16,6 +16,7 @@ import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import com.greenart7c3.nostrsigner.database.AppDatabase +import com.greenart7c3.nostrsigner.database.HistoryDatabase import com.greenart7c3.nostrsigner.database.LogDatabase import com.greenart7c3.nostrsigner.models.Account import com.greenart7c3.nostrsigner.models.AmberSettings @@ -117,6 +118,7 @@ class Amber : Application(), LifecycleObserver { private var databases = ConcurrentHashMap() private var logDatabases = ConcurrentHashMap() + private var historyDatabases = ConcurrentHashMap() val isOnMobileDataState = mutableStateOf(false) val isOnWifiDataState = mutableStateOf(false) @@ -382,6 +384,13 @@ class Amber : Application(), LifecycleObserver { return logDatabases[npub]!! } + fun getHistoryDatabase(npub: String): HistoryDatabase { + if (!historyDatabases.containsKey(npub)) { + historyDatabases[npub] = HistoryDatabase.getDatabase(this, npub) + } + return historyDatabases[npub]!! + } + fun getSavedRelays(): Set { val savedRelays = mutableSetOf() LocalPreferences.allSavedAccounts(this).forEach { accountInfo -> diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/SignerProvider.kt b/app/src/main/java/com/greenart7c3/nostrsigner/SignerProvider.kt index 35a4f467..dbecd39b 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/SignerProvider.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/SignerProvider.kt @@ -99,6 +99,7 @@ class SignerProvider : ContentProvider() { return null } val database = Amber.instance.getDatabase(account.npub) + val historyDatabase = Amber.instance.getHistoryDatabase(account.npub) val permission = database .applicationDao() @@ -113,7 +114,7 @@ class SignerProvider : ContentProvider() { } if (!isRemembered) { scope.launch { - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, packageName, @@ -122,6 +123,7 @@ class SignerProvider : ContentProvider() { TimeUtils.now(), false, ), + account.npub, ) } val cursor = @@ -134,7 +136,7 @@ class SignerProvider : ContentProvider() { val result = signString(message, account.signer.keyPair.privKey!!).toHexKey() scope.launch { - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, packageName, @@ -143,6 +145,7 @@ class SignerProvider : ContentProvider() { TimeUtils.now(), true, ), + account.npub, ) } @@ -180,6 +183,7 @@ class SignerProvider : ContentProvider() { } val database = Amber.instance.getDatabase(account.npub) + val historyDatabase = Amber.instance.getHistoryDatabase(account.npub) var permission = database .applicationDao() @@ -211,7 +215,7 @@ class SignerProvider : ContentProvider() { } if (!isRemembered) { scope.launch { - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, packageName, @@ -220,6 +224,7 @@ class SignerProvider : ContentProvider() { TimeUtils.now(), false, ), + account.npub, ) } @@ -234,7 +239,7 @@ class SignerProvider : ContentProvider() { val signedEvent = account.signer.signerSync.sign(event.createdAt, event.kind, event.tags, event.content) scope.launch { - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, packageName, @@ -243,6 +248,7 @@ class SignerProvider : ContentProvider() { TimeUtils.now(), true, ), + account.npub, ) } @@ -278,6 +284,7 @@ class SignerProvider : ContentProvider() { val account = LocalPreferences.loadFromEncryptedStorageSync(context!!, npub) ?: return null val database = Amber.instance.getDatabase(account.npub) val logDatabase = Amber.instance.getLogDatabase(account.npub) + val historyDatabase = Amber.instance.getHistoryDatabase(account.npub) var permission = database .applicationDao() @@ -312,7 +319,7 @@ class SignerProvider : ContentProvider() { } if (!isRemembered) { scope.launch { - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, packageName, @@ -321,6 +328,7 @@ class SignerProvider : ContentProvider() { TimeUtils.now(), false, ), + account.npub, ) } @@ -366,7 +374,7 @@ class SignerProvider : ContentProvider() { } scope.launch { - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, packageName, @@ -375,6 +383,7 @@ class SignerProvider : ContentProvider() { TimeUtils.now(), true, ), + account.npub, ) } @@ -402,6 +411,7 @@ class SignerProvider : ContentProvider() { return null } val database = Amber.instance.getDatabase(account.npub) + val historyDatabase = Amber.instance.getHistoryDatabase(account.npub) val permission = database .applicationDao() @@ -417,7 +427,7 @@ class SignerProvider : ContentProvider() { } if (!isRemembered) { scope.launch { - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, packageName, @@ -426,6 +436,7 @@ class SignerProvider : ContentProvider() { TimeUtils.now(), false, ), + account.npub, ) } @@ -438,7 +449,7 @@ class SignerProvider : ContentProvider() { } scope.launch { - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, packageName, @@ -447,6 +458,7 @@ class SignerProvider : ContentProvider() { TimeUtils.now(), true, ), + account.npub, ) } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/database/AppDatabase.kt b/app/src/main/java/com/greenart7c3/nostrsigner/database/AppDatabase.kt index 60d8f703..49043fc1 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/database/AppDatabase.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/database/AppDatabase.kt @@ -138,13 +138,22 @@ val MIGRATION_14_15 = object : Migration(14, 15) { } } +val MIGRATION_15_16 = object : Migration(15, 16) { + override fun migrate(db: SupportSQLiteDatabase) { + try { + db.execSQL("DROP TABLE history2") + } catch (e: Exception) { + Log.e(Amber.TAG, "No amber_log table", e) + } + } +} + @Database( entities = [ ApplicationEntity::class, ApplicationPermissionsEntity::class, - HistoryEntity::class, ], - version = 15, + version = 16, ) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { @@ -181,6 +190,7 @@ abstract class AppDatabase : RoomDatabase() { .addMigrations(MIGRATION_12_13) .addMigrations(MIGRATION_13_14) .addMigrations(MIGRATION_14_15) + .addMigrations(MIGRATION_15_16) .build() instance.openHelper.writableDatabase.execSQL("VACUUM") diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/database/ApplicationDao.kt b/app/src/main/java/com/greenart7c3/nostrsigner/database/ApplicationDao.kt index b8a559c0..a80ec594 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/database/ApplicationDao.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/database/ApplicationDao.kt @@ -1,13 +1,11 @@ package com.greenart7c3.nostrsigner.database -import android.util.Log import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction -import com.greenart7c3.nostrsigner.Amber import kotlinx.coroutines.flow.Flow @Dao @@ -130,50 +128,14 @@ interface ApplicationDao { @Transaction suspend fun delete(key: String) - @Insert(onConflict = OnConflictStrategy.REPLACE) - @Transaction - suspend fun innerAddHistory(entity: HistoryEntity) - - @Insert(onConflict = OnConflictStrategy.IGNORE) - @Transaction - suspend fun addHistory(entity: HistoryEntity) { - try { - innerAddHistory(entity) - updateLastUsed(entity.pkKey, entity.time) - } catch (e: Exception) { - Log.e(Amber.TAG, "Error adding history", e) - } - } - @Query("UPDATE application SET lastUsed = :time where `key` = :key") @Transaction suspend fun updateLastUsed(key: String, time: Long) - @Query("SELECT * FROM history2 where pkKey = :pk ORDER BY time DESC") - fun getAllHistory(pk: String): Flow> - - @Query("SELECT * FROM history2 ORDER BY time DESC") - fun getAllHistory(): Flow> - - @Query("DELETE FROM history2 where pkKey = :pk") - suspend fun deleteHistory(pk: String) - @Delete @Transaction suspend fun deletePermission(permission: ApplicationPermissionsEntity) - @Query("SELECT COUNT(*) FROM history2 WHERE time < :time") - @Transaction - suspend fun countOldHistory(time: Long): Long - - @Query("SELECT * FROM history2 WHERE time < :time LIMIT 100") - @Transaction - suspend fun getOldHistory(time: Long): List - - @Delete - @Transaction - suspend fun deleteHistory(historyEntity: HistoryEntity) - @Query("DELETE FROM application WHERE deleteAfter < :time AND deleteAfter > 0") @Transaction suspend fun deleteOldApplications(time: Long): Int diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDao.kt b/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDao.kt new file mode 100644 index 00000000..289346b3 --- /dev/null +++ b/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDao.kt @@ -0,0 +1,52 @@ +package com.greenart7c3.nostrsigner.database + +import android.util.Log +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Transaction +import com.greenart7c3.nostrsigner.Amber +import kotlinx.coroutines.flow.Flow + +@Dao +interface HistoryDao { + @Query("SELECT * FROM history where pkKey = :pk ORDER BY time DESC") + fun getAllHistory(pk: String): Flow> + + @Query("SELECT * FROM history ORDER BY time DESC") + fun getAllHistory(): Flow> + + @Query("DELETE FROM history where pkKey = :pk") + suspend fun deleteHistory(pk: String) + + @Query("SELECT COUNT(*) FROM history WHERE time < :time") + @Transaction + suspend fun countOldHistory(time: Long): Long + + @Query("SELECT * FROM history WHERE time < :time LIMIT 100") + @Transaction + suspend fun getOldHistory(time: Long): List + + @Delete + @Transaction + suspend fun deleteHistory(historyEntity: HistoryEntity) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + @Transaction + suspend fun innerAddHistory(entity: HistoryEntity) + + @Insert(onConflict = OnConflictStrategy.IGNORE) + @Transaction + suspend fun addHistory(entity: HistoryEntity, npub: String?) { + try { + innerAddHistory(entity) + npub?.let { + Amber.instance.getDatabase(npub).applicationDao().updateLastUsed(entity.pkKey, entity.time) + } + } catch (e: Exception) { + Log.e(Amber.TAG, "Error adding history", e) + } + } +} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDatabase.kt b/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDatabase.kt new file mode 100644 index 00000000..42cc83c7 --- /dev/null +++ b/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryDatabase.kt @@ -0,0 +1,44 @@ +package com.greenart7c3.nostrsigner.database + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import java.util.concurrent.Executors + +@Database( + entities = [ + HistoryEntity::class, + ], + version = 1, +) +@TypeConverters(Converters::class) +abstract class HistoryDatabase : RoomDatabase() { + abstract fun dao(): HistoryDao + + companion object { + fun getDatabase( + context: Context, + npub: String, + ): HistoryDatabase { + return synchronized(this) { + val executor = Executors.newCachedThreadPool() + val transactionExecutor = Executors.newCachedThreadPool() + + val instance = + Room.databaseBuilder( + context, + HistoryDatabase::class.java, + "history_db_$npub", + ) + .setQueryExecutor(executor) + .setTransactionExecutor(transactionExecutor) + .build() + instance.openHelper.writableDatabase.execSQL("VACUUM") + + instance + } + } + } +} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryEntity.kt b/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryEntity.kt index 9c33d6f7..87937e6d 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryEntity.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/database/HistoryEntity.kt @@ -5,15 +5,15 @@ import androidx.room.Index import androidx.room.PrimaryKey @Entity( - tableName = "history2", + tableName = "history", indices = [ Index( value = ["pkKey"], - name = "history_by_pk_key2", + name = "history_by_pk_key", ), Index( value = ["id"], - name = "history_by_id2", + name = "history_by_id", ), Index( value = ["time"], diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/BunkerRequestUtils.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/BunkerRequestUtils.kt index 13f0bd68..64ce1bc2 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/BunkerRequestUtils.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/BunkerRequestUtils.kt @@ -239,11 +239,12 @@ object BunkerRequestUtils { onLoading(true) Amber.instance.applicationIOScope.launch { val database = Amber.instance.getDatabase(account.npub) + val historyDatabase = Amber.instance.getHistoryDatabase(account.npub) val defaultRelays = Amber.instance.settings.defaultRelays if (oldKey.isNotBlank()) { database.applicationDao().delete(oldKey) - database.applicationDao().deleteHistory(oldKey) + historyDatabase.dao().deleteHistory(oldKey) } var savedApplication = database.applicationDao().getByKey(key) @@ -350,7 +351,7 @@ object BunkerRequestUtils { // assume that everything worked and try to revert it if it fails EventNotificationConsumer(context).notificationManager().cancelAll() database.applicationDao().insertApplicationWithPermissions(application) - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, key, @@ -359,6 +360,7 @@ object BunkerRequestUtils { TimeUtils.now(), true, ), + account.npub, ) if (didChangeRelays) { Amber.instance.checkForNewRelaysAndUpdateAllFilters(true) @@ -448,7 +450,7 @@ object BunkerRequestUtils { if (bunkerRequest.request !is BunkerRequestConnect) { Amber.instance.getDatabase(account.npub).applicationDao().insertApplicationWithPermissions(application) - Amber.instance.getDatabase(account.npub).applicationDao().addHistory( + Amber.instance.getHistoryDatabase(account.npub).dao().addHistory( HistoryEntity( 0, key, @@ -457,6 +459,7 @@ object BunkerRequestUtils { TimeUtils.now(), false, ), + account.npub, ) } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/ClearLogsWorker.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/ClearLogsWorker.kt index 1703cadb..5ff6e3d2 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/ClearLogsWorker.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/ClearLogsWorker.kt @@ -15,43 +15,42 @@ class ClearLogsWorker(appContext: Context, workerParams: WorkerParameters) : override suspend fun doWork(): Result { LocalPreferences.allSavedAccounts(Amber.instance).forEach { - Amber.instance.getDatabase(it.npub).let { database -> - try { - val oneWeek = System.currentTimeMillis() - (ONE_WEEK * 1000L) - val oneWeekAgo = TimeUtils.oneWeekAgo() - val countHistory = database.applicationDao().countOldHistory(oneWeekAgo) - Log.d(Amber.TAG, "Deleting $countHistory old history entries") - if (countHistory > 0) { - var logs = database.applicationDao().getOldHistory(oneWeekAgo) - var count = 0 - while (logs.isNotEmpty()) { - count++ - logs.forEach { history -> - database.applicationDao().deleteHistory(history) - } - logs = database.applicationDao().getOldHistory(oneWeekAgo) + try { + val oneWeek = System.currentTimeMillis() - (ONE_WEEK * 1000L) + val oneWeekAgo = TimeUtils.oneWeekAgo() + val historyDatabase = Amber.instance.getHistoryDatabase(it.npub) + val countHistory = historyDatabase.dao().countOldHistory(oneWeekAgo) + Log.d(Amber.TAG, "Deleting $countHistory old history entries") + if (countHistory > 0) { + var logs = historyDatabase.dao().getOldHistory(oneWeekAgo) + var count = 0 + while (logs.isNotEmpty()) { + count++ + logs.forEach { history -> + historyDatabase.dao().deleteHistory(history) } + logs = historyDatabase.dao().getOldHistory(oneWeekAgo) } - - val logDatabase = Amber.instance.getLogDatabase(it.npub) - val countLog = logDatabase.logDao().countOldLog(oneWeek) - Log.d(Amber.TAG, "Deleting $countLog old log entries from ${com.greenart7c3.nostrsigner.models.TimeUtils.formatLongToCustomDateTimeWithSeconds(oneWeek)}") - if (countLog > 0) { - var logs = logDatabase.logDao().getOldLog(oneWeek) - var count = 0 - while (logs.isNotEmpty()) { - count++ - logs.forEach { history -> - Log.d(Amber.TAG, "Deleting log entry ${com.greenart7c3.nostrsigner.models.TimeUtils.formatLongToCustomDateTimeWithSeconds(history.time)}") - logDatabase.logDao().deleteLog(history) - } - logs = logDatabase.logDao().getOldLog(oneWeek) - } - } - } catch (e: Exception) { - if (e is CancellationException) throw e - Log.e(Amber.TAG, "Error deleting old log entries", e) } + + val logDatabase = Amber.instance.getLogDatabase(it.npub) + val countLog = logDatabase.logDao().countOldLog(oneWeek) + Log.d(Amber.TAG, "Deleting $countLog old log entries from ${com.greenart7c3.nostrsigner.models.TimeUtils.formatLongToCustomDateTimeWithSeconds(oneWeek)}") + if (countLog > 0) { + var logs = logDatabase.logDao().getOldLog(oneWeek) + var count = 0 + while (logs.isNotEmpty()) { + count++ + logs.forEach { history -> + Log.d(Amber.TAG, "Deleting log entry ${com.greenart7c3.nostrsigner.models.TimeUtils.formatLongToCustomDateTimeWithSeconds(history.time)}") + logDatabase.logDao().deleteLog(history) + } + logs = logDatabase.logDao().getOldLog(oneWeek) + } + } + } catch (e: Exception) { + if (e is CancellationException) throw e + Log.e(Amber.TAG, "Error deleting old log entries", e) } } return Result.success() diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt index 3e890534..9a8740c1 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt @@ -150,6 +150,7 @@ class EventNotificationConsumer(private val applicationContext: Context) { val database = Amber.instance.getDatabase(acc.npub) val dao = database.applicationDao() val logDao = Amber.instance.getLogDatabase(acc.npub).logDao() + val historyDao = Amber.instance.getHistoryDatabase(acc.npub).dao() val notification = Amber.instance.notificationCache[event.id] if (notification != null) return @@ -237,7 +238,7 @@ class EventNotificationConsumer(private val applicationContext: Context) { val permission = dao.getByKey(event.pubKey) if (permission != null && ((permission.application.secret != permission.application.key && permission.application.useSecret) || permission.application.isConnected) && type == SignerType.CONNECT) { Amber.instance.applicationIOScope.launch { - dao + historyDao .addHistory( HistoryEntity( 0, @@ -247,6 +248,7 @@ class EventNotificationConsumer(private val applicationContext: Context) { TimeUtils.now(), true, ), + acc.npub, ) } BunkerRequestUtils.sendBunkerResponse( diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt index 580b1e1b..c5f0e457 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt @@ -567,6 +567,7 @@ object IntentUtils { onLoading(true) Amber.instance.applicationIOScope.launch { val database = Amber.instance.getDatabase(account.npub) + val historyDatabase = Amber.instance.getHistoryDatabase(account.npub) val defaultRelays = Amber.instance.settings.defaultRelays val savedApplication = database.applicationDao().getByKey(key) val relays = savedApplication?.application?.relays?.ifEmpty { defaultRelays } ?: defaultRelays @@ -637,7 +638,7 @@ object IntentUtils { if (packageName != null) { database.applicationDao().insertApplicationWithPermissions(application) - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, key, @@ -646,6 +647,7 @@ object IntentUtils { TimeUtils.now(), true, ), + account.npub, ) val intent = Intent() @@ -777,7 +779,7 @@ object IntentUtils { } Amber.instance.getDatabase(account.npub).applicationDao().insertApplicationWithPermissions(application) - Amber.instance.getDatabase(account.npub).applicationDao().addHistory( + Amber.instance.getHistoryDatabase(account.npub).dao().addHistory( HistoryEntity( 0, key, @@ -786,6 +788,7 @@ object IntentUtils { TimeUtils.now(), false, ), + account.npub, ) val activity = Amber.instance.getMainActivity() diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/ActivitiesScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/ActivitiesScreen.kt index 5a5c6610..d675ca4e 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/ActivitiesScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/ActivitiesScreen.kt @@ -61,8 +61,8 @@ fun ActivitiesScreen( topPadding: Dp, account: Account, ) { - val database = Amber.instance.getDatabase(account.npub) - val activities = database.applicationDao().getAllHistory().collectAsStateWithLifecycle(emptyList()) + val database = Amber.instance.getHistoryDatabase(account.npub) + val activities = database.dao().getAllHistory().collectAsStateWithLifecycle(emptyList()) val context = LocalContext.current // State for the search query var searchQuery by remember { mutableStateOf("") } @@ -133,7 +133,7 @@ fun ActivitiesScreen( ApplicationName( key = activity.pkKey, accepted = activity.accepted, - database = database, + database = Amber.instance.getDatabase(account.npub), account = account, ) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/EditConfigurationScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/EditConfigurationScreen.kt index 7b24f2aa..491e00d4 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/EditConfigurationScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/EditConfigurationScreen.kt @@ -318,7 +318,7 @@ fun EditConfigurationScreen( application?.let { scope.launch(Dispatchers.IO) { Amber.instance.getDatabase(account.npub).applicationDao().delete(it.application) - Amber.instance.getDatabase(account.npub).applicationDao().deleteHistory(it.application.key) + Amber.instance.getHistoryDatabase(account.npub).dao().deleteHistory(it.application.key) scope.launch(Dispatchers.Main) { navController.navigate(Route.Applications.route) { diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/SettingsScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/SettingsScreen.kt index 90ba20da..09c24d58 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/SettingsScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/SettingsScreen.kt @@ -270,55 +270,54 @@ fun SettingsScreen( Amber.instance.applicationIOScope.launch { isLoading = true LocalPreferences.allSavedAccounts(Amber.instance).forEach { - Amber.instance.getDatabase(it.npub).let { database -> - try { - status = context.getString(R.string.deleting_old_log_entries_from, it.npub) - val oneWeek = System.currentTimeMillis() - (ONE_WEEK * 1000L) - val oneWeekAgo = TimeUtils.oneWeekAgo() - val countHistory = database.applicationDao().countOldHistory(oneWeekAgo) - if (countHistory > 0) { - status = context.getString(R.string.deleting_old_history_entries, countHistory) - var logs = database.applicationDao().getOldHistory(oneWeekAgo) - var count = 0 - while (logs.isNotEmpty()) { - count++ - status = context.getString(R.string.deleting_old_history_entries_2, 100 * count, countHistory) - logs.forEach { history -> - database.applicationDao().deleteHistory(history) - } - logs = database.applicationDao().getOldHistory(oneWeekAgo) + try { + status = context.getString(R.string.deleting_old_log_entries_from, it.npub) + val oneWeek = System.currentTimeMillis() - (ONE_WEEK * 1000L) + val oneWeekAgo = TimeUtils.oneWeekAgo() + val historyDatabase = Amber.instance.getHistoryDatabase(it.npub) + val countHistory = historyDatabase.dao().countOldHistory(oneWeekAgo) + if (countHistory > 0) { + status = context.getString(R.string.deleting_old_history_entries, countHistory) + var logs = historyDatabase.dao().getOldHistory(oneWeekAgo) + var count = 0 + while (logs.isNotEmpty()) { + count++ + status = context.getString(R.string.deleting_old_history_entries_2, 100 * count, countHistory) + logs.forEach { history -> + historyDatabase.dao().deleteHistory(history) } + logs = historyDatabase.dao().getOldHistory(oneWeekAgo) } - - val logDatabase = Amber.instance.getLogDatabase(it.npub) - val countLog = logDatabase.logDao().countOldLog(oneWeek) - if (countLog > 0) { - status = context.getString(R.string.deleting_old_log_entries, countLog) - var logs = logDatabase.logDao().getOldLog(oneWeek) - var count = 0 - while (logs.isNotEmpty()) { - count++ - status = context.getString(R.string.deleting_old_log_entries_2, 100 * count, countLog) - logs.forEach { history -> - logDatabase.logDao().deleteLog(history) - } - logs = logDatabase.logDao().getOldLog(oneWeek) - } - } - val dbFile = context.getDatabasePath("amber_db_${account.npub}") - val df = DecimalFormat("#.###") - sizeInMBFormatted = df.format(dbFile.length() / (1024.0 * 1024.0)) - status = "" - isLoading = false - } catch (e: Exception) { - isLoading = false - if (e is CancellationException) throw e - Log.e(Amber.TAG, "Error deleting old log entries", e) - val dbFile = context.getDatabasePath("amber_db_${account.npub}") - val df = DecimalFormat("#.###") - sizeInMBFormatted = df.format(dbFile.length() / (1024.0 * 1024.0)) - status = "" } + + val logDatabase = Amber.instance.getLogDatabase(it.npub) + val countLog = logDatabase.logDao().countOldLog(oneWeek) + if (countLog > 0) { + status = context.getString(R.string.deleting_old_log_entries, countLog) + var logs = logDatabase.logDao().getOldLog(oneWeek) + var count = 0 + while (logs.isNotEmpty()) { + count++ + status = context.getString(R.string.deleting_old_log_entries_2, 100 * count, countLog) + logs.forEach { history -> + logDatabase.logDao().deleteLog(history) + } + logs = logDatabase.logDao().getOldLog(oneWeek) + } + } + val dbFile = context.getDatabasePath("amber_db_${account.npub}") + val df = DecimalFormat("#.###") + sizeInMBFormatted = df.format(dbFile.length() / (1024.0 * 1024.0)) + status = "" + isLoading = false + } catch (e: Exception) { + isLoading = false + if (e is CancellationException) throw e + Log.e(Amber.TAG, "Error deleting old log entries", e) + val dbFile = context.getDatabasePath("amber_db_${account.npub}") + val df = DecimalFormat("#.###") + sizeInMBFormatted = df.format(dbFile.length() / (1024.0 * 1024.0)) + status = "" } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/ActivityScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/ActivityScreen.kt index 5d0c0b93..0b1b1cdf 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/ActivityScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/ActivityScreen.kt @@ -56,7 +56,7 @@ fun ActivityScreen( account: Account, key: String, ) { - val activities = Amber.instance.getDatabase(account.npub).applicationDao().getAllHistory(key).collectAsStateWithLifecycle(emptyList()) + val activities = Amber.instance.getHistoryDatabase(account.npub).dao().getAllHistory(key).collectAsStateWithLifecycle(emptyList()) val context = LocalContext.current // State for the search query var searchQuery by remember { mutableStateOf("") } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt index 85194b33..5ef8e4a7 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt @@ -224,6 +224,7 @@ fun IntentMultiEventHomeScreen( val localKey = packageName ?: continue val database = Amber.instance.getDatabase(thisAccount.npub) + val historyDatabase = Amber.instance.getHistoryDatabase(thisAccount.npub) val savedApplication = database.applicationDao().getByKey(localKey) val application = @@ -268,7 +269,7 @@ fun IntentMultiEventHomeScreen( database.applicationDao().insertApplicationWithPermissions(application) - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, localKey, @@ -277,6 +278,7 @@ fun IntentMultiEventHomeScreen( TimeUtils.now(), intentData.checked.value, ), + thisAccount.npub, ) if (intentData.checked.value) { @@ -320,7 +322,7 @@ fun IntentMultiEventHomeScreen( } database.applicationDao().insertApplicationWithPermissions(application) - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, localKey, @@ -329,6 +331,7 @@ fun IntentMultiEventHomeScreen( TimeUtils.now(), intentData.checked.value, ), + thisAccount.npub, ) val signedMessage = signString(intentData.data, thisAccount.signer.keyPair.privKey!!).toHexKey() @@ -358,7 +361,7 @@ fun IntentMultiEventHomeScreen( database.applicationDao().insertApplicationWithPermissions(application) - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, localKey, @@ -367,6 +370,7 @@ fun IntentMultiEventHomeScreen( TimeUtils.now(), intentData.checked.value, ), + thisAccount.npub, ) val signature = intentData.encryptedData ?: continue @@ -592,6 +596,7 @@ fun BunkerMultiEventHomeScreen( val localKey = request.localKey val database = Amber.instance.getDatabase(thisAccount.npub) + val historyDatabase = Amber.instance.getHistoryDatabase(thisAccount.npub) val savedApplication = database.applicationDao().getByKey(localKey) val secret = if (request.request is BunkerRequestConnect) { @@ -642,7 +647,7 @@ fun BunkerMultiEventHomeScreen( database.applicationDao().insertApplicationWithPermissions(application) - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( entity = HistoryEntity( id = 0, pkKey = localKey, @@ -651,6 +656,7 @@ fun BunkerMultiEventHomeScreen( time = TimeUtils.now(), accepted = request.checked.value, ), + thisAccount.npub, ) val localBunkerRequest = request.copy() @@ -694,7 +700,7 @@ fun BunkerMultiEventHomeScreen( } database.applicationDao().insertApplicationWithPermissions(application) - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, localKey, @@ -703,6 +709,7 @@ fun BunkerMultiEventHomeScreen( TimeUtils.now(), request.checked.value, ), + thisAccount.npub, ) val signedMessage = signString(request.request.params.first(), thisAccount.signer.keyPair.privKey!!).toHexKey() @@ -737,7 +744,7 @@ fun BunkerMultiEventHomeScreen( if (savedApplication == null) { database.applicationDao().insertApplicationWithPermissions(application) - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, localKey, @@ -746,6 +753,7 @@ fun BunkerMultiEventHomeScreen( TimeUtils.now(), request.checked.value, ), + thisAccount.npub, ) val localBunkerRequest = request.copy() @@ -791,7 +799,7 @@ fun BunkerMultiEventHomeScreen( database.applicationDao().insertApplicationWithPermissions(application) - database.applicationDao().addHistory( + historyDatabase.dao().addHistory( HistoryEntity( 0, localKey, @@ -800,6 +808,7 @@ fun BunkerMultiEventHomeScreen( TimeUtils.now(), request.checked.value, ), + thisAccount.npub, ) val signature = request.encryptDecryptResponse ?: continue