mirror of
https://github.com/SimpleMobileTools/Simple-SMS-Messenger.git
synced 2025-01-30 09:04:50 +01:00
Use DiffUtil with thread recyclerview
Also added a progress indicator at the top while fetching older messages
This commit is contained in:
parent
c5034a64d5
commit
d8dadd1f55
@ -55,6 +55,7 @@ import com.simplemobiletools.smsmessenger.dialogs.ScheduleMessageDialog
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.models.*
|
||||
import com.simplemobiletools.smsmessenger.models.ThreadItem.*
|
||||
import kotlinx.android.synthetic.main.activity_thread.*
|
||||
import kotlinx.android.synthetic.main.item_selected_contact.view.*
|
||||
import kotlinx.android.synthetic.main.layout_attachment_picker.*
|
||||
@ -116,8 +117,8 @@ class ThreadActivity : SimpleActivity() {
|
||||
|
||||
bus = EventBus.getDefault()
|
||||
bus!!.register(this)
|
||||
handlePermission(PERMISSION_READ_PHONE_STATE) {
|
||||
if (it) {
|
||||
handlePermission(PERMISSION_READ_PHONE_STATE) { granted ->
|
||||
if (granted) {
|
||||
setupButtons()
|
||||
setupCachedMessages {
|
||||
val searchedMessageId = intent.getLongExtra(SEARCHED_MESSAGE_ID, -1L)
|
||||
@ -263,7 +264,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
}
|
||||
|
||||
private fun setupThread() {
|
||||
val privateCursor = getMyContactsCursor(false, true)
|
||||
val privateCursor = getMyContactsCursor(favoritesOnly = false, withPhoneNumbersOnly = true)
|
||||
ensureBackgroundThread {
|
||||
privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
|
||||
|
||||
@ -329,33 +330,40 @@ class ThreadActivity : SimpleActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOrCreateThreadAdapter(): ThreadAdapter {
|
||||
var currAdapter = thread_messages_list.adapter
|
||||
if (currAdapter == null) {
|
||||
currAdapter = ThreadAdapter(
|
||||
activity = this,
|
||||
recyclerView = thread_messages_list,
|
||||
itemClick = { handleItemClick(it) },
|
||||
deleteMessages = { deleteMessages(it) }
|
||||
)
|
||||
|
||||
thread_messages_list.adapter = currAdapter
|
||||
thread_messages_list.endlessScrollListener = object : MyRecyclerView.EndlessScrollListener {
|
||||
override fun updateBottom() {}
|
||||
|
||||
override fun updateTop() {
|
||||
fetchNextMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
return currAdapter as ThreadAdapter
|
||||
}
|
||||
|
||||
private fun setupAdapter() {
|
||||
threadItems = getThreadItems()
|
||||
|
||||
runOnUiThread {
|
||||
refreshMenuItems()
|
||||
|
||||
val currAdapter = thread_messages_list.adapter
|
||||
if (currAdapter == null) {
|
||||
ThreadAdapter(
|
||||
activity = this,
|
||||
messages = threadItems,
|
||||
recyclerView = thread_messages_list,
|
||||
itemClick = { handleItemClick(it) },
|
||||
onThreadIdUpdate = { threadId = it }
|
||||
).apply {
|
||||
thread_messages_list.adapter = this
|
||||
getOrCreateThreadAdapter().apply {
|
||||
val scrollPosition = if (currentList.lastOrNull() != threadItems.lastOrNull()) {
|
||||
threadItems.lastIndex
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
|
||||
thread_messages_list.endlessScrollListener = object : MyRecyclerView.EndlessScrollListener {
|
||||
override fun updateBottom() {}
|
||||
|
||||
override fun updateTop() {
|
||||
fetchNextMessages()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(currAdapter as ThreadAdapter).updateMessages(threadItems)
|
||||
updateMessages(threadItems, scrollPosition)
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,6 +395,11 @@ class ThreadActivity : SimpleActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun scrollToBottom() {
|
||||
val position = getOrCreateThreadAdapter().currentList.lastIndex
|
||||
if (position >= 0) thread_messages_list.smoothScrollToPosition(position)
|
||||
}
|
||||
|
||||
private fun handleItemClick(any: Any) {
|
||||
when {
|
||||
any is Message && any.isScheduled -> showScheduledMessageInfo(any)
|
||||
@ -394,8 +407,51 @@ class ThreadActivity : SimpleActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteMessages(messagesToRemove: List<Message>) {
|
||||
messages.removeAll(messagesToRemove.toSet())
|
||||
threadItems = getThreadItems()
|
||||
|
||||
runOnUiThread {
|
||||
if (messages.isEmpty()) {
|
||||
finish()
|
||||
} else {
|
||||
getOrCreateThreadAdapter().apply {
|
||||
updateMessages(threadItems)
|
||||
finishActMode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messagesToRemove.forEach { message ->
|
||||
val messageId = message.id
|
||||
if (message.isScheduled) {
|
||||
deleteScheduledMessage(messageId)
|
||||
cancelScheduleSendPendingIntent(messageId)
|
||||
} else {
|
||||
deleteMessage(messageId, message.isMMS)
|
||||
}
|
||||
}
|
||||
updateLastConversationMessage(threadId)
|
||||
|
||||
if (messages.isNotEmpty() && messages.all { it.isScheduled }) {
|
||||
// move all scheduled messages to a temporary thread as there are no real messages left
|
||||
val message = messagesToRemove.last()
|
||||
val newThreadId = generateRandomId()
|
||||
createTemporaryThread(message, newThreadId)
|
||||
updateScheduledMessagesThreadId(messagesToRemove, newThreadId)
|
||||
threadId = newThreadId
|
||||
}
|
||||
refreshMessages()
|
||||
}
|
||||
|
||||
private fun fetchNextMessages() {
|
||||
if (messages.isEmpty() || allMessagesFetched || loadingOlderMessages) {
|
||||
if (allMessagesFetched) {
|
||||
getOrCreateThreadAdapter().apply {
|
||||
val newList = currentList.toMutableList().apply { removeAll { it is ThreadLoading } }
|
||||
updateMessages(newMessages = newList as ArrayList<ThreadItem>, scrollPosition = 0)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -414,16 +470,13 @@ class ThreadActivity : SimpleActivity() {
|
||||
.filter { message -> !messages.contains(message) }
|
||||
|
||||
messages.addAll(0, olderMessages)
|
||||
threadItems = getThreadItems()
|
||||
|
||||
allMessagesFetched = olderMessages.size < MESSAGES_LIMIT || olderMessages.isEmpty()
|
||||
threadItems = getThreadItems()
|
||||
|
||||
runOnUiThread {
|
||||
loadingOlderMessages = false
|
||||
val itemAtRefreshIndex = threadItems.indexOfFirst { it == firstItem }
|
||||
(thread_messages_list.adapter as ThreadAdapter).apply {
|
||||
updateMessages(threadItems, itemAtRefreshIndex)
|
||||
}
|
||||
getOrCreateThreadAdapter().updateMessages(threadItems, itemAtRefreshIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -819,6 +872,10 @@ class ThreadActivity : SimpleActivity() {
|
||||
bus?.post(Events.RefreshMessages())
|
||||
}
|
||||
|
||||
if (!allMessagesFetched && messages.size >= MESSAGES_LIMIT) {
|
||||
items.add(0, ThreadLoading(generateRandomId()))
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
@ -988,6 +1045,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
showErrorToast(getString(R.string.unknown_error_occurred))
|
||||
return
|
||||
}
|
||||
scrollToBottom()
|
||||
|
||||
text = removeDiacriticsIfNeeded(text)
|
||||
|
||||
|
@ -11,6 +11,7 @@ import android.util.TypedValue
|
||||
import android.view.Menu
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
@ -21,7 +22,7 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
|
||||
import com.simplemobiletools.commons.adapters.MyRecyclerViewListAdapter
|
||||
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||
@ -35,7 +36,10 @@ import com.simplemobiletools.smsmessenger.activities.VCardViewerActivity
|
||||
import com.simplemobiletools.smsmessenger.dialogs.SelectTextDialog
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.models.*
|
||||
import com.simplemobiletools.smsmessenger.models.Attachment
|
||||
import com.simplemobiletools.smsmessenger.models.Message
|
||||
import com.simplemobiletools.smsmessenger.models.ThreadItem
|
||||
import com.simplemobiletools.smsmessenger.models.ThreadItem.*
|
||||
import kotlinx.android.synthetic.main.item_attachment_image.view.*
|
||||
import kotlinx.android.synthetic.main.item_received_message.view.*
|
||||
import kotlinx.android.synthetic.main.item_received_message.view.thread_mesage_attachments_holder
|
||||
@ -45,12 +49,16 @@ import kotlinx.android.synthetic.main.item_received_message.view.thread_message_
|
||||
import kotlinx.android.synthetic.main.item_sent_message.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_date_time.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_error.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_loading.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_sending.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_success.view.*
|
||||
|
||||
class ThreadAdapter(
|
||||
activity: SimpleActivity, var messages: ArrayList<ThreadItem>, recyclerView: MyRecyclerView, itemClick: (Any) -> Unit, val onThreadIdUpdate: (Long) -> Unit
|
||||
) : MyRecyclerViewAdapter(activity, recyclerView, itemClick) {
|
||||
activity: SimpleActivity,
|
||||
recyclerView: MyRecyclerView,
|
||||
itemClick: (Any) -> Unit,
|
||||
val deleteMessages: (messages: List<Message>) -> Unit
|
||||
) : MyRecyclerViewListAdapter<ThreadItem>(activity, recyclerView, ThreadItemDiffCallback(), itemClick) {
|
||||
private var fontSize = activity.getTextSize()
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@ -59,6 +67,7 @@ class ThreadAdapter(
|
||||
|
||||
init {
|
||||
setupDragListener(true)
|
||||
setHasStableIds(true)
|
||||
}
|
||||
|
||||
override fun getActionMenuId() = R.menu.cab_thread
|
||||
@ -92,13 +101,13 @@ class ThreadAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelectableItemCount() = messages.filter { it is Message }.size
|
||||
override fun getSelectableItemCount() = currentList.filterIsInstance<Message>().size
|
||||
|
||||
override fun getIsItemSelectable(position: Int) = !isThreadDateTime(position)
|
||||
|
||||
override fun getItemSelectionKey(position: Int) = (messages.getOrNull(position) as? Message)?.hashCode()
|
||||
override fun getItemSelectionKey(position: Int) = (currentList.getOrNull(position) as? Message)?.hashCode()
|
||||
|
||||
override fun getItemKeyPosition(key: Int) = messages.indexOfFirst { (it as? Message)?.hashCode() == key }
|
||||
override fun getItemKeyPosition(key: Int) = currentList.indexOfFirst { (it as? Message)?.hashCode() == key }
|
||||
|
||||
override fun onActionModeCreated() {}
|
||||
|
||||
@ -106,6 +115,7 @@ class ThreadAdapter(
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val layout = when (viewType) {
|
||||
THREAD_LOADING -> R.layout.item_thread_loading
|
||||
THREAD_DATE_TIME -> R.layout.item_thread_date_time
|
||||
THREAD_RECEIVED_MESSAGE -> R.layout.item_received_message
|
||||
THREAD_SENT_MESSAGE_ERROR -> R.layout.item_thread_error
|
||||
@ -117,32 +127,37 @@ class ThreadAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val item = messages[position]
|
||||
val item = getItem(position)
|
||||
val isClickable = item is ThreadError || item is Message
|
||||
val isLongClickable = item is Message
|
||||
holder.bindView(item, isClickable, isLongClickable) { itemView, layoutPosition ->
|
||||
holder.bindView(item, isClickable, isLongClickable) { itemView, _ ->
|
||||
when (item) {
|
||||
is ThreadLoading -> setupThreadLoading(itemView)
|
||||
is ThreadDateTime -> setupDateTime(itemView, item)
|
||||
is ThreadSent -> setupThreadSuccess(itemView, item.delivered)
|
||||
is ThreadError -> setupThreadError(itemView)
|
||||
is ThreadSent -> setupThreadSuccess(itemView, item.delivered)
|
||||
is ThreadSending -> setupThreadSending(itemView)
|
||||
else -> setupView(holder, itemView, item as Message)
|
||||
is Message -> setupView(holder, itemView, item)
|
||||
}
|
||||
}
|
||||
bindViewHolder(holder)
|
||||
}
|
||||
|
||||
override fun getItemCount() = messages.size
|
||||
override fun getItemId(position: Int): Long {
|
||||
val item = getItem(position)
|
||||
// hashcode is probably okay as the chances of collision here are nearly zero considering the max list size of 50 items
|
||||
// future: generate a unique id for each item instead of relying on the telephony id for uniqueness
|
||||
return item.hashCode().toLong()
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
val item = messages[position]
|
||||
return when {
|
||||
item is ThreadDateTime -> THREAD_DATE_TIME
|
||||
(messages[position] as? Message)?.isReceivedMessage() == true -> THREAD_RECEIVED_MESSAGE
|
||||
item is ThreadError -> THREAD_SENT_MESSAGE_ERROR
|
||||
item is ThreadSent -> THREAD_SENT_MESSAGE_SENT
|
||||
item is ThreadSending -> THREAD_SENT_MESSAGE_SENDING
|
||||
else -> THREAD_SENT_MESSAGE
|
||||
return when (val item = getItem(position)) {
|
||||
is ThreadLoading -> THREAD_LOADING
|
||||
is ThreadDateTime -> THREAD_DATE_TIME
|
||||
is ThreadError -> THREAD_SENT_MESSAGE_ERROR
|
||||
is ThreadSent -> THREAD_SENT_MESSAGE_SENT
|
||||
is ThreadSending -> THREAD_SENT_MESSAGE_SENDING
|
||||
is Message -> if (item.isReceivedMessage()) THREAD_RECEIVED_MESSAGE else THREAD_SENT_MESSAGE
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,45 +200,14 @@ class ThreadAdapter(
|
||||
|
||||
ConfirmationDialog(activity, question) {
|
||||
ensureBackgroundThread {
|
||||
deleteMessages()
|
||||
val messagesToRemove = getSelectedItems()
|
||||
if (messagesToRemove.isNotEmpty()) {
|
||||
deleteMessages(messagesToRemove.filterIsInstance<Message>())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteMessages() {
|
||||
val messagesToRemove = getSelectedItems()
|
||||
if (messagesToRemove.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val positions = getSelectedItemPositions()
|
||||
val threadId = (messagesToRemove.firstOrNull() as? Message)?.threadId ?: return
|
||||
messagesToRemove.forEach {
|
||||
activity.deleteMessage((it as Message).id, it.isMMS)
|
||||
}
|
||||
messages.removeAll(messagesToRemove.toSet())
|
||||
activity.updateLastConversationMessage(threadId)
|
||||
|
||||
val messages = messages.filterIsInstance<Message>()
|
||||
if (messages.isNotEmpty() && messages.all { it.isScheduled }) {
|
||||
// move all scheduled messages to a temporary thread as there are no real messages left
|
||||
val message = messages.last()
|
||||
val newThreadId = generateRandomId()
|
||||
activity.createTemporaryThread(message, newThreadId)
|
||||
activity.updateScheduledMessagesThreadId(messages, newThreadId)
|
||||
onThreadIdUpdate(newThreadId)
|
||||
}
|
||||
|
||||
activity.runOnUiThread {
|
||||
if (messages.isEmpty()) {
|
||||
activity.finish()
|
||||
} else {
|
||||
removeSelectedItems(positions)
|
||||
}
|
||||
refreshMessages()
|
||||
}
|
||||
}
|
||||
|
||||
private fun forwardMessage() {
|
||||
val message = getSelectedItems().firstOrNull() as? Message ?: return
|
||||
val attachment = message.attachment?.attachments?.firstOrNull()
|
||||
@ -239,17 +223,13 @@ class ThreadAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSelectedItems() = messages.filter { selectedKeys.contains((it as? Message)?.hashCode() ?: 0) } as ArrayList<ThreadItem>
|
||||
private fun getSelectedItems() = currentList.filter { selectedKeys.contains((it as? Message)?.hashCode() ?: 0) } as ArrayList<ThreadItem>
|
||||
|
||||
private fun isThreadDateTime(position: Int) = messages.getOrNull(position) is ThreadDateTime
|
||||
private fun isThreadDateTime(position: Int) = currentList.getOrNull(position) is ThreadDateTime
|
||||
|
||||
fun updateMessages(newMessages: ArrayList<ThreadItem>, scrollPosition: Int = newMessages.size - 1) {
|
||||
val latestMessages = newMessages.clone() as ArrayList<ThreadItem>
|
||||
val oldHashCode = messages.hashCode()
|
||||
val newHashCode = latestMessages.hashCode()
|
||||
if (newHashCode != oldHashCode) {
|
||||
messages = latestMessages
|
||||
notifyDataSetChanged()
|
||||
fun updateMessages(newMessages: ArrayList<ThreadItem>, scrollPosition: Int = newMessages.lastIndex) {
|
||||
val latestMessages = newMessages.toMutableList()
|
||||
submitList(latestMessages) {
|
||||
recyclerView.scrollToPosition(scrollPosition)
|
||||
}
|
||||
}
|
||||
@ -484,6 +464,8 @@ class ThreadAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupThreadLoading(view: View) = view.thread_loading.setIndicatorColor(properPrimaryColor)
|
||||
|
||||
override fun onViewRecycled(holder: ViewHolder) {
|
||||
super.onViewRecycled(holder)
|
||||
if (!activity.isDestroyed && !activity.isFinishing && holder.itemView.thread_message_sender_photo != null) {
|
||||
@ -491,3 +473,30 @@ class ThreadAdapter(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ThreadItemDiffCallback : DiffUtil.ItemCallback<ThreadItem>() {
|
||||
|
||||
override fun areItemsTheSame(oldItem: ThreadItem, newItem: ThreadItem): Boolean {
|
||||
if (oldItem::class.java != newItem::class.java) return false
|
||||
return when (oldItem) {
|
||||
is ThreadLoading -> oldItem.id == (newItem as ThreadLoading).id
|
||||
is ThreadDateTime -> oldItem.date == (newItem as ThreadDateTime).date
|
||||
is ThreadError -> oldItem.messageId == (newItem as ThreadError).messageId
|
||||
is ThreadSent -> oldItem.messageId == (newItem as ThreadSent).messageId
|
||||
is ThreadSending -> oldItem.messageId == (newItem as ThreadSending).messageId
|
||||
is Message -> Message.areItemsTheSame(oldItem, newItem as Message)
|
||||
}
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: ThreadItem, newItem: ThreadItem): Boolean {
|
||||
if (oldItem::class.java != newItem::class.java) return false
|
||||
return when (oldItem) {
|
||||
is ThreadLoading -> false
|
||||
is ThreadDateTime -> oldItem.simID == (newItem as ThreadDateTime).simID
|
||||
is ThreadError -> oldItem.messageText == (newItem as ThreadError).messageText
|
||||
is ThreadSent -> oldItem.delivered == (newItem as ThreadSent).delivered
|
||||
is ThreadSending -> true
|
||||
is Message -> Message.areContentsTheSame(oldItem, newItem as Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ const val THREAD_SENT_MESSAGE = 3
|
||||
const val THREAD_SENT_MESSAGE_ERROR = 4
|
||||
const val THREAD_SENT_MESSAGE_SENT = 5
|
||||
const val THREAD_SENT_MESSAGE_SENDING = 6
|
||||
const val THREAD_LOADING = 7
|
||||
|
||||
// view types for attachment list
|
||||
const val ATTACHMENT_DOCUMENT = 7
|
||||
|
@ -27,4 +27,21 @@ data class Message(
|
||||
fun isReceivedMessage() = type == Telephony.Sms.MESSAGE_TYPE_INBOX
|
||||
|
||||
fun millis() = date * 1000L
|
||||
|
||||
companion object {
|
||||
fun areItemsTheSame(old: Message, new: Message): Boolean {
|
||||
return old.id == new.id
|
||||
}
|
||||
|
||||
fun areContentsTheSame(old: Message, new: Message): Boolean {
|
||||
return old.body == new.body &&
|
||||
old.type == new.type &&
|
||||
old.threadId == new.threadId &&
|
||||
old.isMMS == new.isMMS &&
|
||||
old.attachment == new.attachment &&
|
||||
old.senderName == new.senderName &&
|
||||
old.senderPhotoUri == new.senderPhotoUri &&
|
||||
old.isScheduled == new.isScheduled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
data class ThreadDateTime(val date: Int, val simID: String) : ThreadItem()
|
@ -1,3 +0,0 @@
|
||||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
data class ThreadError(val messageId: Long, val messageText: String) : ThreadItem()
|
@ -1,3 +0,0 @@
|
||||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
open class ThreadItem
|
@ -0,0 +1,12 @@
|
||||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
/**
|
||||
* Thread item representations for the main thread recyclerview. [Message] is also a [ThreadItem]
|
||||
*/
|
||||
sealed class ThreadItem {
|
||||
data class ThreadLoading(val id: Long) : ThreadItem()
|
||||
data class ThreadDateTime(val date: Int, val simID: String) : ThreadItem()
|
||||
data class ThreadError(val messageId: Long, val messageText: String) : ThreadItem()
|
||||
data class ThreadSent(val messageId: Long, val delivered: Boolean) : ThreadItem()
|
||||
data class ThreadSending(val messageId: Long) : ThreadItem()
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
data class ThreadSending(val messageId: Long) : ThreadItem()
|
@ -1,5 +0,0 @@
|
||||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
// show a check after the latest message, if it is a sent one and succeeded,
|
||||
// show a double check if it is delivered
|
||||
data class ThreadSent(val messageID: Long, val delivered: Boolean) : ThreadItem()
|
17
app/src/main/res/layout/item_thread_loading.xml
Normal file
17
app/src/main/res/layout/item_thread_loading.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:id="@+id/thread_loading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="@dimen/small_margin"
|
||||
android:indeterminate="true"
|
||||
app:indicatorSize="@dimen/big_margin"
|
||||
app:trackCornerRadius="@dimen/normal_margin" />
|
||||
</LinearLayout>
|
Loading…
x
Reference in New Issue
Block a user