Skip to content

Commit d249729

Browse files
authored
Merge pull request #6623 from nextcloud/removing_emojipicker_message_input
(fix): Adding google emoji picker to message input fragment
2 parents e08b218 + 53f2e64 commit d249729

5 files changed

Lines changed: 128 additions & 1 deletion

File tree

app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
package com.nextcloud.talk.chat
99

10+
import android.content.Context
1011
import android.content.res.Resources
1112
import android.os.Build
1213
import android.os.Bundle
@@ -28,6 +29,7 @@ import android.view.animation.AlphaAnimation
2829
import android.view.animation.Animation
2930
import android.view.animation.Animation.AnimationListener
3031
import android.view.animation.LinearInterpolator
32+
import android.view.inputmethod.InputMethodManager
3133
import android.widget.Chronometer
3234
import android.widget.ImageButton
3335
import android.widget.ImageView
@@ -37,9 +39,11 @@ import android.widget.RelativeLayout
3739
import android.widget.SeekBar
3840
import androidx.appcompat.view.ContextThemeWrapper
3941
import androidx.compose.material3.MaterialTheme
42+
import androidx.core.content.ContextCompat
4043
import androidx.core.graphics.drawable.toDrawable
4144
import androidx.core.view.isVisible
4245
import androidx.core.widget.doAfterTextChanged
46+
import androidx.emoji2.emojipicker.RecentEmojiProvider
4347
import androidx.emoji2.widget.EmojiTextView
4448
import androidx.fragment.app.Fragment
4549
import androidx.fragment.app.activityViewModels
@@ -117,6 +121,7 @@ class MessageInputFragment : Fragment() {
117121
private var typedWhileTypingTimerIsRunning: Boolean = false
118122
private var typingTimer: CountDownTimer? = null
119123
private lateinit var chatActivity: ChatActivity
124+
private var restoreKeyboardOnEmojiDismiss = false
120125
private var mentionAutocomplete: Autocomplete<*>? = null
121126
private var xcounter = 0f
122127
private var ycounter = 0f
@@ -147,6 +152,7 @@ class MessageInputFragment : Fragment() {
147152
}
148153

149154
override fun onDestroyView() {
155+
restoreKeyboardOnEmojiDismiss = false
150156
super.onDestroyView()
151157
if (mentionAutocomplete != null && mentionAutocomplete!!.isPopupShowing) {
152158
mentionAutocomplete?.dismissPopup()
@@ -179,6 +185,7 @@ class MessageInputFragment : Fragment() {
179185
when (state) {
180186
is ChatViewModel.GetCapabilitiesUpdateState -> {
181187
initMessageInputView(state.spreedCapabilities)
188+
initSmileyKeyboardToggler()
182189
setupMentionAutocomplete()
183190
initVoiceRecordButton()
184191
initThreadHandling()
@@ -188,6 +195,7 @@ class MessageInputFragment : Fragment() {
188195

189196
is ChatViewModel.GetCapabilitiesInitialLoadState -> {
190197
initMessageInputView(state.spreedCapabilities)
198+
initSmileyKeyboardToggler()
191199
setupMentionAutocomplete()
192200
initVoiceRecordButton()
193201
initThreadHandling()
@@ -773,6 +781,8 @@ class MessageInputFragment : Fragment() {
773781

774782
private fun showRecordAudioUi(show: Boolean) {
775783
if (show) {
784+
restoreKeyboardOnEmojiDismiss = false
785+
binding.emojiPicker.isVisible = false
776786
val animation: Animation = AlphaAnimation(FULLY_OPAQUE, FULLY_TRANSPARENT)
777787
animation.duration = ANIMATION_DURATION
778788
animation.interpolator = LinearInterpolator()
@@ -785,6 +795,7 @@ class MessageInputFragment : Fragment() {
785795
binding.fragmentMessageInputView.audioRecordDuration.visibility = View.VISIBLE
786796
binding.fragmentMessageInputView.slideToCancelDescription.visibility = View.VISIBLE
787797
binding.fragmentMessageInputView.attachmentButton.visibility = View.GONE
798+
binding.fragmentMessageInputView.smileyButton.visibility = View.GONE
788799
binding.fragmentMessageInputView.messageInput.visibility = View.GONE
789800
binding.fragmentMessageInputView.messageInput.hint = ""
790801
binding.fragmentMessageInputView.scheduledMessagesButton.visibility = View.GONE
@@ -796,12 +807,95 @@ class MessageInputFragment : Fragment() {
796807
binding.fragmentMessageInputView.audioRecordDuration.visibility = View.GONE
797808
binding.fragmentMessageInputView.slideToCancelDescription.visibility = View.GONE
798809
binding.fragmentMessageInputView.attachmentButton.visibility = View.VISIBLE
810+
binding.fragmentMessageInputView.smileyButton.visibility = View.VISIBLE
799811
binding.fragmentMessageInputView.messageInput.visibility = View.VISIBLE
800812
binding.fragmentMessageInputView.messageInput.hint =
801813
requireContext().resources?.getString(R.string.nc_hint_enter_a_message)
802814
}
803815
}
804816

817+
private fun updateSmileyButtonIcon() {
818+
val icon = if (binding.emojiPicker.isVisible) {
819+
R.drawable.ic_baseline_keyboard_24
820+
} else {
821+
R.drawable.ic_insert_emoticon_black_24dp
822+
}
823+
val drawable = ContextCompat.getDrawable(requireContext(), icon)
824+
binding.fragmentMessageInputView.smileyButton.setImageDrawable(drawable)
825+
}
826+
827+
private fun showKeyboard() {
828+
val editText = binding.fragmentMessageInputView.inputEditText
829+
editText.requestFocus()
830+
val inputMethodManager =
831+
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
832+
inputMethodManager?.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT)
833+
}
834+
835+
private fun hideKeyboard() {
836+
val view = activity?.currentFocus
837+
if (view != null) {
838+
val imm = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
839+
imm?.hideSoftInputFromWindow(view.windowToken, 0)
840+
}
841+
}
842+
843+
private fun initSmileyKeyboardToggler() {
844+
val inputEditText = binding.fragmentMessageInputView.inputEditText
845+
val emojiPicker = binding.emojiPicker
846+
847+
emojiPicker.setRecentEmojiProvider(MessageInputRecentEmojiProvider(requireContext()))
848+
emojiPicker.setOnEmojiPickedListener { item ->
849+
val start = inputEditText.selectionStart
850+
val end = inputEditText.selectionEnd
851+
inputEditText.editableText.replace(start, end, item.emoji)
852+
}
853+
854+
viewThemeUtils.talk.themeEmojiPicker(emojiPicker)
855+
updateSmileyButtonIcon()
856+
857+
binding.fragmentMessageInputView.smileyButton.setOnClickListener {
858+
if (emojiPicker.isVisible) {
859+
showKeyboard()
860+
emojiPicker.isVisible = false
861+
} else {
862+
hideKeyboard()
863+
emojiPicker.isVisible = true
864+
}
865+
updateSmileyButtonIcon()
866+
}
867+
868+
inputEditText.setOnClickListener {
869+
if (emojiPicker.isVisible) {
870+
emojiPicker.isVisible = false
871+
updateSmileyButtonIcon()
872+
}
873+
}
874+
}
875+
876+
private class MessageInputRecentEmojiProvider(context: Context) : RecentEmojiProvider {
877+
private val prefs = context.getSharedPreferences("recent_emojis", Context.MODE_PRIVATE)
878+
879+
override fun recordSelection(emoji: String) {
880+
val updated = listOf(emoji) + getStoredList().filterNot { it == emoji }
881+
prefs.edit()
882+
.putString("recent", updated.take(MAX_STORED_RECENT_EMOJIS).joinToString(","))
883+
.apply()
884+
}
885+
886+
override suspend fun getRecentEmojiList(): List<String> = getStoredList()
887+
888+
private fun getStoredList(): List<String> =
889+
prefs.getString("recent", null)
890+
?.split(",")
891+
?.filter { it.isNotBlank() }
892+
?: emptyList()
893+
894+
companion object {
895+
private const val MAX_STORED_RECENT_EMOJIS = 20
896+
}
897+
}
898+
805899
private fun replyToMessage(quotedMessageText: String?, quotedActorDisplayName: String?, quotedImageUrl: String?) {
806900
Log.d(TAG, "Reply")
807901
val view = binding.fragmentMessageInputView

app/src/main/java/com/nextcloud/talk/ui/MessageInput.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class MessageInput : FrameLayout {
2626
lateinit var slideToCancelDescription: TextView
2727
lateinit var microphoneEnabledInfo: ImageView
2828
lateinit var microphoneEnabledInfoBackground: ImageView
29+
lateinit var smileyButton: ImageButton
2930
lateinit var deleteVoiceRecording: ImageView
3031
lateinit var sendVoiceRecording: ImageView
3132
lateinit var micInputCloud: MicInputCloud
@@ -70,6 +71,7 @@ class MessageInput : FrameLayout {
7071
slideToCancelDescription = findViewById(R.id.slideToCancelDescription)
7172
microphoneEnabledInfo = findViewById(R.id.microphoneEnabledInfo)
7273
microphoneEnabledInfoBackground = findViewById(R.id.microphoneEnabledInfoBackground)
74+
smileyButton = findViewById(R.id.smileyButton)
7375
deleteVoiceRecording = findViewById(R.id.deleteVoiceRecording)
7476
sendVoiceRecording = findViewById(R.id.sendVoiceRecording)
7577
micInputCloud = findViewById(R.id.micInputCloud)

app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import androidx.core.content.res.ResourcesCompat
3535
import androidx.core.graphics.ColorUtils
3636
import androidx.core.graphics.drawable.DrawableCompat
3737
import androidx.core.view.ViewCompat
38+
import androidx.emoji2.emojipicker.EmojiPickerView
3839
import com.google.android.material.button.MaterialButton
3940
import com.google.android.material.card.MaterialCardView
4041
import com.google.android.material.chip.Chip
@@ -492,6 +493,18 @@ class TalkSpecificViewThemeUtils @Inject constructor(
492493
}
493494
}
494495

496+
fun themeEmojiPicker(emojiPickerView: EmojiPickerView) {
497+
withScheme(emojiPickerView.context) { scheme ->
498+
emojiPickerView.setBackgroundColor(dynamicColor.surfaceContainerLow().getArgb(scheme))
499+
themeEmojiPickerCategoryTabs(
500+
emojiPickerView,
501+
dynamicColor.primary().getArgb(scheme),
502+
dynamicColor.onSurfaceVariant().getArgb(scheme)
503+
)
504+
protectEmojiPickerScrollGesture(emojiPickerView)
505+
}
506+
}
507+
495508
companion object {
496509
private val THEMEABLE_PLACEHOLDER_IDS = listOf(
497510
R.drawable.ic_mimetype_package_x_generic,

app/src/main/res/layout/fragment_message_input.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@
8181
android:textColor="@color/medium_emphasis_text"
8282
android:visibility="gone"
8383
tools:visibility="visible" />
84+
85+
<androidx.emoji2.emojipicker.EmojiPickerView
86+
android:id="@+id/emoji_picker"
87+
android:layout_width="match_parent"
88+
android:layout_height="250dp"
89+
android:visibility="gone" />
8490
</LinearLayout>
8591
</com.google.android.material.card.MaterialCardView>
8692
</LinearLayout>

app/src/main/res/layout/view_message_input.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,25 @@
4444
android:src="@drawable/ic_baseline_attach_file_24"
4545
app:tint="?attr/colorControlNormal" />
4646

47+
<ImageButton
48+
android:id="@+id/smileyButton"
49+
android:layout_width="@dimen/min_size_clickable_area"
50+
android:layout_height="@dimen/min_size_clickable_area"
51+
android:layout_alignBottom="@id/messageInput"
52+
android:layout_toEndOf="@id/attachmentButton"
53+
android:background="@color/transparent"
54+
android:contentDescription="@string/nc_add_emojis"
55+
android:gravity="bottom"
56+
android:src="@drawable/ic_insert_emoticon_black_24dp"
57+
app:tint="?attr/colorControlNormal" />
58+
4759
<com.nextcloud.talk.utils.ImageEmojiEditText
4860
android:id="@+id/messageInput"
4961
android:layout_width="match_parent"
5062
android:layout_height="wrap_content"
5163
android:layout_centerHorizontal="true"
5264
android:layout_marginEnd="48dp"
53-
android:layout_toEndOf="@id/attachmentButton"
65+
android:layout_toEndOf="@id/smileyButton"
5466
android:background="@null"
5567
android:imeOptions="actionDone"
5668
android:inputType="textAutoCorrect|textMultiLine|textCapSentences"

0 commit comments

Comments
 (0)