Merge branch 'master' into feature/451-recycle-bin
This commit is contained in:
commit
b29d664dc4
71 changed files with 1263 additions and 201 deletions
|
|
@ -0,0 +1,160 @@
|
|||
package com.simplemobiletools.smsmessenger.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.adapters.ArchivedConversationsAdapter
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
import com.simplemobiletools.smsmessenger.models.Events
|
||||
import kotlinx.android.synthetic.main.activity_archived_conversations.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class ArchivedConversationsActivity : SimpleActivity() {
|
||||
private var bus: EventBus? = null
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
isMaterialActivity = true
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_archived_conversations)
|
||||
setupOptionsMenu()
|
||||
|
||||
updateMaterialActivityViews(archive_coordinator, conversations_list, useTransparentNavigation = true, useTopSearchMenu = false)
|
||||
setupMaterialScrollListener(conversations_list, archive_toolbar)
|
||||
|
||||
loadArchivedConversations()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
setupToolbar(archive_toolbar, NavigationIcon.Arrow)
|
||||
updateMenuColors()
|
||||
|
||||
loadArchivedConversations()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
bus?.unregister(this)
|
||||
}
|
||||
|
||||
private fun setupOptionsMenu() {
|
||||
archive_toolbar.inflateMenu(R.menu.archive_menu)
|
||||
|
||||
archive_toolbar.setOnMenuItemClickListener { menuItem ->
|
||||
when (menuItem.itemId) {
|
||||
R.id.empty_archive -> removeAll()
|
||||
else -> return@setOnMenuItemClickListener false
|
||||
}
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateOptionsMenu(conversations: ArrayList<Conversation>) {
|
||||
archive_toolbar.menu.apply {
|
||||
findItem(R.id.empty_archive).isVisible = conversations.isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateMenuColors() {
|
||||
updateStatusbarColor(getProperBackgroundColor())
|
||||
}
|
||||
|
||||
private fun loadArchivedConversations() {
|
||||
ensureBackgroundThread {
|
||||
val conversations = try {
|
||||
conversationsDB.getAllArchived().toMutableList() as ArrayList<Conversation>
|
||||
} catch (e: Exception) {
|
||||
ArrayList()
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
setupConversations(conversations)
|
||||
}
|
||||
}
|
||||
|
||||
bus = EventBus.getDefault()
|
||||
try {
|
||||
bus!!.register(this)
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeAll() {
|
||||
ConfirmationDialog(this, "", R.string.empty_archive_confirmation, R.string.yes, R.string.no) {
|
||||
removeAllArchivedConversations {
|
||||
loadArchivedConversations()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOrCreateConversationsAdapter(): ArchivedConversationsAdapter {
|
||||
var currAdapter = conversations_list.adapter
|
||||
if (currAdapter == null) {
|
||||
hideKeyboard()
|
||||
currAdapter = ArchivedConversationsAdapter(
|
||||
activity = this,
|
||||
recyclerView = conversations_list,
|
||||
onRefresh = { notifyDatasetChanged() },
|
||||
itemClick = { handleConversationClick(it) }
|
||||
)
|
||||
|
||||
conversations_list.adapter = currAdapter
|
||||
if (areSystemAnimationsEnabled) {
|
||||
conversations_list.scheduleLayoutAnimation()
|
||||
}
|
||||
}
|
||||
return currAdapter as ArchivedConversationsAdapter
|
||||
}
|
||||
|
||||
private fun setupConversations(conversations: ArrayList<Conversation>) {
|
||||
val sortedConversations = conversations.sortedWith(
|
||||
compareByDescending<Conversation> { config.pinnedConversations.contains(it.threadId.toString()) }
|
||||
.thenByDescending { it.date }
|
||||
).toMutableList() as ArrayList<Conversation>
|
||||
|
||||
showOrHidePlaceholder(conversations.isEmpty())
|
||||
updateOptionsMenu(conversations)
|
||||
|
||||
try {
|
||||
getOrCreateConversationsAdapter().apply {
|
||||
updateConversations(sortedConversations)
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun showOrHidePlaceholder(show: Boolean) {
|
||||
conversations_fastscroller.beGoneIf(show)
|
||||
no_conversations_placeholder.beVisibleIf(show)
|
||||
no_conversations_placeholder.text = getString(R.string.no_archived_conversations)
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun notifyDatasetChanged() {
|
||||
getOrCreateConversationsAdapter().notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private fun handleConversationClick(any: Any) {
|
||||
Intent(this, ThreadActivity::class.java).apply {
|
||||
val conversation = any as Conversation
|
||||
putExtra(THREAD_ID, conversation.threadId)
|
||||
putExtra(THREAD_TITLE, conversation.title)
|
||||
putExtra(WAS_PROTECTION_HANDLED, true)
|
||||
startActivity(this)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun refreshMessages(event: Events.RefreshMessages) {
|
||||
loadArchivedConversations()
|
||||
}
|
||||
}
|
||||
|
|
@ -179,6 +179,7 @@ class MainActivity : SimpleActivity() {
|
|||
R.id.export_messages -> tryToExportMessages()
|
||||
R.id.more_apps_from_us -> launchMoreAppsFromUsIntent()
|
||||
R.id.show_recycle_bin -> launchRecycleBin()
|
||||
R.id.show_archived -> launchArchivedConversations()
|
||||
R.id.settings -> launchSettings()
|
||||
R.id.about -> launchAbout()
|
||||
else -> return@setOnMenuItemClickListener false
|
||||
|
|
@ -253,7 +254,10 @@ class MainActivity : SimpleActivity() {
|
|||
handlePermission(PERMISSION_READ_CONTACTS) {
|
||||
handleNotificationPermission { granted ->
|
||||
if (!granted) {
|
||||
PermissionRequiredDialog(this, R.string.allow_notifications_incoming_messages)
|
||||
PermissionRequiredDialog(
|
||||
activity = this,
|
||||
textId = R.string.allow_notifications_incoming_messages,
|
||||
positiveActionCallback = { openNotificationSettings() })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -291,15 +295,21 @@ class MainActivity : SimpleActivity() {
|
|||
private fun getCachedConversations() {
|
||||
ensureBackgroundThread {
|
||||
val conversations = try {
|
||||
conversationsDB.getAll().toMutableList() as ArrayList<Conversation>
|
||||
conversationsDB.getNonArchived().toMutableList() as ArrayList<Conversation>
|
||||
} catch (e: Exception) {
|
||||
ArrayList()
|
||||
}
|
||||
|
||||
val archived = try {
|
||||
conversationsDB.getAllArchived()
|
||||
} catch (e: Exception) {
|
||||
listOf()
|
||||
}
|
||||
|
||||
updateUnreadCountBadge(conversations)
|
||||
runOnUiThread {
|
||||
setupConversations(conversations, cached = true)
|
||||
getNewConversations(conversations)
|
||||
getNewConversations((conversations + archived).toMutableList() as ArrayList<Conversation>)
|
||||
}
|
||||
conversations.forEach {
|
||||
clearExpiredScheduledMessages(it.threadId)
|
||||
|
|
@ -353,7 +363,7 @@ class MainActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
val allConversations = conversationsDB.getAll() as ArrayList<Conversation>
|
||||
val allConversations = conversationsDB.getNonArchived() as ArrayList<Conversation>
|
||||
runOnUiThread {
|
||||
setupConversations(allConversations)
|
||||
}
|
||||
|
|
@ -563,6 +573,11 @@ class MainActivity : SimpleActivity() {
|
|||
startActivity(Intent(applicationContext, RecycleBinConversationsActivity::class.java))
|
||||
}
|
||||
|
||||
private fun launchArchivedConversations() {
|
||||
hideKeyboard()
|
||||
startActivity(Intent(applicationContext, ArchivedConversationsActivity::class.java))
|
||||
}
|
||||
|
||||
private fun launchSettings() {
|
||||
hideKeyboard()
|
||||
startActivity(Intent(applicationContext, SettingsActivity::class.java))
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.simplemobiletools.smsmessenger.activities
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.AlarmManager
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
|
|
@ -44,6 +45,7 @@ import com.google.gson.Gson
|
|||
import com.google.gson.reflect.TypeToken
|
||||
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
||||
import com.simplemobiletools.commons.dialogs.FeatureLockedDialog
|
||||
import com.simplemobiletools.commons.dialogs.PermissionRequiredDialog
|
||||
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
|
|
@ -51,6 +53,7 @@ import com.simplemobiletools.commons.models.PhoneNumber
|
|||
import com.simplemobiletools.commons.models.RadioItem
|
||||
import com.simplemobiletools.commons.models.SimpleContact
|
||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
import com.simplemobiletools.smsmessenger.BuildConfig
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.adapters.AttachmentsAdapter
|
||||
import com.simplemobiletools.smsmessenger.adapters.AutoCompleteTextViewAdapter
|
||||
|
|
@ -244,6 +247,8 @@ class ThreadActivity : SimpleActivity() {
|
|||
val firstPhoneNumber = participants.firstOrNull()?.phoneNumbers?.firstOrNull()?.value
|
||||
thread_toolbar.menu.apply {
|
||||
findItem(R.id.delete).isVisible = threadItems.isNotEmpty()
|
||||
findItem(R.id.archive).isVisible = threadItems.isNotEmpty() && conversation?.isArchived == false
|
||||
findItem(R.id.unarchive).isVisible = threadItems.isNotEmpty() && conversation?.isArchived == true
|
||||
findItem(R.id.rename_conversation).isVisible = participants.size > 1 && conversation != null
|
||||
findItem(R.id.conversation_details).isVisible = conversation != null
|
||||
findItem(R.id.block_number).title = addLockedLabelIfNeeded(R.string.block_number)
|
||||
|
|
@ -268,6 +273,8 @@ class ThreadActivity : SimpleActivity() {
|
|||
when (menuItem.itemId) {
|
||||
R.id.block_number -> tryBlocking()
|
||||
R.id.delete -> askConfirmDelete()
|
||||
R.id.archive -> archiveConversation()
|
||||
R.id.unarchive -> unarchiveConversation()
|
||||
R.id.rename_conversation -> renameConversation()
|
||||
R.id.conversation_details -> showConversationDetails()
|
||||
R.id.add_number_to_contact -> addNumberToContact()
|
||||
|
|
@ -715,6 +722,25 @@ class ThreadActivity : SimpleActivity() {
|
|||
setupScheduleSendUi()
|
||||
}
|
||||
|
||||
private fun askForExactAlarmPermissionIfNeeded(callback: () -> Unit = {}) {
|
||||
if (isSPlus()) {
|
||||
val alarmManager: AlarmManager = getSystemService(ALARM_SERVICE) as AlarmManager
|
||||
if (alarmManager.canScheduleExactAlarms()) {
|
||||
callback()
|
||||
} else {
|
||||
PermissionRequiredDialog(
|
||||
activity = this,
|
||||
textId = R.string.allow_alarm_scheduled_messages,
|
||||
positiveActionCallback = {
|
||||
openRequestExactAlarmSettings(BuildConfig.APPLICATION_ID)
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAttachmentSizes() {
|
||||
messages.filter { it.attachment != null }.forEach { message ->
|
||||
message.attachment!!.attachments.forEach {
|
||||
|
|
@ -893,7 +919,8 @@ class ThreadActivity : SimpleActivity() {
|
|||
}
|
||||
|
||||
private fun askConfirmDelete() {
|
||||
ConfirmationDialog(this, getString(R.string.delete_whole_conversation_confirmation)) {
|
||||
val confirmationMessage = R.string.delete_whole_conversation_confirmation
|
||||
ConfirmationDialog(this, getString(confirmationMessage)) {
|
||||
ensureBackgroundThread {
|
||||
deleteConversation(threadId)
|
||||
runOnUiThread {
|
||||
|
|
@ -904,6 +931,26 @@ class ThreadActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun archiveConversation() {
|
||||
ensureBackgroundThread {
|
||||
updateConversationArchivedStatus(threadId, true)
|
||||
runOnUiThread {
|
||||
refreshMessages()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unarchiveConversation() {
|
||||
ensureBackgroundThread {
|
||||
updateConversationArchivedStatus(threadId, false)
|
||||
runOnUiThread {
|
||||
refreshMessages()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun dialNumber() {
|
||||
val phoneNumber = participants.first().phoneNumbers.first().normalizedNumber
|
||||
dialNumber(phoneNumber)
|
||||
|
|
@ -1126,7 +1173,7 @@ class ThreadActivity : SimpleActivity() {
|
|||
contacts = arrayListOf(contact),
|
||||
showExportingToast = false,
|
||||
) {
|
||||
if (it == VcfExporter.ExportResult.EXPORT_OK) {
|
||||
if (it == ExportResult.EXPORT_OK) {
|
||||
val vCardUri = getMyFileUri(outputFile)
|
||||
runOnUiThread {
|
||||
addAttachment(vCardUri)
|
||||
|
|
@ -1332,6 +1379,7 @@ class ThreadActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
messagesDB.insertOrUpdate(message)
|
||||
updateConversationArchivedStatus(message.threadId, false)
|
||||
}
|
||||
|
||||
// show selected contacts, properly split to new lines when appropriate
|
||||
|
|
@ -1534,10 +1582,12 @@ class ThreadActivity : SimpleActivity() {
|
|||
}
|
||||
|
||||
private fun launchScheduleSendDialog(originalDateTime: DateTime? = null) {
|
||||
ScheduleMessageDialog(this, originalDateTime) { newDateTime ->
|
||||
if (newDateTime != null) {
|
||||
scheduledDateTime = newDateTime
|
||||
showScheduleMessageDialog()
|
||||
askForExactAlarmPermissionIfNeeded {
|
||||
ScheduleMessageDialog(this, originalDateTime) { newDateTime ->
|
||||
if (newDateTime != null) {
|
||||
scheduledDateTime = newDateTime
|
||||
showScheduleMessageDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
package com.simplemobiletools.smsmessenger.adapters
|
||||
|
||||
import android.view.Menu
|
||||
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
||||
import com.simplemobiletools.commons.extensions.notificationManager
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||
import com.simplemobiletools.smsmessenger.extensions.deleteConversation
|
||||
import com.simplemobiletools.smsmessenger.extensions.updateConversationArchivedStatus
|
||||
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
|
||||
class ArchivedConversationsAdapter(
|
||||
activity: SimpleActivity, recyclerView: MyRecyclerView, onRefresh: () -> Unit, itemClick: (Any) -> Unit
|
||||
) : BaseConversationsAdapter(activity, recyclerView, onRefresh, itemClick) {
|
||||
override fun getActionMenuId() = R.menu.cab_archived_conversations
|
||||
|
||||
override fun prepareActionMode(menu: Menu) {}
|
||||
|
||||
override fun actionItemPressed(id: Int) {
|
||||
if (selectedKeys.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
when (id) {
|
||||
R.id.cab_delete -> askConfirmDelete()
|
||||
R.id.cab_unarchive -> unarchiveConversation()
|
||||
R.id.cab_select_all -> selectAll()
|
||||
}
|
||||
}
|
||||
|
||||
private fun askConfirmDelete() {
|
||||
val itemsCnt = selectedKeys.size
|
||||
val items = resources.getQuantityString(R.plurals.delete_conversations, itemsCnt, itemsCnt)
|
||||
|
||||
val baseString = R.string.deletion_confirmation
|
||||
val question = String.format(resources.getString(baseString), items)
|
||||
|
||||
ConfirmationDialog(activity, question) {
|
||||
ensureBackgroundThread {
|
||||
deleteConversations()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteConversations() {
|
||||
if (selectedKeys.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val conversationsToRemove = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
||||
conversationsToRemove.forEach {
|
||||
activity.deleteConversation(it.threadId)
|
||||
activity.notificationManager.cancel(it.threadId.hashCode())
|
||||
}
|
||||
|
||||
removeConversationsFromList(conversationsToRemove)
|
||||
}
|
||||
|
||||
private fun unarchiveConversation() {
|
||||
if (selectedKeys.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
ensureBackgroundThread {
|
||||
val conversationsToUnarchive = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
||||
conversationsToUnarchive.forEach {
|
||||
activity.updateConversationArchivedStatus(it.threadId, false)
|
||||
}
|
||||
|
||||
removeConversationsFromList(conversationsToUnarchive)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeConversationsFromList(removedConversations: List<Conversation>) {
|
||||
val newList = try {
|
||||
currentList.toMutableList().apply { removeAll(removedConversations) }
|
||||
} catch (ignored: Exception) {
|
||||
currentList.toMutableList()
|
||||
}
|
||||
|
||||
activity.runOnUiThread {
|
||||
if (newList.none { selectedKeys.contains(it.hashCode()) }) {
|
||||
refreshMessages()
|
||||
finishActMode()
|
||||
} else {
|
||||
submitList(newList)
|
||||
if (newList.isEmpty()) {
|
||||
refreshMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
package com.simplemobiletools.smsmessenger.adapters
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.os.Parcelable
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
|
||||
import com.simplemobiletools.commons.adapters.MyRecyclerViewListAdapter
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
import kotlinx.android.synthetic.main.item_conversation.view.*
|
||||
|
||||
@Suppress("LeakingThis")
|
||||
abstract class BaseConversationsAdapter(
|
||||
activity: SimpleActivity, recyclerView: MyRecyclerView, onRefresh: () -> Unit, itemClick: (Any) -> Unit
|
||||
) : MyRecyclerViewListAdapter<Conversation>(activity, recyclerView, ConversationDiffCallback(), itemClick, onRefresh),
|
||||
RecyclerViewFastScroller.OnPopupTextUpdate {
|
||||
private var fontSize = activity.getTextSize()
|
||||
private var drafts = HashMap<Long, String?>()
|
||||
|
||||
private var recyclerViewState: Parcelable? = null
|
||||
|
||||
init {
|
||||
setupDragListener(true)
|
||||
ensureBackgroundThread {
|
||||
fetchDrafts(drafts)
|
||||
}
|
||||
setHasStableIds(true)
|
||||
|
||||
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() = restoreRecyclerViewState()
|
||||
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) = restoreRecyclerViewState()
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) = restoreRecyclerViewState()
|
||||
})
|
||||
}
|
||||
|
||||
fun updateFontSize() {
|
||||
fontSize = activity.getTextSize()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun updateConversations(newConversations: ArrayList<Conversation>, commitCallback: (() -> Unit)? = null) {
|
||||
saveRecyclerViewState()
|
||||
submitList(newConversations.toList(), commitCallback)
|
||||
}
|
||||
|
||||
fun updateDrafts() {
|
||||
ensureBackgroundThread {
|
||||
val newDrafts = HashMap<Long, String?>()
|
||||
fetchDrafts(newDrafts)
|
||||
if (drafts.hashCode() != newDrafts.hashCode()) {
|
||||
drafts = newDrafts
|
||||
activity.runOnUiThread {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelectableItemCount() = itemCount
|
||||
|
||||
protected fun getSelectedItems() = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
||||
|
||||
override fun getIsItemSelectable(position: Int) = true
|
||||
|
||||
override fun getItemSelectionKey(position: Int) = currentList.getOrNull(position)?.hashCode()
|
||||
|
||||
override fun getItemKeyPosition(key: Int) = currentList.indexOfFirst { it.hashCode() == key }
|
||||
|
||||
override fun onActionModeCreated() {}
|
||||
|
||||
override fun onActionModeDestroyed() {}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_conversation, parent)
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val conversation = getItem(position)
|
||||
holder.bindView(conversation, allowSingleClick = true, allowLongClick = true) { itemView, _ ->
|
||||
setupView(itemView, conversation)
|
||||
}
|
||||
bindViewHolder(holder)
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int) = getItem(position).threadId
|
||||
|
||||
override fun onViewRecycled(holder: ViewHolder) {
|
||||
super.onViewRecycled(holder)
|
||||
if (!activity.isDestroyed && !activity.isFinishing) {
|
||||
Glide.with(activity).clear(holder.itemView.conversation_image)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchDrafts(drafts: HashMap<Long, String?>) {
|
||||
drafts.clear()
|
||||
for ((threadId, draft) in activity.getAllDrafts()) {
|
||||
drafts[threadId] = draft
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupView(view: View, conversation: Conversation) {
|
||||
view.apply {
|
||||
setupViewBackground(activity)
|
||||
val smsDraft = drafts[conversation.threadId]
|
||||
draft_indicator.beVisibleIf(smsDraft != null)
|
||||
draft_indicator.setTextColor(properPrimaryColor)
|
||||
|
||||
pin_indicator.beVisibleIf(activity.config.pinnedConversations.contains(conversation.threadId.toString()))
|
||||
pin_indicator.applyColorFilter(textColor)
|
||||
|
||||
conversation_frame.isSelected = selectedKeys.contains(conversation.hashCode())
|
||||
|
||||
conversation_address.apply {
|
||||
text = conversation.title
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize * 1.2f)
|
||||
}
|
||||
|
||||
conversation_body_short.apply {
|
||||
text = smsDraft ?: conversation.snippet
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize * 0.9f)
|
||||
}
|
||||
|
||||
conversation_date.apply {
|
||||
text = conversation.date.formatDateOrTime(context, true, false)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize * 0.8f)
|
||||
}
|
||||
|
||||
val style = if (conversation.read) {
|
||||
conversation_body_short.alpha = 0.7f
|
||||
if (conversation.isScheduled) Typeface.ITALIC else Typeface.NORMAL
|
||||
} else {
|
||||
conversation_body_short.alpha = 1f
|
||||
if (conversation.isScheduled) Typeface.BOLD_ITALIC else Typeface.BOLD
|
||||
|
||||
}
|
||||
conversation_address.setTypeface(null, style)
|
||||
conversation_body_short.setTypeface(null, style)
|
||||
|
||||
arrayListOf<TextView>(conversation_address, conversation_body_short, conversation_date).forEach {
|
||||
it.setTextColor(textColor)
|
||||
}
|
||||
|
||||
// at group conversations we use an icon as the placeholder, not any letter
|
||||
val placeholder = if (conversation.isGroupConversation) {
|
||||
SimpleContactsHelper(context).getColoredGroupIcon(conversation.title)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
SimpleContactsHelper(context).loadContactImage(conversation.photoUri, conversation_image, conversation.title, placeholder)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChange(position: Int) = currentList.getOrNull(position)?.title ?: ""
|
||||
|
||||
private fun saveRecyclerViewState() {
|
||||
recyclerViewState = recyclerView.layoutManager?.onSaveInstanceState()
|
||||
}
|
||||
|
||||
private fun restoreRecyclerViewState() {
|
||||
recyclerView.layoutManager?.onRestoreInstanceState(recyclerViewState)
|
||||
}
|
||||
|
||||
private class ConversationDiffCallback : DiffUtil.ItemCallback<Conversation>() {
|
||||
override fun areItemsTheSame(oldItem: Conversation, newItem: Conversation): Boolean {
|
||||
return Conversation.areItemsTheSame(oldItem, newItem)
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: Conversation, newItem: Conversation): Boolean {
|
||||
return Conversation.areContentsTheSame(oldItem, newItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +1,12 @@
|
|||
package com.simplemobiletools.smsmessenger.adapters
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.os.Parcelable
|
||||
import android.text.TextUtils
|
||||
import android.util.TypedValue
|
||||
import android.view.Menu
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
|
||||
import com.simplemobiletools.commons.adapters.MyRecyclerViewListAdapter
|
||||
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
||||
import com.simplemobiletools.commons.dialogs.FeatureLockedDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.KEY_PHONE
|
||||
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.commons.helpers.isNougatPlus
|
||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
|
|
@ -29,31 +17,10 @@ import com.simplemobiletools.smsmessenger.extensions.*
|
|||
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
||||
import com.simplemobiletools.smsmessenger.messaging.isShortCodeWithLetters
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
import kotlinx.android.synthetic.main.item_conversation.view.*
|
||||
|
||||
class ConversationsAdapter(
|
||||
activity: SimpleActivity, recyclerView: MyRecyclerView, onRefresh: () -> Unit, itemClick: (Any) -> Unit
|
||||
) : MyRecyclerViewListAdapter<Conversation>(activity, recyclerView, ConversationDiffCallback(), itemClick, onRefresh),
|
||||
RecyclerViewFastScroller.OnPopupTextUpdate {
|
||||
private var fontSize = activity.getTextSize()
|
||||
private var drafts = HashMap<Long, String?>()
|
||||
|
||||
private var recyclerViewState: Parcelable? = null
|
||||
|
||||
init {
|
||||
setupDragListener(true)
|
||||
ensureBackgroundThread {
|
||||
fetchDrafts(drafts)
|
||||
}
|
||||
setHasStableIds(true)
|
||||
|
||||
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() = restoreRecyclerViewState()
|
||||
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) = restoreRecyclerViewState()
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) = restoreRecyclerViewState()
|
||||
})
|
||||
}
|
||||
|
||||
) : BaseConversationsAdapter(activity, recyclerView, onRefresh, itemClick) {
|
||||
override fun getActionMenuId() = R.menu.cab_conversations
|
||||
|
||||
override fun prepareActionMode(menu: Menu) {
|
||||
|
|
@ -86,6 +53,7 @@ class ConversationsAdapter(
|
|||
R.id.cab_dial_number -> dialNumber()
|
||||
R.id.cab_copy_number -> copyNumberToClipboard()
|
||||
R.id.cab_delete -> askConfirmDelete()
|
||||
R.id.cab_archive -> askConfirmArchive()
|
||||
R.id.cab_rename_conversation -> renameConversation(getSelectedItems().first())
|
||||
R.id.cab_mark_as_read -> markAsRead()
|
||||
R.id.cab_mark_as_unread -> markAsUnread()
|
||||
|
|
@ -95,37 +63,6 @@ class ConversationsAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getSelectableItemCount() = itemCount
|
||||
|
||||
override fun getIsItemSelectable(position: Int) = true
|
||||
|
||||
override fun getItemSelectionKey(position: Int) = currentList.getOrNull(position)?.hashCode()
|
||||
|
||||
override fun getItemKeyPosition(key: Int) = currentList.indexOfFirst { it.hashCode() == key }
|
||||
|
||||
override fun onActionModeCreated() {}
|
||||
|
||||
override fun onActionModeDestroyed() {}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_conversation, parent)
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val conversation = getItem(position)
|
||||
holder.bindView(conversation, allowSingleClick = true, allowLongClick = true) { itemView, _ ->
|
||||
setupView(itemView, conversation)
|
||||
}
|
||||
bindViewHolder(holder)
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int) = getItem(position).threadId
|
||||
|
||||
override fun onViewRecycled(holder: ViewHolder) {
|
||||
super.onViewRecycled(holder)
|
||||
if (!activity.isDestroyed && !activity.isFinishing) {
|
||||
Glide.with(activity).clear(holder.itemView.conversation_image)
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryBlocking() {
|
||||
if (activity.isOrWasThankYouInstalled()) {
|
||||
askConfirmBlock()
|
||||
|
|
@ -191,6 +128,50 @@ class ConversationsAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun askConfirmArchive() {
|
||||
val itemsCnt = selectedKeys.size
|
||||
val items = resources.getQuantityString(R.plurals.delete_conversations, itemsCnt, itemsCnt)
|
||||
|
||||
val baseString = R.string.archive_confirmation
|
||||
val question = String.format(resources.getString(baseString), items)
|
||||
|
||||
ConfirmationDialog(activity, question) {
|
||||
ensureBackgroundThread {
|
||||
archiveConversations()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun archiveConversations() {
|
||||
if (selectedKeys.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val conversationsToRemove = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
||||
conversationsToRemove.forEach {
|
||||
activity.updateConversationArchivedStatus(it.threadId, true)
|
||||
activity.notificationManager.cancel(it.threadId.hashCode())
|
||||
}
|
||||
|
||||
val newList = try {
|
||||
currentList.toMutableList().apply { removeAll(conversationsToRemove) }
|
||||
} catch (ignored: Exception) {
|
||||
currentList.toMutableList()
|
||||
}
|
||||
|
||||
activity.runOnUiThread {
|
||||
if (newList.none { selectedKeys.contains(it.hashCode()) }) {
|
||||
refreshMessages()
|
||||
finishActMode()
|
||||
} else {
|
||||
submitList(newList)
|
||||
if (newList.isEmpty()) {
|
||||
refreshMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteConversations() {
|
||||
if (selectedKeys.isEmpty()) {
|
||||
return
|
||||
|
|
@ -276,8 +257,6 @@ class ConversationsAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getSelectedItems() = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
||||
|
||||
private fun pinConversation(pin: Boolean) {
|
||||
val conversations = getSelectedItems()
|
||||
if (conversations.isEmpty()) {
|
||||
|
|
@ -303,113 +282,10 @@ class ConversationsAdapter(
|
|||
menu.findItem(R.id.cab_unpin_conversation).isVisible = selectedConversations.any { pinnedConversations.contains(it.threadId.toString()) }
|
||||
}
|
||||
|
||||
private fun fetchDrafts(drafts: HashMap<Long, String?>) {
|
||||
drafts.clear()
|
||||
for ((threadId, draft) in activity.getAllDrafts()) {
|
||||
drafts[threadId] = draft
|
||||
}
|
||||
}
|
||||
|
||||
fun updateFontSize() {
|
||||
fontSize = activity.getTextSize()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun updateConversations(newConversations: ArrayList<Conversation>, commitCallback: (() -> Unit)? = null) {
|
||||
saveRecyclerViewState()
|
||||
submitList(newConversations.toList(), commitCallback)
|
||||
}
|
||||
|
||||
fun updateDrafts() {
|
||||
ensureBackgroundThread {
|
||||
val newDrafts = HashMap<Long, String?>()
|
||||
fetchDrafts(newDrafts)
|
||||
if (drafts.hashCode() != newDrafts.hashCode()) {
|
||||
drafts = newDrafts
|
||||
activity.runOnUiThread {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupView(view: View, conversation: Conversation) {
|
||||
view.apply {
|
||||
setupViewBackground(activity)
|
||||
val smsDraft = drafts[conversation.threadId]
|
||||
draft_indicator.beVisibleIf(smsDraft != null)
|
||||
draft_indicator.setTextColor(properPrimaryColor)
|
||||
|
||||
pin_indicator.beVisibleIf(activity.config.pinnedConversations.contains(conversation.threadId.toString()))
|
||||
pin_indicator.applyColorFilter(textColor)
|
||||
|
||||
conversation_frame.isSelected = selectedKeys.contains(conversation.hashCode())
|
||||
|
||||
conversation_address.apply {
|
||||
text = conversation.title
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize * 1.2f)
|
||||
}
|
||||
|
||||
conversation_body_short.apply {
|
||||
text = smsDraft ?: conversation.snippet
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize * 0.9f)
|
||||
}
|
||||
|
||||
conversation_date.apply {
|
||||
text = conversation.date.formatDateOrTime(context, true, false)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize * 0.8f)
|
||||
}
|
||||
|
||||
val style = if (conversation.read) {
|
||||
conversation_body_short.alpha = 0.7f
|
||||
if (conversation.isScheduled) Typeface.ITALIC else Typeface.NORMAL
|
||||
} else {
|
||||
conversation_body_short.alpha = 1f
|
||||
if (conversation.isScheduled) Typeface.BOLD_ITALIC else Typeface.BOLD
|
||||
|
||||
}
|
||||
conversation_address.setTypeface(null, style)
|
||||
conversation_body_short.setTypeface(null, style)
|
||||
|
||||
arrayListOf<TextView>(conversation_address, conversation_body_short, conversation_date).forEach {
|
||||
it.setTextColor(textColor)
|
||||
}
|
||||
|
||||
// at group conversations we use an icon as the placeholder, not any letter
|
||||
val placeholder = if (conversation.isGroupConversation) {
|
||||
SimpleContactsHelper(context).getColoredGroupIcon(conversation.title)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
SimpleContactsHelper(context).loadContactImage(conversation.photoUri, conversation_image, conversation.title, placeholder)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChange(position: Int) = currentList.getOrNull(position)?.title ?: ""
|
||||
|
||||
private fun refreshConversations() {
|
||||
activity.runOnUiThread {
|
||||
refreshMessages()
|
||||
finishActMode()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveRecyclerViewState() {
|
||||
recyclerViewState = recyclerView.layoutManager?.onSaveInstanceState()
|
||||
}
|
||||
|
||||
private fun restoreRecyclerViewState() {
|
||||
recyclerView.layoutManager?.onRestoreInstanceState(recyclerViewState)
|
||||
}
|
||||
|
||||
private class ConversationDiffCallback : DiffUtil.ItemCallback<Conversation>() {
|
||||
override fun areItemsTheSame(oldItem: Conversation, newItem: Conversation): Boolean {
|
||||
return Conversation.areItemsTheSame(oldItem, newItem)
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: Conversation, newItem: Conversation): Boolean {
|
||||
return Conversation.areContentsTheSame(oldItem, newItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ abstract class MessagesDatabase : RoomDatabase() {
|
|||
private val MIGRATION_7_8 = object : Migration(7, 8) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.apply {
|
||||
execSQL("ALTER TABLE conversations ADD COLUMN archived INTEGER NOT NULL DEFAULT 0")
|
||||
execSQL("CREATE TABLE IF NOT EXISTS `recycle_bin_messages` (`id` INTEGER PRIMARY KEY, `deleted_ts` INTEGER NOT NULL)")
|
||||
execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_recycle_bin_messages_id` ON `recycle_bin_messages` (`id`)")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,7 +268,8 @@ fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList<
|
|||
Threads.SNIPPET,
|
||||
Threads.DATE,
|
||||
Threads.READ,
|
||||
Threads.RECIPIENT_IDS
|
||||
Threads.RECIPIENT_IDS,
|
||||
Threads.ARCHIVED
|
||||
)
|
||||
|
||||
var selection = "${Threads.MESSAGE_COUNT} > ?"
|
||||
|
|
@ -307,7 +308,8 @@ fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList<
|
|||
val photoUri = if (phoneNumbers.size == 1) simpleContactHelper.getPhotoUriFromPhoneNumber(phoneNumbers.first()) else ""
|
||||
val isGroupConversation = phoneNumbers.size > 1
|
||||
val read = cursor.getIntValue(Threads.READ) == 1
|
||||
val conversation = Conversation(id, snippet, date.toInt(), read, title, photoUri, isGroupConversation, phoneNumbers.first())
|
||||
val archived = cursor.getIntValue(Threads.ARCHIVED) == 1
|
||||
val conversation = Conversation(id, snippet, date.toInt(), read, title, photoUri, isGroupConversation, phoneNumbers.first(), isArchived = archived)
|
||||
conversations.add(conversation)
|
||||
}
|
||||
|
||||
|
|
@ -620,6 +622,20 @@ fun Context.insertNewSMS(
|
|||
}
|
||||
}
|
||||
|
||||
fun Context.removeAllArchivedConversations(callback: (() -> Unit)? = null) {
|
||||
ensureBackgroundThread {
|
||||
try {
|
||||
for (conversation in conversationsDB.getAllArchived()) {
|
||||
deleteConversation(conversation.threadId)
|
||||
}
|
||||
toast(R.string.archive_emptied_successfully)
|
||||
callback?.invoke()
|
||||
} catch (e: Exception) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.deleteConversation(threadId: Long) {
|
||||
var uri = Sms.CONTENT_URI
|
||||
val selection = "${Sms.THREAD_ID} = ?"
|
||||
|
|
@ -671,6 +687,21 @@ fun Context.moveMessageToRecycleBin(id: Long) {
|
|||
}
|
||||
}
|
||||
|
||||
fun Context.updateConversationArchivedStatus(threadId: Long, archived: Boolean) {
|
||||
val uri = Threads.CONTENT_URI
|
||||
val values = ContentValues().apply {
|
||||
put(Threads.ARCHIVED, archived)
|
||||
}
|
||||
val selection = "${Threads._ID} = ?"
|
||||
val selectionArgs = arrayOf(threadId.toString())
|
||||
contentResolver.update(uri, values, selection, selectionArgs)
|
||||
if (archived) {
|
||||
conversationsDB.moveToArchive(threadId)
|
||||
} else {
|
||||
conversationsDB.unarchive(threadId)
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.deleteMessage(id: Long, isMMS: Boolean) {
|
||||
val uri = if (isMMS) Mms.CONTENT_URI else Sms.CONTENT_URI
|
||||
val selection = "${Sms._ID} = ?"
|
||||
|
|
@ -999,7 +1030,8 @@ fun Context.createTemporaryThread(message: Message, threadId: Long = generateRan
|
|||
isGroupConversation = addresses.size > 1,
|
||||
phoneNumber = addresses.first(),
|
||||
isScheduled = true,
|
||||
usesCustomTitle = cachedConv?.usesCustomTitle == true
|
||||
usesCustomTitle = cachedConv?.usesCustomTitle == true,
|
||||
isArchived = false
|
||||
)
|
||||
try {
|
||||
conversationsDB.insertOrUpdate(conversation)
|
||||
|
|
|
|||
|
|
@ -111,4 +111,8 @@ class Config(context: Context) : BaseConfig(context) {
|
|||
var lastRecycleBinCheck: Long
|
||||
get() = prefs.getLong(LAST_RECYCLE_BIN_CHECK, 0L)
|
||||
set(lastRecycleBinCheck) = prefs.edit().putLong(LAST_RECYCLE_BIN_CHECK, lastRecycleBinCheck).apply()
|
||||
|
||||
var useArchive: Boolean
|
||||
get() = prefs.getBoolean(USE_ARCHIVE, false)
|
||||
set(useArchive) = prefs.edit().putBoolean(USE_ARCHIVE, useArchive).apply()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ const val IS_MMS = "is_mms"
|
|||
const val MESSAGE_ID = "message_id"
|
||||
const val USE_RECYCLE_BIN = "use_recycle_bin"
|
||||
const val LAST_RECYCLE_BIN_CHECK = "last_recycle_bin_check"
|
||||
const val USE_ARCHIVE = "use_archive"
|
||||
|
||||
private const val PATH = "com.simplemobiletools.smsmessenger.action."
|
||||
const val MARK_AS_READ = PATH + "mark_as_read"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
package com.simplemobiletools.smsmessenger.interfaces
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.*
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
|
||||
@Dao
|
||||
|
|
@ -11,8 +8,11 @@ interface ConversationsDao {
|
|||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertOrUpdate(conversation: Conversation): Long
|
||||
|
||||
@Query("SELECT * FROM conversations")
|
||||
fun getAll(): List<Conversation>
|
||||
@Query("SELECT * FROM conversations WHERE archived = 0")
|
||||
fun getNonArchived(): List<Conversation>
|
||||
|
||||
@Query("SELECT * FROM conversations WHERE archived = 1")
|
||||
fun getAllArchived(): List<Conversation>
|
||||
|
||||
@Query("SELECT * FROM conversations WHERE (SELECT COUNT(*) FROM messages LEFT OUTER JOIN recycle_bin_messages ON messages.id = recycle_bin_messages.id WHERE recycle_bin_messages.id IS NOT NULL AND messages.thread_id = conversations.thread_id) > 0")
|
||||
fun getAllWithMessagesInRecycleBin(): List<Conversation>
|
||||
|
|
@ -32,6 +32,12 @@ interface ConversationsDao {
|
|||
@Query("UPDATE conversations SET read = 0 WHERE thread_id = :threadId")
|
||||
fun markUnread(threadId: Long)
|
||||
|
||||
@Query("UPDATE conversations SET archived = 1 WHERE thread_id = :threadId")
|
||||
fun moveToArchive(threadId: Long)
|
||||
|
||||
@Query("UPDATE conversations SET archived = 0 WHERE thread_id = :threadId")
|
||||
fun unarchive(threadId: Long)
|
||||
|
||||
@Query("DELETE FROM conversations WHERE thread_id = :threadId")
|
||||
fun deleteThreadId(threadId: Long)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
tableName = "archived_conversations",
|
||||
indices = [(Index(value = ["thread_id"], unique = true))]
|
||||
)
|
||||
data class ArchivedConversation(
|
||||
@PrimaryKey @ColumnInfo(name = "thread_id") var threadId: Long,
|
||||
@ColumnInfo(name = "deleted_ts") var deletedTs: Long
|
||||
)
|
||||
|
|
@ -16,7 +16,8 @@ data class Conversation(
|
|||
@ColumnInfo(name = "is_group_conversation") var isGroupConversation: Boolean,
|
||||
@ColumnInfo(name = "phone_number") var phoneNumber: String,
|
||||
@ColumnInfo(name = "is_scheduled") var isScheduled: Boolean = false,
|
||||
@ColumnInfo(name = "uses_custom_title") var usesCustomTitle: Boolean = false
|
||||
@ColumnInfo(name = "uses_custom_title") var usesCustomTitle: Boolean = false,
|
||||
@ColumnInfo(name = "archived") var isArchived: Boolean = false
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ class SmsReceiver : BroadcastReceiver() {
|
|||
subscriptionId
|
||||
)
|
||||
context.messagesDB.insertOrUpdate(message)
|
||||
context.updateConversationArchivedStatus(threadId, false)
|
||||
refreshMessages()
|
||||
context.showReceivedMessageNotification(newMessageId, address, body, threadId, bitmap)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue