mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-09-28 07:51:42 +01:00
Add sticker details.
This commit is contained in:
+2
@@ -6,6 +6,7 @@
|
||||
package org.thoughtcrime.securesms.stickers.preview
|
||||
|
||||
import org.thoughtcrime.securesms.database.model.StickerPackParams
|
||||
import org.thoughtcrime.securesms.stickers.StickerManifest
|
||||
|
||||
/**
|
||||
* Actions that happen in [StickerPackPreviewActivityV2]
|
||||
@@ -13,6 +14,7 @@ import org.thoughtcrime.securesms.database.model.StickerPackParams
|
||||
sealed interface StickerPackPreviewAction {
|
||||
data class SendPack(val params: StickerPackParams) : StickerPackPreviewAction
|
||||
data class ShareExternally(val params: StickerPackParams) : StickerPackPreviewAction
|
||||
data class SendSticker(val sticker: StickerManifest.Sticker) : StickerPackPreviewAction
|
||||
data object LinkCopied : StickerPackPreviewAction
|
||||
data object PackUnavailable : StickerPackPreviewAction
|
||||
}
|
||||
|
||||
+64
-4
@@ -12,6 +12,7 @@ import androidx.activity.compose.setContent
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -84,12 +85,14 @@ import org.thoughtcrime.securesms.database.model.StickerPackId
|
||||
import org.thoughtcrime.securesms.database.model.StickerPackKey
|
||||
import org.thoughtcrime.securesms.database.model.StickerPackParams
|
||||
import org.thoughtcrime.securesms.sharing.MultiShareArgs
|
||||
import org.thoughtcrime.securesms.stickers.StickerLocator
|
||||
import org.thoughtcrime.securesms.stickers.StickerManifest
|
||||
import org.thoughtcrime.securesms.stickers.StickerPreviewDataFactory
|
||||
import org.thoughtcrime.securesms.stickers.StickerRemoteUri
|
||||
import org.thoughtcrime.securesms.stickers.StickerUrl
|
||||
import org.thoughtcrime.securesms.stickers.preview.StickerPackPreviewUiState.ContentState
|
||||
import org.thoughtcrime.securesms.stickers.preview.StickerPackPreviewUiState.UserPrompt
|
||||
import org.thoughtcrime.securesms.util.MediaUtil
|
||||
import org.thoughtcrime.securesms.util.viewModel
|
||||
import java.text.NumberFormat
|
||||
import kotlin.jvm.optionals.getOrElse
|
||||
@@ -147,6 +150,7 @@ class StickerPackPreviewActivityV2 : PassphraseRequiredActivity() {
|
||||
Toast.makeText(this, R.string.StickerPackPreviewActivity_failed_to_load_sticker_pack, Toast.LENGTH_SHORT).show()
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
is StickerPackPreviewAction.SendSticker -> openStickerShareSheet(action.sticker)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +168,24 @@ class StickerPackPreviewActivityV2 : PassphraseRequiredActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun openStickerShareSheet(sticker: StickerManifest.Sticker) {
|
||||
val uri = sticker.uri.orNull() ?: return
|
||||
|
||||
MultiselectForwardFragment.showBottomSheet(
|
||||
supportFragmentManager = supportFragmentManager,
|
||||
multiselectForwardFragmentArgs = MultiselectForwardFragmentArgs(
|
||||
multiShareArgs = listOf(
|
||||
MultiShareArgs.Builder()
|
||||
.withDataUri(uri)
|
||||
.withDataType(sticker.contentType?.takeIf { it.isNotBlank() } ?: MediaUtil.IMAGE_WEBP)
|
||||
.withStickerLocator(StickerLocator(sticker.packId, sticker.packKey, sticker.id, sticker.emoji))
|
||||
.build()
|
||||
),
|
||||
title = R.string.StickerManagement_share_sheet_title
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun openSystemShareSheet(params: StickerPackParams) {
|
||||
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
||||
type = "text/plain"
|
||||
@@ -280,6 +302,15 @@ private fun StickerPackPreviewScreen(
|
||||
)
|
||||
}
|
||||
|
||||
if (uiState.userPrompt is UserPrompt.PreviewSticker && stickerManifest != null) {
|
||||
StickerPreviewSheet(
|
||||
stickerManifest,
|
||||
uiState.userPrompt.sticker,
|
||||
loadedState.isPackInstalled,
|
||||
onEvent
|
||||
)
|
||||
}
|
||||
|
||||
when (uiState.contentState) {
|
||||
is ContentState.Loading -> {
|
||||
Box(
|
||||
@@ -380,7 +411,8 @@ private fun StickerPackPreviewContent(
|
||||
) { item ->
|
||||
StickerImage(
|
||||
sticker = item,
|
||||
modifier = Modifier.size(96.dp)
|
||||
modifier = Modifier.size(96.dp),
|
||||
onClick = { onEvent(StickerPackPreviewEvent.StickerClicked(item)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -542,6 +574,29 @@ private fun StickerPackShareSheet(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun StickerPreviewSheet(
|
||||
stickerManifest: StickerManifest,
|
||||
sticker: StickerManifest.Sticker,
|
||||
isPackInstalled: Boolean,
|
||||
onEvent: (StickerPackPreviewEvent) -> Unit
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
BottomSheets.BottomSheet(
|
||||
sheetState = sheetState,
|
||||
onDismissRequest = { onEvent(StickerPackPreviewEvent.ShareSheetDismissed) }
|
||||
) {
|
||||
StickerPreviewSheetContent(
|
||||
stickerManifest,
|
||||
sticker,
|
||||
isPackInstalled
|
||||
) { sheetState.dismissWithAnimation(scope, onComplete = { onEvent(StickerPackPreviewEvent.StickerSent(sticker)) }) }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StickerPackShareSheetContent(
|
||||
params: StickerPackParams,
|
||||
@@ -627,15 +682,20 @@ private fun StickerPackShareSheetContent(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StickerImage(
|
||||
fun StickerImage(
|
||||
modifier: Modifier = Modifier,
|
||||
sticker: StickerManifest.Sticker
|
||||
sticker: StickerManifest.Sticker,
|
||||
onClick: (() -> Unit)? = null
|
||||
) {
|
||||
if (!LocalInspectionMode.current) {
|
||||
GlideImage(
|
||||
model = sticker.imageModel,
|
||||
enableApngAnimation = true,
|
||||
modifier = modifier
|
||||
modifier = if (onClick != null) {
|
||||
modifier.clickable(onClick = { onClick() })
|
||||
} else {
|
||||
modifier
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
|
||||
+4
@@ -5,6 +5,8 @@
|
||||
|
||||
package org.thoughtcrime.securesms.stickers.preview
|
||||
|
||||
import org.thoughtcrime.securesms.stickers.StickerManifest
|
||||
|
||||
/**
|
||||
* Events that happen in [StickerPackPreviewActivityV2]
|
||||
*/
|
||||
@@ -19,4 +21,6 @@ sealed interface StickerPackPreviewEvent {
|
||||
data object ShareSheetDismissed : StickerPackPreviewEvent
|
||||
data object CopyLinkClicked : StickerPackPreviewEvent
|
||||
data object ShareExternallyClicked : StickerPackPreviewEvent
|
||||
data class StickerClicked(val sticker: StickerManifest.Sticker) : StickerPackPreviewEvent
|
||||
data class StickerSent(val sticker: StickerManifest.Sticker) : StickerPackPreviewEvent
|
||||
}
|
||||
|
||||
+8
@@ -99,6 +99,13 @@ class StickerPackPreviewViewModelV2(
|
||||
showPrompt(null)
|
||||
internalActions.send(StickerPackPreviewAction.ShareExternally(params))
|
||||
}
|
||||
|
||||
is StickerPackPreviewEvent.StickerClicked -> showPrompt(UserPrompt.PreviewSticker(event.sticker))
|
||||
|
||||
is StickerPackPreviewEvent.StickerSent -> {
|
||||
showPrompt(null)
|
||||
internalActions.send(StickerPackPreviewAction.SendSticker(event.sticker))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,5 +186,6 @@ data class StickerPackPreviewUiState(
|
||||
sealed interface UserPrompt {
|
||||
data object ConfirmRemovePack : UserPrompt
|
||||
data object ShareStickerPack : UserPrompt
|
||||
data class PreviewSticker(val sticker: StickerManifest.Sticker) : UserPrompt
|
||||
}
|
||||
}
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.stickers.preview
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.signal.core.ui.compose.DayNightPreviews
|
||||
import org.signal.core.ui.compose.Dividers
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.signal.core.ui.compose.Rows
|
||||
import org.signal.core.ui.compose.SignalIcons
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.core.util.toOptional
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.stickers.StickerManifest
|
||||
import org.thoughtcrime.securesms.stickers.StickerPreviewDataFactory
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun StickerPreviewSheetContent(
|
||||
stickerManifest: StickerManifest,
|
||||
sticker: StickerManifest.Sticker,
|
||||
isPackInstalled: Boolean,
|
||||
onForwardClick: () -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
StickerImage(
|
||||
sticker = sticker,
|
||||
modifier = Modifier
|
||||
.padding(vertical = 24.dp)
|
||||
.size(144.dp)
|
||||
)
|
||||
|
||||
val title = stickerManifest.title.orNull() ?: stringResource(R.string.StickerPackPreviewActivity_untitled)
|
||||
val author = stickerManifest.author.orNull() ?: stringResource(R.string.StickerManagement_author_unknown)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.StickerPreviewBottomSheet__title_author, title, author),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
)
|
||||
|
||||
if (isPackInstalled && sticker.uri.isPresent) {
|
||||
Dividers.Default()
|
||||
|
||||
Rows.TextRow(
|
||||
text = stringResource(R.string.StickerManagement_menu_send_pack),
|
||||
icon = SignalIcons.Forward.imageVector,
|
||||
onClick = onForwardClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
private fun StickerPackShareSheetContentPreview() {
|
||||
Previews.BottomSheetContentPreview {
|
||||
val cover = StickerManifest.Sticker("packId0", "packKey0", 0, "👍", null, Uri.EMPTY)
|
||||
|
||||
StickerPreviewSheetContent(
|
||||
stickerManifest = StickerManifest(
|
||||
cover.packId,
|
||||
cover.packKey,
|
||||
"Misbrands".toOptional(),
|
||||
"Sticker Pack Author".toOptional(),
|
||||
cover.toOptional(),
|
||||
StickerPreviewDataFactory.manifestStickers(33)
|
||||
),
|
||||
sticker = cover,
|
||||
isPackInstalled = true,
|
||||
onForwardClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -2874,6 +2874,8 @@
|
||||
<string name="StickerPackPreviewActivity_remove">Uninstall</string>
|
||||
<string name="StickerPackPreviewActivity_stickers">Stickers</string>
|
||||
<string name="StickerPackPreviewActivity_failed_to_load_sticker_pack">Failed to load sticker pack</string>
|
||||
<!-- Bottom sheet describing a sticker. First placeholder is the title of the sticker pack and second is the author -->
|
||||
<string name="StickerPreviewBottomSheet__title_author">%1$s · %2$s</string>
|
||||
|
||||
<!-- Menu option to save a debug log file to disk. -->
|
||||
<string name="SubmitDebugLogActivity_save">Save</string>
|
||||
|
||||
Reference in New Issue
Block a user