Fix various edge-to-edge issues.

This commit is contained in:
Greyson Parrelli
2026-08-04 10:57:44 -04:00
committed by Alex Hart
parent 9cf02d9bb2
commit c973c8ac91
22 changed files with 61 additions and 14 deletions
@@ -48,6 +48,13 @@ abstract class DSLSettingsFragment(
*/
protected open val listScrollsBehindToolbar: Boolean = false
/**
* Set by layouts with text input inside the list. The host windows are edge-to-edge, so `adjustResize` no
* longer shrinks the window when the keyboard opens; the list has to carry the IME inset itself to keep the
* focused field visible.
*/
protected open val listAvoidsKeyboard: Boolean = false
@CallSuper
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
toolbar = view.findViewById(R.id.toolbar)
@@ -113,7 +120,9 @@ abstract class DSLSettingsFragment(
}
recyclerView?.let { recycler ->
val insetTypes = WindowInsetsCompat.Type.navigationBars() or if (listScrollsBehindToolbar) WindowInsetsCompat.Type.statusBars() else 0
val insetTypes = WindowInsetsCompat.Type.navigationBars() or
(if (listScrollsBehindToolbar) WindowInsetsCompat.Type.statusBars() else 0) or
(if (listAvoidsKeyboard) WindowInsetsCompat.Type.ime() else 0)
if (listScrollsBehindToolbar) {
recycler.updatePadding(top = recycler.paddingTop + resources.getDimensionPixelSize(R.dimen.signal_m3_toolbar_height))
@@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.registration.fragments.ContactSupportBottomShe
import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate
import org.thoughtcrime.securesms.registration.fragments.SignalStrengthPhoneStateListener
import org.thoughtcrime.securesms.registration.sms.ReceivedSmsEvent
import org.thoughtcrime.securesms.util.SystemWindowInsetsSetter
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener
import org.thoughtcrime.securesms.util.navigation.safeNavigate
import org.thoughtcrime.securesms.util.visible
@@ -56,6 +57,8 @@ class ChangeNumberEnterCodeFragment : LoggingFragment(R.layout.fragment_change_n
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
SystemWindowInsetsSetter.attach(view, viewLifecycleOwner, SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD)
val toolbar: Toolbar = view.findViewById(R.id.toolbar)
toolbar.title = viewModel.number.fullFormattedNumber
toolbar.setNavigationOnClickListener {
@@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.databinding.FragmentChangeNumberEnterPhoneNumb
import org.thoughtcrime.securesms.registration.ui.countrycode.Country
import org.thoughtcrime.securesms.registration.util.ChangeNumberInputController
import org.thoughtcrime.securesms.util.Dialogs
import org.thoughtcrime.securesms.util.SystemWindowInsetsSetter
import org.thoughtcrime.securesms.util.navigation.safeNavigate
/**
@@ -38,6 +39,8 @@ class ChangeNumberEnterPhoneNumberFragment : LoggingFragment(R.layout.fragment_c
private val viewModel by activityViewModels<ChangeNumberViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
SystemWindowInsetsSetter.attach(view, viewLifecycleOwner, SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD)
val toolbar: Toolbar = view.findViewById(R.id.toolbar)
toolbar.setTitle(R.string.ChangeNumberEnterPhoneNumberFragment__change_number)
toolbar.setNavigationOnClickListener { findNavController().navigateUp() }
@@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.registration.data.network.VerificationCodeRequ
import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.SupportEmailUtil
import org.thoughtcrime.securesms.util.SystemWindowInsetsSetter
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.navigation.safeNavigate
import org.whispersystems.signalservice.api.kbs.PinHashUtil
@@ -52,6 +53,7 @@ class ChangeNumberRegistrationLockFragment : LoggingFragment(R.layout.fragment_c
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
SystemWindowInsetsSetter.attach(view, viewLifecycleOwner, SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD)
RegistrationViewDelegate.setDebugLogSubmitMultiTapView(view.findViewById(R.id.kbs_lock_pin_title))
val toolbar: Toolbar = view.findViewById(R.id.toolbar)
toolbar.setNavigationOnClickListener { navigateUp() }
@@ -11,10 +11,12 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
@@ -125,7 +127,10 @@ class CreateFoldersFragment : ComposeFragment() {
CreateFolderScreen(
state = state,
focusRequester = focusRequester,
modifier = Modifier.padding(contentPadding),
modifier = Modifier
.padding(contentPadding)
.consumeWindowInsets(contentPadding)
.imePadding(),
isNewFolder = isNewFolder,
hasChanges = viewModel.hasChanges(),
onAddChat = {
@@ -8,6 +8,7 @@ import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
@@ -23,6 +24,7 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
import org.thoughtcrime.securesms.components.settings.app.notifications.profiles.EditNotificationProfileViewModel.SaveNotificationProfileResult
import org.thoughtcrime.securesms.components.settings.app.notifications.profiles.models.NotificationProfileNamePreset
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment
import org.thoughtcrime.securesms.util.SystemWindowInsetsSetter
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.navigation.safeNavigate
@@ -62,6 +64,8 @@ class EditNotificationProfileFragment : DSLSettingsFragment(layoutId = R.layout.
val nameView: EditText = view.findViewById(R.id.edit_notification_profile_name)
val nameTextWrapper: TextInputLayout = view.findViewById(R.id.edit_notification_profile_name_wrapper)
SystemWindowInsetsSetter.attach(saveButton, viewLifecycleOwner, WindowInsetsCompat.Type.navigationBars() or WindowInsetsCompat.Type.ime(), SystemWindowInsetsSetter.ApplyMode.MARGIN)
EditTextUtil.addGraphemeClusterLimitFilter(nameView, NOTIFICATION_PROFILE_NAME_MAX_GLYPHS)
nameView.addTextChangedListener(
AfterTextChanged { editable: Editable ->
@@ -111,6 +111,8 @@ class DonateToSignalFragment :
override val listScrollsBehindToolbar: Boolean = true
override val listAvoidsKeyboard: Boolean = true
override fun onToolbarNavigationClicked() {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
@@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.LabeledEditText;
import org.thoughtcrime.securesms.registration.ui.countrycode.Country;
import org.thoughtcrime.securesms.util.SpanUtil;
import org.thoughtcrime.securesms.util.SystemWindowInsetsSetter;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
@@ -59,6 +60,8 @@ public class DeleteAccountFragment extends Fragment {
View confirm = view.findViewById(R.id.delete_account_fragment_delete);
Toolbar toolbar = view.findViewById(R.id.toolbar);
SystemWindowInsetsSetter.attach(view, getViewLifecycleOwner(), SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD);
bullets = view.findViewById(R.id.delete_account_fragment_bullets);
countryCode = view.findViewById(R.id.delete_account_fragment_country_code);
number = view.findViewById(R.id.delete_account_fragment_number);
@@ -2,8 +2,10 @@ package org.thoughtcrime.securesms.linkdevice
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.MaterialTheme
@@ -85,7 +87,10 @@ class EditDeviceNameFragment : ComposeFragment() {
) { contentPadding: PaddingValues ->
EditNameScreen(
state = state,
modifier = Modifier.padding(contentPadding),
modifier = Modifier
.padding(contentPadding)
.consumeWindowInsets(contentPadding)
.imePadding(),
onSave = { viewModel.saveName(it) }
)
}
@@ -59,7 +59,7 @@ public abstract class BaseSvrPinFragment<ViewModel extends BaseSvrPinViewModel>
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
SystemWindowInsetsSetter.attach(view, getViewLifecycleOwner());
SystemWindowInsetsSetter.attach(view, getViewLifecycleOwner(), SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD);
initializeViews(view);
@@ -75,7 +75,7 @@ public class EditProxyFragment extends Fragment {
this.saveButton = view.findViewById(R.id.edit_proxy_save);
this.shareButton = view.findViewById(R.id.edit_proxy_share);
SystemWindowInsetsSetter.attach(saveButton, getViewLifecycleOwner(), WindowInsetsCompat.Type.navigationBars(), SystemWindowInsetsSetter.ApplyMode.MARGIN);
SystemWindowInsetsSetter.attach(saveButton, getViewLifecycleOwner(), WindowInsetsCompat.Type.navigationBars() | WindowInsetsCompat.Type.ime(), SystemWindowInsetsSetter.ApplyMode.MARGIN);
lifecycleDisposable = new LifecycleDisposable();
lifecycleDisposable.bindTo(getViewLifecycleOwner());
@@ -90,7 +90,7 @@ public class CreateProfileFragment extends LoggingFragment {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
SystemWindowInsetsSetter.attach(binding.container, getViewLifecycleOwner());
SystemWindowInsetsSetter.attach(binding.container, getViewLifecycleOwner(), SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD);
GroupId groupId = GroupId.parseNullableOrThrow(requireArguments().getString(GROUP_ID, null));
@@ -77,7 +77,7 @@ public class EditAboutFragment extends Fragment implements EditProfileActivity.E
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
SystemWindowInsetsSetter.attach(view, getViewLifecycleOwner());
SystemWindowInsetsSetter.attach(view, getViewLifecycleOwner(), SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD);
this.emojiView = view.findViewById(R.id.edit_about_emoji);
this.bodyView = view.findViewById(R.id.edit_about_body);
@@ -46,7 +46,7 @@ public class EditProfileNameFragment extends Fragment {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
SystemWindowInsetsSetter.attach(view, getViewLifecycleOwner());
SystemWindowInsetsSetter.attach(view, getViewLifecycleOwner(), SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD);
this.givenName = view.findViewById(R.id.edit_profile_name_given_name);
this.familyName = view.findViewById(R.id.edit_profile_name_family_name);
@@ -70,7 +70,7 @@ public class UsernameEditFragment extends LoggingFragment {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
SystemWindowInsetsSetter.attach(view, getViewLifecycleOwner());
SystemWindowInsetsSetter.attach(view, getViewLifecycleOwner(), SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD);
Bundle bundle = getArguments();
if (bundle != null) {
@@ -68,7 +68,7 @@ class EnterCodeFragment : LoggingFragment(R.layout.fragment_registration_enter_c
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
SystemWindowInsetsSetter.attach(view, viewLifecycleOwner)
SystemWindowInsetsSetter.attach(view, viewLifecycleOwner, SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD)
setDebugLogSubmitMultiTapView(binding.verifyHeader)
phoneStateListener = SignalStrengthPhoneStateListener(this, PhoneStateCallback())
@@ -100,7 +100,7 @@ class EnterPhoneNumberFragment : LoggingFragment(R.layout.fragment_registration_
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
SystemWindowInsetsSetter.attach(view, viewLifecycleOwner)
SystemWindowInsetsSetter.attach(view, viewLifecycleOwner, SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD)
setDebugLogSubmitMultiTapView(binding.verifyHeader)
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
@@ -44,7 +44,7 @@ class RegistrationLockFragment : LoggingFragment(R.layout.fragment_registration_
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
SystemWindowInsetsSetter.attach(view, viewLifecycleOwner)
SystemWindowInsetsSetter.attach(view, viewLifecycleOwner, SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD)
setDebugLogSubmitMultiTapView(view.findViewById(R.id.kbs_lock_pin_title))
val args: RegistrationLockFragmentArgs = RegistrationLockFragmentArgs.fromBundle(requireArguments())
@@ -45,7 +45,7 @@ class ReRegisterWithPinFragment : LoggingFragment(R.layout.fragment_registration
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
SystemWindowInsetsSetter.attach(view, viewLifecycleOwner)
SystemWindowInsetsSetter.attach(view, viewLifecycleOwner, SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD)
RegistrationViewDelegate.setDebugLogSubmitMultiTapView(binding.pinRestorePinTitle)
binding.pinRestorePinDescription.setText(R.string.RegistrationLockFragment__enter_the_pin_you_created_for_your_account)
@@ -112,7 +112,7 @@ public class ShareInterstitialActivity extends PassphraseRequiredActivity {
toolbar.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
SystemWindowInsetsSetter.attach(toolbar, this, WindowInsetsCompat.Type.statusBars());
SystemWindowInsetsSetter.attach(confirm, this, WindowInsetsCompat.Type.navigationBars(), SystemWindowInsetsSetter.ApplyMode.MARGIN);
SystemWindowInsetsSetter.attach(confirm, this, WindowInsetsCompat.Type.navigationBars() | WindowInsetsCompat.Type.ime(), SystemWindowInsetsSetter.ApplyMode.MARGIN);
SelectionAwareEmojiEditText text = findViewById(R.id.text);
@@ -16,6 +16,7 @@ import io.reactivex.rxjava3.kotlin.subscribeBy
import org.signal.core.util.concurrent.LifecycleDisposable
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.util.SystemWindowInsetsSetter
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton
@@ -42,6 +43,8 @@ class EditStoryNameFragment : Fragment(R.layout.stories_edit_story_name_fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
lifecycleDisposable.bindTo(viewLifecycleOwner)
SystemWindowInsetsSetter.attach(view, viewLifecycleOwner, SystemWindowInsetsSetter.SAFE_AREA_WITH_KEYBOARD)
val toolbar: Toolbar = view.findViewById(R.id.toolbar)
toolbar.navigationIcon?.colorFilter = SimpleColorFilter(ContextCompat.getColor(requireContext(), R.color.signal_icon_tint_primary))
toolbar.setNavigationOnClickListener { findNavController().popBackStack() }
@@ -11,6 +11,14 @@ import androidx.lifecycle.LifecycleOwner
object SystemWindowInsetsSetter {
/**
* The safe area for a screen root that contains text input. The host windows are edge-to-edge, so
* `adjustResize` no longer shrinks the window when the keyboard opens and the root has to carry the
* IME inset itself. Bottom resolves to the keyboard height or the navigation bar, whichever is larger.
*/
@JvmField
val SAFE_AREA_WITH_KEYBOARD: Int = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.ime()
/**
* How the resolved system-bar insets are applied to the target view. In both modes the view's
* original padding/margin (captured when [attach] is called) is preserved and the insets are