Unverified Commit 51f365cd authored by Lucio Maciel's avatar Lucio Maciel Committed by GitHub

Merge branch 'develop' into collpasable-quotes

parents 34e4ede1 e83228d1
package chat.rocket.android.chatroom.adapter
import android.annotation.SuppressLint
import android.text.SpannableStringBuilder
import android.text.style.ImageSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import chat.rocket.android.R
import chat.rocket.android.chatroom.adapter.EmojiSuggestionsAdapter.EmojiSuggestionViewHolder
import chat.rocket.android.chatroom.uimodel.suggestion.EmojiSuggestionUiModel
import chat.rocket.android.emoji.EmojiParser
import chat.rocket.android.emoji.internal.isCustom
import chat.rocket.android.suggestions.model.SuggestionModel
import chat.rocket.android.suggestions.strategy.trie.TrieCompletionStrategy
import chat.rocket.android.suggestions.ui.BaseSuggestionViewHolder
import chat.rocket.android.suggestions.ui.SuggestionsAdapter
import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.suggestion_emoji_item.view.*
class EmojiSuggestionsAdapter : SuggestionsAdapter<EmojiSuggestionViewHolder>(
token = ":",
completionStrategy = TrieCompletionStrategy()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmojiSuggestionViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.suggestion_emoji_item, parent,false)
return EmojiSuggestionViewHolder(view)
}
class EmojiSuggestionViewHolder(view: View) : BaseSuggestionViewHolder(view) {
@SuppressLint("SetTextI18n")
override fun bind(item: SuggestionModel, itemClickListener: SuggestionsAdapter.ItemClickListener?) {
item as EmojiSuggestionUiModel
with(itemView) {
text_emoji_shortname.text = ":${item.text}"
if (item.emoji.isCustom()) {
view_flipper_emoji.displayedChild = 1
val sp = SpannableStringBuilder().append(item.emoji.shortname)
Glide.with(context).asDrawable().load(item.emoji.url).into(image_emoji)
} else {
text_emoji.text = EmojiParser.parse(context, item.emoji.unicode)
view_flipper_emoji.displayedChild = 0
}
setOnClickListener {
itemClickListener?.onClick(item)
}
}
}
}
}
...@@ -13,10 +13,12 @@ import chat.rocket.android.chatroom.uimodel.RoomUiModel ...@@ -13,10 +13,12 @@ import chat.rocket.android.chatroom.uimodel.RoomUiModel
import chat.rocket.android.chatroom.uimodel.UiModelMapper import chat.rocket.android.chatroom.uimodel.UiModelMapper
import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.EmojiSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import chat.rocket.android.core.behaviours.showMessage import chat.rocket.android.core.behaviours.showMessage
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.emoji.EmojiRepository
import chat.rocket.android.helper.MessageHelper import chat.rocket.android.helper.MessageHelper
import chat.rocket.android.helper.UserHelper import chat.rocket.android.helper.UserHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
...@@ -34,7 +36,6 @@ import chat.rocket.android.server.domain.useRealName ...@@ -34,7 +36,6 @@ import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.state import chat.rocket.android.server.infraestructure.state
import chat.rocket.android.util.extension.compressImageAndGetByteArray import chat.rocket.android.util.extension.compressImageAndGetByteArray
import chat.rocket.android.util.extension.compressImageAndGetInputStream
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
...@@ -80,9 +81,7 @@ import kotlinx.coroutines.experimental.launch ...@@ -80,9 +81,7 @@ import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import org.threeten.bp.Instant import org.threeten.bp.Instant
import timber.log.Timber import timber.log.Timber
import java.io.InputStream
import java.util.* import java.util.*
import java.util.zip.DeflaterInputStream
import javax.inject.Inject import javax.inject.Inject
class ChatRoomPresenter @Inject constructor( class ChatRoomPresenter @Inject constructor(
...@@ -1009,6 +1008,20 @@ class ChatRoomPresenter @Inject constructor( ...@@ -1009,6 +1008,20 @@ class ChatRoomPresenter @Inject constructor(
} }
} }
fun loadEmojis() {
launchUI(strategy) {
val emojiSuggestionUiModels = EmojiRepository.getAll().map {
EmojiSuggestionUiModel(
text = it.shortname.replaceFirst(":", ""),
pinned = false,
emoji = it,
searchList = listOf(it.shortname)
)
}
view.populateEmojiSuggestions(emojis = emojiSuggestionUiModels)
}
}
fun runCommand(text: String, roomId: String) { fun runCommand(text: String, roomId: String) {
launchUI(strategy) { launchUI(strategy) {
try { try {
......
...@@ -3,6 +3,7 @@ package chat.rocket.android.chatroom.presentation ...@@ -3,6 +3,7 @@ package chat.rocket.android.chatroom.presentation
import chat.rocket.android.chatroom.uimodel.BaseUiModel import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.EmojiSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import chat.rocket.android.core.behaviours.LoadingView import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView import chat.rocket.android.core.behaviours.MessageView
...@@ -127,6 +128,9 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -127,6 +128,9 @@ interface ChatRoomView : LoadingView, MessageView {
fun populatePeopleSuggestions(members: List<PeopleSuggestionUiModel>) fun populatePeopleSuggestions(members: List<PeopleSuggestionUiModel>)
fun populateRoomSuggestions(chatRooms: List<ChatRoomSuggestionUiModel>) fun populateRoomSuggestions(chatRooms: List<ChatRoomSuggestionUiModel>)
fun populateEmojiSuggestions(emojis: List<EmojiSuggestionUiModel>)
/** /**
* This user has joined the chat callback. * This user has joined the chat callback.
* *
......
...@@ -35,6 +35,7 @@ import chat.rocket.android.analytics.AnalyticsManager ...@@ -35,6 +35,7 @@ import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatroom.adapter.ChatRoomAdapter import chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import chat.rocket.android.chatroom.adapter.CommandSuggestionsAdapter import chat.rocket.android.chatroom.adapter.CommandSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.EmojiSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.PEOPLE import chat.rocket.android.chatroom.adapter.PEOPLE
import chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter import chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter import chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter
...@@ -45,6 +46,7 @@ import chat.rocket.android.chatroom.uimodel.BaseUiModel ...@@ -45,6 +46,7 @@ import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.chatroom.uimodel.MessageUiModel import chat.rocket.android.chatroom.uimodel.MessageUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.EmojiSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import chat.rocket.android.draw.main.ui.DRAWING_BYTE_ARRAY_EXTRA_DATA import chat.rocket.android.draw.main.ui.DRAWING_BYTE_ARRAY_EXTRA_DATA
import chat.rocket.android.draw.main.ui.DrawingActivity import chat.rocket.android.draw.main.ui.DrawingActivity
...@@ -556,11 +558,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -556,11 +558,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
override fun showReplyingAction( override fun showReplyingAction(username: String, replyMarkdown: String, quotedMessage: String) {
username: String,
replyMarkdown: String,
quotedMessage: String
) {
ui { ui {
citation = replyMarkdown citation = replyMarkdown
actionSnackbar.title = username actionSnackbar.title = username
...@@ -610,6 +608,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -610,6 +608,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
override fun populateEmojiSuggestions(emojis: List<EmojiSuggestionUiModel>) {
ui {
suggestions_view.addItems(":", emojis)
}
}
override fun copyToClipboard(message: String) { override fun copyToClipboard(message: String) {
ui { ui {
val clipboard = it.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipboard = it.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
...@@ -769,10 +773,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -769,10 +773,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
button_show_attachment_options.alpha = 1f button_show_attachment_options.alpha = 1f
button_show_attachment_options.isVisible = true button_show_attachment_options.isVisible = true
activity?.supportFragmentManager?.addOnBackStackChangedListener {
println("attach")
}
activity?.supportFragmentManager?.registerFragmentLifecycleCallbacks( activity?.supportFragmentManager?.registerFragmentLifecycleCallbacks(
object : FragmentManager.FragmentLifecycleCallbacks() { object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentAttached( override fun onFragmentAttached(
...@@ -837,16 +837,16 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -837,16 +837,16 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}, 400) }, 400)
} }
button_add_reaction.setOnClickListener { view -> button_add_reaction.setOnClickListener { _ ->
openEmojiKeyboardPopup() openEmojiKeyboardPopup()
} }
button_drawing.setOnClickListener { button_drawing.setOnClickListener {
activity?.let { activity?.let { fragmentActivity ->
if (!ImageHelper.canWriteToExternalStorage(it)) { if (!ImageHelper.canWriteToExternalStorage(fragmentActivity)) {
ImageHelper.checkWritingPermission(it) ImageHelper.checkWritingPermission(fragmentActivity)
} else { } else {
val intent = Intent(it, DrawingActivity::class.java) val intent = Intent(fragmentActivity, DrawingActivity::class.java)
startActivityForResult(intent, REQUEST_CODE_FOR_DRAW) startActivityForResult(intent, REQUEST_CODE_FOR_DRAW)
} }
} }
...@@ -877,6 +877,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -877,6 +877,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
.addTokenAdapter(PeopleSuggestionsAdapter(context!!)) .addTokenAdapter(PeopleSuggestionsAdapter(context!!))
.addTokenAdapter(CommandSuggestionsAdapter()) .addTokenAdapter(CommandSuggestionsAdapter())
.addTokenAdapter(RoomSuggestionsAdapter()) .addTokenAdapter(RoomSuggestionsAdapter())
.addTokenAdapter(EmojiSuggestionsAdapter())
.addSuggestionProviderAction("@") { query -> .addSuggestionProviderAction("@") { query ->
if (query.isNotEmpty()) { if (query.isNotEmpty()) {
presenter.spotlight(query, PEOPLE, true) presenter.spotlight(query, PEOPLE, true)
...@@ -887,10 +888,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -887,10 +888,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
presenter.loadChatRooms() presenter.loadChatRooms()
} }
} }
.addSuggestionProviderAction("/") { _ -> .addSuggestionProviderAction("/") {
presenter.loadCommands() presenter.loadCommands()
} }
.addSuggestionProviderAction(":") {
presenter.loadEmojis()
}
presenter.loadEmojis()
presenter.loadCommands() presenter.loadCommands()
} }
......
package chat.rocket.android.chatroom.uimodel.suggestion
import chat.rocket.android.emoji.Emoji
import chat.rocket.android.suggestions.model.SuggestionModel
import chat.rocket.common.model.UserStatus
class EmojiSuggestionUiModel(
text: String,
pinned: Boolean = false,
val emoji: Emoji,
searchList: List<String>
) : SuggestionModel(text, searchList, pinned) {
override fun toString(): String {
return "EmojiSuggestionUiModel(text='$text', searchList='$searchList', pinned=$pinned)"
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="2dp"
android:layout_marginEnd="2dp"
android:layout_marginBottom="2dp"
android:background="@color/suggestion_background_color">
<ViewFlipper
android:id="@+id/view_flipper_emoji"
android:layout_width="24dp"
android:layout_height="24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/text_emoji"
android:layout_width="24dp"
android:layout_height="24dp"
android:textColor="@android:color/black"
tools:text=":)" />
<ImageView
android:id="@+id/image_emoji"
android:layout_width="24dp"
android:layout_height="24dp"
tools:src="@tools:sample/avatars" />
</ViewFlipper>
<TextView
android:id="@+id/text_emoji_shortname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:maxLines="1"
android:textColor="@color/colorBlack"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/view_flipper_emoji"
app:layout_constraintTop_toTopOf="parent"
tools:text=":grinning:" />
</androidx.constraintlayout.widget.ConstraintLayout>
...@@ -146,7 +146,7 @@ object EmojiRepository { ...@@ -146,7 +146,7 @@ object EmojiRepository {
* *
* @return All emojis for all categories. * @return All emojis for all categories.
*/ */
internal suspend fun getAll(): List<Emoji> = withContext(CommonPool) { suspend fun getAll(): List<Emoji> = withContext(CommonPool) {
return@withContext db.emojiDao().loadAllEmojis() return@withContext db.emojiDao().loadAllEmojis()
} }
......
package yampsample.leonardoaramaki.github.com.suggestions;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("yampsample.leonardoaramaki.github.com.suggestions.test", appContext.getPackageName());
}
}
package chat.rocket.android.suggestions.model package chat.rocket.android.suggestions.model
abstract class SuggestionModel(val text: String, // This is the text key for searches, must be unique. abstract class SuggestionModel(
val searchList: List<String> = emptyList(), // Where to search for matches. val text: String, // This is the text key for searches, must be unique.
val pinned: Boolean = false /* If pinned item will have priority to show */) { val searchList: List<String> = emptyList(), // Where to search for matches.
val pinned: Boolean = false /* If pinned item will have priority to show */
) {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is SuggestionModel) return false if (other !is SuggestionModel) return false
......
...@@ -18,7 +18,9 @@ class TrieCompletionStrategy : CompletionStrategy { ...@@ -18,7 +18,9 @@ class TrieCompletionStrategy : CompletionStrategy {
return item return item
} }
override fun autocompleteItems(prefix: String) = trie.autocompleteItems(prefix) override fun autocompleteItems(prefix: String): List<SuggestionModel> {
return trie.autocompleteItems(prefix)
}
override fun addAll(list: List<SuggestionModel>) { override fun addAll(list: List<SuggestionModel>) {
items.addAll(list) items.addAll(list)
......
...@@ -34,10 +34,7 @@ internal class Trie { ...@@ -34,10 +34,7 @@ internal class Trie {
val sanitizedWord = word.trim().toLowerCase() val sanitizedWord = word.trim().toLowerCase()
var current = root var current = root
sanitizedWord.forEach { ch -> sanitizedWord.forEach { ch ->
val child = current.getChild(ch) val child = current.getChild(ch) ?: return false
if (child == null) {
return false
}
current = child current = child
} }
if (current.isLeaf) { if (current.isLeaf) {
...@@ -63,7 +60,7 @@ internal class Trie { ...@@ -63,7 +60,7 @@ internal class Trie {
lastNode = lastNode?.getChild(ch) lastNode = lastNode?.getChild(ch)
if (lastNode == null) return emptyList() if (lastNode == null) return emptyList()
} }
return lastNode!!.getItems() return lastNode!!.getItems().take(5).toList()
} }
fun getCount() = count fun getCount() = count
......
package chat.rocket.android.suggestions.strategy.trie.data package chat.rocket.android.suggestions.strategy.trie.data
import chat.rocket.android.suggestions.model.SuggestionModel import chat.rocket.android.suggestions.model.SuggestionModel
import kotlin.coroutines.experimental.buildSequence
internal class TrieNode(
internal var data: Char,
internal var parent: TrieNode? = null,
internal var isLeaf: Boolean = false,
internal var item: SuggestionModel? = null
) {
internal class TrieNode(internal var data: Char,
internal var parent: TrieNode? = null,
internal var isLeaf: Boolean = false,
internal var item: SuggestionModel? = null) {
val children = hashMapOf<Char, TrieNode>() val children = hashMapOf<Char, TrieNode>()
fun getChild(c: Char): TrieNode? { fun getChild(c: Char): TrieNode? {
...@@ -28,19 +32,17 @@ internal class TrieNode(internal var data: Char, ...@@ -28,19 +32,17 @@ internal class TrieNode(internal var data: Char,
return list return list
} }
class X : SuggestionModel("") fun getItems(): Sequence<SuggestionModel> = buildSequence {
fun getItems(): List<SuggestionModel> {
val list = arrayListOf<SuggestionModel>()
if (isLeaf) { if (isLeaf) {
list.add(item!!) yield(item!!)
} }
children.forEach { node -> children.forEach { node ->
node.value.let { node.value.let {
list.addAll(it.getItems()) yieldAll(it.getItems())
} }
} }
return list
} }
override fun toString(): String = if (parent == null) "" else "${parent.toString()}$data" override fun toString(): String = if (parent == null) "" else "${parent.toString()}$data"
......
...@@ -10,17 +10,9 @@ import kotlin.properties.Delegates ...@@ -10,17 +10,9 @@ import kotlin.properties.Delegates
abstract class SuggestionsAdapter<VH : BaseSuggestionViewHolder>( abstract class SuggestionsAdapter<VH : BaseSuggestionViewHolder>(
val token: String, val token: String,
val constraint: Int = CONSTRAINT_UNBOUND, val constraint: Int = CONSTRAINT_UNBOUND,
threshold: Int = MAX_RESULT_COUNT) : RecyclerView.Adapter<VH>() { completionStrategy: CompletionStrategy? = null,
companion object { threshold: Int = MAX_RESULT_COUNT
// Any number of results. ) : RecyclerView.Adapter<VH>() {
const val RESULT_COUNT_UNLIMITED = -1
// Trigger suggestions only if on the line start.
const val CONSTRAINT_BOUND_TO_START = 0
// Trigger suggestions from anywhere.
const val CONSTRAINT_UNBOUND = 1
// Maximum number of results to display by default.
private const val MAX_RESULT_COUNT = 5
}
private var itemType: Type? = null private var itemType: Type? = null
private var itemClickListener: ItemClickListener? = null private var itemClickListener: ItemClickListener? = null
...@@ -30,7 +22,7 @@ abstract class SuggestionsAdapter<VH : BaseSuggestionViewHolder>( ...@@ -30,7 +22,7 @@ abstract class SuggestionsAdapter<VH : BaseSuggestionViewHolder>(
// Maximum number of results/suggestions to display. // Maximum number of results/suggestions to display.
private var resultsThreshold: Int = if (threshold > 0) threshold else RESULT_COUNT_UNLIMITED private var resultsThreshold: Int = if (threshold > 0) threshold else RESULT_COUNT_UNLIMITED
// The strategy used for suggesting completions. // The strategy used for suggesting completions.
private val strategy: CompletionStrategy = StringMatchingCompletionStrategy(resultsThreshold) private val strategy: CompletionStrategy = completionStrategy ?: StringMatchingCompletionStrategy(resultsThreshold)
// Current input term to look up for suggestions. // Current input term to look up for suggestions.
private var currentTerm: String by Delegates.observable("") { _, _, newTerm -> private var currentTerm: String by Delegates.observable("") { _, _, newTerm ->
val items = strategy.autocompleteItems(newTerm) val items = strategy.autocompleteItems(newTerm)
...@@ -105,4 +97,15 @@ abstract class SuggestionsAdapter<VH : BaseSuggestionViewHolder>( ...@@ -105,4 +97,15 @@ abstract class SuggestionsAdapter<VH : BaseSuggestionViewHolder>(
interface ItemClickListener { interface ItemClickListener {
fun onClick(item: SuggestionModel) fun onClick(item: SuggestionModel)
} }
}
\ No newline at end of file companion object {
// Any number of results.
const val RESULT_COUNT_UNLIMITED = -1
// Trigger suggestions only if on the line start.
const val CONSTRAINT_BOUND_TO_START = 0
// Trigger suggestions from anywhere.
const val CONSTRAINT_UNBOUND = 1
// Maximum number of results to display by default.
private const val MAX_RESULT_COUNT = 5
}
}
...@@ -149,15 +149,14 @@ class SuggestionsView : FrameLayout, TextWatcher { ...@@ -149,15 +149,14 @@ class SuggestionsView : FrameLayout, TextWatcher {
} }
fun addTokenAdapter(adapter: SuggestionsAdapter<*>): SuggestionsView { fun addTokenAdapter(adapter: SuggestionsAdapter<*>): SuggestionsView {
adaptersByToken.getOrPut(adapter.token, { adapter }) adaptersByToken.getOrPut(adapter.token) { adapter }
return this return this
} }
fun addItems(token: String, list: List<SuggestionModel>): SuggestionsView { fun addItems(token: String, list: List<SuggestionModel>): SuggestionsView {
if (list.isNotEmpty()) { if (list.isNotEmpty()) {
val adapter = adapter(token) val adapter = adapter(token)
localProvidersByToken.getOrPut(token, { hashMapOf() }) localProvidersByToken.getOrPut(token) { hashMapOf() }.put(adapter.term(), list)
.put(adapter.term(), list)
if (completionOffset.get() > NO_STATE_INDEX && adapter.itemCount == 0) expand() if (completionOffset.get() > NO_STATE_INDEX && adapter.itemCount == 0) expand()
adapter.addItems(list) adapter.addItems(list)
} }
......
package yampsample.leonardoaramaki.github.com.suggestions;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment