🎉 Separate messages by identity
Also, allow deleting all messages/conversations in a list
This commit is contained in:
parent
9f2508c1a5
commit
a9602368fb
@ -85,10 +85,10 @@ class AddressListFragment : AbstractItemListFragment<Void, BitmessageAddress>()
|
|||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
initFab(activity as MainActivity)
|
initFab(activity as MainActivity)
|
||||||
updateList()
|
reloadList()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateList() {
|
override fun reloadList() {
|
||||||
adapter.clear()
|
adapter.clear()
|
||||||
context?.let { context ->
|
context?.let { context ->
|
||||||
val addressRepo = Singleton.getAddressRepository(context)
|
val addressRepo = Singleton.getAddressRepository(context)
|
||||||
@ -138,7 +138,7 @@ class AddressListFragment : AbstractItemListFragment<Void, BitmessageAddress>()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateList(label: Void) = updateList()
|
override fun updateList(label: Void) = reloadList()
|
||||||
|
|
||||||
private data class ViewHolder(
|
private data class ViewHolder(
|
||||||
val ctx: Context,
|
val ctx: Context,
|
||||||
|
@ -33,6 +33,7 @@ import ch.dissem.apps.abit.listener.ListSelectionListener
|
|||||||
import ch.dissem.apps.abit.repository.AndroidMessageRepository
|
import ch.dissem.apps.abit.repository.AndroidMessageRepository
|
||||||
import ch.dissem.apps.abit.service.Singleton
|
import ch.dissem.apps.abit.service.Singleton
|
||||||
import ch.dissem.apps.abit.service.Singleton.currentLabel
|
import ch.dissem.apps.abit.service.Singleton.currentLabel
|
||||||
|
import ch.dissem.apps.abit.util.preferences
|
||||||
import ch.dissem.bitmessage.entity.Conversation
|
import ch.dissem.bitmessage.entity.Conversation
|
||||||
import ch.dissem.bitmessage.entity.valueobject.Label
|
import ch.dissem.bitmessage.entity.valueobject.Label
|
||||||
import ch.dissem.bitmessage.utils.ConversationService
|
import ch.dissem.bitmessage.utils.ConversationService
|
||||||
@ -43,7 +44,9 @@ import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchAct
|
|||||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||||
import io.github.kobakei.materialfabspeeddial.FabSpeedDialMenu
|
import io.github.kobakei.materialfabspeeddial.FabSpeedDialMenu
|
||||||
import kotlinx.android.synthetic.main.fragment_message_list.*
|
import kotlinx.android.synthetic.main.fragment_message_list.*
|
||||||
|
import org.jetbrains.anko.cancelButton
|
||||||
import org.jetbrains.anko.doAsync
|
import org.jetbrains.anko.doAsync
|
||||||
|
import org.jetbrains.anko.support.v4.alert
|
||||||
import org.jetbrains.anko.support.v4.onUiThread
|
import org.jetbrains.anko.support.v4.onUiThread
|
||||||
import org.jetbrains.anko.uiThread
|
import org.jetbrains.anko.uiThread
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -90,6 +93,7 @@ class ConversationListFragment : Fragment(), ListHolder<Label> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var emptyTrashMenuItem: MenuItem? = null
|
private var emptyTrashMenuItem: MenuItem? = null
|
||||||
|
private var deleteAllMenuItem: MenuItem? = null
|
||||||
private lateinit var messageRepo: AndroidMessageRepository
|
private lateinit var messageRepo: AndroidMessageRepository
|
||||||
private lateinit var conversationService: ConversationService
|
private lateinit var conversationService: ConversationService
|
||||||
private var activateOnItemClick: Boolean = false
|
private var activateOnItemClick: Boolean = false
|
||||||
@ -103,7 +107,8 @@ class ConversationListFragment : Fragment(), ListHolder<Label> {
|
|||||||
val conversationIds = messageRepo.findConversations(
|
val conversationIds = messageRepo.findConversations(
|
||||||
currentLabel.value,
|
currentLabel.value,
|
||||||
messageAdapter.itemCount,
|
messageAdapter.itemCount,
|
||||||
PAGE_SIZE
|
PAGE_SIZE,
|
||||||
|
context?.preferences?.separateIdentities == true
|
||||||
)
|
)
|
||||||
conversationIds.forEach { conversationId ->
|
conversationIds.forEach { conversationId ->
|
||||||
val conversation = conversationService.getConversation(conversationId)
|
val conversation = conversationService.getConversation(conversationId)
|
||||||
@ -139,6 +144,8 @@ class ConversationListFragment : Fragment(), ListHolder<Label> {
|
|||||||
super.onPause()
|
super.onPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun reloadList() = doUpdateList(currentLabel.value)
|
||||||
|
|
||||||
private fun doUpdateList(label: Label?) {
|
private fun doUpdateList(label: Label?) {
|
||||||
val mainActivity = activity as? MainActivity
|
val mainActivity = activity as? MainActivity
|
||||||
swipeableConversationAdapter?.clear(label)
|
swipeableConversationAdapter?.clear(label)
|
||||||
@ -148,6 +155,9 @@ class ConversationListFragment : Fragment(), ListHolder<Label> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
emptyTrashMenuItem?.isVisible = label.type == Label.Type.TRASH
|
emptyTrashMenuItem?.isVisible = label.type == Label.Type.TRASH
|
||||||
|
// I'm not yet sure if it's a good idea in conversation views, so it's off for now
|
||||||
|
deleteAllMenuItem?.isVisible = false
|
||||||
|
|
||||||
mainActivity?.apply {
|
mainActivity?.apply {
|
||||||
if ("archive" == label.toString()) {
|
if ("archive" == label.toString()) {
|
||||||
updateTitle(getString(R.string.archive))
|
updateTitle(getString(R.string.archive))
|
||||||
@ -298,6 +308,7 @@ class ConversationListFragment : Fragment(), ListHolder<Label> {
|
|||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
inflater.inflate(R.menu.message_list, menu)
|
inflater.inflate(R.menu.message_list, menu)
|
||||||
emptyTrashMenuItem = menu.findItem(R.id.empty_trash)
|
emptyTrashMenuItem = menu.findItem(R.id.empty_trash)
|
||||||
|
deleteAllMenuItem = menu.findItem(R.id.delete_all)
|
||||||
super.onCreateOptionsMenu(menu, inflater)
|
super.onCreateOptionsMenu(menu, inflater)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,13 +318,21 @@ class ConversationListFragment : Fragment(), ListHolder<Label> {
|
|||||||
currentLabel.value?.let { label ->
|
currentLabel.value?.let { label ->
|
||||||
if (label.type != Label.Type.TRASH) return true
|
if (label.type != Label.Type.TRASH) return true
|
||||||
|
|
||||||
doAsync {
|
deleteAllMessages(label)
|
||||||
for (message in messageRepo.findMessages(label)) {
|
|
||||||
messageRepo.remove(message)
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
uiThread { doUpdateList(label) }
|
|
||||||
}
|
}
|
||||||
|
R.id.delete_all -> {
|
||||||
|
currentLabel.value?.let { label ->
|
||||||
|
alert(
|
||||||
|
title = R.string.delete_all_messages_in_list,
|
||||||
|
message = R.string.delete_all_messages_in_list_ask
|
||||||
|
) {
|
||||||
|
positiveButton(R.string.delete) {
|
||||||
|
deleteAllMessages(label)
|
||||||
|
}
|
||||||
|
cancelButton { }
|
||||||
|
}.show()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -321,6 +340,16 @@ class ConversationListFragment : Fragment(), ListHolder<Label> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun deleteAllMessages(label: Label) {
|
||||||
|
doAsync {
|
||||||
|
for (message in messageRepo.findMessages(label, 0, 0, context?.preferences?.separateIdentities == true)) {
|
||||||
|
messageRepo.remove(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
uiThread { doUpdateList(label) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun updateList(label: Label) {
|
override fun updateList(label: Label) {
|
||||||
currentLabel.value = label
|
currentLabel.value = label
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ package ch.dissem.apps.abit
|
|||||||
* @author Christian Basler
|
* @author Christian Basler
|
||||||
*/
|
*/
|
||||||
interface ListHolder<in L> {
|
interface ListHolder<in L> {
|
||||||
|
fun reloadList()
|
||||||
|
|
||||||
fun updateList(label: L)
|
fun updateList(label: L)
|
||||||
|
|
||||||
fun setActivateOnItemClick(activateOnItemClick: Boolean)
|
fun setActivateOnItemClick(activateOnItemClick: Boolean)
|
||||||
|
@ -29,6 +29,7 @@ import ch.dissem.apps.abit.drawer.ProfileImageListener
|
|||||||
import ch.dissem.apps.abit.drawer.ProfileSelectionListener
|
import ch.dissem.apps.abit.drawer.ProfileSelectionListener
|
||||||
import ch.dissem.apps.abit.listener.ListSelectionListener
|
import ch.dissem.apps.abit.listener.ListSelectionListener
|
||||||
import ch.dissem.apps.abit.repository.AndroidLabelRepository.Companion.LABEL_ARCHIVE
|
import ch.dissem.apps.abit.repository.AndroidLabelRepository.Companion.LABEL_ARCHIVE
|
||||||
|
import ch.dissem.apps.abit.repository.AndroidMessageRepository
|
||||||
import ch.dissem.apps.abit.service.Singleton
|
import ch.dissem.apps.abit.service.Singleton
|
||||||
import ch.dissem.apps.abit.service.Singleton.currentLabel
|
import ch.dissem.apps.abit.service.Singleton.currentLabel
|
||||||
import ch.dissem.apps.abit.util.*
|
import ch.dissem.apps.abit.util.*
|
||||||
@ -92,6 +93,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
private lateinit var bmc: BitmessageContext
|
private lateinit var bmc: BitmessageContext
|
||||||
|
private lateinit var messageRepo: AndroidMessageRepository
|
||||||
private lateinit var accountHeader: AccountHeader
|
private lateinit var accountHeader: AccountHeader
|
||||||
|
|
||||||
private lateinit var drawer: Drawer
|
private lateinit var drawer: Drawer
|
||||||
@ -104,6 +106,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
instance = WeakReference(this)
|
instance = WeakReference(this)
|
||||||
bmc = Singleton.getBitmessageContext(this)
|
bmc = Singleton.getBitmessageContext(this)
|
||||||
|
messageRepo = Singleton.getMessageRepository(this)
|
||||||
|
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
fab.hide()
|
fab.hide()
|
||||||
@ -299,8 +302,8 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
for (label in labels) {
|
for (label in labels) {
|
||||||
addLabelEntry(label)
|
addLabelEntry(label)
|
||||||
}
|
}
|
||||||
currentLabel.value?.let {
|
currentLabel.value?.let { label ->
|
||||||
drawer.setSelection(it.id as Long)
|
drawer.setSelection(label.id as Long)
|
||||||
}
|
}
|
||||||
updateUnread()
|
updateUnread()
|
||||||
}
|
}
|
||||||
@ -334,7 +337,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
when (item.name.textRes) {
|
when (item.name.textRes) {
|
||||||
R.string.contacts_and_subscriptions -> {
|
R.string.contacts_and_subscriptions -> {
|
||||||
if (itemList is AddressListFragment) {
|
if (itemList is AddressListFragment) {
|
||||||
itemList.updateList()
|
itemList.reloadList()
|
||||||
} else {
|
} else {
|
||||||
changeList(AddressListFragment())
|
changeList(AddressListFragment())
|
||||||
}
|
}
|
||||||
@ -432,7 +435,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
if (item.tag is Label) {
|
if (item.tag is Label) {
|
||||||
val label = item.tag as Label
|
val label = item.tag as Label
|
||||||
if (label !== LABEL_ARCHIVE) {
|
if (label !== LABEL_ARCHIVE) {
|
||||||
val unread = bmc.messages.countUnread(label)
|
val unread = messageRepo.countUnread(label, preferences.separateIdentities)
|
||||||
if (unread > 0) {
|
if (unread > 0) {
|
||||||
(item as PrimaryDrawerItem).withBadge(unread.toString())
|
(item as PrimaryDrawerItem).withBadge(unread.toString())
|
||||||
} else {
|
} else {
|
||||||
|
@ -33,6 +33,7 @@ import ch.dissem.apps.abit.listener.ListSelectionListener
|
|||||||
import ch.dissem.apps.abit.repository.AndroidMessageRepository
|
import ch.dissem.apps.abit.repository.AndroidMessageRepository
|
||||||
import ch.dissem.apps.abit.service.Singleton
|
import ch.dissem.apps.abit.service.Singleton
|
||||||
import ch.dissem.apps.abit.service.Singleton.currentLabel
|
import ch.dissem.apps.abit.service.Singleton.currentLabel
|
||||||
|
import ch.dissem.apps.abit.util.preferences
|
||||||
import ch.dissem.bitmessage.entity.Plaintext
|
import ch.dissem.bitmessage.entity.Plaintext
|
||||||
import ch.dissem.bitmessage.entity.valueobject.Label
|
import ch.dissem.bitmessage.entity.valueobject.Label
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.animator.SwipeDismissItemAnimator
|
import com.h6ah4i.android.widget.advrecyclerview.animator.SwipeDismissItemAnimator
|
||||||
@ -42,9 +43,9 @@ import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchAct
|
|||||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||||
import io.github.kobakei.materialfabspeeddial.FabSpeedDialMenu
|
import io.github.kobakei.materialfabspeeddial.FabSpeedDialMenu
|
||||||
import kotlinx.android.synthetic.main.fragment_message_list.*
|
import kotlinx.android.synthetic.main.fragment_message_list.*
|
||||||
import org.jetbrains.anko.doAsync
|
import org.jetbrains.anko.*
|
||||||
|
import org.jetbrains.anko.support.v4.alert
|
||||||
import org.jetbrains.anko.support.v4.onUiThread
|
import org.jetbrains.anko.support.v4.onUiThread
|
||||||
import org.jetbrains.anko.uiThread
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
private const val PAGE_SIZE = 15
|
private const val PAGE_SIZE = 15
|
||||||
@ -89,6 +90,7 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var emptyTrashMenuItem: MenuItem? = null
|
private var emptyTrashMenuItem: MenuItem? = null
|
||||||
|
private var deleteAllMenuItem: MenuItem? = null
|
||||||
private lateinit var messageRepo: AndroidMessageRepository
|
private lateinit var messageRepo: AndroidMessageRepository
|
||||||
private var activateOnItemClick: Boolean = false
|
private var activateOnItemClick: Boolean = false
|
||||||
|
|
||||||
@ -98,10 +100,12 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
isLoading = true
|
isLoading = true
|
||||||
swipeableMessageAdapter?.let { messageAdapter ->
|
swipeableMessageAdapter?.let { messageAdapter ->
|
||||||
doAsync {
|
doAsync {
|
||||||
|
val label = currentLabel.value
|
||||||
val messages = messageRepo.findMessages(
|
val messages = messageRepo.findMessages(
|
||||||
currentLabel.value,
|
label,
|
||||||
messageAdapter.itemCount,
|
messageAdapter.itemCount,
|
||||||
PAGE_SIZE
|
PAGE_SIZE,
|
||||||
|
context?.preferences?.separateIdentities == true && label?.type != Label.Type.BROADCAST
|
||||||
)
|
)
|
||||||
onUiThread {
|
onUiThread {
|
||||||
messageAdapter.addAll(messages)
|
messageAdapter.addAll(messages)
|
||||||
@ -133,6 +137,8 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
super.onPause()
|
super.onPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun reloadList() = doUpdateList(currentLabel.value)
|
||||||
|
|
||||||
private fun doUpdateList(label: Label?) {
|
private fun doUpdateList(label: Label?) {
|
||||||
// If the menu item isn't available yet, we should wait - the method will be called again once it's
|
// If the menu item isn't available yet, we should wait - the method will be called again once it's
|
||||||
// initialized.
|
// initialized.
|
||||||
@ -155,6 +161,7 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
|
|
||||||
loadMoreItems()
|
loadMoreItems()
|
||||||
}
|
}
|
||||||
|
deleteAllMenuItem?.isVisible = label?.type != Label.Type.TRASH
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
@ -300,6 +307,7 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
inflater.inflate(R.menu.message_list, menu)
|
inflater.inflate(R.menu.message_list, menu)
|
||||||
emptyTrashMenuItem = menu.findItem(R.id.empty_trash)
|
emptyTrashMenuItem = menu.findItem(R.id.empty_trash)
|
||||||
|
deleteAllMenuItem = menu.findItem(R.id.delete_all)
|
||||||
currentLabel.value?.let { doUpdateList(it) }
|
currentLabel.value?.let { doUpdateList(it) }
|
||||||
super.onCreateOptionsMenu(menu, inflater)
|
super.onCreateOptionsMenu(menu, inflater)
|
||||||
}
|
}
|
||||||
@ -310,13 +318,21 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
currentLabel.value?.let { label ->
|
currentLabel.value?.let { label ->
|
||||||
if (label.type != Label.Type.TRASH) return true
|
if (label.type != Label.Type.TRASH) return true
|
||||||
|
|
||||||
doAsync {
|
deleteAllMessages(label)
|
||||||
for (message in messageRepo.findMessages(label)) {
|
|
||||||
messageRepo.remove(message)
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
uiThread { doUpdateList(label) }
|
|
||||||
}
|
}
|
||||||
|
R.id.delete_all -> {
|
||||||
|
currentLabel.value?.let { label ->
|
||||||
|
alert(
|
||||||
|
title = R.string.delete_all_messages_in_list,
|
||||||
|
message = R.string.delete_all_messages_in_list_ask
|
||||||
|
) {
|
||||||
|
positiveButton(R.string.delete) {
|
||||||
|
deleteAllMessages(label)
|
||||||
|
}
|
||||||
|
cancelButton { }
|
||||||
|
}.show()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -324,6 +340,16 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun deleteAllMessages(label: Label) {
|
||||||
|
doAsync {
|
||||||
|
for (message in messageRepo.findMessages(label, 0, 0, context?.preferences?.separateIdentities == true)) {
|
||||||
|
messageRepo.remove(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
uiThread { doUpdateList(label) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun updateList(label: Label) {
|
override fun updateList(label: Label) {
|
||||||
currentLabel.value = label
|
currentLabel.value = label
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,13 @@ class ProfileSelectionListener(
|
|||||||
val tag = profile.tag
|
val tag = profile.tag
|
||||||
if (tag is BitmessageAddress) {
|
if (tag is BitmessageAddress) {
|
||||||
Singleton.setIdentity(tag)
|
Singleton.setIdentity(tag)
|
||||||
|
MainActivity.apply {
|
||||||
|
updateUnread()
|
||||||
|
val itemList = supportFragmentManager.findFragmentById(R.id.item_list)
|
||||||
|
if (itemList is ListHolder<*>) {
|
||||||
|
itemList.reloadList()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import android.database.Cursor
|
|||||||
import android.database.DatabaseUtils
|
import android.database.DatabaseUtils
|
||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import ch.dissem.apps.abit.repository.AndroidLabelRepository.Companion.LABEL_ARCHIVE
|
import ch.dissem.apps.abit.repository.AndroidLabelRepository.Companion.LABEL_ARCHIVE
|
||||||
|
import ch.dissem.apps.abit.util.Preferences
|
||||||
import ch.dissem.apps.abit.util.UuidUtils
|
import ch.dissem.apps.abit.util.UuidUtils
|
||||||
import ch.dissem.apps.abit.util.UuidUtils.asUuid
|
import ch.dissem.apps.abit.util.UuidUtils.asUuid
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress
|
import ch.dissem.bitmessage.entity.BitmessageAddress
|
||||||
@ -38,7 +39,14 @@ import java.util.*
|
|||||||
/**
|
/**
|
||||||
* [MessageRepository] implementation using the Android SQL API.
|
* [MessageRepository] implementation using the Android SQL API.
|
||||||
*/
|
*/
|
||||||
class AndroidMessageRepository(private val sql: SqlHelper) : AbstractMessageRepository() {
|
class AndroidMessageRepository(private val sql: SqlHelper, private val prefs: Preferences) : AbstractMessageRepository() {
|
||||||
|
|
||||||
|
fun findMessages(label: Label?, offset: Int, limit: Int, separateIdentities: Boolean) =
|
||||||
|
if (label === LABEL_ARCHIVE || label === null) {
|
||||||
|
find("id NOT IN (SELECT message_id FROM Message_Label)", offset, limit, separateIdentities)
|
||||||
|
} else {
|
||||||
|
find("id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.id + ")", offset, limit, separateIdentities)
|
||||||
|
}
|
||||||
|
|
||||||
override fun findMessages(label: Label?, offset: Int, limit: Int) =
|
override fun findMessages(label: Label?, offset: Int, limit: Int) =
|
||||||
if (label === LABEL_ARCHIVE) {
|
if (label === LABEL_ARCHIVE) {
|
||||||
@ -54,30 +62,56 @@ class AndroidMessageRepository(private val sql: SqlHelper) : AbstractMessageRepo
|
|||||||
null
|
null
|
||||||
).toInt()
|
).toInt()
|
||||||
|
|
||||||
override fun countUnread(label: Label?) = when {
|
private fun getSelectIdentity(separateIdentities: Boolean): Pair<String, Array<String>> {
|
||||||
|
if (separateIdentities) {
|
||||||
|
val identity = prefs.currentIdentity
|
||||||
|
return if (prefs.separateIdentities && identity != null) {
|
||||||
|
"AND (type = 'BROADCAST' OR recipient=? OR sender=?)" to arrayOf(identity.address, identity.address)
|
||||||
|
} else {
|
||||||
|
"" to emptyArray()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "" to emptyArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun countUnread(label: Label?) = countUnread(label, false)
|
||||||
|
|
||||||
|
fun countUnread(label: Label?, separateIdentities: Boolean) = getSelectIdentity(separateIdentities).let { (selectIdentityQuery, selectIdentityArgs) ->
|
||||||
|
when {
|
||||||
label === LABEL_ARCHIVE -> 0
|
label === LABEL_ARCHIVE -> 0
|
||||||
label == null -> DatabaseUtils.queryNumEntries(
|
label == null -> DatabaseUtils.queryNumEntries(
|
||||||
sql.readableDatabase,
|
sql.readableDatabase,
|
||||||
TABLE_NAME,
|
TABLE_NAME,
|
||||||
"id IN (SELECT message_id FROM Message_Label WHERE label_id IN (SELECT id FROM Label WHERE type=?))",
|
"id IN (SELECT message_id FROM Message_Label WHERE label_id IN (SELECT id FROM Label WHERE type=?)) " +
|
||||||
arrayOf(Label.Type.UNREAD.name)
|
selectIdentityQuery,
|
||||||
|
arrayOf(Label.Type.UNREAD.name, *selectIdentityArgs)
|
||||||
).toInt()
|
).toInt()
|
||||||
else -> DatabaseUtils.queryNumEntries(
|
else -> DatabaseUtils.queryNumEntries(
|
||||||
sql.readableDatabase,
|
sql.readableDatabase,
|
||||||
TABLE_NAME,
|
TABLE_NAME,
|
||||||
"id IN (SELECT message_id FROM Message_Label WHERE label_id=?) " +
|
"id IN (SELECT message_id FROM Message_Label WHERE label_id=?) " +
|
||||||
"AND id IN (SELECT message_id FROM Message_Label WHERE label_id IN (SELECT id FROM Label WHERE type=?))",
|
"AND id IN (SELECT message_id FROM Message_Label WHERE label_id IN (SELECT id FROM Label WHERE type=?)) " +
|
||||||
arrayOf(label.id.toString(), Label.Type.UNREAD.name)
|
selectIdentityQuery,
|
||||||
|
arrayOf(label.id.toString(), Label.Type.UNREAD.name, *selectIdentityArgs)
|
||||||
).toInt()
|
).toInt()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun findConversations(label: Label?, offset: Int, limit: Int): List<UUID> {
|
override fun findConversations(label: Label?, offset: Int, limit: Int): List<UUID> = findConversations(label, offset, limit, false)
|
||||||
|
|
||||||
|
fun findConversations(label: Label?, offset: Int, limit: Int, separateIdentities: Boolean): List<UUID> {
|
||||||
val projection = arrayOf(COLUMN_CONVERSATION)
|
val projection = arrayOf(COLUMN_CONVERSATION)
|
||||||
|
val (selectIdentityQuery, selectIdentityArgs) = getSelectIdentity(separateIdentities)
|
||||||
|
|
||||||
val where = when {
|
val where = when {
|
||||||
label === LABEL_ARCHIVE -> "id NOT IN (SELECT message_id FROM Message_Label)"
|
label === LABEL_ARCHIVE -> "id NOT IN (SELECT message_id FROM Message_Label) $selectIdentityQuery"
|
||||||
label == null -> null
|
label == null -> if (selectIdentityQuery.isNotBlank()) {
|
||||||
else -> "id IN (SELECT message_id FROM Message_Label WHERE label_id=${label.id})"
|
"type = 'BROADCAST' OR recipient=? OR sender=?"
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
else -> "id IN (SELECT message_id FROM Message_Label WHERE label_id=${label.id}) $selectIdentityQuery"
|
||||||
}
|
}
|
||||||
val result = LinkedList<UUID>()
|
val result = LinkedList<UUID>()
|
||||||
sql.readableDatabase.query(
|
sql.readableDatabase.query(
|
||||||
@ -85,7 +119,7 @@ class AndroidMessageRepository(private val sql: SqlHelper) : AbstractMessageRepo
|
|||||||
TABLE_NAME,
|
TABLE_NAME,
|
||||||
projection,
|
projection,
|
||||||
where,
|
where,
|
||||||
null, null, null,
|
selectIdentityArgs, null, null,
|
||||||
"$COLUMN_RECEIVED DESC, $COLUMN_SENT DESC",
|
"$COLUMN_RECEIVED DESC, $COLUMN_SENT DESC",
|
||||||
if (limit == 0) null else "$offset, $limit"
|
if (limit == 0) null else "$offset, $limit"
|
||||||
).use { c ->
|
).use { c ->
|
||||||
@ -140,8 +174,11 @@ class AndroidMessageRepository(private val sql: SqlHelper) : AbstractMessageRepo
|
|||||||
db.update(PARENTS_TABLE_NAME, values, where, null)
|
db.update(PARENTS_TABLE_NAME, values, where, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun find(where: String, offset: Int, limit: Int): List<Plaintext> {
|
override fun find(where: String, offset: Int, limit: Int) = find(where, offset, limit, false)
|
||||||
|
|
||||||
|
fun find(where: String, offset: Int, limit: Int, separateIdentities: Boolean): List<Plaintext> {
|
||||||
val result = LinkedList<Plaintext>()
|
val result = LinkedList<Plaintext>()
|
||||||
|
val (selectIdentityQuery, selectIdentityArgs) = getSelectIdentity(separateIdentities)
|
||||||
|
|
||||||
// Define a projection that specifies which columns from the database
|
// Define a projection that specifies which columns from the database
|
||||||
// you will actually use after this query.
|
// you will actually use after this query.
|
||||||
@ -164,7 +201,7 @@ class AndroidMessageRepository(private val sql: SqlHelper) : AbstractMessageRepo
|
|||||||
|
|
||||||
sql.readableDatabase.query(
|
sql.readableDatabase.query(
|
||||||
TABLE_NAME, projection,
|
TABLE_NAME, projection,
|
||||||
where, null, null, null,
|
"$where $selectIdentityQuery", selectIdentityArgs, null, null,
|
||||||
"$COLUMN_RECEIVED DESC, $COLUMN_SENT DESC",
|
"$COLUMN_RECEIVED DESC, $COLUMN_SENT DESC",
|
||||||
if (limit == 0) null else "$offset, $limit"
|
if (limit == 0) null else "$offset, $limit"
|
||||||
).use { c ->
|
).use { c ->
|
||||||
@ -318,4 +355,5 @@ class AndroidMessageRepository(private val sql: SqlHelper) : AbstractMessageRepo
|
|||||||
private const val JT_COLUMN_MESSAGE = "message_id"
|
private const val JT_COLUMN_MESSAGE = "message_id"
|
||||||
private const val JT_COLUMN_LABEL = "label_id"
|
private const val JT_COLUMN_LABEL = "label_id"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -110,12 +110,13 @@ object Singleton {
|
|||||||
inventory = AndroidInventory(sqlHelper)
|
inventory = AndroidInventory(sqlHelper)
|
||||||
addressRepo = AndroidAddressRepository(sqlHelper)
|
addressRepo = AndroidAddressRepository(sqlHelper)
|
||||||
labelRepo = AndroidLabelRepository(sqlHelper, ctx)
|
labelRepo = AndroidLabelRepository(sqlHelper, ctx)
|
||||||
messageRepo = AndroidMessageRepository(sqlHelper)
|
messageRepo = AndroidMessageRepository(sqlHelper, ctx.preferences)
|
||||||
proofOfWorkRepo = AndroidProofOfWorkRepository(sqlHelper).also { powRepo = it }
|
proofOfWorkRepo = AndroidProofOfWorkRepository(sqlHelper).also { powRepo = it }
|
||||||
networkHandler = NioNetworkHandler(4)
|
networkHandler = NioNetworkHandler(4)
|
||||||
listener = getMessageListener(ctx)
|
listener = getMessageListener(ctx)
|
||||||
labeler = Singleton.labeler
|
labeler = Singleton.labeler
|
||||||
preferences.sendPubkeyOnIdentityCreation = false
|
preferences.sendPubkeyOnIdentityCreation = false
|
||||||
|
preferences.port = context.preferences.listeningPort
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ object Constants {
|
|||||||
const val PREFERENCE_REQUEST_ACK = "request_acknowledgments"
|
const val PREFERENCE_REQUEST_ACK = "request_acknowledgments"
|
||||||
const val PREFERENCE_POW_AVERAGE = "average_pow_time_ms"
|
const val PREFERENCE_POW_AVERAGE = "average_pow_time_ms"
|
||||||
const val PREFERENCE_POW_COUNT = "pow_count"
|
const val PREFERENCE_POW_COUNT = "pow_count"
|
||||||
|
const val PREFERENCE_SEPARATE_IDENTITIES = "separate_identities"
|
||||||
|
|
||||||
const val BITMESSAGE_URL_SCHEMA = "bitmessage:"
|
const val BITMESSAGE_URL_SCHEMA = "bitmessage:"
|
||||||
|
|
||||||
|
@ -21,10 +21,12 @@ import android.content.Intent
|
|||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.os.BatteryManager
|
import android.os.BatteryManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import ch.dissem.apps.abit.service.Singleton
|
||||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_EMULATE_CONVERSATIONS
|
import ch.dissem.apps.abit.util.Constants.PREFERENCE_EMULATE_CONVERSATIONS
|
||||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_ONLINE
|
import ch.dissem.apps.abit.util.Constants.PREFERENCE_ONLINE
|
||||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_REQUEST_ACK
|
import ch.dissem.apps.abit.util.Constants.PREFERENCE_REQUEST_ACK
|
||||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_REQUIRE_CHARGING
|
import ch.dissem.apps.abit.util.Constants.PREFERENCE_REQUIRE_CHARGING
|
||||||
|
import ch.dissem.apps.abit.util.Constants.PREFERENCE_SEPARATE_IDENTITIES
|
||||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_WIFI_ONLY
|
import ch.dissem.apps.abit.util.Constants.PREFERENCE_WIFI_ONLY
|
||||||
import org.jetbrains.anko.batteryManager
|
import org.jetbrains.anko.batteryManager
|
||||||
import org.jetbrains.anko.connectivityManager
|
import org.jetbrains.anko.connectivityManager
|
||||||
@ -48,6 +50,8 @@ class Preferences internal constructor(private val ctx: Context) {
|
|||||||
|
|
||||||
private val isAllowedForCharging get() = !requireCharging || isCharging
|
private val isAllowedForCharging get() = !requireCharging || isCharging
|
||||||
|
|
||||||
|
private val sharedPreferences = ctx.defaultSharedPreferences
|
||||||
|
|
||||||
private val isCharging
|
private val isCharging
|
||||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
ctx.batteryManager.isCharging
|
ctx.batteryManager.isCharging
|
||||||
@ -58,20 +62,20 @@ class Preferences internal constructor(private val ctx: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var wifiOnly
|
var wifiOnly
|
||||||
get() = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_WIFI_ONLY, true)
|
get() = sharedPreferences.getBoolean(PREFERENCE_WIFI_ONLY, true)
|
||||||
set(value) {
|
set(value) {
|
||||||
ctx.defaultSharedPreferences.edit()
|
sharedPreferences.edit()
|
||||||
.putBoolean(PREFERENCE_WIFI_ONLY, value)
|
.putBoolean(PREFERENCE_WIFI_ONLY, value)
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
val requireCharging get() = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_REQUIRE_CHARGING, true)
|
val requireCharging get() = sharedPreferences.getBoolean(PREFERENCE_REQUIRE_CHARGING, true)
|
||||||
|
|
||||||
val emulateConversations get() = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_EMULATE_CONVERSATIONS, true)
|
val emulateConversations get() = sharedPreferences.getBoolean(PREFERENCE_EMULATE_CONVERSATIONS, true)
|
||||||
|
|
||||||
val exportDirectory by lazy { File(ctx.filesDir, "exports") }
|
val exportDirectory by lazy { File(ctx.filesDir, "exports") }
|
||||||
|
|
||||||
val requestAcknowledgements = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_REQUEST_ACK, true)
|
val requestAcknowledgements = sharedPreferences.getBoolean(PREFERENCE_REQUEST_ACK, true)
|
||||||
|
|
||||||
fun cleanupExportDirectory() {
|
fun cleanupExportDirectory() {
|
||||||
if (exportDirectory.exists()) {
|
if (exportDirectory.exists()) {
|
||||||
@ -88,9 +92,9 @@ class Preferences internal constructor(private val ctx: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var online
|
var online
|
||||||
get() = ctx.defaultSharedPreferences.getBoolean(PREFERENCE_ONLINE, true)
|
get() = sharedPreferences.getBoolean(PREFERENCE_ONLINE, true)
|
||||||
set(value) {
|
set(value) {
|
||||||
ctx.defaultSharedPreferences.edit()
|
sharedPreferences.edit()
|
||||||
.putBoolean(PREFERENCE_ONLINE, value)
|
.putBoolean(PREFERENCE_ONLINE, value)
|
||||||
.apply()
|
.apply()
|
||||||
if (value) {
|
if (value) {
|
||||||
@ -100,6 +104,16 @@ class Preferences internal constructor(private val ctx: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val separateIdentities
|
||||||
|
get() = sharedPreferences.getBoolean(PREFERENCE_SEPARATE_IDENTITIES, false)
|
||||||
|
|
||||||
|
val currentIdentity
|
||||||
|
get() = Singleton.getIdentity(ctx)
|
||||||
|
|
||||||
|
val listeningPort
|
||||||
|
get() = sharedPreferences.getString("listening_port", null)?.toIntOrNull()
|
||||||
|
?: 8444
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var instance: WeakReference<Preferences>? = null
|
private var instance: WeakReference<Preferences>? = null
|
||||||
|
|
||||||
|
@ -4,8 +4,15 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/empty_trash"
|
android:id="@+id/empty_trash"
|
||||||
app:showAsAction="never"
|
|
||||||
android:icon="@drawable/ic_action_delete"
|
android:icon="@drawable/ic_action_delete"
|
||||||
android:title="@string/empty_trash"
|
android:title="@string/empty_trash"
|
||||||
android:visible="false"/>
|
android:visible="false"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/delete_all"
|
||||||
|
android:icon="@drawable/ic_action_delete"
|
||||||
|
android:title="@string/delete_all_messages_in_list"
|
||||||
|
android:visible="true"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
@ -145,4 +145,5 @@ Als Alternative kann in den Einstellungen ein vertrauenswürdiger Knoten konfigu
|
|||||||
<string name="preference_group_user_experience">Verhalten</string>
|
<string name="preference_group_user_experience">Verhalten</string>
|
||||||
<string name="preference_group_user_experience_summary">Ändern, wie Nachrichten dargestellt werden</string>
|
<string name="preference_group_user_experience_summary">Ändern, wie Nachrichten dargestellt werden</string>
|
||||||
<string name="bitmessage_service_description">Hält die Verbindung zum Bitmessage-Netzwerk.</string>
|
<string name="bitmessage_service_description">Hält die Verbindung zum Bitmessage-Netzwerk.</string>
|
||||||
|
<string name="preference_port">Port</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -156,4 +156,10 @@ As an alternative you could configure a trusted node in the settings, but as of
|
|||||||
<string name="online">Online</string>
|
<string name="online">Online</string>
|
||||||
<string name="warning_low_memory">Low memory!</string>
|
<string name="warning_low_memory">Low memory!</string>
|
||||||
<string name="bitmessage_service_description">Keeps the connection to the bitmessage network.</string>
|
<string name="bitmessage_service_description">Keeps the connection to the bitmessage network.</string>
|
||||||
|
<string name="preference_port">Port</string>
|
||||||
|
<string name="preference_port_summary">Listen on this port for incoming connections. You might need to shortly go offline before the new port is used.</string>
|
||||||
|
<string name="preference_separate_identities_summary">Show messages for selected identity only</string>
|
||||||
|
<string name="preference_separate_identities">Filter messages by identity</string>
|
||||||
|
<string name="delete_all_messages_in_list">Delete all</string>
|
||||||
|
<string name="delete_all_messages_in_list_ask">Delete all messages in list?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -3,10 +3,15 @@
|
|||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
android:key="preference_ux"
|
android:key="preference_ux"
|
||||||
android:title="@string/preference_group_user_experience"
|
android:persistent="false"
|
||||||
android:summary="@string/preference_group_user_experience_summary"
|
android:summary="@string/preference_group_user_experience_summary"
|
||||||
android:persistent="false">
|
android:title="@string/preference_group_user_experience">
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="separate_identities"
|
||||||
|
android:summary="@string/preference_separate_identities_summary"
|
||||||
|
android:title="@string/preference_separate_identities" />
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:key="emulate_conversations"
|
android:key="emulate_conversations"
|
||||||
@ -22,9 +27,9 @@
|
|||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
android:key="preference_network_and_performance"
|
android:key="preference_network_and_performance"
|
||||||
android:title="@string/preference_group_network_and_performance"
|
android:persistent="false"
|
||||||
android:summary="@string/preference_group_network_and_performance_summary"
|
android:summary="@string/preference_group_network_and_performance_summary"
|
||||||
android:persistent="false">
|
android:title="@string/preference_group_network_and_performance">
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
@ -46,9 +51,9 @@
|
|||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
android:key="preference_advanced"
|
android:key="preference_advanced"
|
||||||
android:title="@string/preference_group_advanced"
|
android:persistent="false"
|
||||||
android:summary="@string/preference_group_advanced_summary"
|
android:summary="@string/preference_group_advanced_summary"
|
||||||
android:persistent="false">
|
android:title="@string/preference_group_advanced">
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="cleanup"
|
android:key="cleanup"
|
||||||
@ -68,6 +73,13 @@
|
|||||||
android:summary="@string/status_summary"
|
android:summary="@string/status_summary"
|
||||||
android:title="@string/status" />
|
android:title="@string/status" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:defaultValue="8444"
|
||||||
|
android:key="listening_port"
|
||||||
|
android:summary="@string/preference_port_summary"
|
||||||
|
android:title="@string/preference_port"
|
||||||
|
android:numeric="integer"/>
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
|
Loading…
Reference in New Issue
Block a user