Added chat notifications as bubbles
This commit is contained in:
parent
a757b097fa
commit
b047ebae13
12 changed files with 406 additions and 12 deletions
|
@ -121,6 +121,12 @@
|
|||
android:launchMode="singleTop"
|
||||
android:noHistory="true" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.chat_bubble.ChatBubbleActivity"
|
||||
android:allowEmbedded="true"
|
||||
android:documentLaunchMode="always"
|
||||
android:resizeableActivity="true" />
|
||||
|
||||
<!-- Services -->
|
||||
|
||||
<service
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2021 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.linphone.activities.chat_bubble
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.R
|
||||
import org.linphone.activities.GenericActivity
|
||||
import org.linphone.activities.main.MainActivity
|
||||
import org.linphone.activities.main.chat.adapters.ChatMessagesListAdapter
|
||||
import org.linphone.activities.main.chat.viewmodels.*
|
||||
import org.linphone.activities.main.viewmodels.ListTopBarViewModel
|
||||
import org.linphone.core.ChatRoom
|
||||
import org.linphone.core.Factory
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.databinding.ChatBubbleActivityBinding
|
||||
|
||||
class ChatBubbleActivity : GenericActivity() {
|
||||
private lateinit var binding: ChatBubbleActivityBinding
|
||||
private lateinit var viewModel: ChatRoomViewModel
|
||||
private lateinit var listViewModel: ChatMessagesListViewModel
|
||||
private lateinit var chatSendingViewModel: ChatMessageSendingViewModel
|
||||
private lateinit var adapter: ChatMessagesListAdapter
|
||||
|
||||
private val observer = object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||
if (positionStart == adapter.itemCount - 1) {
|
||||
adapter.notifyItemChanged(positionStart - 1) // For grouping purposes
|
||||
scrollToBottom()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = DataBindingUtil.setContentView(this, R.layout.chat_bubble_activity)
|
||||
binding.lifecycleOwner = this
|
||||
|
||||
val localSipUri = intent.getStringExtra("LocalSipUri")
|
||||
val remoteSipUri = intent.getStringExtra("RemoteSipUri")
|
||||
var chatRoom: ChatRoom? = null
|
||||
|
||||
if (localSipUri != null && remoteSipUri != null) {
|
||||
Log.i("[Chat Bubble] Found local [$localSipUri] & remote [$remoteSipUri] addresses in arguments")
|
||||
val localAddress = Factory.instance().createAddress(localSipUri)
|
||||
val remoteSipAddress = Factory.instance().createAddress(remoteSipUri)
|
||||
chatRoom = coreContext.core.searchChatRoom(
|
||||
null, localAddress, remoteSipAddress, arrayOfNulls(
|
||||
0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
chatRoom ?: return
|
||||
chatRoom.markAsRead()
|
||||
|
||||
viewModel = ViewModelProvider(
|
||||
this,
|
||||
ChatRoomViewModelFactory(chatRoom)
|
||||
)[ChatRoomViewModel::class.java]
|
||||
binding.viewModel = viewModel
|
||||
|
||||
listViewModel = ViewModelProvider(
|
||||
this,
|
||||
ChatMessagesListViewModelFactory(chatRoom)
|
||||
)[ChatMessagesListViewModel::class.java]
|
||||
|
||||
chatSendingViewModel = ViewModelProvider(
|
||||
this,
|
||||
ChatMessageSendingViewModelFactory(chatRoom)
|
||||
)[ChatMessageSendingViewModel::class.java]
|
||||
binding.chatSendingViewModel = chatSendingViewModel
|
||||
|
||||
val listSelectionViewModel = ViewModelProvider(this).get(ListTopBarViewModel::class.java)
|
||||
adapter = ChatMessagesListAdapter(listSelectionViewModel, this)
|
||||
// SubmitList is done on a background thread
|
||||
// We need this adapter data observer to know when to scroll
|
||||
binding.chatMessagesList.adapter = adapter
|
||||
adapter.registerAdapterDataObserver(observer)
|
||||
|
||||
// Disable context menu on each message
|
||||
adapter.disableContextMenu()
|
||||
|
||||
val layoutManager = LinearLayoutManager(this)
|
||||
layoutManager.stackFromEnd = true
|
||||
binding.chatMessagesList.layoutManager = layoutManager
|
||||
|
||||
listViewModel.events.observe(this, { events ->
|
||||
adapter.submitList(events)
|
||||
})
|
||||
|
||||
chatSendingViewModel.textToSend.observe(this, {
|
||||
chatSendingViewModel.onTextToSendChanged(it)
|
||||
})
|
||||
|
||||
binding.setOpenAppClickListener {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
intent.putExtra("RemoteSipUri", remoteSipUri)
|
||||
intent.putExtra("LocalSipUri", localSipUri)
|
||||
intent.putExtra("Chat", true)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK and Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.setCloseBubbleClickListener {
|
||||
val notificationId = coreContext.notificationsManager.getChatNotificationIdForSipUri(viewModel.chatRoom.peerAddress.asStringUriOnly())
|
||||
coreContext.notificationsManager.cancel(notificationId)
|
||||
}
|
||||
|
||||
binding.setSendMessageClickListener {
|
||||
chatSendingViewModel.sendMessage()
|
||||
binding.message.text?.clear()
|
||||
}
|
||||
}
|
||||
|
||||
private fun scrollToBottom() {
|
||||
if (adapter.itemCount > 0) {
|
||||
binding.chatMessagesList.scrollToPosition(adapter.itemCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -83,6 +83,8 @@ class ChatMessagesListAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
private var contextMenuDisabled: Boolean = false
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
EventLog.Type.ConferenceChatMessage.toInt() -> createChatMessageViewHolder(parent)
|
||||
|
@ -119,6 +121,10 @@ class ChatMessagesListAdapter(
|
|||
return eventLog.type.toInt()
|
||||
}
|
||||
|
||||
fun disableContextMenu() {
|
||||
contextMenuDisabled = true
|
||||
}
|
||||
|
||||
inner class ChatMessageViewHolder(
|
||||
private val binding: ChatMessageListCellBinding
|
||||
) : RecyclerView.ViewHolder(binding.root), PopupMenu.OnMenuItemClickListener {
|
||||
|
@ -176,6 +182,8 @@ class ChatMessagesListAdapter(
|
|||
|
||||
executePendingBindings()
|
||||
|
||||
if (contextMenuDisabled) return
|
||||
|
||||
setContextMenuClickListener {
|
||||
val popup = PopupMenu(root.context, background)
|
||||
popup.setOnMenuItemClickListener(this@ChatMessageViewHolder)
|
||||
|
|
|
@ -72,8 +72,6 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
|||
}
|
||||
}
|
||||
|
||||
private var chatRoomAddress: String? = null
|
||||
|
||||
override fun getLayoutId(): Int {
|
||||
return R.layout.chat_room_detail_fragment
|
||||
}
|
||||
|
@ -121,7 +119,6 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
|||
|
||||
Compatibility.setLocusIdInContentCaptureSession(binding.root, chatRoom)
|
||||
|
||||
chatRoomAddress = chatRoom.peerAddress.asStringUriOnly()
|
||||
isSecure = chatRoom.currentParams.encryptionEnabled()
|
||||
|
||||
viewModel = ViewModelProvider(
|
||||
|
@ -328,7 +325,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
|||
super.onResume()
|
||||
|
||||
// Prevent notifications for this chat room to be displayed
|
||||
coreContext.notificationsManager.currentlyDisplayedChatRoomAddress = chatRoomAddress
|
||||
coreContext.notificationsManager.currentlyDisplayedChatRoomAddress = viewModel.chatRoom.peerAddress.asStringUriOnly()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
package org.linphone.compatibility
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.NotificationManager
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
@ -53,6 +54,14 @@ class Api29Compatibility {
|
|||
}
|
||||
}
|
||||
|
||||
fun canChatMessageChannelBubble(context: Context): Boolean {
|
||||
val notificationManager: NotificationManager =
|
||||
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
val bubblesAllowed = notificationManager.areBubblesAllowed()
|
||||
Log.i("[Notifications Manager] Bubbles notifications are ${if (bubblesAllowed) "allowed" else "forbidden"}")
|
||||
return bubblesAllowed
|
||||
}
|
||||
|
||||
suspend fun addImageToMediaStore(context: Context, content: Content): Boolean {
|
||||
val filePath = content.filePath
|
||||
if (filePath == null) {
|
||||
|
|
|
@ -167,6 +167,13 @@ class Compatibility {
|
|||
}
|
||||
}
|
||||
|
||||
fun canChatMessageChannelBubble(context: Context): Boolean {
|
||||
if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10)) {
|
||||
return Api29Compatibility.canChatMessageChannelBubble(context)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
suspend fun addImageToMediaStore(context: Context, content: Content): Boolean {
|
||||
if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10)) {
|
||||
return Api29Compatibility.addImageToMediaStore(context, content)
|
||||
|
|
|
@ -136,7 +136,7 @@ open class Contact : Comparable<Contact> {
|
|||
if (bm == null) IconCompat.createWithResource(
|
||||
coreContext.context,
|
||||
R.drawable.avatar
|
||||
) else IconCompat.createWithBitmap(bm)
|
||||
) else IconCompat.createWithAdaptiveBitmap(bm)
|
||||
if (icon != null) {
|
||||
personBuilder.setIcon(icon)
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ class NativeContact(val nativeId: String, private val lookupKey: String? = null)
|
|||
if (bm == null) IconCompat.createWithResource(
|
||||
coreContext.context,
|
||||
R.drawable.avatar
|
||||
) else IconCompat.createWithBitmap(bm)
|
||||
) else IconCompat.createWithAdaptiveBitmap(bm)
|
||||
if (icon != null) {
|
||||
personBuilder.setIcon(icon)
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.linphone.R
|
|||
import org.linphone.activities.call.CallActivity
|
||||
import org.linphone.activities.call.IncomingCallActivity
|
||||
import org.linphone.activities.call.OutgoingCallActivity
|
||||
import org.linphone.activities.chat_bubble.ChatBubbleActivity
|
||||
import org.linphone.activities.main.MainActivity
|
||||
import org.linphone.compatibility.Compatibility
|
||||
import org.linphone.contact.Contact
|
||||
|
@ -68,7 +69,8 @@ private class NotifiableMessage(
|
|||
val time: Long,
|
||||
val senderAvatar: Bitmap? = null,
|
||||
var filePath: Uri? = null,
|
||||
var fileMime: String? = null
|
||||
var fileMime: String? = null,
|
||||
val isOutgoing: Boolean = false
|
||||
)
|
||||
|
||||
class NotificationsManager(private val context: Context) {
|
||||
|
@ -149,6 +151,10 @@ class NotificationsManager(private val context: Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if (corePreferences.chatRoomShortcuts) {
|
||||
Log.i("[Notifications Manager] Ensure chat room shortcut exists for bubble notification")
|
||||
Compatibility.createShortcutsToChatRooms(context)
|
||||
}
|
||||
displayIncomingChatNotification(room, message)
|
||||
}
|
||||
}
|
||||
|
@ -240,6 +246,14 @@ class NotificationsManager(private val context: Context) {
|
|||
return null
|
||||
}
|
||||
|
||||
fun getChatNotificationIdForSipUri(sipUri: String): Int {
|
||||
val notifiable: Notifiable? = chatNotificationsMap[sipUri]
|
||||
if (notifiable != null) {
|
||||
return notifiable.notificationId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/* Service related */
|
||||
|
||||
fun startForeground() {
|
||||
|
@ -529,8 +543,13 @@ class NotificationsManager(private val context: Context) {
|
|||
.setArguments(args)
|
||||
.createPendingIntent()
|
||||
|
||||
val target = Intent(context, ChatBubbleActivity::class.java)
|
||||
target.putExtra("RemoteSipUri", peerAddress)
|
||||
target.putExtra("LocalSipUri", localAddress)
|
||||
val bubbleIntent = PendingIntent.getActivity(context, notifiable.notificationId, target, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
|
||||
val id = LinphoneUtils.getChatRoomId(localAddress, peerAddress)
|
||||
val notification = createMessageNotification(notifiable, pendingIntent, id)
|
||||
val notification = createMessageNotification(notifiable, pendingIntent, bubbleIntent, id)
|
||||
notify(notifiable.notificationId, notification)
|
||||
}
|
||||
|
||||
|
@ -548,7 +567,7 @@ class NotificationsManager(private val context: Context) {
|
|||
text += content.name
|
||||
}
|
||||
}
|
||||
val notifiableMessage = NotifiableMessage(text, contact, displayName, message.time, senderAvatar = roundPicture)
|
||||
val notifiableMessage = NotifiableMessage(text, contact, displayName, message.time, senderAvatar = roundPicture, isOutgoing = message.isOutgoing)
|
||||
notifiable.messages.add(notifiableMessage)
|
||||
|
||||
for (content in message.contents) {
|
||||
|
@ -601,7 +620,8 @@ class NotificationsManager(private val context: Context) {
|
|||
message.textContent.orEmpty(),
|
||||
null,
|
||||
notifiable.myself ?: LinphoneUtils.getDisplayName(message.fromAddress),
|
||||
System.currentTimeMillis()
|
||||
System.currentTimeMillis(),
|
||||
isOutgoing = true
|
||||
)
|
||||
notifiable.messages.add(reply)
|
||||
|
||||
|
@ -623,12 +643,14 @@ class NotificationsManager(private val context: Context) {
|
|||
private fun createMessageNotification(
|
||||
notifiable: Notifiable,
|
||||
pendingIntent: PendingIntent,
|
||||
bubbleIntent: PendingIntent,
|
||||
id: String
|
||||
): Notification {
|
||||
val me = Person.Builder().setName(notifiable.myself).build()
|
||||
val style = NotificationCompat.MessagingStyle(me)
|
||||
val largeIcon: Bitmap? = notifiable.messages.last().senderAvatar
|
||||
|
||||
var lastPerson: Person? = null
|
||||
for (message in notifiable.messages) {
|
||||
val contact = message.contact
|
||||
val person = if (contact != null) {
|
||||
|
@ -637,7 +659,7 @@ class NotificationsManager(private val context: Context) {
|
|||
val builder = Person.Builder().setName(message.sender)
|
||||
val userIcon =
|
||||
if (message.senderAvatar != null) {
|
||||
IconCompat.createWithBitmap(message.senderAvatar)
|
||||
IconCompat.createWithAdaptiveBitmap(message.senderAvatar)
|
||||
} else {
|
||||
IconCompat.createWithResource(context, R.drawable.avatar)
|
||||
}
|
||||
|
@ -645,6 +667,11 @@ class NotificationsManager(private val context: Context) {
|
|||
builder.build()
|
||||
}
|
||||
|
||||
// We don't want to see our own avatar
|
||||
if (!message.isOutgoing) {
|
||||
lastPerson = person
|
||||
}
|
||||
|
||||
val msg = if (!corePreferences.hideChatMessageContentInNotification) {
|
||||
NotificationCompat.MessagingStyle.Message(message.message, message.time, person)
|
||||
} else {
|
||||
|
@ -663,6 +690,10 @@ class NotificationsManager(private val context: Context) {
|
|||
}
|
||||
style.isGroupConversation = notifiable.isGroup
|
||||
|
||||
val icon = lastPerson?.icon ?: IconCompat.createWithResource(context, R.drawable.avatar)
|
||||
val bubble = NotificationCompat.BubbleMetadata.Builder(bubbleIntent, icon)
|
||||
.build()
|
||||
|
||||
val notificationBuilder = NotificationCompat.Builder(context, context.getString(R.string.notification_channel_chat_id))
|
||||
.setSmallIcon(R.drawable.topbar_chat_notification)
|
||||
.setAutoCancel(true)
|
||||
|
@ -680,11 +711,16 @@ class NotificationsManager(private val context: Context) {
|
|||
.addAction(getMarkMessageAsReadAction(notifiable))
|
||||
.setShortcutId(id)
|
||||
.setLocusId(LocusIdCompat(id))
|
||||
|
||||
if (corePreferences.markAsReadUponChatMessageNotificationDismissal) {
|
||||
Log.i("[Notifications Manager] Chat room will be marked as read when notification will be dismissed")
|
||||
notificationBuilder
|
||||
.setDeleteIntent(getMarkMessageAsReadPendingIntent(notifiable))
|
||||
}
|
||||
|
||||
if (Compatibility.canChatMessageChannelBubble(context)) {
|
||||
notificationBuilder.bubbleMetadata = bubble
|
||||
}
|
||||
return notificationBuilder.build()
|
||||
}
|
||||
|
||||
|
|
184
app/src/main/res/layout/chat_bubble_activity.xml
Normal file
184
app/src/main/res/layout/chat_bubble_activity.xml
Normal file
|
@ -0,0 +1,184 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View"/>
|
||||
<variable
|
||||
name="closeBubbleClickListener"
|
||||
type="android.view.View.OnClickListener"/>
|
||||
<variable
|
||||
name="openAppClickListener"
|
||||
type="android.view.View.OnClickListener"/>
|
||||
<variable
|
||||
name="sendMessageClickListener"
|
||||
type="android.view.View.OnClickListener"/>
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="org.linphone.activities.main.chat.viewmodels.ChatRoomViewModel" />
|
||||
<variable
|
||||
name="chatSendingViewModel"
|
||||
type="org.linphone.activities.main.chat.viewmodels.ChatMessageSendingViewModel" />
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/top_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:background="?attr/lightToolbarBackgroundColor"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Disabled for now -->
|
||||
<ImageView
|
||||
android:contentDescription="@string/content_description_close_bubble"
|
||||
android:visibility="gone"
|
||||
android:onClick="@{closeBubbleClickListener}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0.2"
|
||||
android:layout_margin="5dp"
|
||||
android:background="?attr/button_background_drawable"
|
||||
android:src="@drawable/menu_delete_default"
|
||||
app:tint="?attr/drawableTintColor" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="0.4"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="5dp">
|
||||
|
||||
<org.linphone.views.MarqueeTextView
|
||||
android:text="@{viewModel.oneToOneChatRoom ? (viewModel.contact.fullName ?? viewModel.displayName) : viewModel.subject}"
|
||||
style="@style/toolbar_small_title_font"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true" />
|
||||
|
||||
<org.linphone.views.MarqueeTextView
|
||||
android:text="@{viewModel.participants}"
|
||||
android:visibility="@{viewModel.oneToOneChatRoom ? View.GONE : View.VISIBLE}"
|
||||
style="@style/toolbar_small_title_font"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Disabled for now -->
|
||||
<ImageView
|
||||
android:onClick="@{openAppClickListener}"
|
||||
android:visibility="gone"
|
||||
android:contentDescription="@string/content_description_open_app"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0.2"
|
||||
android:layout_margin="5dp"
|
||||
android:background="?attr/button_background_drawable"
|
||||
app:tint="?attr/colorPrimary"
|
||||
android:src="@drawable/linphone_logo"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/footer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:gravity="center_vertical"
|
||||
android:background="?attr/backgroundColor"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<org.linphone.activities.main.chat.views.RichEditText
|
||||
android:id="@+id/message"
|
||||
android:enabled="@{!chatSendingViewModel.isReadOnly}"
|
||||
android:text="@={chatSendingViewModel.textToSend}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/resizable_text_field"
|
||||
android:imeOptions="flagNoExtractUi"
|
||||
android:inputType="textShortMessage|textMultiLine|textAutoComplete|textAutoCorrect|textCapSentences"
|
||||
android:maxLines="6"
|
||||
android:padding="5dp"
|
||||
android:textColor="@color/black_color"
|
||||
android:textCursorDrawable="@null" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/send_message"
|
||||
android:onClick="@{sendMessageClickListener}"
|
||||
android:enabled="@{chatSendingViewModel.sendMessageEnabled && !chatSendingViewModel.isReadOnly}"
|
||||
android:contentDescription="@string/content_description_send_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
android:src="@drawable/chat_send_message" />
|
||||
|
||||
<ImageView
|
||||
android:visibility="@{viewModel.chatRoom.ephemeralEnabled() ? View.VISIBLE : View.GONE, default=gone}"
|
||||
android:enabled="@{chatSendingViewModel.sendMessageEnabled && !chatSendingViewModel.isReadOnly}"
|
||||
android:contentDescription="@string/content_description_ephemeral_message"
|
||||
android:clickable="false"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_alignRight="@id/send_message"
|
||||
android:layout_alignBottom="@id/send_message"
|
||||
android:padding="5dp"
|
||||
android:src="@drawable/ephemeral_messages" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/remote_composing"
|
||||
android:text="@{viewModel.composingList, default=@string/chat_remote_is_composing}"
|
||||
android:visibility="@{viewModel.remoteIsComposing ? View.VISIBLE : View.INVISIBLE}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/footer"
|
||||
android:layout_marginLeft="5dp"
|
||||
style="@style/standard_small_text_font" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/chat_messages_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@id/remote_composing"
|
||||
android:layout_below="@+id/top_bar"
|
||||
android:cacheColorHint="@color/transparent_color"
|
||||
android:choiceMode="multipleChoice"
|
||||
android:divider="@android:color/transparent"
|
||||
android:listSelector="@color/transparent_color"
|
||||
android:transcriptMode="normal" />
|
||||
|
||||
<ImageView
|
||||
android:src="@{viewModel.securityLevelIcon, default=@drawable/security_alert_indicator}"
|
||||
android:visibility="@{viewModel.encryptedChatRoom ? View.VISIBLE : View.GONE}"
|
||||
android:contentDescription="@{viewModel.securityLevelContentDescription}"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/top_bar"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginRight="8dp" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</layout>
|
|
@ -185,7 +185,8 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_margin="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/resizable_text_field"
|
||||
android:imeOptions="flagNoExtractUi"
|
||||
|
|
|
@ -707,4 +707,6 @@
|
|||
<string name="content_description_toggle_call_stats">Show or hide call statistics</string>
|
||||
<string name="content_description_chat_message_video_attachment">Video attachment</string>
|
||||
<string name="content_description_take_screenshot">Take a screenshot of received video</string>
|
||||
<string name="content_description_close_bubble">Close notification bubble</string>
|
||||
<string name="content_description_open_app">Open conversation in app instead of bubble</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue