diff --git a/CHANGELOG.md b/CHANGELOG.md index ea345c667..e4709b815 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/app/build.gradle b/app/build.gradle index 61fc0d523..7f076c908 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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") diff --git a/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageData.kt b/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageData.kt index 58cfdb409..b0b7c6433 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageData.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageData.kt @@ -56,6 +56,8 @@ class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMes val text = MutableLiveData() + val isTextEmoji = MutableLiveData() + val replyData = MutableLiveData() val isDisplayed = MutableLiveData() @@ -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("(?:)?"), @@ -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}") } diff --git a/app/src/main/java/org/linphone/activities/main/chat/viewmodels/ChatMessageSendingViewModel.kt b/app/src/main/java/org/linphone/activities/main/chat/viewmodels/ChatMessageSendingViewModel.kt index f2f188796..403ee5b5c 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/viewmodels/ChatMessageSendingViewModel.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/viewmodels/ChatMessageSendingViewModel.kt @@ -103,6 +103,10 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel() EditorInfo.IME_FLAG_NO_EXTRACT_UI } + val isEmojiPickerOpen = MutableLiveData() + + val isEmojiPickerVisible = MutableLiveData() + 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() diff --git a/app/src/main/java/org/linphone/core/CorePreferences.kt b/app/src/main/java/org/linphone/core/CorePreferences.kt index 28de260fb..8d5caea8e 100644 --- a/app/src/main/java/org/linphone/core/CorePreferences.kt +++ b/app/src/main/java/org/linphone/core/CorePreferences.kt @@ -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) diff --git a/app/src/main/java/org/linphone/utils/AppUtils.kt b/app/src/main/java/org/linphone/utils/AppUtils.kt index 81a538dfa..a8ae1a91c 100644 --- a/app/src/main/java/org/linphone/utils/AppUtils.kt +++ b/app/src/main/java/org/linphone/utils/AppUtils.kt @@ -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, diff --git a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt index 57f3d0ed3..94e7454ba 100644 --- a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt +++ b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt @@ -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) + } +} diff --git a/app/src/main/res/drawable-xhdpi/emoji_default.png b/app/src/main/res/drawable-xhdpi/emoji_default.png new file mode 100644 index 000000000..563340737 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/emoji_default.png differ diff --git a/app/src/main/res/drawable/edit.xml b/app/src/main/res/drawable/edit.xml index f692e5846..180eea212 100644 --- a/app/src/main/res/drawable/edit.xml +++ b/app/src/main/res/drawable/edit.xml @@ -4,6 +4,10 @@ + + + diff --git a/app/src/main/res/drawable/emoji.xml b/app/src/main/res/drawable/emoji.xml new file mode 100644 index 000000000..24c7d6d8d --- /dev/null +++ b/app/src/main/res/drawable/emoji.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/chat_message_list_cell.xml b/app/src/main/res/layout/chat_message_list_cell.xml index d451c3976..71b7d4b89 100644 --- a/app/src/main/res/layout/chat_message_list_cell.xml +++ b/app/src/main/res/layout/chat_message_list_cell.xml @@ -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}" /> diff --git a/app/src/main/res/layout/chat_room_sending.xml b/app/src/main/res/layout/chat_room_sending.xml index d25972d4e..efc28de06 100644 --- a/app/src/main/res/layout/chat_room_sending.xml +++ b/app/src/main/res/layout/chat_room_sending.xml @@ -110,10 +110,20 @@ + + + app:layout_constraintTop_toBottomOf="@id/emoji_picker" /> + + + + + app:layout_constraintTop_toTopOf="@id/emoji_picker" /> + app:layout_constraintTop_toBottomOf="@id/emoji_picker" /> diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 79054ac01..0f0e3918b 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -86,4 +86,7 @@ 25dp 1dp 2dp + 290dp + 15sp + 45sp \ No newline at end of file