From e6ec8b74f701e2aad9329e5716d73a318bf3c49e Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Sun, 13 Jan 2019 16:03:27 +0100
Subject: [PATCH] Implement buffer search, fix #167

---
 .../chat/buffers/BufferViewConfigFragment.kt  | 33 +++++++++
 .../main/res/layout/fragment_chat_list.xml    | 69 ++++++++++++++++---
 app/src/main/res/values/strings.xml           |  1 +
 .../viewmodel/QuasselViewModel.kt             | 14 ++--
 4 files changed, 104 insertions(+), 13 deletions(-)

diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
index 068628cf5..1eea6bbde 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
@@ -21,8 +21,12 @@ package de.kuschku.quasseldroid.ui.chat.buffers
 
 import android.os.Bundle
 import android.os.Parcelable
+import android.text.Editable
+import android.text.TextWatcher
 import android.view.*
 import android.widget.AdapterView
+import android.widget.EditText
+import androidx.appcompat.widget.AppCompatImageButton
 import androidx.appcompat.widget.AppCompatSpinner
 import androidx.appcompat.widget.Toolbar
 import androidx.lifecycle.Observer
@@ -76,6 +80,15 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
   @BindView(R.id.feature_context_bufferactivitysync)
   lateinit var featureContextBufferActivitySync: WarningBarView
 
+  @BindView(R.id.buffer_search)
+  lateinit var bufferSearch: EditText
+
+  @BindView(R.id.buffer_search_clear)
+  lateinit var bufferSearchClear: AppCompatImageButton
+
+  @BindView(R.id.buffer_search_container)
+  lateinit var bufferSearchContainer: ViewGroup
+
   @Inject
   lateinit var appearanceSettings: AppearanceSettings
 
@@ -469,6 +482,26 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
       hasSetBufferViewConfigId = false
     })
 
+    viewModel.bufferViewConfig.toLiveData().observe(this, Observer {
+      val visible = it.orNull()?.showSearch() ?: false
+      bufferSearchContainer.visibleIf(visible)
+      if (!visible) bufferSearch.setText("")
+    })
+
+    bufferSearch.addTextChangedListener(object : TextWatcher {
+      override fun afterTextChanged(s: Editable) {
+        viewModel.bufferSearch.onNext(s.toString())
+      }
+
+      override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit
+
+      override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) = Unit
+    })
+
+    bufferSearchClear.setOnClickListener {
+      bufferSearch.setText("")
+    }
+
     return view
   }
 
diff --git a/app/src/main/res/layout/fragment_chat_list.xml b/app/src/main/res/layout/fragment_chat_list.xml
index cb69b2eb1..647c026ba 100644
--- a/app/src/main/res/layout/fragment_chat_list.xml
+++ b/app/src/main/res/layout/fragment_chat_list.xml
@@ -32,20 +32,71 @@
     <androidx.appcompat.widget.Toolbar
       android:id="@+id/chatListToolbar"
       android:layout_width="match_parent"
-      android:layout_height="?attr/actionBarSize"
-      app:contentInsetStartWithNavigation="0dp"
+      android:layout_height="wrap_content"
+      android:minHeight="?attr/actionBarSize"
+      app:contentInsetLeft="0dip"
+      app:contentInsetStart="0dip"
+      app:contentInsetStartWithNavigation="0dip"
       app:popupTheme="?attr/actionBarPopupTheme">
 
-      <androidx.appcompat.widget.AppCompatSpinner
-        android:id="@+id/chatListSpinner"
-        style="@style/Widget.FullWidthSpinner"
-        android:layout_width="fill_parent"
-        android:layout_height="match_parent"
-        app:popupTheme="?attr/actionBarPopupTheme"
-        tools:listitem="@layout/widget_spinner_item_toolbar" />
+      <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <androidx.appcompat.widget.AppCompatSpinner
+          android:id="@+id/chatListSpinner"
+          style="@style/Widget.FullWidthSpinner"
+          android:layout_width="fill_parent"
+          android:layout_height="match_parent"
+          app:popupTheme="?attr/actionBarPopupTheme"
+          tools:listitem="@layout/widget_spinner_item_toolbar" />
+      </LinearLayout>
 
     </androidx.appcompat.widget.Toolbar>
 
+    <androidx.cardview.widget.CardView
+      android:id="@+id/buffer_search_container"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_marginLeft="6dp"
+      android:layout_marginRight="6dp"
+      android:layout_marginBottom="6dp">
+
+      <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <EditText
+          android:id="@+id/buffer_search"
+          android:layout_width="0dip"
+          android:layout_height="wrap_content"
+          android:layout_weight="1"
+          android:background="@android:color/transparent"
+          android:hint="@string/label_search_buffer"
+          android:imeOptions="actionSearch"
+          android:inputType="textNoSuggestions"
+          android:lines="1"
+          android:minHeight="40dp"
+          android:paddingLeft="8dp"
+          android:paddingRight="8dp"
+          android:textColor="?colorTextPrimary"
+          android:textColorHint="?colorTextSecondary"
+          android:textSize="16sp" />
+
+        <androidx.appcompat.widget.AppCompatImageButton
+          android:id="@+id/buffer_search_clear"
+          android:layout_width="40dp"
+          android:layout_height="match_parent"
+          android:background="?selectableItemBackgroundBorderless"
+          app:srcCompat="@drawable/ic_close"
+          app:tint="?colorTextSecondary" />
+
+      </LinearLayout>
+
+    </androidx.cardview.widget.CardView>
+
   </com.google.android.material.appbar.AppBarLayout>
 
   <de.kuschku.quasseldroid.util.ui.WarningBarView
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c89d1515a..ca13816f4 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -93,6 +93,7 @@
   <string name="label_query_long">Open private chat with user</string>
   <string name="label_rename">Rename</string>
   <string name="label_save">Save</string>
+  <string name="label_search_buffer">Search…</string>
   <string name="label_select">Select</string>
   <string name="label_send">Send</string>
   <string name="label_set_default">Set Default</string>
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
index bfa9a5171..85492ab33 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
@@ -62,6 +62,8 @@ class QuasselViewModel : ViewModel() {
     return result.size
   }
 
+  val bufferSearch = BehaviorSubject.createDefault("")
+
   val expandedMessages = BehaviorSubject.createDefault(emptySet<MsgId>())
 
   val buffer = BehaviorSubject.createDefault(Int.MAX_VALUE)
@@ -111,7 +113,7 @@ class QuasselViewModel : ViewModel() {
   val bufferViewConfig = bufferViewManager.flatMapSwitchMap { manager ->
     bufferViewConfigId.map { id ->
       Optional.ofNullable(manager.bufferViewConfig(id))
-    }
+    }.mapSwitchMap(BufferViewConfig::liveUpdates)
   }
 
   val errors = sessionManager.switchMap {
@@ -350,8 +352,8 @@ class QuasselViewModel : ViewModel() {
     }
 
   val bufferList: Observable<Pair<BufferViewConfig?, List<BufferProps>>> =
-    combineLatest(session, bufferViewConfig, showHidden)
-      .switchMap { (sessionOptional, configOptional, showHiddenRaw) ->
+    combineLatest(session, bufferViewConfig, showHidden, bufferSearch)
+      .switchMap { (sessionOptional, configOptional, showHiddenRaw, bufferSearch) ->
         val session = sessionOptional.orNull()
         val bufferSyncer = session?.bufferSyncer
         val showHidden = showHiddenRaw ?: false
@@ -370,6 +372,10 @@ class QuasselViewModel : ViewModel() {
                   fun transformIds(ids: Collection<BufferId>, state: BufferHiddenState) =
                     ids.asSequence().mapNotNull { id ->
                       bufferSyncer.bufferInfo(id)
+                    }.filter {
+                      bufferSearch.isBlank() ||
+                      it.type.hasFlag(Buffer_Type.StatusBuffer) ||
+                      it.bufferName?.contains(bufferSearch, ignoreCase = true) == true
                     }.filter {
                       currentConfig.networkId() <= 0 || currentConfig.networkId() == it.networkId
                     }.filter {
@@ -507,7 +513,7 @@ class QuasselViewModel : ViewModel() {
                   }
 
                   bufferSyncer.liveBufferInfos().switchMap {
-                    val buffers = if (showHidden) {
+                    val buffers = if (showHidden || bufferSearch.isNotBlank()) {
                       transformIds(ids, BufferHiddenState.VISIBLE) +
                       transformIds(temp - ids, BufferHiddenState.HIDDEN_TEMPORARY) +
                       transformIds(perm - temp - ids, BufferHiddenState.HIDDEN_PERMANENT) +
-- 
GitLab