Add StickerPackId and StickerPackKey value classes.

This commit is contained in:
Jeffrey Starke
2025-04-22 11:43:45 -04:00
committed by Cody Henthorne
parent 3779dfd290
commit 3fc386d4a3
6 changed files with 64 additions and 41 deletions

View File

@@ -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
)

View File

@@ -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<String> = 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)

View File

@@ -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)
}

View File

@@ -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<StickerPackResult>) {
fun getStickerPacks(callback: Callback<StickerPacksResult>) {
coroutineScope.launch {
callback.onComplete(loadStickerPacks())
}
@@ -48,7 +50,7 @@ object StickerManagementRepository {
/**
* Emits the sticker packs along with any updates.
*/
fun getStickerPacks(): Flow<StickerPackResult> = callbackFlow {
fun getStickerPacks(): Flow<StickerPacksResult> = 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<StickerPackRecord>()
val availablePacks = mutableListOf<StickerPackRecord>()
val blessedPacks = mutableListOf<StickerPackRecord>()
val sortOrderById = mutableMapOf<String, Int>()
val sortOrderById = mutableMapOf<StickerPackId, Int>()
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<StickerPackRecord>,
val availablePacks: List<StickerPackRecord>,
val blessedPacks: List<StickerPackRecord>,
val sortOrderByPackId: Map<String, Int>
val sortOrderByPackId: Map<StickerPackId, Int>
)

View File

@@ -17,9 +17,9 @@ import java.util.List;
final class StickerManagementViewModel extends ViewModel {
private final Application application;
private final StickerManagementRepository repository;
private final MutableLiveData<StickerPackResult> packs;
private final DatabaseObserver.Observer observer;
private final StickerManagementRepository repository;
private final MutableLiveData<StickerPacksResult> 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<StickerPackResult> getStickerPacks() {
@NonNull LiveData<StickerPacksResult> getStickerPacks() {
repository.getStickerPacks(packs::postValue);
return packs;
}

View File

@@ -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<StickerManagementUiState> = _uiState.asStateFlow()
private val downloadStatusByPackId: MutableStateFlow<Map<String, DownloadStatus>> = MutableStateFlow(emptyMap())
private val downloadStatusByPackId: MutableStateFlow<Map<StickerPackId, DownloadStatus>> = 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)
}