From d61100643876235d3bd40923650c19241816fb93 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Mon, 24 May 2021 08:45:41 +0900 Subject: [PATCH] Convert Emoji parsing utils to kotlin. austinhuang0131/barinsta#1311 --- .../instagrabber/activities/MainActivity.java | 2 +- .../DirectReactionViewHolder.java | 2 +- .../customviews/DirectItemContextMenu.java | 2 +- .../emoji/EmojiBottomSheetDialog.java | 4 +- .../emoji/EmojiCategoryPageViewHolder.java | 3 + .../customviews/emoji/EmojiGridAdapter.java | 4 +- .../customviews/emoji/EmojiPicker.java | 4 +- .../emoji/EmojiPickerPageAdapter.java | 5 +- .../customviews/emoji/ReactionsManager.java | 11 +- .../instagrabber/utils/SingletonHolder.kt | 27 ++++ .../utils/emoji/EmojiCategoryDeserializer.kt | 76 ++++----- .../utils/emoji/EmojiDeserializer.kt | 64 ++++---- .../instagrabber/utils/emoji/EmojiParser.kt | 152 ++++++------------ 13 files changed, 160 insertions(+), 196 deletions(-) create mode 100644 app/src/main/java/awais/instagrabber/utils/SingletonHolder.kt diff --git a/app/src/main/java/awais/instagrabber/activities/MainActivity.java b/app/src/main/java/awais/instagrabber/activities/MainActivity.java index 7914e5c4..d959dd54 100644 --- a/app/src/main/java/awais/instagrabber/activities/MainActivity.java +++ b/app/src/main/java/awais/instagrabber/activities/MainActivity.java @@ -184,7 +184,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage getSupportFragmentManager().addOnBackStackChangedListener(this); // Initialise the internal map AppExecutors.INSTANCE.getTasksThread().execute(() -> { - EmojiParser.setup(this); + EmojiParser.Companion.getInstance(this); EmojiVariantManager.getInstance(); }); initEmojiCompat(); diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectReactionViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectReactionViewHolder.java index 497f7a06..84ee907d 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectReactionViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectReactionViewHolder.java @@ -31,7 +31,7 @@ public class DirectReactionViewHolder extends RecyclerView.ViewHolder { this.onReactionClickListener = onReactionClickListener; binding.info.setVisibility(View.GONE); binding.secondaryImage.setVisibility(View.VISIBLE); - emojiParser = EmojiParser.getInstance(); + emojiParser = EmojiParser.Companion.getInstance(itemView.getContext()); } public void bind(final DirectItemEmojiReaction reaction, diff --git a/app/src/main/java/awais/instagrabber/customviews/DirectItemContextMenu.java b/app/src/main/java/awais/instagrabber/customviews/DirectItemContextMenu.java index 65a18141..b13a3a47 100644 --- a/app/src/main/java/awais/instagrabber/customviews/DirectItemContextMenu.java +++ b/app/src/main/java/awais/instagrabber/customviews/DirectItemContextMenu.java @@ -80,7 +80,7 @@ public class DirectItemContextMenu extends PopupWindow { if (!showReactions && (options == null || options.isEmpty())) { throw new IllegalArgumentException("showReactions is set false and options are empty"); } - reactionsManager = ReactionsManager.getInstance(); + reactionsManager = ReactionsManager.getInstance(context); final Resources resources = context.getResources(); emojiSize = resources.getDimensionPixelSize(R.dimen.reaction_picker_emoji_size); emojiMargin = resources.getDimensionPixelSize(R.dimen.reaction_picker_emoji_margin); diff --git a/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiBottomSheetDialog.java b/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiBottomSheetDialog.java index cdf5767c..340afc5d 100644 --- a/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiBottomSheetDialog.java +++ b/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiBottomSheetDialog.java @@ -20,6 +20,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import awais.instagrabber.R; import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration; import awais.instagrabber.utils.Utils; +import awais.instagrabber.utils.emoji.EmojiParser; public class EmojiBottomSheetDialog extends BottomSheetDialogFragment { public static final String TAG = EmojiBottomSheetDialog.class.getSimpleName(); @@ -89,7 +90,8 @@ public class EmojiBottomSheetDialog extends BottomSheetDialogFragment { grid.setHasFixedSize(true); grid.setClipToPadding(false); grid.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(8))); - final EmojiGridAdapter adapter = new EmojiGridAdapter(null, (view, emoji) -> { + final EmojiParser emojiParser = EmojiParser.Companion.getInstance(context); + final EmojiGridAdapter adapter = new EmojiGridAdapter(emojiParser, null, (view, emoji) -> { if (callback != null) { callback.onClick(view, emoji); } diff --git a/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategoryPageViewHolder.java b/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategoryPageViewHolder.java index b875c5a3..8af0cb52 100644 --- a/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategoryPageViewHolder.java +++ b/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategoryPageViewHolder.java @@ -6,12 +6,14 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import awais.instagrabber.customviews.emoji.EmojiPicker.OnEmojiClickListener; +import awais.instagrabber.utils.emoji.EmojiParser; public class EmojiCategoryPageViewHolder extends RecyclerView.ViewHolder { // private static final String TAG = EmojiCategoryPageViewHolder.class.getSimpleName(); private final View rootView; private final OnEmojiClickListener onEmojiClickListener; + private final EmojiParser emojiParser = EmojiParser.Companion.getInstance(itemView.getContext()); public EmojiCategoryPageViewHolder(@NonNull final View rootView, @NonNull final RecyclerView itemView, @@ -24,6 +26,7 @@ public class EmojiCategoryPageViewHolder extends RecyclerView.ViewHolder { public void bind(final EmojiCategory emojiCategory) { final RecyclerView emojiGrid = (RecyclerView) itemView; final EmojiGridAdapter adapter = new EmojiGridAdapter( + emojiParser, emojiCategory.getType(), onEmojiClickListener, (position, view, parent) -> { diff --git a/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiGridAdapter.java b/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiGridAdapter.java index 2dc2274e..02e96364 100644 --- a/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiGridAdapter.java +++ b/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiGridAdapter.java @@ -43,14 +43,14 @@ public class EmojiGridAdapter extends RecyclerView.Adapter(new AdapterListUpdateCallback(this), new AsyncDifferConfig.Builder<>(diffCallback).build()); - final EmojiParser emojiParser = EmojiParser.getInstance(); final Map categoryMap = emojiParser.getCategoryMap(); emojiVariantManager = EmojiVariantManager.getInstance(); appExecutors = AppExecutors.INSTANCE; diff --git a/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPicker.java b/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPicker.java index 1c79e77f..9621c246 100644 --- a/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPicker.java +++ b/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPicker.java @@ -67,7 +67,9 @@ public class EmojiPicker extends LinearLayout { viewPager2.setAdapter(new EmojiPickerPageAdapter(rootView, onEmojiClickListener)); viewPager2.setOffscreenPageLimit(1); - final EmojiParser emojiParser = EmojiParser.getInstance(); + final Context context = getContext(); + if (context == null) return; + final EmojiParser emojiParser = EmojiParser.Companion.getInstance(context); final List categories = emojiParser.getEmojiCategories(); new TabLayoutMediator(tabLayout, viewPager2, (tab, position) -> { diff --git a/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPickerPageAdapter.java b/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPickerPageAdapter.java index d7b7ff03..7fe330b6 100644 --- a/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPickerPageAdapter.java +++ b/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPickerPageAdapter.java @@ -37,13 +37,14 @@ public class EmojiPickerPageAdapter extends RecyclerView.Adapter differ; - public EmojiPickerPageAdapter(final View rootView, + public EmojiPickerPageAdapter(@NonNull final View rootView, final OnEmojiClickListener onEmojiClickListener) { this.rootView = rootView; this.onEmojiClickListener = onEmojiClickListener; differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this), new AsyncDifferConfig.Builder<>(diffCallback).build()); - differ.submitList(EmojiParser.getInstance().getEmojiCategories()); + final EmojiParser emojiParser = EmojiParser.Companion.getInstance(rootView.getContext()); + differ.submitList(emojiParser.getEmojiCategories()); setHasStableIds(true); } diff --git a/app/src/main/java/awais/instagrabber/customviews/emoji/ReactionsManager.java b/app/src/main/java/awais/instagrabber/customviews/emoji/ReactionsManager.java index 7e08f1aa..d75ba8cc 100644 --- a/app/src/main/java/awais/instagrabber/customviews/emoji/ReactionsManager.java +++ b/app/src/main/java/awais/instagrabber/customviews/emoji/ReactionsManager.java @@ -1,7 +1,10 @@ package awais.instagrabber.customviews.emoji; +import android.content.Context; import android.util.Log; +import androidx.annotation.NonNull; + import com.google.common.collect.ImmutableList; import org.json.JSONArray; @@ -26,24 +29,24 @@ public class ReactionsManager { private static ReactionsManager instance; - public static ReactionsManager getInstance() { + public static ReactionsManager getInstance(@NonNull final Context context) { if (instance == null) { synchronized (LOCK) { if (instance == null) { - instance = new ReactionsManager(); + instance = new ReactionsManager(context); } } } return instance; } - private ReactionsManager() { + private ReactionsManager(@NonNull final Context context) { + final EmojiParser emojiParser = EmojiParser.Companion.getInstance(context); String reactionsJson = Utils.settingsHelper.getString(PREF_REACTIONS); if (TextUtils.isEmpty(reactionsJson)) { final ImmutableList list = ImmutableList.of("❤️", "\uD83D\uDE02", "\uD83D\uDE2E", "\uD83D\uDE22", "\uD83D\uDE21", "\uD83D\uDC4D"); reactionsJson = new JSONArray(list).toString(); } - final EmojiParser emojiParser = EmojiParser.getInstance(); final Map allEmojis = emojiParser.getAllEmojis(); try { final JSONArray reactionsJsonArray = new JSONArray(reactionsJson); diff --git a/app/src/main/java/awais/instagrabber/utils/SingletonHolder.kt b/app/src/main/java/awais/instagrabber/utils/SingletonHolder.kt new file mode 100644 index 00000000..e5eaa5f1 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/utils/SingletonHolder.kt @@ -0,0 +1,27 @@ +package awais.instagrabber.utils + +open class SingletonHolder(creator: (A) -> T) { + private var creator: ((A) -> T)? = creator + + @Volatile + private var instance: T? = null + + fun getInstance(arg: A): T { + val i = instance + if (i != null) { + return i + } + + return synchronized(this) { + val i2 = instance + if (i2 != null) { + i2 + } else { + val created = creator!!(arg) + instance = created + creator = null + created + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/emoji/EmojiCategoryDeserializer.kt b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiCategoryDeserializer.kt index 1fc510e2..32c2ba94 100644 --- a/app/src/main/java/awais/instagrabber/utils/emoji/EmojiCategoryDeserializer.kt +++ b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiCategoryDeserializer.kt @@ -1,52 +1,44 @@ -package awais.instagrabber.utils.emoji; +package awais.instagrabber.utils.emoji -import android.util.Log; +import android.util.Log +import awais.instagrabber.customviews.emoji.Emoji +import awais.instagrabber.customviews.emoji.EmojiCategory +import awais.instagrabber.customviews.emoji.EmojiCategoryType +import awais.instagrabber.utils.extensions.TAG +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import com.google.gson.JsonParseException +import java.lang.reflect.Type -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; +class EmojiCategoryDeserializer : JsonDeserializer { -import java.lang.reflect.Type; -import java.util.LinkedHashMap; -import java.util.Map; - -import awais.instagrabber.customviews.emoji.Emoji; -import awais.instagrabber.customviews.emoji.EmojiCategory; -import awais.instagrabber.customviews.emoji.EmojiCategoryType; - -public class EmojiCategoryDeserializer implements JsonDeserializer { - private static final String TAG = EmojiCategoryDeserializer.class.getSimpleName(); - - @Override - public EmojiCategory deserialize(final JsonElement json, - final Type typeOfT, - final JsonDeserializationContext context) throws JsonParseException { - final JsonObject jsonObject = json.getAsJsonObject(); - final JsonElement typeElement = jsonObject.get("type"); - final JsonObject emojisObject = jsonObject.getAsJsonObject("emojis"); + @Throws(JsonParseException::class) + override fun deserialize( + json: JsonElement, + typeOfT: Type, + context: JsonDeserializationContext + ): EmojiCategory { + val jsonObject = json.asJsonObject + val typeElement = jsonObject["type"] + val emojisObject = jsonObject.getAsJsonObject("emojis") if (typeElement == null || emojisObject == null) { - throw new JsonParseException("Invalid json for EmojiCategory"); + throw JsonParseException("Invalid json for EmojiCategory") } - final String typeString = typeElement.getAsString(); - EmojiCategoryType type; - try { - type = EmojiCategoryType.valueOf(typeString); - } catch (IllegalArgumentException e) { - Log.e(TAG, "deserialize: ", e); - type = EmojiCategoryType.OTHERS; + val typeString = typeElement.asString + val type: EmojiCategoryType = try { + EmojiCategoryType.valueOf(typeString) + } catch (e: IllegalArgumentException) { + Log.e(TAG, "deserialize: ", e) + EmojiCategoryType.OTHERS } - final Map emojis = new LinkedHashMap<>(); - for (final Map.Entry emojiObjectEntry : emojisObject.entrySet()) { - final String unicode = emojiObjectEntry.getKey(); - final JsonElement value = emojiObjectEntry.getValue(); + val emojis: MutableMap = linkedMapOf() + for ((unicode, value) in emojisObject.entrySet()) { if (unicode == null || value == null) { - throw new JsonParseException("Invalid json for EmojiCategory"); + throw JsonParseException("Invalid json for EmojiCategory") } - final Emoji emoji = context.deserialize(value, Emoji.class); - emojis.put(unicode, emoji); + emojis[unicode] = context.deserialize(value, Emoji::class.java) } - return new EmojiCategory(type, emojis); + return EmojiCategory(type, emojis) } -} +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/emoji/EmojiDeserializer.kt b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiDeserializer.kt index cc774d60..3674b100 100644 --- a/app/src/main/java/awais/instagrabber/utils/emoji/EmojiDeserializer.kt +++ b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiDeserializer.kt @@ -1,44 +1,40 @@ -package awais.instagrabber.utils.emoji; +package awais.instagrabber.utils.emoji -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; +import awais.instagrabber.customviews.emoji.Emoji +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import com.google.gson.JsonParseException +import java.lang.reflect.Type -import java.lang.reflect.Type; -import java.util.LinkedList; -import java.util.List; - -import awais.instagrabber.customviews.emoji.Emoji; - -public class EmojiDeserializer implements JsonDeserializer { - @Override - public Emoji deserialize(final JsonElement json, - final Type typeOfT, - final JsonDeserializationContext context) throws JsonParseException { - final JsonObject jsonObject = json.getAsJsonObject(); - final JsonElement unicodeElement = jsonObject.get("unicode"); - final JsonElement nameElement = jsonObject.get("name"); +class EmojiDeserializer : JsonDeserializer { + @Throws(JsonParseException::class) + override fun deserialize( + json: JsonElement, + typeOfT: Type, + context: JsonDeserializationContext + ): Emoji { + val jsonObject = json.asJsonObject + val unicodeElement = jsonObject["unicode"] + val nameElement = jsonObject["name"] if (unicodeElement == null || nameElement == null) { - throw new JsonParseException("Invalid json for Emoji class"); + throw JsonParseException("Invalid json for Emoji class") } - final JsonElement variantsElement = jsonObject.get("variants"); - final List variants = new LinkedList<>(); + val variantsElement = jsonObject["variants"] + val variants: MutableList = mutableListOf() if (variantsElement != null) { - final JsonArray variantsArray = variantsElement.getAsJsonArray(); - for (final JsonElement variantElement : variantsArray) { - final Emoji variant = context.deserialize(variantElement, Emoji.class); + val variantsArray = variantsElement.asJsonArray + for (variantElement in variantsArray) { + val variant = context.deserialize(variantElement, Emoji::class.java) if (variant != null) { - variants.add(variant); + variants.add(variant) } } } - return new Emoji( - unicodeElement.getAsString(), - nameElement.getAsString(), - variants - ); + return Emoji( + unicodeElement.asString, + nameElement.asString, + variants + ) } -} +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.kt b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.kt index 4012688b..ac2f6793 100644 --- a/app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.kt +++ b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.kt @@ -1,115 +1,53 @@ -package awais.instagrabber.utils.emoji; +package awais.instagrabber.utils.emoji -import android.content.Context; -import android.util.Log; +import android.content.Context +import android.util.Log +import awais.instagrabber.R +import awais.instagrabber.customviews.emoji.Emoji +import awais.instagrabber.customviews.emoji.EmojiCategory +import awais.instagrabber.customviews.emoji.EmojiCategoryType +import awais.instagrabber.utils.NetworkUtils +import awais.instagrabber.utils.SingletonHolder +import awais.instagrabber.utils.extensions.TAG +import com.google.gson.FieldNamingPolicy +import com.google.gson.GsonBuilder +import com.google.gson.reflect.TypeToken -import androidx.annotation.NonNull; +class EmojiParser private constructor(context: Context) { + var allEmojis: Map = emptyMap() + var categoryMap: Map = emptyMap() + val emojiCategories: List by lazy { + categoryMap.values.toList() + } -import com.google.common.collect.ImmutableList; -import com.google.gson.FieldNamingPolicy; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; + fun getEmoji(emoji: String): Emoji? { + return allEmojis[emoji] + } -import java.io.InputStream; -import java.lang.reflect.Type; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import awais.instagrabber.R; -import awais.instagrabber.customviews.emoji.Emoji; -import awais.instagrabber.customviews.emoji.EmojiCategory; -import awais.instagrabber.customviews.emoji.EmojiCategoryType; -import awais.instagrabber.utils.NetworkUtils; - -public final class EmojiParser { - private static final String TAG = EmojiParser.class.getSimpleName(); - private static final Object LOCK = new Object(); - - private static EmojiParser instance; - - private Map allEmojis = Collections.emptyMap(); - private Map categoryMap = Collections.emptyMap(); - private ImmutableList categories; - - public static void setup(@NonNull final Context context) { - if (instance == null) { - synchronized (LOCK) { - if (instance == null) { - instance = new EmojiParser(context); - } + init { + try { + context.applicationContext.resources.openRawResource(R.raw.emojis).use { `in` -> + val json = NetworkUtils.readFromInputStream(`in`) + val gson = GsonBuilder().apply { + setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + registerTypeAdapter(EmojiCategory::class.java, EmojiCategoryDeserializer()) + registerTypeAdapter(Emoji::class.java, EmojiDeserializer()) + setLenient() + }.create() + val type = object : TypeToken>() {}.type + categoryMap = gson.fromJson(json, type) + // Log.d(TAG, "EmojiParser: " + categoryMap); + allEmojis = categoryMap + .flatMap { (_, emojiCategory) -> emojiCategory.emojis.values } + .flatMap { listOf(it) + it.variants } + .filterNotNull() + .map { it.unicode to it } + .toMap() } + } catch (e: Exception) { + Log.e(TAG, "EmojiParser: ", e) } } - public static EmojiParser getInstance() { - if (instance == null) { - throw new RuntimeException("Setup not done!"); - } - return instance; - } - - private EmojiParser(final Context context) { - try (final InputStream in = context.getResources().openRawResource(R.raw.emojis)) { - final String json = NetworkUtils.readFromInputStream(in); - final Gson gson = new GsonBuilder() - .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) - .registerTypeAdapter(EmojiCategory.class, new EmojiCategoryDeserializer()) - .registerTypeAdapter(Emoji.class, new EmojiDeserializer()) - .setLenient() - .create(); - final Type type = new TypeToken>() {}.getType(); - categoryMap = gson.fromJson(json, type); - // Log.d(TAG, "EmojiParser: " + categoryMap); - allEmojis = categoryMap.values() - .stream() - .flatMap((Function>) emojiCategory -> { - final Map emojis = emojiCategory.getEmojis(); - return emojis.values().stream(); - }) - .flatMap(emoji -> ImmutableList.builder() - .add(emoji) - .addAll(emoji.getVariants()) - .build() - .stream()) - .collect(Collectors.toMap( - Emoji::getUnicode, - Function.identity(), - (u, v) -> u, - LinkedHashMap::new - )); - } catch (Exception e) { - Log.e(TAG, "EmojiParser: ", e); - } - } - - public Map getCategoryMap() { - return categoryMap; - } - - public List getEmojiCategories() { - if (categories == null) { - final Collection categoryCollection = categoryMap.values(); - categories = ImmutableList.copyOf(categoryCollection); - } - return categories; - } - - public Map getAllEmojis() { - return allEmojis; - } - - public Emoji getEmoji(final String emoji) { - if (emoji == null) { - return null; - } - return allEmojis.get(emoji); - } -} - + companion object : SingletonHolder(::EmojiParser) +} \ No newline at end of file