forked from GitHub-Mirror/riotX-android
Autocomplete : start integrating commands. Still need to work on it
This commit is contained in:
parent
a9b8c57464
commit
56563412aa
@ -171,6 +171,8 @@ dependencies {
|
||||
implementation "ru.noties.markwon:core:$markwon_version"
|
||||
implementation "ru.noties.markwon:html:$markwon_version"
|
||||
|
||||
implementation 'com.otaliastudios:autocomplete:1.1.0'
|
||||
|
||||
// Butterknife
|
||||
implementation 'com.jakewharton:butterknife:10.1.0'
|
||||
kapt 'com.jakewharton:butterknife-compiler:10.1.0'
|
||||
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.content.Context
|
||||
import android.database.DataSetObserver
|
||||
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
|
||||
|
||||
abstract class EpoxyViewPresenter<T>(context: Context) : AutocompletePresenter<T>(context) {
|
||||
|
||||
private var recyclerView: EpoxyRecyclerView? = null
|
||||
private var clicks: AutocompletePresenter.ClickProvider<T>? = null
|
||||
private var observer: Observer? = null
|
||||
|
||||
override fun registerClickProvider(provider: AutocompletePresenter.ClickProvider<T>) {
|
||||
this.clicks = provider
|
||||
}
|
||||
|
||||
override fun registerDataSetObserver(observer: DataSetObserver) {
|
||||
this.observer = Observer(observer)
|
||||
}
|
||||
|
||||
override fun getView(): ViewGroup? {
|
||||
recyclerView = EpoxyRecyclerView(context).apply {
|
||||
setController(providesController())
|
||||
observer?.let {
|
||||
adapter?.registerAdapterDataObserver(it)
|
||||
}
|
||||
itemAnimator = null
|
||||
}
|
||||
return recyclerView
|
||||
}
|
||||
|
||||
override fun onViewShown() {}
|
||||
|
||||
|
||||
override fun onViewHidden() {
|
||||
recyclerView = null
|
||||
observer = null
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
private class Observer internal constructor(private val root: DataSetObserver) : RecyclerView.AdapterDataObserver() {
|
||||
|
||||
override fun onChanged() {
|
||||
root.onChanged()
|
||||
}
|
||||
|
||||
override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
|
||||
root.onChanged()
|
||||
}
|
||||
|
||||
override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
|
||||
root.onChanged()
|
||||
}
|
||||
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||
root.onChanged()
|
||||
}
|
||||
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
root.onChanged()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 com.airbnb.epoxy.TypedEpoxyController
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
|
||||
class AutocompleteCommandController(private val stringProvider: StringProvider) : TypedEpoxyController<List<Command>>() {
|
||||
|
||||
override fun buildModels(data: List<Command>?) {
|
||||
if (data.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
data.forEach {
|
||||
autocompleteCommandItem {
|
||||
id(it.command)
|
||||
name(it.command)
|
||||
parameters(it.parameters)
|
||||
description(stringProvider.getString(it.description))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_command_autocomplete)
|
||||
abstract class AutocompleteCommandItem : VectorEpoxyModel<AutocompleteCommandItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var name: CharSequence? = null
|
||||
@EpoxyAttribute
|
||||
var parameters: CharSequence? = null
|
||||
@EpoxyAttribute
|
||||
var description: CharSequence? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.nameView.text = name
|
||||
holder.parametersView.text = parameters
|
||||
holder.descriptionView.text = description
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val nameView by bind<TextView>(R.id.commandName)
|
||||
val parametersView by bind<TextView>(R.id.commandParameter)
|
||||
val descriptionView by bind<TextView>(R.id.commandDescription)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.content.Context
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.riotredesign.features.autocomplete.EpoxyViewPresenter
|
||||
|
||||
class AutocompleteCommandPresenter(context: Context,
|
||||
private val controller: AutocompleteCommandController
|
||||
) : EpoxyViewPresenter<Command>(context) {
|
||||
|
||||
override fun providesController(): EpoxyController {
|
||||
return controller
|
||||
}
|
||||
|
||||
override fun onQuery(query: CharSequence?) {
|
||||
val data = Command.values().filter {
|
||||
if (query.isNullOrEmpty()) {
|
||||
true
|
||||
} else {
|
||||
it.command.startsWith(query, 1, true)
|
||||
}
|
||||
}
|
||||
controller.setData(data)
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 androidx.annotation.StringRes
|
||||
import im.vector.riotredesign.R
|
||||
|
||||
enum class Command(val command: String, val parameters: String, @StringRes val description: Int) {
|
||||
EMOTE("/me", "<message>", R.string.command_description_emote),
|
||||
BAN_USER("/ban", "<user-id> [reason]", R.string.command_description_ban_user),
|
||||
UNBAN_USER("/unban", "<user-id>", R.string.command_description_unban_user),
|
||||
SET_USER_POWER_LEVEL("/op", "<user-id> [<power-level>]", R.string.command_description_op_user),
|
||||
RESET_USER_POWER_LEVEL("/deop", "<user-id>", R.string.command_description_deop_user),
|
||||
INVITE("/invite", "<user-id>", R.string.command_description_invite_user),
|
||||
JOIN_ROOM("/join", "<room-alias>", R.string.command_description_join_room),
|
||||
PART("/part", "<room-alias>", R.string.command_description_part_room),
|
||||
TOPIC("/topic", "<topic>", R.string.command_description_topic),
|
||||
KICK_USER("/kick", "<user-id> [reason]", R.string.command_description_kick_user),
|
||||
CHANGE_DISPLAY_NAME("/nick", "<display-name>", R.string.command_description_nick),
|
||||
MARKDOWN("/markdown", "<on|off>", R.string.command_description_markdown),
|
||||
CLEAR_SCALAR_TOKEN("/clear_scalar_token", "", R.string.command_description_clear_scalar_token);
|
||||
}
|
@ -18,6 +18,8 @@ package im.vector.riotredesign.features.home
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.riotredesign.core.glide.GlideApp
|
||||
import im.vector.riotredesign.features.autocomplete.command.AutocompleteCommandController
|
||||
import im.vector.riotredesign.features.autocomplete.command.AutocompleteCommandPresenter
|
||||
import im.vector.riotredesign.features.home.group.GroupSummaryController
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.*
|
||||
@ -75,6 +77,9 @@ class HomeModule {
|
||||
GroupSummaryController()
|
||||
}
|
||||
|
||||
|
||||
scope(ROOM_DETAIL_SCOPE) { (fragment: Fragment) ->
|
||||
val commandController = AutocompleteCommandController(get())
|
||||
AutocompleteCommandPresenter(fragment.requireContext(), commandController)
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@
|
||||
|
||||
package im.vector.riotredesign.features.home.room.detail
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.View
|
||||
@ -23,11 +25,15 @@ 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.CharPolicy
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.R
|
||||
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.Command
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
import im.vector.riotredesign.features.home.HomeModule
|
||||
import im.vector.riotredesign.features.home.HomePermalinkHandler
|
||||
@ -63,6 +69,7 @@ class RoomDetailFragment : VectorBaseFragment(), TimelineEventController.Callbac
|
||||
|
||||
private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
|
||||
private val timelineEventController: TimelineEventController by inject { parametersOf(this) }
|
||||
private val autocompleteCommandPresenter: AutocompleteCommandPresenter by inject { parametersOf(this) }
|
||||
private val homePermalinkHandler: HomePermalinkHandler by inject()
|
||||
|
||||
private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback
|
||||
@ -74,7 +81,7 @@ class RoomDetailFragment : VectorBaseFragment(), TimelineEventController.Callbac
|
||||
bindScope(getOrCreateScope(HomeModule.ROOM_DETAIL_SCOPE))
|
||||
setupRecyclerView()
|
||||
setupToolbar()
|
||||
setupSendButton()
|
||||
setupComposer()
|
||||
roomDetailViewModel.subscribe { renderState(it) }
|
||||
}
|
||||
|
||||
@ -114,7 +121,16 @@ class RoomDetailFragment : VectorBaseFragment(), TimelineEventController.Callbac
|
||||
timelineEventController.callback = this
|
||||
}
|
||||
|
||||
private fun setupSendButton() {
|
||||
private fun setupComposer() {
|
||||
val elevation = 6f
|
||||
val backgroundDrawable = ColorDrawable(Color.WHITE)
|
||||
Autocomplete.on<Command>(composerEditText)
|
||||
.with(CharPolicy('/', false))
|
||||
.with(autocompleteCommandPresenter)
|
||||
.with(elevation)
|
||||
.with(backgroundDrawable)
|
||||
.build()
|
||||
|
||||
sendButton.setOnClickListener {
|
||||
val textMessage = composerEditText.text.toString()
|
||||
if (textMessage.isNotBlank()) {
|
||||
|
46
vector/src/main/res/layout/item_command_autocomplete.xml
Normal file
46
vector/src/main/res/layout/item_command_autocomplete.xml
Normal file
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="6dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/commandName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="/invite" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/commandParameter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_toEndOf="@+id/commandName"
|
||||
android:layout_toRightOf="@+id/commandName"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="italic"
|
||||
tools:text="<user-id>" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/commandDescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/commandName"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
tools:text="@string/command_description_invite_user" />
|
||||
|
||||
</RelativeLayout>
|
Loading…
Reference in New Issue
Block a user