Add message grouping by subject
This commit is contained in:
		@@ -114,7 +114,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
 | 
			
		||||
        val toolbar = findViewById<Toolbar>(R.id.toolbar)
 | 
			
		||||
        setSupportActionBar(toolbar)
 | 
			
		||||
 | 
			
		||||
        val listFragment = MessageListFragment()
 | 
			
		||||
        val listFragment = ConversationListFragment()
 | 
			
		||||
        supportFragmentManager
 | 
			
		||||
            .beginTransaction()
 | 
			
		||||
            .replace(R.id.item_list, listFragment)
 | 
			
		||||
@@ -303,6 +303,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
 | 
			
		||||
                    currentLabel.value = intent.getSerializableExtra(EXTRA_SHOW_LABEL) as Label
 | 
			
		||||
                } else if (currentLabel.value == null) {
 | 
			
		||||
                    currentLabel.value = labels[0]
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                for (label in labels) {
 | 
			
		||||
                    addLabelEntry(label)
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,11 @@ package ch.dissem.apps.abit.listener
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import ch.dissem.apps.abit.MainActivity
 | 
			
		||||
import ch.dissem.apps.abit.notification.NewMessageNotification
 | 
			
		||||
import ch.dissem.apps.abit.util.Preferences
 | 
			
		||||
import ch.dissem.bitmessage.BitmessageContext
 | 
			
		||||
import ch.dissem.bitmessage.entity.Plaintext
 | 
			
		||||
import ch.dissem.bitmessage.ports.MessageRepository
 | 
			
		||||
import ch.dissem.bitmessage.utils.ConversationService
 | 
			
		||||
import java.util.*
 | 
			
		||||
import java.util.concurrent.Executors
 | 
			
		||||
 | 
			
		||||
@@ -33,14 +36,26 @@ import java.util.concurrent.Executors
 | 
			
		||||
 * notifications should be combined.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
class MessageListener(ctx: Context) : BitmessageContext.Listener {
 | 
			
		||||
class MessageListener(ctx: Context) : BitmessageContext.Listener.WithContext {
 | 
			
		||||
    override fun setContext(ctx: BitmessageContext) {
 | 
			
		||||
        messageRepo = ctx.messages
 | 
			
		||||
        conversationService = ConversationService(messageRepo)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val unacknowledged = LinkedList<Plaintext>()
 | 
			
		||||
    private var numberOfUnacknowledgedMessages = 0
 | 
			
		||||
    private val notification = NewMessageNotification(ctx)
 | 
			
		||||
    private val pool = Executors.newSingleThreadExecutor()
 | 
			
		||||
    private lateinit var messageRepo: MessageRepository
 | 
			
		||||
    private lateinit var conversationService: ConversationService
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        emulateConversations = Preferences.isEmulateConversations(ctx)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun receive(plaintext: Plaintext) {
 | 
			
		||||
        pool.submit {
 | 
			
		||||
            updateConversation(plaintext)
 | 
			
		||||
            unacknowledged.addFirst(plaintext)
 | 
			
		||||
            numberOfUnacknowledgedMessages++
 | 
			
		||||
            if (unacknowledged.size > 5) {
 | 
			
		||||
@@ -65,4 +80,17 @@ class MessageListener(ctx: Context) : BitmessageContext.Listener {
 | 
			
		||||
            numberOfUnacknowledgedMessages = 0
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateConversation(plaintext: Plaintext) {
 | 
			
		||||
        if (emulateConversations && plaintext.encoding != Plaintext.Encoding.EXTENDED) {
 | 
			
		||||
            conversationService.getSubject(listOf(plaintext))?.let { subject ->
 | 
			
		||||
                plaintext.conversationId = UUID.nameUUIDFromBytes(subject.toByteArray())
 | 
			
		||||
                messageRepo.save(plaintext)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private var emulateConversations = false
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ import java.util.regex.Pattern
 | 
			
		||||
 */
 | 
			
		||||
object Constants {
 | 
			
		||||
    const val PREFERENCE_WIFI_ONLY = "wifi_only"
 | 
			
		||||
    const val PREFERENCE_EMULATE_CONVERSATIONS = "emulate_conversations"
 | 
			
		||||
    const val PREFERENCE_TRUSTED_NODE = "trusted_node"
 | 
			
		||||
    const val PREFERENCE_SYNC_TIMEOUT = "sync_timeout"
 | 
			
		||||
    const val PREFERENCE_SERVER_POW = "server_pow"
 | 
			
		||||
 
 | 
			
		||||
@@ -17,15 +17,16 @@
 | 
			
		||||
package ch.dissem.apps.abit.util
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.preference.PreferenceManager
 | 
			
		||||
import ch.dissem.apps.abit.R
 | 
			
		||||
import ch.dissem.apps.abit.notification.ErrorNotification
 | 
			
		||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_EMULATE_CONVERSATIONS
 | 
			
		||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_FULL_NODE
 | 
			
		||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_REQUEST_ACK
 | 
			
		||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_SYNC_TIMEOUT
 | 
			
		||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE
 | 
			
		||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_WIFI_ONLY
 | 
			
		||||
import org.jetbrains.anko.connectivityManager
 | 
			
		||||
import org.jetbrains.anko.defaultSharedPreferences
 | 
			
		||||
import org.slf4j.LoggerFactory
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
@@ -77,50 +78,41 @@ object Preferences {
 | 
			
		||||
        return 8444
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getTimeoutInSeconds(ctx: Context): Long {
 | 
			
		||||
        val preference = getPreference(ctx, PREFERENCE_SYNC_TIMEOUT) ?: return 120
 | 
			
		||||
        return preference.toLong()
 | 
			
		||||
    }
 | 
			
		||||
    fun getTimeoutInSeconds(ctx: Context): Long =
 | 
			
		||||
        getPreference(ctx, PREFERENCE_SYNC_TIMEOUT)?.toLong() ?: 120
 | 
			
		||||
 | 
			
		||||
    private fun getPreference(ctx: Context, name: String): String? {
 | 
			
		||||
        val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
 | 
			
		||||
    private fun getPreference(ctx: Context, name: String): String? =
 | 
			
		||||
        ctx.defaultSharedPreferences.getString(name, null)
 | 
			
		||||
 | 
			
		||||
        return preferences.getString(name, null)
 | 
			
		||||
    }
 | 
			
		||||
    fun isConnectionAllowed(ctx: Context) =
 | 
			
		||||
        !isWifiOnly(ctx) || !ctx.connectivityManager.isActiveNetworkMetered
 | 
			
		||||
 | 
			
		||||
    fun isConnectionAllowed(ctx: Context) = !isWifiOnly(ctx) || !ctx.connectivityManager.isActiveNetworkMetered
 | 
			
		||||
 | 
			
		||||
    fun isWifiOnly(ctx: Context): Boolean {
 | 
			
		||||
        val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
 | 
			
		||||
        return preferences.getBoolean(PREFERENCE_WIFI_ONLY, true)
 | 
			
		||||
    }
 | 
			
		||||
    fun isWifiOnly(ctx: Context) =
 | 
			
		||||
        ctx.defaultSharedPreferences.getBoolean(PREFERENCE_WIFI_ONLY, true)
 | 
			
		||||
 | 
			
		||||
    fun setWifiOnly(ctx: Context, status: Boolean) {
 | 
			
		||||
        val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
 | 
			
		||||
        preferences.edit().putBoolean(PREFERENCE_WIFI_ONLY, status).apply()
 | 
			
		||||
        ctx.defaultSharedPreferences.edit()
 | 
			
		||||
            .putBoolean(PREFERENCE_WIFI_ONLY, status)
 | 
			
		||||
            .apply()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun isFullNodeActive(ctx: Context): Boolean {
 | 
			
		||||
        val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
 | 
			
		||||
        return preferences.getBoolean(PREFERENCE_FULL_NODE, false)
 | 
			
		||||
    }
 | 
			
		||||
    fun isEmulateConversations(ctx: Context) =
 | 
			
		||||
        ctx.defaultSharedPreferences.getBoolean(PREFERENCE_EMULATE_CONVERSATIONS, true)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    fun isFullNodeActive(ctx: Context) =
 | 
			
		||||
        ctx.defaultSharedPreferences.getBoolean(PREFERENCE_FULL_NODE, false)
 | 
			
		||||
 | 
			
		||||
    fun setFullNodeActive(ctx: Context, status: Boolean) {
 | 
			
		||||
        val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
 | 
			
		||||
        preferences.edit().putBoolean(PREFERENCE_FULL_NODE, status).apply()
 | 
			
		||||
        ctx.defaultSharedPreferences.edit()
 | 
			
		||||
            .putBoolean(PREFERENCE_FULL_NODE, status)
 | 
			
		||||
            .apply()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getExportDirectory(ctx: Context) = File(ctx.filesDir, "exports")
 | 
			
		||||
 | 
			
		||||
    fun requestAcknowledgements(ctx: Context): Boolean {
 | 
			
		||||
        val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
 | 
			
		||||
        return preferences.getBoolean(PREFERENCE_REQUEST_ACK, true)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setRequestAcknowledgements(ctx: Context, status: Boolean) {
 | 
			
		||||
        val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
 | 
			
		||||
        preferences.edit().putBoolean(PREFERENCE_REQUEST_ACK, status).apply()
 | 
			
		||||
    }
 | 
			
		||||
    fun requestAcknowledgements(ctx: Context) =
 | 
			
		||||
        ctx.defaultSharedPreferences.getBoolean(PREFERENCE_REQUEST_ACK, true)
 | 
			
		||||
 | 
			
		||||
    fun cleanupExportDirectory(ctx: Context) {
 | 
			
		||||
        val exportDirectory = getExportDirectory(ctx)
 | 
			
		||||
 
 | 
			
		||||
@@ -137,4 +137,6 @@ Als Alternative kann in den Einstellungen ein vertrauenswürdiger Knoten konfigu
 | 
			
		||||
    <string name="broadcasts">Broadcasts</string>
 | 
			
		||||
    <string name="encoding_simple">einfach</string>
 | 
			
		||||
    <string name="encoding_extended">erweitert</string>
 | 
			
		||||
    <string name="emulate_conversations">Konversation erraten</string>
 | 
			
		||||
    <string name="emulate_conversations_summary">Benutze Betreff um zu erraten welche Nachrichten zusammengehören. Die Reihenfolge stimmt häufig nicht.</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -137,4 +137,6 @@ As an alternative you could configure a trusted node in the settings, but as of
 | 
			
		||||
    <string name="encoding_simple">simple</string>
 | 
			
		||||
    <string name="encoding_extended">extended</string>
 | 
			
		||||
    <string name="context_menu">actions</string>
 | 
			
		||||
    <string name="emulate_conversations">Guess conversations</string>
 | 
			
		||||
    <string name="emulate_conversations_summary">Use subject to determine which messages belong together. The order will likely be wrong.</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,11 @@
 | 
			
		||||
        android:key="wifi_only"
 | 
			
		||||
        android:summary="@string/wifi_only_summary"
 | 
			
		||||
        android:title="@string/wifi_only" />
 | 
			
		||||
    <android.support.v7.preference.SwitchPreferenceCompat
 | 
			
		||||
        android:defaultValue="true"
 | 
			
		||||
        android:key="emulate_conversations"
 | 
			
		||||
        android:summary="@string/emulate_conversations_summary"
 | 
			
		||||
        android:title="@string/emulate_conversations" />
 | 
			
		||||
    <android.support.v7.preference.SwitchPreferenceCompat
 | 
			
		||||
        android:defaultValue="true"
 | 
			
		||||
        android:key="request_acknowledgements"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user