Extract AutoComplete feature from RoomDetailFragment
This commit is contained in:
parent
ed097bcf37
commit
d73a1135ae
@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
* 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.riotx.features.home.room.detail
|
||||||
|
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.Spannable
|
||||||
|
import android.widget.EditText
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.otaliastudios.autocomplete.Autocomplete
|
||||||
|
import com.otaliastudios.autocomplete.AutocompleteCallback
|
||||||
|
import com.otaliastudios.autocomplete.CharPolicy
|
||||||
|
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||||
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
|
import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
|
import im.vector.matrix.android.api.util.toRoomAliasMatrixItem
|
||||||
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.glide.GlideApp
|
||||||
|
import im.vector.riotx.features.autocomplete.command.AutocompleteCommandPresenter
|
||||||
|
import im.vector.riotx.features.autocomplete.command.CommandAutocompletePolicy
|
||||||
|
import im.vector.riotx.features.autocomplete.group.AutocompleteGroupPresenter
|
||||||
|
import im.vector.riotx.features.autocomplete.room.AutocompleteRoomPresenter
|
||||||
|
import im.vector.riotx.features.autocomplete.user.AutocompleteUserPresenter
|
||||||
|
import im.vector.riotx.features.command.Command
|
||||||
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
|
import im.vector.riotx.features.home.room.detail.composer.TextComposerViewState
|
||||||
|
import im.vector.riotx.features.html.PillImageSpan
|
||||||
|
import im.vector.riotx.features.themes.ThemeUtils
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class AutoCompleter @Inject constructor(
|
||||||
|
private val avatarRenderer: AvatarRenderer,
|
||||||
|
private val commandAutocompletePolicy: CommandAutocompletePolicy,
|
||||||
|
private val autocompleteCommandPresenter: AutocompleteCommandPresenter,
|
||||||
|
private val autocompleteUserPresenter: AutocompleteUserPresenter,
|
||||||
|
private val autocompleteRoomPresenter: AutocompleteRoomPresenter,
|
||||||
|
private val autocompleteGroupPresenter: AutocompleteGroupPresenter
|
||||||
|
) {
|
||||||
|
private lateinit var fragment: Fragment
|
||||||
|
|
||||||
|
fun enterSpecialMode() {
|
||||||
|
commandAutocompletePolicy.enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exitSpecialMode() {
|
||||||
|
commandAutocompletePolicy.enabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private val glideRequests by lazy {
|
||||||
|
GlideApp.with(fragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setup(fragment: Fragment, editText: EditText, listener: AutoCompleterListener) {
|
||||||
|
this.fragment = fragment
|
||||||
|
|
||||||
|
val elevation = 6f
|
||||||
|
val backgroundDrawable = ColorDrawable(ThemeUtils.getColor(fragment.requireContext(), R.attr.riotx_background))
|
||||||
|
Autocomplete.on<Command>(editText)
|
||||||
|
.with(commandAutocompletePolicy)
|
||||||
|
.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()
|
||||||
|
|
||||||
|
autocompleteRoomPresenter.callback = listener
|
||||||
|
Autocomplete.on<RoomSummary>(editText)
|
||||||
|
.with(CharPolicy('#', true))
|
||||||
|
.with(autocompleteRoomPresenter)
|
||||||
|
.with(elevation)
|
||||||
|
.with(backgroundDrawable)
|
||||||
|
.with(object : AutocompleteCallback<RoomSummary> {
|
||||||
|
override fun onPopupItemClicked(editable: Editable, item: RoomSummary): 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 matrixItem = item.toRoomAliasMatrixItem()
|
||||||
|
val displayName = matrixItem.getBestName()
|
||||||
|
|
||||||
|
// with a trailing space
|
||||||
|
editable.replace(startIndex, endIndex, "$displayName ")
|
||||||
|
|
||||||
|
// Add the span
|
||||||
|
val span = PillImageSpan(
|
||||||
|
glideRequests,
|
||||||
|
avatarRenderer,
|
||||||
|
fragment.requireContext(),
|
||||||
|
matrixItem
|
||||||
|
)
|
||||||
|
span.bind(editText)
|
||||||
|
|
||||||
|
editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPopupVisibilityChanged(shown: Boolean) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
|
||||||
|
autocompleteGroupPresenter.callback = listener
|
||||||
|
Autocomplete.on<GroupSummary>(editText)
|
||||||
|
.with(CharPolicy('+', true))
|
||||||
|
.with(autocompleteGroupPresenter)
|
||||||
|
.with(elevation)
|
||||||
|
.with(backgroundDrawable)
|
||||||
|
.with(object : AutocompleteCallback<GroupSummary> {
|
||||||
|
override fun onPopupItemClicked(editable: Editable, item: GroupSummary): 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 matrixItem = item.toMatrixItem()
|
||||||
|
val displayName = matrixItem.getBestName()
|
||||||
|
|
||||||
|
// with a trailing space
|
||||||
|
editable.replace(startIndex, endIndex, "$displayName ")
|
||||||
|
|
||||||
|
// Add the span
|
||||||
|
val span = PillImageSpan(
|
||||||
|
glideRequests,
|
||||||
|
avatarRenderer,
|
||||||
|
fragment.requireContext(),
|
||||||
|
matrixItem
|
||||||
|
)
|
||||||
|
span.bind(editText)
|
||||||
|
|
||||||
|
editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPopupVisibilityChanged(shown: Boolean) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
|
||||||
|
autocompleteUserPresenter.callback = listener
|
||||||
|
Autocomplete.on<User>(editText)
|
||||||
|
.with(CharPolicy('@', true))
|
||||||
|
.with(autocompleteUserPresenter)
|
||||||
|
.with(elevation)
|
||||||
|
.with(backgroundDrawable)
|
||||||
|
.with(object : AutocompleteCallback<User> {
|
||||||
|
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 matrixItem = item.toMatrixItem()
|
||||||
|
val displayName = matrixItem.getBestName()
|
||||||
|
|
||||||
|
// with a trailing space
|
||||||
|
editable.replace(startIndex, endIndex, "$displayName ")
|
||||||
|
|
||||||
|
// Add the span
|
||||||
|
val span = PillImageSpan(
|
||||||
|
glideRequests,
|
||||||
|
avatarRenderer,
|
||||||
|
fragment.requireContext(),
|
||||||
|
matrixItem
|
||||||
|
)
|
||||||
|
span.bind(editText)
|
||||||
|
|
||||||
|
editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPopupVisibilityChanged(shown: Boolean) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun render(state: TextComposerViewState) {
|
||||||
|
autocompleteUserPresenter.render(state.asyncUsers)
|
||||||
|
autocompleteRoomPresenter.render(state.asyncRooms)
|
||||||
|
autocompleteGroupPresenter.render(state.asyncGroups)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AutoCompleterListener :
|
||||||
|
AutocompleteUserPresenter.Callback,
|
||||||
|
AutocompleteRoomPresenter.Callback,
|
||||||
|
AutocompleteGroupPresenter.Callback
|
||||||
|
}
|
@ -20,12 +20,10 @@ import android.annotation.SuppressLint
|
|||||||
import android.app.Activity.RESULT_OK
|
import android.app.Activity.RESULT_OK
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.text.Editable
|
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
@ -52,25 +50,18 @@ import com.github.piasy.biv.BigImageViewer
|
|||||||
import com.github.piasy.biv.loader.ImageLoader
|
import com.github.piasy.biv.loader.ImageLoader
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.android.material.textfield.TextInputEditText
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
import com.otaliastudios.autocomplete.Autocomplete
|
|
||||||
import com.otaliastudios.autocomplete.AutocompleteCallback
|
|
||||||
import com.otaliastudios.autocomplete.CharPolicy
|
|
||||||
import im.vector.matrix.android.api.permalinks.PermalinkFactory
|
import im.vector.matrix.android.api.permalinks.PermalinkFactory
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
|
||||||
import im.vector.matrix.android.api.session.room.model.Membership
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
|
||||||
import im.vector.matrix.android.api.session.room.model.message.*
|
import im.vector.matrix.android.api.session.room.model.message.*
|
||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
|
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
|
||||||
import im.vector.matrix.android.api.util.MatrixItem
|
import im.vector.matrix.android.api.util.MatrixItem
|
||||||
import im.vector.matrix.android.api.util.toMatrixItem
|
import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
import im.vector.matrix.android.api.util.toRoomAliasMatrixItem
|
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.dialogs.withColoredButton
|
import im.vector.riotx.core.dialogs.withColoredButton
|
||||||
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
|
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
|
||||||
@ -84,11 +75,6 @@ import im.vector.riotx.core.utils.*
|
|||||||
import im.vector.riotx.features.attachments.AttachmentTypeSelectorView
|
import im.vector.riotx.features.attachments.AttachmentTypeSelectorView
|
||||||
import im.vector.riotx.features.attachments.AttachmentsHelper
|
import im.vector.riotx.features.attachments.AttachmentsHelper
|
||||||
import im.vector.riotx.features.attachments.ContactAttachment
|
import im.vector.riotx.features.attachments.ContactAttachment
|
||||||
import im.vector.riotx.features.autocomplete.command.AutocompleteCommandPresenter
|
|
||||||
import im.vector.riotx.features.autocomplete.command.CommandAutocompletePolicy
|
|
||||||
import im.vector.riotx.features.autocomplete.group.AutocompleteGroupPresenter
|
|
||||||
import im.vector.riotx.features.autocomplete.room.AutocompleteRoomPresenter
|
|
||||||
import im.vector.riotx.features.autocomplete.user.AutocompleteUserPresenter
|
|
||||||
import im.vector.riotx.features.command.Command
|
import im.vector.riotx.features.command.Command
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
import im.vector.riotx.features.home.getColorFromUserId
|
import im.vector.riotx.features.home.getColorFromUserId
|
||||||
@ -117,7 +103,6 @@ import im.vector.riotx.features.permalink.PermalinkHandler
|
|||||||
import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
|
import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
|
||||||
import im.vector.riotx.features.settings.VectorPreferences
|
import im.vector.riotx.features.settings.VectorPreferences
|
||||||
import im.vector.riotx.features.share.SharedData
|
import im.vector.riotx.features.share.SharedData
|
||||||
import im.vector.riotx.features.themes.ThemeUtils
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
@ -142,11 +127,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val avatarRenderer: AvatarRenderer,
|
private val avatarRenderer: AvatarRenderer,
|
||||||
private val timelineEventController: TimelineEventController,
|
private val timelineEventController: TimelineEventController,
|
||||||
private val commandAutocompletePolicy: CommandAutocompletePolicy,
|
private val autoCompleter: AutoCompleter,
|
||||||
private val autocompleteCommandPresenter: AutocompleteCommandPresenter,
|
|
||||||
private val autocompleteUserPresenter: AutocompleteUserPresenter,
|
|
||||||
private val autocompleteRoomPresenter: AutocompleteRoomPresenter,
|
|
||||||
private val autocompleteGroupPresenter: AutocompleteGroupPresenter,
|
|
||||||
private val permalinkHandler: PermalinkHandler,
|
private val permalinkHandler: PermalinkHandler,
|
||||||
private val notificationDrawerManager: NotificationDrawerManager,
|
private val notificationDrawerManager: NotificationDrawerManager,
|
||||||
val roomDetailViewModelFactory: RoomDetailViewModel.Factory,
|
val roomDetailViewModelFactory: RoomDetailViewModel.Factory,
|
||||||
@ -156,9 +137,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
) :
|
) :
|
||||||
VectorBaseFragment(),
|
VectorBaseFragment(),
|
||||||
TimelineEventController.Callback,
|
TimelineEventController.Callback,
|
||||||
AutocompleteUserPresenter.Callback,
|
AutoCompleter.AutoCompleterListener,
|
||||||
AutocompleteRoomPresenter.Callback,
|
|
||||||
AutocompleteGroupPresenter.Callback,
|
|
||||||
VectorInviteView.Callback,
|
VectorInviteView.Callback,
|
||||||
JumpToReadMarkerView.Callback,
|
JumpToReadMarkerView.Callback,
|
||||||
AttachmentTypeSelectorView.Callback,
|
AttachmentTypeSelectorView.Callback,
|
||||||
@ -397,7 +376,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun renderRegularMode(text: String) {
|
private fun renderRegularMode(text: String) {
|
||||||
commandAutocompletePolicy.enabled = true
|
autoCompleter.exitSpecialMode()
|
||||||
composerLayout.collapse()
|
composerLayout.collapse()
|
||||||
|
|
||||||
updateComposerText(text)
|
updateComposerText(text)
|
||||||
@ -408,7 +387,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
@DrawableRes iconRes: Int,
|
@DrawableRes iconRes: Int,
|
||||||
@StringRes descriptionRes: Int,
|
@StringRes descriptionRes: Int,
|
||||||
defaultContent: String) {
|
defaultContent: String) {
|
||||||
commandAutocompletePolicy.enabled = false
|
autoCompleter.enterSpecialMode()
|
||||||
// switch to expanded bar
|
// switch to expanded bar
|
||||||
composerLayout.composerRelatedMessageTitle.apply {
|
composerLayout.composerRelatedMessageTitle.apply {
|
||||||
text = event.getDisambiguatedDisplayName()
|
text = event.getDisambiguatedDisplayName()
|
||||||
@ -580,164 +559,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupComposer() {
|
private fun setupComposer() {
|
||||||
val elevation = 6f
|
autoCompleter.setup(this, composerLayout.composerEditText, this)
|
||||||
val backgroundDrawable = ColorDrawable(ThemeUtils.getColor(requireContext(), R.attr.riotx_background))
|
|
||||||
Autocomplete.on<Command>(composerLayout.composerEditText)
|
|
||||||
.with(commandAutocompletePolicy)
|
|
||||||
.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()
|
|
||||||
|
|
||||||
autocompleteRoomPresenter.callback = this
|
|
||||||
Autocomplete.on<RoomSummary>(composerLayout.composerEditText)
|
|
||||||
.with(CharPolicy('#', true))
|
|
||||||
.with(autocompleteRoomPresenter)
|
|
||||||
.with(elevation)
|
|
||||||
.with(backgroundDrawable)
|
|
||||||
.with(object : AutocompleteCallback<RoomSummary> {
|
|
||||||
override fun onPopupItemClicked(editable: Editable, item: RoomSummary): 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 matrixItem = item.toRoomAliasMatrixItem()
|
|
||||||
val displayName = matrixItem.getBestName()
|
|
||||||
|
|
||||||
// with a trailing space
|
|
||||||
editable.replace(startIndex, endIndex, "$displayName ")
|
|
||||||
|
|
||||||
// Add the span
|
|
||||||
val span = PillImageSpan(
|
|
||||||
glideRequests,
|
|
||||||
avatarRenderer,
|
|
||||||
requireContext(),
|
|
||||||
matrixItem
|
|
||||||
)
|
|
||||||
span.bind(composerLayout.composerEditText)
|
|
||||||
|
|
||||||
editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPopupVisibilityChanged(shown: Boolean) {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
|
|
||||||
autocompleteGroupPresenter.callback = this
|
|
||||||
Autocomplete.on<GroupSummary>(composerLayout.composerEditText)
|
|
||||||
.with(CharPolicy('+', true))
|
|
||||||
.with(autocompleteGroupPresenter)
|
|
||||||
.with(elevation)
|
|
||||||
.with(backgroundDrawable)
|
|
||||||
.with(object : AutocompleteCallback<GroupSummary> {
|
|
||||||
override fun onPopupItemClicked(editable: Editable, item: GroupSummary): 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 matrixItem = item.toMatrixItem()
|
|
||||||
val displayName = matrixItem.getBestName()
|
|
||||||
|
|
||||||
// with a trailing space
|
|
||||||
editable.replace(startIndex, endIndex, "$displayName ")
|
|
||||||
|
|
||||||
// Add the span
|
|
||||||
val span = PillImageSpan(
|
|
||||||
glideRequests,
|
|
||||||
avatarRenderer,
|
|
||||||
requireContext(),
|
|
||||||
matrixItem
|
|
||||||
)
|
|
||||||
span.bind(composerLayout.composerEditText)
|
|
||||||
|
|
||||||
editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPopupVisibilityChanged(shown: Boolean) {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
|
|
||||||
autocompleteUserPresenter.callback = this
|
|
||||||
Autocomplete.on<User>(composerLayout.composerEditText)
|
|
||||||
.with(CharPolicy('@', true))
|
|
||||||
.with(autocompleteUserPresenter)
|
|
||||||
.with(elevation)
|
|
||||||
.with(backgroundDrawable)
|
|
||||||
.with(object : AutocompleteCallback<User> {
|
|
||||||
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 matrixItem = item.toMatrixItem()
|
|
||||||
val displayName = matrixItem.getBestName()
|
|
||||||
|
|
||||||
// with a trailing space
|
|
||||||
editable.replace(startIndex, endIndex, "$displayName ")
|
|
||||||
|
|
||||||
// Add the span
|
|
||||||
val span = PillImageSpan(
|
|
||||||
glideRequests,
|
|
||||||
avatarRenderer,
|
|
||||||
requireContext(),
|
|
||||||
matrixItem
|
|
||||||
)
|
|
||||||
span.bind(composerLayout.composerEditText)
|
|
||||||
|
|
||||||
editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPopupVisibilityChanged(shown: Boolean) {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
|
|
||||||
composerLayout.callback = object : TextComposerView.Callback {
|
composerLayout.callback = object : TextComposerView.Callback {
|
||||||
override fun onAddAttachment() {
|
override fun onAddAttachment() {
|
||||||
@ -834,9 +656,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun renderTextComposerState(state: TextComposerViewState) {
|
private fun renderTextComposerState(state: TextComposerViewState) {
|
||||||
autocompleteUserPresenter.render(state.asyncUsers)
|
autoCompleter.render(state)
|
||||||
autocompleteRoomPresenter.render(state.asyncRooms)
|
|
||||||
autocompleteGroupPresenter.render(state.asyncGroups)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderTombstoneEventHandling(async: Async<String>) {
|
private fun renderTombstoneEventHandling(async: Async<String>) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user