diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4ac4837..0ce2016 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -115,6 +115,14 @@ jobs: "build-tools;$build_tools" \ "ndk;$ndk" + - name: Test Vesper settings and theme lifecycle + working-directory: work + run: >- + ./gradlew :Signal-Android:testWebsiteProdReleaseUnitTest + --tests 'org.thoughtcrime.securesms.keyvalue.VesperSettingsValuesTest' + --tests 'org.thoughtcrime.securesms.profiles.manage.EditProfileActivityThemeLifecycleTest' + --stacktrace + - name: Build release APKs working-directory: work run: ./gradlew :Signal-Android:assembleWebsiteProdRelease --stacktrace diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 30eed72..f16fd90 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -82,6 +82,14 @@ jobs: "build-tools;$build_tools" \ "ndk;$ndk" + - name: Test Vesper settings and theme lifecycle + working-directory: work + run: >- + ./gradlew :Signal-Android:testWebsiteProdReleaseUnitTest + --tests 'org.thoughtcrime.securesms.keyvalue.VesperSettingsValuesTest' + --tests 'org.thoughtcrime.securesms.profiles.manage.EditProfileActivityThemeLifecycleTest' + --stacktrace + - name: Build release APKs working-directory: work run: ./gradlew :Signal-Android:assembleWebsiteProdRelease --stacktrace diff --git a/patches/0001-feat-add-Material-You-dynamic-theming.patch b/patches/0001-feat-add-Material-You-dynamic-theming.patch index 072c2d5..5474b72 100644 --- a/patches/0001-feat-add-Material-You-dynamic-theming.patch +++ b/patches/0001-feat-add-Material-You-dynamic-theming.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: amber Date: Sat, 25 Jul 2026 18:30:41 +0100 -Subject: [PATCH 01/24] feat: add Material You dynamic theming +Subject: [PATCH 01/27] feat: add Material You dynamic theming Adapted from Molly work by Oscar Mira and other Molly contributors. @@ -38,7 +38,7 @@ Signed-off-by: amber .../ConversationListFilterPullView.kt | 6 +- .../chatfilter/FilterCircleView.kt | 10 +- .../keyboard/emoji/KeyboardPageSearchView.kt | 6 +- - .../securesms/keyvalue/SettingsValues.java | 21 ++ + .../securesms/keyvalue/SettingsValues.kt | 23 +- .../logsubmit/SubmitDebugLogActivity.java | 16 +- .../securesms/main/MainNavigationBar.kt | 15 +- .../mediasend/AvatarSelectionActivity.java | 10 + @@ -78,7 +78,7 @@ Signed-off-by: amber .../core/ui/compose/theme/SignalTheme.kt | 208 +++++++++-- .../org/signal/core/ui/fonts/SignalSymbols.kt | 9 +- .../org/signal/core/ui/util/ThemeUtil.java | 27 +- - 71 files changed, 990 insertions(+), 325 deletions(-) + 71 files changed, 990 insertions(+), 327 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/util/DynamicMediaPreviewTheme.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/AvatarPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/AvatarPreviewActivity.java @@ -123,7 +123,7 @@ index 4982c2605f..22721e75df 100644 WindowUtil.clearLightStatusBar(getWindow()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt -index 347f8bedd6..aadafb2593 100644 +index 168554abe4..fbad2c63c5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt @@ -56,7 +56,6 @@ import androidx.compose.ui.Modifier @@ -145,7 +145,7 @@ index 347f8bedd6..aadafb2593 100644 import org.signal.core.util.AppForegroundObserver import org.signal.core.util.Util import org.signal.core.util.concurrent.LifecycleDisposable -@@ -423,12 +422,11 @@ class MainActivity : +@@ -418,12 +417,11 @@ class MainActivity : val detailLocation by mainNavigationViewModel.detailLocation.collectAsStateWithLifecycle() val isConversationFullscreen = !isSplitPane && detailLocation is MainDetailRoute.Conversation @@ -159,7 +159,7 @@ index 347f8bedd6..aadafb2593 100644 } LaunchedEffect(isDarkTheme, navBarColor) { -@@ -443,6 +441,10 @@ class MainActivity : +@@ -438,6 +436,10 @@ class MainActivity : } else { enableEdgeToEdge() } @@ -170,7 +170,7 @@ index 347f8bedd6..aadafb2593 100644 } val convoTransitionState = ConversationTransitionState.remember(isSplitPane) -@@ -626,7 +628,7 @@ class MainActivity : +@@ -621,7 +623,7 @@ class MainActivity : Column( modifier = Modifier .clip(contentLayoutData.navigationBarShape) @@ -299,7 +299,7 @@ index 6acd230877..7f9e0cacb5 100644 } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java -index 2952e812d8..c61bf7c840 100644 +index 0f4a96bc8e..520d835dca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java @@ -1,7 +1,5 @@ @@ -577,10 +577,10 @@ index 9e080f65b8..d65ba284d3 100644 val language: String, val isCompactNavigationBar: Boolean diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/appearance/AppearanceSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/appearance/AppearanceSettingsViewModel.kt -index b8d0f12674..e7fb539d5c 100644 +index 85398c05e6..131b3f3426 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/appearance/AppearanceSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/appearance/AppearanceSettingsViewModel.kt -@@ -20,9 +20,9 @@ class AppearanceSettingsViewModel : ViewModel() { +@@ -21,9 +21,9 @@ class AppearanceSettingsViewModel : ViewModel() { store.update { getState() } } @@ -593,13 +593,13 @@ index b8d0f12674..e7fb539d5c 100644 SplashScreenUtil.setSplashScreenThemeIfNecessary(activity, theme) } -@@ -41,6 +41,7 @@ class AppearanceSettingsViewModel : ViewModel() { +@@ -42,6 +42,7 @@ class AppearanceSettingsViewModel : ViewModel() { private fun getState(): AppearanceSettingsState { return AppearanceSettingsState( SignalStore.settings.theme, + SignalStore.settings.isDynamicColorsEnabled, SignalStore.settings.messageFontSize, - SignalStore.settings.language, + PlainTextKeyValueStore.language, SignalStore.settings.useCompactNavigationBar diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalFragment.kt index 8e840e8eea..c2d21f258d 100644 @@ -664,10 +664,10 @@ index 960882133d..9e57182fc2 100644 enableEdgeToEdge() setContentView(R.layout.username_qr_image_selection_activity) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallActivity.kt -index b20457eb37..e39bea32e6 100644 +index 3dd7490e56..8fada10899 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallActivity.kt -@@ -82,6 +82,8 @@ import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet +@@ -84,6 +84,8 @@ import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet import org.thoughtcrime.securesms.service.webrtc.CallLinkDisconnectReason import org.thoughtcrime.securesms.service.webrtc.SignalCallManager import org.thoughtcrime.securesms.sms.MessageSender @@ -676,7 +676,7 @@ index b20457eb37..e39bea32e6 100644 import org.thoughtcrime.securesms.util.FullscreenHelper import org.thoughtcrime.securesms.util.RemoteConfig import org.thoughtcrime.securesms.util.TextSecurePreferences -@@ -126,6 +128,7 @@ class WebRtcCallActivity : BaseActivity(), SafetyNumberChangeDialog.Callback, Re +@@ -137,6 +139,7 @@ class WebRtcCallActivity : BaseActivity(), SafetyNumberChangeDialog.Callback, Re private var ephemeralStateDisposable = Disposable.empty() private val callPermissionsDialogController = CallPermissionsDialogController() private val eventBusSubscriber = EventBusSubscriber() @@ -684,7 +684,7 @@ index b20457eb37..e39bea32e6 100644 private val mediaProjectionLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == RESULT_OK && result.data != null) { AppDependencies.signalCallManager.startScreenShare(result.data!!) -@@ -144,6 +147,7 @@ class WebRtcCallActivity : BaseActivity(), SafetyNumberChangeDialog.Callback, Re +@@ -155,6 +158,7 @@ class WebRtcCallActivity : BaseActivity(), SafetyNumberChangeDialog.Callback, Re @SuppressLint("MissingInflatedId") override fun onCreate(savedInstanceState: Bundle?) { @@ -692,7 +692,7 @@ index b20457eb37..e39bea32e6 100644 val callIntent: CallIntent = getCallIntent() Log.i(TAG, "onCreate(${callIntent.isStartedFromFullScreen})") -@@ -255,6 +259,7 @@ class WebRtcCallActivity : BaseActivity(), SafetyNumberChangeDialog.Callback, Re +@@ -270,6 +274,7 @@ class WebRtcCallActivity : BaseActivity(), SafetyNumberChangeDialog.Callback, Re override fun onResume() { Log.i(TAG, "onResume()") super.onResume() @@ -714,7 +714,7 @@ index 051afd67c5..2443b290f1 100644 } ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java -index e64788b483..f23f213570 100644 +index 1c6e4ae2c5..bce9bf3063 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -129,6 +129,7 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreview; @@ -793,10 +793,10 @@ index 2fc122fe05..1c4ce2306e 100644 collapsedButton.setVisibility(VISIBLE); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/NewConversationActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/NewConversationActivity.kt -index 28d0459bfa..e27ed7465f 100644 +index d0970aa94a..765b8b0bbf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/NewConversationActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/NewConversationActivity.kt -@@ -60,11 +60,24 @@ import org.thoughtcrime.securesms.recipients.ui.RecipientSelection +@@ -68,11 +68,24 @@ import org.thoughtcrime.securesms.recipients.ui.RecipientSelection import org.thoughtcrime.securesms.recipients.ui.findby.FindByActivity import org.thoughtcrime.securesms.recipients.ui.findby.FindByMode import org.thoughtcrime.securesms.util.CommunicationActions @@ -849,10 +849,10 @@ index 5b58874ee0..10a6aab945 100644 ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt -index f47ac2773b..946f331f86 100644 +index ff87ff9097..46e5ad71b4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt -@@ -404,6 +404,7 @@ import java.util.Optional +@@ -410,6 +410,7 @@ import java.util.Optional import java.util.concurrent.ExecutionException import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.milliseconds @@ -860,7 +860,7 @@ index f47ac2773b..946f331f86 100644 import org.signal.core.ui.R as CoreUiR /** -@@ -1869,9 +1870,9 @@ class ConversationFragment : +@@ -1930,9 +1931,9 @@ class ConversationFragment : if (!resources.isSplitPane() || isFullScreenPane) { binding.toolbar.setNavigationIcon(CoreUiR.drawable.symbol_arrow_start_24) binding.toolbar.navigationIcon?.setTint( @@ -872,7 +872,7 @@ index f47ac2773b..946f331f86 100644 ) ) binding.toolbar.setNavigationContentDescription(R.string.ConversationFragment__content_description_back_button) -@@ -1955,12 +1956,12 @@ class ConversationFragment : +@@ -2016,12 +2017,12 @@ class ConversationFragment : val wallpaperEnabled = chatWallpaper != null || recipient.isReleaseNotes @@ -887,7 +887,7 @@ index f47ac2773b..946f331f86 100644 } ) -@@ -1986,9 +1987,9 @@ class ConversationFragment : +@@ -2047,9 +2048,9 @@ class ConversationFragment : ) binding.scrollDateHeader.setTextColor( @@ -899,7 +899,7 @@ index f47ac2773b..946f331f86 100644 ) ) -@@ -2028,10 +2029,10 @@ class ConversationFragment : +@@ -2089,10 +2090,10 @@ class ConversationFragment : val navColor = if (hasWallpaper) { R.color.conversation_navigation_wallpaper } else { @@ -912,7 +912,7 @@ index f47ac2773b..946f331f86 100644 chatScrims.attachmentKeyboardColor = androidx.compose.ui.graphics.Color( ContextCompat.getColor( requireContext(), -@@ -4397,12 +4398,12 @@ class ConversationFragment : +@@ -4490,12 +4491,12 @@ class ConversationFragment : } } @@ -1045,7 +1045,7 @@ index a383e0d872..52d106744d 100644 } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java -index 3f24462315..75fda86f2d 100644 +index 3f6101fbee..42f176cf56 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java @@ -16,8 +16,6 @@ @@ -1153,77 +1153,56 @@ index 0b2327b9c3..e4e604106b 100644 ImageViewCompat.setImageTintList(navButton, iconTint) ImageViewCompat.setImageTintList(clearButton, iconTint) input.setHintTextColor(iconTint) -diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java -index 906390fa01..bc085dd4a3 100644 ---- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java -+++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java -@@ -41,6 +41,7 @@ public final class SettingsValues extends SignalStoreValues { - public static final String THREAD_TRIM_ENABLED = "pref_trim_threads"; - - public static final String THEME = "settings.theme"; -+ public static final String DYNAMIC_COLORS_ENABLED = "settings.dynamicColors"; - public static final String MESSAGE_FONT_SIZE = "settings.message.font.size"; - public static final String LANGUAGE = "settings.language"; - public static final String PREFER_SYSTEM_EMOJI = "settings.use.system.emoji"; -@@ -78,6 +79,7 @@ public final class SettingsValues extends SignalStoreValues { - private static final String KEEP_MUTED_CHATS_ARCHIVED = "settings.keepMutedChatsArchived"; - private static final String USE_COMPACT_NAVIGATION_BAR = "settings.useCompactNavigationBar"; - private static final String THREAD_TRIM_SYNC_TO_LINKED_DEVICES = "settings.storage.syncThreadTrimDeletes"; -+ private static final String IGNORE_REMOTE_DELETE = "settings.ignore.remote.delete"; - private static final String PASSPHRASE_DISABLED = "settings.passphrase.disabled"; - private static final String PASSPHRASE_TIMEOUT_ENABLED = "settings.passphrase.timeout.enabled"; - private static final String PASSPHRASE_TIMEOUT = "settings.passphrase.timeout"; -@@ -136,6 +138,7 @@ public final class SettingsValues extends SignalStoreValues { - THREAD_TRIM_ENABLED, - LANGUAGE, - THEME, -+ DYNAMIC_COLORS_ENABLED, - MESSAGE_FONT_SIZE, - PREFER_SYSTEM_EMOJI, - ENTER_KEY_SENDS, -@@ -157,6 +160,7 @@ public final class SettingsValues extends SignalStoreValues { - KEEP_MUTED_CHATS_ARCHIVED, - USE_COMPACT_NAVIGATION_BAR, - THREAD_TRIM_SYNC_TO_LINKED_DEVICES, -+ IGNORE_REMOTE_DELETE, - PASSPHRASE_DISABLED, - PASSPHRASE_TIMEOUT_ENABLED, - PASSPHRASE_TIMEOUT, -@@ -250,10 +254,19 @@ public final class SettingsValues extends SignalStoreValues { - } +diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.kt +index c18fb1a306..f4cbf929d9 100644 +--- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.kt ++++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.kt +@@ -35,6 +35,8 @@ class SettingsValues internal constructor(store: KeyValueStore, context: Context + const val THREAD_TRIM_LENGTH = "pref_trim_length" + const val THREAD_TRIM_ENABLED = "pref_trim_threads" + ++ const val DYNAMIC_COLORS_ENABLED = "settings.dynamicColors" ++ private const val IGNORE_REMOTE_DELETE = "settings.ignore.remote.delete" + const val THEME = "settings.theme" + const val MESSAGE_FONT_SIZE = "settings.message.font.size" + const val PREFER_SYSTEM_EMOJI = "settings.use.system.emoji" +@@ -113,6 +115,8 @@ class SettingsValues internal constructor(store: KeyValueStore, context: Context + THREAD_TRIM_LENGTH, + THREAD_TRIM_ENABLED, + THEME, ++ DYNAMIC_COLORS_ENABLED, ++ IGNORE_REMOTE_DELETE, + MESSAGE_FONT_SIZE, + PREFER_SYSTEM_EMOJI, + ENTER_KEY_SENDS, +@@ -193,10 +197,25 @@ class SettingsValues internal constructor(store: KeyValueStore, context: Context + var theme: Theme + get() = Theme.deserialize(getString(THEME, TextSecurePreferences.getTheme(AppDependencies.application))) + set(value) { +- putString(THEME, value.serialize()) +- configurationSettingChanged.postValue(THEME) ++ setTheme(value, isDynamicColorsEnabled) + } - public void setTheme(@NonNull Theme theme) { -+ setTheme(theme, isDynamicColorsEnabled()); ++ fun setTheme(theme: Theme, useDynamicColors: Boolean) { ++ putString(THEME, theme.serialize()) ++ putBoolean(DYNAMIC_COLORS_ENABLED, useDynamicColors) ++ configurationSettingChanged.postValue(THEME) + } + -+ public void setTheme(@NonNull Theme theme, boolean useDynamicColors) { - putString(THEME, theme.serialize()); -+ putBoolean(DYNAMIC_COLORS_ENABLED, useDynamicColors); - onConfigurationSettingChanged.postValue(THEME); - } - -+ public boolean isDynamicColorsEnabled() { -+ return getBoolean(DYNAMIC_COLORS_ENABLED, false); -+ } ++ val isDynamicColorsEnabled: Boolean ++ get() = getBoolean(DYNAMIC_COLORS_ENABLED, false) + - public int getMessageFontSize() { - return getInteger(MESSAGE_FONT_SIZE, TextSecurePreferences.getMessageBodyTextSize(AppDependencies.getApplication())); - } -@@ -599,6 +612,14 @@ public final class SettingsValues extends SignalStoreValues { - return getBoolean(USE_COMPACT_NAVIGATION_BAR, false); - } - -+ public boolean isIgnoreRemoteDeleteEnabled() { -+ return getBoolean(IGNORE_REMOTE_DELETE, false); -+ } ++ val isIgnoreRemoteDeleteEnabled: Boolean ++ get() = getBoolean(IGNORE_REMOTE_DELETE, false) + -+ public void setIgnoreRemoteDelete(boolean enabled) { -+ putBoolean(IGNORE_REMOTE_DELETE, enabled); ++ fun setIgnoreRemoteDelete(enabled: Boolean) { ++ putBoolean(IGNORE_REMOTE_DELETE, enabled) + } + - public void setPassphraseDisabled(boolean disabled) { - putBoolean(PASSPHRASE_DISABLED, disabled); - } + var messageFontSize: Int + get() = getInteger(MESSAGE_FONT_SIZE, TextSecurePreferences.getMessageBodyTextSize(AppDependencies.application)) + set(value) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogActivity.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogActivity.java index 6d9f12eea0..21b64822c2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogActivity.java @@ -1535,10 +1514,10 @@ index b709e562b8..2794bb4fd2 100644 case CONNECTED: proxyStatus.setText(R.string.preferences_connected_to_proxy); diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragment.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragment.java -index 520f25032c..e8b4fbbf02 100644 +index 76cc7dc4d6..9d29648622 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragment.java -@@ -190,8 +190,8 @@ public class UsernameEditFragment extends LoggingFragment { +@@ -182,8 +182,8 @@ public class UsernameEditFragment extends LoggingFragment { case DISCRIMINATOR_CANNOT_START_WITH_0 -> getString(R.string.UsernameEditFragment__this_number_cant_start_with_0); }; @@ -1550,7 +1529,7 @@ index 520f25032c..e8b4fbbf02 100644 binding.usernameTextFocusedStroke.setBackgroundColor(color); binding.usernameTextWrapper.setHintTextColor(ColorStateList.valueOf(color)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt -index 89c81e5071..c4f053968f 100644 +index 59bf8c3a04..4822091569 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt @@ -61,7 +61,6 @@ import org.whispersystems.signalservice.api.util.OptionalUtil @@ -1561,7 +1540,7 @@ index 89c81e5071..c4f053968f 100644 /** * A recipient represents something you can send messages to or receive messages from. They could be individuals, groups, or even distribution lists. -@@ -708,7 +707,7 @@ class Recipient( +@@ -734,7 +733,7 @@ class Recipient( if (isIndividual && !isSelf) { val isLtr = ViewUtil.isLtr(context) @@ -1571,10 +1550,10 @@ index 89c81e5071..c4f053968f 100644 if (isLtr) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/RecipientPicker.kt b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/RecipientPicker.kt -index cff02b51b8..cc56a17a38 100644 +index a47bf1df9f..87877ad391 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/RecipientPicker.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/RecipientPicker.kt -@@ -320,7 +320,7 @@ private suspend fun showItemContextMenu( +@@ -323,7 +323,7 @@ private suspend fun showItemContextMenu( val messageItem = ActionItem( iconRes = CoreUiR.drawable.ic_chat_message_24, title = context.getString(R.string.NewConversationActivity__message), @@ -1583,7 +1562,7 @@ index cff02b51b8..cc56a17a38 100644 action = { callbacks.onMessage(recipient.id) } ) add(messageItem) -@@ -329,7 +329,7 @@ private suspend fun showItemContextMenu( +@@ -332,7 +332,7 @@ private suspend fun showItemContextMenu( val voiceCallItem = ActionItem( iconRes = R.drawable.ic_phone_right_24, title = context.getString(R.string.NewConversationActivity__audio_call), @@ -1592,7 +1571,7 @@ index cff02b51b8..cc56a17a38 100644 action = { callbacks.onVoiceCall(recipient) } ) add(voiceCallItem) -@@ -339,7 +339,7 @@ private suspend fun showItemContextMenu( +@@ -342,7 +342,7 @@ private suspend fun showItemContextMenu( val videoCallItem = ActionItem( iconRes = R.drawable.ic_video_call_24, title = context.getString(R.string.NewConversationActivity__video_call), @@ -1601,7 +1580,7 @@ index cff02b51b8..cc56a17a38 100644 action = { callbacks.onVideoCall(recipient) } ) add(videoCallItem) -@@ -349,7 +349,7 @@ private suspend fun showItemContextMenu( +@@ -352,7 +352,7 @@ private suspend fun showItemContextMenu( val removeItem = ActionItem( iconRes = R.drawable.ic_minus_circle_20, title = context.getString(R.string.NewConversationActivity__remove), @@ -1610,7 +1589,7 @@ index cff02b51b8..cc56a17a38 100644 action = { callbacks.onRemove(recipient) } ) add(removeItem) -@@ -359,7 +359,7 @@ private suspend fun showItemContextMenu( +@@ -362,7 +362,7 @@ private suspend fun showItemContextMenu( val blockItem = ActionItem( iconRes = R.drawable.ic_block_tinted_24, title = context.getString(R.string.NewConversationActivity__block), @@ -2793,7 +2772,7 @@ index 41467894f7..b1a94b4fa7 100644 content = imageContent ) diff --git a/core/ui/src/main/java/org/signal/core/ui/compose/theme/SignalTheme.kt b/core/ui/src/main/java/org/signal/core/ui/compose/theme/SignalTheme.kt -index 2d79b5165b..d44a03f1ab 100644 +index f867cfd326..913005e6ca 100644 --- a/core/ui/src/main/java/org/signal/core/ui/compose/theme/SignalTheme.kt +++ b/core/ui/src/main/java/org/signal/core/ui/compose/theme/SignalTheme.kt @@ -1,21 +1,31 @@ @@ -2859,16 +2838,16 @@ index 2d79b5165b..d44a03f1ab 100644 surfaceVariant = Color(0xFFE7E6F2), background = Color(0xFFF6F6FF), error = Color(0xFFE00052), -@@ -134,7 +159,7 @@ private val lightExtendedColors = ExtendedColors( - private val darkExtendedColors = ExtendedColors( +@@ -137,7 +162,7 @@ private val darkExtendedColors = ExtendedColors( neutralSurface = Color(0x14FFFFFF), + neutralFill = Color(0x33FFFFFF), colorOnCustom = Color(0xFFFFFFFF), - colorOnCustomVariant = Color(0x18FFFFFF), + colorOnCustomVariant = Color(0xB3FFFFFF), colorSurface1 = Color(0xFF181137), colorSurface2 = Color(0xFF1E1645), colorSurface3 = Color(0xFF231A4E), -@@ -161,13 +186,28 @@ private val darkExtendedColors = ExtendedColors( +@@ -165,13 +190,28 @@ private val darkExtendedColors = ExtendedColors( ) private val darkColorScheme = darkColorScheme( @@ -2899,7 +2878,7 @@ index 2d79b5165b..d44a03f1ab 100644 surfaceVariant = Color(0xFF302F33), background = Color(0xFF100E1F), error = Color(0xFFFE006E), -@@ -182,35 +222,38 @@ private val darkColorScheme = darkColorScheme( +@@ -186,35 +226,38 @@ private val darkColorScheme = darkColorScheme( outline = Color(0xFF5D5D66) ) @@ -2957,7 +2936,7 @@ index 2d79b5165b..d44a03f1ab 100644 typography = typography, content = content ) -@@ -231,6 +274,123 @@ fun ForceLightColors(content: @Composable () -> Unit) { +@@ -235,6 +278,123 @@ fun ForceLightColors(content: @Composable () -> Unit) { } } diff --git a/patches/0002-fix-adapt-dynamic-theme-to-inline-media-send.patch b/patches/0002-fix-adapt-dynamic-theme-to-inline-media-send.patch index 59b94d8..7055a08 100644 --- a/patches/0002-fix-adapt-dynamic-theme-to-inline-media-send.patch +++ b/patches/0002-fix-adapt-dynamic-theme-to-inline-media-send.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Fri, 21 Aug 2026 14:14:19 +0000 -Subject: [PATCH 02/24] fix: adapt dynamic theme to inline media send +Subject: [PATCH 02/27] fix: adapt dynamic theme to inline media send Signed-off-by: Vesper Materializer --- diff --git a/patches/0003-feat-allow-disabling-debug-logs.patch b/patches/0003-feat-allow-disabling-debug-logs.patch index d5034d7..d1d89cd 100644 --- a/patches/0003-feat-allow-disabling-debug-logs.patch +++ b/patches/0003-feat-allow-disabling-debug-logs.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Thu, 6 Aug 2026 07:44:38 +0000 -Subject: [PATCH 03/24] feat: allow disabling debug logs +Subject: [PATCH 03/27] feat: allow disabling debug logs Signed-off-by: Vesper Materializer --- diff --git a/patches/0004-feat-optionally-preserve-remotely-deleted-messages.patch b/patches/0004-feat-optionally-preserve-remotely-deleted-messages.patch index 7940ae4..1857210 100644 --- a/patches/0004-feat-optionally-preserve-remotely-deleted-messages.patch +++ b/patches/0004-feat-optionally-preserve-remotely-deleted-messages.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Sat, 15 Aug 2026 08:31:14 +0000 -Subject: [PATCH 04/24] feat: optionally preserve remotely deleted messages +Subject: [PATCH 04/27] feat: optionally preserve remotely deleted messages Signed-off-by: Vesper Materializer --- diff --git a/patches/0005-feat-finish-Vesper-settings-and-help-surfaces.patch b/patches/0005-feat-finish-Vesper-settings-and-help-surfaces.patch index 8b48ae5..c8b917c 100644 --- a/patches/0005-feat-finish-Vesper-settings-and-help-surfaces.patch +++ b/patches/0005-feat-finish-Vesper-settings-and-help-surfaces.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: amber Date: Sat, 25 Jul 2026 18:30:42 +0100 -Subject: [PATCH 05/24] feat: finish Vesper settings and help surfaces +Subject: [PATCH 05/27] feat: finish Vesper settings and help surfaces Adapted from Molly work by Oscar Mira and other Molly contributors. diff --git a/patches/0006-feat-add-installable-debug-build-variant.patch b/patches/0006-feat-add-installable-debug-build-variant.patch index 7834f4e..0bab2e9 100644 --- a/patches/0006-feat-add-installable-debug-build-variant.patch +++ b/patches/0006-feat-add-installable-debug-build-variant.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Thu, 6 Aug 2026 07:45:10 +0000 -Subject: [PATCH 06/24] feat: add installable debug build variant +Subject: [PATCH 06/27] feat: add installable debug build variant Signed-off-by: Vesper Materializer --- diff --git a/patches/0007-feat-support-16-KB-devices-without-MobileCoin-paymen.patch b/patches/0007-feat-support-16-KB-devices-without-MobileCoin-paymen.patch index 80f9aa9..316065d 100644 --- a/patches/0007-feat-support-16-KB-devices-without-MobileCoin-paymen.patch +++ b/patches/0007-feat-support-16-KB-devices-without-MobileCoin-paymen.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Sat, 15 Aug 2026 08:31:55 +0000 -Subject: [PATCH 07/24] feat: support 16 KB devices without MobileCoin payments +Subject: [PATCH 07/27] feat: support 16 KB devices without MobileCoin payments Signed-off-by: Vesper Materializer --- diff --git a/patches/0008-fix-refine-Vesper-conversation-appearance.patch b/patches/0008-fix-refine-Vesper-conversation-appearance.patch index edc422c..520e1da 100644 --- a/patches/0008-fix-refine-Vesper-conversation-appearance.patch +++ b/patches/0008-fix-refine-Vesper-conversation-appearance.patch @@ -1,13 +1,13 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Sat, 15 Aug 2026 08:32:16 +0000 -Subject: [PATCH 08/24] fix: refine Vesper conversation appearance +Subject: [PATCH 08/27] fix: refine Vesper conversation appearance Signed-off-by: Vesper Materializer --- .../settings/app/help/HelpSettingsFragment.kt | 30 +--------------- .../V2ConversationItemTextOnlyViewHolder.kt | 5 +-- - .../securesms/keyvalue/SettingsValues.java | 2 +- + .../securesms/keyvalue/SettingsValues.kt | 2 +- .../reactions/ReactionsConversationView.java | 35 +++++++++++++++++-- 4 files changed, 38 insertions(+), 34 deletions(-) @@ -120,19 +120,19 @@ index 24cc490730..a21a43e16b 100644 conversationContext.clickListener.onItemClick(getMultiselectPartForLatestTouch()) } } -diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java -index bc085dd4a3..f57d6f1724 100644 ---- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java -+++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java -@@ -264,7 +264,7 @@ public final class SettingsValues extends SignalStoreValues { +diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.kt +index abf3e450c0..55e740629d 100644 +--- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.kt ++++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.kt +@@ -209,7 +209,7 @@ class SettingsValues internal constructor(store: KeyValueStore, context: Context } - public boolean isDynamicColorsEnabled() { -- return getBoolean(DYNAMIC_COLORS_ENABLED, false); -+ return getBoolean(DYNAMIC_COLORS_ENABLED, true); - } + val isDynamicColorsEnabled: Boolean +- get() = getBoolean(DYNAMIC_COLORS_ENABLED, false) ++ get() = getBoolean(DYNAMIC_COLORS_ENABLED, true) - public int getMessageFontSize() { + val isIgnoreRemoteDeleteEnabled: Boolean + get() = getBoolean(IGNORE_REMOTE_DELETE, false) diff --git a/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionsConversationView.java b/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionsConversationView.java index f984e9acd3..7378fbc511 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionsConversationView.java diff --git a/patches/0009-chore-clarify-licensing-and-interface-copy.patch b/patches/0009-chore-clarify-licensing-and-interface-copy.patch index a765cd3..7acc5ec 100644 --- a/patches/0009-chore-clarify-licensing-and-interface-copy.patch +++ b/patches/0009-chore-clarify-licensing-and-interface-copy.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Sat, 15 Aug 2026 08:32:53 +0000 -Subject: [PATCH 09/24] chore: clarify licensing and interface copy +Subject: [PATCH 09/27] chore: clarify licensing and interface copy Signed-off-by: Vesper Materializer --- diff --git a/patches/0010-fix-make-Vesper-build-identity-deterministic.patch b/patches/0010-fix-make-Vesper-build-identity-deterministic.patch index 471d801..f9910e5 100644 --- a/patches/0010-fix-make-Vesper-build-identity-deterministic.patch +++ b/patches/0010-fix-make-Vesper-build-identity-deterministic.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Sat, 15 Aug 2026 08:33:23 +0000 -Subject: [PATCH 10/24] fix: make Vesper build identity deterministic +Subject: [PATCH 10/27] fix: make Vesper build identity deterministic Signed-off-by: Vesper Materializer --- diff --git a/patches/0011-feat-complete-Vesper-theme-controls.patch b/patches/0011-feat-complete-Vesper-theme-controls.patch index 12f5d17..04150cd 100644 --- a/patches/0011-feat-complete-Vesper-theme-controls.patch +++ b/patches/0011-feat-complete-Vesper-theme-controls.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Thu, 6 Aug 2026 07:45:11 +0000 -Subject: [PATCH 11/24] feat: complete Vesper theme controls +Subject: [PATCH 11/27] feat: complete Vesper theme controls Signed-off-by: Vesper Materializer --- @@ -12,7 +12,7 @@ Signed-off-by: Vesper Materializer - -- -+ - - - -diff --git a/app/src/main/res/drawable/camera_send_button_background.xml b/app/src/main/res/drawable/camera_send_button_background.xml -index 7bcb1e9d44..d42c4edfbb 100644 ---- a/app/src/main/res/drawable/camera_send_button_background.xml -+++ b/app/src/main/res/drawable/camera_send_button_background.xml -@@ -1,4 +1,4 @@ - - -- -- -\ No newline at end of file -+ -+ diff --git a/app/src/main/res/drawable/compose_background.xml b/app/src/main/res/drawable/compose_background.xml index 583b389025..64f21f4ded 100644 --- a/app/src/main/res/drawable/compose_background.xml @@ -694,7 +665,7 @@ index d58facf745..fd13072f77 100644 \ No newline at end of file + diff --git a/app/src/main/res/layout/v2_conversation_fragment.xml b/app/src/main/res/layout/v2_conversation_fragment.xml -index 100be3be57..e923326045 100644 +index a09ad5b378..bc1ec771cf 100644 --- a/app/src/main/res/layout/v2_conversation_fragment.xml +++ b/app/src/main/res/layout/v2_conversation_fragment.xml @@ -104,13 +104,13 @@ @@ -1344,10 +1315,10 @@ index bbe42c1f7c..4b698938a3 100644 @style/Signal.DayNight.Dialog.FullScreen @style/Widget.Signal.FixedRoundedCorners diff --git a/core/ui/src/main/java/org/signal/core/ui/compose/theme/SignalTheme.kt b/core/ui/src/main/java/org/signal/core/ui/compose/theme/SignalTheme.kt -index d284847ea6..183e031e82 100644 +index 38cc3755ee..ba48f6801d 100644 --- a/core/ui/src/main/java/org/signal/core/ui/compose/theme/SignalTheme.kt +++ b/core/ui/src/main/java/org/signal/core/ui/compose/theme/SignalTheme.kt -@@ -222,6 +222,71 @@ private val darkColorScheme = darkColorScheme( +@@ -226,6 +226,71 @@ private val darkColorScheme = darkColorScheme( outline = Color(0xFF5D5D66) ) @@ -1419,7 +1390,7 @@ index d284847ea6..183e031e82 100644 @Composable fun SignalTheme( isDarkMode: Boolean = LocalConfiguration.current.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES, -@@ -231,7 +296,8 @@ fun SignalTheme( +@@ -235,7 +300,8 @@ fun SignalTheme( ) { val context = LocalContext.current val maySupportDynamicColor = Build.VERSION.SDK_INT >= 31 @@ -1429,7 +1400,7 @@ index d284847ea6..183e031e82 100644 // API 31-33's Compose fallback is more saturated than the platform resource // palette. Use the same resources for XML and Compose. -@@ -243,10 +309,12 @@ fun SignalTheme( +@@ -247,10 +313,12 @@ fun SignalTheme( if (isDarkMode) systemAlignedDarkColorScheme() else systemAlignedLightColorScheme() } } @@ -1445,7 +1416,7 @@ index d284847ea6..183e031e82 100644 val snackbarColors = snackbarColors(colorScheme) ProvideIncognitoKeyboard(enabled = incognitoKeyboardEnabled) { -@@ -355,7 +423,13 @@ private fun isThemeUsingDynamicColors(context: Context): Boolean { +@@ -359,7 +427,13 @@ private fun isThemeUsingDynamicColors(context: Context): Boolean { typedValue.data != 0 } @@ -1460,7 +1431,7 @@ index d284847ea6..183e031e82 100644 return when { isDynamic -> lightExtendedColors.copy( colorSurface1 = colorScheme.surfaceContainerLow, -@@ -364,8 +438,10 @@ private fun extendedColors(colorScheme: ColorScheme, isDarkMode: Boolean, isDyna +@@ -368,8 +442,10 @@ private fun extendedColors(colorScheme: ColorScheme, isDarkMode: Boolean, isDyna colorSurface4 = colorScheme.surfaceContainerHighest, colorSurface5 = if (isDarkMode) colorScheme.surfaceBright else colorScheme.surfaceDim ) diff --git a/patches/0012-fix-stabilize-identity-and-media-controls.patch b/patches/0012-fix-stabilize-identity-and-media-controls.patch index 68c8028..ab17c4d 100644 --- a/patches/0012-fix-stabilize-identity-and-media-controls.patch +++ b/patches/0012-fix-stabilize-identity-and-media-controls.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Thu, 6 Aug 2026 07:45:11 +0000 -Subject: [PATCH 12/24] fix: stabilize identity and media controls +Subject: [PATCH 12/27] fix: stabilize identity and media controls Signed-off-by: Vesper Materializer --- diff --git a/patches/0013-fix-theme-media-control-backgrounds.patch b/patches/0013-fix-theme-media-control-backgrounds.patch index 04a266b..5cdb1f8 100644 --- a/patches/0013-fix-theme-media-control-backgrounds.patch +++ b/patches/0013-fix-theme-media-control-backgrounds.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Thu, 6 Aug 2026 07:45:11 +0000 -Subject: [PATCH 13/24] fix: theme media control backgrounds +Subject: [PATCH 13/27] fix: theme media control backgrounds Signed-off-by: Vesper Materializer --- diff --git a/patches/0014-feat-integrate-Vesper-logo-and-wordmark.patch b/patches/0014-feat-integrate-Vesper-logo-and-wordmark.patch index 297b287..3dd5f34 100644 --- a/patches/0014-feat-integrate-Vesper-logo-and-wordmark.patch +++ b/patches/0014-feat-integrate-Vesper-logo-and-wordmark.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Thu, 6 Aug 2026 07:45:11 +0000 -Subject: [PATCH 14/24] feat: integrate Vesper logo and wordmark +Subject: [PATCH 14/27] feat: integrate Vesper logo and wordmark Signed-off-by: Vesper Materializer --- diff --git a/patches/0015-feat-use-profile-avatar-as-main-menu-trigger.patch b/patches/0015-feat-use-profile-avatar-as-main-menu-trigger.patch index e5f0140..955d635 100644 --- a/patches/0015-feat-use-profile-avatar-as-main-menu-trigger.patch +++ b/patches/0015-feat-use-profile-avatar-as-main-menu-trigger.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Thu, 6 Aug 2026 07:45:12 +0000 -Subject: [PATCH 15/24] feat: use profile avatar as main menu trigger +Subject: [PATCH 15/27] feat: use profile avatar as main menu trigger Signed-off-by: Vesper Materializer --- diff --git a/patches/0016-fix-use-Vesper-icon-on-Android-splash-screen.patch b/patches/0016-fix-use-Vesper-icon-on-Android-splash-screen.patch index e898388..018998c 100644 --- a/patches/0016-fix-use-Vesper-icon-on-Android-splash-screen.patch +++ b/patches/0016-fix-use-Vesper-icon-on-Android-splash-screen.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Thu, 6 Aug 2026 07:45:12 +0000 -Subject: [PATCH 16/24] fix: use Vesper icon on Android splash screen +Subject: [PATCH 16/27] fix: use Vesper icon on Android splash screen Signed-off-by: Vesper Materializer --- diff --git a/patches/0017-feat-add-Vesper-author-verification-badge.patch b/patches/0017-feat-add-Vesper-author-verification-badge.patch index e0411fa..4641407 100644 --- a/patches/0017-feat-add-Vesper-author-verification-badge.patch +++ b/patches/0017-feat-add-Vesper-author-verification-badge.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Thu, 6 Aug 2026 07:45:12 +0000 -Subject: [PATCH 17/24] feat: add Vesper author verification badge +Subject: [PATCH 17/27] feat: add Vesper author verification badge Signed-off-by: Vesper Materializer --- @@ -44,7 +44,7 @@ index 2443b290f1..928dfb5149 100644 val drawable = context.requireDrawable(CoreUiR.drawable.symbol_person_circle_24).apply { setTint(ThemeUtil.getThemedColor(context, com.google.android.material.R.attr.colorOnSurface)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.kt -index a09b62ef1c..36a02fc7fa 100644 +index 208ddc1f0c..504d6a204d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.kt @@ -112,6 +112,7 @@ class ConversationHeaderView : AbstractComposeView { @@ -53,9 +53,9 @@ index a09b62ef1c..36a02fc7fa 100644 val isOfficialAccount = recipient.showVerified + val showVerifiedBadge = recipient.showVerifiedBadge - val showUnverifiedName = if (recipient.isGroup) { - !info.groupInfo.nameVerified -@@ -132,7 +133,7 @@ class ConversationHeaderView : AbstractComposeView { + val hasUsernameOrSharedName = recipient.hasUsernameOrSharedName + +@@ -134,7 +135,7 @@ class ConversationHeaderView : AbstractComposeView { ConversationHeaderContent( recipientId = recipient.id, displayName = displayName, @@ -78,10 +78,10 @@ index 455a82e044..2812e6b6d1 100644 } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt -index c4f053968f..9205cd95a4 100644 +index 4822091569..1882cd4c9d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt -@@ -335,6 +335,12 @@ class Recipient( +@@ -340,6 +340,12 @@ class Recipient( /** Whether or not to show a special verified badge, indicating this is a special conversation (like release notes or note to self). */ val showVerified: Boolean = isReleaseNotes || isSelf @@ -94,7 +94,7 @@ index c4f053968f..9205cd95a4 100644 /** The notification channel, if both set and supported by the system. Otherwise null. */ val notificationChannel: String? = if (!NotificationChannels.supported()) null else notificationChannelValue -@@ -692,7 +698,7 @@ class Recipient( +@@ -718,7 +724,7 @@ class Recipient( return buildSpannedString { append(name) diff --git a/patches/0018-fix-theme-automatic-message-bubbles.patch b/patches/0018-fix-theme-automatic-message-bubbles.patch index bbeb444..2baefa0 100644 --- a/patches/0018-fix-theme-automatic-message-bubbles.patch +++ b/patches/0018-fix-theme-automatic-message-bubbles.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Thu, 6 Aug 2026 07:45:12 +0000 -Subject: [PATCH 18/24] fix: theme automatic message bubbles +Subject: [PATCH 18/27] fix: theme automatic message bubbles Signed-off-by: Vesper Materializer --- diff --git a/patches/0019-feat-load-shared-Vesper-account-configuration.patch b/patches/0019-feat-load-shared-Vesper-account-configuration.patch index 306b72f..b25ddf8 100644 --- a/patches/0019-feat-load-shared-Vesper-account-configuration.patch +++ b/patches/0019-feat-load-shared-Vesper-account-configuration.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Thu, 6 Aug 2026 07:45:12 +0000 -Subject: [PATCH 19/24] feat: load shared Vesper account configuration +Subject: [PATCH 19/27] feat: load shared Vesper account configuration Signed-off-by: Vesper Materializer --- diff --git a/patches/0020-fix-adapt-dynamic-theme-to-Signal-v8.21-edge-to-edge.patch b/patches/0020-fix-adapt-dynamic-theme-to-Signal-v8.21-edge-to-edge.patch index 117272e..d61d8a9 100644 --- a/patches/0020-fix-adapt-dynamic-theme-to-Signal-v8.21-edge-to-edge.patch +++ b/patches/0020-fix-adapt-dynamic-theme-to-Signal-v8.21-edge-to-edge.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Thu, 6 Aug 2026 07:45:12 +0000 -Subject: [PATCH 20/24] fix: adapt dynamic theme to Signal v8.21 edge-to-edge +Subject: [PATCH 20/27] fix: adapt dynamic theme to Signal v8.21 edge-to-edge Signed-off-by: Vesper Materializer --- diff --git a/patches/0021-feat-add-backup-subscription-diagnostics.patch b/patches/0021-feat-add-backup-subscription-diagnostics.patch index 8dc6b57..1382156 100644 --- a/patches/0021-feat-add-backup-subscription-diagnostics.patch +++ b/patches/0021-feat-add-backup-subscription-diagnostics.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Tue, 11 Aug 2026 10:53:42 +0000 -Subject: [PATCH 21/24] feat: add backup subscription diagnostics +Subject: [PATCH 21/27] feat: add backup subscription diagnostics Signed-off-by: Vesper Materializer --- diff --git a/patches/0022-fix-isolate-backup-diagnostics-actions.patch b/patches/0022-fix-isolate-backup-diagnostics-actions.patch index f8d674e..45f95bf 100644 --- a/patches/0022-fix-isolate-backup-diagnostics-actions.patch +++ b/patches/0022-fix-isolate-backup-diagnostics-actions.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Tue, 11 Aug 2026 11:21:16 +0000 -Subject: [PATCH 22/24] fix: isolate backup diagnostics actions +Subject: [PATCH 22/27] fix: isolate backup diagnostics actions Signed-off-by: Vesper Materializer --- @@ -54,13 +54,13 @@ index aa8edb8ef7..e89ec386ca 100644 } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt -index 4501ee809e..fe54f98fd8 100644 +index 4291e0ad03..f169cdc289 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt @@ -96,7 +96,11 @@ object RecurringInAppPaymentRepository { response.result.ifPresent { result -> val lastEndOfPeriod = SignalDatabase.inAppPayments.getByLatestEndOfPeriod(type.inAppPaymentType)?.endOfPeriodSeconds ?: 0L - if (result.isActive && result.activeSubscription.endOfCurrentPeriod > lastEndOfPeriod) { + if (result.isActive && result.activeSubscription!!.endOfCurrentPeriod > lastEndOfPeriod) { - InAppPaymentKeepAliveJob.enqueueAndTrackTime(System.currentTimeMillis().milliseconds) + if (type == InAppPaymentSubscriberRecord.Type.BACKUP) { + InAppPaymentKeepAliveJob.enqueueBackup() @@ -71,10 +71,10 @@ index 4501ee809e..fe54f98fd8 100644 } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt -index 5d3bb61c46..d4e94ec4d5 100644 +index 5037078ea1..bc7ed0b8a3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt -@@ -110,12 +110,17 @@ class BackupMessagesJob private constructor( +@@ -120,12 +120,17 @@ class BackupMessagesJob private constructor( } fun enqueue() { @@ -94,10 +94,10 @@ index 5d3bb61c46..d4e94ec4d5 100644 fun cancel() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentKeepAliveJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentKeepAliveJob.kt -index 9d7317945c..36f997e117 100644 +index 7fd1aabf60..09ce1058fe 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentKeepAliveJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentKeepAliveJob.kt -@@ -89,14 +89,24 @@ class InAppPaymentKeepAliveJob private constructor( +@@ -90,14 +90,24 @@ class InAppPaymentKeepAliveJob private constructor( } @JvmStatic @@ -167,7 +167,7 @@ index 1e9fb58b2c..9947b2c4bf 100644 @Test diff --git a/app/src/test/java/org/thoughtcrime/securesms/jobs/InAppPaymentKeepAliveJobTest.kt b/app/src/test/java/org/thoughtcrime/securesms/jobs/InAppPaymentKeepAliveJobTest.kt -index 3b0f22b4fb..f3ff9359e8 100644 +index fad009be4b..00a9386f3d 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/jobs/InAppPaymentKeepAliveJobTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/jobs/InAppPaymentKeepAliveJobTest.kt @@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.jobs @@ -178,7 +178,7 @@ index 3b0f22b4fb..f3ff9359e8 100644 import assertk.assertions.isTrue import io.mockk.every import io.mockk.verify -@@ -29,7 +30,6 @@ import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription.Cha +@@ -30,7 +31,6 @@ import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription.Cha import org.whispersystems.signalservice.api.subscriptions.SubscriberId import org.whispersystems.signalservice.internal.EmptyResponse import org.whispersystems.signalservice.internal.ServiceResponse @@ -186,7 +186,7 @@ index 3b0f22b4fb..f3ff9359e8 100644 @RunWith(RobolectricTestRunner::class) @Config(manifest = Config.NONE, application = Application::class) -@@ -63,16 +63,16 @@ class InAppPaymentKeepAliveJobTest { +@@ -64,16 +64,16 @@ class InAppPaymentKeepAliveJobTest { iapSubscriptionId = null ) every { InAppPaymentsRepository.getSubscriber(InAppPaymentSubscriberRecord.Type.DONATION) } returns subscriber @@ -207,7 +207,7 @@ index 3b0f22b4fb..f3ff9359e8 100644 verify(exactly = 1) { AppDependencies.jobManager.add( match { job -> -@@ -87,7 +87,27 @@ class InAppPaymentKeepAliveJobTest { +@@ -88,7 +88,27 @@ class InAppPaymentKeepAliveJobTest { } ) } diff --git a/patches/0023-fix-apply-profile-theme-before-window-creation.patch b/patches/0023-fix-apply-profile-theme-before-window-creation.patch index 248645f..8484d39 100644 --- a/patches/0023-fix-apply-profile-theme-before-window-creation.patch +++ b/patches/0023-fix-apply-profile-theme-before-window-creation.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Vesper Materializer Date: Sun, 16 Aug 2026 13:57:30 +0000 -Subject: [PATCH 23/24] fix: apply profile theme before window creation +Subject: [PATCH 23/27] fix: apply profile theme before window creation Signed-off-by: Vesper Materializer --- diff --git a/patches/0024-fix-preserve-public-Vesper-labs-access.patch b/patches/0024-fix-preserve-public-Vesper-labs-access.patch index b76419f..0705db0 100644 --- a/patches/0024-fix-preserve-public-Vesper-labs-access.patch +++ b/patches/0024-fix-preserve-public-Vesper-labs-access.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: amber Date: Fri, 21 Aug 2026 14:29:19 +0000 -Subject: [PATCH 24/24] fix: preserve public Vesper labs access +Subject: [PATCH 24/27] fix: preserve public Vesper labs access Signed-off-by: amber --- diff --git a/patches/0025-test-cover-Vesper-settings-after-upstream-Kotlin-mig.patch b/patches/0025-test-cover-Vesper-settings-after-upstream-Kotlin-mig.patch new file mode 100644 index 0000000..f12feae --- /dev/null +++ b/patches/0025-test-cover-Vesper-settings-after-upstream-Kotlin-mig.patch @@ -0,0 +1,74 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: amber +Date: Tue, 15 Sep 2026 15:51:40 +0000 +Subject: [PATCH 25/27] test: cover Vesper settings after upstream Kotlin + migration + +Signed-off-by: amber +--- + .../keyvalue/VesperSettingsValuesTest.kt | 56 +++++++++++++++++++ + 1 file changed, 56 insertions(+) + create mode 100644 app/src/test/java/org/thoughtcrime/securesms/keyvalue/VesperSettingsValuesTest.kt + +diff --git a/app/src/test/java/org/thoughtcrime/securesms/keyvalue/VesperSettingsValuesTest.kt b/app/src/test/java/org/thoughtcrime/securesms/keyvalue/VesperSettingsValuesTest.kt +new file mode 100644 +index 0000000000..4525a7d150 +--- /dev/null ++++ b/app/src/test/java/org/thoughtcrime/securesms/keyvalue/VesperSettingsValuesTest.kt +@@ -0,0 +1,56 @@ ++/* ++ * Copyright 2026 Vesper contributors ++ * SPDX-License-Identifier: AGPL-3.0-only ++ */ ++package org.thoughtcrime.securesms.keyvalue ++ ++import android.app.Application ++import org.junit.Assert.assertEquals ++import org.junit.Assert.assertFalse ++import org.junit.Assert.assertTrue ++import org.junit.Rule ++import org.junit.Test ++import org.junit.rules.RuleChain ++import org.junit.runner.RunWith ++import org.robolectric.RobolectricTestRunner ++import org.robolectric.annotation.Config ++import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule ++import org.thoughtcrime.securesms.testutil.SignalStoreRule ++ ++@RunWith(RobolectricTestRunner::class) ++@Config(application = Application::class, sdk = [34]) ++class VesperSettingsValuesTest { ++ @get:Rule ++ val rules: RuleChain = RuleChain.outerRule(MockAppDependenciesRule()).around(SignalStoreRule()) ++ ++ @Test ++ fun `theme setter preserves an explicit dynamic color opt out`() { ++ val settings = SignalStore.settings ++ assertTrue(settings.isDynamicColorsEnabled) ++ settings.setTheme(SettingsValues.Theme.LIGHT, false) ++ settings.theme = SettingsValues.Theme.DARK ++ assertEquals(SettingsValues.Theme.DARK, settings.theme) ++ assertFalse(settings.isDynamicColorsEnabled) ++ settings.setTheme(SettingsValues.Theme.SYSTEM, true) ++ assertTrue(settings.isDynamicColorsEnabled) ++ } ++ ++ @Test ++ fun `Vesper and remote delete controls retain their defaults and stored values`() { ++ val settings = SignalStore.settings ++ assertTrue(settings.isVesperThemeEnabled) ++ assertFalse(settings.isIgnoreRemoteDeleteEnabled) ++ settings.isVesperThemeEnabled = false ++ settings.setIgnoreRemoteDelete(true) ++ assertFalse(settings.isVesperThemeEnabled) ++ assertTrue(settings.isIgnoreRemoteDeleteEnabled) ++ } ++ ++ @Test ++ fun `downstream settings retain stable backup keys`() { ++ val keys = SignalStore.settings.getKeysToIncludeInBackup() ++ assertTrue(keys.contains("settings.dynamicColors")) ++ assertTrue(keys.contains("settings.vesperTheme")) ++ assertTrue(keys.contains("settings.ignore.remote.delete")) ++ } ++} diff --git a/patches/0026-fix-adapt-backup-diagnostics-to-Kotlin-subscription-.patch b/patches/0026-fix-adapt-backup-diagnostics-to-Kotlin-subscription-.patch new file mode 100644 index 0000000..1867a3f --- /dev/null +++ b/patches/0026-fix-adapt-backup-diagnostics-to-Kotlin-subscription-.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Vesper Materializer +Date: Wed, 16 Sep 2026 12:49:14 +0000 +Subject: [PATCH 26/27] fix: adapt backup diagnostics to Kotlin subscription + response + +Signed-off-by: Vesper Materializer +--- + .../subscription/BackupSubscriptionDiagnosticsViewModel.kt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/subscription/BackupSubscriptionDiagnosticsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/subscription/BackupSubscriptionDiagnosticsViewModel.kt +index e89ec386ca..60ed0b087a 100644 +--- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/subscription/BackupSubscriptionDiagnosticsViewModel.kt ++++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/subscription/BackupSubscriptionDiagnosticsViewModel.kt +@@ -125,7 +125,7 @@ class BackupSubscriptionDiagnosticsViewModel : ViewModel() { + serverSubscriptionActive = response.isActive, + serverSubscriptionStatus = subscription?.let { ActiveSubscription.Status.getStatus(it.status).name } ?: "None", + serverRenewalEpochSeconds = subscription?.endOfCurrentPeriod, +- serverWillCancel = response.willCancelAtPeriodEnd(), ++ serverWillCancel = response.willCancelAtPeriodEnd, + actionMessage = current.actionMessage + ) + } diff --git a/patches/0027-test-initialise-plaintext-settings-before-profile-ac.patch b/patches/0027-test-initialise-plaintext-settings-before-profile-ac.patch new file mode 100644 index 0000000..9c83cfc --- /dev/null +++ b/patches/0027-test-initialise-plaintext-settings-before-profile-ac.patch @@ -0,0 +1,34 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Vesper Materializer +Date: Wed, 16 Sep 2026 13:00:08 +0000 +Subject: [PATCH 27/27] test: initialise plaintext settings before profile + activity + +Signed-off-by: Vesper Materializer +--- + .../profiles/manage/EditProfileActivityThemeLifecycleTest.kt | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/app/src/test/java/org/thoughtcrime/securesms/profiles/manage/EditProfileActivityThemeLifecycleTest.kt b/app/src/test/java/org/thoughtcrime/securesms/profiles/manage/EditProfileActivityThemeLifecycleTest.kt +index 10927f5b86..8a0da50215 100644 +--- a/app/src/test/java/org/thoughtcrime/securesms/profiles/manage/EditProfileActivityThemeLifecycleTest.kt ++++ b/app/src/test/java/org/thoughtcrime/securesms/profiles/manage/EditProfileActivityThemeLifecycleTest.kt +@@ -14,8 +14,10 @@ import org.junit.rules.RuleChain + import org.junit.runner.RunWith + import org.robolectric.Robolectric + import org.robolectric.RobolectricTestRunner ++import org.robolectric.RuntimeEnvironment + import org.robolectric.annotation.Config + import org.signal.core.ui.R ++import org.thoughtcrime.securesms.keyvalue.PlainTextKeyValueStore + import org.thoughtcrime.securesms.keyvalue.SettingsValues + import org.thoughtcrime.securesms.keyvalue.SignalStore + import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule +@@ -32,6 +34,7 @@ class EditProfileActivityThemeLifecycleTest { + + @Test + fun `dynamic theme is applied from the pre-create lifecycle hook`() { ++ PlainTextKeyValueStore.init(RuntimeEnvironment.getApplication()) + SignalStore.settings.setTheme(SettingsValues.Theme.LIGHT, true) + val activity = Robolectric.buildActivity(EditProfileActivity::class.java).create().get() + diff --git a/tools/test_materialised_theme.py b/tools/test_materialised_theme.py index f11b05b..6b4d872 100644 --- a/tools/test_materialised_theme.py +++ b/tools/test_materialised_theme.py @@ -15,6 +15,20 @@ class MaterialisedThemeContractTest(unittest.TestCase): + def test_kotlin_settings_preserve_dynamic_theme_contract(self) -> None: + source = (SOURCE / "keyvalue/SettingsValues.kt").read_text() + self.assertIn('const val DYNAMIC_COLORS_ENABLED = "settings.dynamicColors"', source) + self.assertIn('fun setTheme(theme: Theme, useDynamicColors: Boolean)', source) + self.assertIn('putBoolean(DYNAMIC_COLORS_ENABLED, useDynamicColors)', source) + self.assertIn('setTheme(value, isDynamicColorsEnabled)', source) + self.assertIn('configurationSettingChanged.postValue(THEME)', source) + + def test_extended_colors_retain_upstream_neutral_fill(self) -> None: + source = (Path(__file__).resolve().parents[1] / "work/core/ui/src/main/java/org/signal/core/ui/compose/theme/SignalTheme.kt").read_text() + dark = source.split('private val darkExtendedColors = ExtendedColors(', 1)[1].split('\n)', 1)[0] + self.assertIn('neutralFill = Color(0x33FFFFFF)', dark) + self.assertIn('colorOnCustomVariant = Color(0xB3FFFFFF)', dark) + def test_media_theme_is_applied_before_activity_creation(self) -> None: source = (SOURCE / "mediasend/v3/MediaSendV3Activity.kt").read_text() self.assertRegex( diff --git a/upstream.pin b/upstream.pin index d2c8c40..b83533f 100644 --- a/upstream.pin +++ b/upstream.pin @@ -1 +1 @@ -signal=v8.26.3 +signal=v8.28.0