Browse Source
Convert Emoji parsing utils to kotlin. austinhuang0131/barinsta#1311
renovate/org.robolectric-robolectric-4.x
Convert Emoji parsing utils to kotlin. austinhuang0131/barinsta#1311
renovate/org.robolectric-robolectric-4.x
Ammar Githam
4 years ago
13 changed files with 161 additions and 197 deletions
-
2app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
2app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectReactionViewHolder.java
-
2app/src/main/java/awais/instagrabber/customviews/DirectItemContextMenu.java
-
4app/src/main/java/awais/instagrabber/customviews/emoji/EmojiBottomSheetDialog.java
-
3app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategoryPageViewHolder.java
-
4app/src/main/java/awais/instagrabber/customviews/emoji/EmojiGridAdapter.java
-
4app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPicker.java
-
5app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPickerPageAdapter.java
-
11app/src/main/java/awais/instagrabber/customviews/emoji/ReactionsManager.java
-
27app/src/main/java/awais/instagrabber/utils/SingletonHolder.kt
-
76app/src/main/java/awais/instagrabber/utils/emoji/EmojiCategoryDeserializer.kt
-
64app/src/main/java/awais/instagrabber/utils/emoji/EmojiDeserializer.kt
-
154app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.kt
@ -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 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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) { |
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) { |
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) |
||||
} |
} |
||||
} |
|
||||
|
} |
@ -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) { |
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) { |
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) { |
if (variant != null) { |
||||
variants.add(variant); |
|
||||
|
variants.add(variant) |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
return new Emoji( |
|
||||
unicodeElement.getAsString(), |
|
||||
nameElement.getAsString(), |
|
||||
variants |
|
||||
); |
|
||||
|
return Emoji( |
||||
|
unicodeElement.asString, |
||||
|
nameElement.asString, |
||||
|
variants |
||||
|
) |
||||
} |
} |
||||
} |
|
||||
|
} |
@ -1,115 +1,53 @@ |
|||||
package awais.instagrabber.utils.emoji; |
|
||||
|
|
||||
import android.content.Context; |
|
||||
import android.util.Log; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
|
||||
|
|
||||
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; |
|
||||
|
|
||||
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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
package awais.instagrabber.utils.emoji |
||||
|
|
||||
|
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 |
||||
|
|
||||
|
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() |
||||
} |
} |
||||
|
|
||||
public static EmojiParser getInstance() { |
|
||||
if (instance == null) { |
|
||||
throw new RuntimeException("Setup not done!"); |
|
||||
} |
|
||||
return instance; |
|
||||
|
fun getEmoji(emoji: String): Emoji? { |
||||
|
return allEmojis[emoji] |
||||
} |
} |
||||
|
|
||||
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; |
|
||||
|
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) |
||||
} |
} |
||||
return allEmojis.get(emoji); |
|
||||
} |
} |
||||
} |
|
||||
|
|
||||
|
companion object : SingletonHolder<EmojiParser, Context>(::EmojiParser) |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue