mirror of
https://github.com/element-hq/element-android
synced 2024-11-23 18:05:36 +03:00
Autocomplete : handle click
This commit is contained in:
parent
c64d6b6b28
commit
3f1cc466ed
5 changed files with 48 additions and 26 deletions
|
@ -19,6 +19,9 @@ package im.vector.riotredesign.core.epoxy
|
|||
import com.airbnb.epoxy.EpoxyModelWithHolder
|
||||
import com.airbnb.epoxy.VisibilityState
|
||||
|
||||
/**
|
||||
* EpoxyModelWithHolder which can listen to visibility state change
|
||||
*/
|
||||
abstract class VectorEpoxyModel<H : VectorEpoxyHolder> : EpoxyModelWithHolder<H>() {
|
||||
|
||||
private var onModelVisibilityStateChangedListener: OnVisibilityStateChangedListener? = null
|
||||
|
|
|
@ -22,7 +22,6 @@ import android.view.ViewGroup
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import com.airbnb.epoxy.EpoxyRecyclerView
|
||||
import com.otaliastudios.autocomplete.AutocompleteCallback
|
||||
import com.otaliastudios.autocomplete.AutocompletePresenter
|
||||
import im.vector.riotredesign.core.listener.Listener
|
||||
|
||||
|
@ -60,25 +59,15 @@ abstract class EpoxyAutocompletePresenter<T>(context: Context) : AutocompletePre
|
|||
}
|
||||
|
||||
abstract fun providesController(): EpoxyController
|
||||
/**
|
||||
* Dispatch click event to [AutocompleteCallback].
|
||||
* Should be called when items are clicked.
|
||||
*
|
||||
* @param item the clicked item.
|
||||
*/
|
||||
protected fun dispatchClick(item: T) {
|
||||
clicks?.click(item)
|
||||
}
|
||||
|
||||
protected fun dispatchLayoutChange() {
|
||||
observer?.onChanged()
|
||||
}
|
||||
|
||||
override fun onEvent(t: T) {
|
||||
dispatchClick(t)
|
||||
clicks?.click(t)
|
||||
}
|
||||
|
||||
|
||||
private class Observer internal constructor(private val root: DataSetObserver) : RecyclerView.AdapterDataObserver() {
|
||||
|
||||
override fun onChanged() {
|
||||
|
|
|
@ -19,7 +19,7 @@ package im.vector.riotredesign.features.autocomplete.command
|
|||
import android.text.Spannable
|
||||
import com.otaliastudios.autocomplete.AutocompletePolicy
|
||||
|
||||
class CommandPolicy : AutocompletePolicy {
|
||||
class CommandAutocompletePolicy : AutocompletePolicy {
|
||||
override fun getQuery(text: Spannable): CharSequence {
|
||||
if (text.length > 0) {
|
||||
return text.substring(1, text.length)
|
|
@ -21,6 +21,7 @@ import android.graphics.drawable.ColorDrawable
|
|||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.text.Editable
|
||||
import android.text.Spannable
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -29,14 +30,16 @@ import com.airbnb.mvrx.fragmentViewModel
|
|||
import com.otaliastudios.autocomplete.Autocomplete
|
||||
import com.otaliastudios.autocomplete.AutocompleteCallback
|
||||
import com.otaliastudios.autocomplete.CharPolicy
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.epoxy.LayoutManagerStateRestorer
|
||||
import im.vector.riotredesign.core.glide.GlideApp
|
||||
import im.vector.riotredesign.core.platform.ToolbarConfigurable
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.features.autocomplete.command.AutocompleteCommandPresenter
|
||||
import im.vector.riotredesign.features.autocomplete.command.CommandPolicy
|
||||
import im.vector.riotredesign.features.autocomplete.command.CommandAutocompletePolicy
|
||||
import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserPresenter
|
||||
import im.vector.riotredesign.features.command.Command
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
|
@ -47,6 +50,7 @@ import im.vector.riotredesign.features.home.room.detail.composer.TextComposerVie
|
|||
import im.vector.riotredesign.features.home.room.detail.composer.TextComposerViewState
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.EndlessRecyclerViewScrollListener
|
||||
import im.vector.riotredesign.features.html.PillImageSpan
|
||||
import im.vector.riotredesign.features.media.MediaContentRenderer
|
||||
import im.vector.riotredesign.features.media.MediaViewerActivity
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
@ -75,6 +79,12 @@ class RoomDetailFragment : VectorBaseFragment(), TimelineEventController.Callbac
|
|||
}
|
||||
}
|
||||
|
||||
private val session by inject<Session>()
|
||||
// TODO Inject?
|
||||
private val glideRequests by lazy {
|
||||
GlideApp.with(this)
|
||||
}
|
||||
|
||||
private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
|
||||
private val textComposerViewModel: TextComposerViewModel by fragmentViewModel()
|
||||
private val timelineEventController: TimelineEventController by inject { parametersOf(this) }
|
||||
|
@ -136,16 +146,16 @@ class RoomDetailFragment : VectorBaseFragment(), TimelineEventController.Callbac
|
|||
val elevation = 6f
|
||||
val backgroundDrawable = ColorDrawable(Color.WHITE)
|
||||
Autocomplete.on<Command>(composerEditText)
|
||||
.with(CommandPolicy())
|
||||
.with(CommandAutocompletePolicy())
|
||||
.with(autocompleteCommandPresenter)
|
||||
.with(elevation)
|
||||
.with(backgroundDrawable)
|
||||
.with(object : AutocompleteCallback<Command> {
|
||||
override fun onPopupItemClicked(editable: Editable?, item: Command?): Boolean {
|
||||
editable?.clear()
|
||||
override fun onPopupItemClicked(editable: Editable, item: Command): Boolean {
|
||||
editable.clear()
|
||||
editable
|
||||
?.append(item?.command)
|
||||
?.append(" ")
|
||||
.append(item.command)
|
||||
.append(" ")
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -156,15 +166,37 @@ class RoomDetailFragment : VectorBaseFragment(), TimelineEventController.Callbac
|
|||
|
||||
autocompleteUserPresenter.callback = this
|
||||
Autocomplete.on<User>(composerEditText)
|
||||
.with(CharPolicy('@', false))
|
||||
.with(CharPolicy('@', true))
|
||||
.with(autocompleteUserPresenter)
|
||||
.with(elevation)
|
||||
.with(backgroundDrawable)
|
||||
.with(object : AutocompleteCallback<User> {
|
||||
override fun onPopupItemClicked(editable: Editable?, item: User?): Boolean {
|
||||
// TODO
|
||||
editable?.append(item?.displayName)
|
||||
?.append(" ")
|
||||
override fun onPopupItemClicked(editable: Editable, item: User): Boolean {
|
||||
// Detect last '@' and remove it
|
||||
var startIndex = editable.lastIndexOf("@")
|
||||
if (startIndex == -1) {
|
||||
startIndex = 0
|
||||
}
|
||||
|
||||
// Detect next word separator
|
||||
var endIndex = editable.indexOf(" ", startIndex)
|
||||
if (endIndex == -1) {
|
||||
endIndex = editable.length
|
||||
}
|
||||
|
||||
// Replace the word by its completion
|
||||
val displayName = item.displayName ?: item.userId
|
||||
|
||||
// with a trailing space
|
||||
editable.replace(startIndex, endIndex, "$displayName ")
|
||||
|
||||
// Add the span
|
||||
val user = session.getUser(item.userId)
|
||||
// FIXME avatar is not displayed
|
||||
val span = PillImageSpan(glideRequests, context!!, item.userId, user)
|
||||
|
||||
editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -224,5 +256,4 @@ class RoomDetailFragment : VectorBaseFragment(), TimelineEventController.Callbac
|
|||
override fun onQueryUsers(query: CharSequence?) {
|
||||
textComposerViewModel.process(TextComposerActions.QueryUsers(query))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ import java.lang.ref.WeakReference
|
|||
* This span is able to replace a text by a [ChipDrawable]
|
||||
* It's needed to call [bind] method to start requesting avatar, otherwise only the placeholder icon will be displayed if not already cached.
|
||||
*/
|
||||
|
||||
class PillImageSpan(private val glideRequests: GlideRequests,
|
||||
private val context: Context,
|
||||
private val userId: String,
|
||||
|
|
Loading…
Reference in a new issue