Skip to content

Commit 1e89c10

Browse files
committed
improvement: Remember previously used barcode format per item #1433
1 parent 516d54a commit 1e89c10

19 files changed

Lines changed: 523 additions & 36 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.artemchep.keyguard.android.downloader.journal
2+
3+
import com.artemchep.keyguard.common.io.IO
4+
import com.artemchep.keyguard.common.model.DBarcodeUsageHistory
5+
import com.artemchep.keyguard.provider.bitwarden.repository.BaseRepository
6+
import kotlinx.coroutines.flow.Flow
7+
8+
interface BarcodeUsageHistoryRepository : BaseRepository<DBarcodeUsageHistory> {
9+
fun getById(
10+
id: String,
11+
): Flow<DBarcodeUsageHistory?>
12+
13+
fun getRecent(
14+
limit: Long = 100L,
15+
): Flow<List<DBarcodeUsageHistory>>
16+
17+
fun removeAll(): IO<Unit>
18+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.artemchep.keyguard.android.downloader.journal
2+
3+
import app.cash.sqldelight.coroutines.asFlow
4+
import app.cash.sqldelight.coroutines.mapToOneOrNull
5+
import com.artemchep.keyguard.common.io.IO
6+
import com.artemchep.keyguard.common.io.effectMap
7+
import com.artemchep.keyguard.common.model.DBarcodeUsageHistory
8+
import com.artemchep.keyguard.common.service.database.DatabaseDispatcher
9+
import com.artemchep.keyguard.common.service.database.vault.VaultDatabaseManager
10+
import com.artemchep.keyguard.common.util.sqldelight.flatMapQueryToList
11+
import com.artemchep.keyguard.data.BarcodeUsageHistory
12+
import com.artemchep.keyguard.data.BarcodeUsageHistoryQueries
13+
import kotlinx.coroutines.CoroutineDispatcher
14+
import kotlinx.coroutines.ExperimentalCoroutinesApi
15+
import kotlinx.coroutines.flow.Flow
16+
import kotlinx.coroutines.flow.asFlow
17+
import kotlinx.coroutines.flow.flatMapLatest
18+
import kotlinx.coroutines.flow.map
19+
import org.kodein.di.DirectDI
20+
import org.kodein.di.instance
21+
22+
@OptIn(ExperimentalCoroutinesApi::class)
23+
class BarcodeUsageHistoryRepositoryImpl(
24+
private val databaseManager: VaultDatabaseManager,
25+
private val dispatcher: CoroutineDispatcher,
26+
) : BarcodeUsageHistoryRepository {
27+
companion object {
28+
private const val TAG = "BarcodeUsageHistoryRepository"
29+
}
30+
31+
constructor(
32+
directDI: DirectDI,
33+
) : this(
34+
databaseManager = directDI.instance(),
35+
dispatcher = directDI.instance(tag = DatabaseDispatcher),
36+
)
37+
38+
override fun get(): Flow<List<DBarcodeUsageHistory>> = getRecent()
39+
40+
override fun getById(
41+
id: String,
42+
): Flow<DBarcodeUsageHistory?> =
43+
daoEffect { dao ->
44+
dao.getById(id = id)
45+
}
46+
.asFlow()
47+
.flatMapLatest { query ->
48+
query
49+
.asFlow()
50+
.mapToOneOrNull(dispatcher)
51+
}
52+
.map { entity ->
53+
entity?.toDomain()
54+
}
55+
56+
override fun getRecent(
57+
limit: Long,
58+
): Flow<List<DBarcodeUsageHistory>> =
59+
daoEffect { dao ->
60+
dao.getRecent(limit = limit)
61+
}
62+
.flatMapQueryToList(dispatcher)
63+
.map { entities ->
64+
entities.map(BarcodeUsageHistory::toDomain)
65+
}
66+
67+
override fun put(model: DBarcodeUsageHistory): IO<Unit> =
68+
databaseManager.mutate(TAG) { db ->
69+
db.barcodeUsageHistoryQueries.upsert(
70+
id = model.id,
71+
type = model.type,
72+
createdAt = model.createdAt,
73+
)
74+
Unit
75+
}
76+
77+
override fun removeAll(): IO<Unit> =
78+
databaseManager.mutate(TAG) { db ->
79+
db.barcodeUsageHistoryQueries.deleteAll()
80+
Unit
81+
}
82+
83+
private inline fun <T> daoEffect(
84+
crossinline block: suspend (BarcodeUsageHistoryQueries) -> T,
85+
): IO<T> = databaseManager
86+
.get()
87+
.effectMap(dispatcher) { db ->
88+
block(db.barcodeUsageHistoryQueries)
89+
}
90+
}
91+
92+
private fun BarcodeUsageHistory.toDomain(): DBarcodeUsageHistory = DBarcodeUsageHistory(
93+
id = id,
94+
type = type,
95+
createdAt = createdAt,
96+
)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.artemchep.keyguard.common.model
2+
3+
import kotlin.time.Instant
4+
5+
data class DBarcodeUsageHistory(
6+
val id: String,
7+
val type: String,
8+
val createdAt: Instant,
9+
)

common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/database/vault/VaultDatabaseManagerImpl.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import com.artemchep.keyguard.core.store.bitwarden.BitwardenOrganization
2727
import com.artemchep.keyguard.core.store.bitwarden.BitwardenProfile
2828
import com.artemchep.keyguard.core.store.bitwarden.BitwardenSend
2929
import com.artemchep.keyguard.core.store.bitwarden.ServiceToken
30+
import com.artemchep.keyguard.data.BarcodeUsageHistory
3031
import com.artemchep.keyguard.data.CipherFilter
3132
import com.artemchep.keyguard.data.CipherUsageHistory
3233
import com.artemchep.keyguard.data.Database
@@ -90,6 +91,7 @@ class VaultDatabaseManagerImpl(
9091
val databaseFactory = { driver: SqlDriver ->
9192
Database(
9293
driver = driver,
94+
barcodeUsageHistoryAdapter = BarcodeUsageHistory.Adapter(InstantToLongAdapter),
9395
cipherUsageHistoryAdapter = CipherUsageHistory.Adapter(InstantToLongAdapter),
9496
sshUsageHistoryAdapter = SshUsageHistory.Adapter(
9597
requestAdapter = SshUsageHistoryRequestTypeToLongAdapter,
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.artemchep.keyguard.common.usecase
2+
3+
import com.artemchep.keyguard.common.model.DBarcodeUsageHistory
4+
import kotlinx.coroutines.flow.Flow
5+
6+
interface GetBarcodeUsageHistory : (String) -> Flow<DBarcodeUsageHistory?>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.artemchep.keyguard.common.usecase
2+
3+
import com.artemchep.keyguard.common.io.IO
4+
5+
interface PutBarcodeUsageHistory : (
6+
String,
7+
String,
8+
) -> IO<Unit>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.artemchep.keyguard.common.usecase.impl
2+
3+
import com.artemchep.keyguard.android.downloader.journal.BarcodeUsageHistoryRepository
4+
import com.artemchep.keyguard.common.usecase.GetBarcodeUsageHistory
5+
import org.kodein.di.DirectDI
6+
import org.kodein.di.instance
7+
8+
class GetBarcodeUsageHistoryImpl(
9+
private val barcodeUsageHistoryRepository: BarcodeUsageHistoryRepository,
10+
) : GetBarcodeUsageHistory {
11+
constructor(directDI: DirectDI) : this(
12+
barcodeUsageHistoryRepository = directDI.instance(),
13+
)
14+
15+
override fun invoke(id: String) = barcodeUsageHistoryRepository.getById(id)
16+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.artemchep.keyguard.common.usecase.impl
2+
3+
import com.artemchep.keyguard.android.downloader.journal.BarcodeUsageHistoryRepository
4+
import com.artemchep.keyguard.common.model.DBarcodeUsageHistory
5+
import com.artemchep.keyguard.common.usecase.PutBarcodeUsageHistory
6+
import org.kodein.di.DirectDI
7+
import org.kodein.di.instance
8+
import kotlin.time.Clock
9+
10+
class PutBarcodeUsageHistoryImpl(
11+
private val barcodeUsageHistoryRepository: BarcodeUsageHistoryRepository,
12+
) : PutBarcodeUsageHistory {
13+
constructor(directDI: DirectDI) : this(
14+
barcodeUsageHistoryRepository = directDI.instance(),
15+
)
16+
17+
override fun invoke(
18+
id: String,
19+
type: String,
20+
) = barcodeUsageHistoryRepository.put(
21+
DBarcodeUsageHistory(
22+
id = id,
23+
type = type,
24+
createdAt = Clock.System.now(),
25+
),
26+
)
27+
}

common/src/commonMain/kotlin/com/artemchep/keyguard/core/session/usecase/SubDI.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package com.artemchep.keyguard.core.session.usecase
22

33
import com.artemchep.keyguard.android.downloader.journal.CipherHistoryOpenedRepository
44
import com.artemchep.keyguard.android.downloader.journal.CipherHistoryOpenedRepositoryImpl
5+
import com.artemchep.keyguard.android.downloader.journal.BarcodeUsageHistoryRepository
6+
import com.artemchep.keyguard.android.downloader.journal.BarcodeUsageHistoryRepositoryImpl
57
import com.artemchep.keyguard.android.downloader.journal.GeneratorHistoryRepository
68
import com.artemchep.keyguard.android.downloader.journal.GeneratorHistoryRepositoryImpl
79
import com.artemchep.keyguard.android.downloader.journal.SshUsageHistoryRepository
@@ -99,6 +101,7 @@ import com.artemchep.keyguard.common.usecase.GetAccountHasError
99101
import com.artemchep.keyguard.common.usecase.GetAccountStatus
100102
import com.artemchep.keyguard.common.usecase.GetAccounts
101103
import com.artemchep.keyguard.common.usecase.GetAccountsHasError
104+
import com.artemchep.keyguard.common.usecase.GetBarcodeUsageHistory
102105
import com.artemchep.keyguard.common.usecase.GetBreaches
103106
import com.artemchep.keyguard.common.usecase.GetBreachesLatestDate
104107
import com.artemchep.keyguard.common.usecase.GetCanAddAccount
@@ -144,6 +147,7 @@ import com.artemchep.keyguard.common.usecase.PatchWatchtowerAlertCipher
144147
import com.artemchep.keyguard.common.usecase.PutAccountColorById
145148
import com.artemchep.keyguard.common.usecase.PutAccountMasterPasswordHintById
146149
import com.artemchep.keyguard.common.usecase.PutAccountNameById
150+
import com.artemchep.keyguard.common.usecase.PutBarcodeUsageHistory
147151
import com.artemchep.keyguard.common.usecase.PutHibpApiToken
148152
import com.artemchep.keyguard.common.usecase.PutProfileHidden
149153
import com.artemchep.keyguard.common.usecase.RePromptCipherById
@@ -185,6 +189,7 @@ import com.artemchep.keyguard.common.usecase.impl.CanPreviewAttachmentImpl
185189
import com.artemchep.keyguard.common.usecase.impl.DownloadAttachmentImpl2
186190
import com.artemchep.keyguard.common.usecase.impl.EditWordlistImpl
187191
import com.artemchep.keyguard.common.usecase.impl.GetAccountStatusImpl
192+
import com.artemchep.keyguard.common.usecase.impl.GetBarcodeUsageHistoryImpl
188193
import com.artemchep.keyguard.common.usecase.impl.GetBreachesImpl
189194
import com.artemchep.keyguard.common.usecase.impl.GetBreachesLatestDateImpl
190195
import com.artemchep.keyguard.common.usecase.impl.GetCanAddAccountImpl
@@ -197,6 +202,7 @@ import com.artemchep.keyguard.common.usecase.impl.GetVaultSearchIndexImpl
197202
import com.artemchep.keyguard.common.usecase.impl.GetVaultSearchQualifierCatalogImpl
198203
import com.artemchep.keyguard.common.usecase.impl.RemoveGeneratorHistoryByIdImpl
199204
import com.artemchep.keyguard.common.usecase.impl.RemoveGeneratorHistoryImpl
205+
import com.artemchep.keyguard.common.usecase.impl.PutBarcodeUsageHistoryImpl
200206
import com.artemchep.keyguard.common.usecase.impl.PutHibpApiTokenImpl
201207
import com.artemchep.keyguard.common.usecase.impl.WatchtowerBroadUris
202208
import com.artemchep.keyguard.common.usecase.impl.WatchtowerDuplicateUris
@@ -827,6 +833,16 @@ fun DI.Builder.createSubDi2(
827833
directDI = this,
828834
)
829835
}
836+
bindSingleton<GetBarcodeUsageHistory> {
837+
GetBarcodeUsageHistoryImpl(
838+
directDI = this,
839+
)
840+
}
841+
bindSingleton<PutBarcodeUsageHistory> {
842+
PutBarcodeUsageHistoryImpl(
843+
directDI = this,
844+
)
845+
}
830846
bindSingleton<AddCipher> {
831847
AddCipherImpl(this)
832848
}
@@ -857,6 +873,9 @@ fun DI.Builder.createSubDi2(
857873
bindSingleton<CipherHistoryOpenedRepository> {
858874
CipherHistoryOpenedRepositoryImpl(this)
859875
}
876+
bindSingleton<BarcodeUsageHistoryRepository> {
877+
BarcodeUsageHistoryRepositoryImpl(this)
878+
}
860879
bindSingleton<SshUsageHistoryRepository> {
861880
SshUsageHistoryRepositoryImpl(this)
862881
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.artemchep.keyguard.feature.barcodetype
2+
3+
import com.artemchep.keyguard.common.service.crypto.CryptoGenerator
4+
import com.artemchep.keyguard.common.util.toHex
5+
6+
// In is better to keep it stable. The key is used to track
7+
// the latest type of the barcode. Changing the format resets
8+
// the saved state for everyone.
9+
fun createBarcodeTypeHistoryKey(
10+
cryptoGenerator: CryptoGenerator,
11+
cipherLocalId: String,
12+
value: String,
13+
): String = cryptoGenerator
14+
.hashMd5("$cipherLocalId:$value".encodeToByteArray())
15+
.toHex()

0 commit comments

Comments
 (0)