Improve system emoji rendering across the app with EmojiCompat2.

Resolves #13327
This commit is contained in:
Dan Brunwasser
2022-09-13 00:17:01 -07:00
committed by Greyson Parrelli
parent abd80c5204
commit 10922594b3
22 changed files with 87 additions and 27 deletions

View File

@@ -42,11 +42,10 @@ public class EmojiEditText extends AppCompatEditText {
super(context, attrs, defStyleAttr);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.EmojiTextView, 0, 0);
boolean forceCustom = a.getBoolean(R.styleable.EmojiTextView_emoji_forceCustom, false);
boolean jumboEmoji = a.getBoolean(R.styleable.EmojiTextView_emoji_forceJumbo, false);
a.recycle();
if (!isInEditMode() && (forceCustom || !SignalStore.settings().isPreferSystemEmoji())) {
if (!isInEditMode() && !SignalStore.settings().isPreferSystemEmoji()) {
setFilters(appendEmojiFilter(this.getFilters(), jumboEmoji));
setEmojiCompatEnabled(false);
}

View File

@@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser;
import org.thoughtcrime.securesms.emoji.EmojiPageCache;
import org.thoughtcrime.securesms.emoji.EmojiSource;
import org.thoughtcrime.securesms.emoji.JumboEmoji;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.DeviceProperties;
import org.thoughtcrime.securesms.util.FutureTaskListener;
@@ -121,6 +122,10 @@ public class EmojiProvider {
return null;
}
if (SignalStore.settings().isPreferSystemEmoji()) {
return new SystemEmojiDrawable(drawInfo.getEmoji());
}
final int lowMemoryDecodeScale = DeviceProperties.isLowMemoryDevice(context) ? 2 : 1;
final EmojiSource source = EmojiSource.getLatest();
final EmojiDrawable drawable = new EmojiDrawable(source, drawInfo, lowMemoryDecodeScale);
@@ -202,6 +207,10 @@ public class EmojiProvider {
return null;
}
if (SignalStore.settings().isPreferSystemEmoji()) {
return new SystemEmojiDrawable(drawInfo.getEmoji());
}
final int lowMemoryDecodeScale = DeviceProperties.isLowMemoryDevice(context) ? 2 : 1;
final EmojiSource source = EmojiSource.getLatest();
final EmojiDrawable drawable = new EmojiDrawable(source, drawInfo, lowMemoryDecodeScale);

View File

@@ -57,7 +57,6 @@ public class EmojiTextView extends AppCompatTextView {
private static final char ELLIPSIS = '…';
private static final float JUMBOMOJI_SCALE = 0.8f;
private boolean forceCustom;
private CharSequence previousText;
private BufferType previousBufferType;
private TransformationMethod previousTransformationMethod;
@@ -93,7 +92,6 @@ public class EmojiTextView extends AppCompatTextView {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.EmojiTextView, 0, 0);
scaleEmojis = a.getBoolean(R.styleable.EmojiTextView_scaleEmojis, false);
maxLength = a.getInteger(R.styleable.EmojiTextView_emoji_maxLength, -1);
forceCustom = a.getBoolean(R.styleable.EmojiTextView_emoji_forceCustom, false);
renderMentions = a.getBoolean(R.styleable.EmojiTextView_emoji_renderMentions, true);
measureLastLine = a.getBoolean(R.styleable.EmojiTextView_measureLastLine, false);
forceJumboEmoji = a.getBoolean(R.styleable.EmojiTextView_emoji_forceJumbo, false);
@@ -445,7 +443,7 @@ public class EmojiTextView extends AppCompatTextView {
}
private boolean useSystemEmoji() {
return isInEditMode() || (!forceCustom && SignalStore.settings().isPreferSystemEmoji());
return isInEditMode() || SignalStore.settings().isPreferSystemEmoji();
}
@Override

View File

@@ -0,0 +1,69 @@
package org.thoughtcrime.securesms.components.emoji
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.Matrix
import android.graphics.PixelFormat
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.drawable.Drawable
import android.os.Build
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import androidx.core.graphics.toRectF
import androidx.core.graphics.withMatrix
import androidx.emoji2.text.EmojiCompat
/**
* [Drawable] that renders an emoji via the system font for available glyphs and EmojiCompat for
* missing glyphs.
*/
class SystemEmojiDrawable(emoji: CharSequence) : Drawable() {
private val emojiLayout: StaticLayout = getStaticLayout(getProcessedEmoji(emoji))
private val transform: Matrix = Matrix()
override fun onBoundsChange(bounds: Rect) {
super.onBoundsChange(bounds)
transform.setRectToRect(emojiLayout.getBounds(), bounds.toRectF(), Matrix.ScaleToFit.CENTER)
}
override fun draw(canvas: Canvas) {
canvas.withMatrix(transform) {
emojiLayout.draw(canvas)
}
}
override fun setAlpha(alpha: Int) {}
override fun setColorFilter(colorFilter: ColorFilter?) {}
@Deprecated(
"Deprecated in Java",
ReplaceWith("PixelFormat.TRANSLUCENT", "android.graphics.PixelFormat")
)
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
companion object {
private val textPaint: TextPaint = TextPaint()
private fun getStaticLayout(emoji: CharSequence): StaticLayout =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
StaticLayout.Builder.obtain(emoji, 0, emoji.length, textPaint, Int.MAX_VALUE).build()
} else {
@Suppress("DEPRECATION")
StaticLayout(emoji, textPaint, Int.MAX_VALUE, Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true)
}
private fun getProcessedEmoji(emoji: CharSequence): CharSequence =
try {
EmojiCompat.get().process(emoji) ?: emoji
} catch (e: IllegalStateException) {
emoji
}
private fun StaticLayout.getBounds(): RectF =
RectF(getLineLeft(0), 0f, getLineRight(0), getLineDescent(0) - getLineAscent(0).toFloat())
}
}

View File

@@ -2,4 +2,4 @@ package org.thoughtcrime.securesms.components.emoji.parsing
import org.thoughtcrime.securesms.emoji.EmojiPage
data class EmojiDrawInfo(val page: EmojiPage, val index: Int, private val emoji: String, val rawEmoji: String?, val jumboSheet: String?)
data class EmojiDrawInfo(val page: EmojiPage, val index: Int, val emoji: String, val rawEmoji: String?, val jumboSheet: String?)