feat: 从网络获取表情

This commit is contained in:
HuanCheng65 2022-06-23 22:00:29 +08:00
parent b7ea0723f1
commit 4558885a65
No known key found for this signature in database
GPG Key ID: E9031EF91A805148
10 changed files with 289 additions and 13 deletions

View File

@ -101,6 +101,7 @@ class BaseApplication : Application(), IApp {
ThemeUtils.init(ThemeDelegate)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
LitePal.initialize(this)
EmotionManager.init(this)
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
private var clipBoardHash: String? = null
private fun updateClipBoardHashCode() {

View File

@ -37,6 +37,7 @@ import com.huanchengfly.tieba.post.plugins.PluginManager;
import com.huanchengfly.tieba.post.utils.AccountUtil;
import com.huanchengfly.tieba.post.utils.BilibiliUtil;
import com.huanchengfly.tieba.post.utils.DateTimeUtils;
import com.huanchengfly.tieba.post.utils.EmotionManager;
import com.huanchengfly.tieba.post.utils.EmotionUtil;
import com.huanchengfly.tieba.post.utils.ImageUtil;
import com.huanchengfly.tieba.post.utils.NavigationHelper;
@ -129,6 +130,7 @@ public class RecyclerFloorAdapter extends BaseSingleTypeAdapter<SubFloorListBean
switch (contentBean.getType()) {
case "2":
contentBean.setText("#(" + contentBean.getC() + ")");
EmotionManager.INSTANCE.registerEmotion(contentBean.getText(), contentBean.getC());
break;
case "3":
case "20":
@ -364,6 +366,7 @@ public class RecyclerFloorAdapter extends BaseSingleTypeAdapter<SubFloorListBean
break;
case "2":
String emojiText = "#(" + contentBean.getC() + ")";
EmotionManager.INSTANCE.registerEmotion(contentBean.getText(), contentBean.getC());
if (appendTextToLastTextView(views, emojiText)) {
TextView textView = createTextView(TEXT_VIEW_TYPE_CONTENT);
textView.setLayoutParams(getLayoutParams(contentBean));

View File

@ -39,6 +39,7 @@ import com.huanchengfly.tieba.post.ui.theme.utils.ThemeUtils
import com.huanchengfly.tieba.post.utils.*
import com.huanchengfly.tieba.post.utils.BilibiliUtil.replaceVideoNumberSpan
import com.huanchengfly.tieba.post.utils.DateTimeUtils.getRelativeTimeString
import com.huanchengfly.tieba.post.utils.EmotionManager.registerEmotion
import com.huanchengfly.tieba.post.utils.TiebaUtil.reportPost
import com.huanchengfly.tieba.post.widgets.MyLinearLayout
import com.huanchengfly.tieba.post.widgets.VoicePlayerView
@ -198,6 +199,10 @@ class ThreadReplyAdapter(context: Context) :
)
"2" -> {
val emojiText = "#(" + contentBean.c + ")"
registerEmotion(
contentBean.text!!,
contentBean.c!!
)
builder.append(emojiText)
}
"4", "9" -> builder.append(contentBean.text)

View File

@ -0,0 +1,53 @@
package com.huanchengfly.tieba.post.components.spans;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.style.ImageSpan;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class EmotionSpanV2 extends ImageSpan {
public static final String TAG = EmotionSpanV2.class.getSimpleName();
private final int size;
public EmotionSpanV2(Drawable drawable, int size) {
super(drawable, ALIGN_BASELINE);
this.size = size;
}
@Override
public Drawable getDrawable() {
Drawable drawable = super.getDrawable();
drawable.setBounds(0, 0, size, size);
return drawable;
}
@Override
public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) {
if (fm != null) {
Paint.FontMetricsInt fontFm = paint.getFontMetricsInt();
fm.ascent = fontFm.top;
fm.descent = fontFm.bottom;
fm.top = fm.ascent;
fm.bottom = fm.descent;
}
return size;
}
@Override
public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end,
float x, int top, int y, int bottom, @NonNull Paint paint) {
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
Drawable drawable = getDrawable();
int transY = y - drawable.getBounds().bottom + fm.descent;
canvas.save();
canvas.translate(x, transY);
drawable.draw(canvas);
canvas.restore();
}
}

View File

@ -0,0 +1,6 @@
package com.huanchengfly.tieba.post.models
data class EmotionCache(
var ids: List<String> = emptyList(),
var mapping: Map<String, String> = emptyMap()
)

View File

@ -0,0 +1,203 @@
package com.huanchengfly.tieba.post.utils
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import com.bumptech.glide.Glide
import com.huanchengfly.tieba.post.BaseApplication
import com.huanchengfly.tieba.post.models.EmotionCache
import com.huanchengfly.tieba.post.toJson
import java.io.File
import java.lang.ref.WeakReference
import java.util.concurrent.Executors
object EmotionManager {
private val DEFAULT_EMOTION_MAPPING: Map<String, String> = mapOf(
"呵呵" to "image_emoticon1",
"哈哈" to "image_emoticon2",
"吐舌" to "image_emoticon3",
"" to "image_emoticon4",
"" to "image_emoticon5",
"" to "image_emoticon6",
"开心" to "image_emoticon7",
"" to "image_emoticon8",
"" to "image_emoticon9",
"黑线" to "image_emoticon10",
"鄙视" to "image_emoticon11",
"不高兴" to "image_emoticon12",
"真棒" to "image_emoticon13",
"" to "image_emoticon14",
"疑问" to "image_emoticon15",
"阴险" to "image_emoticon16",
"" to "image_emoticon17",
"" to "image_emoticon18",
"委屈" to "image_emoticon19",
"花心" to "image_emoticon20",
"呼~" to "image_emoticon21",
"笑眼" to "image_emoticon22",
"" to "image_emoticon23",
"太开心" to "image_emoticon24",
"滑稽" to "image_emoticon25",
"勉强" to "image_emoticon26",
"狂汗" to "image_emoticon27",
"" to "image_emoticon28",
"睡觉" to "image_emoticon29",
"惊哭" to "image_emoticon30",
"生气" to "image_emoticon31",
"惊讶" to "image_emoticon32",
"" to "image_emoticon33",
"爱心" to "image_emoticon34",
"心碎" to "image_emoticon35",
"玫瑰" to "image_emoticon36",
"礼物" to "image_emoticon37",
"彩虹" to "image_emoticon38",
"星星月亮" to "image_emoticon39",
"太阳" to "image_emoticon40",
"钱币" to "image_emoticon41",
"灯泡" to "image_emoticon42",
"茶杯" to "image_emoticon43",
"蛋糕" to "image_emoticon44",
"音乐" to "image_emoticon45",
"haha" to "image_emoticon46",
"胜利" to "image_emoticon47",
"大拇指" to "image_emoticon48",
"" to "image_emoticon49",
"OK" to "image_emoticon50",
"生气" to "image_emoticon61",
"沙发" to "image_emoticon77",
"手纸" to "image_emoticon78",
"香蕉" to "image_emoticon79",
"便便" to "image_emoticon80",
"药丸" to "image_emoticon81",
"红领巾" to "image_emoticon82",
"蜡烛" to "image_emoticon83",
"三道杠" to "image_emoticon84",
)
private lateinit var contextRef: WeakReference<Context>
private val emotionIds: MutableList<String> = mutableListOf()
private val emotionMapping: MutableMap<String, String> = mutableMapOf()
private val drawableCache: MutableMap<String, Drawable> = mutableMapOf()
private val executor by lazy { Executors.newCachedThreadPool() }
fun init(context: Context) {
contextRef = WeakReference(context)
val emotionCache = getEmotionDataCache()
if (emotionCache.ids.isEmpty()) {
for (i in 1..137) {
emotionIds.add("image_emoticon$i")
}
} else {
emotionIds.addAll(emotionCache.ids)
}
if (emotionCache.mapping.isEmpty()) {
emotionMapping.putAll(DEFAULT_EMOTION_MAPPING)
} else {
emotionMapping.putAll(emotionCache.mapping)
}
updateCache()
executor.submit {
fetchEmotions()
}
}
private fun updateCache() {
val emotionDataCacheFile = File(getEmotionCacheDir(), "emotion_data_cache")
if (!emotionDataCacheFile.exists()) {
emotionDataCacheFile.createNewFile()
}
FileUtil.writeFile(
emotionDataCacheFile,
EmotionCache(emotionIds, emotionMapping).toJson(),
false
)
}
private fun getContext(): Context {
return contextRef.get() ?: BaseApplication.instance
}
fun getEmotionDataCache(): EmotionCache {
val emotionDataCacheFile = File(getEmotionCacheDir(), "emotion_data_cache")
if (emotionDataCacheFile.exists()) {
return GsonUtil.getGson()
.fromJson(emotionDataCacheFile.reader(), EmotionCache::class.java)
}
return EmotionCache()
}
fun getEmotionCacheDir(): File {
return File(getContext().externalCacheDir ?: getContext().cacheDir, "emotion")
}
fun getEmotionFile(id: String): File {
val emotionsCacheDir = getEmotionCacheDir()
if (emotionsCacheDir.exists() && emotionsCacheDir.isFile) {
emotionsCacheDir.delete()
emotionsCacheDir.mkdirs()
} else if (!emotionsCacheDir.exists()) {
emotionsCacheDir.mkdirs()
}
return File(emotionsCacheDir, "$id.png")
}
fun getEmotionIdByName(name: String): String? {
return emotionMapping[name]
}
fun getEmotionDrawable(id: String?): Drawable? {
if (id == null) {
return null
}
if (drawableCache.containsKey(id)) {
return drawableCache[id]
}
val emotionFile = getEmotionFile(id)
if (!emotionFile.exists()) {
return null
}
return BitmapDrawable(
getContext().resources,
emotionFile.inputStream()
).also { drawableCache[id] = it }
}
private fun fetchEmotions() {
emotionIds.forEach {
val emotionFile = getEmotionFile(it)
if (!emotionFile.exists()) {
try {
val emotionBitmap =
Glide.with(getContext())
.asBitmap()
.load("http://static.tieba.baidu.com/tb/editor/images/client/$it.png")
.submit()
.get()
ImageUtil.bitmapToFile(
emotionBitmap,
emotionFile,
Bitmap.CompressFormat.PNG
)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
fun registerEmotion(id: String, name: String) {
val realId = if (id == "image_emoticon") "image_emoticon1" else id
var changed = false
if (!emotionIds.contains(realId)) {
emotionIds.add(realId)
changed = true
}
if (!emotionMapping.containsKey(name)) {
emotionMapping[name] = realId
changed = true
}
if (changed) updateCache()
}
}

View File

@ -24,9 +24,9 @@ public class EmotionUtil {
public static final int EMOTION_EMOJI_WEB_TYPE = 5;
@RegExp
private static final String REGEX_WEB = "\\(#([\u4e00-\u9fa5\\w\u007e])+\\)";
private static final String REGEX_WEB = "\\(#(([\u4e00-\u9fa5\\w\u007e])+)\\)";
@RegExp
private static final String REGEX = "#\\(([一-龥\\w~])+\\)";
private static final String REGEX = "#\\((([一-龥\\w~])+)\\)";
private static final Map<String, Integer> EMPTY_MAP;
private static final Map<String, Integer> EMOTION_ALL_MAP;
private static final Map<String, Integer> EMOTION_CLASSIC_MAP;

View File

@ -136,9 +136,12 @@ public class ImageUtil {
}
public static File bitmapToFile(Bitmap bitmap, File output) {
return bitmapToFile(bitmap, output, Bitmap.CompressFormat.JPEG);
}
public static File bitmapToFile(Bitmap bitmap, File output, Bitmap.CompressFormat format) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int quality = 100;
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
bitmap.compress(format, 100, baos);
try {
FileOutputStream fos = new FileOutputStream(output);
try {

View File

@ -26,6 +26,7 @@ import com.huanchengfly.tieba.post.isTablet
import com.huanchengfly.tieba.post.models.PhotoViewBean
import com.huanchengfly.tieba.post.ui.theme.utils.ThemeUtils
import com.huanchengfly.tieba.post.utils.BilibiliUtil.replaceVideoNumberSpan
import com.huanchengfly.tieba.post.utils.EmotionManager.registerEmotion
import com.huanchengfly.tieba.post.widgets.MyImageView
import com.huanchengfly.tieba.post.widgets.VideoPlayerStandard
import com.huanchengfly.tieba.post.widgets.VoicePlayerView
@ -318,6 +319,10 @@ class PostListAdapterHelper(
}
"2" -> {
val emojiText = "#(" + contentBean.c + ")"
registerEmotion(
contentBean.text!!,
contentBean.c!!
)
if (appendTextToLastTextView(views, emojiText)) {
val textView: TextView = createTextView()
textView.layoutParams =

View File

@ -1,6 +1,7 @@
package com.huanchengfly.tieba.post.utils;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextPaint;
@ -8,7 +9,7 @@ import android.text.TextUtils;
import android.widget.TextView;
import com.huanchengfly.tieba.post.R;
import com.huanchengfly.tieba.post.components.spans.EmotionSpan;
import com.huanchengfly.tieba.post.components.spans.EmotionSpanV2;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -31,16 +32,12 @@ public class StringUtil {
while (matcherEmotion.find()) {
String key = matcherEmotion.group();
int start = matcherEmotion.start();
int imgRes = EmotionUtil.getImgByName(emotion_map_type, key);
if (imgRes != -1) {
String group1 = matcherEmotion.group(1);
Drawable emotionDrawable = EmotionManager.INSTANCE.getEmotionDrawable(EmotionManager.INSTANCE.getEmotionIdByName(group1));
if (emotionDrawable != null) {
TextPaint paint = tv.getPaint();
int size = Math.round(-paint.ascent() + paint.descent());
/*
Bitmap bitmap = BitmapFactory.decodeResource(res, imgRes);
Bitmap scaleBitmap = Bitmap.createScaledBitmap(bitmap, size, size, true);
ImageSpan span = new MyImageSpan(context, scaleBitmap);
*/
EmotionSpan span = new EmotionSpan(tv.getContext(), imgRes, size);
EmotionSpanV2 span = new EmotionSpanV2(emotionDrawable, size);
spannableString.setSpan(span, start, start + key.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}