Update some info dynamically

This commit is contained in:
Sylvain Berfini 2023-10-12 14:43:55 +02:00
parent d9b6f0482a
commit 8269228b8a
8 changed files with 167 additions and 59 deletions

View file

@ -125,7 +125,7 @@ class ConversationFragment : GenericFragment() {
}
adapter = ConversationEventAdapter(viewLifecycleOwner)
binding.eventsList.setHasFixedSize(false)
binding.eventsList.setHasFixedSize(true)
binding.eventsList.adapter = adapter
val layoutManager = LinearLayoutManager(requireContext())
@ -142,9 +142,11 @@ class ConversationFragment : GenericFragment() {
adapter.submitList(items)
Log.i("$TAG Events (messages) list updated with [${items.size}] items")
(view.parent as? ViewGroup)?.doOnPreDraw {
startPostponedEnterTransition()
sharedViewModel.openSlidingPaneEvent.value = Event(true)
if (currentCount == 0 && items.isNotEmpty()) {
(view.parent as? ViewGroup)?.doOnPreDraw {
startPostponedEnterTransition()
sharedViewModel.openSlidingPaneEvent.value = Event(true)
}
}
if (currentCount < items.size) {

View file

@ -24,9 +24,13 @@ import androidx.annotation.WorkerThread
import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.Address
import org.linphone.core.ChatMessage
import org.linphone.core.ChatMessageListenerStub
import org.linphone.core.ChatRoom
import org.linphone.core.ChatRoom.Capabilities
import org.linphone.core.ChatRoomListenerStub
import org.linphone.core.EventLog
import org.linphone.core.Friend
import org.linphone.core.tools.Log
import org.linphone.ui.main.contacts.model.ContactAvatarModel
@ -62,7 +66,7 @@ class ConversationModel @WorkerThread constructor(private val chatRoom: ChatRoom
val composingLabel = MutableLiveData<Boolean>()
val lastMessage = MutableLiveData<String>()
val lastMessageText = MutableLiveData<String>()
val lastMessageIcon = MutableLiveData<Int>()
@ -76,9 +80,47 @@ class ConversationModel @WorkerThread constructor(private val chatRoom: ChatRoom
val groupAvatarModel: GroupAvatarModel
private var lastMessage: ChatMessage? = null
private val chatRoomListener = object : ChatRoomListenerStub() {
@WorkerThread
override fun onIsComposingReceived(
chatRoom: ChatRoom,
remoteAddress: Address,
isComposing: Boolean
) {
computeComposingLabel()
}
@WorkerThread
override fun onMessagesReceived(chatRoom: ChatRoom, chatMessages: Array<out ChatMessage>) {
updateLastMessage()
}
@WorkerThread
override fun onChatMessageSending(chatRoom: ChatRoom, eventLog: EventLog) {
updateLastMessage()
}
@WorkerThread
override fun onChatRoomRead(chatRoom: ChatRoom) {
unreadMessageCount.postValue(chatRoom.unreadMessagesCount)
}
}
private val chatMessageListener = object : ChatMessageListenerStub() {
@WorkerThread
override fun onMsgStateChanged(message: ChatMessage, state: ChatMessage.State?) {
updateLastMessageStatus(message)
}
}
init {
chatRoom.addListener(chatRoomListener)
subject.postValue(chatRoom.subject)
lastUpdateTime.postValue(chatRoom.lastUpdateTime)
computeComposingLabel()
val friends = arrayListOf<Friend>()
val address = if (chatRoom.hasCapability(Capabilities.Basic.toInt())) {
@ -123,6 +165,14 @@ class ConversationModel @WorkerThread constructor(private val chatRoom: ChatRoom
unreadMessageCount.postValue(chatRoom.unreadMessagesCount)
}
@WorkerThread
fun destroy() {
lastMessage?.removeListener(chatMessageListener)
lastMessage = null
chatRoom.removeListener(chatRoomListener)
}
@UiThread
fun markAsRead() {
coreContext.postOnCoreThread {
@ -171,37 +221,55 @@ class ConversationModel @WorkerThread constructor(private val chatRoom: ChatRoom
}
}
@WorkerThread
private fun updateLastMessageStatus(message: ChatMessage) {
val text = LinphoneUtils.getTextDescribingMessage(message)
lastMessageText.postValue(text)
val isOutgoing = message.isOutgoing
isLastMessageOutgoing.postValue(isOutgoing)
if (isOutgoing) {
val icon = when (message.state) {
ChatMessage.State.Displayed -> {
R.drawable.checks
}
ChatMessage.State.DeliveredToUser -> {
R.drawable.check
}
ChatMessage.State.Delivered -> {
R.drawable.sent
}
ChatMessage.State.InProgress, ChatMessage.State.FileTransferInProgress -> {
R.drawable.in_progress
}
ChatMessage.State.NotDelivered, ChatMessage.State.FileTransferError -> {
R.drawable.warning_circle
}
else -> {
R.drawable.info
}
}
lastMessageIcon.postValue(icon)
}
}
@WorkerThread
private fun updateLastMessage() {
lastMessage?.removeListener(chatMessageListener)
lastMessage = null
val message = chatRoom.lastMessageInHistory
if (message != null) {
val text = LinphoneUtils.getTextDescribingMessage(message)
lastMessage.postValue(text)
updateLastMessageStatus(message)
val isOutgoing = message.isOutgoing
isLastMessageOutgoing.postValue(isOutgoing)
if (isOutgoing) {
val icon = when (message.state) {
ChatMessage.State.Displayed -> {
R.drawable.checks
}
ChatMessage.State.DeliveredToUser -> {
R.drawable.check
}
ChatMessage.State.Delivered -> {
R.drawable.sent
}
ChatMessage.State.InProgress, ChatMessage.State.FileTransferInProgress -> {
R.drawable.in_progress
}
ChatMessage.State.NotDelivered, ChatMessage.State.FileTransferError -> {
R.drawable.warning_circle
}
else -> {
R.drawable.info
}
}
lastMessageIcon.postValue(icon)
if (message.isOutgoing && message.state != ChatMessage.State.Displayed) {
message.addListener(chatMessageListener)
lastMessage = message
}
} else {
Log.w("$TAG No last message to display for chat room [$id]")
@ -225,4 +293,9 @@ class ConversationModel @WorkerThread constructor(private val chatRoom: ChatRoom
}
dateTime.postValue(humanReadableTimestamp)
}
@WorkerThread
private fun computeComposingLabel() {
// TODO
}
}

View file

@ -87,6 +87,7 @@ class ConversationsListViewModel @UiThread constructor() : AbstractTopBarViewMod
super.onCleared()
coreContext.postOnCoreThread { core ->
conversations.value.orEmpty().forEach(ConversationModel::destroy)
coreContext.contactsManager.removeListener(contactsListener)
core.removeListener(coreListener)
}
@ -103,6 +104,8 @@ class ConversationsListViewModel @UiThread constructor() : AbstractTopBarViewMod
@WorkerThread
private fun computeChatRoomsList(filter: String) {
conversations.value.orEmpty().forEach(ConversationModel::destroy)
if (conversations.value.orEmpty().isEmpty()) {
fetchInProgress.postValue(true)
}

View file

@ -95,29 +95,7 @@ class MeetingFragment : GenericFragment() {
}
binding.setShareClickListener {
val intent = Intent(Intent.ACTION_EDIT)
intent.type = "vnd.android.cursor.item/event"
intent.putExtra(CalendarContract.Events.TITLE, viewModel.subject.value)
val description = viewModel.description.value.orEmpty()
if (description.isNotEmpty()) {
intent.putExtra(CalendarContract.Events.DESCRIPTION, description)
}
intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, viewModel.startTimeStamp.value)
intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, viewModel.endTimeStamp.value)
intent.putExtra(CalendarContract.Events.CUSTOM_APP_URI, viewModel.sipUri.value)
intent.putExtra(
CalendarContract.Events.CUSTOM_APP_PACKAGE,
requireContext().packageName
)
try {
startActivity(intent)
} catch (exception: ActivityNotFoundException) {
Log.e("$TAG No activity found to handle intent: $exception")
}
shareMeetingInfoAsCalendarEvent()
}
binding.setMenuClickListener {
@ -180,4 +158,30 @@ class MeetingFragment : GenericFragment() {
popupWindow.elevation = 20f
popupWindow.showAsDropDown(binding.menu, 0, 0, Gravity.BOTTOM)
}
private fun shareMeetingInfoAsCalendarEvent() {
val intent = Intent(Intent.ACTION_EDIT)
intent.type = "vnd.android.cursor.item/event"
intent.putExtra(CalendarContract.Events.TITLE, viewModel.subject.value)
val description = viewModel.description.value.orEmpty()
if (description.isNotEmpty()) {
intent.putExtra(CalendarContract.Events.DESCRIPTION, description)
}
intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, viewModel.startTimeStamp.value)
intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, viewModel.endTimeStamp.value)
intent.putExtra(CalendarContract.Events.CUSTOM_APP_URI, viewModel.sipUri.value)
intent.putExtra(
CalendarContract.Events.CUSTOM_APP_PACKAGE,
requireContext().packageName
)
try {
startActivity(intent)
} catch (exception: ActivityNotFoundException) {
Log.e("${MeetingFragment.TAG} No activity found to handle intent: $exception")
}
}
}

View file

@ -27,6 +27,10 @@ import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.Account
import org.linphone.core.AccountListenerStub
import org.linphone.core.ChatMessage
import org.linphone.core.ChatRoom
import org.linphone.core.Core
import org.linphone.core.CoreListenerStub
import org.linphone.core.RegistrationState
import org.linphone.core.tools.Log
import org.linphone.utils.AppUtils
@ -75,8 +79,25 @@ class AccountModel @WorkerThread constructor(
}
}
private val coreListener = object : CoreListenerStub() {
@WorkerThread
override fun onChatRoomRead(core: Core, chatRoom: ChatRoom) {
computeNotificationsCount()
}
@WorkerThread
override fun onMessagesReceived(
core: Core,
chatRoom: ChatRoom,
messages: Array<out ChatMessage>
) {
computeNotificationsCount()
}
}
init {
account.addListener(accountListener)
coreContext.core.addListener(coreListener)
showTrust.postValue(account.isInSecureMode())
@ -85,6 +106,7 @@ class AccountModel @WorkerThread constructor(
@WorkerThread
fun destroy() {
coreContext.core.removeListener(coreListener)
account.removeListener(accountListener)
}
@ -139,7 +161,7 @@ class AccountModel @WorkerThread constructor(
}
isDefault.postValue(coreContext.core.defaultAccount == account)
notificationsCount.postValue(account.unreadChatMessageCount + account.missedCallsCount)
computeNotificationsCount()
val state = account.state
registrationState.postValue(state)
@ -187,6 +209,11 @@ class AccountModel @WorkerThread constructor(
}
registrationStateSummary.postValue(summary)
}
@WorkerThread
private fun computeNotificationsCount() {
notificationsCount.postValue(account.unreadChatMessageCount + account.missedCallsCount)
}
}
fun Account.isInSecureMode(): Boolean {

View file

@ -24,7 +24,7 @@
<ImageView
android:id="@+id/emojis_background"
android:layout_width="0dp"
android:layout_height="@dimen/chat_bubble_long_press_menu_emojis_height"
android:layout_height="60dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:src="@drawable/shape_squircle_white_background"

View file

@ -135,7 +135,7 @@
android:gravity="center_vertical|start"
android:maxLines="2"
android:ellipsize="end"
android:text="@{model.isComposing ? model.composingLabel : model.lastMessage, default=`Hello there!`}"
android:text="@{model.isComposing ? model.composingLabel : model.lastMessageText, default=`Hello there!`}"
android:textSize="14sp"
android:textColor="@color/gray_main2_400"
android:textStyle="@{model.unreadMessageCount > 0 ? Typeface.BOLD : Typeface.NORMAL}"

View file

@ -55,6 +55,5 @@
<dimen name="chat_bubble_grouped_top_margin">4dp</dimen>
<dimen name="chat_bubble_top_margin">16dp</dimen>
<dimen name="chat_bubble_long_press_menu_emojis_height">60dp</dimen>
<dimen name="chat_bubble_long_press_menu_bubble_offset">105dp</dimen>
<dimen name="chat_bubble_long_press_menu_bubble_offset">110dp</dimen>
</resources>