feat: 从网络获取表情
This commit is contained in:
parent
b7ea0723f1
commit
4558885a65
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.huanchengfly.tieba.post.models
|
||||
|
||||
data class EmotionCache(
|
||||
var ids: List<String> = emptyList(),
|
||||
var mapping: Map<String, String> = emptyMap()
|
||||
)
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue