diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.kt index bd517ec666..c40fca2134 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.kt @@ -252,10 +252,10 @@ class StickerTable( notifyStickerPackListeners() } - fun markPackAsInstalled(packKey: String, notify: Boolean) { + fun markPackAsInstalled(packId: String, notify: Boolean) { updatePackInstalled( db = databaseHelper.signalWritableDatabase, - packId = packKey, + packId = packId, installed = true, notify = notify ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.kt index 4123c905aa..aea2aa132b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.kt @@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.database.model import java.util.Optional /** - * Represents a record for a sticker pack in the [StickerTable]. + * Represents a record for a sticker pack in the [org.thoughtcrime.securesms.database.StickerTable]. */ data class StickerPackRecord( @JvmField val packId: String, @@ -19,3 +19,15 @@ data class StickerPackRecord( @JvmField val authorOptional: Optional = if (author.isBlank()) Optional.empty() else Optional.of(author) } + +/** + * A unique identifier for a sticker pack. + */ +@JvmInline +value class StickerPackId(val value: String) + +/** + * An encryption key for a sticker pack. + */ +@JvmInline +value class StickerPackKey(val value: String) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementActivityV2.kt index 9b220ea70e..c68ca0d9b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementActivityV2.kt @@ -207,7 +207,7 @@ private fun AvailableStickersContent( item { StickerPackSectionHeader(text = stringResource(R.string.StickerManagement_signal_artist_series_header)) } items( items = blessedPacks, - key = { it.record.packId } + key = { it.id.value } ) { AvailableStickerPackRow( pack = it, @@ -225,7 +225,7 @@ private fun AvailableStickersContent( item { StickerPackSectionHeader(text = stringResource(R.string.StickerManagement_stickers_you_received_header)) } items( items = notBlessedPacks, - key = { it.record.packId } + key = { it.id.value } ) { AvailableStickerPackRow( pack = it, @@ -253,7 +253,7 @@ private fun InstalledStickersContent( item { StickerPackSectionHeader(text = stringResource(R.string.StickerManagement_installed_stickers_header)) } items( items = packs, - key = { it.record.packId } + key = { it.id.value } ) { InstalledStickerPackRow(it) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementRepository.kt index 2fae26c79c..f59237efa4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementRepository.kt @@ -20,6 +20,8 @@ import org.thoughtcrime.securesms.database.DatabaseObserver import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.StickerTable import org.thoughtcrime.securesms.database.StickerTable.StickerPackRecordReader +import org.thoughtcrime.securesms.database.model.StickerPackId +import org.thoughtcrime.securesms.database.model.StickerPackKey import org.thoughtcrime.securesms.database.model.StickerPackRecord import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobmanager.JobManager @@ -39,7 +41,7 @@ object StickerManagementRepository { @Discouraged("For Java use only. In Kotlin, use the getStickerPacks() overload that returns a Flow instead.") @WorkerThread - fun getStickerPacks(callback: Callback) { + fun getStickerPacks(callback: Callback) { coroutineScope.launch { callback.onComplete(loadStickerPacks()) } @@ -48,7 +50,7 @@ object StickerManagementRepository { /** * Emits the sticker packs along with any updates. */ - fun getStickerPacks(): Flow = callbackFlow { + fun getStickerPacks(): Flow = callbackFlow { trySend(loadStickerPacks()) val stickersDbObserver = DatabaseObserver.Observer { @@ -64,12 +66,12 @@ object StickerManagementRepository { } } - private suspend fun loadStickerPacks(): StickerPackResult = withContext(Dispatchers.IO) { + private suspend fun loadStickerPacks(): StickerPacksResult = withContext(Dispatchers.IO) { StickerPackRecordReader(stickersDbTable.getAllStickerPacks()).use { reader -> val installedPacks = mutableListOf() val availablePacks = mutableListOf() val blessedPacks = mutableListOf() - val sortOrderById = mutableMapOf() + val sortOrderById = mutableMapOf() reader.asSequence().forEachIndexed { index, record -> when { @@ -77,10 +79,10 @@ object StickerManagementRepository { BlessedPacks.contains(record.packId) -> blessedPacks.add(record) else -> availablePacks.add(record) } - sortOrderById[record.packId] = index + sortOrderById[StickerPackId(record.packId)] = index } - StickerPackResult( + StickerPacksResult( installedPacks = installedPacks, availablePacks = availablePacks, blessedPacks = blessedPacks, @@ -112,34 +114,34 @@ object StickerManagementRepository { fun installStickerPackAsync(packId: String, packKey: String, notify: Boolean) { coroutineScope.launch { - installStickerPack(packId, packKey, notify) + installStickerPack(StickerPackId(packId), StickerPackKey(packKey), notify) } } - suspend fun installStickerPack(packId: String, packKey: String, notify: Boolean) = withContext(Dispatchers.IO) { - if (stickersDbTable.isPackAvailableAsReference(packId)) { - stickersDbTable.markPackAsInstalled(packId, notify) + suspend fun installStickerPack(packId: StickerPackId, packKey: StickerPackKey, notify: Boolean) = withContext(Dispatchers.IO) { + if (stickersDbTable.isPackAvailableAsReference(packId.value)) { + stickersDbTable.markPackAsInstalled(packId.value, notify) } - jobManager.add(StickerPackDownloadJob.forInstall(packId, packKey, notify)) + jobManager.add(StickerPackDownloadJob.forInstall(packId.value, packKey.value, notify)) if (SignalStore.account.hasLinkedDevices) { - jobManager.add(MultiDeviceStickerPackOperationJob(packId, packKey, MultiDeviceStickerPackOperationJob.Type.INSTALL)) + jobManager.add(MultiDeviceStickerPackOperationJob(packId.value, packKey.value, MultiDeviceStickerPackOperationJob.Type.INSTALL)) } } @Discouraged("For Java use only. In Kotlin, use uninstallStickerPack() instead.") fun uninstallStickerPackAsync(packId: String, packKey: String) { coroutineScope.launch { - uninstallStickerPack(packId, packKey) + uninstallStickerPack(StickerPackId(packId), StickerPackKey(packKey)) } } - suspend fun uninstallStickerPack(packId: String, packKey: String) = withContext(Dispatchers.IO) { - stickersDbTable.uninstallPack(packId) + suspend fun uninstallStickerPack(packId: StickerPackId, packKey: StickerPackKey) = withContext(Dispatchers.IO) { + stickersDbTable.uninstallPack(packId.value) if (SignalStore.account.hasLinkedDevices) { - AppDependencies.jobManager.add(MultiDeviceStickerPackOperationJob(packId, packKey, MultiDeviceStickerPackOperationJob.Type.REMOVE)) + AppDependencies.jobManager.add(MultiDeviceStickerPackOperationJob(packId.value, packKey.value, MultiDeviceStickerPackOperationJob.Type.REMOVE)) } } @@ -159,9 +161,9 @@ object StickerManagementRepository { } } -data class StickerPackResult( +data class StickerPacksResult( val installedPacks: List, val availablePacks: List, val blessedPacks: List, - val sortOrderByPackId: Map + val sortOrderByPackId: Map ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementViewModel.java index dde236966c..71b1dbe891 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementViewModel.java @@ -17,9 +17,9 @@ import java.util.List; final class StickerManagementViewModel extends ViewModel { private final Application application; - private final StickerManagementRepository repository; - private final MutableLiveData packs; - private final DatabaseObserver.Observer observer; + private final StickerManagementRepository repository; + private final MutableLiveData packs; + private final DatabaseObserver.Observer observer; private StickerManagementViewModel(@NonNull Application application, @NonNull StickerManagementRepository repository) { this.application = application; @@ -42,7 +42,7 @@ final class StickerManagementViewModel extends ViewModel { repository.deleteOrphanedStickerPacksAsync(); } - @NonNull LiveData getStickerPacks() { + @NonNull LiveData getStickerPacks() { repository.getStickerPacks(packs::postValue); return packs; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementViewModelV2.kt b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementViewModelV2.kt index 045ad41516..4d542cefb0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementViewModelV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementViewModelV2.kt @@ -15,6 +15,8 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import org.thoughtcrime.securesms.database.model.StickerPackId +import org.thoughtcrime.securesms.database.model.StickerPackKey import org.thoughtcrime.securesms.database.model.StickerPackRecord import org.thoughtcrime.securesms.stickers.AvailableStickerPack.DownloadStatus @@ -24,7 +26,7 @@ class StickerManagementViewModelV2 : ViewModel() { private val _uiState = MutableStateFlow(StickerManagementUiState()) val uiState: StateFlow = _uiState.asStateFlow() - private val downloadStatusByPackId: MutableStateFlow> = MutableStateFlow(emptyMap()) + private val downloadStatusByPackId: MutableStateFlow> = MutableStateFlow(emptyMap()) init { viewModelScope.launch { @@ -42,25 +44,26 @@ class StickerManagementViewModelV2 : ViewModel() { private suspend fun loadStickerPacks() { combine(stickerManagementRepo.getStickerPacks(), downloadStatusByPackId, ::Pair) .collectLatest { (stickerPacksResult, downloadStatuses) -> - val recentlyInstalledPacks = stickerPacksResult.installedPacks.filter { downloadStatuses.contains(it.packId) } + val recentlyInstalledPacks = stickerPacksResult.installedPacks.filter { downloadStatuses.contains(StickerPackId(it.packId)) } val allAvailablePacks = (stickerPacksResult.blessedPacks + stickerPacksResult.availablePacks + recentlyInstalledPacks) .map { record -> + val packId = StickerPackId(record.packId) AvailableStickerPack( record = record, isBlessed = BlessedPacks.contains(record.packId), - downloadStatus = downloadStatuses.getOrElse(record.packId) { - downloadStatusByPackId.value.getOrDefault(record.packId, DownloadStatus.NotDownloaded) + downloadStatus = downloadStatuses.getOrElse(packId) { + downloadStatusByPackId.value.getOrDefault(packId, DownloadStatus.NotDownloaded) } ) } - .sortedBy { stickerPacksResult.sortOrderByPackId.getValue(it.record.packId) } + .sortedBy { stickerPacksResult.sortOrderByPackId.getValue(it.id) } val (availableBlessedPacks, availableNotBlessedPacks) = allAvailablePacks.partition { it.isBlessed } val installedPacks = stickerPacksResult.installedPacks.map { record -> InstalledStickerPack( record = record, isBlessed = BlessedPacks.contains(record.packId), - sortOrder = stickerPacksResult.sortOrderByPackId.getValue(record.packId) + sortOrder = stickerPacksResult.sortOrderByPackId.getValue(StickerPackId(record.packId)) ) } @@ -75,16 +78,16 @@ class StickerManagementViewModelV2 : ViewModel() { } fun installStickerPack(pack: AvailableStickerPack) = viewModelScope.launch { - updatePackDownloadStatus(pack.record.packId, DownloadStatus.InProgress) + updatePackDownloadStatus(pack.id, DownloadStatus.InProgress) - StickerManagementRepository.installStickerPack(packId = pack.record.packId, packKey = pack.record.packKey, notify = true) - updatePackDownloadStatus(pack.record.packId, DownloadStatus.Downloaded) + StickerManagementRepository.installStickerPack(packId = pack.id, packKey = pack.key, notify = true) + updatePackDownloadStatus(pack.id, DownloadStatus.Downloaded) delay(1500) // wait, so we show the downloaded status for a bit before removing this row from the available sticker packs list - updatePackDownloadStatus(pack.record.packId, null) + updatePackDownloadStatus(pack.id, null) } - private fun updatePackDownloadStatus(packId: String, newStatus: DownloadStatus?) { + private fun updatePackDownloadStatus(packId: StickerPackId, newStatus: DownloadStatus?) { downloadStatusByPackId.value = if (newStatus == null) { downloadStatusByPackId.value.minus(packId) } else { @@ -93,7 +96,7 @@ class StickerManagementViewModelV2 : ViewModel() { } fun uninstallStickerPack(pack: AvailableStickerPack) = viewModelScope.launch { - StickerManagementRepository.uninstallStickerPack(packId = pack.record.packId, packKey = pack.record.packKey) + StickerManagementRepository.uninstallStickerPack(packId = pack.id, packKey = pack.key) } } @@ -109,6 +112,9 @@ data class AvailableStickerPack( val isBlessed: Boolean, val downloadStatus: DownloadStatus ) { + val id = StickerPackId(record.packId) + val key = StickerPackKey(record.packKey) + sealed class DownloadStatus { data object NotDownloaded : DownloadStatus() data object InProgress : DownloadStatus() @@ -121,4 +127,7 @@ data class InstalledStickerPack( val isBlessed: Boolean, val sortOrder: Int, val isSelected: Boolean = false -) +) { + val id = StickerPackId(record.packId) + val key = StickerPackKey(record.packKey) +}