From 65255121ded168a177666ac0c87a85e9536d7532 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Fri, 23 Jun 2023 13:07:58 -0400 Subject: [PATCH] Add media keyboard support in CFv2. --- .../components/InputAwareConstraintLayout.kt | 16 ++- .../components/emoji/MediaKeyboard.java | 2 - .../v2/ConversationActivityResultContracts.kt | 31 ++++- .../conversation/v2/ConversationFragment.kt | 115 ++++++++++++++++-- .../conversation/v2/ConversationRepository.kt | 11 +- .../conversation/v2/ConversationViewModel.kt | 6 + .../v2/keyboard/AttachmentKeyboardFragment.kt | 2 + .../keyboard/KeyboardPagerFragment.kt | 9 +- 8 files changed, 171 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/InputAwareConstraintLayout.kt b/app/src/main/java/org/thoughtcrime/securesms/components/InputAwareConstraintLayout.kt index b3a3863b7c..52a365e5f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/InputAwareConstraintLayout.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/InputAwareConstraintLayout.kt @@ -34,10 +34,13 @@ class InputAwareConstraintLayout @JvmOverloads constructor( hideInput(resetKeyboardGuideline = false) } - fun toggleInput(fragmentCreator: FragmentCreator, imeTarget: EditText, toggled: (Boolean) -> Unit = { }) { + fun toggleInput(fragmentCreator: FragmentCreator, imeTarget: EditText, showSoftKeyOnHide: Boolean = false) { if (fragmentCreator.id == inputId) { - hideInput(resetKeyboardGuideline = true) - toggled(false) + if (showSoftKeyOnHide) { + showSoftkey(imeTarget) + } else { + hideInput(resetKeyboardGuideline = true) + } } else { hideInput(resetKeyboardGuideline = false) showInput(fragmentCreator, imeTarget) @@ -55,6 +58,7 @@ class InputAwareConstraintLayout @JvmOverloads constructor( fragmentManager .beginTransaction() .replace(R.id.input_container, input!!) + .runOnCommit { (input as? InputFragment)?.show() } .commit() overrideKeyboardGuidelineWithPreviousHeight() @@ -66,6 +70,7 @@ class InputAwareConstraintLayout @JvmOverloads constructor( private fun hideInput(resetKeyboardGuideline: Boolean) { val inputHidden = input != null input?.let { + (input as? InputFragment)?.hide() fragmentManager .beginTransaction() .remove(it) @@ -94,4 +99,9 @@ class InputAwareConstraintLayout @JvmOverloads constructor( fun onInputShown() fun onInputHidden() } + + interface InputFragment { + fun show() + fun hide() + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java index 49f4b2f70b..93dca90b79 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java @@ -105,7 +105,6 @@ public class MediaKeyboard extends FrameLayout implements InputView { if (!isInitialised) initView(); setVisibility(VISIBLE); - if (keyboardListener != null) keyboardListener.onShown(); keyboardPagerFragment.show(); } @@ -113,7 +112,6 @@ public class MediaKeyboard extends FrameLayout implements InputView { public void hide(boolean immediate) { setVisibility(GONE); onCloseEmojiSearchInternal(false); - if (keyboardListener != null) keyboardListener.onHidden(); Log.i(TAG, "hide()"); keyboardPagerFragment.hide(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt index 403b61c46a..f7aeaed120 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityResultContracts.kt @@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.contactshare.Contact import org.thoughtcrime.securesms.contactshare.ContactShareEditActivity import org.thoughtcrime.securesms.conversation.MessageSendType import org.thoughtcrime.securesms.conversation.colors.ChatColors +import org.thoughtcrime.securesms.giph.ui.GiphyActivity import org.thoughtcrime.securesms.mediasend.Media import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity @@ -32,6 +33,7 @@ class ConversationActivityResultContracts(fragment: Fragment, private val callba private val contactShareLauncher = fragment.registerForActivityResult(ContactShareEditor) { contacts -> callbacks.onSendContacts(contacts) } private val mediaSelectionLauncher = fragment.registerForActivityResult(MediaSelection) { result -> callbacks.onMediaSend(result) } + private val gifSearchLauncher = fragment.registerForActivityResult(GifSearch) { result -> callbacks.onMediaSend(result) } fun launchContactShareEditor(uri: Uri, chatColors: ChatColors) { contactShareLauncher.launch(uri to chatColors) @@ -41,14 +43,18 @@ class ConversationActivityResultContracts(fragment: Fragment, private val callba mediaSelectionLauncher.launch(MediaSelectionInput(mediaList, recipientId, text)) } - private object MediaSelection : ActivityResultContract() { + fun launchGifSearch(recipientId: RecipientId, text: CharSequence?) { + gifSearchLauncher.launch(GifSearchInput(recipientId, text)) + } + + private object MediaSelection : ActivityResultContract() { override fun createIntent(context: Context, input: MediaSelectionInput): Intent { val (media, recipientId, text) = input return MediaSelectionActivity.editor(context, MessageSendType.SignalMessageSendType, media, recipientId, text) } - override fun parseResult(resultCode: Int, intent: Intent?): MediaSendActivityResult { - return MediaSendActivityResult.fromData(intent!!) + override fun parseResult(resultCode: Int, intent: Intent?): MediaSendActivityResult? { + return intent?.let { MediaSendActivityResult.fromData(intent) } } } @@ -63,10 +69,27 @@ class ConversationActivityResultContracts(fragment: Fragment, private val callba } } + private object GifSearch : ActivityResultContract() { + override fun createIntent(context: Context, input: GifSearchInput): Intent { + return Intent(context, GiphyActivity::class.java).apply { + putExtra(GiphyActivity.EXTRA_IS_MMS, false) + putExtra(GiphyActivity.EXTRA_RECIPIENT_ID, input.recipientId) + putExtra(GiphyActivity.EXTRA_TRANSPORT, MessageSendType.SignalMessageSendType) + putExtra(GiphyActivity.EXTRA_TEXT, input.text) + } + } + + override fun parseResult(resultCode: Int, intent: Intent?): MediaSendActivityResult? { + return intent?.let { MediaSendActivityResult.fromData(intent) } + } + } + private data class MediaSelectionInput(val media: List, val recipientId: RecipientId, val text: CharSequence?) + private data class GifSearchInput(val recipientId: RecipientId, val text: CharSequence?) + interface Callbacks { fun onSendContacts(contacts: List) - fun onMediaSend(result: MediaSendActivityResult) + fun onMediaSend(result: MediaSendActivityResult?) } } 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 a08bc09f11..4bc6bedba7 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 @@ -52,6 +52,7 @@ import androidx.core.view.doOnPreDraw import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentResultListener +import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner @@ -106,6 +107,9 @@ import org.thoughtcrime.securesms.components.ProgressCardDialogFragmentArgs import org.thoughtcrime.securesms.components.ScrollToPositionDelegate import org.thoughtcrime.securesms.components.SendButton import org.thoughtcrime.securesms.components.ViewBinderDelegate +import org.thoughtcrime.securesms.components.emoji.EmojiEventListener +import org.thoughtcrime.securesms.components.emoji.MediaKeyboard +import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel import org.thoughtcrime.securesms.components.mention.MentionAnnotation import org.thoughtcrime.securesms.components.menu.ActionItem import org.thoughtcrime.securesms.components.menu.SignalBottomActionBar @@ -190,6 +194,12 @@ import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationSuggestio import org.thoughtcrime.securesms.groups.v2.GroupBlockJoinRequestResult import org.thoughtcrime.securesms.invites.InviteActions import org.thoughtcrime.securesms.keyboard.KeyboardPage +import org.thoughtcrime.securesms.keyboard.KeyboardPagerFragment +import org.thoughtcrime.securesms.keyboard.KeyboardPagerViewModel +import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageFragment +import org.thoughtcrime.securesms.keyboard.gif.GifKeyboardPageFragment +import org.thoughtcrime.securesms.keyboard.sticker.StickerKeyboardPageFragment +import org.thoughtcrime.securesms.keyboard.sticker.StickerSearchDialogFragment import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.linkpreview.LinkPreview import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModelV2 @@ -219,6 +229,7 @@ import org.thoughtcrime.securesms.notifications.v2.ConversationId import org.thoughtcrime.securesms.payments.preferences.PaymentsActivity import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.profiles.spoofing.ReviewCardDialogFragment +import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.ratelimit.RecaptchaProofBottomSheetFragment import org.thoughtcrime.securesms.reactions.ReactionsBottomSheetDialogFragment import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment @@ -233,7 +244,9 @@ import org.thoughtcrime.securesms.revealable.ViewOnceMessageActivity import org.thoughtcrime.securesms.revealable.ViewOnceUtil import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet import org.thoughtcrime.securesms.sms.MessageSender +import org.thoughtcrime.securesms.stickers.StickerEventListener import org.thoughtcrime.securesms.stickers.StickerLocator +import org.thoughtcrime.securesms.stickers.StickerManagementActivity import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent import org.thoughtcrime.securesms.stickers.StickerPackPreviewActivity import org.thoughtcrime.securesms.stories.StoryViewerArgs @@ -271,6 +284,7 @@ import org.thoughtcrime.securesms.wallpaper.ChatWallpaperDimLevelUtil import java.util.Locale import java.util.Optional import java.util.concurrent.ExecutionException +import kotlin.time.Duration.Companion.milliseconds /** * A single unified fragment for Conversations. @@ -278,7 +292,13 @@ import java.util.concurrent.ExecutionException class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment), ReactWithAnyEmojiBottomSheetDialogFragment.Callback, - ReactionsBottomSheetDialogFragment.Callback { + ReactionsBottomSheetDialogFragment.Callback, + EmojiKeyboardPageFragment.Callback, + EmojiEventListener, + GifKeyboardPageFragment.Host, + StickerEventListener, + StickerKeyboardPageFragment.Callback, + MediaKeyboard.MediaKeyboardListener { companion object { private val TAG = Log.tag(ConversationFragment::class.java) @@ -340,6 +360,8 @@ class ConversationFragment : ConversationSearchViewModel(getString(R.string.note_to_self)) } + private val keyboardPagerViewModel: KeyboardPagerViewModel by activityViewModels() + private val stickerViewModel: StickerSuggestionsViewModel by viewModel { StickerSuggestionsViewModel() } @@ -347,6 +369,7 @@ class ConversationFragment : private val conversationTooltips = ConversationTooltips(this) private val colorizer = Colorizer() private val textDraftSaveDebouncer = Debouncer(500) + private val recentEmojis: RecentEmojiPageModel by lazy { RecentEmojiPageModel(ApplicationDependencies.getApplication(), TextSecurePreferences.RECENT_STORAGE_KEY) } private lateinit var layoutManager: LinearLayoutManager private lateinit var markReadHelper: MarkReadHelper @@ -441,7 +464,7 @@ class ConversationFragment : container.fragmentManager = childFragmentManager ToolbarDependentMarginListener(binding.toolbar) - initializeMediaKeyboardToggle() + initializeMediaKeyboard() } override fun onViewStateRestored(savedInstanceState: Bundle?) { @@ -506,6 +529,70 @@ class ConversationFragment : clearFocusedItem() } + override fun openEmojiSearch() { + // TODO [cfv2] emoji search + } + + override fun onEmojiSelected(emoji: String?) { + if (emoji != null) { + inputPanel.onEmojiSelected(emoji) + recentEmojis.onCodePointSelected(emoji) + } + } + + override fun onKeyEvent(keyEvent: KeyEvent?) { + if (keyEvent != null) { + inputPanel.onKeyEvent(keyEvent) + } + } + + override fun openStickerSearch() { + StickerSearchDialogFragment.show(childFragmentManager) + } + + override fun onStickerSelected(sticker: StickerRecord) { + sendSticker( + stickerRecord = sticker, + clearCompose = false + ) + } + + override fun onStickerManagementClicked() { + startActivity(StickerManagementActivity.getIntent(requireContext())) + container.hideInput() + } + + override fun isMms(): Boolean { + return false + } + + override fun openGifSearch() { + val recipientId = viewModel.recipientSnapshot?.id ?: return + conversationActivityResultContracts.launchGifSearch(recipientId, composeText.textTrimmed) + } + + override fun onGifSelectSuccess(blobUri: Uri, width: Int, height: Int) { + setMedia( + uri = blobUri, + mediaType = SlideFactory.MediaType.from(BlobProvider.getMimeType(blobUri))!!, + width = width, + height = height, + videoGif = true + ) + } + + override fun onShown() { + inputPanel.mediaKeyboardListener.onShown() + } + + override fun onHidden() { + inputPanel.mediaKeyboardListener.onHidden() + } + + override fun onKeyboardChanged(page: KeyboardPage) { + inputPanel.mediaKeyboardListener.onKeyboardChanged(page) + } + private fun observeConversationThread() { var firstRender = true disposables += viewModel @@ -1069,20 +1156,21 @@ class ConversationFragment : .addTo(disposables) } - private fun initializeMediaKeyboardToggle() { + private fun initializeMediaKeyboard() { val isSystemEmojiPreferred = SignalStore.settings().isPreferSystemEmoji val keyboardMode: TextSecurePreferences.MediaKeyboardMode = TextSecurePreferences.getMediaKeyboardMode(requireContext()) val stickerIntro: Boolean = !TextSecurePreferences.hasSeenStickerIntroTooltip(requireContext()) inputPanel.showMediaKeyboardToggle(true) - val toggleMode = when (keyboardMode) { + val keyboardPage = when (keyboardMode) { TextSecurePreferences.MediaKeyboardMode.EMOJI -> if (isSystemEmojiPreferred) KeyboardPage.STICKER else KeyboardPage.EMOJI TextSecurePreferences.MediaKeyboardMode.STICKER -> KeyboardPage.STICKER TextSecurePreferences.MediaKeyboardMode.GIF -> KeyboardPage.GIF } - inputPanel.setMediaKeyboardToggleMode(toggleMode) + inputPanel.setMediaKeyboardToggleMode(keyboardPage) + keyboardPagerViewModel.switchToPage(keyboardPage) if (stickerIntro) { TextSecurePreferences.setMediaKeyboardMode(requireContext(), TextSecurePreferences.MediaKeyboardMode.STICKER) @@ -1165,6 +1253,8 @@ class ConversationFragment : ) sendMessageWithoutComposeInput(slide, clearCompose = clearCompose) + + viewModel.updateStickerLastUsedTime(stickerRecord, System.currentTimeMillis().milliseconds) } private fun sendMessageWithoutComposeInput( @@ -1199,7 +1289,7 @@ class ConversationFragment : preUploadResults: List = emptyList(), afterSendComplete: () -> Unit = {} ) { - val metricId = viewModel.recipientSnapshot?.let { if (it.isGroup == true) SignalLocalMetrics.GroupMessageSend.start() else SignalLocalMetrics.IndividualMessageSend.start() } + val metricId = viewModel.recipientSnapshot?.let { if (it.isGroup) SignalLocalMetrics.GroupMessageSend.start() else SignalLocalMetrics.IndividualMessageSend.start() } val send: Completable = viewModel.sendMessage( metricId = metricId, @@ -2511,7 +2601,11 @@ class ConversationFragment : ) } - override fun onMediaSend(result: MediaSendActivityResult) { + override fun onMediaSend(result: MediaSendActivityResult?) { + if (result == null) { + return + } + val recipientSnapshot = viewModel.recipientSnapshot if (result.recipientId != recipientSnapshot?.id) { Log.w(TAG, "Result's recipientId did not match ours! Result: " + result.recipientId + ", Ours: " + recipientSnapshot?.id) @@ -2955,7 +3049,7 @@ class ConversationFragment : } override fun onEmojiToggle() { - // TODO [cfv2] Not yet implemented + container.toggleInput(MediaKeyboardFragmentCreator, composeText, showSoftKeyOnHide = true) } override fun onLinkPreviewCanceled() { @@ -3032,6 +3126,11 @@ class ConversationFragment : } } + private object MediaKeyboardFragmentCreator : InputAwareConstraintLayout.FragmentCreator { + override val id: Int = 2 + override fun create(): Fragment = KeyboardPagerFragment() + } + private inner class KeyboardEvents : OnBackPressedCallback(false), InputAwareConstraintLayout.Listener { override fun handleOnBackPressed() { container.hideInput() diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt index e3a188af18..9c22de7fc2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt @@ -68,6 +68,7 @@ import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.Quote import org.thoughtcrime.securesms.database.model.ReactionRecord +import org.thoughtcrime.securesms.database.model.StickerRecord import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob @@ -102,6 +103,7 @@ import org.thoughtcrime.securesms.util.requireTextSlide import java.io.IOException import java.util.Optional import kotlin.math.max +import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds class ConversationRepository( @@ -225,7 +227,8 @@ class ConversationRepository( messageToEdit = messageToEdit?.id ?: 0, mentions = mentions, sharedContacts = contacts, - linkPreviews = linkPreviews + linkPreviews = linkPreviews, + attachments = slideDeck?.asAttachments() ?: emptyList() ) if (preUploadResults.isEmpty()) { @@ -551,6 +554,12 @@ class ConversationRepository( } } + fun updateStickerLastUsedTime(stickerRecord: StickerRecord, timestamp: Duration) { + SignalExecutors.BOUNDED_IO.execute { + SignalDatabase.stickers.updateStickerLastUsedTime(stickerRecord.rowId, timestamp.inWholeMilliseconds) + } + } + /** * Glide target for a contact photo which expects an error drawable, and publishes * the result to the given emitter. diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt index 944a5dd19f..c3505501aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt @@ -42,6 +42,7 @@ import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.Quote import org.thoughtcrime.securesms.database.model.ReactionRecord +import org.thoughtcrime.securesms.database.model.StickerRecord import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.jobs.RetrieveProfileJob @@ -62,6 +63,7 @@ import org.thoughtcrime.securesms.util.hasGiftBadge import org.thoughtcrime.securesms.util.rx.RxStore import org.thoughtcrime.securesms.wallpaper.ChatWallpaper import java.util.Optional +import kotlin.time.Duration /** * ConversationViewModel, which operates solely off of a thread id that never changes. @@ -362,4 +364,8 @@ class ConversationViewModel( fun deleteSlideData(slides: List) { repository.deleteSlideData(slides) } + + fun updateStickerLastUsedTime(stickerRecord: StickerRecord, timestamp: Duration) { + repository.updateStickerLastUsedTime(stickerRecord, timestamp) + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/keyboard/AttachmentKeyboardFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/keyboard/AttachmentKeyboardFragment.kt index ff6da0f845..ea9a2aa8c5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/keyboard/AttachmentKeyboardFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/keyboard/AttachmentKeyboardFragment.kt @@ -12,6 +12,7 @@ import androidx.core.os.bundleOf import androidx.fragment.app.setFragmentResult import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.kotlin.subscribeBy import org.signal.core.util.concurrent.LifecycleDisposable import org.signal.core.util.concurrent.addTo @@ -62,6 +63,7 @@ class AttachmentKeyboardFragment : LoggingFragment(R.layout.attachment_keyboard_ conversationViewModel = ViewModelProvider(requireParentFragment()).get(ConversationViewModel::class.java) conversationViewModel .recipient + .observeOn(AndroidSchedulers.mainThread()) .subscribeBy { attachmentKeyboardView.setWallpaperEnabled(it.hasWallpaper()) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyboard/KeyboardPagerFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/keyboard/KeyboardPagerFragment.kt index 6ea3850ecd..d05ffe9587 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyboard/KeyboardPagerFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyboard/KeyboardPagerFragment.kt @@ -7,6 +7,7 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.InputAwareConstraintLayout import org.thoughtcrime.securesms.components.emoji.MediaKeyboard import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageFragment import org.thoughtcrime.securesms.keyboard.gif.GifKeyboardPageFragment @@ -20,7 +21,7 @@ import org.thoughtcrime.securesms.util.fragments.findListener import org.thoughtcrime.securesms.util.visible import kotlin.reflect.KClass -class KeyboardPagerFragment : Fragment() { +class KeyboardPagerFragment : Fragment(), InputAwareConstraintLayout.InputFragment { private lateinit var emojiButton: View private lateinit var stickerButton: View @@ -113,7 +114,8 @@ class KeyboardPagerFragment : Fragment() { transaction.commitAllowingStateLoss() } - fun show() { + override fun show() { + findListener()?.onShown() if (isAdded && view != null) { onHiddenChanged(false) @@ -121,7 +123,8 @@ class KeyboardPagerFragment : Fragment() { } } - fun hide() { + override fun hide() { + findListener()?.onHidden() if (isAdded && view != null) { onHiddenChanged(true)