commit
9b513fa2ba
@ -0,0 +1,62 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.search
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import kotlinx.android.synthetic.main.view_search_bottom_bar.view.*
|
||||
import network.loki.messenger.R
|
||||
|
||||
|
||||
class SearchBottomBar : LinearLayout {
|
||||
private var eventListener: EventListener? = null
|
||||
|
||||
// region Lifecycle
|
||||
constructor(context: Context) : super(context) { initialize() }
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() }
|
||||
|
||||
fun initialize() {
|
||||
LayoutInflater.from(context).inflate(R.layout.view_search_bottom_bar, this)
|
||||
}
|
||||
|
||||
fun setData(position: Int, count: Int) {
|
||||
searchProgressWheel.visibility = GONE
|
||||
searchUp.setOnClickListener { v: View? ->
|
||||
if (eventListener != null) {
|
||||
eventListener!!.onSearchMoveUpPressed()
|
||||
}
|
||||
}
|
||||
searchDown.setOnClickListener { v: View? ->
|
||||
if (eventListener != null) {
|
||||
eventListener!!.onSearchMoveDownPressed()
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
searchPosition.text = resources.getString(R.string.ConversationActivity_search_position, position + 1, count)
|
||||
} else {
|
||||
searchPosition.text = ""
|
||||
}
|
||||
setViewEnabled(searchUp, position < count - 1)
|
||||
setViewEnabled(searchDown, position > 0)
|
||||
}
|
||||
|
||||
fun showLoading() {
|
||||
searchProgressWheel.visibility = VISIBLE
|
||||
}
|
||||
|
||||
private fun setViewEnabled(view: View, enabled: Boolean) {
|
||||
view.isEnabled = enabled
|
||||
view.alpha = if (enabled) 1f else 0.25f
|
||||
}
|
||||
|
||||
fun setEventListener(eventListener: EventListener?) {
|
||||
this.eventListener = eventListener
|
||||
}
|
||||
|
||||
interface EventListener {
|
||||
fun onSearchMoveUpPressed()
|
||||
fun onSearchMoveDownPressed()
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.search
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import org.session.libsession.utilities.Debouncer
|
||||
import org.session.libsession.utilities.Util.runOnMain
|
||||
import org.session.libsession.utilities.concurrent.SignalExecutors
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor
|
||||
import org.thoughtcrime.securesms.database.CursorList
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.search.SearchRepository
|
||||
import org.thoughtcrime.securesms.search.model.MessageResult
|
||||
import org.thoughtcrime.securesms.util.CloseableLiveData
|
||||
import java.io.Closeable
|
||||
|
||||
|
||||
class SearchViewModel(application: Application) : AndroidViewModel(application) {
|
||||
private val searchRepository: SearchRepository
|
||||
private val result: CloseableLiveData<SearchResult>
|
||||
private val debouncer: Debouncer
|
||||
private var firstSearch = false
|
||||
private var searchOpen = false
|
||||
private var activeQuery: String? = null
|
||||
private var activeThreadId: Long = 0
|
||||
val searchResults: LiveData<SearchResult>
|
||||
get() = result
|
||||
|
||||
fun onQueryUpdated(query: String, threadId: Long) {
|
||||
if (query == activeQuery) {
|
||||
return
|
||||
}
|
||||
updateQuery(query, threadId)
|
||||
}
|
||||
|
||||
fun onMissingResult() {
|
||||
if (activeQuery != null) {
|
||||
updateQuery(activeQuery!!, activeThreadId)
|
||||
}
|
||||
}
|
||||
|
||||
fun onMoveUp() {
|
||||
debouncer.clear()
|
||||
val messages = result.value!!.getResults() as CursorList<MessageResult?>
|
||||
val position = Math.min(result.value!!.position + 1, messages.size - 1)
|
||||
result.setValue(SearchResult(messages, position), false)
|
||||
}
|
||||
|
||||
fun onMoveDown() {
|
||||
debouncer.clear()
|
||||
val messages = result.value!!.getResults() as CursorList<MessageResult?>
|
||||
val position = Math.max(result.value!!.position - 1, 0)
|
||||
result.setValue(SearchResult(messages, position), false)
|
||||
}
|
||||
|
||||
fun onSearchOpened() {
|
||||
searchOpen = true
|
||||
firstSearch = true
|
||||
}
|
||||
|
||||
fun onSearchClosed() {
|
||||
searchOpen = false
|
||||
activeQuery = null
|
||||
debouncer.clear()
|
||||
result.close()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
result.close()
|
||||
}
|
||||
|
||||
private fun updateQuery(query: String, threadId: Long) {
|
||||
activeQuery = query
|
||||
activeThreadId = threadId
|
||||
debouncer.publish {
|
||||
firstSearch = false
|
||||
searchRepository.query(query, threadId) { messages: CursorList<MessageResult?> ->
|
||||
runOnMain {
|
||||
if (searchOpen && query == activeQuery) {
|
||||
result.setValue(SearchResult(messages, 0))
|
||||
} else {
|
||||
messages.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SearchResult(private val results: CursorList<MessageResult?>, val position: Int) : Closeable {
|
||||
|
||||
fun getResults(): List<MessageResult?> {
|
||||
return results
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
results.close()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val context = application.applicationContext
|
||||
result = CloseableLiveData()
|
||||
debouncer = Debouncer(500)
|
||||
searchRepository = SearchRepository(context,
|
||||
DatabaseFactory.getSearchDatabase(context),
|
||||
DatabaseFactory.getThreadDatabase(context),
|
||||
ContactAccessor.getInstance(),
|
||||
SignalExecutors.SERIAL)
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/searchBottomBarConstraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/input_bar_height"
|
||||
android:background="@color/compose_view_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:background="@color/separator" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/searchUp"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:padding="4dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/ic_baseline_keyboard_arrow_up_24"
|
||||
android:tint="@color/accent"
|
||||
tools:ignore="UseAppTint" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/searchDown"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:padding="4dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/ic_baseline_keyboard_arrow_down_24"
|
||||
android:tint="@color/accent"
|
||||
tools:ignore="UseAppTint" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/searchPosition"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:text="37 of 73"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<com.github.ybq.android.spinkit.SpinKitView
|
||||
style="@style/SpinKitView.DoubleBounce"
|
||||
android:id="@+id/searchProgressWheel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="8dp"
|
||||
android:background="@color/compose_view_background"
|
||||
app:SpinKit_Color="?android:textColorPrimary"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
Loading…
Reference in New Issue