mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-22 09:25:49 +03:00
Autocomplete : handle click and better detection for / commands
This commit is contained in:
parent
6d3028c2d7
commit
c64d6b6b28
11 changed files with 149 additions and 18 deletions
|
@ -27,7 +27,7 @@ import kotlin.reflect.KProperty
|
|||
* See [SampleKotlinModelWithHolder] for a usage example.
|
||||
*/
|
||||
abstract class VectorEpoxyHolder : EpoxyHolder() {
|
||||
private lateinit var view: View
|
||||
lateinit var view: View
|
||||
|
||||
override fun bindView(itemView: View) {
|
||||
view = itemView
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotredesign.core.listener
|
||||
|
||||
/**
|
||||
* Simple generic listener interface
|
||||
*/
|
||||
interface Listener<T> {
|
||||
|
||||
fun onEvent(t: T)
|
||||
}
|
|
@ -24,8 +24,9 @@ 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
|
||||
|
||||
abstract class EpoxyViewPresenter<T>(context: Context) : AutocompletePresenter<T>(context) {
|
||||
abstract class EpoxyAutocompletePresenter<T>(context: Context) : AutocompletePresenter<T>(context), Listener<T> {
|
||||
|
||||
private var recyclerView: EpoxyRecyclerView? = null
|
||||
private var clicks: AutocompletePresenter.ClickProvider<T>? = null
|
||||
|
@ -73,6 +74,10 @@ abstract class EpoxyViewPresenter<T>(context: Context) : AutocompletePresenter<T
|
|||
observer?.onChanged()
|
||||
}
|
||||
|
||||
override fun onEvent(t: T) {
|
||||
dispatchClick(t)
|
||||
}
|
||||
|
||||
|
||||
private class Observer internal constructor(private val root: DataSetObserver) : RecyclerView.AdapterDataObserver() {
|
||||
|
|
@ -17,21 +17,27 @@
|
|||
package im.vector.riotredesign.features.autocomplete.command
|
||||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import im.vector.riotredesign.core.listener.Listener
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.command.Command
|
||||
|
||||
class AutocompleteCommandController(private val stringProvider: StringProvider) : TypedEpoxyController<List<Command>>() {
|
||||
|
||||
var listener: Listener<Command>? = null
|
||||
|
||||
override fun buildModels(data: List<Command>?) {
|
||||
if (data.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
data.forEach {
|
||||
data.forEach { command ->
|
||||
autocompleteCommandItem {
|
||||
id(it.command)
|
||||
name(it.command)
|
||||
parameters(it.parameters)
|
||||
description(stringProvider.getString(it.description))
|
||||
id(command.command)
|
||||
name(command.command)
|
||||
parameters(command.parameters)
|
||||
description(stringProvider.getString(command.description))
|
||||
clickListener { _ ->
|
||||
listener?.onEvent(command)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package im.vector.riotredesign.features.autocomplete.command
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
|
@ -32,8 +33,12 @@ abstract class AutocompleteCommandItem : VectorEpoxyModel<AutocompleteCommandIte
|
|||
var parameters: CharSequence? = null
|
||||
@EpoxyAttribute
|
||||
var description: CharSequence? = null
|
||||
@EpoxyAttribute
|
||||
var clickListener: View.OnClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.view.setOnClickListener(clickListener)
|
||||
|
||||
holder.nameView.text = name
|
||||
holder.parametersView.text = parameters
|
||||
holder.descriptionView.text = description
|
||||
|
|
|
@ -18,12 +18,16 @@ package im.vector.riotredesign.features.autocomplete.command
|
|||
|
||||
import android.content.Context
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.riotredesign.features.autocomplete.EpoxyViewPresenter
|
||||
import im.vector.riotredesign.features.autocomplete.EpoxyAutocompletePresenter
|
||||
import im.vector.riotredesign.features.command.Command
|
||||
|
||||
class AutocompleteCommandPresenter(context: Context,
|
||||
private val controller: AutocompleteCommandController
|
||||
) : EpoxyViewPresenter<Command>(context) {
|
||||
private val controller: AutocompleteCommandController) :
|
||||
EpoxyAutocompletePresenter<Command>(context) {
|
||||
|
||||
init {
|
||||
controller.listener = this
|
||||
}
|
||||
|
||||
override fun providesController(): EpoxyController {
|
||||
return controller
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotredesign.features.autocomplete.command
|
||||
|
||||
import android.text.Spannable
|
||||
import com.otaliastudios.autocomplete.AutocompletePolicy
|
||||
|
||||
class CommandPolicy : AutocompletePolicy {
|
||||
override fun getQuery(text: Spannable): CharSequence {
|
||||
if (text.length > 0) {
|
||||
return text.substring(1, text.length)
|
||||
}
|
||||
|
||||
// Should not happen
|
||||
return ""
|
||||
}
|
||||
|
||||
override fun onDismiss(text: Spannable?) {
|
||||
}
|
||||
|
||||
// Only if text which starts with '/' and without space
|
||||
override fun shouldShowPopup(text: Spannable?, cursorPos: Int): Boolean {
|
||||
return text?.startsWith("/") == true
|
||||
&& !text.contains(" ")
|
||||
}
|
||||
|
||||
override fun shouldDismissPopup(text: Spannable?, cursorPos: Int): Boolean {
|
||||
return !shouldShowPopup(text, cursorPos)
|
||||
}
|
||||
}
|
|
@ -18,18 +18,24 @@ package im.vector.riotredesign.features.autocomplete.user
|
|||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotredesign.core.listener.Listener
|
||||
|
||||
class AutocompleteUserController() : TypedEpoxyController<List<User>>() {
|
||||
class AutocompleteUserController : TypedEpoxyController<List<User>>() {
|
||||
|
||||
var listener: Listener<User>? = null
|
||||
|
||||
override fun buildModels(data: List<User>?) {
|
||||
if (data.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
data.forEach {
|
||||
data.forEach { user ->
|
||||
autocompleteUserItem {
|
||||
id(it.userId)
|
||||
name(it.displayName)
|
||||
avatarUrl(it.avatarUrl)
|
||||
id(user.userId)
|
||||
name(user.displayName)
|
||||
avatarUrl(user.avatarUrl)
|
||||
clickListener { _ ->
|
||||
listener?.onEvent(user)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package im.vector.riotredesign.features.autocomplete.user
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
|
@ -32,8 +33,12 @@ abstract class AutocompleteUserItem : VectorEpoxyModel<AutocompleteUserItem.Hold
|
|||
var name: String? = null
|
||||
@EpoxyAttribute
|
||||
var avatarUrl: String? = null
|
||||
@EpoxyAttribute
|
||||
var clickListener: View.OnClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.view.setOnClickListener(clickListener)
|
||||
|
||||
holder.nameView.text = name
|
||||
AvatarRenderer.render(avatarUrl, name, holder.avatarImageView)
|
||||
}
|
||||
|
|
|
@ -21,14 +21,19 @@ import com.airbnb.epoxy.EpoxyController
|
|||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Success
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotredesign.features.autocomplete.EpoxyViewPresenter
|
||||
import im.vector.riotredesign.core.listener.Listener
|
||||
import im.vector.riotredesign.features.autocomplete.EpoxyAutocompletePresenter
|
||||
|
||||
class AutocompleteUserPresenter(context: Context,
|
||||
private val controller: AutocompleteUserController
|
||||
) : EpoxyViewPresenter<User>(context) {
|
||||
) : EpoxyAutocompletePresenter<User>(context), Listener<User> {
|
||||
|
||||
var callback: Callback? = null
|
||||
|
||||
init {
|
||||
controller.listener = this
|
||||
}
|
||||
|
||||
override fun providesController(): EpoxyController {
|
||||
return controller
|
||||
}
|
||||
|
|
|
@ -20,12 +20,14 @@ import android.graphics.Color
|
|||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.text.Editable
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
||||
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.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
|
@ -34,6 +36,7 @@ import im.vector.riotredesign.core.epoxy.LayoutManagerStateRestorer
|
|||
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.user.AutocompleteUserPresenter
|
||||
import im.vector.riotredesign.features.command.Command
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
|
@ -133,10 +136,22 @@ class RoomDetailFragment : VectorBaseFragment(), TimelineEventController.Callbac
|
|||
val elevation = 6f
|
||||
val backgroundDrawable = ColorDrawable(Color.WHITE)
|
||||
Autocomplete.on<Command>(composerEditText)
|
||||
.with(CharPolicy('/', false))
|
||||
.with(CommandPolicy())
|
||||
.with(autocompleteCommandPresenter)
|
||||
.with(elevation)
|
||||
.with(backgroundDrawable)
|
||||
.with(object : AutocompleteCallback<Command> {
|
||||
override fun onPopupItemClicked(editable: Editable?, item: Command?): Boolean {
|
||||
editable?.clear()
|
||||
editable
|
||||
?.append(item?.command)
|
||||
?.append(" ")
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPopupVisibilityChanged(shown: Boolean) {
|
||||
}
|
||||
})
|
||||
.build()
|
||||
|
||||
autocompleteUserPresenter.callback = this
|
||||
|
@ -145,6 +160,17 @@ class RoomDetailFragment : VectorBaseFragment(), TimelineEventController.Callbac
|
|||
.with(autocompleteUserPresenter)
|
||||
.with(elevation)
|
||||
.with(backgroundDrawable)
|
||||
.with(object : AutocompleteCallback<User> {
|
||||
override fun onPopupItemClicked(editable: Editable?, item: User?): Boolean {
|
||||
// TODO
|
||||
editable?.append(item?.displayName)
|
||||
?.append(" ")
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPopupVisibilityChanged(shown: Boolean) {
|
||||
}
|
||||
})
|
||||
.build()
|
||||
|
||||
sendButton.setOnClickListener {
|
||||
|
|
Loading…
Reference in a new issue