Skip to content

Commit

Permalink
API: VariantEmoji#getVariants method to modify which variants shall b…
Browse files Browse the repository at this point in the history
…e displayed. Also by default filters out the Variant Selector 16 variants. (#1092)
  • Loading branch information
vanniktech authored May 18, 2024
1 parent 3e68e9a commit a7f084c
Show file tree
Hide file tree
Showing 24 changed files with 405 additions and 298 deletions.
3 changes: 3 additions & 0 deletions emoji/api/current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -349,20 +349,23 @@ package com.vanniktech.emoji.variant {
public final class NoVariantEmoji implements com.vanniktech.emoji.variant.VariantEmoji {
method public void addVariant(com.vanniktech.emoji.Emoji newVariant);
method public com.vanniktech.emoji.Emoji getVariant(com.vanniktech.emoji.Emoji desiredEmoji);
method public java.util.List<com.vanniktech.emoji.Emoji> getVariants(com.vanniktech.emoji.Emoji emoji);
method public void persist();
field public static final com.vanniktech.emoji.variant.NoVariantEmoji INSTANCE;
}

public interface VariantEmoji {
method public void addVariant(com.vanniktech.emoji.Emoji newVariant);
method public com.vanniktech.emoji.Emoji getVariant(com.vanniktech.emoji.Emoji desiredEmoji);
method public java.util.List<com.vanniktech.emoji.Emoji> getVariants(com.vanniktech.emoji.Emoji emoji);
method public void persist();
}

public final class VariantEmojiManager implements com.vanniktech.emoji.variant.VariantEmoji {
ctor public VariantEmojiManager(android.content.Context context);
method public void addVariant(com.vanniktech.emoji.Emoji newVariant);
method public com.vanniktech.emoji.Emoji getVariant(com.vanniktech.emoji.Emoji desiredEmoji);
method public java.util.List<com.vanniktech.emoji.Emoji> getVariants(com.vanniktech.emoji.Emoji emoji);
method public void persist();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ class EmojiView @JvmOverloads constructor(
object : EmojiPagerDelegate {
override fun onEmojiClick(emoji: Emoji) = handleEmojiClick(emoji)

override fun onEmojiLongClick(view: EmojiImageView, emoji: Emoji) {
variantPopup.show(view, emoji)
override fun onEmojiLongClick(view: EmojiImageView, emoji: Emoji, variants: List<Emoji>) {
variantPopup.show(view, emoji, variants)
}
},
recentEmoji, variantEmoji, theming,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ import com.vanniktech.emoji.variant.VariantEmoji
internal class EmojiArrayAdapter(
context: Context,
emojis: Collection<Emoji>,
private val variantEmoji: VariantEmoji?,
private val variantEmoji: VariantEmoji,
private val listener: OnEmojiClickListener?,
private val longListener: OnEmojiLongClickListener?,
private val theming: EmojiTheming,
) : ArrayAdapter<Emoji>(context, 0, emojis.toList()) {
) : ArrayAdapter<Emoji>(context, 0, emojis.toMutableList()) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var image = convertView as? EmojiImageView
val context = context
Expand All @@ -43,10 +43,11 @@ internal class EmojiArrayAdapter(
image.clickListener = listener
image.longClickListener = longListener
}
val emoji = getItem(position)!!
val variantToUse = variantEmoji?.getVariant(emoji) ?: emoji
image.contentDescription = emoji.unicode
image.setEmoji(theming, variantToUse, variantEmoji)
image.setEmoji(
theming = theming,
emoji = getItem(position)!!,
variantEmoji = variantEmoji,
)
return image
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import com.vanniktech.emoji.Emoji
import com.vanniktech.emoji.EmojiManager
import com.vanniktech.emoji.EmojiTheming
import com.vanniktech.emoji.listeners.OnEmojiClickListener
import com.vanniktech.emoji.variant.NoVariantEmoji
import com.vanniktech.emoji.variant.VariantEmoji

internal class EmojiImageView @JvmOverloads constructor(
Expand Down Expand Up @@ -87,30 +86,39 @@ internal class EmojiImageView @JvmOverloads constructor(
imageLoadingTask = null
}

fun setEmoji(theming: EmojiTheming, emoji: Emoji, variantEmoji: VariantEmoji?) {
fun setEmoji(
theming: EmojiTheming,
emoji: Emoji,
variantEmoji: VariantEmoji,
) {
variantIndicatorPaint.color = theming.dividerColor
postInvalidate()

if (emoji != currentEmoji) {
val variant = variantEmoji.getVariant(emoji)

if (variant != currentEmoji) {
contentDescription = variant.unicode
setImageDrawable(null)
currentEmoji = emoji
hasVariants = emoji.base.variants.isNotEmpty() && variantEmoji !is NoVariantEmoji
currentEmoji = variant

val variants = variantEmoji.getVariants(emoji)
hasVariants = variants.isNotEmpty()
cancelFuture()
setOnClickListener {
clickListener?.onEmojiClick(currentEmoji!!)
}
setOnLongClickListener(
when {
hasVariants -> OnLongClickListener {
longClickListener?.onEmojiLongClick(this, emoji)
longClickListener?.onEmojiLongClick(this, emoji, variants)
true
}
else -> null
},
)

imageLoadingTask = ImageLoadingTask(this)
imageLoadingTask?.start(emoji)
imageLoadingTask?.start(variant)
}
}

Expand All @@ -124,6 +132,7 @@ internal class EmojiImageView @JvmOverloads constructor(
fun updateEmoji(emoji: Emoji) {
if (emoji != currentEmoji) {
currentEmoji = emoji
contentDescription = emoji.unicode
setImageDrawable(EmojiManager.emojiDrawableProvider().getDrawable(emoji, context))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ internal class EmojiVariantPopup internal constructor(
) {
private var popupWindow: PopupWindow? = null

fun show(clickedImage: EmojiImageView, emoji: Emoji) {
fun show(clickedImage: EmojiImageView, emoji: Emoji, variants: List<Emoji>) {
dismiss()

val context = clickedImage.context
val content = initView(context, emoji, clickedImage.width, clickedImage)
val content = initView(context, emoji, variants, clickedImage.width, clickedImage)
content.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))
val location = Utils.locationOnScreen(clickedImage)
val desiredLocation = Point(
Expand All @@ -68,14 +68,14 @@ internal class EmojiVariantPopup internal constructor(
popupWindow = null
}

private fun initView(context: Context, emoji: Emoji, width: Int, clickedImage: EmojiImageView): View {
private fun initView(context: Context, emoji: Emoji, variants: List<Emoji>, width: Int, clickedImage: EmojiImageView): View {
val result = View.inflate(context, R.layout.emoji_popup_window_skin, null)
val imageContainer = result.findViewById<LinearLayout>(R.id.emojiPopupWindowSkinPopupContainer)
val variants = emoji.base.variants.toMutableList()
variants.add(0, emoji.base)
val inflater = LayoutInflater.from(context)

for (variant in variants) {
val emojis = listOf(emoji) + variants

for (variant in emojis) {
val emojiImage = inflater.inflate(R.layout.emoji_adapter_item_emoji, imageContainer, false) as ImageView
val layoutParams = emojiImage.layoutParams as MarginLayoutParams
val margin = Utils.dpToPx(context, MARGIN.toFloat())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ package com.vanniktech.emoji.internal
import com.vanniktech.emoji.Emoji

internal fun interface OnEmojiLongClickListener {
fun onEmojiLongClick(view: EmojiImageView, emoji: Emoji)
fun onEmojiLongClick(view: EmojiImageView, emoji: Emoji, variants: List<Emoji>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import android.util.AttributeSet
import com.vanniktech.emoji.EmojiTheming
import com.vanniktech.emoji.listeners.OnEmojiClickListener
import com.vanniktech.emoji.recent.RecentEmoji
import com.vanniktech.emoji.variant.NoVariantEmoji

internal class RecentEmojiGridView @JvmOverloads constructor(
context: Context,
Expand All @@ -37,12 +38,12 @@ internal class RecentEmojiGridView @JvmOverloads constructor(
): RecentEmojiGridView {
recentEmojis = recentEmoji
emojiArrayAdapter = EmojiArrayAdapter(
context,
recentEmoji.getRecentEmojis(),
null,
onEmojiClickListener,
onEmojiLongClickListener,
theming,
context = context,
emojis = recentEmoji.getRecentEmojis(),
variantEmoji = NoVariantEmoji,
listener = onEmojiClickListener,
longListener = onEmojiLongClickListener,
theming = theming,
)
adapter = emojiArrayAdapter
return this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.vanniktech.emoji.variant
import android.content.Context
import com.vanniktech.emoji.Emoji
import com.vanniktech.emoji.EmojiManager
import com.vanniktech.emoji.isVariantSelector16

class VariantEmojiManager(
context: Context,
Expand All @@ -35,6 +36,14 @@ class VariantEmojiManager(
return variantsList.firstOrNull { it.base == baseEmoji } ?: desiredEmoji
}

override fun getVariants(emoji: Emoji): List<Emoji> {
if (emoji.isVariantSelector16()) {
return emptyList()
}

return emoji.variants
}

override fun addVariant(newVariant: Emoji) {
val newVariantBase = newVariant.base

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,92 +19,98 @@ package com.vanniktech.emoji
import android.text.SpannableString
import com.vanniktech.emoji.internal.EmojiSpan
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
import kotlin.test.assertEquals

@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner::class)
class EmojiManagerAndroidTest {
private lateinit var provider: EmojiProvider

@Before fun setUp() {
val emoji1 = TestEmoji(intArrayOf(0x1234), listOf("test"))
val emoji2 = TestEmoji(intArrayOf(0x4321), listOf("test"))
val emoji3 = TestEmoji(intArrayOf(0x5678), listOf("test"))
val emoji4 = TestEmoji(intArrayOf(0x1234, 0x4321, 0x9999), listOf("test"))
provider = TestEmojiProvider(emoji1, emoji2, emoji3, emoji4)
EmojiManager.install(TestEmojiProvider)
}

@After fun tearDown() {
EmojiManager.destroy()
}

@Suppress("DEPRECATION")
@Test fun simple() {
EmojiManager.install(provider)
val text = SpannableString(String(intArrayOf(0x1234), 0, 1))
EmojiManager.replaceWithImages(RuntimeEnvironment.application, text, 44f)
assertEquals(1, text.getSpans(0, text.length, EmojiSpan::class.java).size)
assertEquals(
expected = 1,
actual = spansFor(SpannableString(emojiBalloon.unicode)).size,
)
}

@Suppress("DEPRECATION")
@Test fun inString() {
EmojiManager.install(provider)
val text = SpannableString("test" + String(intArrayOf(0x1234), 0, 1) + "abc")
EmojiManager.replaceWithImages(RuntimeEnvironment.application, text, 22f)
assertEquals(1, text.getSpans(0, text.length, EmojiSpan::class.java).size)
assertEquals(
expected = 1,
actual = spansFor(SpannableString("test" + emojiBalloon.unicode + "abc")).size,
)
}

@Suppress("DEPRECATION")
@Test fun multiple() {
EmojiManager.install(provider)
val text = SpannableString(String(intArrayOf(0x1234), 0, 1) + String(intArrayOf(0x5678), 0, 1))
EmojiManager.replaceWithImages(RuntimeEnvironment.application, text, 22f)
assertEquals(2, text.getSpans(0, text.length, EmojiSpan::class.java).size)
assertEquals(
expected = 2,
actual = spansFor(SpannableString(emojiBalloon.unicode + emojiYoYo.unicode)).size,
)
}

@Suppress("DEPRECATION")
@Test fun multipleInString() {
EmojiManager.install(provider)
val text = SpannableString("abc" + String(intArrayOf(0x1234), 0, 1) + "cba" + String(intArrayOf(0x5678), 0, 1) + "xyz")
EmojiManager.replaceWithImages(RuntimeEnvironment.application, text, 22f)
assertEquals(2, text.getSpans(0, text.length, EmojiSpan::class.java).size)
assertEquals(
expected = 2,
actual = spansFor(SpannableString("abc" + emojiBalloon.unicode + "cba" + emojiYoYo.unicode + "xyz")).size,
)
}

@Suppress("DEPRECATION")
@Test fun halfPath() {
EmojiManager.install(provider)
val text = SpannableString(String(intArrayOf(0x1234, 0x4321), 0, 1))
EmojiManager.replaceWithImages(RuntimeEnvironment.application, text, 11f)
assertEquals(1, text.getSpans(0, text.length, EmojiSpan::class.java).size)
@Test fun emoji() {
assertEquals(
expected = 1,
actual = spansFor(SpannableString(emojiReminderRibbon.unicode)).size,
)
}

@Suppress("DEPRECATION")
@Test fun fullPath() {
EmojiManager.install(provider)
val text = SpannableString(String(intArrayOf(0x1234, 0x4321, 0x9999), 0, 1))
EmojiManager.replaceWithImages(RuntimeEnvironment.application, text, 22f)
assertEquals(1, text.getSpans(0, text.length, EmojiSpan::class.java).size)
@Test fun emojiVariantSelector16() {
assertEquals(
expected = 1,
actual = spansFor(SpannableString(emojiReminderRibbon.variants.first().unicode)).size,
)
}

@Test fun emojiCombined() {
assertEquals(
expected = 1,
actual = spansFor(SpannableString(emojiBaldPerson.unicode)).size,
)
}

@Test fun emojiCombinedVariant() {
assertEquals(
expected = 1,
actual = spansFor(SpannableString(emojiBaldPerson.variants.first().unicode)).size,
)
}

@Suppress("DEPRECATION")
@Test fun empty() {
EmojiManager.install(provider)
val text = SpannableString("")
EmojiManager.replaceWithImages(RuntimeEnvironment.application, text, 22f)
assertEquals(0, text.getSpans(0, text.length, EmojiSpan::class.java).size)
assertEquals(
expected = 0,
actual = spansFor(SpannableString("")).size,
)
}

@Suppress("DEPRECATION")
@Test fun noneInString() {
EmojiManager.install(provider)
val text = SpannableString("abcdefg")
assertEquals(
expected = 0,
actual = spansFor(SpannableString("abcdefg")).size,
)
}

@Suppress("DEPRECATION")
private fun spansFor(text: SpannableString): Array<EmojiSpan> {
EmojiManager.replaceWithImages(RuntimeEnvironment.application, text, 22f)
assertEquals(0, text.getSpans(0, text.length, EmojiSpan::class.java).size)
return text.getSpans(0, text.length, EmojiSpan::class.java)
}
}
Loading

0 comments on commit a7f084c

Please sign in to comment.