refactor: Remove year old Room->Sqldelight db migration

This commit is contained in:
Artem Chepurnyi 2024-10-01 20:13:11 +03:00
parent 1042d60af2
commit 16e02dee82
24 changed files with 2 additions and 836 deletions

View File

@ -3,17 +3,11 @@ package com.artemchep.keyguard.android.downloader.journal.room
import android.content.Context
import android.database.SQLException
import androidx.room.Room
import androidx.room.withTransaction
import com.artemchep.keyguard.common.io.IO
import com.artemchep.keyguard.common.io.bind
import com.artemchep.keyguard.common.io.effectMap
import com.artemchep.keyguard.common.io.ioEffect
import com.artemchep.keyguard.common.io.retry
import com.artemchep.keyguard.common.io.shared
import com.artemchep.keyguard.common.usecase.DeviceEncryptionKeyUseCase
import com.artemchep.keyguard.data.Database
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import net.zetetic.database.sqlcipher.SupportOpenHelperFactory
@ -70,27 +64,4 @@ class DownloadDatabaseManager(
block(db)
}
}
fun migrateIfExists(
sqld: Database,
): IO<Unit> = ioEffect(Dispatchers.IO) {
val room = dbIo.bind()
// Nothing to migrate.
?: return@ioEffect
room.withTransaction {
val generatorHistory = room.generatorDao().getAll().first()
sqld.transaction {
generatorHistory.forEach { item ->
sqld.generatorHistoryQueries.insert(
value_ = item.value,
createdAt = item.createdDate,
isPassword = item.isPassword,
isUsername = item.isUsername,
isEmailRelay = false,
)
}
}
room.generatorDao().removeAll()
}
}
}

View File

@ -8,12 +8,11 @@ import app.cash.sqldelight.db.AfterVersion
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
import com.artemchep.keyguard.android.downloader.ExportManagerImpl
import com.artemchep.keyguard.android.downloader.journal.room.DownloadDatabaseManager
import com.artemchep.keyguard.common.NotificationsWorker
import com.artemchep.keyguard.common.io.IO
import com.artemchep.keyguard.common.io.bind
import com.artemchep.keyguard.common.io.flatMap
import com.artemchep.keyguard.common.io.ioEffect
import com.artemchep.keyguard.common.io.ioUnit
import com.artemchep.keyguard.common.model.MasterKey
import com.artemchep.keyguard.common.service.export.ExportManager
import com.artemchep.keyguard.common.usecase.QueueSyncAll
@ -21,14 +20,11 @@ import com.artemchep.keyguard.common.usecase.QueueSyncById
import com.artemchep.keyguard.copy.QueueSyncAllAndroid
import com.artemchep.keyguard.copy.QueueSyncByIdAndroid
import com.artemchep.keyguard.core.store.DatabaseManager
import com.artemchep.keyguard.core.store.DatabaseManagerAndroid
import com.artemchep.keyguard.core.store.DatabaseManagerImpl
import com.artemchep.keyguard.core.store.SqlHelper
import com.artemchep.keyguard.core.store.SqlManager
import com.artemchep.keyguard.data.Database
import com.artemchep.keyguard.feature.crashlytics.crashlyticsTap
import com.artemchep.keyguard.provider.bitwarden.usecase.NotificationsImpl
import com.artemchep.keyguard.room.RoomMigrationException
import net.zetetic.database.sqlcipher.SQLiteDatabase
import net.zetetic.database.sqlcipher.SupportOpenHelperFactory
import org.kodein.di.DI
@ -56,26 +52,10 @@ actual fun DI.Builder.createSubDi(
NotificationsImpl(this)
}
bindSingleton<DatabaseManager> {
val downloadDb = instance<DownloadDatabaseManager>()
val roomDb = DatabaseManagerAndroid(
applicationContext = instance(),
json = instance(),
name = "database",
masterKey = masterKey,
)
val sqlManager: SqlManager = SqlManagerFile2(
context = instance<Application>(),
onCreate = { database ->
roomDb
.migrateIfExists(database)
.flatMap {
downloadDb.migrateIfExists(database)
}
// Log this exception, it's important to see how the migration
// process goes. It's okay if it fails, but not great.
.crashlyticsTap { e ->
RoomMigrationException(e)
}
ioUnit()
},
)

View File

@ -1,312 +0,0 @@
package com.artemchep.keyguard.core.store
import android.content.Context
import android.database.SQLException
import android.util.Log
import androidx.room.Room
import com.artemchep.keyguard.common.io.IO
import com.artemchep.keyguard.common.io.bind
import com.artemchep.keyguard.common.io.effectMap
import com.artemchep.keyguard.common.io.io
import com.artemchep.keyguard.common.io.ioEffect
import com.artemchep.keyguard.common.io.retry
import com.artemchep.keyguard.common.io.shared
import com.artemchep.keyguard.common.model.CipherHistoryType
import com.artemchep.keyguard.common.model.MasterKey
import com.artemchep.keyguard.data.Database
import com.artemchep.keyguard.room.AppDatabase
import com.artemchep.keyguard.room.RoomSecretConverter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.serialization.json.Json
import net.zetetic.database.sqlcipher.SupportOpenHelperFactory
class DatabaseManagerAndroid(
private val applicationContext: Context,
private val json: Json,
private val name: String,
masterKey: MasterKey,
) {
companion object {
private const val TAG = "DatabaseManager"
private val mutex = Mutex()
}
private val dbFileIo = ioEffect { applicationContext.getDatabasePath(name) }
.shared("dbFileIo")
private val dbIo = io(masterKey)
.effectMap { masterKey ->
val databaseFile = dbFileIo
.bind()
if (!databaseFile.exists()) {
return@effectMap null
}
// Create encrypted database using the provided master key. If the
// key is incorrect, trying to open the database would lead to a crash.
val factory = SupportOpenHelperFactory(masterKey.byteArray, null, false)
Room
.databaseBuilder(
applicationContext,
AppDatabase::class.java,
name,
)
.openHelperFactory(factory)
.addTypeConverter(RoomSecretConverter(json))
.build()
}
.retry { e, attempt ->
e.printStackTrace()
if (e is SQLException && attempt == 0) {
applicationContext.deleteDatabase(name)
return@retry true
}
false
}
.shared("dbIo")
fun migrateIfExists(
sqld: Database,
): IO<Unit> = ioEffect(Dispatchers.IO) {
val room = dbIo.bind()
// Nothing to migrate.
?: return@ioEffect
// account
MigrationLogger.log("Account") {
val accounts = room.accountDao().getAll().first()
sqld.transaction {
val accountIds = sqld.accountQueries
.getIds()
.executeAsList()
.toSet()
accounts.forEach { account ->
if (account.accountId in accountIds) {
ignored()
return@forEach
}
sqld.accountQueries.insert(
accountId = account.accountId,
data = account.content,
)
changed()
}
}
}
// profile
MigrationLogger.log("Profile") {
val profiles = room.profileDao().getAll().first()
sqld.transaction {
val profileIds = sqld.profileQueries
.getIds()
.executeAsList()
.toSet()
profiles.forEach { profile ->
if (profile.profileId in profileIds) {
ignored()
return@forEach
}
sqld.profileQueries.insert(
profileId = profile.profileId,
accountId = profile.accountId,
data = profile.content,
)
changed()
}
}
}
// meta
MigrationLogger.log("Meta") {
val metas = room.metaDao().getAll().first()
sqld.transaction {
val accountIds = sqld.metaQueries
.getIds()
.executeAsList()
.toSet()
metas.forEach { meta ->
if (meta.accountId in accountIds) {
ignored()
return@forEach
}
sqld.metaQueries.insert(
accountId = meta.accountId,
data = meta.content,
)
changed()
}
}
}
// folder
MigrationLogger.log("Folder") {
val folders = room.folderDao().getAll().first()
sqld.transaction {
val folderIds = sqld.folderQueries
.getIds()
.executeAsList()
.toSet()
folders.forEach { folder ->
if (folder.folderId in folderIds) {
ignored()
return@forEach
}
sqld.folderQueries.insert(
folderId = folder.folderId,
accountId = folder.accountId,
data = folder.content,
)
changed()
}
}
}
// org
MigrationLogger.log("Organization") {
val orgs = room.organizationDao().getAll().first()
sqld.transaction {
val organizationIds = sqld.organizationQueries
.getIds()
.executeAsList()
.toSet()
orgs.forEach { org ->
if (org.organizationId in organizationIds) {
ignored()
return@forEach
}
sqld.organizationQueries.insert(
organizationId = org.organizationId,
accountId = org.accountId,
data = org.content,
)
changed()
}
}
}
// collection
MigrationLogger.log("Collection") {
val collections = room.collectionDao().getAll().first()
sqld.transaction {
val collectionIds = sqld.collectionQueries
.getIds()
.executeAsList()
.toSet()
collections.forEach { collection ->
if (collection.collectionId in collectionIds) {
ignored()
return@forEach
}
sqld.collectionQueries.insert(
collectionId = collection.collectionId,
accountId = collection.accountId,
data = collection.content,
)
changed()
}
}
}
// cipher
MigrationLogger.log("Cipher") {
val ciphers = room.cipherDao().getAll().first()
sqld.transaction {
val cipherIds = sqld.cipherQueries
.getIds()
.executeAsList()
.toSet()
ciphers.forEach { cipher ->
if (cipher.itemId in cipherIds) {
ignored()
return@forEach
}
sqld.cipherQueries.insert(
cipherId = cipher.itemId,
accountId = cipher.accountId,
folderId = cipher.folderId,
data = cipher.content,
updatedAt = cipher.content.revisionDate,
)
changed()
}
}
}
// history
MigrationLogger.log("History") {
val openCiphers = room.openCipherHistoryDao().getAll().first()
val fillCiphers = room.fillCipherHistoryDao().getAll().first()
sqld.transaction {
val hasExistingHistory = sqld.cipherUsageHistoryQueries
.get(1)
.executeAsList()
.isNotEmpty()
if (hasExistingHistory) {
return@transaction
}
openCiphers.forEach { openCipher ->
sqld.cipherUsageHistoryQueries.insert(
cipherId = openCipher.itemId,
credentialId = null,
createdAt = openCipher.timestamp
.let(Instant::fromEpochMilliseconds),
type = CipherHistoryType.OPENED,
)
changed()
}
fillCiphers.forEach { fillCipher ->
sqld.cipherUsageHistoryQueries.insert(
cipherId = fillCipher.itemId,
credentialId = null,
createdAt = fillCipher.timestamp
.let(Instant::fromEpochMilliseconds),
type = CipherHistoryType.USED_AUTOFILL,
)
changed()
}
}
}
val dbFile = dbFileIo.bind()
dbFile.delete()
}
private class MigrationLogger(
private val tag: String,
) : MigrationLoggerScope {
companion object {
suspend fun log(
tag: String,
block: suspend MigrationLoggerScope.() -> Unit,
) {
val logger = MigrationLogger(tag)
block(logger)
logger.flush()
}
}
private val now = Clock.System.now()
var changed = 0
var ignored = 0
override fun changed() {
changed += 1
}
override fun ignored() {
ignored += 1
}
fun flush() {
val dt = Clock.System.now() - now
Log.i("MigrationLogger", "$tag: Changed $changed, ignored $ignored! It took $dt.")
}
}
interface MigrationLoggerScope {
fun changed()
fun ignored()
}
}

View File

@ -1,52 +0,0 @@
package com.artemchep.keyguard.room
import androidx.room.AutoMigration
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.artemchep.keyguard.room.dao.AccountDao
import com.artemchep.keyguard.room.dao.CipherDao
import com.artemchep.keyguard.room.dao.CollectionDao
import com.artemchep.keyguard.room.dao.FillCipherHistoryDao
import com.artemchep.keyguard.room.dao.FolderDao
import com.artemchep.keyguard.room.dao.MetaDao
import com.artemchep.keyguard.room.dao.OpenCipherHistoryDao
import com.artemchep.keyguard.room.dao.OrganizationDao
import com.artemchep.keyguard.room.dao.ProfileDao
@Database(
entities = [
RoomBitwardenCipher::class,
RoomBitwardenCollection::class,
RoomBitwardenOrganization::class,
RoomBitwardenFolder::class,
RoomBitwardenProfile::class,
RoomBitwardenMeta::class,
RoomBitwardenToken::class,
// history
RoomOpenCipherHistory::class,
RoomFillCipherHistory::class,
],
autoMigrations = [
AutoMigration(from = 1, to = 2),
AutoMigration(from = 2, to = 3),
AutoMigration(from = 3, to = 4),
AutoMigration(from = 4, to = 5),
AutoMigration(from = 5, to = 6),
AutoMigration(from = 6, to = 7),
],
version = 7,
exportSchema = true,
)
@TypeConverters(RoomSecretConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun cipherDao(): CipherDao
abstract fun collectionDao(): CollectionDao
abstract fun organizationDao(): OrganizationDao
abstract fun metaDao(): MetaDao
abstract fun folderDao(): FolderDao
abstract fun profileDao(): ProfileDao
abstract fun accountDao(): AccountDao
abstract fun fillCipherHistoryDao(): FillCipherHistoryDao
abstract fun openCipherHistoryDao(): OpenCipherHistoryDao
}

View File

@ -1,26 +0,0 @@
package com.artemchep.keyguard.room
import androidx.room.Entity
import androidx.room.ForeignKey
import com.artemchep.keyguard.core.store.bitwarden.BitwardenCipher
@Entity(
tableName = "RoomBitwardenCipher",
primaryKeys = [
"itemId",
],
foreignKeys = [
ForeignKey(
entity = RoomBitwardenToken::class,
parentColumns = arrayOf("accountId"),
childColumns = arrayOf("accountId"),
onDelete = ForeignKey.CASCADE,
),
],
)
data class RoomBitwardenCipher(
val itemId: String,
val accountId: String,
val folderId: String?,
val content: BitwardenCipher,
)

View File

@ -1,26 +0,0 @@
package com.artemchep.keyguard.room
import androidx.room.Entity
import androidx.room.ForeignKey
import com.artemchep.keyguard.core.store.bitwarden.BitwardenCollection
@Entity(
tableName = "RoomBitwardenCollection",
primaryKeys = [
"collectionId",
"accountId",
],
foreignKeys = [
ForeignKey(
entity = RoomBitwardenToken::class,
parentColumns = arrayOf("accountId"),
childColumns = arrayOf("accountId"),
onDelete = ForeignKey.CASCADE,
),
],
)
data class RoomBitwardenCollection(
val collectionId: String,
val accountId: String,
val content: BitwardenCollection,
)

View File

@ -1,25 +0,0 @@
package com.artemchep.keyguard.room
import androidx.room.Entity
import androidx.room.ForeignKey
import com.artemchep.keyguard.core.store.bitwarden.BitwardenFolder
@Entity(
tableName = "RoomBitwardenFolder",
primaryKeys = [
"folderId",
],
foreignKeys = [
ForeignKey(
entity = RoomBitwardenToken::class,
parentColumns = arrayOf("accountId"),
childColumns = arrayOf("accountId"),
onDelete = ForeignKey.CASCADE,
),
],
)
data class RoomBitwardenFolder(
val folderId: String,
val accountId: String,
val content: BitwardenFolder,
)

View File

@ -1,24 +0,0 @@
package com.artemchep.keyguard.room
import androidx.room.Entity
import androidx.room.ForeignKey
import com.artemchep.keyguard.core.store.bitwarden.BitwardenMeta
@Entity(
tableName = "RoomBitwardenMeta",
primaryKeys = [
"accountId",
],
foreignKeys = [
ForeignKey(
entity = RoomBitwardenToken::class,
parentColumns = arrayOf("accountId"),
childColumns = arrayOf("accountId"),
onDelete = ForeignKey.CASCADE,
),
],
)
data class RoomBitwardenMeta(
val accountId: String,
val content: BitwardenMeta,
)

View File

@ -1,26 +0,0 @@
package com.artemchep.keyguard.room
import androidx.room.Entity
import androidx.room.ForeignKey
import com.artemchep.keyguard.core.store.bitwarden.BitwardenOrganization
@Entity(
tableName = "RoomBitwardenOrganization",
primaryKeys = [
"organizationId",
"accountId",
],
foreignKeys = [
ForeignKey(
entity = RoomBitwardenToken::class,
parentColumns = arrayOf("accountId"),
childColumns = arrayOf("accountId"),
onDelete = ForeignKey.CASCADE,
),
],
)
data class RoomBitwardenOrganization(
val organizationId: String,
val accountId: String,
val content: BitwardenOrganization,
)

View File

@ -1,26 +0,0 @@
package com.artemchep.keyguard.room
import androidx.room.Entity
import androidx.room.ForeignKey
import com.artemchep.keyguard.core.store.bitwarden.BitwardenProfile
@Entity(
tableName = "RoomBitwardenProfile",
primaryKeys = [
"profileId",
"accountId",
],
foreignKeys = [
ForeignKey(
entity = RoomBitwardenToken::class,
parentColumns = arrayOf("accountId"),
childColumns = arrayOf("accountId"),
onDelete = ForeignKey.CASCADE,
),
],
)
data class RoomBitwardenProfile(
val profileId: String,
val accountId: String,
val content: BitwardenProfile,
)

View File

@ -1,15 +0,0 @@
package com.artemchep.keyguard.room
import androidx.room.Entity
import com.artemchep.keyguard.core.store.bitwarden.BitwardenToken
@Entity(
tableName = "RoomBitwardenToken",
primaryKeys = [
"accountId",
],
)
data class RoomBitwardenToken(
val accountId: String,
val content: BitwardenToken,
)

View File

@ -1,30 +0,0 @@
package com.artemchep.keyguard.room
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
tableName = "RoomFillCipherHistory",
foreignKeys = [
ForeignKey(
entity = RoomBitwardenToken::class,
parentColumns = arrayOf("accountId"),
childColumns = arrayOf("accountId"),
onDelete = ForeignKey.CASCADE,
),
ForeignKey(
entity = RoomBitwardenCipher::class,
parentColumns = arrayOf("itemId"),
childColumns = arrayOf("itemId"),
onDelete = ForeignKey.CASCADE,
),
],
)
data class RoomFillCipherHistory(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
val itemId: String,
val accountId: String,
val timestamp: Long,
)

View File

@ -1,5 +0,0 @@
package com.artemchep.keyguard.room
class RoomMigrationException(
e: Throwable,
) : RuntimeException(e)

View File

@ -1,30 +0,0 @@
package com.artemchep.keyguard.room
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
tableName = "RoomOpenCipherHistory",
foreignKeys = [
ForeignKey(
entity = RoomBitwardenToken::class,
parentColumns = arrayOf("accountId"),
childColumns = arrayOf("accountId"),
onDelete = ForeignKey.CASCADE,
),
ForeignKey(
entity = RoomBitwardenCipher::class,
parentColumns = arrayOf("itemId"),
childColumns = arrayOf("itemId"),
onDelete = ForeignKey.CASCADE,
),
],
)
data class RoomOpenCipherHistory(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
val itemId: String,
val accountId: String,
val timestamp: Long,
)

View File

@ -1,80 +0,0 @@
package com.artemchep.keyguard.room
import androidx.room.ProvidedTypeConverter
import androidx.room.TypeConverter
import com.artemchep.keyguard.core.store.bitwarden.BitwardenCipher
import com.artemchep.keyguard.core.store.bitwarden.BitwardenCollection
import com.artemchep.keyguard.core.store.bitwarden.BitwardenFolder
import com.artemchep.keyguard.core.store.bitwarden.BitwardenMeta
import com.artemchep.keyguard.core.store.bitwarden.BitwardenOrganization
import com.artemchep.keyguard.core.store.bitwarden.BitwardenProfile
import com.artemchep.keyguard.core.store.bitwarden.BitwardenToken
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@ProvidedTypeConverter
class RoomSecretConverter(
private val json: Json,
) {
@TypeConverter
fun encodeToken(model: BitwardenToken?) = encode(model)
@TypeConverter
fun encodeCipher(model: BitwardenCipher?) = encode(model)
@TypeConverter
fun encodeCollection(model: BitwardenCollection?) = encode(model)
@TypeConverter
fun encodeFolder(model: BitwardenFolder?) = encode(model)
@TypeConverter
fun encodeMeta(model: BitwardenMeta?) = encode(model)
@TypeConverter
fun encodeProfile(model: BitwardenProfile?) = encode(model)
@TypeConverter
fun encodeOrganization(model: BitwardenOrganization?) = encode(model)
private inline fun <reified T : Any> encode(model: T?): String? {
if (model == null) {
// We can not serialize null models, so just
// write them as null.
return null
}
return json.encodeToString(model)
}
@TypeConverter
fun decodeToken(text: String?): BitwardenToken? = decode(text)
@TypeConverter
fun decodeCipher(text: String?): BitwardenCipher? = decode(text)
@TypeConverter
fun decodeCollection(text: String?): BitwardenCollection? = decode(text)
@TypeConverter
fun decodeFolder(text: String?): BitwardenFolder? = decode(text)
@TypeConverter
fun decodeMeta(text: String?): BitwardenMeta? = decode(text)
@TypeConverter
fun decodeProfile(text: String?): BitwardenProfile? = decode(text)
@TypeConverter
fun decodeOrganization(text: String?): BitwardenOrganization? = decode(text)
private inline fun <reified T : Any> decode(text: String?): T? {
if (text == null) {
// We can not serialize null models, so just
// write them as null.
return null
}
return json.decodeFromString<T>(text)
}
}

View File

@ -1,12 +0,0 @@
package com.artemchep.keyguard.room.dao
import androidx.room.Dao
import androidx.room.Query
import com.artemchep.keyguard.room.RoomBitwardenToken
import kotlinx.coroutines.flow.Flow
@Dao
interface AccountDao {
@Query("SELECT * FROM RoomBitwardenToken")
fun getAll(): Flow<List<RoomBitwardenToken>>
}

View File

@ -1,12 +0,0 @@
package com.artemchep.keyguard.room.dao
import androidx.room.Dao
import androidx.room.Query
import com.artemchep.keyguard.room.RoomBitwardenCipher
import kotlinx.coroutines.flow.Flow
@Dao
interface CipherDao {
@Query("SELECT * FROM RoomBitwardenCipher")
fun getAll(): Flow<List<RoomBitwardenCipher>>
}

View File

@ -1,12 +0,0 @@
package com.artemchep.keyguard.room.dao
import androidx.room.Dao
import androidx.room.Query
import com.artemchep.keyguard.room.RoomBitwardenCollection
import kotlinx.coroutines.flow.Flow
@Dao
interface CollectionDao {
@Query("SELECT * FROM RoomBitwardenCollection")
fun getAll(): Flow<List<RoomBitwardenCollection>>
}

View File

@ -1,12 +0,0 @@
package com.artemchep.keyguard.room.dao
import androidx.room.Dao
import androidx.room.Query
import com.artemchep.keyguard.room.RoomFillCipherHistory
import kotlinx.coroutines.flow.Flow
@Dao
interface FillCipherHistoryDao {
@Query("SELECT * FROM RoomFillCipherHistory")
fun getAll(): Flow<List<RoomFillCipherHistory>>
}

View File

@ -1,12 +0,0 @@
package com.artemchep.keyguard.room.dao
import androidx.room.Dao
import androidx.room.Query
import com.artemchep.keyguard.room.RoomBitwardenFolder
import kotlinx.coroutines.flow.Flow
@Dao
interface FolderDao {
@Query("SELECT * FROM RoomBitwardenFolder")
fun getAll(): Flow<List<RoomBitwardenFolder>>
}

View File

@ -1,12 +0,0 @@
package com.artemchep.keyguard.room.dao
import androidx.room.Dao
import androidx.room.Query
import com.artemchep.keyguard.room.RoomBitwardenMeta
import kotlinx.coroutines.flow.Flow
@Dao
interface MetaDao {
@Query("SELECT * FROM RoomBitwardenMeta")
fun getAll(): Flow<List<RoomBitwardenMeta>>
}

View File

@ -1,12 +0,0 @@
package com.artemchep.keyguard.room.dao
import androidx.room.Dao
import androidx.room.Query
import com.artemchep.keyguard.room.RoomOpenCipherHistory
import kotlinx.coroutines.flow.Flow
@Dao
interface OpenCipherHistoryDao {
@Query("SELECT * FROM RoomOpenCipherHistory ORDER BY timestamp DESC")
fun getAll(): Flow<List<RoomOpenCipherHistory>>
}

View File

@ -1,12 +0,0 @@
package com.artemchep.keyguard.room.dao
import androidx.room.Dao
import androidx.room.Query
import com.artemchep.keyguard.room.RoomBitwardenOrganization
import kotlinx.coroutines.flow.Flow
@Dao
interface OrganizationDao {
@Query("SELECT * FROM RoomBitwardenOrganization")
fun getAll(): Flow<List<RoomBitwardenOrganization>>
}

View File

@ -1,12 +0,0 @@
package com.artemchep.keyguard.room.dao
import androidx.room.Dao
import androidx.room.Query
import com.artemchep.keyguard.room.RoomBitwardenProfile
import kotlinx.coroutines.flow.Flow
@Dao
interface ProfileDao {
@Query("SELECT * FROM RoomBitwardenProfile")
fun getAll(): Flow<List<RoomBitwardenProfile>>
}