From d65bf005539ab1ad752e9938dbf71c97af9f265f Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Thu, 22 Feb 2018 02:14:14 +0100
Subject: [PATCH] Allow hiding message types

---
 app/build.gradle.kts                          |  1 +
 app/src/main/AndroidManifest.xml              |  4 +-
 .../persistence/AccountDatabase.kt            |  1 -
 .../persistence/QuasselBacklogStorage.kt      |  5 +-
 .../persistence/QuasselDatabase.kt            | 71 +++++++++++++---
 .../quasseldroid_ng/ui/chat/ChatActivity.kt   | 68 ++++++++++++++--
 .../ui/chat/buffers/BufferListAdapter.kt      | 50 ++++++------
 .../chat/buffers/BufferViewConfigFragment.kt  | 49 ++++++++---
 .../ui/chat/messages/MessageListFragment.kt   | 23 +++---
 .../ui/viewmodel/QuasselViewModel.kt          | 32 ++++----
 .../util/service/ServiceBoundActivity.kt      | 10 ++-
 .../util/service/ServiceBoundFragment.kt      |  9 ++-
 app/src/main/res/menu/activity_main.xml       |  9 ++-
 app/src/main/res/menu/setup_edit_account.xml  |  4 +-
 app/src/main/res/values/strings.xml           | 34 +++-----
 app/src/main/res/values/strings_messages.xml  | 36 +++++++++
 ...references.xml => strings_preferences.xml} |  2 +-
 app/src/main/res/values/themes_quassel.xml    |  4 +-
 .../java/de/kuschku/libquassel/util/Flag.kt   | 64 +++++++++------
 .../de/kuschku/libquassel/util/LongFlag.kt    | 81 ++++++++++---------
 .../de/kuschku/libquassel/util/ShortFlag.kt   | 73 +++++++++--------
 21 files changed, 412 insertions(+), 218 deletions(-)
 create mode 100644 app/src/main/res/values/strings_messages.xml
 rename app/src/main/res/values/{preferences.xml => strings_preferences.xml} (98%)

diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 16103cc43..46b14cc6d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -139,6 +139,7 @@ dependencies {
 
   // UI
   implementation("me.zhanghai.android.materialprogressbar", "library", "1.4.2")
+  implementation("com.afollestad.material-dialogs", "core", "0.9.6.0")
 
   // Quality Assurance
   implementation(project(":malheur"))
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b1135242e..cbd8970d0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -31,7 +31,7 @@
     <activity
       android:name=".ui.settings.SettingsActivity"
       android:exported="false"
-      android:label="@string/app_name"
+      android:label="@string/label_settings"
       android:parentActivityName=".ui.chat.ChatActivity"
       android:windowSoftInputMode="adjustResize" />
     <activity
@@ -51,7 +51,7 @@
       android:name=".service.QuasselService"
       android:description="@string/connection_service_description"
       android:exported="false"
-      android:label="@string/connection_service" />
+      android:label="@string/connection_service_title" />
   </application>
 
 </manifest>
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/AccountDatabase.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/AccountDatabase.kt
index ebe1e6100..3be55470e 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/AccountDatabase.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/AccountDatabase.kt
@@ -43,7 +43,6 @@ abstract class AccountDatabase : RoomDatabase() {
 
   object Creator {
     private var database: AccountDatabase? = null
-      private set
 
     // For Singleton instantiation
     private val LOCK = Any()
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselBacklogStorage.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselBacklogStorage.kt
index a0134e299..f3eb2a29a 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselBacklogStorage.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselBacklogStorage.kt
@@ -5,8 +5,9 @@ import de.kuschku.libquassel.protocol.Message
 import de.kuschku.libquassel.session.BacklogStorage
 
 class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage {
-  override fun storeMessages(vararg messages: Message, initialLoad: Boolean)
-    = storeMessages(messages.asIterable(), initialLoad)
+  override fun storeMessages(vararg messages: Message, initialLoad: Boolean) = storeMessages(
+    messages.asIterable(), initialLoad
+  )
 
   override fun storeMessages(messages: Iterable<Message>, initialLoad: Boolean) {
     if (initialLoad)
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselDatabase.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselDatabase.kt
index 1c9637abe..5d7d0746a 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselDatabase.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselDatabase.kt
@@ -1,18 +1,24 @@
 package de.kuschku.quasseldroid_ng.persistence
 
+import android.arch.lifecycle.LiveData
 import android.arch.paging.DataSource
+import android.arch.persistence.db.SupportSQLiteDatabase
 import android.arch.persistence.room.*
+import android.arch.persistence.room.migration.Migration
 import android.content.Context
 import android.support.annotation.IntRange
 import android.support.v7.recyclerview.extensions.DiffCallback
 import de.kuschku.libquassel.protocol.Message_Flag
 import de.kuschku.libquassel.protocol.Message_Type
+import de.kuschku.quasseldroid_ng.persistence.QuasselDatabase.DatabaseMessage
+import de.kuschku.quasseldroid_ng.persistence.QuasselDatabase.Filtered
 import org.threeten.bp.Instant
 
-@Database(entities = [(QuasselDatabase.DatabaseMessage::class)], version = 2)
-@TypeConverters(QuasselDatabase.DatabaseMessage.MessageTypeConverters::class)
+@Database(entities = [DatabaseMessage::class, Filtered::class], version = 3)
+@TypeConverters(DatabaseMessage.MessageTypeConverters::class)
 abstract class QuasselDatabase : RoomDatabase() {
   abstract fun message(): MessageDao
+  abstract fun filtered(): FilteredDao
 
   @Entity(tableName = "message")
   data class DatabaseMessage(
@@ -43,12 +49,10 @@ abstract class QuasselDatabase : RoomDatabase() {
 
     object MessageDiffCallback : DiffCallback<DatabaseMessage>() {
       override fun areContentsTheSame(oldItem: QuasselDatabase.DatabaseMessage,
-                                      newItem: QuasselDatabase.DatabaseMessage)
-        = oldItem == newItem
+                                      newItem: QuasselDatabase.DatabaseMessage) = oldItem == newItem
 
       override fun areItemsTheSame(oldItem: QuasselDatabase.DatabaseMessage,
-                                   newItem: QuasselDatabase.DatabaseMessage)
-        = oldItem.messageId == newItem.messageId
+                                   newItem: QuasselDatabase.DatabaseMessage) = oldItem.messageId == newItem.messageId
     }
   }
 
@@ -60,8 +64,10 @@ abstract class QuasselDatabase : RoomDatabase() {
     @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId ASC")
     fun findByBufferId(bufferId: Int): List<DatabaseMessage>
 
-    @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId DESC")
-    fun findByBufferIdPaged(bufferId: Int): DataSource.Factory<Int, DatabaseMessage>
+    @Query(
+      "SELECT * FROM message WHERE bufferId = :bufferId AND type & ~ :type > 0 ORDER BY messageId DESC"
+    )
+    fun findByBufferIdPaged(bufferId: Int, type: Int): DataSource.Factory<Int, DatabaseMessage>
 
     @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId DESC LIMIT 1")
     fun findLastByBufferId(bufferId: Int): DatabaseMessage?
@@ -90,6 +96,41 @@ abstract class QuasselDatabase : RoomDatabase() {
     fun clearMessages(@IntRange(from = 0) bufferId: Int, first: Int, last: Int)
   }
 
+  @Entity(tableName = "filtered", primaryKeys = ["accountId", "bufferId"])
+  data class Filtered(
+    var accountId: Long,
+    var bufferId: Int,
+    var filtered: Int
+  )
+
+  @Dao
+  interface FilteredDao {
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun replace(vararg entities: Filtered)
+
+    @Query(
+      "SELECT filtered FROM filtered WHERE bufferId = :bufferId AND accountId = :accountId UNION SELECT 0 as filtered ORDER BY filtered DESC LIMIT 1"
+    )
+    fun get(accountId: Long, bufferId: Int): Int?
+
+    @Query(
+      "SELECT filtered FROM filtered WHERE bufferId = :bufferId AND accountId = :accountId UNION SELECT 0 as filtered ORDER BY filtered DESC LIMIT 1"
+    )
+    fun listen(accountId: Long, bufferId: Int): LiveData<Int>
+
+    @Query("SELECT * FROM filtered WHERE accountId = :accountId")
+    fun listen(accountId: Long): LiveData<List<Filtered>>
+
+    @Query("DELETE FROM filtered")
+    fun clear()
+
+    @Query("DELETE FROM filtered WHERE accountId = :accountId")
+    fun clear(accountId: Long)
+
+    @Query("DELETE FROM filtered WHERE bufferId = :bufferId AND accountId = :accountId")
+    fun clear(accountId: Long, bufferId: Int)
+  }
+
   object Creator {
     private var database: QuasselDatabase? = null
 
@@ -103,6 +144,14 @@ abstract class QuasselDatabase : RoomDatabase() {
             database = Room.databaseBuilder(
               context.applicationContext,
               QuasselDatabase::class.java, DATABASE_NAME
+            ).addMigrations(
+              object : Migration(2, 3) {
+                override fun migrate(database: SupportSQLiteDatabase) {
+                  database.execSQL(
+                    "CREATE TABLE filtered(bufferId INTEGER, accountId INTEGER, filtered INTEGER, PRIMARY KEY(accountId, bufferId));"
+                  )
+                }
+              }
             ).build()
           }
         }
@@ -116,7 +165,9 @@ abstract class QuasselDatabase : RoomDatabase() {
   }
 }
 
-fun QuasselDatabase.MessageDao.clearMessages(@IntRange(from = 0)
-                                             bufferId: Int, idRange: kotlin.ranges.IntRange) {
+fun QuasselDatabase.MessageDao.clearMessages(
+  @IntRange(from = 0) bufferId: Int,
+  idRange: kotlin.ranges.IntRange
+) {
   this.clearMessages(bufferId, idRange.first, idRange.last)
 }
\ No newline at end of file
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt
index 0f7582815..471a9054e 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt
@@ -17,8 +17,13 @@ import android.widget.Button
 import android.widget.EditText
 import butterknife.BindView
 import butterknife.ButterKnife
+import com.afollestad.materialdialogs.MaterialDialog
+import de.kuschku.libquassel.protocol.Message
+import de.kuschku.libquassel.protocol.Message_Type
 import de.kuschku.libquassel.session.ConnectionState
 import de.kuschku.libquassel.session.SocketAddress
+import de.kuschku.libquassel.util.and
+import de.kuschku.libquassel.util.or
 import de.kuschku.quasseldroid_ng.Keys
 import de.kuschku.quasseldroid_ng.R
 import de.kuschku.quasseldroid_ng.persistence.AccountDatabase
@@ -84,8 +89,8 @@ class ChatActivity : ServiceBoundActivity() {
     drawerToggle = ActionBarDrawerToggle(
       this,
       drawerLayout,
-      R.string.drawer_open,
-      R.string.drawer_close
+      R.string.label_drawer_open,
+      R.string.label_drawer_close
     )
     drawerToggle.syncState()
 
@@ -94,8 +99,6 @@ class ChatActivity : ServiceBoundActivity() {
       if (backendValue != null) {
         val database = AccountDatabase.Creator.init(this)
         handler.post {
-          val accountId = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE)
-                            ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
           if (accountId == -1L) {
             setResult(Activity.RESULT_OK)
             finish()
@@ -179,10 +182,61 @@ class ChatActivity : ServiceBoundActivity() {
   }
 
   override fun onOptionsItemSelected(item: MenuItem?) = when (item?.itemId) {
-    android.R.id.home -> {
+    android.R.id.home    -> {
       drawerToggle.onOptionsItemSelected(item)
     }
-    R.id.clear        -> {
+
+    R.id.filter_messages -> {
+      handler.post {
+        val buffer = viewModel.getBuffer().value
+        if (buffer != null) {
+          val filtered = Message_Type.of(database.filtered().get(accountId, buffer) ?: 0)
+          val flags = intArrayOf(
+            Message.MessageType.Join.bit or Message.MessageType.NetsplitJoin.bit,
+            Message.MessageType.Part.bit,
+            Message.MessageType.Quit.bit or Message.MessageType.NetsplitQuit.bit,
+            Message.MessageType.Nick.bit,
+            Message.MessageType.Mode.bit,
+            Message.MessageType.Topic.bit
+          )
+          val selectedIndices = flags.withIndex().mapNotNull { (index, flag) ->
+            if ((filtered and flag).isNotEmpty()) {
+              index
+            } else {
+              null
+            }
+          }.toTypedArray()
+
+          runOnUiThread {
+            MaterialDialog.Builder(this)
+              .title(R.string.label_filter_messages)
+              .items(R.array.message_filter_types)
+              .itemsIds(flags)
+              .itemsCallbackMultiChoice(selectedIndices, { _, _, _ -> false })
+              .positiveText(R.string.label_select_multiple)
+              .negativeText(R.string.label_cancel)
+              .onPositive { dialog, _ ->
+                val selected = dialog.selectedIndices ?: emptyArray()
+                handler.post {
+                  val newlyFiltered = selected
+                    .map { flags[it] }
+                    .fold(Message_Type.of()) { acc, i -> acc or i }
+
+                  database.filtered().replace(
+                    QuasselDatabase.Filtered(accountId, buffer, newlyFiltered.value)
+                  )
+                }
+              }.negativeColorAttr(R.attr.colorTextPrimary)
+              .backgroundColorAttr(R.attr.colorBackgroundCard)
+              .contentColorAttr(R.attr.colorTextPrimary)
+              .build()
+              .show()
+          }
+        }
+      }
+      true
+    }
+    R.id.clear           -> {
       handler.post {
         viewModel.sessionManager { manager ->
           viewModel.getBuffer().let { buffer ->
@@ -197,7 +251,7 @@ class ChatActivity : ServiceBoundActivity() {
       }
       true
     }
-    R.id.settings     -> {
+    R.id.settings        -> {
       startActivity(Intent(applicationContext, SettingsActivity::class.java))
       true
     }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferListAdapter.kt
index b6c653436..bf4dc0cc0 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferListAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferListAdapter.kt
@@ -15,10 +15,7 @@ import android.widget.ImageView
 import android.widget.TextView
 import butterknife.BindView
 import butterknife.ButterKnife
-import de.kuschku.libquassel.protocol.BufferId
-import de.kuschku.libquassel.protocol.Buffer_Activity
-import de.kuschku.libquassel.protocol.Buffer_Type
-import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.protocol.*
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
 import de.kuschku.libquassel.util.hasFlag
@@ -143,8 +140,9 @@ class BufferListAdapter(
     val network: INetwork.NetworkInfo,
     val bufferStatus: BufferStatus,
     val description: CharSequence,
-    val activity: Buffer_Activity,
-    val highlights: Int = 0
+    val activity: Message_Types,
+    val highlights: Int = 0,
+    val bufferActivity: Buffer_Activities = Buffer_Activity.of(Buffer_Activity.NoActivity)
   )
 
   data class BufferState(
@@ -204,11 +202,11 @@ class BufferListAdapter(
         networkId = props.info.networkId
 
         name.setTextColor(
-          when (props.activity) {
-            Buffer_Activity.NoActivity    -> none
-            Buffer_Activity.OtherActivity -> activity
-            Buffer_Activity.NewMessage    -> message
-            Buffer_Activity.Highlight     -> highlight
+          when {
+            props.bufferActivity.hasFlag(Buffer_Activity.Highlight)     -> highlight
+            props.bufferActivity.hasFlag(Buffer_Activity.NewMessage)    -> message
+            props.bufferActivity.hasFlag(Buffer_Activity.OtherActivity) -> activity
+            else                                                        -> none
           }
         )
 
@@ -276,11 +274,11 @@ class BufferListAdapter(
         description.text = props.description
 
         name.setTextColor(
-          when (props.activity) {
-            Buffer_Activity.NoActivity    -> none
-            Buffer_Activity.OtherActivity -> activity
-            Buffer_Activity.NewMessage    -> message
-            Buffer_Activity.Highlight     -> highlight
+          when {
+            props.bufferActivity.hasFlag(Buffer_Activity.Highlight)     -> highlight
+            props.bufferActivity.hasFlag(Buffer_Activity.NewMessage)    -> message
+            props.bufferActivity.hasFlag(Buffer_Activity.OtherActivity) -> activity
+            else                                                        -> none
           }
         )
 
@@ -351,11 +349,11 @@ class BufferListAdapter(
         description.text = props.description
 
         name.setTextColor(
-          when (props.activity) {
-            Buffer_Activity.NoActivity    -> none
-            Buffer_Activity.OtherActivity -> activity
-            Buffer_Activity.NewMessage    -> message
-            Buffer_Activity.Highlight     -> highlight
+          when {
+            props.bufferActivity.hasFlag(Buffer_Activity.Highlight)     -> highlight
+            props.bufferActivity.hasFlag(Buffer_Activity.NewMessage)    -> message
+            props.bufferActivity.hasFlag(Buffer_Activity.OtherActivity) -> activity
+            else                                                        -> none
           }
         )
 
@@ -429,11 +427,11 @@ class BufferListAdapter(
         description.text = props.description
 
         name.setTextColor(
-          when (props.activity) {
-            Buffer_Activity.NoActivity    -> none
-            Buffer_Activity.OtherActivity -> activity
-            Buffer_Activity.NewMessage    -> message
-            Buffer_Activity.Highlight     -> highlight
+          when {
+            props.bufferActivity.hasFlag(Buffer_Activity.Highlight)     -> highlight
+            props.bufferActivity.hasFlag(Buffer_Activity.NewMessage)    -> message
+            props.bufferActivity.hasFlag(Buffer_Activity.OtherActivity) -> activity
+            else                                                        -> none
           }
         )
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferViewConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferViewConfigFragment.kt
index d2e8bfb0d..7feb5e9e1 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferViewConfigFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferViewConfigFragment.kt
@@ -10,12 +10,19 @@ import android.widget.AdapterView
 import butterknife.BindView
 import butterknife.ButterKnife
 import de.kuschku.libquassel.protocol.BufferId
+import de.kuschku.libquassel.protocol.Buffer_Activity
+import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.protocol.Message_Type
+import de.kuschku.libquassel.util.hasFlag
+import de.kuschku.libquassel.util.minus
 import de.kuschku.quasseldroid_ng.R
+import de.kuschku.quasseldroid_ng.persistence.QuasselDatabase
 import de.kuschku.quasseldroid_ng.ui.settings.Settings
 import de.kuschku.quasseldroid_ng.ui.settings.data.AppearanceSettings
 import de.kuschku.quasseldroid_ng.ui.viewmodel.QuasselViewModel
 import de.kuschku.quasseldroid_ng.util.AndroidHandlerThread
 import de.kuschku.quasseldroid_ng.util.helper.map
+import de.kuschku.quasseldroid_ng.util.helper.zip
 import de.kuschku.quasseldroid_ng.util.irc.format.IrcFormatDeserializer
 import de.kuschku.quasseldroid_ng.util.service.ServiceBoundFragment
 
@@ -32,6 +39,7 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
   lateinit var chatList: RecyclerView
 
   private lateinit var viewModel: QuasselViewModel
+  private lateinit var database: QuasselDatabase
 
   private var ircFormatDeserializer: IrcFormatDeserializer? = null
   private lateinit var appearanceSettings: AppearanceSettings
@@ -41,6 +49,7 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
     super.onCreate(savedInstanceState)
 
     viewModel = ViewModelProviders.of(activity!!)[QuasselViewModel::class.java]
+    database = QuasselDatabase.Creator.init(activity!!)
     appearanceSettings = Settings.appearance(activity!!)
 
     if (ircFormatDeserializer == null) {
@@ -53,9 +62,7 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
     val view = inflater.inflate(R.layout.fragment_chat_list, container, false)
     ButterKnife.bind(this, view)
 
-    val adapter = BufferViewConfigAdapter(
-      this, viewModel.bufferViewConfigs
-    )
+    val adapter = BufferViewConfigAdapter(this, viewModel.bufferViewConfigs)
 
     chatListSpinner.adapter = adapter
     chatListSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
@@ -70,14 +77,34 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
 
     chatList.adapter = BufferListAdapter(
       this,
-      viewModel.bufferList.map {
-        it.map {
-          it.copy(
-            description = ircFormatDeserializer?.formatString(
-              it.description.toString(), appearanceSettings.colorizeMirc
-            ) ?: it.description
-          )
-        }
+      viewModel.bufferList.zip(database.filtered().listen(accountId)).map {
+        val (list, activityList) = it
+        val activities = activityList.map { it.bufferId to it.filtered }.toMap()
+        list
+          ?.map {
+            val activity = it.activity - (activities[it.info.bufferId] ?: 0)
+            it.bufferActivity to it.copy(
+              description = ircFormatDeserializer?.formatString(
+                it.description.toString(), appearanceSettings.colorizeMirc
+              ) ?: it.description,
+              activity = activity,
+              bufferActivity = Buffer_Activity.of(
+                when {
+                  it.highlights > 0                     -> Buffer_Activity.Highlight
+                  activity.hasFlag(Message_Type.Plain) ||
+                  activity.hasFlag(Message_Type.Notice) ||
+                  activity.hasFlag(Message_Type.Action) -> Buffer_Activity.NewMessage
+                  activity.isNotEmpty()                 -> Buffer_Activity.OtherActivity
+                  else                                  -> Buffer_Activity.NoActivity
+                }
+              )
+            )
+          }?.filter { (minimumActivity, props) ->
+            minimumActivity.toInt() <= props.bufferActivity.toInt() ||
+            props.info.type.hasFlag(Buffer_Type.StatusBuffer)
+          }?.map { (_, props) ->
+            props
+          }
       },
       handlerThread::post,
       activity!!::runOnUiThread,
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/messages/MessageListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/messages/MessageListFragment.kt
index 1b5a92978..f3a923a63 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/messages/MessageListFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/messages/MessageListFragment.kt
@@ -52,7 +52,6 @@ class MessageListFragment : ServiceBoundFragment() {
     super.onCreate(savedInstanceState)
     viewModel = ViewModelProviders.of(activity!!)[QuasselViewModel::class.java]
     appearanceSettings = Settings.appearance(activity!!)
-    setHasOptionsMenu(true)
   }
 
   private val boundaryCallback = object :
@@ -87,16 +86,18 @@ class MessageListFragment : ServiceBoundFragment() {
     )
 
     database = QuasselDatabase.Creator.init(context!!.applicationContext)
-    val data = viewModel.getBuffer().switchMapNotNull {
-      LivePagedListBuilder(
-        database.message().findByBufferIdPaged(it),
-        PagedList.Config.Builder()
-          .setPageSize(backlogSettings.dynamicAmount)
-          .setPrefetchDistance(backlogSettings.dynamicAmount)
-          .setInitialLoadSizeHint(backlogSettings.dynamicAmount)
-          .setEnablePlaceholders(true)
-          .build()
-      ).setBoundaryCallback(boundaryCallback).build()
+    val data = viewModel.getBuffer().switchMapNotNull { buffer ->
+      database.filtered().listen(accountId, buffer).switchMapNotNull { filtered ->
+        LivePagedListBuilder(
+          database.message().findByBufferIdPaged(buffer, filtered),
+          PagedList.Config.Builder()
+            .setPageSize(backlogSettings.dynamicAmount)
+            .setPrefetchDistance(backlogSettings.dynamicAmount)
+            .setInitialLoadSizeHint(backlogSettings.dynamicAmount)
+            .setEnablePlaceholders(true)
+            .build()
+        ).setBoundaryCallback(boundaryCallback).build()
+      }
     }
 
     handler.post {
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt
index 5341b76c3..bd37272ed 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt
@@ -4,9 +4,7 @@ import android.arch.lifecycle.LiveData
 import android.arch.lifecycle.MutableLiveData
 import android.arch.lifecycle.ViewModel
 import de.kuschku.libquassel.protocol.BufferId
-import de.kuschku.libquassel.protocol.Buffer_Activity
 import de.kuschku.libquassel.protocol.Buffer_Type
-import de.kuschku.libquassel.protocol.Message
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
 import de.kuschku.libquassel.quassel.syncables.IrcChannel
@@ -232,16 +230,9 @@ class QuasselViewModel : ViewModel() {
               }.map { (info, network) ->
                 bufferSyncer.liveActivity(info.bufferId).switchMap { activity ->
                   bufferSyncer.liveHighlightCount(info.bufferId).map { highlights ->
-                    when {
-                      highlights > 0                               -> Buffer_Activity.Highlight
-                      activity.hasFlag(Message.MessageType.Plain) ||
-                      activity.hasFlag(Message.MessageType.Notice) ||
-                      activity.hasFlag(Message.MessageType.Action) -> Buffer_Activity.NewMessage
-                      activity.isNotEmpty()                        -> Buffer_Activity.OtherActivity
-                      else                                         -> Buffer_Activity.NoActivity
-                    }
+                    activity to highlights
                   }
-                }.switchMap { activity ->
+                }.switchMap { (activity, highlights) ->
                   when (info.type.toInt()) {
                     BufferInfo.Type.QueryBuffer.toInt()   -> {
                       network.liveIrcUser(info.bufferName).switchMap { user ->
@@ -256,7 +247,9 @@ class QuasselViewModel : ViewModel() {
                                 else                 -> BufferListAdapter.BufferStatus.ONLINE
                               },
                               description = realName,
-                              activity = activity
+                              activity = activity,
+                              highlights = highlights,
+                              bufferActivity = config.minimumActivity()
                             )
                           }
                         }
@@ -275,7 +268,9 @@ class QuasselViewModel : ViewModel() {
                               else            -> BufferListAdapter.BufferStatus.ONLINE
                             },
                             description = topic,
-                            activity = activity
+                            activity = activity,
+                            highlights = highlights,
+                            bufferActivity = config.minimumActivity()
                           )
                         }
                       }
@@ -287,7 +282,9 @@ class QuasselViewModel : ViewModel() {
                           network = network.networkInfo(),
                           bufferStatus = BufferListAdapter.BufferStatus.OFFLINE,
                           description = "",
-                          activity = activity
+                          activity = activity,
+                          highlights = highlights,
+                          bufferActivity = config.minimumActivity()
                         )
                       }
                     }
@@ -297,7 +294,9 @@ class QuasselViewModel : ViewModel() {
                         network = network.networkInfo(),
                         bufferStatus = BufferListAdapter.BufferStatus.OFFLINE,
                         description = "",
-                        activity = activity
+                        activity = activity,
+                        highlights = highlights,
+                        bufferActivity = config.minimumActivity()
                       )
                     )
                   }
@@ -309,9 +308,6 @@ class QuasselViewModel : ViewModel() {
             }
             ).map { list ->
               list.filter {
-                config.minimumActivity().value <= it.activity.bit ||
-                it.info.type.hasFlag(Buffer_Type.StatusBuffer)
-              }.filter {
                 (!config.hideInactiveBuffers()) ||
                 it.bufferStatus != BufferListAdapter.BufferStatus.OFFLINE ||
                 it.info.type.hasFlag(Buffer_Type.StatusBuffer)
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/service/ServiceBoundActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/service/ServiceBoundActivity.kt
index a64921334..ad471ecfc 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/util/service/ServiceBoundActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/service/ServiceBoundActivity.kt
@@ -1,11 +1,13 @@
 package de.kuschku.quasseldroid_ng.util.service
 
 import android.arch.lifecycle.LiveData
+import android.content.Context
 import android.os.Bundle
 import android.support.annotation.ColorRes
 import android.support.annotation.DrawableRes
 import android.support.v7.app.AppCompatActivity
 import de.kuschku.libquassel.session.Backend
+import de.kuschku.quasseldroid_ng.Keys
 import de.kuschku.quasseldroid_ng.R
 import de.kuschku.quasseldroid_ng.ui.settings.Settings
 import de.kuschku.quasseldroid_ng.ui.settings.data.AppearanceSettings
@@ -18,15 +20,19 @@ abstract class ServiceBoundActivity : AppCompatActivity() {
   protected val recentsHeaderColor: Int = R.color.colorPrimary
 
   private val connection = BackendServiceConnection()
-  val backend: LiveData<Backend?>
+  protected val backend: LiveData<Backend?>
     get() = connection.backend
 
   protected lateinit var appearanceSettings: AppearanceSettings
+  protected var accountId: Long = -1
 
   override fun onCreate(savedInstanceState: Bundle?) {
-
     connection.context = this
+
     appearanceSettings = Settings.appearance(this)
+    accountId = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE)
+      ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
+
     setTheme(appearanceSettings.theme.style)
     super.onCreate(savedInstanceState)
     connection.start()
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/service/ServiceBoundFragment.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/service/ServiceBoundFragment.kt
index 6d42d7227..c524cc583 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/util/service/ServiceBoundFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/service/ServiceBoundFragment.kt
@@ -1,17 +1,24 @@
 package de.kuschku.quasseldroid_ng.util.service
 
 import android.arch.lifecycle.LiveData
+import android.content.Context
 import android.os.Bundle
 import android.support.v4.app.Fragment
 import de.kuschku.libquassel.session.Backend
+import de.kuschku.quasseldroid_ng.Keys
 
 abstract class ServiceBoundFragment : Fragment() {
   private var connection = BackendServiceConnection()
 
-  val backend: LiveData<Backend?>
+  protected val backend: LiveData<Backend?>
     get() = connection.backend
 
+  protected var accountId: Long = -1
+
   override fun onCreate(savedInstanceState: Bundle?) {
+    accountId = context?.getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE)
+      ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
+
     connection.context = context
     super.onCreate(savedInstanceState)
     connection.start()
diff --git a/app/src/main/res/menu/activity_main.xml b/app/src/main/res/menu/activity_main.xml
index 3c59fa43c..1dd3df6f7 100644
--- a/app/src/main/res/menu/activity_main.xml
+++ b/app/src/main/res/menu/activity_main.xml
@@ -1,12 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
+  <item
+    android:id="@+id/filter_messages"
+    android:title="@string/label_filter_messages" />
   <item
     android:id="@+id/clear"
-    android:title="Clear Backlog" />
+    android:title="@string/label_clear_backlog" />
   <item
     android:id="@+id/settings"
-    android:title="Settings" />
+    android:title="@string/label_settings" />
   <item
     android:id="@+id/disconnect"
-    android:title="Disconnect" />
+    android:title="@string/label_disconnect" />
 </menu>
diff --git a/app/src/main/res/menu/setup_edit_account.xml b/app/src/main/res/menu/setup_edit_account.xml
index 6c17b9047..06155b5aa 100644
--- a/app/src/main/res/menu/setup_edit_account.xml
+++ b/app/src/main/res/menu/setup_edit_account.xml
@@ -3,12 +3,12 @@
   xmlns:app="http://schemas.android.com/apk/res-auto">
   <item
     android:id="@+id/save"
-    android:title="Save"
+    android:title="@string/label_save"
     app:showAsAction="ifRoom" />
   <item
     android:id="@+id/delete"
     android:icon="@drawable/ic_delete"
-    android:title="Delete"
+    android:title="@string/label_delete"
     app:iconTint="#fff"
     app:showAsAction="never" />
 </menu>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5706a39f2..5012780a9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,29 +1,17 @@
 <resources>
   <string name="app_name">QuasselDroid</string>
 
-  <string name="connection_service">Connection Service</string>
+  <string name="connection_service_title">Connection Service</string>
   <string name="connection_service_description">Keeps a connection to your core to allow for notifications, and to transmit messages.</string>
 
-  <string name="crash_text">QD-NG has crashed</string>
-  <string name="drawer_open">Open</string>
-  <string name="drawer_close">Close</string>
-
-  <string name="message_format_plain">%1$s%2$s: %3$s</string>
-  <string name="message_format_action">* %1$s%2$s %3$s</string>
-  <string name="message_format_notice">[%1$s%2$s] %3$s</string>
-  <string name="message_format_nick">%1$s%2$s is now known as %3$s%4$s</string>
-  <string name="message_format_mode">%1$s by %2$s%3$s</string>
-  <string name="message_format_join">%1$s%2$s joined</string>
-  <string name="message_format_part_1">%1$s%2$s left</string>
-  <string name="message_format_part_2">%1$s%2$s left: %3$s</string>
-  <string name="message_format_quit_1">%1$s%2$s quit</string>
-  <string name="message_format_quit_2">%1$s%2$s quit (%3$s)</string>
-  <plurals name="message_netsplit_join">
-    <item quantity="one">Netsplit between %1$s and %2$s ended: %3$d user joined</item>
-    <item quantity="other">Netsplit between %1$s and %2$s ended: %3$d users joined</item>
-  </plurals>
-  <plurals name="message_netsplit_quit">
-    <item quantity="one">Netsplit between %1$s and %2$s: %3$d user quit</item>
-    <item quantity="other">Netsplit between %1$s and %2$s: %3$d users quit</item>
-  </plurals>
+  <string name="label_cancel">Cancel</string>
+  <string name="label_clear_backlog">Clear Backlog</string>
+  <string name="label_delete">Delete</string>
+  <string name="label_disconnect">Disconnect</string>
+  <string name="label_drawer_close">Close</string>
+  <string name="label_drawer_open">Open</string>
+  <string name="label_filter_messages">Filter Messages</string>
+  <string name="label_save">Save</string>
+  <string name="label_select_multiple">Select</string>
+  <string name="label_settings">Settings</string>
 </resources>
diff --git a/app/src/main/res/values/strings_messages.xml b/app/src/main/res/values/strings_messages.xml
new file mode 100644
index 000000000..67ed8824c
--- /dev/null
+++ b/app/src/main/res/values/strings_messages.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <string name="message_type_join">Join</string>
+  <string name="message_type_part">Part</string>
+  <string name="message_type_quit">Quit</string>
+  <string name="message_type_nick">Nick</string>
+  <string name="message_type_mode">Mode</string>
+  <string name="message_type_topic">Topic</string>
+  <string-array name="message_filter_types">
+    <item>@string/message_type_join</item>
+    <item>@string/message_type_part</item>
+    <item>@string/message_type_quit</item>
+    <item>@string/message_type_nick</item>
+    <item>@string/message_type_mode</item>
+    <item>@string/message_type_topic</item>
+  </string-array>
+
+  <string name="message_format_plain">%1$s%2$s: %3$s</string>
+  <string name="message_format_action">* %1$s%2$s %3$s</string>
+  <string name="message_format_notice">[%1$s%2$s] %3$s</string>
+  <string name="message_format_nick">%1$s%2$s is now known as %3$s%4$s</string>
+  <string name="message_format_mode">%1$s by %2$s%3$s</string>
+  <string name="message_format_join">%1$s%2$s joined</string>
+  <string name="message_format_part_1">%1$s%2$s left</string>
+  <string name="message_format_part_2">%1$s%2$s left: %3$s</string>
+  <string name="message_format_quit_1">%1$s%2$s quit</string>
+  <string name="message_format_quit_2">%1$s%2$s quit (%3$s)</string>
+  <plurals name="message_netsplit_join">
+    <item quantity="one">Netsplit between %1$s and %2$s ended: %3$d user joined</item>
+    <item quantity="other">Netsplit between %1$s and %2$s ended: %3$d users joined</item>
+  </plurals>
+  <plurals name="message_netsplit_quit">
+    <item quantity="one">Netsplit between %1$s and %2$s: %3$d user quit</item>
+    <item quantity="other">Netsplit between %1$s and %2$s: %3$d users quit</item>
+  </plurals>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/preferences.xml b/app/src/main/res/values/strings_preferences.xml
similarity index 98%
rename from app/src/main/res/values/preferences.xml
rename to app/src/main/res/values/strings_preferences.xml
index 6af1e9c18..2ec76c925 100644
--- a/app/src/main/res/values/preferences.xml
+++ b/app/src/main/res/values/strings_preferences.xml
@@ -47,7 +47,7 @@
 
   <string name="preference_show_prefix_key" translatable="false">show_prefix</string>
   <string name="preference_show_prefix_title">Show sendermodes</string>
-  <string name="preference_show_prefix_entry_all">Allv modes</string>
+  <string name="preference_show_prefix_entry_all">All modes</string>
   <string name="preference_show_prefix_entry_highest">Highest mode</string>
   <string name="preference_show_prefix_entry_none">No modes</string>
   <string-array name="preference_show_prefix_entries">
diff --git a/app/src/main/res/values/themes_quassel.xml b/app/src/main/res/values/themes_quassel.xml
index aa5fab1c6..016983950 100644
--- a/app/src/main/res/values/themes_quassel.xml
+++ b/app/src/main/res/values/themes_quassel.xml
@@ -31,7 +31,7 @@
 
     <item name="android:windowBackground">@color/quassel_light_background</item>
     <item name="colorBackground">#FAFAFA</item>
-    <item name="colorBackgroundHighlight">#FFA726</item>
+    <item name="colorBackgroundHighlight">#FFCA28</item>
     <item name="colorBackgroundSecondary">@null</item>
     <item name="colorBackgroundCard">#FFFFFF</item>
     <item name="colorBackgroundDialog">#FAFAFA</item>
@@ -73,7 +73,7 @@
 
     <item name="android:windowBackground">@color/quassel_dark_background</item>
     <item name="colorBackground">#303030</item>
-    <item name="colorBackgroundHighlight">#FFA726</item>
+    <item name="colorBackgroundHighlight">#FFCA28</item>
     <item name="colorBackgroundSecondary">@null</item>
     <item name="colorBackgroundCard">#424242</item>
     <item name="colorBackgroundDialog">#303030</item>
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/Flag.kt b/lib/src/main/java/de/kuschku/libquassel/util/Flag.kt
index 59e57ea45..f2a29ec53 100644
--- a/lib/src/main/java/de/kuschku/libquassel/util/Flag.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/util/Flag.kt
@@ -46,11 +46,14 @@ data class Flags<E>(
   }
 
   companion object {
-    inline fun <reified T> of(int: Int): Flags<T> where T : Flag<T>, T : Enum<T>
-      = Flags(int, enumValues())
-
-    inline fun <reified T> of(vararg flags: Flag<T>): Flags<T> where T : Flag<T>, T : Enum<T>
-      = Flags(flags.map(Flag<T>::bit).distinct().sum(), enumValues())
+    inline fun <reified T> of(int: Int): Flags<T> where T : Flag<T>, T : Enum<T> = Flags(
+      int, enumValues()
+    )
+
+    inline fun <reified T> of(
+      vararg flags: Flag<T>): Flags<T> where T : Flag<T>, T : Enum<T> = Flags(
+      flags.map(Flag<T>::bit).distinct().sum(), enumValues()
+    )
   }
 
   interface Factory<E> where E : Flag<E>, E : Enum<E> {
@@ -67,28 +70,41 @@ infix fun <T> Flags<T>.hasFlag(which: T): Boolean where T : Enum<T>, T : Flag<T>
   return value and which.bit == which.bit
 }
 
-infix fun <T> Flags<T>.or(other: Flag<T>): Flags<T> where T : kotlin.Enum<T>, T : Flag<T> = Flags(
-  value or other.bit
-)
+infix fun <T> Flags<T>.or(other: Int): Flags<T>
+  where T : kotlin.Enum<T>, T : Flag<T> = Flags(value or other)
+
+infix fun <T> Flags<T>.or(other: Flag<T>): Flags<T>
+  where T : kotlin.Enum<T>, T : Flag<T> = Flags(value or other.bit)
+
+infix fun <T> Flags<T>.or(other: Flags<T>): Flags<T>
+  where T : kotlin.Enum<T>, T : Flag<T> = Flags(value or other.value)
+
+infix fun <T> Flags<T>.and(other: Int): Flags<T>
+  where T : kotlin.Enum<T>, T : Flag<T> = Flags(value and other)
+
+infix fun <T> Flags<T>.and(other: Flag<T>): Flags<T>
+  where T : kotlin.Enum<T>, T : Flag<T> = Flags(value and other.bit)
+
+infix fun <T> Flags<T>.and(other: Flags<T>): Flags<T>
+  where T : kotlin.Enum<T>, T : Flag<T> = Flags(value and other.value)
+
+infix operator fun <T> Flags<T>.plus(other: Int): Flags<T>
+  where T : Enum<T>, T : Flag<T> = Flags(value or other)
 
-infix fun <T> Flags<T>.or(other: Flags<T>): Flags<T> where T : kotlin.Enum<T>, T : Flag<T> = Flags(
-  value or other.value
-)
+infix operator fun <T> Flags<T>.plus(other: Flag<T>): Flags<T>
+  where T : Enum<T>, T : Flag<T> = Flags(value or other.bit)
 
-infix fun <T> Flags<T>.and(other: Flag<T>): Flags<T> where T : kotlin.Enum<T>, T : Flag<T> = Flags(
-  value and other.bit
-)
+infix operator fun <T> Flags<T>.plus(other: Flags<T>): Flags<T>
+  where T : Enum<T>, T : Flag<T> = Flags(value or other.value)
 
-infix fun <T> Flags<T>.and(other: Flags<T>): Flags<T> where T : kotlin.Enum<T>, T : Flag<T> = Flags(
-  value and other.value
-)
+infix operator fun <T> Flags<T>.minus(other: Int): Flags<T>
+  where T : Enum<T>, T : Flag<T> = Flags(value and other.inv())
 
-operator infix fun <T> Flags<T>.plus(
-  other: Flags<T>): Flags<T>  where T : Enum<T>, T : Flag<T> = Flags(value or other.value)
+infix operator fun <T> Flags<T>.minus(other: Flag<T>): Flags<T>
+  where T : Enum<T>, T : Flag<T> = Flags(value and other.bit.inv())
 
-operator infix fun <T> Flags<T>.plus(
-  other: Flag<T>): Flags<T>  where T : Enum<T>, T : Flag<T> = Flags(value or other.bit)
+infix operator fun <T> Flags<T>.minus(other: Flags<T>): Flags<T>
+  where T : Enum<T>, T : Flag<T> = Flags(value and other.value.inv())
 
-infix fun <T> Flags<T>.unset(which: T): Flags<T>  where T : Enum<T>, T : Flag<T> = Flags(
-  value xor which.bit
-)
+infix fun <T> Flags<T>.unset(which: T): Flags<T>
+  where T : Enum<T>, T : Flag<T> = Flags(value xor which.bit)
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/LongFlag.kt b/lib/src/main/java/de/kuschku/libquassel/util/LongFlag.kt
index 266f21eba..a2807b50c 100644
--- a/lib/src/main/java/de/kuschku/libquassel/util/LongFlag.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/util/LongFlag.kt
@@ -46,12 +46,13 @@ data class LongFlags<E>(
   }
 
   companion object {
-    inline fun <reified T> of(int: Long): LongFlags<T> where T : LongFlag<T>, T : Enum<T>
-      = LongFlags(int, enumValues())
+    inline fun <reified T> of(
+      int: Long): LongFlags<T> where T : LongFlag<T>, T : Enum<T> = LongFlags(int, enumValues())
 
     inline fun <reified T> of(
-      vararg flags: LongFlag<T>): LongFlags<T> where T : LongFlag<T>, T : Enum<T>
-      = LongFlags(flags.map(LongFlag<T>::bit).distinct().sum(), enumValues())
+      vararg flags: LongFlag<T>): LongFlags<T> where T : LongFlag<T>, T : Enum<T> = LongFlags(
+      flags.map(LongFlag<T>::bit).distinct().sum(), enumValues()
+    )
   }
 
   interface Factory<E> where E : LongFlag<E>, E : Enum<E> {
@@ -67,37 +68,41 @@ infix fun <T> LongFlags<T>.hasFlag(which: T): Boolean where T : Enum<T>, T : Lon
   return value and which.bit == which.bit
 }
 
-infix fun <T> LongFlags<T>.or(
-  other: LongFlag<T>): LongFlags<T> where T : kotlin.Enum<T>, T : LongFlag<T> = LongFlags(
-  value or other.bit
-)
-
-infix fun <T> LongFlags<T>.or(
-  other: LongFlags<T>): LongFlags<T> where T : kotlin.Enum<T>, T : LongFlag<T> = LongFlags(
-  value or other.value
-)
-
-infix fun <T> LongFlags<T>.and(
-  other: LongFlag<T>): LongFlags<T> where T : kotlin.Enum<T>, T : LongFlag<T> = LongFlags(
-  value and other.bit
-)
-
-infix fun <T> LongFlags<T>.and(
-  other: LongFlags<T>): LongFlags<T> where T : kotlin.Enum<T>, T : LongFlag<T> = LongFlags(
-  value and other.value
-)
-
-operator infix fun <T> LongFlags<T>.plus(
-  other: LongFlags<T>): LongFlags<T>  where T : Enum<T>, T : LongFlag<T> = LongFlags(
-  value or other.value
-)
-
-operator infix fun <T> LongFlags<T>.plus(
-  other: LongFlag<T>): LongFlags<T>  where T : Enum<T>, T : LongFlag<T> = LongFlags(
-  value or other.bit
-)
-
-infix fun <T> LongFlags<T>.unset(
-  which: T): LongFlags<T>  where T : Enum<T>, T : LongFlag<T> = LongFlags(
-  value xor which.bit
-)
+infix fun <T> LongFlags<T>.or(other: Long): LongFlags<T>
+  where T : kotlin.Enum<T>, T : LongFlag<T> = LongFlags(value or other)
+
+infix fun <T> LongFlags<T>.or(other: LongFlag<T>): LongFlags<T>
+  where T : kotlin.Enum<T>, T : LongFlag<T> = LongFlags(value or other.bit)
+
+infix fun <T> LongFlags<T>.or(other: LongFlags<T>): LongFlags<T>
+  where T : kotlin.Enum<T>, T : LongFlag<T> = LongFlags(value or other.value)
+
+infix fun <T> LongFlags<T>.and(other: Long): LongFlags<T>
+  where T : kotlin.Enum<T>, T : LongFlag<T> = LongFlags(value and other)
+
+infix fun <T> LongFlags<T>.and(other: LongFlag<T>): LongFlags<T>
+  where T : kotlin.Enum<T>, T : LongFlag<T> = LongFlags(value and other.bit)
+
+infix fun <T> LongFlags<T>.and(other: LongFlags<T>): LongFlags<T>
+  where T : kotlin.Enum<T>, T : LongFlag<T> = LongFlags(value and other.value)
+
+infix operator fun <T> LongFlags<T>.plus(other: Long): LongFlags<T>
+  where T : Enum<T>, T : LongFlag<T> = LongFlags(value or other)
+
+infix operator fun <T> LongFlags<T>.plus(other: LongFlag<T>): LongFlags<T>
+  where T : Enum<T>, T : LongFlag<T> = LongFlags(value or other.bit)
+
+infix operator fun <T> LongFlags<T>.plus(other: LongFlags<T>): LongFlags<T>
+  where T : Enum<T>, T : LongFlag<T> = LongFlags(value or other.value)
+
+infix operator fun <T> LongFlags<T>.minus(other: Long): LongFlags<T>
+  where T : Enum<T>, T : LongFlag<T> = LongFlags(value and other.inv())
+
+infix operator fun <T> LongFlags<T>.minus(other: LongFlag<T>): LongFlags<T>
+  where T : Enum<T>, T : LongFlag<T> = LongFlags(value and other.bit.inv())
+
+infix operator fun <T> LongFlags<T>.minus(other: LongFlags<T>): LongFlags<T>
+  where T : Enum<T>, T : LongFlag<T> = LongFlags(value and other.value.inv())
+
+infix fun <T> LongFlags<T>.unset(which: T): LongFlags<T>
+  where T : Enum<T>, T : LongFlag<T> = LongFlags(value xor which.bit)
\ No newline at end of file
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/ShortFlag.kt b/lib/src/main/java/de/kuschku/libquassel/util/ShortFlag.kt
index f5305d475..8f22a03bd 100644
--- a/lib/src/main/java/de/kuschku/libquassel/util/ShortFlag.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/util/ShortFlag.kt
@@ -1,6 +1,7 @@
 package de.kuschku.libquassel.util
 
 import kotlin.experimental.and
+import kotlin.experimental.inv
 import kotlin.experimental.or
 import kotlin.experimental.xor
 
@@ -71,37 +72,41 @@ infix fun <T> ShortFlags<T>.hasFlag(which: T): Boolean where T : Enum<T>, T : Sh
   return value and which.bit == which.bit
 }
 
-infix fun <T> ShortFlags<T>.or(
-  other: ShortFlag<T>): ShortFlags<T> where T : kotlin.Enum<T>, T : ShortFlag<T> = ShortFlags(
-  value or other.bit
-)
-
-infix fun <T> ShortFlags<T>.or(
-  other: ShortFlags<T>): ShortFlags<T> where T : kotlin.Enum<T>, T : ShortFlag<T> = ShortFlags(
-  value or other.value
-)
-
-infix fun <T> ShortFlags<T>.and(
-  other: ShortFlag<T>): ShortFlags<T> where T : kotlin.Enum<T>, T : ShortFlag<T> = ShortFlags(
-  value and other.bit
-)
-
-infix fun <T> ShortFlags<T>.and(
-  other: ShortFlags<T>): ShortFlags<T> where T : kotlin.Enum<T>, T : ShortFlag<T> = ShortFlags(
-  value and other.value
-)
-
-operator infix fun <T> ShortFlags<T>.plus(
-  other: ShortFlags<T>): ShortFlags<T>  where T : Enum<T>, T : ShortFlag<T> = ShortFlags(
-  value or other.value
-)
-
-operator infix fun <T> ShortFlags<T>.plus(
-  other: ShortFlag<T>): ShortFlags<T>  where T : Enum<T>, T : ShortFlag<T> = ShortFlags(
-  value or other.bit
-)
-
-infix fun <T> ShortFlags<T>.unset(
-  which: T): ShortFlags<T>  where T : Enum<T>, T : ShortFlag<T> = ShortFlags(
-  value xor which.bit
-)
+infix fun <T> ShortFlags<T>.or(other: Short): ShortFlags<T>
+  where T : kotlin.Enum<T>, T : ShortFlag<T> = ShortFlags(value or other)
+
+infix fun <T> ShortFlags<T>.or(other: ShortFlag<T>): ShortFlags<T>
+  where T : kotlin.Enum<T>, T : ShortFlag<T> = ShortFlags(value or other.bit)
+
+infix fun <T> ShortFlags<T>.or(other: ShortFlags<T>): ShortFlags<T>
+  where T : kotlin.Enum<T>, T : ShortFlag<T> = ShortFlags(value or other.value)
+
+infix fun <T> ShortFlags<T>.and(other: Short): ShortFlags<T>
+  where T : kotlin.Enum<T>, T : ShortFlag<T> = ShortFlags(value and other)
+
+infix fun <T> ShortFlags<T>.and(other: ShortFlag<T>): ShortFlags<T>
+  where T : kotlin.Enum<T>, T : ShortFlag<T> = ShortFlags(value and other.bit)
+
+infix fun <T> ShortFlags<T>.and(other: ShortFlags<T>): ShortFlags<T>
+  where T : kotlin.Enum<T>, T : ShortFlag<T> = ShortFlags(value and other.value)
+
+infix operator fun <T> ShortFlags<T>.plus(other: Short): ShortFlags<T>
+  where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value or other)
+
+infix operator fun <T> ShortFlags<T>.plus(other: ShortFlag<T>): ShortFlags<T>
+  where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value or other.bit)
+
+infix operator fun <T> ShortFlags<T>.plus(other: ShortFlags<T>): ShortFlags<T>
+  where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value or other.value)
+
+infix operator fun <T> ShortFlags<T>.minus(other: Short): ShortFlags<T>
+  where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value and other.inv())
+
+infix operator fun <T> ShortFlags<T>.minus(other: ShortFlag<T>): ShortFlags<T>
+  where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value and other.bit.inv())
+
+infix operator fun <T> ShortFlags<T>.minus(other: ShortFlags<T>): ShortFlags<T>
+  where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value and other.value.inv())
+
+infix fun <T> ShortFlags<T>.unset(which: T): ShortFlags<T>
+  where T : Enum<T>, T : ShortFlag<T> = ShortFlags(value xor which.bit)
\ No newline at end of file
-- 
GitLab