mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-24 16:57:50 +00:00
add search bottom bar ui
This commit is contained in:
parent
2b26876c4c
commit
61ff68b532
@ -39,7 +39,6 @@ import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher
|
||||
import org.thoughtcrime.securesms.conversation.ConversationActivity
|
||||
import org.thoughtcrime.securesms.conversation.v2.dialogs.*
|
||||
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarButton
|
||||
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarDelegate
|
||||
@ -291,7 +290,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
||||
ConversationMenuHelper.onPrepareOptionsMenu(menu, menuInflater, thread, this) { onOptionsItemSelected(it) }
|
||||
ConversationMenuHelper.onPrepareOptionsMenu(menu, menuInflater, thread, threadID, this) { onOptionsItemSelected(it) }
|
||||
super.onPrepareOptionsMenu(menu)
|
||||
return true
|
||||
}
|
||||
@ -750,4 +749,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
draftDB.insertDrafts(threadID, drafts)
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Search
|
||||
fun onSearchQueryUpdated(query: String?) {
|
||||
adapter.onSearchQueryUpdated(query)
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
@ -6,17 +6,26 @@ import android.graphics.PorterDuffColorFilter
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.appcompat.widget.SearchView.OnQueryTextListener
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import kotlinx.android.synthetic.main.activity_conversation_v2.*
|
||||
import kotlinx.android.synthetic.main.session_logo_action_bar_content.*
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.utilities.ExpirationUtil
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.components.ConversationSearchBottomBar
|
||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||
import org.thoughtcrime.securesms.conversation.v2.search.SearchViewModel
|
||||
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
||||
|
||||
object ConversationMenuHelper {
|
||||
|
||||
fun onPrepareOptionsMenu(menu: Menu, inflater: MenuInflater, thread: Recipient, context: Context, onOptionsItemSelected: (MenuItem) -> Unit) {
|
||||
fun onPrepareOptionsMenu(menu: Menu, inflater: MenuInflater, thread: Recipient, threadId: Long, context: Context, onOptionsItemSelected: (MenuItem) -> Unit) {
|
||||
// Prepare
|
||||
menu.clear()
|
||||
val isOpenGroup = thread.isOpenGroupRecipient
|
||||
@ -61,6 +70,51 @@ object ConversationMenuHelper {
|
||||
} else {
|
||||
inflater.inflate(R.menu.menu_conversation_unmuted, menu)
|
||||
}
|
||||
// TODO: Implement search
|
||||
// Search
|
||||
val searchViewItem = menu.findItem(R.id.menu_search)
|
||||
val searchView = searchViewItem.actionView as SearchView
|
||||
val searchViewModel:SearchViewModel = ViewModelProvider(context as ConversationActivityV2).get(SearchViewModel::class.java)
|
||||
val queryListener = object : OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
searchViewModel.onQueryUpdated(query, threadId)
|
||||
context.searchBottomBar.showLoading()
|
||||
context.onSearchQueryUpdated(query)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(query: String): Boolean {
|
||||
searchViewModel.onQueryUpdated(query, threadId)
|
||||
context.searchBottomBar.showLoading()
|
||||
context.onSearchQueryUpdated(query)
|
||||
return true
|
||||
}
|
||||
}
|
||||
searchViewItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
searchView.setOnQueryTextListener(queryListener)
|
||||
searchViewModel.onSearchOpened()
|
||||
context.searchBottomBar.visibility = View.VISIBLE
|
||||
context.searchBottomBar.setData(0, 0)
|
||||
context.inputBar.visibility = View.GONE
|
||||
context.supportActionBar?.setDisplayHomeAsUpEnabled(false)
|
||||
for (i in 0 until menu.size()) {
|
||||
if (menu.getItem(i) != searchViewItem) {
|
||||
menu.getItem(i).isVisible = false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
searchView.setOnQueryTextListener(null)
|
||||
searchViewModel.onSearchClosed()
|
||||
context.searchBottomBar.visibility = View.GONE
|
||||
context.inputBar.visibility = View.VISIBLE
|
||||
context.supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
context.onSearchQueryUpdated(null)
|
||||
context.invalidateOptionsMenu()
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
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 android.widget.RelativeLayout
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
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.setText(R.string.ConversationActivity_no_results)
|
||||
}
|
||||
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,114 @@
|
||||
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 (firstSearch && query.length < 2) {
|
||||
result.postValue(SearchResult(CursorList.emptyList(), 0))
|
||||
return
|
||||
}
|
||||
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
|
||||
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)
|
||||
}
|
||||
}
|
@ -34,6 +34,13 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true" />
|
||||
|
||||
<org.thoughtcrime.securesms.conversation.v2.search.SearchBottomBar
|
||||
android:id="@+id/searchBottomBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/additionalContentContainer"
|
||||
android:layout_width="match_parent"
|
||||
|
68
app/src/main/res/layout/view_search_bottom_bar.xml
Normal file
68
app/src/main/res/layout/view_search_bottom_bar.xml
Normal file
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
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" />
|
||||
|
||||
<com.github.ybq.android.spinkit.SpinKitView
|
||||
style="@style/SpinKitView.DoubleBounce"
|
||||
android:id="@+id/searchProgressWheel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:padding="8dp"
|
||||
android:background="@color/compose_view_background"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
Loading…
x
Reference in New Issue
Block a user