Convert Emoji parsing utils to kotlin. austinhuang0131/barinsta#1311

This commit is contained in:
Ammar Githam 2021-05-24 08:45:41 +09:00
parent 2ac05569e1
commit d611006438
13 changed files with 160 additions and 196 deletions

View File

@ -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();

View File

@ -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,

View File

@ -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);

View File

@ -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);
}

View File

@ -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) -> {

View File

@ -43,14 +43,14 @@ public class EmojiGridAdapter extends RecyclerView.Adapter<EmojiGridAdapter.Emoj
private final EmojiVariantManager emojiVariantManager;
private final AppExecutors appExecutors;
public EmojiGridAdapter(final EmojiCategoryType emojiCategoryType,
public EmojiGridAdapter(@NonNull final EmojiParser emojiParser,
final EmojiCategoryType emojiCategoryType,
final OnEmojiClickListener onEmojiClickListener,
final OnEmojiLongClickListener onEmojiLongClickListener) {
this.onEmojiClickListener = onEmojiClickListener;
this.onEmojiLongClickListener = onEmojiLongClickListener;
differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),
new AsyncDifferConfig.Builder<>(diffCallback).build());
final EmojiParser emojiParser = EmojiParser.getInstance();
final Map<EmojiCategoryType, EmojiCategory> categoryMap = emojiParser.getCategoryMap();
emojiVariantManager = EmojiVariantManager.getInstance();
appExecutors = AppExecutors.INSTANCE;

View File

@ -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<EmojiCategory> categories = emojiParser.getEmojiCategories();
new TabLayoutMediator(tabLayout, viewPager2, (tab, position) -> {

View File

@ -37,13 +37,14 @@ public class EmojiPickerPageAdapter extends RecyclerView.Adapter<EmojiCategoryPa
private final OnEmojiClickListener onEmojiClickListener;
private final AsyncListDiffer<EmojiCategory> 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);
}

View File

@ -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<String> 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<String, Emoji> allEmojis = emojiParser.getAllEmojis();
try {
final JSONArray reactionsJsonArray = new JSONArray(reactionsJson);

View File

@ -0,0 +1,27 @@
package awais.instagrabber.utils
open class SingletonHolder<out T : Any, in A>(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
}
}
}
}

View File

@ -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<EmojiCategory> {
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<EmojiCategory> {
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<String, Emoji> emojis = new LinkedHashMap<>();
for (final Map.Entry<String, JsonElement> emojiObjectEntry : emojisObject.entrySet()) {
final String unicode = emojiObjectEntry.getKey();
final JsonElement value = emojiObjectEntry.getValue();
val emojis: MutableMap<String, Emoji> = 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)
}
}
}

View File

@ -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<Emoji> {
@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<Emoji> {
@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<Emoji> variants = new LinkedList<>();
val variantsElement = jsonObject["variants"]
val variants: MutableList<Emoji> = 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<Emoji>(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
)
}
}
}

View File

@ -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<String, Emoji> = emptyMap()
var categoryMap: Map<EmojiCategoryType, EmojiCategory> = emptyMap()
val emojiCategories: List<EmojiCategory> 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<String, Emoji> allEmojis = Collections.emptyMap();
private Map<EmojiCategoryType, EmojiCategory> categoryMap = Collections.emptyMap();
private ImmutableList<EmojiCategory> 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<Map<EmojiCategoryType, EmojiCategory>>() {}.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<Map<EmojiCategoryType, EmojiCategory>>() {}.getType();
categoryMap = gson.fromJson(json, type);
// Log.d(TAG, "EmojiParser: " + categoryMap);
allEmojis = categoryMap.values()
.stream()
.flatMap((Function<EmojiCategory, Stream<Emoji>>) emojiCategory -> {
final Map<String, Emoji> emojis = emojiCategory.getEmojis();
return emojis.values().stream();
})
.flatMap(emoji -> ImmutableList.<Emoji>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<EmojiCategoryType, EmojiCategory> getCategoryMap() {
return categoryMap;
}
public List<EmojiCategory> getEmojiCategories() {
if (categories == null) {
final Collection<EmojiCategory> categoryCollection = categoryMap.values();
categories = ImmutableList.copyOf(categoryCollection);
}
return categories;
}
public Map<String, Emoji> getAllEmojis() {
return allEmojis;
}
public Emoji getEmoji(final String emoji) {
if (emoji == null) {
return null;
}
return allEmojis.get(emoji);
}
}
companion object : SingletonHolder<EmojiParser, Context>(::EmojiParser)
}