Skip to content

Commit

Permalink
API: New emoji-androidx-emoji2 module which uses androidx.emoji2:emoj…
Browse files Browse the repository at this point in the history
…i2 (#1086)
  • Loading branch information
vanniktech authored May 17, 2024
1 parent 36c98f6 commit ec82b5d
Show file tree
Hide file tree
Showing 60 changed files with 6,409 additions and 31 deletions.
7 changes: 4 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,13 @@ android {
}

dependencies {
implementation(project(":emoji-ios"))
implementation(project(":emoji-androidx-emoji2"))
implementation(project(":emoji-facebook"))
implementation(project(":emoji-google"))
implementation(project(":emoji-google-compat"))
implementation(project(":emoji-twitter"))
implementation(project(":emoji-facebook"))
implementation(project(":emoji-ios"))
implementation(project(":emoji-material"))
implementation(project(":emoji-twitter"))
implementation(libs.timber)
}

Expand Down
28 changes: 23 additions & 5 deletions app/src/main/kotlin/com/vanniktech/emoji/sample/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ import android.widget.PopupMenu
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.provider.FontRequest
import androidx.emoji.text.EmojiCompat
import androidx.emoji.text.FontRequestEmojiCompatConfig
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.vanniktech.emoji.EmojiManager
import com.vanniktech.emoji.EmojiPopup
import com.vanniktech.emoji.androidxemoji2.AndroidxEmoji2Provider
import com.vanniktech.emoji.facebook.FacebookEmojiProvider
import com.vanniktech.emoji.google.GoogleEmojiProvider
import com.vanniktech.emoji.googlecompat.GoogleCompatEmojiProvider
Expand All @@ -46,14 +46,17 @@ import com.vanniktech.emoji.sample.databinding.ActivityMainBinding
import com.vanniktech.emoji.traits.EmojiTrait
import com.vanniktech.emoji.twitter.TwitterEmojiProvider
import timber.log.Timber
import androidx.emoji.text.EmojiCompat as EmojiCompat1
import androidx.emoji2.text.EmojiCompat as EmojiCompat2
import com.vanniktech.emoji.R as EmojiR

// We don't care about duplicated code in the sample.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var chatAdapter: ChatAdapter
private lateinit var emojiPopup: EmojiPopup
private var emojiCompat: EmojiCompat? = null
private var emojiCompat1: EmojiCompat1? = null
private var emojiCompat2: EmojiCompat2? = null
private var searchInPlaceEmojiTrait: EmojiTrait? = null
private var disableKeyboardInputEmojiTrait: EmojiTrait? = null
private var forceSingleEmojiTrait: EmojiTrait? = null
Expand Down Expand Up @@ -188,8 +191,8 @@ class MainActivity : AppCompatActivity() {
return@setOnMenuItemClickListener true
}
R.id.menuEmojiProviderGoogleCompat -> {
if (emojiCompat == null) {
emojiCompat = EmojiCompat.init(
if (emojiCompat1 == null) {
emojiCompat1 = EmojiCompat1.init(
FontRequestEmojiCompatConfig(
this,
FontRequest(
Expand All @@ -201,8 +204,23 @@ class MainActivity : AppCompatActivity() {
).setReplaceAll(true),
)
}

emojiCompat2 = null

EmojiManager.destroy()
EmojiManager.install(GoogleCompatEmojiProvider(emojiCompat1!!))
recreate()
return@setOnMenuItemClickListener true
}
R.id.menuEmojiProviderAndroidxEmoji2 -> {
if (emojiCompat2 == null) {
emojiCompat2 = EmojiCompat2.init(this)
}

emojiCompat1 = null

EmojiManager.destroy()
EmojiManager.install(GoogleCompatEmojiProvider(emojiCompat!!))
EmojiManager.install(AndroidxEmoji2Provider(emojiCompat2!!))
recreate()
return@setOnMenuItemClickListener true
}
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/menu/menu_emoji_provider.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
<item
android:id="@+id/menuEmojiProviderGoogleCompat"
android:title="Google Compat" />
<item
android:id="@+id/menuEmojiProviderAndroidxEmoji2"
android:title="AndroidX Emoji 2" />
<item
android:id="@+id/menuEmojiProviderTwitter"
android:title="Twitter" />
Expand Down
22 changes: 22 additions & 0 deletions emoji-androidx-emoji2/api/current.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Signature format: 4.0
package com.vanniktech.emoji.androidxemoji2 {

public final class AndroidxEmoji2Provider implements com.vanniktech.emoji.EmojiAndroidProvider com.vanniktech.emoji.EmojiProvider com.vanniktech.emoji.EmojiReplacer {
ctor public AndroidxEmoji2Provider(androidx.emoji2.text.EmojiCompat emojiCompat);
method public com.vanniktech.emoji.EmojiCategory![] getCategories();
method public android.graphics.drawable.Drawable getDrawable(com.vanniktech.emoji.Emoji emoji, android.content.Context context);
method public int getIcon(com.vanniktech.emoji.EmojiCategory emojiCategory);
method public void release();
method public void replaceWithImages(android.content.Context context, android.text.Spannable text, float emojiSize, com.vanniktech.emoji.EmojiReplacer? fallback);
property public com.vanniktech.emoji.EmojiCategory![] categories;
}

public final class AndroidxEmoji2Provider implements com.vanniktech.emoji.EmojiProvider {
ctor public AndroidxEmoji2Provider();
method public com.vanniktech.emoji.EmojiCategory![] getCategories();
method public void release();
property public com.vanniktech.emoji.EmojiCategory![] categories;
}

}

84 changes: 84 additions & 0 deletions emoji-androidx-emoji2/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
plugins {
id("org.jetbrains.dokka")
id("org.jetbrains.kotlin.multiplatform")
id("com.android.library")
id("org.jetbrains.kotlin.plugin.parcelize")
id("me.tylerbwong.gradle.metalava")
id("com.vanniktech.maven.publish")
id("app.cash.licensee")
}

licensee {
allow("Apache-2.0")
}

metalava {
filename.set("api/current.txt")
sourcePaths.setFrom("src/commonMain", "src/androidMain", "src/jvmMain")
}

kotlin {
applyDefaultHierarchyTemplate()

androidTarget {
publishLibraryVariants("release")
}
// ios("ios")
jvm()
jvmToolchain(11)

sourceSets {
val commonMain by getting {
dependencies {
api(project(":emoji"))
}
}

val commonTest by getting {
dependencies {
implementation(libs.kotlin.test.common)
implementation(libs.kotlin.test.annotations.common)
}
}

val androidMain by getting {
dependencies {
api(libs.androidx.emoji2)
}
}

val androidUnitTest by getting {
dependencies {
implementation(libs.kotlin.test.junit)
implementation(libs.robolectric)
}
}

val jvmTest by getting {
dependencies {
implementation(libs.kotlin.test.junit)
}
}
}
}

android {
namespace = "com.vanniktech.emoji.androidxemoji2"

compileSdk = libs.versions.compileSdk.get().toInt()

compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()
}

resourcePrefix = "emoji_androidxemoji2_"
}

dependencies {
api(project(":emoji"))
}
3 changes: 3 additions & 0 deletions emoji-androidx-emoji2/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POM_NAME=Emoji
POM_ARTIFACT_ID=emoji-androidx-emoji2
POM_PACKAGING=aar
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2016 - Niklas Baudy, Ruben Gees, Mario Đanić and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.vanniktech.emoji.androidxemoji2

import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.Paint
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import android.text.Spanned
import android.text.TextPaint
import androidx.emoji2.text.EmojiCompat
import androidx.emoji2.text.EmojiSpan
import kotlin.math.roundToInt

/**
* An emoji drawable backed by a span generated by the Google emoji support library.
*/
internal class AndroidxEmoji2Drawable(
unicode: String,
) : Drawable() {
private var emojiSpan: EmojiSpan? = null
private var processed = false
private var emojiCharSequence: CharSequence? = unicode
private val textPaint = TextPaint().apply {
style = Paint.Style.FILL
color = -0x1
isAntiAlias = true
}

private fun process() {
val sequence = EmojiCompat.get().process(emojiCharSequence)
emojiCharSequence = sequence

if (sequence is Spanned) {
val spans = sequence.getSpans(0, sequence.length, EmojiSpan::class.java)

if (spans.isNotEmpty()) {
emojiSpan = spans[0] as EmojiSpan
}
}
}

override fun draw(canvas: Canvas) {
val bounds = bounds
textPaint.textSize = bounds.height() * TEXT_SIZE_FACTOR
val y = (bounds.bottom - bounds.height() * BASELINE_OFFSET_FACTOR).roundToInt()

if (!processed && EmojiCompat.get().loadState != EmojiCompat.LOAD_STATE_LOADING) {
processed = true
if (EmojiCompat.get().loadState != EmojiCompat.LOAD_STATE_FAILED) {
process()
}
}

val sequence = emojiCharSequence

if (sequence != null) {
if (emojiSpan == null) {
canvas.drawText(sequence, 0, sequence.length, bounds.left.toFloat(), y.toFloat(), textPaint)
} else {
emojiSpan!!.draw(canvas, sequence, 0, sequence.length, bounds.left.toFloat(), bounds.top, y, bounds.bottom, textPaint)
}
}
}

override fun setAlpha(alpha: Int) {
textPaint.alpha = alpha
}

override fun setColorFilter(colorFilter: ColorFilter?) {
textPaint.colorFilter = colorFilter
}

@Suppress("OVERRIDE_DEPRECATION")
override fun getOpacity(): Int = PixelFormat.UNKNOWN

internal companion object {
private const val TEXT_SIZE_FACTOR = 0.8f
private const val BASELINE_OFFSET_FACTOR = 0.225f
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (C) 2016 - Niklas Baudy, Ruben Gees, Mario Đanić and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.vanniktech.emoji.androidxemoji2

import android.content.Context
import android.graphics.drawable.Drawable
import android.text.Spannable
import androidx.emoji2.text.EmojiCompat
import com.vanniktech.emoji.Emoji
import com.vanniktech.emoji.EmojiAndroidProvider
import com.vanniktech.emoji.EmojiCategory
import com.vanniktech.emoji.EmojiProvider
import com.vanniktech.emoji.EmojiReplacer
import com.vanniktech.emoji.androidxemoji2.category.ActivitiesCategory
import com.vanniktech.emoji.androidxemoji2.category.AnimalsAndNatureCategory
import com.vanniktech.emoji.androidxemoji2.category.FlagsCategory
import com.vanniktech.emoji.androidxemoji2.category.FoodAndDrinkCategory
import com.vanniktech.emoji.androidxemoji2.category.ObjectsCategory
import com.vanniktech.emoji.androidxemoji2.category.SmileysAndPeopleCategory
import com.vanniktech.emoji.androidxemoji2.category.SymbolsCategory
import com.vanniktech.emoji.androidxemoji2.category.TravelAndPlacesCategory

class AndroidxEmoji2Provider(
@Suppress("unused") private val emojiCompat: EmojiCompat,
) : EmojiProvider, EmojiAndroidProvider, EmojiReplacer {
override val categories: Array<EmojiCategory>
get() = arrayOf(
SmileysAndPeopleCategory(),
AnimalsAndNatureCategory(),
FoodAndDrinkCategory(),
ActivitiesCategory(),
TravelAndPlacesCategory(),
ObjectsCategory(),
SymbolsCategory(),
FlagsCategory(),
)

override fun getIcon(emojiCategory: EmojiCategory): Int = when (emojiCategory) {
is SmileysAndPeopleCategory -> R.drawable.emoji_androidxemoji2_category_smileysandpeople
is AnimalsAndNatureCategory -> R.drawable.emoji_androidxemoji2_category_animalsandnature
is FoodAndDrinkCategory -> R.drawable.emoji_androidxemoji2_category_foodanddrink
is ActivitiesCategory -> R.drawable.emoji_androidxemoji2_category_activities
is TravelAndPlacesCategory -> R.drawable.emoji_androidxemoji2_category_travelandplaces
is ObjectsCategory -> R.drawable.emoji_androidxemoji2_category_objects
is SymbolsCategory -> R.drawable.emoji_androidxemoji2_category_symbols
is FlagsCategory -> R.drawable.emoji_androidxemoji2_category_flags
else -> error("Unknown $emojiCategory")
}

override fun replaceWithImages(
context: Context,
text: Spannable,
emojiSize: Float,
fallback: EmojiReplacer?,
) {
val emojiCompat = EmojiCompat.get()
if (emojiCompat.loadState != EmojiCompat.LOAD_STATE_SUCCEEDED || emojiCompat.process(text, 0, text.length) !== text) {
fallback?.replaceWithImages(context, text, emojiSize, null)
}
}

override fun getDrawable(emoji: Emoji, context: Context): Drawable = AndroidxEmoji2Drawable(emoji.unicode)
override fun release() = Unit
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions emoji-androidx-emoji2/src/androidMain/res/values/public.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<public />
</resources>
Loading

0 comments on commit ec82b5d

Please sign in to comment.