From 9326c1726a16fba396f3c0f7e5c63a5c6ca9b92b Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Mon, 19 Sep 2022 14:24:44 -0300 Subject: [PATCH] Increase stories caption limit to 1500 grapheme clusters. --- .../components/ViewBinderDelegate.kt | 4 - .../mediasend/v2/MediaSelectionRepository.kt | 2 +- .../mediasend/v2/MediaSelectionViewModel.kt | 24 ++++++ .../v2/review/AddMessageCharacterCount.kt | 21 ++++++ .../v2/review/AddMessageDialogFragment.kt | 73 ++++++++++--------- .../securesms/sharing/MultiShareArgs.java | 2 +- .../securesms/sharing/MultiShareSender.java | 2 +- .../thoughtcrime/securesms/stories/Stories.kt | 3 +- .../viewer/page/StoryViewerPageFragment.kt | 11 +++ .../viewer/page/StoryViewerPlaybackState.kt | 5 +- .../rounded_rectangle_secondary_dark.xml | 4 +- .../layout/stories_viewer_fragment_page.xml | 4 +- .../v2_media_add_message_dialog_fragment.xml | 5 +- ...ia_add_message_dialog_fragment_content.xml | 23 ++++-- 14 files changed, 125 insertions(+), 58 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/AddMessageCharacterCount.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ViewBinderDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/components/ViewBinderDelegate.kt index bbe273bff..3112abf18 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ViewBinderDelegate.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ViewBinderDelegate.kt @@ -3,10 +3,8 @@ package org.thoughtcrime.securesms.components import android.view.View import androidx.fragment.app.Fragment import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.viewbinding.ViewBinding -import org.whispersystems.signalservice.api.util.Preconditions import kotlin.reflect.KProperty /** @@ -18,8 +16,6 @@ class ViewBinderDelegate(private val bindingFactory: (View) -> private var binding: T? = null operator fun getValue(thisRef: Fragment, property: KProperty<*>): T { - Preconditions.checkState(thisRef.viewLifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) - if (binding == null) { thisRef.viewLifecycleOwner.lifecycle.addObserver(this@ViewBinderDelegate) binding = bindingFactory(thisRef.requireView()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt index 4679f3564..365a41c6a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt @@ -164,7 +164,7 @@ class MediaSelectionRepository(context: Context) { } else { val iterator = BreakIteratorCompat.getInstance() iterator.setText(body) - iterator.take(Stories.MAX_BODY_SIZE).toString() + iterator.take(Stories.MAX_CAPTION_SIZE).toString() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt index dff93601e..9f1dbca06 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt @@ -5,21 +5,27 @@ import android.os.Bundle import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Maybe import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.subscribeBy +import io.reactivex.rxjava3.processors.BehaviorProcessor +import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.subjects.BehaviorSubject import io.reactivex.rxjava3.subjects.PublishSubject import io.reactivex.rxjava3.subjects.Subject +import org.signal.core.util.BreakIteratorCompat import org.thoughtcrime.securesms.components.mention.MentionAnnotation import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey import org.thoughtcrime.securesms.conversation.MessageSendType import org.thoughtcrime.securesms.mediasend.Media import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult import org.thoughtcrime.securesms.mediasend.VideoEditorFragment +import org.thoughtcrime.securesms.mediasend.v2.review.AddMessageCharacterCount import org.thoughtcrime.securesms.mms.MediaConstraints import org.thoughtcrime.securesms.mms.SentMediaQuality import org.thoughtcrime.securesms.recipients.Recipient @@ -53,6 +59,8 @@ class MediaSelectionViewModel( ) ) + private val addAMessageUpdatePublisher = BehaviorProcessor.create() + val isContactSelectionRequired = destination == MediaSelectionDestination.ChooseAfterMediaSelection val state: LiveData = store.stateLiveData @@ -64,6 +72,22 @@ class MediaSelectionViewModel( private val disposables = CompositeDisposable() + fun watchAddAMessageCount(): Flowable { + return addAMessageUpdatePublisher + .onBackpressureLatest() + .map { + val iterator = BreakIteratorCompat.getInstance() + iterator.setText(it) + AddMessageCharacterCount(iterator.countBreaks()) + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + } + + fun updateAddAMessageCount(input: CharSequence?) { + addAMessageUpdatePublisher.onNext(input ?: "") + } + private val isMeteredDisposable: Disposable = repository.isMetered.subscribe { metered -> store.update { it.copy( diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/AddMessageCharacterCount.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/AddMessageCharacterCount.kt new file mode 100644 index 000000000..7b1c0da03 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/AddMessageCharacterCount.kt @@ -0,0 +1,21 @@ +package org.thoughtcrime.securesms.mediasend.v2.review + +import org.thoughtcrime.securesms.stories.Stories +import kotlin.math.max + +/** + * Manages the current character count for the add-message input. + * + * We only want to display the count if DISPLAY_COUNT or less characters remain. + * The actual count is calculated in the background by the ViewModel. + */ +@JvmInline +value class AddMessageCharacterCount(private val count: Int) { + fun getRemaining(): Int = max(Stories.MAX_CAPTION_SIZE - count, 0) + + fun shouldDisplayCount(): Boolean = getRemaining() <= DISPLAY_COUNT_THRESHOLD + + companion object { + private const val DISPLAY_COUNT_THRESHOLD = 50 + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/AddMessageDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/AddMessageDialogFragment.kt index bd54ae071..8bfba993e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/AddMessageDialogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/AddMessageDialogFragment.kt @@ -7,6 +7,7 @@ import android.view.KeyEvent import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.widget.addTextChangedListener import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers @@ -14,10 +15,8 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import org.signal.core.util.EditTextUtil import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.components.ComposeText -import org.thoughtcrime.securesms.components.InputAwareLayout import org.thoughtcrime.securesms.components.KeyboardEntryDialogFragment -import org.thoughtcrime.securesms.components.emoji.EmojiToggle +import org.thoughtcrime.securesms.components.ViewBinderDelegate import org.thoughtcrime.securesms.components.emoji.MediaKeyboard import org.thoughtcrime.securesms.components.mention.MentionAnnotation import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQuery @@ -27,6 +26,7 @@ import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQueryResults import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQueryViewModel import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerFragment import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerViewModel +import org.thoughtcrime.securesms.databinding.V2MediaAddMessageDialogFragmentBinding import org.thoughtcrime.securesms.keyboard.KeyboardPage import org.thoughtcrime.securesms.keyboard.KeyboardPagerViewModel import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -58,11 +58,8 @@ class AddMessageDialogFragment : KeyboardEntryDialogFragment(R.layout.v2_media_a ownerProducer = { requireActivity() } ) - private lateinit var input: ComposeText - private lateinit var emojiDrawerToggle: EmojiToggle + private val binding by ViewBinderDelegate(V2MediaAddMessageDialogFragmentBinding::bind) private lateinit var emojiDrawerStub: Stub - private lateinit var hud: InputAwareLayout - private lateinit var mentionsContainer: ViewGroup private lateinit var inlineQueryResultsController: InlineQueryResultsController private var requestedEmojiDrawer: Boolean = false @@ -79,28 +76,34 @@ class AddMessageDialogFragment : KeyboardEntryDialogFragment(R.layout.v2_media_a } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - input = view.findViewById(R.id.add_a_message_input) + emojiDrawerStub = Stub(binding.content.emojiDrawerStub) if (Stories.isFeatureEnabled()) { - EditTextUtil.addGraphemeClusterLimitFilter(input, Stories.MAX_BODY_SIZE) + EditTextUtil.addGraphemeClusterLimitFilter(binding.content.addAMessageInput, Stories.MAX_CAPTION_SIZE) } - input.setText(requireArguments().getCharSequence(ARG_INITIAL_TEXT)) + binding.content.addAMessageInput.addTextChangedListener(afterTextChanged = { + viewModel.updateAddAMessageCount(it) + }) + + binding.content.addAMessageInput.setText(requireArguments().getCharSequence(ARG_INITIAL_TEXT)) - emojiDrawerToggle = view.findViewById(R.id.emoji_toggle) - emojiDrawerStub = Stub(view.findViewById(R.id.emoji_drawer_stub)) if (SignalStore.settings().isPreferSystemEmoji) { - emojiDrawerToggle.visible = false + binding.content.emojiToggle.visible = false } else { - emojiDrawerToggle.setOnClickListener { onEmojiToggleClicked() } + binding.content.emojiToggle.setOnClickListener { onEmojiToggleClicked() } } - hud = view.findViewById(R.id.hud) - hud.setOnClickListener { dismissAllowingStateLoss() } + binding.hud.setOnClickListener { dismissAllowingStateLoss() } val confirm: View = view.findViewById(R.id.confirm_button) confirm.setOnClickListener { dismissAllowingStateLoss() } + disposables += viewModel.watchAddAMessageCount().subscribe { count -> + binding.content.addAMessageLimit.visible = count.shouldDisplayCount() + binding.content.addAMessageLimit.text = count.getRemaining().toString() + } + disposables.add( viewModel.hudCommands.observeOn(AndroidSchedulers.mainThread()).subscribe { when (it) { @@ -120,18 +123,18 @@ class AddMessageDialogFragment : KeyboardEntryDialogFragment(R.layout.v2_media_a super.onResume() requestedEmojiDrawer = false - ViewUtil.focusAndShowKeyboard(input) + ViewUtil.focusAndShowKeyboard(binding.content.addAMessageInput) } override fun onPause() { super.onPause() - ViewUtil.hideKeyboard(requireContext(), input) + ViewUtil.hideKeyboard(requireContext(), binding.content.addAMessageInput) } override fun onDismiss(dialog: DialogInterface) { super.onDismiss(dialog) - viewModel.setMessage(input.text) + viewModel.setMessage(binding.content.addAMessageInput.text) } override fun onKeyboardHidden() { @@ -151,23 +154,21 @@ class AddMessageDialogFragment : KeyboardEntryDialogFragment(R.layout.v2_media_a super.onDestroyView() disposables.dispose() - input.setInlineQueryChangedListener(null) - input.setMentionValidator(null) + binding.content.addAMessageInput.setInlineQueryChangedListener(null) + binding.content.addAMessageInput.setMentionValidator(null) } private fun initializeMentions() { - mentionsContainer = requireView().findViewById(R.id.mentions_picker_container) - inlineQueryResultsController = InlineQueryResultsController( requireContext(), inlineQueryViewModel, requireView().findViewById(R.id.background_holder), (requireView() as ViewGroup), - input, + binding.content.addAMessageInput, viewLifecycleOwner ) - input.setInlineQueryChangedListener(object : InlineQueryChangedListener { + binding.content.addAMessageInput.setInlineQueryChangedListener(object : InlineQueryChangedListener { override fun onQueryChanged(inlineQuery: InlineQuery) { when (inlineQuery) { is InlineQuery.Mention -> { @@ -196,7 +197,7 @@ class AddMessageDialogFragment : KeyboardEntryDialogFragment(R.layout.v2_media_a disposables += inlineQueryViewModel .selection .observeOn(AndroidSchedulers.mainThread()) - .subscribe { r -> input.replaceText(r) } + .subscribe { r -> binding.content.addAMessageInput.replaceText(r) } val recipientId: RecipientId = viewModel.destination.getRecipientSearchKey()?.recipientId ?: return @@ -204,7 +205,7 @@ class AddMessageDialogFragment : KeyboardEntryDialogFragment(R.layout.v2_media_a this.recipient = recipient mentionsViewModel.onRecipientChange(recipient) - input.setMentionValidator { annotations -> + binding.content.addAMessageInput.setMentionValidator { annotations -> if (!recipient.isPushV2Group) { annotations } else { @@ -221,7 +222,7 @@ class AddMessageDialogFragment : KeyboardEntryDialogFragment(R.layout.v2_media_a } mentionsViewModel.selectedRecipient.observe(viewLifecycleOwner) { recipient -> - input.replaceTextWithMention(recipient.getDisplayName(requireContext()), recipient.id) + binding.content.addAMessageInput.replaceTextWithMention(recipient.getDisplayName(requireContext()), recipient.id) } } @@ -239,17 +240,17 @@ class AddMessageDialogFragment : KeyboardEntryDialogFragment(R.layout.v2_media_a if (!emojiDrawerStub.resolved()) { keyboardPagerViewModel.setOnlyPage(KeyboardPage.EMOJI) emojiDrawerStub.get().setFragmentManager(childFragmentManager) - emojiDrawerToggle.attach(emojiDrawerStub.get()) + binding.content.emojiToggle.attach(emojiDrawerStub.get()) } - if (hud.currentInput == emojiDrawerStub.get()) { + if (binding.hud.currentInput == emojiDrawerStub.get()) { requestedEmojiDrawer = false - hud.showSoftkey(input) + binding.hud.showSoftkey(binding.content.addAMessageInput) } else { requestedEmojiDrawer = true - hud.hideSoftkey(input) { - hud.post { - hud.show(input, emojiDrawerStub.get()) + binding.hud.hideSoftkey(binding.content.addAMessageInput) { + binding.hud.post { + binding.hud.show(binding.content.addAMessageInput, emojiDrawerStub.get()) } } } @@ -268,11 +269,11 @@ class AddMessageDialogFragment : KeyboardEntryDialogFragment(R.layout.v2_media_a } private fun onEmojiSelected(emoji: String?) { - input.insertEmoji(emoji) + binding.content.addAMessageInput.insertEmoji(emoji) } private fun onKeyEvent(keyEvent: KeyEvent?) { - input.dispatchKeyEvent(keyEvent) + binding.content.addAMessageInput.dispatchKeyEvent(keyEvent) } companion object { diff --git a/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareArgs.java b/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareArgs.java index 32738a00d..924484bd5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareArgs.java +++ b/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareArgs.java @@ -168,7 +168,7 @@ public final class MultiShareArgs implements Parcelable { BreakIteratorCompat breakIteratorCompat = BreakIteratorCompat.getInstance(); breakIteratorCompat.setText(getDraftText()); - if (breakIteratorCompat.countBreaks() > Stories.MAX_BODY_SIZE) { + if (breakIteratorCompat.countBreaks() > Stories.MAX_TEXT_STORY_SIZE) { return false; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareSender.java b/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareSender.java index 44556c086..cc8b17add 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareSender.java +++ b/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareSender.java @@ -432,7 +432,7 @@ public final class MultiShareSender { BreakIteratorCompat breakIteratorCompat = BreakIteratorCompat.getInstance(); breakIteratorCompat.setText(draftText); - String trimmed = breakIteratorCompat.take(Stories.MAX_BODY_SIZE).toString(); + String trimmed = breakIteratorCompat.take(Stories.MAX_TEXT_STORY_SIZE).toString(); if (linkPreview == null) { return trimmed; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt index b5bb482aa..203ea74f3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt @@ -47,7 +47,8 @@ object Stories { private val TAG = Log.tag(Stories::class.java) - const val MAX_BODY_SIZE = 700 + const val MAX_TEXT_STORY_SIZE = 700 + const val MAX_CAPTION_SIZE = 1500 @JvmField val MAX_VIDEO_DURATION_MILLIS = TimeUnit.SECONDS.toMillis(30) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt index 800dd7d0c..1ba1eb455 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt @@ -12,7 +12,9 @@ import android.media.AudioManager import android.net.Uri import android.os.Build import android.os.Bundle +import android.text.method.ScrollingMovementMethod import android.view.GestureDetector +import android.view.GestureDetector.SimpleOnGestureListener import android.view.MotionEvent import android.view.ScaleGestureDetector import android.view.View @@ -826,9 +828,16 @@ class StoryViewerPageFragment : } private fun onShowCaptionOverlay(caption: TextView, largeCaption: TextView, largeCaptionOverlay: View) { + sharedViewModel.setIsChildScrolling(true) + caption.visible = false largeCaption.visible = true largeCaptionOverlay.visible = true + largeCaption.movementMethod = ScrollingMovementMethod() + largeCaption.scrollY = 0 + largeCaption.setOnClickListener { + onHideCaptionOverlay(caption, largeCaption, largeCaptionOverlay) + } largeCaptionOverlay.setOnClickListener { onHideCaptionOverlay(caption, largeCaption, largeCaptionOverlay) } @@ -839,8 +848,10 @@ class StoryViewerPageFragment : caption.visible = true largeCaption.visible = false largeCaptionOverlay.visible = false + largeCaption.setOnClickListener(null) largeCaptionOverlay.setOnClickListener(null) viewModel.setIsDisplayingCaptionOverlay(false) + sharedViewModel.setIsChildScrolling(false) } private fun presentFrom(from: TextView, storyPost: StoryPost) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPlaybackState.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPlaybackState.kt index 7f51a6b95..2bde47866 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPlaybackState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPlaybackState.kt @@ -25,7 +25,10 @@ data class StoryViewerPlaybackState( ) { val hideChromeImmediate: Boolean = isRunningSharedElementAnimation - val hideChrome: Boolean = isRunningSharedElementAnimation || isUserLongTouching || isUserScrollingChild || isUserScaling + val hideChrome: Boolean = isRunningSharedElementAnimation || + isUserLongTouching || + (isUserScrollingChild && !isDisplayingCaptionOverlay) || + isUserScaling val isPaused: Boolean = !areSegmentsInitialized || isUserTouching || diff --git a/app/src/main/res/drawable/rounded_rectangle_secondary_dark.xml b/app/src/main/res/drawable/rounded_rectangle_secondary_dark.xml index 955aa219d..050b25ea1 100644 --- a/app/src/main/res/drawable/rounded_rectangle_secondary_dark.xml +++ b/app/src/main/res/drawable/rounded_rectangle_secondary_dark.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/layout/stories_viewer_fragment_page.xml b/app/src/main/res/layout/stories_viewer_fragment_page.xml index 0fb60beaf..0448ad2e6 100644 --- a/app/src/main/res/layout/stories_viewer_fragment_page.xml +++ b/app/src/main/res/layout/stories_viewer_fragment_page.xml @@ -132,14 +132,14 @@ - + \ No newline at end of file diff --git a/app/src/main/res/layout/v2_media_add_message_dialog_fragment_content.xml b/app/src/main/res/layout/v2_media_add_message_dialog_fragment_content.xml index 50626f70c..d36265300 100644 --- a/app/src/main/res/layout/v2_media_add_message_dialog_fragment_content.xml +++ b/app/src/main/res/layout/v2_media_add_message_dialog_fragment_content.xml @@ -1,8 +1,7 @@ @@ -26,7 +25,7 @@ android:layout_height="0dp" android:background="@drawable/rounded_rectangle_secondary_dark" app:layout_constraintBottom_toBottomOf="@id/add_a_message_input" - app:layout_constraintEnd_toEndOf="@id/add_a_message_input" + app:layout_constraintEnd_toEndOf="@id/add_a_message_limit" app:layout_constraintStart_toStartOf="@id/emoji_toggle" app:layout_constraintTop_toTopOf="@id/add_a_message_input" /> @@ -49,7 +48,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:layout_marginEnd="6dp" + app:layout_goneMarginEnd="6dp" android:layout_marginBottom="8dp" android:background="@null" android:hint="@string/MediaReviewFragment__add_a_message" @@ -61,11 +60,25 @@ android:paddingEnd="10dp" android:textAppearance="@style/Signal.Text.Body" app:layout_constraintBottom_toTopOf="@id/emoji_drawer_stub" - app:layout_constraintEnd_toStartOf="@id/confirm_button" + app:layout_constraintEnd_toStartOf="@id/add_a_message_limit" app:layout_constraintStart_toEndOf="@id/emoji_toggle" app:layout_constraintTop_toTopOf="@id/input_barrier" app:layout_goneMarginStart="12dp" /> + +