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.Parcelable
import android.provider.MediaStore
import android.view.MenuInflater
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.view.menu.MenuPopupHelper
import android.view.*
import android.widget.PopupWindow
import androidx.core.content.FileProvider
import androidx.core.view.doOnPreDraw
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
@ -62,6 +59,7 @@ import org.linphone.compatibility.Compatibility
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatRoomDetailFragmentBinding
import org.linphone.databinding.ChatRoomMenuBindingImpl
import org.linphone.utils.*
import org.linphone.utils.Event
@ -620,53 +618,71 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
}
private fun showPopupMenu(chatRoom: ChatRoom) {
val builder = MenuBuilder(requireContext())
val popupMenu = MenuPopupHelper(requireContext(), builder, binding.menu)
popupMenu.setForceShowIcon(true)
val popupView: ChatRoomMenuBindingImpl = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.chat_room_menu, null, false
)
MenuInflater(requireContext()).inflate(R.menu.chat_room_menu, builder)
if (viewModel.oneToOneChatRoom) {
builder.removeItem(R.id.chat_room_group_info)
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) {
popupView.groupInfoHidden = true
totalSize -= itemSize
}
// 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
if (viewModel.oneParticipantOneDevice) {
builder.removeItem(R.id.chat_room_participants_devices)
popupView.devicesHidden = true
totalSize -= itemSize
}
}
if (!viewModel.encryptedChatRoom) {
builder.removeItem(R.id.chat_room_participants_devices)
builder.removeItem(R.id.chat_room_ephemeral_messages)
}
builder.setCallback(object : MenuBuilder.Callback {
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)
true
}
R.id.chat_room_participants_devices -> {
showParticipantsDevices()
true
}
R.id.chat_room_ephemeral_messages -> {
showEphemeralMessages()
true
}
R.id.chat_room_delete_messages -> {
enterEditionMode()
true
}
else -> false
if (viewModel.ephemeralChatRoom) {
if (chatRoom.currentParams.ephemeralMode == ChatRoomEphemeralMode.AdminManaged &&
viewModel.meAdmin.value == false
) {
popupView.ephemeralHidden = true
totalSize -= itemSize
}
}
})
}
popupMenu.show()
// When using WRAP_CONTENT instead of real size, fails to place the
// popup window above if not enough space is available below
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
popupView.setGroupInfoListener {
showGroupInfo(chatRoom)
popupWindow.dismiss()
}
popupView.setDevicesListener {
showParticipantsDevices()
popupWindow.dismiss()
}
popupView.setEphemeralListener {
showEphemeralMessages()
popupWindow.dismiss()
}
popupView.setEditionModeListener {
enterEditionMode()
popupWindow.dismiss()
}
popupWindow.showAsDropDown(binding.menu, 0, 0, Gravity.BOTTOM)
}
private fun scrollToBottom() {

View file

@ -21,6 +21,7 @@ package org.linphone.activities.main.chat.viewmodels
import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.main.viewmodels.ErrorReportingViewModel
import org.linphone.contact.ContactsUpdatedListenerStub
@ -163,6 +164,11 @@ class ChatRoomCreationViewModel : ErrorReportingViewModel() {
if (encrypted) {
params.enableEncryption(true)
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)
}

View file

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

View file

@ -23,6 +23,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.main.chat.GroupChatRoomMember
import org.linphone.activities.main.chat.data.GroupInfoParticipantData
@ -117,6 +118,13 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : ErrorReportingViewModel() {
val params: ChatRoomParams = coreContext.core.createDefaultChatRoomParams()
params.enableEncryption(isEncrypted.value == 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
val addresses = arrayOfNulls<Address>(participants.value.orEmpty().size)

View file

@ -417,6 +417,10 @@ class CorePreferences constructor(private val context: Context) {
val voiceMessagesFormatMkv: Boolean
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 */
val echoCancellerCalibration: Int

View file

@ -627,7 +627,7 @@ class NotificationsManager(private val context: Context) {
val notifiableMessage = getNotifiableMessage(message, contact)
notifiable.messages.add(notifiableMessage)
} else {
for (chatMessage in room.getUnreadHistory()) {
for (chatMessage in room.unreadHistory) {
val notifiableMessage = getNotifiableMessage(chatMessage, contact)
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
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:contentDescription="@string/content_description_show_chat_room_menu"
android:layout_width="0dp"
@ -122,7 +122,7 @@
<ImageView
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:layout_width="0dp"
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="call_overlay_size">60dp</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_room_popup_item_height">50dp</dimen>
<dimen name="chat_message_round_corner_radius">6.7dp</dimen>
<dimen name="play_pause_button_size">30dp</dimen>
<dimen name="progress_bar_height">40dp</dimen>