Added emoji picker in chat
This commit is contained in:
parent
b1b426389d
commit
22d4a8baf1
13 changed files with 135 additions and 26 deletions
|
@ -16,6 +16,7 @@ Group changes to describe their impact on the project, as follows:
|
|||
- Showing short term presence for contacts whom publish it + added setting to disable it (enabled by default for sip.linphone.org accounts)
|
||||
- Confirmation dialog before removing account
|
||||
- Attended transfer instead of blind transfer if there is more than 1 call
|
||||
- Added emoji picker in chat room, and increase size of text if it only contains emojis
|
||||
- Added hidden setting to disable video completely
|
||||
- Added hidden setting to prevent adding / editing / removing native contacts
|
||||
- Added hidden setting to protect settings access using account password
|
||||
|
|
|
@ -194,14 +194,14 @@ android {
|
|||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation 'androidx.core:core-ktx:1.10.0'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
||||
implementation 'androidx.emoji2:emoji2:1.3.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.0'
|
||||
implementation 'androidx.emoji2:emoji2:1.4.0-beta01'
|
||||
implementation 'androidx.emoji2:emoji2-emojipicker:1.4.0-beta01'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
|
||||
implementation 'androidx.media:media:1.6.0'
|
||||
implementation "androidx.security:security-crypto-ktx:1.1.0-alpha05"
|
||||
implementation "androidx.window:window:1.0.0"
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
|
||||
def nav_version = "2.5.3"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
||||
|
@ -219,7 +219,7 @@ dependencies {
|
|||
implementation 'com.google.android.flexbox:flexbox:3.0.0'
|
||||
|
||||
// https://github.com/coil-kt/coil/blob/main/LICENSE.txt Apache v2.0
|
||||
def coil_version = "2.2.2"
|
||||
def coil_version = "2.3.0"
|
||||
implementation("io.coil-kt:coil:$coil_version")
|
||||
implementation("io.coil-kt:coil-gif:$coil_version")
|
||||
implementation("io.coil-kt:coil-svg:$coil_version")
|
||||
|
|
|
@ -56,6 +56,8 @@ class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMes
|
|||
|
||||
val text = MutableLiveData<Spannable>()
|
||||
|
||||
val isTextEmoji = MutableLiveData<Boolean>()
|
||||
|
||||
val replyData = MutableLiveData<ChatMessageData>()
|
||||
|
||||
val isDisplayed = MutableLiveData<Boolean>()
|
||||
|
@ -188,7 +190,8 @@ class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMes
|
|||
data.listener = contentListener
|
||||
list.add(data)
|
||||
} else if (content.isText) {
|
||||
val spannable = Spannable.Factory.getInstance().newSpannable(content.utf8Text?.trim())
|
||||
val textContent = content.utf8Text.orEmpty().trim()
|
||||
val spannable = Spannable.Factory.getInstance().newSpannable(textContent)
|
||||
text.value = PatternClickableSpan()
|
||||
.add(
|
||||
Pattern.compile("(?:<?sips?:)?[a-zA-Z0-9+_.\\-]+(?:@([a-zA-Z0-9+_.\\-;=~]+))+(>)?"),
|
||||
|
@ -217,6 +220,7 @@ class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMes
|
|||
}
|
||||
}
|
||||
).build(spannable)
|
||||
isTextEmoji.value = AppUtils.isTextOnlyContainingEmoji(textContent)
|
||||
} else {
|
||||
Log.e("[Chat Message Data] Unexpected content with type: ${content.type}/${content.subtype}")
|
||||
}
|
||||
|
|
|
@ -103,6 +103,10 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
EditorInfo.IME_FLAG_NO_EXTRACT_UI
|
||||
}
|
||||
|
||||
val isEmojiPickerOpen = MutableLiveData<Boolean>()
|
||||
|
||||
val isEmojiPickerVisible = MutableLiveData<Boolean>()
|
||||
|
||||
private lateinit var recorder: Recorder
|
||||
|
||||
private var voiceRecordAudioFocusRequest: AudioFocusRequestCompat? = null
|
||||
|
@ -136,6 +140,8 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
|
||||
attachFileEnabled.value = true
|
||||
sendMessageEnabled.value = false
|
||||
isEmojiPickerOpen.value = false
|
||||
isEmojiPickerVisible.value = corePreferences.showEmojiPickerButton
|
||||
updateChatRoomReadOnlyState()
|
||||
}
|
||||
|
||||
|
@ -208,6 +214,10 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
}
|
||||
}
|
||||
|
||||
fun toggleEmojiPicker() {
|
||||
isEmojiPickerOpen.value = isEmojiPickerOpen.value == false
|
||||
}
|
||||
|
||||
fun sendMessage() {
|
||||
if (!isPlayerClosed()) {
|
||||
stopVoiceRecordPlayer()
|
||||
|
|
|
@ -498,6 +498,9 @@ class CorePreferences constructor(private val context: Context) {
|
|||
val allowEndToEndEncryptedChatWithoutPresence: Boolean
|
||||
get() = config.getBool("app", "allow_lime_friend_without_capability", false)
|
||||
|
||||
val showEmojiPickerButton: Boolean
|
||||
get() = config.getBool("app", "show_emoji_picker", true)
|
||||
|
||||
// This will prevent UI from showing up, except for the launcher & the foreground service notification
|
||||
val preventInterfaceFromShowingUp: Boolean
|
||||
get() = config.getBool("app", "keep_app_invisible", false)
|
||||
|
|
|
@ -45,6 +45,18 @@ import org.linphone.core.tools.Log
|
|||
*/
|
||||
class AppUtils {
|
||||
companion object {
|
||||
var emojiCompat: EmojiCompat? = null
|
||||
get() = initEmojiCompat()
|
||||
|
||||
private fun initEmojiCompat(): EmojiCompat? {
|
||||
return try {
|
||||
EmojiCompat.get()
|
||||
} catch (ise: IllegalStateException) {
|
||||
Log.e("[App Utils] Can't get EmojiCompat: $ise")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun getString(id: Int): String {
|
||||
return coreContext.context.getString(id)
|
||||
}
|
||||
|
@ -68,16 +80,10 @@ class AppUtils {
|
|||
var initials = ""
|
||||
var characters = 0
|
||||
|
||||
val emoji = try {
|
||||
EmojiCompat.get()
|
||||
} catch (ise: IllegalStateException) {
|
||||
Log.e("[App Utils] Can't get EmojiCompat: $ise")
|
||||
null
|
||||
}
|
||||
|
||||
for (i in split.indices) {
|
||||
if (split[i].isNotEmpty()) {
|
||||
try {
|
||||
val emoji = emojiCompat
|
||||
if (emoji?.hasEmojiGlyph(split[i]) == true) {
|
||||
val glyph = emoji.process(split[i])
|
||||
if (characters > 0) { // Limit initial to 1 emoji only
|
||||
|
@ -100,6 +106,19 @@ class AppUtils {
|
|||
return initials
|
||||
}
|
||||
|
||||
fun isTextOnlyContainingEmoji(text: String): Boolean {
|
||||
val emoji = emojiCompat
|
||||
emoji ?: return false
|
||||
|
||||
for (split in text.split(" ")) {
|
||||
// We only check the first and last chars of the split for commodity
|
||||
if (emoji.getEmojiStart(split, 0) == -1 || emoji.getEmojiEnd(split, split.length - 1) == -1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun pixelsToDp(pixels: Float): Float {
|
||||
return TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
|
|
|
@ -34,6 +34,8 @@ import androidx.appcompat.content.res.AppCompatResources
|
|||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.constraintlayout.widget.Guideline
|
||||
import androidx.databinding.*
|
||||
import androidx.emoji2.emojipicker.EmojiPickerView
|
||||
import androidx.emoji2.emojipicker.EmojiViewItem
|
||||
import coil.dispose
|
||||
import coil.load
|
||||
import coil.request.CachePolicy
|
||||
|
@ -792,3 +794,14 @@ fun ImageView.setPresenceIcon(presence: ConsolidatedPresence) {
|
|||
}
|
||||
setContentDescription(contentDescription)
|
||||
}
|
||||
|
||||
interface EmojiPickedListener {
|
||||
fun onEmojiPicked(item: EmojiViewItem)
|
||||
}
|
||||
|
||||
@BindingAdapter("emojiPickedListener")
|
||||
fun EmojiPickerView.setEmojiPickedListener(listener: EmojiPickedListener) {
|
||||
setOnEmojiPickedListener { emoji ->
|
||||
listener.onEmojiPicked(emoji)
|
||||
}
|
||||
}
|
||||
|
|
BIN
app/src/main/res/drawable-xhdpi/emoji_default.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/emoji_default.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
|
@ -4,6 +4,10 @@
|
|||
<bitmap android:src="@drawable/edit_default"
|
||||
android:tint="?attr/drawableTintOverColor"/>
|
||||
</item>
|
||||
<item android:state_selected="true">
|
||||
<bitmap android:src="@drawable/edit_default"
|
||||
android:tint="?attr/drawableTintOverColor"/>
|
||||
</item>
|
||||
<item android:state_enabled="false">
|
||||
<bitmap android:src="@drawable/edit_default"
|
||||
android:tint="?attr/drawableTintDisabledColor"/>
|
||||
|
|
19
app/src/main/res/drawable/emoji.xml
Normal file
19
app/src/main/res/drawable/emoji.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<bitmap android:src="@drawable/emoji_default"
|
||||
android:tint="?attr/drawableTintOverColor"/>
|
||||
</item>
|
||||
<item android:state_selected="true">
|
||||
<bitmap android:src="@drawable/emoji_default"
|
||||
android:tint="?attr/drawableTintOverColor"/>
|
||||
</item>
|
||||
<item android:state_enabled="false">
|
||||
<bitmap android:src="@drawable/emoji_default"
|
||||
android:tint="?attr/drawableTintDisabledColor"/>
|
||||
</item>
|
||||
<item>
|
||||
<bitmap android:src="@drawable/emoji_default"
|
||||
android:tint="?attr/drawableTintColor"/>
|
||||
</item>
|
||||
</selector>
|
|
@ -173,7 +173,7 @@
|
|||
android:onLongClick="@{contextMenuClickListener}"
|
||||
android:text="@{data.text}"
|
||||
android:textColor="@color/dark_grey_color"
|
||||
android:textSize="15sp"
|
||||
android:textSize="@{data.isTextEmoji ? @dimen/chat_message_emoji_font_size : @dimen/chat_message_text_font_size, default=@dimen/chat_message_text_font_size}"
|
||||
android:textStyle="normal"
|
||||
android:visibility="@{data.text.length > 0 ? View.VISIBLE : View.GONE}" />
|
||||
|
||||
|
|
|
@ -110,10 +110,20 @@
|
|||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<androidx.emoji2.emojipicker.EmojiPickerView
|
||||
android:id="@+id/emoji_picker"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/chat_room_emoji_picker_height"
|
||||
android:visibility="@{chatSendingViewModel.isEmojiPickerOpen ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:emojiPickedListener="@{(emoji) -> message.getText().insert(message.getSelectionStart(), emoji.emoji)}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/attached_files" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/attach_file"
|
||||
android:layout_width="@dimen/chat_message_sending_icons_size"
|
||||
android:layout_height="@dimen/chat_message_sending_icons_size"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="@dimen/chat_message_sending_icons_margin"
|
||||
android:layout_marginEnd="@dimen/chat_message_sending_icons_margin"
|
||||
android:contentDescription="@string/content_description_attach_file"
|
||||
|
@ -129,7 +139,7 @@
|
|||
<ImageView
|
||||
android:id="@+id/voice_record"
|
||||
android:layout_width="@dimen/chat_message_sending_icons_size"
|
||||
android:layout_height="@dimen/chat_message_sending_icons_size"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="@dimen/chat_message_sending_icons_margin"
|
||||
android:layout_marginEnd="@dimen/chat_message_sending_icons_margin"
|
||||
android:contentDescription="@string/content_description_voice_recording"
|
||||
|
@ -147,13 +157,12 @@
|
|||
android:id="@+id/message"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/attached_files"
|
||||
android:layout_below="@id/emoji_picker"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/chat_message_sending_icons_margin"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="@dimen/chat_message_sending_icons_margin"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@drawable/resizable_text_field"
|
||||
android:background="@color/header_background_color"
|
||||
android:hint="@{chatSendingViewModel.isPendingAnswer ? @string/chat_room_sending_reply_hint : @string/chat_room_sending_message_hint}"
|
||||
android:imeOptions="@{chatSendingViewModel.imeFlags}"
|
||||
android:inputType="textShortMessage|textMultiLine|textAutoComplete|textAutoCorrect|textCapSentences"
|
||||
|
@ -163,14 +172,38 @@
|
|||
android:textColor="@color/black_color"
|
||||
android:textCursorDrawable="@null"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/send_message"
|
||||
app:layout_constraintEnd_toStartOf="@id/message_right_barrier"
|
||||
app:layout_constraintStart_toEndOf="@id/voice_record"
|
||||
app:layout_constraintTop_toBottomOf="@id/attached_files" />
|
||||
app:layout_constraintTop_toBottomOf="@id/emoji_picker" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/message_right_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="left"
|
||||
app:constraint_referenced_ids="send_message, emoji_picker_toggle"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/emoji_picker_toggle"
|
||||
android:layout_width="@dimen/chat_message_sending_icons_size"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="@dimen/chat_message_sending_icons_margin"
|
||||
android:onClick="@{() -> chatSendingViewModel.toggleEmojiPicker()}"
|
||||
android:paddingTop="@dimen/chat_message_sending_icons_margin"
|
||||
android:paddingBottom="@dimen/chat_message_sending_icons_margin"
|
||||
android:layout_marginEnd="@dimen/chat_message_sending_icons_margin"
|
||||
android:src="@drawable/emoji"
|
||||
android:background="@color/header_background_color"
|
||||
android:selected="@{chatSendingViewModel.isEmojiPickerOpen}"
|
||||
android:visibility="@{chatSendingViewModel.isEmojiPickerVisible ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintBottom_toBottomOf="@id/message"
|
||||
app:layout_constraintEnd_toStartOf="@id/send_message"
|
||||
app:layout_constraintTop_toTopOf="@id/message" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/send_message"
|
||||
android:layout_width="@dimen/chat_message_sending_icons_size"
|
||||
android:layout_height="@dimen/chat_message_sending_icons_size"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="@dimen/chat_message_sending_icons_margin"
|
||||
android:layout_marginEnd="@dimen/chat_message_sending_icons_margin"
|
||||
android:contentDescription="@string/content_description_send_message"
|
||||
|
@ -205,10 +238,10 @@
|
|||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:visibility="@{chatSendingViewModel.attachingFileInProgress ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintBottom_toBottomOf="@id/attached_files"
|
||||
app:layout_constraintBottom_toBottomOf="@id/emoji_picker"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/attached_files" />
|
||||
app:layout_constraintTop_toTopOf="@id/emoji_picker" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/attaching_files_in_progress"
|
||||
|
@ -221,7 +254,7 @@
|
|||
app:layout_constraintBottom_toTopOf="@id/attaching_files_in_progress_label"
|
||||
app:layout_constraintEnd_toEndOf="@id/attaching_files_in_progress_background"
|
||||
app:layout_constraintStart_toStartOf="@id/attaching_files_in_progress_background"
|
||||
app:layout_constraintTop_toBottomOf="@id/attached_files" />
|
||||
app:layout_constraintTop_toBottomOf="@id/emoji_picker" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attaching_files_in_progress_label"
|
||||
|
@ -232,7 +265,7 @@
|
|||
android:textColor="@color/primary_color"
|
||||
android:textStyle="bold"
|
||||
android:visibility="@{chatSendingViewModel.attachingFileInProgress ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintBottom_toTopOf="@id/attached_files"
|
||||
app:layout_constraintBottom_toTopOf="@id/emoji_picker"
|
||||
app:layout_constraintEnd_toEndOf="@id/attaching_files_in_progress_background"
|
||||
app:layout_constraintStart_toStartOf="@id/attaching_files_in_progress_background"
|
||||
app:layout_constraintTop_toBottomOf="@id/attaching_files_in_progress" />
|
||||
|
|
|
@ -86,4 +86,7 @@
|
|||
<dimen name="contact_presence_big_badge_size">25dp</dimen>
|
||||
<dimen name="contact_presence_badge_padding">1dp</dimen>
|
||||
<dimen name="contact_presence_big_badge_padding">2dp</dimen>
|
||||
<dimen name="chat_room_emoji_picker_height">290dp</dimen>
|
||||
<dimen name="chat_message_text_font_size">15sp</dimen>
|
||||
<dimen name="chat_message_emoji_font_size">45sp</dimen>
|
||||
</resources>
|
Loading…
Reference in a new issue