From f74827aefa7bd18f94bbb6365963f1f72f3b10e9 Mon Sep 17 00:00:00 2001 From: Adolfo Santiago Date: Wed, 17 Sep 2025 07:23:34 +0200 Subject: [PATCH] New emoji dialog picker when composing a status --- .../components/compose/ComposeActivity.kt | 74 ++++++------------- .../fragments/SearchStatusesFragment.kt | 4 +- .../tusky/fragment/NotificationsFragment.java | 4 +- .../tusky/fragment/TimelineFragment.java | 4 +- .../tusky/fragment/ViewThreadFragment.java | 4 +- .../emojireactions/CustomEmojiPickerPage.kt | 4 +- .../emojireactions/EmojiDialogFragment.kt | 15 ++-- .../emojireactions/UnicodeEmojiPickerPage.kt | 4 +- 8 files changed, 42 insertions(+), 71 deletions(-) diff --git a/husky/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt b/husky/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt index 070051b0..f777e2af 100644 --- a/husky/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt +++ b/husky/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt @@ -32,7 +32,6 @@ import android.graphics.PorterDuff import android.graphics.PorterDuffColorFilter import android.graphics.drawable.Drawable import android.net.Uri -import android.os.Build import android.os.Build.VERSION import android.os.Build.VERSION_CODES import android.os.Bundle @@ -71,8 +70,6 @@ import com.google.android.material.snackbar.Snackbar import com.keylesspalace.tusky.BaseActivity import com.keylesspalace.tusky.BuildConfig import com.keylesspalace.tusky.R -import com.keylesspalace.tusky.adapter.EmojiAdapter -import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener import com.keylesspalace.tusky.appstore.Event import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.StatusPreviewEvent @@ -96,14 +93,12 @@ import com.keylesspalace.tusky.databinding.ActivityComposeBinding import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.DraftAttachment import com.keylesspalace.tusky.entity.Attachment -import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.NewPoll import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.util.BBCodeEdit import com.keylesspalace.tusky.util.ComposeTokenizer import com.keylesspalace.tusky.util.HTMLEdit -import com.keylesspalace.tusky.util.PostFormat import com.keylesspalace.tusky.util.PostFormat.BBCODE import com.keylesspalace.tusky.util.PostFormat.HTML import com.keylesspalace.tusky.util.PostFormat.MARKDOWN @@ -118,6 +113,7 @@ import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.visible import com.keylesspalace.tusky.util.withLifecycleContext import com.keylesspalace.tusky.view.EmojiKeyboard +import com.keylesspalace.tusky.view.emojireactions.EmojiDialogFragment import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt @@ -139,7 +135,6 @@ class ComposeActivity : BaseActivity(), ComposeOptionsListener, ComposeAutoCompleteAdapter.AutocompletionProvider, - OnEmojiSelectedListener, InputConnectionCompat.OnCommitContentListener, TimePickerDialog.OnTimeSetListener, EmojiKeyboard.OnEmojiSelectedListener { @@ -150,7 +145,6 @@ class ComposeActivity : private lateinit var composeOptionsBehavior: BottomSheetBehavior<*> private lateinit var addMediaBehavior: BottomSheetBehavior<*> - private lateinit var emojiBehavior: BottomSheetBehavior<*> private lateinit var scheduleBehavior: BottomSheetBehavior<*> private lateinit var stickerBehavior: BottomSheetBehavior<*> private lateinit var previewBehavior: BottomSheetBehavior<*> @@ -170,14 +164,12 @@ class ComposeActivity : // Acting like a teen: deliberately ignoring parent. if (composeOptionsBehavior.state == BottomSheetBehavior.STATE_EXPANDED || addMediaBehavior.state == BottomSheetBehavior.STATE_EXPANDED || - emojiBehavior.state == BottomSheetBehavior.STATE_EXPANDED || scheduleBehavior.state == BottomSheetBehavior.STATE_EXPANDED || stickerBehavior.state == BottomSheetBehavior.STATE_EXPANDED || previewBehavior.state == BottomSheetBehavior.STATE_EXPANDED ) { composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN - emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN stickerBehavior.state = BottomSheetBehavior.STATE_HIDDEN previewBehavior.state = BottomSheetBehavior.STATE_HIDDEN @@ -236,7 +228,7 @@ class ComposeActivity : val composeOptions = intent.getParcelableExtra(COMPOSE_OPTIONS_EXTRA) suggestFormattingSyntax = if (!composeOptions?.formattingSyntax.isNullOrEmpty()) { - composeOptions?.formattingSyntax!! + composeOptions.formattingSyntax!! } else { activeAccount.defaultFormattingSyntax } @@ -249,7 +241,7 @@ class ComposeActivity : } if (!composeOptions?.scheduledAt.isNullOrEmpty()) { - binding.composeScheduleView.setDateTime(composeOptions?.scheduledAt) + binding.composeScheduleView.setDateTime(composeOptions.scheduledAt) } viewModel.composeWithZwsp.value = @@ -481,7 +473,11 @@ class ComposeActivity : } } - viewModel.emoji.observe { emoji -> setEmojiList(emoji) } + viewModel.emoji.observe { emojis -> + val emojiListIsEmpty = emojis.isNullOrEmpty() + enableButton(binding.composeEmojiButton, !emojiListIsEmpty, !emojiListIsEmpty) + } + combineLiveData( viewModel.markMediaAsSensitive, viewModel.showContentWarning @@ -572,7 +568,6 @@ class ComposeActivity : composeOptionsBehavior = BottomSheetBehavior.from(binding.composeOptionsBottomSheet) addMediaBehavior = BottomSheetBehavior.from(binding.addMediaBottomSheet) scheduleBehavior = BottomSheetBehavior.from(binding.composeScheduleView) - emojiBehavior = BottomSheetBehavior.from(binding.emojiView) stickerBehavior = BottomSheetBehavior.from(binding.stickerKeyboard) previewBehavior = BottomSheetBehavior.from(binding.previewScroll) @@ -974,7 +969,6 @@ class ComposeActivity : if (composeOptionsBehavior.state == BottomSheetBehavior.STATE_HIDDEN || composeOptionsBehavior.state == BottomSheetBehavior.STATE_COLLAPSED) { composeOptionsBehavior.state = BottomSheetBehavior.STATE_EXPANDED addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN - emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN stickerBehavior.state = BottomSheetBehavior.STATE_HIDDEN scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN previewBehavior.state = BottomSheetBehavior.STATE_HIDDEN @@ -996,7 +990,6 @@ class ComposeActivity : scheduleBehavior.state = BottomSheetBehavior.STATE_EXPANDED composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN - emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN stickerBehavior.state = BottomSheetBehavior.STATE_HIDDEN previewBehavior.state = BottomSheetBehavior.STATE_HIDDEN } else { @@ -1005,25 +998,20 @@ class ComposeActivity : } private fun showEmojis() { - binding.emojiView.adapter?.let { - if (it.itemCount == 0) { - val errorMessage = getString( - R.string.error_no_custom_emojis, - accountManager.value.activeAccount!!.domain - ) - Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show() - } else { - if (emojiBehavior.state == BottomSheetBehavior.STATE_HIDDEN || emojiBehavior.state == BottomSheetBehavior.STATE_COLLAPSED) { - emojiBehavior.state = BottomSheetBehavior.STATE_EXPANDED - stickerBehavior.state = BottomSheetBehavior.STATE_HIDDEN - composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN - addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN - scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN - previewBehavior.state = BottomSheetBehavior.STATE_HIDDEN - } else { - emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN - } - } + if (viewModel.emoji.value.isNullOrEmpty()) { + val errorMessage = getString( + R.string.error_no_custom_emojis, accountManager.value.activeAccount!!.domain + ) + Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show() + } else { + EmojiDialogFragment( + viewModel.emoji.value, onEmojiClick = { isCustomEmoji, shortcode -> + if (isCustomEmoji) { + replaceTextAtCaret(":$shortcode: ") + } else { + replaceTextAtCaret("$shortcode ") + } + }).show(supportFragmentManager, EmojiDialogFragment.DIALOG_TAG) } } @@ -1031,7 +1019,6 @@ class ComposeActivity : if (addMediaBehavior.state == BottomSheetBehavior.STATE_HIDDEN || addMediaBehavior.state == BottomSheetBehavior.STATE_COLLAPSED) { addMediaBehavior.state = BottomSheetBehavior.STATE_EXPANDED composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN - emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN stickerBehavior.state = BottomSheetBehavior.STATE_HIDDEN scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN previewBehavior.state = BottomSheetBehavior.STATE_HIDDEN @@ -1204,7 +1191,6 @@ class ComposeActivity : previewBehavior.state = BottomSheetBehavior.STATE_EXPANDED addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN - emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN stickerBehavior.state = BottomSheetBehavior.STATE_HIDDEN } @@ -1529,27 +1515,11 @@ class ComposeActivity : return viewModel.searchAutocompleteSuggestions(token) } - override fun onEmojiSelected(shortcode: String) { - replaceTextAtCaret(":$shortcode: ") - } - - private fun setEmojiList(emojiList: List?) { - if (emojiList != null) { - binding.emojiView.adapter = EmojiAdapter( - emojiList, - this@ComposeActivity, - preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) - ) - enableButton(binding.composeEmojiButton, true, emojiList.isNotEmpty()) - } - } - private fun showStickers() { if (stickerBehavior.state == BottomSheetBehavior.STATE_HIDDEN || stickerBehavior.state == BottomSheetBehavior.STATE_COLLAPSED) { stickerBehavior.state = BottomSheetBehavior.STATE_EXPANDED addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN - emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN previewBehavior.state = BottomSheetBehavior.STATE_HIDDEN } else { diff --git a/husky/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/husky/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt index ba34896e..01cd3308 100644 --- a/husky/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt +++ b/husky/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt @@ -551,8 +551,8 @@ class SearchStatusesFragment : } else { EmojiDialogFragment( searchViewModel.instance.value?.emojiList, - onReactionCallback = { emojiReact -> - searchViewModel.emojiReact(react, emojiReact, statusId) + onEmojiClick = { _, shortcode -> + searchViewModel.emojiReact(react, shortcode, statusId) } ).show(parentFragmentManager, EmojiDialogFragment.DIALOG_TAG) } diff --git a/husky/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java b/husky/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java index 3d4e4d3e..e00b6dd9 100644 --- a/husky/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java +++ b/husky/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java @@ -1627,8 +1627,8 @@ public void onEmojiReact(final boolean react, @NonNull final String emoji, @NonN } else { EmojiDialogFragment dialog = new EmojiDialogFragment( instanceRepo.getInstanceInfoDb().getEmojiList(), - emojiReact -> { - timelineCases.getValue().react(emojiReact, statusId, true) + (isCustomEmoji, shortcode) -> { + timelineCases.getValue().react(shortcode, statusId, true) .observeOn(AndroidSchedulers.mainThread()).as(autoDisposable(from(this))) .subscribe((newStatus) -> setEmojiReactForStatus(posAndNotification.first, newStatus), (t) -> Timber.e(t, "Failed to react with " + emoji + " on status: " + statusId)); diff --git a/husky/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java b/husky/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java index d60aea2f..db57c243 100644 --- a/husky/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java +++ b/husky/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java @@ -1772,8 +1772,8 @@ public void onEmojiReact(final boolean react, @NonNull final String emoji, @NonN } else { EmojiDialogFragment dialog = new EmojiDialogFragment( instanceRepo.getInstanceInfoDb().getEmojiList(), - emojiReact -> { - timelineCases.getValue().react(emojiReact, statusId, true) + (isCustomEmoji, shortcode) -> { + timelineCases.getValue().react(shortcode, statusId, true) .observeOn(AndroidSchedulers.mainThread()).as(autoDisposable(from(this))) .subscribe((newStatus) -> setEmojiReactionForStatus(position, newStatus), (t) -> Timber.e(t, "Failed to react with " + emoji + " on status: " + statusId)); diff --git a/husky/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java b/husky/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java index 017c6022..8f834322 100644 --- a/husky/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java +++ b/husky/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java @@ -915,8 +915,8 @@ public void onEmojiReact(final boolean react, @NonNull final String emoji, @NonN } else { EmojiDialogFragment dialog = new EmojiDialogFragment( instanceRepo.getInstanceInfoDb().getEmojiList(), - emojiReact -> { - timelineCases.getValue().react(emojiReact, statusId, true) + (isCustomEmoji, shortcode) -> { + timelineCases.getValue().react(shortcode, statusId, true) .observeOn(AndroidSchedulers.mainThread()).as(autoDisposable(from(this))) .subscribe((newStatus) -> setEmojiReactionForStatus(position, newStatus), (t) -> Timber.e(t, "Failed to react with " + emoji + " on status: " + statusId)); diff --git a/husky/app/src/main/java/com/keylesspalace/tusky/view/emojireactions/CustomEmojiPickerPage.kt b/husky/app/src/main/java/com/keylesspalace/tusky/view/emojireactions/CustomEmojiPickerPage.kt index 1dc5acfb..48d71282 100644 --- a/husky/app/src/main/java/com/keylesspalace/tusky/view/emojireactions/CustomEmojiPickerPage.kt +++ b/husky/app/src/main/java/com/keylesspalace/tusky/view/emojireactions/CustomEmojiPickerPage.kt @@ -23,7 +23,7 @@ import org.koin.androidx.viewmodel.ext.android.viewModel class CustomEmojiPickerPage( private val emojiList: List, - private val onReactionCallback: (String) -> Unit + private val onEmojiClick: (String) -> Unit ) : Fragment() { private lateinit var binding: LayoutEmojiCustomBinding @@ -33,7 +33,7 @@ class CustomEmojiPickerPage( ListEmojiAdapter( object : OnEmojiSelectedListener { override fun onEmojiSelected(shortcode: String) { - onReactionCallback(shortcode) + onEmojiClick(shortcode) } }, animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) diff --git a/husky/app/src/main/java/com/keylesspalace/tusky/view/emojireactions/EmojiDialogFragment.kt b/husky/app/src/main/java/com/keylesspalace/tusky/view/emojireactions/EmojiDialogFragment.kt index aa67f235..554ef0b1 100644 --- a/husky/app/src/main/java/com/keylesspalace/tusky/view/emojireactions/EmojiDialogFragment.kt +++ b/husky/app/src/main/java/com/keylesspalace/tusky/view/emojireactions/EmojiDialogFragment.kt @@ -15,7 +15,7 @@ import com.keylesspalace.tusky.entity.Emoji class EmojiDialogFragment( private val emojis: List?, - private val onReactionCallback: (String) -> Unit + private val onEmojiClick: (isCustomEmoji: Boolean, shortcode: String) -> Unit ) : DialogFragment() { companion object { @@ -29,9 +29,9 @@ class EmojiDialogFragment( binding = LayoutEmojiBinding.inflate(layoutInflater) val viewPager = binding.dialogViewPager.apply { - adapter = DialogViewPagerAdapter(requireActivity(), emojis) { shortcode -> + adapter = DialogViewPagerAdapter(requireActivity(), emojis) { isCustomEmoji, shortcode -> dismissAllowingStateLoss() - onReactionCallback(shortcode) + onEmojiClick(isCustomEmoji, shortcode) } isUserInputEnabled = false @@ -64,7 +64,8 @@ class EmojiDialogFragment( .create() dialog.window?.setSoftInputMode( - WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE + WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE or + WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE ) return dialog @@ -80,7 +81,7 @@ class EmojiDialogFragment( class DialogViewPagerAdapter( activity: FragmentActivity, private val emojis: List?, - private val onReactionCallback: (String) -> Unit + private val onEmojiClick: (isCustomEmoji: Boolean, shortcode: String) -> Unit ) : FragmentStateAdapter(activity) { override fun getItemCount(): Int { @@ -99,12 +100,12 @@ class EmojiDialogFragment( return when (position) { 0 -> { CustomEmojiPickerPage(emojis ?: emptyList()) { shortcode -> - onReactionCallback(shortcode) + onEmojiClick(true, shortcode) } } 1 -> { UnicodeEmojiPickerPage { emoji -> - onReactionCallback(emoji) + onEmojiClick(false, emoji) } } else -> { diff --git a/husky/app/src/main/java/com/keylesspalace/tusky/view/emojireactions/UnicodeEmojiPickerPage.kt b/husky/app/src/main/java/com/keylesspalace/tusky/view/emojireactions/UnicodeEmojiPickerPage.kt index cf708cf1..3e98206e 100644 --- a/husky/app/src/main/java/com/keylesspalace/tusky/view/emojireactions/UnicodeEmojiPickerPage.kt +++ b/husky/app/src/main/java/com/keylesspalace/tusky/view/emojireactions/UnicodeEmojiPickerPage.kt @@ -9,7 +9,7 @@ import com.keylesspalace.tusky.databinding.LayoutEmojiUnicodeBinding import com.keylesspalace.tusky.view.EmojiKeyboard class UnicodeEmojiPickerPage( - private val onReactionCallback: (String) -> Unit + private val onEmojiClick: (String) -> Unit ) : Fragment() { private lateinit var binding: LayoutEmojiUnicodeBinding @@ -27,7 +27,7 @@ class UnicodeEmojiPickerPage( binding.dialogEmojiKeyboard.setupKeyboard( "CustomEmojiKeyboard", EmojiKeyboard.UNICODE_MODE ) { _, emoji -> - onReactionCallback(emoji) + onEmojiClick(emoji) } } }