Add large-screen media send toolbars for image editing.

This commit is contained in:
Alex Hart
2026-04-10 14:35:43 -03:00
committed by Cody Henthorne
parent b4bfb67a44
commit 773d6c36dc
41 changed files with 674 additions and 117 deletions

View File

@@ -205,7 +205,7 @@ private fun FeatureBullet(text: String) {
modifier = Modifier.padding(vertical = 2.dp)
) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.symbol_check_24),
imageVector = ImageVector.vectorResource(id = CoreUiR.drawable.symbol_check_24),
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(20.dp)

View File

@@ -24,6 +24,7 @@ import kotlinx.coroutines.withContext
import org.signal.core.util.billing.BillingPurchaseResult
import org.signal.core.util.concurrent.SignalDispatchers
import org.signal.core.util.logging.Log
import org.signal.core.util.next
import org.signal.donations.InAppPaymentType
import org.thoughtcrime.securesms.backup.DeletionState
import org.thoughtcrime.securesms.backup.v2.BackupRepository
@@ -45,7 +46,6 @@ import org.thoughtcrime.securesms.jobs.InAppPaymentPurchaseTokenJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.storage.StorageSyncHelper
import org.thoughtcrime.securesms.util.next
import org.whispersystems.signalservice.api.storage.IAPSubscriptionId
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration
import kotlin.time.Duration.Companion.seconds

View File

@@ -72,6 +72,7 @@ import org.signal.core.ui.compose.Previews
import org.signal.core.ui.compose.Rows
import org.signal.core.ui.compose.horizontalGutters
import org.thoughtcrime.securesms.R
import org.signal.core.ui.R as CoreUiR
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -413,7 +414,7 @@ private fun IssueChip(
leadingIcon = {
Icon(
imageVector = if (isSelected) {
ImageVector.vectorResource(R.drawable.symbol_check_24)
ImageVector.vectorResource(CoreUiR.drawable.symbol_check_24)
} else {
ImageVector.vectorResource(issue.category.icon)
},

View File

@@ -704,7 +704,7 @@ public final class ConversationReactionOverlay extends FrameLayout {
}
if (menuState.shouldShowSaveAttachmentAction()) {
items.add(new ActionItem(R.drawable.symbol_save_android_24, getResources().getString(R.string.conversation_selection__menu_save), () -> handleActionItemClicked(Action.DOWNLOAD)));
items.add(new ActionItem(org.signal.core.ui.R.drawable.symbol_save_android_24, getResources().getString(R.string.conversation_selection__menu_save), () -> handleActionItemClicked(Action.DOWNLOAD)));
}
if (menuState.shouldShowCopyAction()) {

View File

@@ -93,7 +93,7 @@ object PinnedContextMenu {
message.slideDeck.getStickerSlide() == null
) {
add(
ActionItem(R.drawable.symbol_save_android_24, context.getString(R.string.conversation_selection__menu_save)) {
ActionItem(CoreUiR.drawable.symbol_save_android_24, context.getString(R.string.conversation_selection__menu_save)) {
callbacks.onSave()
}
)

View File

@@ -64,7 +64,7 @@ final class SafetyNumberChangeAdapter extends ListAdapter<ChangedRecipient, Safe
if (changedRecipient.isUnverified() || changedRecipient.isVerified()) {
subtitle.setText(R.string.safety_number_change_dialog__previous_verified);
Drawable check = DrawableUtil.tint(ContextUtil.requireDrawable(itemView.getContext(), R.drawable.symbol_check_24), ContextCompat.getColor(itemView.getContext(), R.color.signal_text_secondary));
Drawable check = DrawableUtil.tint(ContextUtil.requireDrawable(itemView.getContext(), org.signal.core.ui.R.drawable.symbol_check_24), ContextCompat.getColor(itemView.getContext(), R.color.signal_text_secondary));
check.setBounds(0, 0, ViewUtil.dpToPx(12), ViewUtil.dpToPx(12));
subtitle.setCompoundDrawables(check, null, null, null);
} else if (changedRecipient.getRecipient().hasAUserSetDisplayName(itemView.getContext())) {

View File

@@ -2623,7 +2623,7 @@ class ConversationFragment :
if (menuState.shouldShowSaveAttachmentAction()) {
items.add(
ActionItem(R.drawable.symbol_save_android_24, resources.getString(R.string.conversation_selection__menu_save)) {
ActionItem(CoreUiR.drawable.symbol_save_android_24, resources.getString(R.string.conversation_selection__menu_save)) {
handleSaveAttachment(getSelectedConversationMessage().messageRecord as MmsMessageRecord)
finishActionMode()
}

View File

@@ -81,7 +81,7 @@ fun LearnMoreSheet() {
)
Row(modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp, start = 40.dp, end = 32.dp)) {
Icon(
painter = painterResource(R.drawable.symbol_save_android_24),
imageVector = SignalIcons.Save.imageVector,
contentDescription = stringResource(R.string.preferences__linked_devices),
modifier = Modifier.size(24.dp).padding(top = 4.dp)
)

View File

@@ -53,7 +53,7 @@ class MediaOverviewContextMenu(
private fun getSaveActionItem(mediaRecord: MediaTable.MediaRecord): ActionItem? {
if (mediaRecord.attachment == null) return null
return ActionItem(
iconRes = R.drawable.symbol_save_android_24,
iconRes = CoreUiR.drawable.symbol_save_android_24,
title = fragment.getString(R.string.save)
) {
callbacks.onSave(mediaRecord)

View File

@@ -511,7 +511,7 @@ public final class MediaOverviewPageFragment extends LoggingFragment
int selectionCount = getListAdapter().getSectionCount();
bottomActionBar.setItems(Arrays.asList(
new ActionItem(R.drawable.symbol_save_android_24, getResources().getQuantityString(R.plurals.MediaOverviewActivity_save_plural, selectionCount), () -> {
new ActionItem(org.signal.core.ui.R.drawable.symbol_save_android_24, getResources().getQuantityString(R.plurals.MediaOverviewActivity_save_plural, selectionCount), () -> {
Collection<MediaTable.MediaRecord> selected = getListAdapter().getSelectedMedia();
if (SignalStore.backup().getOptimizeStorage()) {

View File

@@ -419,8 +419,8 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul
}
val icon = when (state.quality) {
SentMediaQuality.HIGH -> R.drawable.symbol_quality_high_24
else -> R.drawable.symbol_quality_high_slash_24
SentMediaQuality.HIGH -> CoreUiR.drawable.symbol_quality_high_24
else -> CoreUiR.drawable.symbol_quality_high_slash_24
}
MediaReviewToastPopupWindow.show(controls, icon, description)
@@ -494,8 +494,8 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul
}
qualityButton.setImageResource(
when (state.quality) {
SentMediaQuality.STANDARD -> R.drawable.symbol_quality_high_slash_24
SentMediaQuality.HIGH -> R.drawable.symbol_quality_high_24
SentMediaQuality.STANDARD -> CoreUiR.drawable.symbol_quality_high_slash_24
SentMediaQuality.HIGH -> CoreUiR.drawable.symbol_quality_high_24
}
)
}

View File

@@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.mediasend.v2.text
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import org.thoughtcrime.securesms.util.next
import org.signal.core.util.next
typealias OnTextAlignmentChanged = (TextAlignment) -> Unit

View File

@@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.mediasend.v2.text
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import org.thoughtcrime.securesms.util.next
import org.signal.core.util.next
typealias OnTextColorStyleChanged = (TextColorStyle) -> Unit

View File

@@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.mediasend.v2.text
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import org.signal.core.util.next
import org.thoughtcrime.securesms.fonts.TextFont
import org.thoughtcrime.securesms.util.next
typealias OnTextFontChanged = (TextFont) -> Unit

View File

@@ -34,6 +34,7 @@ import org.thoughtcrime.securesms.util.ConversationUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences
import java.util.Optional
import androidx.core.app.Person as PersonCompat
import org.signal.core.ui.R as CoreUiR
private const val BIG_PICTURE_DIMEN = 500
@@ -237,7 +238,7 @@ sealed class NotificationBuilder(protected val context: Context) {
val markAsRead: PendingIntent? = conversation.getMarkAsReadIntent(context)
if (markAsRead != null) {
val markAsReadAction: NotificationCompat.Action =
NotificationCompat.Action.Builder(R.drawable.symbol_check_24, context.getString(R.string.MessageNotifier_mark_read), markAsRead)
NotificationCompat.Action.Builder(CoreUiR.drawable.symbol_check_24, context.getString(R.string.MessageNotifier_mark_read), markAsRead)
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ)
.setShowsUserInterface(false)
.build()
@@ -251,7 +252,7 @@ sealed class NotificationBuilder(protected val context: Context) {
val markAsRead: PendingIntent? = state.getMarkAsReadIntent(context)
if (markAsRead != null) {
val markAllAsReadAction = NotificationCompat.Action(R.drawable.symbol_check_24, context.getString(R.string.MessageNotifier_mark_all_as_read), markAsRead)
val markAllAsReadAction = NotificationCompat.Action(CoreUiR.drawable.symbol_check_24, context.getString(R.string.MessageNotifier_mark_all_as_read), markAsRead)
builder.addAction(markAllAsReadAction)
builder.extend(NotificationCompat.WearableExtender().addAction(markAllAsReadAction))
}
@@ -260,7 +261,7 @@ sealed class NotificationBuilder(protected val context: Context) {
override fun addTurnOffJoinedNotificationsAction(pendingIntent: PendingIntent?) {
if (pendingIntent != null) {
val turnOffTheseNotifications = NotificationCompat.Action(
R.drawable.symbol_check_24,
CoreUiR.drawable.symbol_check_24,
context.getString(R.string.MessageNotifier_turn_off_these_notifications),
pendingIntent
)

View File

@@ -29,6 +29,7 @@ import org.thoughtcrime.securesms.scribbles.HSVColorSlider.setUpForColor
import org.thoughtcrime.securesms.util.Debouncer
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.visible
import org.signal.core.ui.R as CoreUiR
class ImageEditorHudV2 @JvmOverloads constructor(
context: Context,
@@ -146,9 +147,9 @@ class ImageEditorHudV2 @JvmOverloads constructor(
cropAspectLockButton.setOnClickListener {
listener?.onCropAspectLock()
if (listener?.isCropAspectLocked == true) {
cropAspectLockButton.setImageResource(R.drawable.symbol_crop_lock_24)
cropAspectLockButton.setImageResource(CoreUiR.drawable.symbol_crop_lock_24)
} else {
cropAspectLockButton.setImageResource(R.drawable.symbol_crop_unlock_24)
cropAspectLockButton.setImageResource(CoreUiR.drawable.symbol_crop_unlock_24)
}
}
@@ -355,7 +356,7 @@ class ImageEditorHudV2 @JvmOverloads constructor(
private fun presentModeDraw() {
drawButton.isSelected = true
brushToggle.setImageResource(R.drawable.symbol_brush_pen_24)
brushToggle.setImageResource(CoreUiR.drawable.symbol_brush_pen_24)
widthSeekBar.progress = SignalStore.imageEditor.getMarkerPercentage()
listener?.onColorChange(getActiveColor())
updateColorIndicator()
@@ -368,7 +369,7 @@ class ImageEditorHudV2 @JvmOverloads constructor(
private fun presentModeHighlight() {
drawButton.isSelected = true
brushToggle.setImageResource(R.drawable.symbol_brush_highlighter_24)
brushToggle.setImageResource(CoreUiR.drawable.symbol_brush_highlighter_24)
widthSeekBar.progress = SignalStore.imageEditor.getHighlighterPercentage()
listener?.onColorChange(getActiveColor())
updateColorIndicator()

View File

@@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.components.transfercontrols.TransferProgressSt
import org.thoughtcrime.securesms.stickers.StickerPreviewDataFactory
import org.thoughtcrime.securesms.stickers.manage.AvailableStickerPack.DownloadStatus
import org.thoughtcrime.securesms.util.DeviceProperties
import org.signal.core.ui.R as CoreUiR
@Composable
fun StickerPackSectionHeader(
@@ -90,7 +91,7 @@ fun AvailableStickerPackRow(
)
val readyIcon = ImageVector.vectorResource(R.drawable.symbol_arrow_circle_down_24)
val downloadedIcon = ImageVector.vectorResource(R.drawable.symbol_check_24)
val downloadedIcon = ImageVector.vectorResource(CoreUiR.drawable.symbol_check_24)
val startButtonContentDesc = stringResource(R.string.StickerManagement_accessibility_download)
val startButtonOnClickLabel = stringResource(R.string.StickerManagement_accessibility_download_pack, pack.record.title)

View File

@@ -242,7 +242,7 @@ object StoryContextMenu {
}
)
add(
ActionItem(R.drawable.symbol_save_android_24, context.getString(R.string.save)) {
ActionItem(CoreUiR.drawable.symbol_save_android_24, context.getString(R.string.save)) {
callbacks.onSave()
}
)

View File

@@ -1,13 +0,0 @@
package org.thoughtcrime.securesms.util
/**
* Treating an Enum as a circular list, returns the "next"
* value after the caller, wrapping around to the first value
* in the enum as necessary.
*/
inline fun <reified T : Enum<T>> T.next(): T {
val values = enumValues<T>()
val nextOrdinal = (ordinal + 1) % values.size
return values[nextOrdinal]
}