Tweak emoji suggestions UX.
This commit is contained in:
parent
440d041402
commit
d7d923c820
7 changed files with 23 additions and 16 deletions
|
@ -52,7 +52,7 @@ import static org.thoughtcrime.securesms.database.MentionUtil.MENTION_STARTER;
|
||||||
public class ComposeText extends EmojiEditText {
|
public class ComposeText extends EmojiEditText {
|
||||||
|
|
||||||
private static final char EMOJI_STARTER = ':';
|
private static final char EMOJI_STARTER = ':';
|
||||||
private static final long EMOJI_KEYWORD_DELAY = TimeUnit.SECONDS.toMillis(1);
|
private static final long EMOJI_KEYWORD_DELAY = 1500;
|
||||||
|
|
||||||
private CharSequence hint;
|
private CharSequence hint;
|
||||||
private SpannableString subHint;
|
private SpannableString subHint;
|
||||||
|
@ -322,11 +322,9 @@ public class ComposeText extends EmojiEditText {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doAfterCursorChange(@NonNull Editable text) {
|
private void doAfterCursorChange(@NonNull Editable text) {
|
||||||
removeCallbacks(keywordSearchRunnable);
|
|
||||||
if (enoughToFilter(text, false)) {
|
if (enoughToFilter(text, false)) {
|
||||||
performFiltering(text, false);
|
performFiltering(text, false);
|
||||||
} else {
|
} else {
|
||||||
postDelayed(keywordSearchRunnable, EMOJI_KEYWORD_DELAY);
|
|
||||||
clearInlineQuery();
|
clearInlineQuery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2358,6 +2358,7 @@ public class ConversationParentFragment extends Fragment
|
||||||
composeText,
|
composeText,
|
||||||
getViewLifecycleOwner()
|
getViewLifecycleOwner()
|
||||||
);
|
);
|
||||||
|
inlineQueryResultsController.onOrientationChange(getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE);
|
||||||
|
|
||||||
recipient.observe(getViewLifecycleOwner(), r -> {
|
recipient.observe(getViewLifecycleOwner(), r -> {
|
||||||
if (r.isPushV2Group() && !mentionsSuggestions.resolved()) {
|
if (r.isPushV2Group() && !mentionsSuggestions.resolved()) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ sealed class InlineQueryReplacement(@get:JvmName("isKeywordSearch") val keywordS
|
||||||
|
|
||||||
class Emoji(private val emoji: String, keywordSearch: Boolean) : InlineQueryReplacement(keywordSearch) {
|
class Emoji(private val emoji: String, keywordSearch: Boolean) : InlineQueryReplacement(keywordSearch) {
|
||||||
override fun toCharSequence(context: Context): CharSequence {
|
override fun toCharSequence(context: Context): CharSequence {
|
||||||
return "$emoji "
|
return emoji
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||||
import org.signal.core.util.DimensionUnit
|
import org.signal.core.util.DimensionUnit
|
||||||
import org.thoughtcrime.securesms.components.ComposeText
|
import org.thoughtcrime.securesms.components.ComposeText
|
||||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||||
import org.thoughtcrime.securesms.util.VibrateUtil
|
|
||||||
import org.thoughtcrime.securesms.util.adapter.mapping.AnyMappingModel
|
import org.thoughtcrime.securesms.util.adapter.mapping.AnyMappingModel
|
||||||
import org.thoughtcrime.securesms.util.doOnEachLayout
|
import org.thoughtcrime.securesms.util.doOnEachLayout
|
||||||
|
|
||||||
|
@ -30,6 +29,7 @@ class InlineQueryResultsController(
|
||||||
private var popup: InlineQueryResultsPopup? = null
|
private var popup: InlineQueryResultsPopup? = null
|
||||||
private var previousResults: List<AnyMappingModel>? = null
|
private var previousResults: List<AnyMappingModel>? = null
|
||||||
private var canShow: Boolean = false
|
private var canShow: Boolean = false
|
||||||
|
private var isLandscape: Boolean = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
lifecycleDisposable.bindTo(lifecycleOwner)
|
lifecycleDisposable.bindTo(lifecycleOwner)
|
||||||
|
@ -61,6 +61,8 @@ class InlineQueryResultsController(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onOrientationChange(isLandscape: Boolean) {
|
fun onOrientationChange(isLandscape: Boolean) {
|
||||||
|
this.isLandscape = isLandscape
|
||||||
|
|
||||||
if (isLandscape) {
|
if (isLandscape) {
|
||||||
dismiss()
|
dismiss()
|
||||||
} else {
|
} else {
|
||||||
|
@ -70,7 +72,7 @@ class InlineQueryResultsController(
|
||||||
|
|
||||||
private fun updateList(results: List<AnyMappingModel>) {
|
private fun updateList(results: List<AnyMappingModel>) {
|
||||||
previousResults = results
|
previousResults = results
|
||||||
if (results.isEmpty() || !canShow) {
|
if (results.isEmpty() || !canShow || isLandscape) {
|
||||||
dismiss()
|
dismiss()
|
||||||
} else if (popup != null) {
|
} else if (popup != null) {
|
||||||
popup?.setResults(results)
|
popup?.setResults(results)
|
||||||
|
@ -82,7 +84,6 @@ class InlineQueryResultsController(
|
||||||
baseOffsetX = DimensionUnit.DP.toPixels(16f).toInt(),
|
baseOffsetX = DimensionUnit.DP.toPixels(16f).toInt(),
|
||||||
callback = this
|
callback = this
|
||||||
).show()
|
).show()
|
||||||
VibrateUtil.vibrateTick(context)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ import android.os.Build
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.ViewOutlineProvider
|
||||||
import android.widget.PopupWindow
|
import android.widget.PopupWindow
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil
|
import org.thoughtcrime.securesms.util.ViewUtil
|
||||||
|
@ -34,7 +34,11 @@ class InlineQueryResultsPopup(
|
||||||
private val adapter: MappingAdapter
|
private val adapter: MappingAdapter
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.signal_context_menu_background))
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
|
contentView.outlineProvider = ViewOutlineProvider.BACKGROUND
|
||||||
|
contentView.clipToOutline = true
|
||||||
|
}
|
||||||
|
|
||||||
inputMethodMode = INPUT_METHOD_NOT_NEEDED
|
inputMethodMode = INPUT_METHOD_NOT_NEEDED
|
||||||
|
|
||||||
setOnDismissListener {
|
setOnDismissListener {
|
||||||
|
|
|
@ -18,18 +18,20 @@ private const val MINIMUM_QUERY_THRESHOLD = 1
|
||||||
private const val MINIMUM_INLINE_QUERY_THRESHOLD = 2
|
private const val MINIMUM_INLINE_QUERY_THRESHOLD = 2
|
||||||
private const val EMOJI_SEARCH_LIMIT = 20
|
private const val EMOJI_SEARCH_LIMIT = 20
|
||||||
|
|
||||||
|
private val NOT_PUNCTUATION = "[A-Za-z0-9 ]".toRegex()
|
||||||
|
|
||||||
class EmojiSearchRepository(private val context: Context) {
|
class EmojiSearchRepository(private val context: Context) {
|
||||||
|
|
||||||
private val emojiSearchDatabase: EmojiSearchDatabase = SignalDatabase.emojiSearch
|
private val emojiSearchDatabase: EmojiSearchDatabase = SignalDatabase.emojiSearch
|
||||||
|
|
||||||
fun submitQuery(query: String, limit: Int = EMOJI_SEARCH_LIMIT): Single<List<String>> {
|
fun submitQuery(query: String, limit: Int = EMOJI_SEARCH_LIMIT): Single<List<String>> {
|
||||||
if (query.length < MINIMUM_INLINE_QUERY_THRESHOLD) {
|
val result = if (query.length >= MINIMUM_INLINE_QUERY_THRESHOLD && NOT_PUNCTUATION.matches(query.substring(query.lastIndex))) {
|
||||||
return Single.just(emptyList())
|
Single.fromCallable<List<String>> { emojiSearchDatabase.query(query, limit) }
|
||||||
|
} else {
|
||||||
|
Single.just(emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
return Single.fromCallable<List<String>> {
|
return result.subscribeOn(Schedulers.io())
|
||||||
emojiSearchDatabase.query(query, limit)
|
|
||||||
}.subscribeOn(Schedulers.io())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun submitQuery(query: String, includeRecents: Boolean, limit: Int = EMOJI_SEARCH_LIMIT, consumer: Consumer<EmojiPageModel>) {
|
fun submitQuery(query: String, includeRecents: Boolean, limit: Int = EMOJI_SEARCH_LIMIT, consumer: Consumer<EmojiPageModel>) {
|
||||||
|
|
|
@ -2,16 +2,17 @@
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/signal_context_menu_background">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/inline_query_results_list"
|
android:id="@+id/inline_query_results_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
|
android:clipToPadding="false"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingHorizontal="8dp"
|
android:paddingHorizontal="8dp"
|
||||||
android:clipToPadding="false"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
Loading…
Add table
Reference in a new issue