kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
feat: add unread message count
rodzic
e4f5d9b89c
commit
d7013e1386
|
@ -125,6 +125,24 @@ class PacketDaoTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_getUnreadCount() = runBlocking {
|
||||||
|
testContactKeys.forEach { contactKey ->
|
||||||
|
val unreadCount = packetDao.getUnreadCount(contactKey)
|
||||||
|
assertEquals(SAMPLE_SIZE, unreadCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_clearUnreadCount() = runBlocking {
|
||||||
|
val timestamp = System.currentTimeMillis()
|
||||||
|
testContactKeys.forEach { contactKey ->
|
||||||
|
packetDao.clearUnreadCount(contactKey, timestamp)
|
||||||
|
val unreadCount = packetDao.getUnreadCount(contactKey)
|
||||||
|
assertEquals(0, unreadCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_deleteContacts() = runBlocking {
|
fun test_deleteContacts() = runBlocking {
|
||||||
packetDao.deleteContacts(testContactKeys)
|
packetDao.deleteContacts(testContactKeys)
|
||||||
|
|
|
@ -24,6 +24,14 @@ class PacketRepository @Inject constructor(private val packetDaoLazy: dagger.Laz
|
||||||
packetDao.getMessageCount(contact)
|
packetDao.getMessageCount(contact)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getUnreadCount(contact: String): Int = withContext(Dispatchers.IO) {
|
||||||
|
packetDao.getUnreadCount(contact)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun clearUnreadCount(contact: String, timestamp: Long) = withContext(Dispatchers.IO) {
|
||||||
|
packetDao.clearUnreadCount(contact, timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getQueuedPackets(): List<DataPacket>? = withContext(Dispatchers.IO) {
|
suspend fun getQueuedPackets(): List<DataPacket>? = withContext(Dispatchers.IO) {
|
||||||
packetDao.getQueuedPackets()
|
packetDao.getQueuedPackets()
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,25 @@ interface PacketDao {
|
||||||
)
|
)
|
||||||
suspend fun getMessageCount(contact: String): Int
|
suspend fun getMessageCount(contact: String): Int
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT COUNT(*) FROM packet
|
||||||
|
WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo))
|
||||||
|
AND port_num = 1 AND contact_key = :contact AND read = 0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getUnreadCount(contact: String): Int
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
UPDATE packet
|
||||||
|
SET read = 1
|
||||||
|
WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo))
|
||||||
|
AND port_num = 1 AND contact_key = :contact AND read = 0 AND received_time <= :timestamp
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun clearUnreadCount(contact: String, timestamp: Long)
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
fun insert(packet: Packet)
|
fun insert(packet: Packet)
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ class ContactsViewModel @Inject constructor(
|
||||||
longName = longName,
|
longName = longName,
|
||||||
lastMessageTime = getShortDateTime(data.time),
|
lastMessageTime = getShortDateTime(data.time),
|
||||||
lastMessageText = if (fromLocal) data.text else "$shortName: ${data.text}",
|
lastMessageText = if (fromLocal) data.text else "$shortName: ${data.text}",
|
||||||
unreadCount = 0,
|
unreadCount = packetRepository.getUnreadCount(contactKey),
|
||||||
messageCount = packetRepository.getMessageCount(contactKey),
|
messageCount = packetRepository.getMessageCount(contactKey),
|
||||||
isMuted = settings[contactKey]?.isMuted == true,
|
isMuted = settings[contactKey]?.isMuted == true,
|
||||||
)
|
)
|
||||||
|
|
|
@ -290,6 +290,10 @@ class UIViewModel @Inject constructor(
|
||||||
packetRepository.deleteWaypoint(id)
|
packetRepository.deleteWaypoint(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clearUnreadCount(contact: String, timestamp: Long) = viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
packetRepository.clearUnreadCount(contact, timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getPreferences(context: Context): SharedPreferences =
|
fun getPreferences(context: Context): SharedPreferences =
|
||||||
context.getSharedPreferences("ui-prefs", Context.MODE_PRIVATE)
|
context.getSharedPreferences("ui-prefs", Context.MODE_PRIVATE)
|
||||||
|
|
|
@ -603,7 +603,7 @@ class MeshService : Service(), Logging {
|
||||||
dataPacket.dataType,
|
dataPacket.dataType,
|
||||||
contactKey,
|
contactKey,
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
true, // TODO isLocal
|
fromLocal,
|
||||||
dataPacket
|
dataPacket
|
||||||
)
|
)
|
||||||
serviceScope.handledLaunch {
|
serviceScope.handledLaunch {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.geeksville.mesh.ui
|
package com.geeksville.mesh.ui
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.graphics.Rect
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
@ -98,6 +99,21 @@ class MessagesFragment : Fragment(), Logging {
|
||||||
if (itemCount > 0) layoutManager.scrollToPosition(itemCount - 1)
|
if (itemCount > 0) layoutManager.scrollToPosition(itemCount - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun scrollToFirstUnreadMessage() {
|
||||||
|
val position = messages.indexOfFirst { !it.read }
|
||||||
|
if (position > 0) {
|
||||||
|
val rect = Rect()
|
||||||
|
binding.toolbar.getGlobalVisibleRect(rect)
|
||||||
|
val toolbarOffset = rect.bottom
|
||||||
|
val offset = binding.messageListView.height - toolbarOffset
|
||||||
|
|
||||||
|
layoutManager.scrollToPositionWithOffset(position, offset)
|
||||||
|
messages[position].apply { model.clearUnreadCount(contact_key, received_time) }
|
||||||
|
} else {
|
||||||
|
scrollToBottom()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int = messages.size
|
override fun getItemCount(): Int = messages.size
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
@ -226,13 +242,14 @@ class MessagesFragment : Fragment(), Logging {
|
||||||
/// Called when our node DB changes
|
/// Called when our node DB changes
|
||||||
fun onMessagesChanged(messages: List<Packet>) {
|
fun onMessagesChanged(messages: List<Packet>) {
|
||||||
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
||||||
val shouldScrollToBottom =
|
val shouldScrollToUnread = lastVisibleItemPosition <= 0
|
||||||
lastVisibleItemPosition <= 0 || lastVisibleItemPosition == itemCount - 1
|
val shouldScrollToBottom = lastVisibleItemPosition == itemCount - 1
|
||||||
|
|
||||||
this.messages = messages
|
this.messages = messages
|
||||||
notifyDataSetChanged() // FIXME, this is super expensive and redraws all messages
|
notifyDataSetChanged() // FIXME, this is super expensive and redraws all messages
|
||||||
|
|
||||||
if (shouldScrollToBottom) scrollToBottom()
|
if (shouldScrollToBottom) scrollToBottom()
|
||||||
|
if (shouldScrollToUnread) scrollToFirstUnreadMessage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,6 +300,25 @@ class MessagesFragment : Fragment(), Logging {
|
||||||
layoutManager.stackFromEnd = true // We want the last rows to always be shown
|
layoutManager.stackFromEnd = true // We want the last rows to always be shown
|
||||||
binding.messageListView.layoutManager = layoutManager
|
binding.messageListView.layoutManager = layoutManager
|
||||||
|
|
||||||
|
binding.messageListView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
|
|
||||||
|
val firstUnreadItem = messagesAdapter.messages.firstOrNull { !it.read }
|
||||||
|
if (firstUnreadItem != null && dy > 0) {
|
||||||
|
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
||||||
|
if (lastVisibleItemPosition != RecyclerView.NO_POSITION) {
|
||||||
|
val lastVisibleItem = messagesAdapter.messages[lastVisibleItemPosition]
|
||||||
|
val timestamp = lastVisibleItem.received_time
|
||||||
|
|
||||||
|
if (timestamp > firstUnreadItem.received_time) {
|
||||||
|
model.clearUnreadCount(contactKey, timestamp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
model.getMessagesFrom(contactKey).asLiveData().observe(viewLifecycleOwner) {
|
model.getMessagesFrom(contactKey).asLiveData().observe(viewLifecycleOwner) {
|
||||||
debug("New messages received: ${it.size}")
|
debug("New messages received: ${it.size}")
|
||||||
messagesAdapter.onMessagesChanged(it)
|
messagesAdapter.onMessagesChanged(it)
|
||||||
|
|
Ładowanie…
Reference in New Issue