diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4848fc2ac13..08034a340ff 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -508,6 +508,7 @@ dependencies { implementation(libs.androidx.profileinstaller) implementation(libs.androidx.asynclayoutinflater) implementation(libs.androidx.asynclayoutinflater.appcompat) + implementation(libs.androidx.emoji2) implementation(libs.firebase.messaging) { exclude(group = "com.google.firebase", module = "firebase-core") exclude(group = "com.google.firebase", module = "firebase-analytics") diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java index b617522d407..431dcf226d3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java @@ -23,6 +23,7 @@ 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 void onFailure(ExecutionException exception) { 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); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/SystemEmojiDrawable.kt b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/SystemEmojiDrawable.kt new file mode 100644 index 00000000000..d4a5b431168 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/SystemEmojiDrawable.kt @@ -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()) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiDrawInfo.kt b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiDrawInfo.kt index 28de3aca78b..76978f52128 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiDrawInfo.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiDrawInfo.kt @@ -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?) diff --git a/dependencies.gradle.kts b/dependencies.gradle.kts index 01c8e78492b..25dd18258a7 100644 --- a/dependencies.gradle.kts +++ b/dependencies.gradle.kts @@ -93,6 +93,7 @@ dependencyResolutionManagement { library("androidx-profileinstaller", "androidx.profileinstaller:profileinstaller:1.2.2") library("androidx-asynclayoutinflater", "androidx.asynclayoutinflater:asynclayoutinflater:1.1.0-alpha01") library("androidx-asynclayoutinflater-appcompat", "androidx.asynclayoutinflater:asynclayoutinflater-appcompat:1.1.0-alpha01") + library("androidx-emoji2", "androidx.emoji2:emoji2:1.4.0") // Material library("material-material", "com.google.android.material:material:1.8.0")