Add support for archiving conversations
Archiving messages currently acts like recycle bin in Simple Gallery, meaning that after 30 days, conversations will be deleted permanently. Any updates to the conversation (new messages) removes it from archive. This closes #177
This commit is contained in:
parent
9942fb788a
commit
47861f605d
|
@ -51,6 +51,13 @@
|
||||||
android:configChanges="orientation"
|
android:configChanges="orientation"
|
||||||
android:exported="true" />
|
android:exported="true" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".activities.ArchivedConversationsActivity"
|
||||||
|
android:configChanges="orientation"
|
||||||
|
android:exported="true"
|
||||||
|
android:label="Archived Conversations"
|
||||||
|
android:parentActivityName=".activities.MainActivity" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.ThreadActivity"
|
android:name=".activities.ThreadActivity"
|
||||||
android:configChanges="orientation"
|
android:configChanges="orientation"
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
package com.simplemobiletools.smsmessenger.activities
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
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 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() {
|
||||||
|
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())
|
||||||
|
|
||||||
|
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_conversations_found)
|
||||||
|
}
|
||||||
|
|
||||||
|
@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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,6 +64,7 @@ class MainActivity : SimpleActivity() {
|
||||||
updateMaterialActivityViews(main_coordinator, conversations_list, useTransparentNavigation = true, useTopSearchMenu = true)
|
updateMaterialActivityViews(main_coordinator, conversations_list, useTransparentNavigation = true, useTopSearchMenu = true)
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
|
checkAndDeleteOldArchivedConversations()
|
||||||
handleAppPasswordProtection {
|
handleAppPasswordProtection {
|
||||||
wasProtectionHandled = it
|
wasProtectionHandled = it
|
||||||
if (it) {
|
if (it) {
|
||||||
|
@ -177,6 +178,7 @@ class MainActivity : SimpleActivity() {
|
||||||
R.id.import_messages -> tryImportMessages()
|
R.id.import_messages -> tryImportMessages()
|
||||||
R.id.export_messages -> tryToExportMessages()
|
R.id.export_messages -> tryToExportMessages()
|
||||||
R.id.more_apps_from_us -> launchMoreAppsFromUsIntent()
|
R.id.more_apps_from_us -> launchMoreAppsFromUsIntent()
|
||||||
|
R.id.show_archived -> launchArchivedConversations()
|
||||||
R.id.settings -> launchSettings()
|
R.id.settings -> launchSettings()
|
||||||
R.id.about -> launchAbout()
|
R.id.about -> launchAbout()
|
||||||
else -> return@setOnMenuItemClickListener false
|
else -> return@setOnMenuItemClickListener false
|
||||||
|
@ -289,15 +291,20 @@ class MainActivity : SimpleActivity() {
|
||||||
private fun getCachedConversations() {
|
private fun getCachedConversations() {
|
||||||
ensureBackgroundThread {
|
ensureBackgroundThread {
|
||||||
val conversations = try {
|
val conversations = try {
|
||||||
conversationsDB.getAll().toMutableList() as ArrayList<Conversation>
|
conversationsDB.getNonArchived().toMutableList() as ArrayList<Conversation>
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
ArrayList()
|
ArrayList()
|
||||||
}
|
}
|
||||||
|
val archived = try {
|
||||||
|
conversationsDB.getAllArchived()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
listOf()
|
||||||
|
}
|
||||||
|
|
||||||
updateUnreadCountBadge(conversations)
|
updateUnreadCountBadge(conversations)
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
setupConversations(conversations, cached = true)
|
setupConversations(conversations, cached = true)
|
||||||
getNewConversations(conversations)
|
getNewConversations((conversations + archived).toMutableList() as ArrayList<Conversation>)
|
||||||
}
|
}
|
||||||
conversations.forEach {
|
conversations.forEach {
|
||||||
clearExpiredScheduledMessages(it.threadId)
|
clearExpiredScheduledMessages(it.threadId)
|
||||||
|
@ -351,7 +358,7 @@ class MainActivity : SimpleActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val allConversations = conversationsDB.getAll() as ArrayList<Conversation>
|
val allConversations = conversationsDB.getNonArchived() as ArrayList<Conversation>
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
setupConversations(allConversations)
|
setupConversations(allConversations)
|
||||||
}
|
}
|
||||||
|
@ -556,6 +563,11 @@ class MainActivity : SimpleActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun launchArchivedConversations() {
|
||||||
|
hideKeyboard()
|
||||||
|
startActivity(Intent(applicationContext, ArchivedConversationsActivity::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
private fun launchSettings() {
|
private fun launchSettings() {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
startActivity(Intent(applicationContext, SettingsActivity::class.java))
|
startActivity(Intent(applicationContext, SettingsActivity::class.java))
|
||||||
|
|
|
@ -11,12 +11,15 @@ import com.simplemobiletools.commons.helpers.*
|
||||||
import com.simplemobiletools.commons.models.RadioItem
|
import com.simplemobiletools.commons.models.RadioItem
|
||||||
import com.simplemobiletools.smsmessenger.R
|
import com.simplemobiletools.smsmessenger.R
|
||||||
import com.simplemobiletools.smsmessenger.extensions.config
|
import com.simplemobiletools.smsmessenger.extensions.config
|
||||||
|
import com.simplemobiletools.smsmessenger.extensions.conversationsDB
|
||||||
|
import com.simplemobiletools.smsmessenger.extensions.removeAllArchivedConversations
|
||||||
import com.simplemobiletools.smsmessenger.helpers.*
|
import com.simplemobiletools.smsmessenger.helpers.*
|
||||||
import kotlinx.android.synthetic.main.activity_settings.*
|
import kotlinx.android.synthetic.main.activity_settings.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class SettingsActivity : SimpleActivity() {
|
class SettingsActivity : SimpleActivity() {
|
||||||
private var blockedNumbersAtPause = -1
|
private var blockedNumbersAtPause = -1
|
||||||
|
private var recycleBinConversations = 0
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
isMaterialActivity = true
|
isMaterialActivity = true
|
||||||
|
@ -47,6 +50,8 @@ class SettingsActivity : SimpleActivity() {
|
||||||
setupGroupMessageAsMMS()
|
setupGroupMessageAsMMS()
|
||||||
setupLockScreenVisibility()
|
setupLockScreenVisibility()
|
||||||
setupMMSFileSizeLimit()
|
setupMMSFileSizeLimit()
|
||||||
|
setupUseRecycleBin()
|
||||||
|
setupEmptyRecycleBin()
|
||||||
setupAppPasswordProtection()
|
setupAppPasswordProtection()
|
||||||
updateTextColors(settings_nested_scrollview)
|
updateTextColors(settings_nested_scrollview)
|
||||||
|
|
||||||
|
@ -59,6 +64,7 @@ class SettingsActivity : SimpleActivity() {
|
||||||
settings_general_settings_label,
|
settings_general_settings_label,
|
||||||
settings_outgoing_messages_label,
|
settings_outgoing_messages_label,
|
||||||
settings_notifications_label,
|
settings_notifications_label,
|
||||||
|
settings_recycle_bin_label,
|
||||||
settings_security_label
|
settings_security_label
|
||||||
).forEach {
|
).forEach {
|
||||||
it.setTextColor(getProperPrimaryColor())
|
it.setTextColor(getProperPrimaryColor())
|
||||||
|
@ -244,6 +250,43 @@ class SettingsActivity : SimpleActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setupUseRecycleBin() {
|
||||||
|
updateRecycleBinButtons()
|
||||||
|
settings_use_recycle_bin.isChecked = config.useArchive
|
||||||
|
settings_use_recycle_bin_holder.setOnClickListener {
|
||||||
|
settings_use_recycle_bin.toggle()
|
||||||
|
config.useArchive = settings_use_recycle_bin.isChecked
|
||||||
|
updateRecycleBinButtons()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateRecycleBinButtons() {
|
||||||
|
settings_empty_recycle_bin_holder.beVisibleIf(config.useArchive)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupEmptyRecycleBin() {
|
||||||
|
ensureBackgroundThread {
|
||||||
|
recycleBinConversations = conversationsDB.getArchivedCount()
|
||||||
|
runOnUiThread {
|
||||||
|
settings_empty_recycle_bin_size.text =
|
||||||
|
resources.getQuantityString(R.plurals.delete_conversations, recycleBinConversations, recycleBinConversations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings_empty_recycle_bin_holder.setOnClickListener {
|
||||||
|
if (recycleBinConversations == 0) {
|
||||||
|
toast(R.string.recycle_bin_empty)
|
||||||
|
} else {
|
||||||
|
ConfirmationDialog(this, "", R.string.empty_recycle_bin_confirmation, R.string.yes, R.string.no) {
|
||||||
|
removeAllArchivedConversations()
|
||||||
|
recycleBinConversations = 0
|
||||||
|
settings_empty_recycle_bin_size.text =
|
||||||
|
resources.getQuantityString(R.plurals.delete_conversations, recycleBinConversations, recycleBinConversations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupAppPasswordProtection() {
|
private fun setupAppPasswordProtection() {
|
||||||
settings_app_password_protection.isChecked = config.isAppPasswordProtectionOn
|
settings_app_password_protection.isChecked = config.isAppPasswordProtectionOn
|
||||||
settings_app_password_protection_holder.setOnClickListener {
|
settings_app_password_protection_holder.setOnClickListener {
|
||||||
|
|
|
@ -55,6 +55,7 @@ import com.simplemobiletools.smsmessenger.R
|
||||||
import com.simplemobiletools.smsmessenger.adapters.AttachmentsAdapter
|
import com.simplemobiletools.smsmessenger.adapters.AttachmentsAdapter
|
||||||
import com.simplemobiletools.smsmessenger.adapters.AutoCompleteTextViewAdapter
|
import com.simplemobiletools.smsmessenger.adapters.AutoCompleteTextViewAdapter
|
||||||
import com.simplemobiletools.smsmessenger.adapters.ThreadAdapter
|
import com.simplemobiletools.smsmessenger.adapters.ThreadAdapter
|
||||||
|
import com.simplemobiletools.smsmessenger.dialogs.DeleteConfirmationDialog
|
||||||
import com.simplemobiletools.smsmessenger.dialogs.InvalidNumberDialog
|
import com.simplemobiletools.smsmessenger.dialogs.InvalidNumberDialog
|
||||||
import com.simplemobiletools.smsmessenger.dialogs.RenameConversationDialog
|
import com.simplemobiletools.smsmessenger.dialogs.RenameConversationDialog
|
||||||
import com.simplemobiletools.smsmessenger.dialogs.ScheduleMessageDialog
|
import com.simplemobiletools.smsmessenger.dialogs.ScheduleMessageDialog
|
||||||
|
@ -888,9 +889,13 @@ class ThreadActivity : SimpleActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun askConfirmDelete() {
|
private fun askConfirmDelete() {
|
||||||
ConfirmationDialog(this, getString(R.string.delete_whole_conversation_confirmation)) {
|
DeleteConfirmationDialog(this, getString(R.string.delete_whole_conversation_confirmation), config.useArchive) { skipRecycleBin ->
|
||||||
ensureBackgroundThread {
|
ensureBackgroundThread {
|
||||||
deleteConversation(threadId)
|
if (skipRecycleBin || config.useArchive.not()) {
|
||||||
|
deleteConversation(threadId)
|
||||||
|
} else {
|
||||||
|
moveConversationToRecycleBin(threadId)
|
||||||
|
}
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
refreshMessages()
|
refreshMessages()
|
||||||
finish()
|
finish()
|
||||||
|
@ -1327,6 +1332,7 @@ class ThreadActivity : SimpleActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
messagesDB.insertOrUpdate(message)
|
messagesDB.insertOrUpdate(message)
|
||||||
|
conversationsDB.deleteThreadFromArchivedConversations(message.threadId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// show selected contacts, properly split to new lines when appropriate
|
// show selected contacts, properly split to new lines when appropriate
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
package com.simplemobiletools.smsmessenger.adapters
|
||||||
|
|
||||||
|
import android.view.Menu
|
||||||
|
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.dialogs.DeleteConfirmationDialog
|
||||||
|
import com.simplemobiletools.smsmessenger.extensions.conversationsDB
|
||||||
|
import com.simplemobiletools.smsmessenger.extensions.deleteConversation
|
||||||
|
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 -> ensureBackgroundThread { 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)
|
||||||
|
|
||||||
|
DeleteConfirmationDialog(activity, question, showSkipRecycleBinOption = false) { _ ->
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
val conversationsToUnarchive = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
||||||
|
conversationsToUnarchive.forEach {
|
||||||
|
activity.conversationsDB.deleteThreadFromArchivedConversations(it.threadId)
|
||||||
|
}
|
||||||
|
|
||||||
|
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,59 +1,27 @@
|
||||||
package com.simplemobiletools.smsmessenger.adapters
|
package com.simplemobiletools.smsmessenger.adapters
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.os.Parcelable
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.Menu
|
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.ConfirmationDialog
|
||||||
import com.simplemobiletools.commons.dialogs.FeatureLockedDialog
|
import com.simplemobiletools.commons.dialogs.FeatureLockedDialog
|
||||||
import com.simplemobiletools.commons.extensions.*
|
import com.simplemobiletools.commons.extensions.*
|
||||||
import com.simplemobiletools.commons.helpers.KEY_PHONE
|
import com.simplemobiletools.commons.helpers.KEY_PHONE
|
||||||
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
|
||||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
import com.simplemobiletools.commons.helpers.isNougatPlus
|
import com.simplemobiletools.commons.helpers.isNougatPlus
|
||||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||||
import com.simplemobiletools.smsmessenger.R
|
import com.simplemobiletools.smsmessenger.R
|
||||||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||||
|
import com.simplemobiletools.smsmessenger.dialogs.DeleteConfirmationDialog
|
||||||
import com.simplemobiletools.smsmessenger.dialogs.RenameConversationDialog
|
import com.simplemobiletools.smsmessenger.dialogs.RenameConversationDialog
|
||||||
import com.simplemobiletools.smsmessenger.extensions.*
|
import com.simplemobiletools.smsmessenger.extensions.*
|
||||||
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
||||||
import com.simplemobiletools.smsmessenger.messaging.isShortCodeWithLetters
|
import com.simplemobiletools.smsmessenger.messaging.isShortCodeWithLetters
|
||||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||||
import kotlinx.android.synthetic.main.item_conversation.view.*
|
|
||||||
|
|
||||||
class ConversationsAdapter(
|
class ConversationsAdapter(
|
||||||
activity: SimpleActivity, recyclerView: MyRecyclerView, onRefresh: () -> Unit, itemClick: (Any) -> Unit
|
activity: SimpleActivity, recyclerView: MyRecyclerView, onRefresh: () -> Unit, itemClick: (Any) -> Unit
|
||||||
) : MyRecyclerViewListAdapter<Conversation>(activity, recyclerView, ConversationDiffCallback(), itemClick, onRefresh),
|
) : BaseConversationsAdapter(activity, recyclerView, onRefresh, itemClick) {
|
||||||
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()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getActionMenuId() = R.menu.cab_conversations
|
override fun getActionMenuId() = R.menu.cab_conversations
|
||||||
|
|
||||||
override fun prepareActionMode(menu: Menu) {
|
override fun prepareActionMode(menu: Menu) {
|
||||||
|
@ -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() {
|
private fun tryBlocking() {
|
||||||
if (activity.isOrWasThankYouInstalled()) {
|
if (activity.isOrWasThankYouInstalled()) {
|
||||||
askConfirmBlock()
|
askConfirmBlock()
|
||||||
|
@ -184,21 +121,25 @@ class ConversationsAdapter(
|
||||||
val baseString = R.string.deletion_confirmation
|
val baseString = R.string.deletion_confirmation
|
||||||
val question = String.format(resources.getString(baseString), items)
|
val question = String.format(resources.getString(baseString), items)
|
||||||
|
|
||||||
ConfirmationDialog(activity, question) {
|
DeleteConfirmationDialog(activity, question, activity.config.useArchive) { skipRecycleBin ->
|
||||||
ensureBackgroundThread {
|
ensureBackgroundThread {
|
||||||
deleteConversations()
|
deleteConversations(skipRecycleBin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteConversations() {
|
private fun deleteConversations(skipRecycleBin: Boolean) {
|
||||||
if (selectedKeys.isEmpty()) {
|
if (selectedKeys.isEmpty()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val conversationsToRemove = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
val conversationsToRemove = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
||||||
conversationsToRemove.forEach {
|
conversationsToRemove.forEach {
|
||||||
activity.deleteConversation(it.threadId)
|
if (skipRecycleBin || activity.config.useArchive.not()) {
|
||||||
|
activity.deleteConversation(it.threadId)
|
||||||
|
} else {
|
||||||
|
activity.moveConversationToRecycleBin(it.threadId)
|
||||||
|
}
|
||||||
activity.notificationManager.cancel(it.threadId.hashCode())
|
activity.notificationManager.cancel(it.threadId.hashCode())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,8 +217,6 @@ class ConversationsAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSelectedItems() = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
|
||||||
|
|
||||||
private fun pinConversation(pin: Boolean) {
|
private fun pinConversation(pin: Boolean) {
|
||||||
val conversations = getSelectedItems()
|
val conversations = getSelectedItems()
|
||||||
if (conversations.isEmpty()) {
|
if (conversations.isEmpty()) {
|
||||||
|
@ -303,113 +242,10 @@ class ConversationsAdapter(
|
||||||
menu.findItem(R.id.cab_unpin_conversation).isVisible = selectedConversations.any { pinnedConversations.contains(it.threadId.toString()) }
|
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() {
|
private fun refreshConversations() {
|
||||||
activity.runOnUiThread {
|
activity.runOnUiThread {
|
||||||
refreshMessages()
|
refreshMessages()
|
||||||
finishActMode()
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,9 @@ import com.simplemobiletools.smsmessenger.interfaces.AttachmentsDao
|
||||||
import com.simplemobiletools.smsmessenger.interfaces.ConversationsDao
|
import com.simplemobiletools.smsmessenger.interfaces.ConversationsDao
|
||||||
import com.simplemobiletools.smsmessenger.interfaces.MessageAttachmentsDao
|
import com.simplemobiletools.smsmessenger.interfaces.MessageAttachmentsDao
|
||||||
import com.simplemobiletools.smsmessenger.interfaces.MessagesDao
|
import com.simplemobiletools.smsmessenger.interfaces.MessagesDao
|
||||||
import com.simplemobiletools.smsmessenger.models.Attachment
|
import com.simplemobiletools.smsmessenger.models.*
|
||||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
|
||||||
import com.simplemobiletools.smsmessenger.models.Message
|
|
||||||
import com.simplemobiletools.smsmessenger.models.MessageAttachment
|
|
||||||
|
|
||||||
@Database(entities = [Conversation::class, Attachment::class, MessageAttachment::class, Message::class], version = 7)
|
@Database(entities = [Conversation::class, ArchivedConversation::class, Attachment::class, MessageAttachment::class, Message::class], version = 8)
|
||||||
@TypeConverters(Converters::class)
|
@TypeConverters(Converters::class)
|
||||||
abstract class MessagesDatabase : RoomDatabase() {
|
abstract class MessagesDatabase : RoomDatabase() {
|
||||||
|
|
||||||
|
@ -44,6 +41,7 @@ abstract class MessagesDatabase : RoomDatabase() {
|
||||||
.addMigrations(MIGRATION_4_5)
|
.addMigrations(MIGRATION_4_5)
|
||||||
.addMigrations(MIGRATION_5_6)
|
.addMigrations(MIGRATION_5_6)
|
||||||
.addMigrations(MIGRATION_6_7)
|
.addMigrations(MIGRATION_6_7)
|
||||||
|
.addMigrations(MIGRATION_7_8)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,5 +113,13 @@ abstract class MessagesDatabase : RoomDatabase() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val MIGRATION_7_8 = object : Migration(7, 8) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.apply {
|
||||||
|
execSQL("CREATE TABLE archived_conversations (`thread_id` INTEGER NOT NULL PRIMARY KEY, `deleted_ts` INTEGER NOT NULL)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.simplemobiletools.smsmessenger.dialogs
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import com.simplemobiletools.commons.extensions.beGoneIf
|
||||||
|
import com.simplemobiletools.commons.extensions.getAlertDialogBuilder
|
||||||
|
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||||
|
import com.simplemobiletools.smsmessenger.R
|
||||||
|
import kotlinx.android.synthetic.main.dialog_delete_confirmation.view.delete_remember_title
|
||||||
|
import kotlinx.android.synthetic.main.dialog_delete_confirmation.view.skip_the_recycle_bin_checkbox
|
||||||
|
|
||||||
|
class DeleteConfirmationDialog(
|
||||||
|
private val activity: Activity,
|
||||||
|
private val message: String,
|
||||||
|
private val showSkipRecycleBinOption: Boolean,
|
||||||
|
private val callback: (skipRecycleBin: Boolean) -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
private var dialog: AlertDialog? = null
|
||||||
|
val view = activity.layoutInflater.inflate(R.layout.dialog_delete_confirmation, null)!!
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.delete_remember_title.text = message
|
||||||
|
view.skip_the_recycle_bin_checkbox.beGoneIf(!showSkipRecycleBinOption)
|
||||||
|
activity.getAlertDialogBuilder()
|
||||||
|
.setPositiveButton(R.string.yes) { _, _ -> dialogConfirmed() }
|
||||||
|
.setNegativeButton(R.string.no, null)
|
||||||
|
.apply {
|
||||||
|
activity.setupDialogStuff(view, this) { alertDialog ->
|
||||||
|
dialog = alertDialog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dialogConfirmed() {
|
||||||
|
dialog?.dismiss()
|
||||||
|
callback(view.skip_the_recycle_bin_checkbox.isChecked)
|
||||||
|
}
|
||||||
|
}
|
|
@ -581,6 +581,35 @@ fun Context.insertNewSMS(address: String, subject: String, body: String, date: L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Context.checkAndDeleteOldArchivedConversations(callback: (() -> Unit)? = null) {
|
||||||
|
if (config.useArchive && config.lastArchiveCheck < System.currentTimeMillis() - DAY_SECONDS * 1000) {
|
||||||
|
config.lastArchiveCheck = System.currentTimeMillis()
|
||||||
|
ensureBackgroundThread {
|
||||||
|
try {
|
||||||
|
for (conversation in conversationsDB.getOldArchived(System.currentTimeMillis() - MONTH_SECONDS * 1000L)) {
|
||||||
|
deleteConversation(conversation.threadId)
|
||||||
|
}
|
||||||
|
callback?.invoke()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.removeAllArchivedConversations(callback: (() -> Unit)? = null) {
|
||||||
|
ensureBackgroundThread {
|
||||||
|
try {
|
||||||
|
for (conversation in conversationsDB.getAllArchived()) {
|
||||||
|
deleteConversation(conversation.threadId)
|
||||||
|
}
|
||||||
|
toast(R.string.recycle_bin_emptied)
|
||||||
|
callback?.invoke()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
toast(R.string.unknown_error_occurred)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Context.deleteConversation(threadId: Long) {
|
fun Context.deleteConversation(threadId: Long) {
|
||||||
var uri = Sms.CONTENT_URI
|
var uri = Sms.CONTENT_URI
|
||||||
val selection = "${Sms.THREAD_ID} = ?"
|
val selection = "${Sms.THREAD_ID} = ?"
|
||||||
|
@ -602,6 +631,15 @@ fun Context.deleteConversation(threadId: Long) {
|
||||||
messagesDB.deleteThreadMessages(threadId)
|
messagesDB.deleteThreadMessages(threadId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Context.moveConversationToRecycleBin(threadId: Long) {
|
||||||
|
conversationsDB.archiveConversation(
|
||||||
|
ArchivedConversation(
|
||||||
|
threadId = threadId,
|
||||||
|
deletedTs = System.currentTimeMillis()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun Context.deleteMessage(id: Long, isMMS: Boolean) {
|
fun Context.deleteMessage(id: Long, isMMS: Boolean) {
|
||||||
val uri = if (isMMS) Mms.CONTENT_URI else Sms.CONTENT_URI
|
val uri = if (isMMS) Mms.CONTENT_URI else Sms.CONTENT_URI
|
||||||
val selection = "${Sms._ID} = ?"
|
val selection = "${Sms._ID} = ?"
|
||||||
|
|
|
@ -91,4 +91,12 @@ class Config(context: Context) : BaseConfig(context) {
|
||||||
var keyboardHeight: Int
|
var keyboardHeight: Int
|
||||||
get() = prefs.getInt(SOFT_KEYBOARD_HEIGHT, context.getDefaultKeyboardHeight())
|
get() = prefs.getInt(SOFT_KEYBOARD_HEIGHT, context.getDefaultKeyboardHeight())
|
||||||
set(keyboardHeight) = prefs.edit().putInt(SOFT_KEYBOARD_HEIGHT, keyboardHeight).apply()
|
set(keyboardHeight) = prefs.edit().putInt(SOFT_KEYBOARD_HEIGHT, keyboardHeight).apply()
|
||||||
|
|
||||||
|
var useArchive: Boolean
|
||||||
|
get() = prefs.getBoolean(USE_ARCHIVE, true)
|
||||||
|
set(useArchive) = prefs.edit().putBoolean(USE_ARCHIVE, useArchive).apply()
|
||||||
|
|
||||||
|
var lastArchiveCheck: Long
|
||||||
|
get() = prefs.getLong(LAST_ARCHIVE_CHECK, 0L)
|
||||||
|
set(lastArchiveCheck) = prefs.edit().putLong(LAST_ARCHIVE_CHECK, lastArchiveCheck).apply()
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,8 @@ const val SCHEDULED_MESSAGE_ID = "scheduled_message_id"
|
||||||
const val SOFT_KEYBOARD_HEIGHT = "soft_keyboard_height"
|
const val SOFT_KEYBOARD_HEIGHT = "soft_keyboard_height"
|
||||||
const val IS_MMS = "is_mms"
|
const val IS_MMS = "is_mms"
|
||||||
const val MESSAGE_ID = "message_id"
|
const val MESSAGE_ID = "message_id"
|
||||||
|
const val USE_ARCHIVE = "use_recycle_bin"
|
||||||
|
const val LAST_ARCHIVE_CHECK = "last_bin_check"
|
||||||
|
|
||||||
private const val PATH = "com.simplemobiletools.smsmessenger.action."
|
private const val PATH = "com.simplemobiletools.smsmessenger.action."
|
||||||
const val MARK_AS_READ = PATH + "mark_as_read"
|
const val MARK_AS_READ = PATH + "mark_as_read"
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package com.simplemobiletools.smsmessenger.interfaces
|
package com.simplemobiletools.smsmessenger.interfaces
|
||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.*
|
||||||
import androidx.room.Insert
|
import com.simplemobiletools.smsmessenger.models.ArchivedConversation
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.Query
|
|
||||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
|
@ -11,8 +9,20 @@ interface ConversationsDao {
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
fun insertOrUpdate(conversation: Conversation): Long
|
fun insertOrUpdate(conversation: Conversation): Long
|
||||||
|
|
||||||
@Query("SELECT * FROM conversations")
|
@Query("SELECT conversations.* FROM conversations LEFT OUTER JOIN archived_conversations ON conversations.thread_id = archived_conversations.thread_id WHERE archived_conversations.deleted_ts is NULL")
|
||||||
fun getAll(): List<Conversation>
|
fun getNonArchived(): List<Conversation>
|
||||||
|
|
||||||
|
@Query("SELECT conversations.* FROM archived_conversations INNER JOIN conversations ON conversations.thread_id = archived_conversations.thread_id")
|
||||||
|
fun getAllArchived(): List<Conversation>
|
||||||
|
|
||||||
|
@Query("SELECT COUNT(*) FROM archived_conversations")
|
||||||
|
fun getArchivedCount(): Int
|
||||||
|
|
||||||
|
@Query("SELECT * FROM archived_conversations WHERE deleted_ts < :timestamp")
|
||||||
|
fun getOldArchived(timestamp: Long): List<ArchivedConversation>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun archiveConversation(archivedConversation: ArchivedConversation)
|
||||||
|
|
||||||
@Query("SELECT * FROM conversations WHERE thread_id = :threadId")
|
@Query("SELECT * FROM conversations WHERE thread_id = :threadId")
|
||||||
fun getConversationWithThreadId(threadId: Long): Conversation?
|
fun getConversationWithThreadId(threadId: Long): Conversation?
|
||||||
|
@ -30,5 +40,14 @@ interface ConversationsDao {
|
||||||
fun markUnread(threadId: Long)
|
fun markUnread(threadId: Long)
|
||||||
|
|
||||||
@Query("DELETE FROM conversations WHERE thread_id = :threadId")
|
@Query("DELETE FROM conversations WHERE thread_id = :threadId")
|
||||||
fun deleteThreadId(threadId: Long)
|
fun deleteThreadFromConversations(threadId: Long)
|
||||||
|
|
||||||
|
@Query("DELETE FROM archived_conversations WHERE thread_id = :threadId")
|
||||||
|
fun deleteThreadFromArchivedConversations(threadId: Long)
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
fun deleteThreadId(threadId: Long) {
|
||||||
|
deleteThreadFromConversations(threadId)
|
||||||
|
deleteThreadFromArchivedConversations(threadId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
|
@ -100,6 +100,7 @@ class SmsReceiver : BroadcastReceiver() {
|
||||||
subscriptionId
|
subscriptionId
|
||||||
)
|
)
|
||||||
context.messagesDB.insertOrUpdate(message)
|
context.messagesDB.insertOrUpdate(message)
|
||||||
|
context.conversationsDB.deleteThreadFromArchivedConversations(threadId)
|
||||||
refreshMessages()
|
refreshMessages()
|
||||||
context.showReceivedMessageNotification(newMessageId, address, body, threadId, bitmap)
|
context.showReceivedMessageNotification(newMessageId, address, body, threadId, bitmap)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/archive_coordinator"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/archive_toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="@color/color_primary"
|
||||||
|
app:title="Archived messages"
|
||||||
|
app:titleTextAppearance="@style/AppTheme.ActionBar.TitleTextStyle" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/archive_nested_scrollview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="?attr/actionBarSize"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:scrollbars="none"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:id="@+id/archive_coordinator_wrapper"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/archive_holder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/conversations_progress_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:hideAnimationBehavior="outward"
|
||||||
|
app:showAnimationBehavior="inward"
|
||||||
|
app:showDelay="250"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyTextView
|
||||||
|
android:id="@+id/no_conversations_placeholder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_marginTop="@dimen/bigger_margin"
|
||||||
|
android:alpha="0.8"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingLeft="@dimen/activity_margin"
|
||||||
|
android:paddingRight="@dimen/activity_margin"
|
||||||
|
android:text="@string/no_conversations_found"
|
||||||
|
android:textSize="@dimen/bigger_text_size"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
|
||||||
|
android:id="@+id/conversations_fastscroller"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyRecyclerView
|
||||||
|
android:id="@+id/conversations_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:layoutAnimation="@anim/layout_animation"
|
||||||
|
android:overScrollMode="ifContentScrolls"
|
||||||
|
android:scrollbars="none"
|
||||||
|
app:layoutManager="com.simplemobiletools.commons.views.MyLinearLayoutManager" />
|
||||||
|
|
||||||
|
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
|
||||||
|
</RelativeLayout>
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -346,6 +346,55 @@
|
||||||
android:id="@+id/settings_outgoing_messages_divider"
|
android:id="@+id/settings_outgoing_messages_divider"
|
||||||
layout="@layout/divider" />
|
layout="@layout/divider" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/settings_recycle_bin_label"
|
||||||
|
style="@style/SettingsSectionLabelStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/recycle_bin" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/settings_use_recycle_bin_holder"
|
||||||
|
style="@style/SettingsHolderCheckboxStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
|
||||||
|
android:id="@+id/settings_use_recycle_bin"
|
||||||
|
style="@style/SettingsCheckboxStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/move_items_into_recycle_bin" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/settings_empty_recycle_bin_holder"
|
||||||
|
style="@style/SettingsHolderTextViewStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyTextView
|
||||||
|
android:id="@+id/settings_empty_recycle_bin_label"
|
||||||
|
style="@style/SettingsTextLabelStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/empty_recycle_bin" />
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyTextView
|
||||||
|
android:id="@+id/settings_empty_recycle_bin_size"
|
||||||
|
style="@style/SettingsTextValueStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/settings_empty_recycle_bin_label"
|
||||||
|
tools:text="0 B" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/settings_recycle_bin_divider"
|
||||||
|
layout="@layout/divider" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/settings_security_label"
|
android:id="@+id/settings_security_label"
|
||||||
style="@style/SettingsSectionLabelStyle"
|
style="@style/SettingsSectionLabelStyle"
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/delete_remember_holder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="@dimen/big_margin"
|
||||||
|
android:paddingTop="@dimen/big_margin"
|
||||||
|
android:paddingRight="@dimen/big_margin">
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyTextView
|
||||||
|
android:id="@+id/delete_remember_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="@dimen/small_margin"
|
||||||
|
android:paddingBottom="@dimen/activity_margin"
|
||||||
|
android:text="@string/delete_whole_conversation_confirmation"
|
||||||
|
android:textSize="@dimen/bigger_text_size" />
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
|
||||||
|
android:id="@+id/skip_the_recycle_bin_checkbox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/delete_remember_title"
|
||||||
|
android:text="@string/skip_the_recycle_bin" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:ignore="AppCompatResource,AlwaysShowAction">
|
||||||
|
<item
|
||||||
|
android:id="@+id/empty_archive"
|
||||||
|
android:showAsAction="never"
|
||||||
|
android:title="Empty archive messages"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
</menu>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:ignore="AppCompatResource,AlwaysShowAction">
|
||||||
|
<item
|
||||||
|
android:id="@+id/cab_delete"
|
||||||
|
android:icon="@drawable/ic_delete_vector"
|
||||||
|
android:showAsAction="always"
|
||||||
|
android:title="@string/delete"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/cab_select_all"
|
||||||
|
android:icon="@drawable/ic_select_all_vector"
|
||||||
|
android:title="@string/select_all"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/cab_unarchive"
|
||||||
|
android:showAsAction="never"
|
||||||
|
android:title="Unarchive"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
</menu>
|
|
@ -13,6 +13,11 @@
|
||||||
android:showAsAction="never"
|
android:showAsAction="never"
|
||||||
android:title="@string/export_messages"
|
android:title="@string/export_messages"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/show_archived"
|
||||||
|
android:showAsAction="never"
|
||||||
|
android:title="Show archived messages"
|
||||||
|
app:showAsAction="never" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/settings"
|
android:id="@+id/settings"
|
||||||
android:icon="@drawable/ic_settings_cog_vector"
|
android:icon="@drawable/ic_settings_cog_vector"
|
||||||
|
|
Loading…
Reference in New Issue