Reworked chat room menu + prepared ephemeral session mode + fixed one-to-one not encrypted chat room menu visible

This commit is contained in:
Sylvain Berfini 2021-09-06 12:40:00 +02:00
parent 7f6078737c
commit 3a8b892ee1
13 changed files with 173 additions and 72 deletions

View file

@ -26,14 +26,11 @@ import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.provider.MediaStore import android.provider.MediaStore
import android.view.MenuInflater import android.view.*
import android.view.MenuItem import android.widget.PopupWindow
import android.view.MotionEvent
import android.view.View
import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.view.menu.MenuPopupHelper
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -62,6 +59,7 @@ import org.linphone.compatibility.Compatibility
import org.linphone.core.* import org.linphone.core.*
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.databinding.ChatRoomDetailFragmentBinding import org.linphone.databinding.ChatRoomDetailFragmentBinding
import org.linphone.databinding.ChatRoomMenuBindingImpl
import org.linphone.utils.* import org.linphone.utils.*
import org.linphone.utils.Event import org.linphone.utils.Event
@ -620,53 +618,71 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
} }
private fun showPopupMenu(chatRoom: ChatRoom) { private fun showPopupMenu(chatRoom: ChatRoom) {
val builder = MenuBuilder(requireContext()) val popupView: ChatRoomMenuBindingImpl = DataBindingUtil.inflate(
val popupMenu = MenuPopupHelper(requireContext(), builder, binding.menu) LayoutInflater.from(context),
popupMenu.setForceShowIcon(true) R.layout.chat_room_menu, null, false
)
MenuInflater(requireContext()).inflate(R.menu.chat_room_menu, builder) val itemSize = AppUtils.getDimension(R.dimen.chat_room_popup_item_height).toInt()
var totalSize = itemSize * 4
if (!viewModel.encryptedChatRoom) {
popupView.devicesHidden = true
totalSize -= itemSize
popupView.ephemeralHidden = true
totalSize -= itemSize
} else {
if (viewModel.oneToOneChatRoom) { if (viewModel.oneToOneChatRoom) {
builder.removeItem(R.id.chat_room_group_info) popupView.groupInfoHidden = true
totalSize -= itemSize
}
// If one participant one device, a click on security badge // If one participant one device, a click on security badge
// will directly start a call or show the dialog, so don't show this menu // will directly start a call or show the dialog, so don't show this menu
if (viewModel.oneParticipantOneDevice) { if (viewModel.oneParticipantOneDevice) {
builder.removeItem(R.id.chat_room_participants_devices) popupView.devicesHidden = true
totalSize -= itemSize
}
if (viewModel.ephemeralChatRoom) {
if (chatRoom.currentParams.ephemeralMode == ChatRoomEphemeralMode.AdminManaged &&
viewModel.meAdmin.value == false
) {
popupView.ephemeralHidden = true
totalSize -= itemSize
}
} }
} }
if (!viewModel.encryptedChatRoom) { // When using WRAP_CONTENT instead of real size, fails to place the
builder.removeItem(R.id.chat_room_participants_devices) // popup window above if not enough space is available below
builder.removeItem(R.id.chat_room_ephemeral_messages) val popupWindow = PopupWindow(
} popupView.root,
AppUtils.getDimension(R.dimen.chat_room_popup_width).toInt(),
totalSize,
true
)
// Elevation is for showing a shadow around the popup
popupWindow.elevation = 20f
builder.setCallback(object : MenuBuilder.Callback { popupView.setGroupInfoListener {
override fun onMenuModeChange(menu: MenuBuilder) {}
override fun onMenuItemSelected(menu: MenuBuilder, item: MenuItem): Boolean {
return when (item.itemId) {
R.id.chat_room_group_info -> {
showGroupInfo(chatRoom) showGroupInfo(chatRoom)
true popupWindow.dismiss()
} }
R.id.chat_room_participants_devices -> { popupView.setDevicesListener {
showParticipantsDevices() showParticipantsDevices()
true popupWindow.dismiss()
} }
R.id.chat_room_ephemeral_messages -> { popupView.setEphemeralListener {
showEphemeralMessages() showEphemeralMessages()
true popupWindow.dismiss()
} }
R.id.chat_room_delete_messages -> { popupView.setEditionModeListener {
enterEditionMode() enterEditionMode()
true popupWindow.dismiss()
} }
else -> false
}
}
})
popupMenu.show() popupWindow.showAsDropDown(binding.menu, 0, 0, Gravity.BOTTOM)
} }
private fun scrollToBottom() { private fun scrollToBottom() {

View file

@ -21,6 +21,7 @@ package org.linphone.activities.main.chat.viewmodels
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R import org.linphone.R
import org.linphone.activities.main.viewmodels.ErrorReportingViewModel import org.linphone.activities.main.viewmodels.ErrorReportingViewModel
import org.linphone.contact.ContactsUpdatedListenerStub import org.linphone.contact.ContactsUpdatedListenerStub
@ -163,6 +164,11 @@ class ChatRoomCreationViewModel : ErrorReportingViewModel() {
if (encrypted) { if (encrypted) {
params.enableEncryption(true) params.enableEncryption(true)
params.backend = ChatRoomBackend.FlexisipChat params.backend = ChatRoomBackend.FlexisipChat
params.ephemeralMode = if (corePreferences.useEphemeralPerDeviceMode)
ChatRoomEphemeralMode.DeviceManaged
else
ChatRoomEphemeralMode.AdminManaged
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
params.subject = AppUtils.getString(R.string.chat_room_dummy_subject) params.subject = AppUtils.getString(R.string.chat_room_dummy_subject)
} }

View file

@ -75,7 +75,11 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
val encryptedChatRoom: Boolean = chatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt()) val encryptedChatRoom: Boolean = chatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())
val basicChatRoom: Boolean = chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt()) val ephemeralChatRoom: Boolean = chatRoom.hasCapability(ChatRoomCapabilities.Ephemeral.toInt())
val meAdmin: MutableLiveData<Boolean> by lazy {
MutableLiveData<Boolean>()
}
var oneParticipantOneDevice: Boolean = false var oneParticipantOneDevice: Boolean = false
@ -174,6 +178,10 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
Log.i("[Chat Room] Ephemeral message deleted, updated last message displayed") Log.i("[Chat Room] Ephemeral message deleted, updated last message displayed")
lastMessageText.value = formatLastMessage(chatRoom.lastMessageInHistory) lastMessageText.value = formatLastMessage(chatRoom.lastMessageInHistory)
} }
override fun onParticipantAdminStatusChanged(chatRoom: ChatRoom, eventLog: EventLog) {
meAdmin.value = chatRoom.me?.isAdmin ?: false
}
} }
init { init {
@ -187,6 +195,7 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
subject.value = chatRoom.subject subject.value = chatRoom.subject
updateSecurityIcon() updateSecurityIcon()
meAdmin.value = chatRoom.me?.isAdmin ?: false
contactLookup() contactLookup()
updateParticipants() updateParticipants()
@ -206,6 +215,10 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
chatRoom.core.removeListener(coreListener) chatRoom.core.removeListener(coreListener)
} }
fun hideMenu(): Boolean {
return chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt()) || (oneToOneChatRoom && !encryptedChatRoom)
}
fun contactLookup() { fun contactLookup() {
displayName.value = when { displayName.value = when {
chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt()) -> LinphoneUtils.getDisplayName( chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt()) -> LinphoneUtils.getDisplayName(
@ -304,7 +317,7 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
} }
private fun updateParticipants() { private fun updateParticipants() {
peerSipUri.value = if (oneToOneChatRoom && !basicChatRoom) peerSipUri.value = if (oneToOneChatRoom && !chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt()))
chatRoom.participants.firstOrNull()?.address?.asStringUriOnly() chatRoom.participants.firstOrNull()?.address?.asStringUriOnly()
?: chatRoom.peerAddress.asStringUriOnly() ?: chatRoom.peerAddress.asStringUriOnly()
else chatRoom.peerAddress.asStringUriOnly() else chatRoom.peerAddress.asStringUriOnly()

View file

@ -23,6 +23,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R import org.linphone.R
import org.linphone.activities.main.chat.GroupChatRoomMember import org.linphone.activities.main.chat.GroupChatRoomMember
import org.linphone.activities.main.chat.data.GroupInfoParticipantData import org.linphone.activities.main.chat.data.GroupInfoParticipantData
@ -117,6 +118,13 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : ErrorReportingViewModel() {
val params: ChatRoomParams = coreContext.core.createDefaultChatRoomParams() val params: ChatRoomParams = coreContext.core.createDefaultChatRoomParams()
params.enableEncryption(isEncrypted.value == true) params.enableEncryption(isEncrypted.value == true)
params.enableGroup(true) params.enableGroup(true)
if (isEncrypted.value == true) {
params.ephemeralMode = if (corePreferences.useEphemeralPerDeviceMode)
ChatRoomEphemeralMode.DeviceManaged
else
ChatRoomEphemeralMode.AdminManaged
}
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
params.subject = subject.value params.subject = subject.value
val addresses = arrayOfNulls<Address>(participants.value.orEmpty().size) val addresses = arrayOfNulls<Address>(participants.value.orEmpty().size)

View file

@ -417,6 +417,10 @@ class CorePreferences constructor(private val context: Context) {
val voiceMessagesFormatMkv: Boolean val voiceMessagesFormatMkv: Boolean
get() = config.getBool("app", "record_voice_messages_in_mkv_format", true) get() = config.getBool("app", "record_voice_messages_in_mkv_format", true)
// TODO FIXME: Keeping it device based until we have the API to know if all participants support the new mode
val useEphemeralPerDeviceMode: Boolean
get() = config.getBool("app", "ephemeral_chat_messages_settings_per_device", true)
/* Default values related */ /* Default values related */
val echoCancellerCalibration: Int val echoCancellerCalibration: Int

View file

@ -627,7 +627,7 @@ class NotificationsManager(private val context: Context) {
val notifiableMessage = getNotifiableMessage(message, contact) val notifiableMessage = getNotifiableMessage(message, contact)
notifiable.messages.add(notifiableMessage) notifiable.messages.add(notifiableMessage)
} else { } else {
for (chatMessage in room.getUnreadHistory()) { for (chatMessage in room.unreadHistory) {
val notifiableMessage = getNotifiableMessage(chatMessage, contact) val notifiableMessage = getNotifiableMessage(chatMessage, contact)
notifiable.messages.add(notifiableMessage) notifiable.messages.add(notifiableMessage)
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -110,7 +110,7 @@
<ImageView <ImageView
android:id="@+id/menu" android:id="@+id/menu"
android:visibility="@{viewModel.basicChatRoom || chatSendingViewModel.isReadOnly ? View.GONE : View.VISIBLE}" android:visibility="@{viewModel.hideMenu() || chatSendingViewModel.isReadOnly ? View.GONE : View.VISIBLE}"
android:onClick="@{menuClickListener}" android:onClick="@{menuClickListener}"
android:contentDescription="@string/content_description_show_chat_room_menu" android:contentDescription="@string/content_description_show_chat_room_menu"
android:layout_width="0dp" android:layout_width="0dp"
@ -122,7 +122,7 @@
<ImageView <ImageView
android:onClick="@{editClickListener}" android:onClick="@{editClickListener}"
android:visibility="@{viewModel.basicChatRoom || chatSendingViewModel.isReadOnly ? View.VISIBLE : View.GONE}" android:visibility="@{menu.visibility == View.GONE ? View.VISIBLE : View.GONE}"
android:contentDescription="@string/content_description_enter_edition_mode" android:contentDescription="@string/content_description_enter_edition_mode"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View" />
<variable
name="groupInfoListener"
type="View.OnClickListener" />
<variable
name="devicesListener"
type="View.OnClickListener" />
<variable
name="ephemeralListener"
type="View.OnClickListener" />
<variable
name="editionModeListener"
type="View.OnClickListener" />
<variable
name="groupInfoHidden"
type="Boolean" />
<variable
name="devicesHidden"
type="Boolean" />
<variable
name="ephemeralHidden"
type="Boolean" />
</data>
<LinearLayout
android:layout_width="@dimen/chat_message_popup_width"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="?attr/backgroundColor">
<TextView
android:layout_width="match_parent"
android:layout_height="@dimen/chat_room_popup_item_height"
android:visibility="@{groupInfoHidden ? View.GONE : View.VISIBLE}"
android:background="@drawable/menu_background"
android:onClick="@{groupInfoListener}"
android:drawableRight="@drawable/menu_group_info_default"
style="@style/popup_item_font"
android:text="@string/chat_room_context_menu_group_info" />
<TextView
android:layout_width="match_parent"
android:layout_height="@dimen/chat_room_popup_item_height"
android:visibility="@{devicesHidden ? View.GONE : View.VISIBLE}"
android:background="@drawable/menu_background"
android:onClick="@{devicesListener}"
android:drawableRight="@drawable/menu_security_default"
style="@style/popup_item_font"
android:text="@string/chat_room_context_menu_participants_devices" />
<TextView
android:layout_width="match_parent"
android:layout_height="@dimen/chat_room_popup_item_height"
android:visibility="@{ephemeralHidden ? View.GONE : View.VISIBLE}"
android:background="@drawable/menu_background"
android:onClick="@{ephemeralListener}"
android:drawableRight="@drawable/chat_room_menu_ephemeral"
style="@style/popup_item_font"
android:text="@string/chat_message_context_menu_ephemeral_messages" />
<TextView
android:layout_width="match_parent"
android:layout_height="@dimen/chat_room_popup_item_height"
android:background="@drawable/menu_background"
android:onClick="@{editionModeListener}"
android:drawableRight="@drawable/chat_room_menu_delete"
style="@style/popup_item_font"
android:text="@string/chat_message_context_menu_delete_messages" />
</LinearLayout>
</layout>

View file

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/chat_room_group_info"
android:icon="@drawable/chat_room_menu_group_info"
android:title="@string/chat_room_context_menu_group_info" />
<item
android:id="@+id/chat_room_participants_devices"
android:icon="@drawable/chat_room_menu_security"
android:title="@string/chat_room_context_menu_participants_devices" />
<item
android:id="@+id/chat_room_ephemeral_messages"
android:icon="@drawable/chat_room_menu_ephemeral"
android:title="@string/chat_message_context_menu_ephemeral_messages" />
<item
android:id="@+id/chat_room_delete_messages"
android:icon="@drawable/chat_room_menu_delete"
android:title="@string/chat_message_context_menu_delete_messages" />
</menu>

View file

@ -26,7 +26,9 @@
<dimen name="field_shape_margin">3dp</dimen> <dimen name="field_shape_margin">3dp</dimen>
<dimen name="call_overlay_size">60dp</dimen> <dimen name="call_overlay_size">60dp</dimen>
<dimen name="chat_message_popup_width">250dp</dimen> <dimen name="chat_message_popup_width">250dp</dimen>
<dimen name="chat_room_popup_width">250dp</dimen>
<dimen name="chat_message_popup_item_height">50dp</dimen> <dimen name="chat_message_popup_item_height">50dp</dimen>
<dimen name="chat_room_popup_item_height">50dp</dimen>
<dimen name="chat_message_round_corner_radius">6.7dp</dimen> <dimen name="chat_message_round_corner_radius">6.7dp</dimen>
<dimen name="play_pause_button_size">30dp</dimen> <dimen name="play_pause_button_size">30dp</dimen>
<dimen name="progress_bar_height">40dp</dimen> <dimen name="progress_bar_height">40dp</dimen>