diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt index c4cb5e534e..25a77b1448 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt @@ -30,7 +30,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.colorResource @@ -72,7 +71,7 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.BadgeImag import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository import org.thoughtcrime.securesms.components.settings.app.subscription.completed.InAppPaymentsBottomSheetDelegate import org.thoughtcrime.securesms.compose.ComposeFragment -import org.thoughtcrime.securesms.compose.StatusBarColorNestedScrollConnection +import org.thoughtcrime.securesms.compose.rememberStatusBarColorNestedScrollModifier import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord import org.thoughtcrime.securesms.profiles.ProfileName import org.thoughtcrime.securesms.recipients.Recipient @@ -107,16 +106,11 @@ class AppSettingsFragment : ComposeFragment(), Callbacks { ) } - val nestedScrollConnection = remember { - StatusBarColorNestedScrollConnection(requireActivity()) - } - AppSettingsContent( self = self!!, state = state!!, bannerManager = bannerManager, - callbacks = this, - lazyColumnModifier = Modifier.nestedScroll(nestedScrollConnection) + callbacks = this ) } @@ -176,8 +170,7 @@ private fun AppSettingsContent( self: BioRecipientState, state: AppSettingsState, bannerManager: BannerManager, - callbacks: Callbacks, - lazyColumnModifier: Modifier = Modifier + callbacks: Callbacks ) { val isRegisteredAndUpToDate by rememberUpdatedState(state.isRegisteredAndUpToDate()) @@ -193,7 +186,7 @@ private fun AppSettingsContent( bannerManager.Banner() LazyColumn( - modifier = lazyColumnModifier + modifier = rememberStatusBarColorNestedScrollModifier() ) { item { BioRow( diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/account/AccountSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/account/AccountSettingsFragment.kt index a61fe14bda..1d577d6ec9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/account/AccountSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/account/AccountSettingsFragment.kt @@ -47,6 +47,7 @@ import org.signal.core.ui.compose.SignalPreview import org.signal.core.ui.compose.Texts import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.compose.rememberStatusBarColorNestedScrollModifier import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -278,7 +279,10 @@ fun AccountSettingsScreen( navigationIcon = ImageVector.vectorResource(R.drawable.ic_arrow_left_24) ) { contentPadding -> LazyColumn( - modifier = Modifier.padding(contentPadding).testTag(AccountSettingsTestTags.SCROLLER) + modifier = Modifier + .padding(contentPadding) + .then(rememberStatusBarColorNestedScrollModifier()) + .testTag(AccountSettingsTestTags.SCROLLER) ) { item { Texts.SectionHeader( diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/appearance/AppearanceSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/appearance/AppearanceSettingsFragment.kt index aa1dca296a..a8dc3772e2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/appearance/AppearanceSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/appearance/AppearanceSettingsFragment.kt @@ -24,6 +24,7 @@ import org.signal.core.ui.compose.SignalPreview import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.settings.app.appearance.navbar.ChooseNavigationBarStyleFragment import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.compose.rememberStatusBarColorNestedScrollModifier import org.thoughtcrime.securesms.keyvalue.SettingsValues import org.thoughtcrime.securesms.util.navigation.safeNavigate @@ -107,7 +108,9 @@ private fun AppearanceSettingsScreen( navigationIcon = ImageVector.vectorResource(R.drawable.symbol_arrow_start_24) ) { paddingValues -> LazyColumn( - modifier = Modifier.padding(paddingValues) + modifier = Modifier + .padding(paddingValues) + .then(rememberStatusBarColorNestedScrollModifier()) ) { item { Rows.RadioListRow( diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt index ec8e4b0a33..5bb1ce5160 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt @@ -1,125 +1,247 @@ package org.thoughtcrime.securesms.components.settings.app.chats -import androidx.lifecycle.ViewModelProvider -import androidx.navigation.Navigation +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.fragment.app.viewModels +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.fragment.findNavController +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.Scaffolds +import org.signal.core.ui.compose.SignalPreview +import org.signal.core.ui.compose.Texts import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.components.settings.DSLConfiguration -import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment -import org.thoughtcrime.securesms.components.settings.DSLSettingsText -import org.thoughtcrime.securesms.components.settings.configure +import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.compose.rememberStatusBarColorNestedScrollModifier import org.thoughtcrime.securesms.util.RemoteConfig -import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter import org.thoughtcrime.securesms.util.navigation.safeNavigate -class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__chats) { +/** + * Displays a list of chats settings options to the user, including + * generating link previews and keeping muted chats archived. + */ +class ChatsSettingsFragment : ComposeFragment() { - private lateinit var viewModel: ChatsSettingsViewModel + private val viewModel: ChatsSettingsViewModel by viewModels() override fun onResume() { super.onResume() viewModel.refresh() } - @Suppress("ReplaceGetOrSet") - override fun bindAdapter(adapter: MappingAdapter) { - viewModel = ViewModelProvider(this).get(ChatsSettingsViewModel::class.java) + @Composable + override fun FragmentContent() { + val state by viewModel.state.collectAsStateWithLifecycle() + val callbacks = remember { Callbacks() } - viewModel.state.observe(viewLifecycleOwner) { - adapter.submitList(getConfiguration(it).toMappingModelList()) - } + ChatsSettingsScreen( + state = state, + callbacks = callbacks, + isRemoteBackupsAvailable = RemoteConfig.messageBackups + ) } - private fun getConfiguration(state: ChatsSettingsState): DSLConfiguration { - return configure { - switchPref( - title = DSLSettingsText.from(R.string.preferences__generate_link_previews), - summary = DSLSettingsText.from(R.string.preferences__retrieve_link_previews_from_websites_for_messages), - isEnabled = state.isRegisteredAndUpToDate(), - isChecked = state.generateLinkPreviews, - onClick = { - viewModel.setGenerateLinkPreviewsEnabled(!state.generateLinkPreviews) - } - ) + private inner class Callbacks : ChatsSettingsCallbacks { + override fun onNavigationClick() { + requireActivity().onBackPressedDispatcher.onBackPressed() + } - switchPref( - title = DSLSettingsText.from(R.string.preferences__pref_use_address_book_photos), - summary = DSLSettingsText.from(R.string.preferences__display_contact_photos_from_your_address_book_if_available), - isEnabled = state.isRegisteredAndUpToDate(), - isChecked = state.useAddressBook, - onClick = { - viewModel.setUseAddressBook(!state.useAddressBook) - } - ) + override fun onGenerateLinkPreviewsChanged(enabled: Boolean) { + viewModel.setGenerateLinkPreviewsEnabled(enabled) + } - switchPref( - title = DSLSettingsText.from(R.string.preferences__pref_keep_muted_chats_archived), - summary = DSLSettingsText.from(R.string.preferences__muted_chats_that_are_archived_will_remain_archived), - isEnabled = state.isRegisteredAndUpToDate(), - isChecked = state.keepMutedChatsArchived, - onClick = { - viewModel.setKeepMutedChatsArchived(!state.keepMutedChatsArchived) - } - ) + override fun onUseAddressBookChanged(enabled: Boolean) { + viewModel.setUseAddressBook(enabled) + } - dividerPref() + override fun onKeepMutedChatsArchivedChanged(enabled: Boolean) { + viewModel.setKeepMutedChatsArchived(enabled) + } - sectionHeaderPref(R.string.ChatsSettingsFragment__chat_folders) + override fun onAddAChatFolderClick() { + findNavController().safeNavigate(R.id.action_chatsSettingsFragment_to_chatFoldersFragment) + } - if (state.folderCount == 1) { - clickPref( - title = DSLSettingsText.from(R.string.ChatsSettingsFragment__add_chat_folder), - isEnabled = state.isRegisteredAndUpToDate(), - onClick = { - Navigation.findNavController(requireView()).safeNavigate(R.id.action_chatsSettingsFragment_to_chatFoldersFragment) - } - ) - } else { - clickPref( - title = DSLSettingsText.from(R.string.ChatsSettingsFragment__add_edit_chat_folder), - summary = DSLSettingsText.from(resources.getQuantityString(R.plurals.ChatsSettingsFragment__d_folder, state.folderCount, state.folderCount)), - isEnabled = state.isRegisteredAndUpToDate(), - onClick = { - Navigation.findNavController(requireView()).safeNavigate(R.id.action_chatsSettingsFragment_to_chatFoldersFragment) - } + override fun onAddOrEditFoldersClick() { + findNavController().safeNavigate(R.id.action_chatsSettingsFragment_to_chatFoldersFragment) + } + + override fun onUseSystemEmojiChanged(enabled: Boolean) { + viewModel.setUseSystemEmoji(enabled) + } + + override fun onEnterKeySendsChanged(enabled: Boolean) { + viewModel.setEnterKeySends(enabled) + } + + override fun onChatBackupsClick() { + findNavController().safeNavigate(R.id.action_chatsSettingsFragment_to_backupsPreferenceFragment) + } + } +} + +private interface ChatsSettingsCallbacks { + fun onNavigationClick() = Unit + fun onGenerateLinkPreviewsChanged(enabled: Boolean) = Unit + fun onUseAddressBookChanged(enabled: Boolean) = Unit + fun onKeepMutedChatsArchivedChanged(enabled: Boolean) = Unit + fun onAddAChatFolderClick() = Unit + fun onAddOrEditFoldersClick() = Unit + fun onUseSystemEmojiChanged(enabled: Boolean) = Unit + fun onEnterKeySendsChanged(enabled: Boolean) = Unit + fun onChatBackupsClick() = Unit + + object Empty : ChatsSettingsCallbacks +} + +@Composable +private fun ChatsSettingsScreen( + isRemoteBackupsAvailable: Boolean, + state: ChatsSettingsState, + callbacks: ChatsSettingsCallbacks +) { + Scaffolds.Settings( + title = stringResource(R.string.preferences_chats__chats), + onNavigationClick = callbacks::onNavigationClick, + navigationIcon = ImageVector.vectorResource(R.drawable.symbol_arrow_start_24) + ) { paddingValues -> + LazyColumn( + modifier = Modifier + .padding(paddingValues) + .then(rememberStatusBarColorNestedScrollModifier()) + ) { + item { + Rows.ToggleRow( + text = stringResource(R.string.preferences__generate_link_previews), + label = stringResource(R.string.preferences__retrieve_link_previews_from_websites_for_messages), + enabled = state.isRegisteredAndUpToDate(), + checked = state.generateLinkPreviews, + onCheckChanged = callbacks::onGenerateLinkPreviewsChanged ) } - dividerPref() - - sectionHeaderPref(R.string.ChatsSettingsFragment__keyboard) - - switchPref( - title = DSLSettingsText.from(R.string.preferences_advanced__use_system_emoji), - isEnabled = state.isRegisteredAndUpToDate(), - isChecked = state.useSystemEmoji, - onClick = { - viewModel.setUseSystemEmoji(!state.useSystemEmoji) - } - ) - - switchPref( - title = DSLSettingsText.from(R.string.ChatsSettingsFragment__send_with_enter), - isEnabled = state.isRegisteredAndUpToDate(), - isChecked = state.enterKeySends, - onClick = { - viewModel.setEnterKeySends(!state.enterKeySends) - } - ) - - if (!RemoteConfig.messageBackups) { - dividerPref() - - sectionHeaderPref(R.string.preferences_chats__backups) - - clickPref( - title = DSLSettingsText.from(R.string.preferences_chats__chat_backups), - summary = DSLSettingsText.from(if (state.localBackupsEnabled) R.string.arrays__enabled else R.string.arrays__disabled), - isEnabled = state.localBackupsEnabled || state.isRegisteredAndUpToDate(), - onClick = { - Navigation.findNavController(requireView()).safeNavigate(R.id.action_chatsSettingsFragment_to_backupsPreferenceFragment) - } + item { + Rows.ToggleRow( + text = stringResource(R.string.preferences__pref_use_address_book_photos), + label = stringResource(R.string.preferences__display_contact_photos_from_your_address_book_if_available), + enabled = state.isRegisteredAndUpToDate(), + checked = state.useAddressBook, + onCheckChanged = callbacks::onUseAddressBookChanged ) } + + item { + Rows.ToggleRow( + text = stringResource(R.string.preferences__pref_keep_muted_chats_archived), + label = stringResource(R.string.preferences__muted_chats_that_are_archived_will_remain_archived), + enabled = state.isRegisteredAndUpToDate(), + checked = state.keepMutedChatsArchived, + onCheckChanged = callbacks::onKeepMutedChatsArchivedChanged + ) + } + + item { + Dividers.Default() + } + + item { + Texts.SectionHeader(stringResource(R.string.ChatsSettingsFragment__chat_folders)) + } + + if (state.folderCount == 1) { + item { + Rows.TextRow( + text = stringResource(R.string.ChatsSettingsFragment__add_chat_folder), + enabled = state.isRegisteredAndUpToDate(), + onClick = callbacks::onAddAChatFolderClick + ) + } + } else { + item { + Rows.TextRow( + text = stringResource(R.string.ChatsSettingsFragment__add_edit_chat_folder), + label = pluralStringResource(R.plurals.ChatsSettingsFragment__d_folder, state.folderCount, state.folderCount), + enabled = state.isRegisteredAndUpToDate(), + onClick = callbacks::onAddOrEditFoldersClick + ) + } + } + + item { + Dividers.Default() + } + + item { + Texts.SectionHeader(stringResource(R.string.ChatsSettingsFragment__keyboard)) + } + + item { + Rows.ToggleRow( + text = stringResource(R.string.preferences_advanced__use_system_emoji), + enabled = state.isRegisteredAndUpToDate(), + checked = state.useSystemEmoji, + onCheckChanged = callbacks::onUseSystemEmojiChanged + ) + } + + item { + Rows.ToggleRow( + text = stringResource(R.string.ChatsSettingsFragment__send_with_enter), + enabled = state.isRegisteredAndUpToDate(), + checked = state.enterKeySends, + onCheckChanged = callbacks::onEnterKeySendsChanged + ) + } + + if (!isRemoteBackupsAvailable) { + item { + Dividers.Default() + } + + item { + Texts.SectionHeader(stringResource(R.string.preferences_chats__backups)) + } + + item { + Rows.TextRow( + text = stringResource(R.string.preferences_chats__chat_backups), + label = stringResource(if (state.localBackupsEnabled) R.string.arrays__enabled else R.string.arrays__disabled), + enabled = state.localBackupsEnabled || state.isRegisteredAndUpToDate(), + onClick = callbacks::onChatBackupsClick + ) + } + } } } } + +@SignalPreview +@Composable +private fun ChatsSettingsScreenPreview() { + Previews.Preview { + ChatsSettingsScreen( + state = ChatsSettingsState( + generateLinkPreviews = true, + useAddressBook = true, + keepMutedChatsArchived = true, + useSystemEmoji = false, + enterKeySends = false, + localBackupsEnabled = true, + folderCount = 1, + userUnregistered = false, + clientDeprecated = false + ), + callbacks = ChatsSettingsCallbacks.Empty, + isRemoteBackupsAvailable = false + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsViewModel.kt index b192fba9e7..03f0e3f984 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsViewModel.kt @@ -1,9 +1,11 @@ package org.thoughtcrime.securesms.components.settings.app.chats -import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.thoughtcrime.securesms.components.settings.app.chats.folders.ChatFoldersRepository import org.thoughtcrime.securesms.dependencies.AppDependencies @@ -12,7 +14,6 @@ import org.thoughtcrime.securesms.util.BackupUtil import org.thoughtcrime.securesms.util.ConversationUtil import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.ThrottledDebouncer -import org.thoughtcrime.securesms.util.livedata.Store class ChatsSettingsViewModel @JvmOverloads constructor( private val repository: ChatsSettingsRepository = ChatsSettingsRepository() @@ -20,7 +21,7 @@ class ChatsSettingsViewModel @JvmOverloads constructor( private val refreshDebouncer = ThrottledDebouncer(500L) - private val store: Store = Store( + private val store = MutableStateFlow( ChatsSettingsState( generateLinkPreviews = SignalStore.settings.isLinkPreviewsEnabled, useAddressBook = SignalStore.settings.isPreferSystemContactPhotos, @@ -34,7 +35,7 @@ class ChatsSettingsViewModel @JvmOverloads constructor( ) ) - val state: LiveData = store.stateLiveData + val state: StateFlow = store fun setGenerateLinkPreviewsEnabled(enabled: Boolean) { store.update { it.copy(generateLinkPreviews = enabled) } @@ -70,7 +71,7 @@ class ChatsSettingsViewModel @JvmOverloads constructor( val count = ChatFoldersRepository.getFolderCount() val backupsEnabled = SignalStore.settings.isBackupEnabled && BackupUtil.canUserAccessBackupDirectory(AppDependencies.application) - if (store.state.localBackupsEnabled != backupsEnabled) { + if (store.value.localBackupsEnabled != backupsEnabled) { store.update { it.copy( folderCount = count, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/AddAllowedMembersFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/AddAllowedMembersFragment.kt index d71d04dfbd..3841828166 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/AddAllowedMembersFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/AddAllowedMembersFragment.kt @@ -40,6 +40,7 @@ import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.emoji.EmojiStrings import org.thoughtcrime.securesms.components.settings.app.notifications.profiles.AddAllowedMembersViewModel.NotificationProfileAndRecipients import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.compose.rememberStatusBarColorNestedScrollModifier import org.thoughtcrime.securesms.database.RecipientTable import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile import org.thoughtcrime.securesms.notifications.profiles.NotificationProfileId @@ -166,7 +167,9 @@ private fun AddAllowedMembersContent( modifier = Modifier.padding(contentPadding) ) { LazyColumn( - modifier = Modifier.weight(1f) + modifier = Modifier + .weight(1f) + .then(rememberStatusBarColorNestedScrollModifier()) ) { item { Text( diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsFragment.kt index 75c51d1785..20bc1ebfb4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/advanced/AdvancedPrivacySettingsFragment.kt @@ -42,6 +42,7 @@ import org.signal.core.ui.compose.SignalPreview import org.signal.core.ui.compose.Texts import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.compose.rememberStatusBarColorNestedScrollModifier import org.thoughtcrime.securesms.util.CommunicationActions import org.thoughtcrime.securesms.util.viewModel @@ -176,7 +177,9 @@ private fun AdvancedPrivacySettingsScreen( navigationIcon = ImageVector.vectorResource(R.drawable.symbol_arrow_start_24) ) { paddingValues -> LazyColumn( - modifier = Modifier.padding(paddingValues) + modifier = Modifier + .padding(paddingValues) + .then(rememberStatusBarColorNestedScrollModifier()) ) { item { Rows.ToggleRow( diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/updates/AppUpdatesSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/updates/AppUpdatesSettingsFragment.kt index 1f596c49d8..43a7d4fa37 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/updates/AppUpdatesSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/updates/AppUpdatesSettingsFragment.kt @@ -23,6 +23,7 @@ import org.signal.core.ui.compose.Scaffolds import org.signal.core.ui.compose.SignalPreview import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.compose.rememberStatusBarColorNestedScrollModifier import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobs.ApkUpdateJob import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -90,7 +91,9 @@ private fun AppUpdatesSettingsScreen( ) { paddingValues -> LazyColumn( - modifier = Modifier.padding(paddingValues) + modifier = Modifier + .padding(paddingValues) + .then(rememberStatusBarColorNestedScrollModifier()) ) { if (Build.VERSION.SDK_INT >= 31) { item { diff --git a/app/src/main/java/org/thoughtcrime/securesms/compose/StatusBarColorNestedScrollConnection.kt b/app/src/main/java/org/thoughtcrime/securesms/compose/StatusBarColorNestedScrollConnection.kt index e918bde64c..6225ed946b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/compose/StatusBarColorNestedScrollConnection.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/compose/StatusBarColorNestedScrollConnection.kt @@ -2,9 +2,15 @@ package org.thoughtcrime.securesms.compose import android.animation.ValueAnimator import android.app.Activity +import androidx.appcompat.app.AppCompatActivity +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.Velocity import androidx.core.content.ContextCompat import com.google.android.material.animation.ArgbEvaluatorCompat @@ -14,10 +20,14 @@ import kotlin.math.abs /** * Controls status-bar color based off a nested scroll + * + * Recommended to use this with [rememberStatusBarColorNestedScrollModifier] since it'll prevent you from having to wire through + * an activity or the connection to subcomponents. */ class StatusBarColorNestedScrollConnection( private val activity: Activity ) : NestedScrollConnection { + private var animator: ValueAnimator? = null private val normalColor = ContextCompat.getColor(activity, R.color.signal_colorBackground) @@ -78,3 +88,21 @@ class StatusBarColorNestedScrollConnection( private fun Float.isNearZero(): Boolean = abs(this) < 0.001 } + +/** + * Remembers the nested scroll modifier to ensure the proper status bar coloring behavior. + * + * This is only required if the screen you are modifying does not utilize edgeToEdge. + */ +@Composable +fun rememberStatusBarColorNestedScrollModifier(): Modifier { + val activity = LocalContext.current as? AppCompatActivity + + return remember { + if (activity != null) { + Modifier.nestedScroll(StatusBarColorNestedScrollConnection(activity)) + } else { + Modifier + } + } +}