From 9a26537f12a950a4a52fe8784c115f25c4f2fc5e Mon Sep 17 00:00:00 2001 From: HuanChengFly <609486518@qq.com> Date: Thu, 19 Aug 2021 16:59:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9E=9D=E7=BD=91=E6=9F=A5=E9=87=8D(?= =?UTF-8?q?=E6=8F=92=E4=BB=B6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/plugins.json | 8 + .../tieba/post/BaseApplication.kt | 32 +++- .../com/huanchengfly/tieba/post/Extensions.kt | 22 +-- .../tieba/post/activities/ThreadActivity.kt | 1 - .../post/adapters/RecyclerFloorAdapter.java | 4 +- .../post/adapters/ThreadMainPostAdapter.kt | 14 +- .../tieba/post/adapters/ThreadReplyAdapter.kt | 15 +- .../post/components/spans/MyURLSpan.java | 5 - .../tieba/post/plugins/IPlugin.kt | 16 +- .../tieba/post/plugins/PluginManager.kt | 10 +- .../post/plugins/asoulcnki/MyURLSpan.java | 36 ++++ .../post/plugins/asoulcnki/PluginAsoulCnki.kt | 175 ++++++++++++++++++ .../post/plugins/asoulcnki/api/CheckApi.kt | 25 +++ .../asoulcnki/api/CommonHeaderInterceptor.kt | 19 ++ .../post/plugins/asoulcnki/api/ICheckApi.kt | 16 ++ .../plugins/asoulcnki/models/CheckApiBody.kt | 5 + .../plugins/asoulcnki/models/CheckResult.kt | 53 ++++++ .../tieba/post/plugins/interfaces/IApp.kt | 12 ++ 18 files changed, 438 insertions(+), 30 deletions(-) create mode 100644 app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/MyURLSpan.java create mode 100644 app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/PluginAsoulCnki.kt create mode 100644 app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/api/CheckApi.kt create mode 100644 app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/api/CommonHeaderInterceptor.kt create mode 100644 app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/api/ICheckApi.kt create mode 100644 app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/models/CheckApiBody.kt create mode 100644 app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/models/CheckResult.kt diff --git a/app/src/main/assets/plugins.json b/app/src/main/assets/plugins.json index 14448cf7..b3d36ca2 100644 --- a/app/src/main/assets/plugins.json +++ b/app/src/main/assets/plugins.json @@ -7,6 +7,14 @@ "author": "huanchengfly", "version": "1.0", "main_class": "com.huanchengfly.tieba.post.plugins.PluginCommentLookup" + }, + { + "id": "AsoulCnki", + "name": "枝网查重", + "desc": "调用枝网查重 API 查询重复小作文", + "author": "huanchengfly", + "version": "1.0", + "main_class": "com.huanchengfly.tieba.post.plugins.asoulcnki.PluginAsoulCnki" } ] } \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/BaseApplication.kt b/app/src/main/java/com/huanchengfly/tieba/post/BaseApplication.kt index d5b30190..5aeb9bbf 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/BaseApplication.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/BaseApplication.kt @@ -77,8 +77,8 @@ class BaseApplication : Application(), IApp { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) LitePal.initialize(this) FlurryAgent.Builder() - .withCaptureUncaughtExceptions(true) - .build(this, "ZMRX6W76WNF95ZHT857X") + .withCaptureUncaughtExceptions(true) + .build(this, "ZMRX6W76WNF95ZHT857X") registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { private var clipBoardHash: Int = 0 private fun updateClipBoardHashCode() { @@ -517,7 +517,33 @@ class BaseApplication : Application(), IApp { return this } + override fun getCurrentContext(): Context { + return mActivityList.lastOrNull() ?: this + } + override fun launchUrl(url: String) { - launchUrl(mActivityList.lastOrNull() ?: this, url) + launchUrl(getCurrentContext(), url) + } + + override fun showLoadingDialog(): Dialog { + return LoadingDialog(getCurrentContext()).apply { show() } + } + + override fun toastShort(text: String) { + getCurrentContext().toastShort(text) + } + + override fun showAlertDialog(builder: AlertDialog.Builder.() -> Unit): AlertDialog { + val dialog = AlertDialog.Builder(getCurrentContext()) + .apply(builder) + .create() + if (getCurrentContext() !is BaseActivity || (getCurrentContext() as BaseActivity).isActivityRunning) { + dialog.show() + } + return dialog + } + + override fun copyText(text: String) { + TiebaUtil.copyText(getCurrentContext(), text) } } \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/Extensions.kt b/app/src/main/java/com/huanchengfly/tieba/post/Extensions.kt index 6f4e70e7..f51aa405 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/Extensions.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/Extensions.kt @@ -5,16 +5,19 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.content.res.ColorStateList -import android.os.Build import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.annotation.ColorRes +import androidx.appcompat.content.res.AppCompatResources +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import com.huanchengfly.tieba.post.utils.GsonUtil import com.huanchengfly.tieba.post.utils.MD5Util + fun Float.dpToPx(): Int = dpToPxFloat().toInt() @@ -38,26 +41,21 @@ fun Int.pxToDp(): Int = this.toFloat().pxToDp() fun Int.pxToSp(): Int = this.toFloat().pxToSp() -inline fun String.fromJson() = GsonUtil.getGson().fromJson(this, Data::class.java) +inline fun String.fromJson(): Data { + val type = object : TypeToken() {}.type + return GsonUtil.getGson().fromJson(this, type) +} fun Any.toJson(): String = Gson().toJson(this) fun String.toMD5(): String = MD5Util.toMd5(this) fun Context.getColorCompat(@ColorRes id: Int): Int { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - resources.getColor(id, theme) - } else { - resources.getColor(id) - } + return ContextCompat.getColor(this, id) } fun Context.getColorStateListCompat(id: Int): ColorStateList { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - resources.getColorStateList(id, theme) - } else { - resources.getColorStateList(id) - } + return AppCompatResources.getColorStateList(this, id) } inline fun Context.goToActivity() { diff --git a/app/src/main/java/com/huanchengfly/tieba/post/activities/ThreadActivity.kt b/app/src/main/java/com/huanchengfly/tieba/post/activities/ThreadActivity.kt index 636b2ccd..3b62f183 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/activities/ThreadActivity.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/activities/ThreadActivity.kt @@ -508,7 +508,6 @@ class ThreadActivity : BaseActivity(), View.OnClickListener, IThreadMenuFragment totalPage = Integer.valueOf(threadContentBean.page.totalPage!!) refresh(pid) } - }) } diff --git a/app/src/main/java/com/huanchengfly/tieba/post/adapters/RecyclerFloorAdapter.java b/app/src/main/java/com/huanchengfly/tieba/post/adapters/RecyclerFloorAdapter.java index b1bb47e8..18f3e831 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/adapters/RecyclerFloorAdapter.java +++ b/app/src/main/java/com/huanchengfly/tieba/post/adapters/RecyclerFloorAdapter.java @@ -32,6 +32,7 @@ import com.huanchengfly.tieba.post.fragments.ConfirmDialogFragment; import com.huanchengfly.tieba.post.fragments.MenuDialogFragment; import com.huanchengfly.tieba.post.models.PhotoViewBean; import com.huanchengfly.tieba.post.models.ReplyInfoBean; +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; @@ -178,9 +179,10 @@ public class RecyclerFloorAdapter extends CommonBaseAdapter { + PluginManager.INSTANCE.initPluginMenu(menu, PluginManager.MENU_SUB_POST_ITEM); if (TextUtils.equals(AccountUtil.getLoginInfo(mContext).getUid(), postInfo.getAuthor().getId())) { menu.findItem(R.id.menu_delete).setVisible(true); } diff --git a/app/src/main/java/com/huanchengfly/tieba/post/adapters/ThreadMainPostAdapter.kt b/app/src/main/java/com/huanchengfly/tieba/post/adapters/ThreadMainPostAdapter.kt index d915fc1b..0d109fe7 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/adapters/ThreadMainPostAdapter.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/adapters/ThreadMainPostAdapter.kt @@ -20,6 +20,7 @@ import com.huanchengfly.tieba.post.api.models.ThreadContentBean import com.huanchengfly.tieba.post.components.MyViewHolder import com.huanchengfly.tieba.post.fragments.MenuDialogFragment import com.huanchengfly.tieba.post.models.ReplyInfoBean +import com.huanchengfly.tieba.post.plugins.PluginManager import com.huanchengfly.tieba.post.utils.* import com.huanchengfly.tieba.post.utils.NavigationHelper import com.huanchengfly.tieba.post.utils.TiebaUtil.reportPost @@ -120,13 +121,22 @@ class ThreadMainPostAdapter( stringBuilder.append(contentBean.text) } } - Util.showCopyDialog(context as BaseActivity?, stringBuilder.toString(), threadPostBean!!.id) + Util.showCopyDialog( + context as BaseActivity?, + stringBuilder.toString(), + threadPostBean!!.id + ) true } - else -> false + else -> PluginManager.performPluginMenuClick( + PluginManager.MENU_POST_ITEM, + item.itemId, + threadPostBean!! + ) } } .setInitMenuCallback { menu: Menu -> + PluginManager.initPluginMenu(menu, PluginManager.MENU_POST_ITEM) menu.findItem(R.id.menu_delete).isVisible = false } .show((context as BaseActivity).supportFragmentManager, threadPostBean!!.id + "_Menu") diff --git a/app/src/main/java/com/huanchengfly/tieba/post/adapters/ThreadReplyAdapter.kt b/app/src/main/java/com/huanchengfly/tieba/post/adapters/ThreadReplyAdapter.kt index 2120fb55..4a4af36c 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/adapters/ThreadReplyAdapter.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/adapters/ThreadReplyAdapter.kt @@ -34,6 +34,7 @@ import com.huanchengfly.tieba.post.fragments.ConfirmDialogFragment import com.huanchengfly.tieba.post.fragments.FloorFragment.Companion.newInstance import com.huanchengfly.tieba.post.fragments.MenuDialogFragment import com.huanchengfly.tieba.post.models.ReplyInfoBean +import com.huanchengfly.tieba.post.plugins.PluginManager import com.huanchengfly.tieba.post.ui.theme.utils.ThemeUtils import com.huanchengfly.tieba.post.utils.* import com.huanchengfly.tieba.post.utils.BilibiliUtil.replaceVideoNumberSpan @@ -314,9 +315,14 @@ class ThreadReplyAdapter(context: Context) : BaseSingleTypeDelegateAdapter + PluginManager.initPluginMenu(menu, PluginManager.MENU_SUB_POST_ITEM) menu.findItem(R.id.menu_report).isVisible = false if (TextUtils.equals(AccountUtil.getUid(context), subPostListItemBean.authorId)) { menu.findItem(R.id.menu_delete).isVisible = true @@ -384,9 +390,14 @@ class ThreadReplyAdapter(context: Context) : BaseSingleTypeDelegateAdapter + PluginManager.initPluginMenu(menu, PluginManager.MENU_POST_ITEM) if (TextUtils.equals(dataBean!!.user!!.id, postListItemBean.authorId) || TextUtils.equals(dataBean!!.user!!.id, dataBean!!.thread!!.author!!.id)) { menu.findItem(R.id.menu_delete).isVisible = true } diff --git a/app/src/main/java/com/huanchengfly/tieba/post/components/spans/MyURLSpan.java b/app/src/main/java/com/huanchengfly/tieba/post/components/spans/MyURLSpan.java index ac62fc44..c7742a9c 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/components/spans/MyURLSpan.java +++ b/app/src/main/java/com/huanchengfly/tieba/post/components/spans/MyURLSpan.java @@ -3,27 +3,22 @@ package com.huanchengfly.tieba.post.components.spans; import android.content.Context; import android.text.TextPaint; import android.text.style.ClickableSpan; -import android.util.Log; import android.view.View; import androidx.annotation.NonNull; import com.huanchengfly.tieba.post.R; import com.huanchengfly.tieba.post.ui.theme.utils.ThemeUtils; -import com.huanchengfly.tieba.post.utils.NavigationHelper; import com.huanchengfly.tieba.post.utils.UtilsKt; public class MyURLSpan extends ClickableSpan { public String url; private Context context; - private NavigationHelper navigationHelper; public MyURLSpan(Context context, String url) { super(); - Log.i("MyURLSpan", "MyURLSpan: " + url); this.url = url; this.context = context; - this.navigationHelper = NavigationHelper.newInstance(context); } @Override diff --git a/app/src/main/java/com/huanchengfly/tieba/post/plugins/IPlugin.kt b/app/src/main/java/com/huanchengfly/tieba/post/plugins/IPlugin.kt index 5fd02184..cfef71bf 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/plugins/IPlugin.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/plugins/IPlugin.kt @@ -4,19 +4,29 @@ import android.content.Context import com.huanchengfly.tieba.post.plugins.PluginMenuItem.ClickCallback import com.huanchengfly.tieba.post.plugins.interfaces.IApp import com.huanchengfly.tieba.post.plugins.models.PluginManifest +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlin.coroutines.CoroutineContext abstract class IPlugin( val app: IApp, val manifest: PluginManifest -) { +) : CoroutineScope { + val job = Job() + override val coroutineContext: CoroutineContext + get() = Dispatchers.Main + job + val context: Context - get() = app.getAppContext() + get() = app.getCurrentContext() open fun onCreate() {} open fun onEnable() {} - open fun onDisable() {} + open fun onDisable() { + job.cancel() + } open fun onDestroy() {} } diff --git a/app/src/main/java/com/huanchengfly/tieba/post/plugins/PluginManager.kt b/app/src/main/java/com/huanchengfly/tieba/post/plugins/PluginManager.kt index 2efedb3e..83a6e86f 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/plugins/PluginManager.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/plugins/PluginManager.kt @@ -4,6 +4,8 @@ import android.content.Context import android.content.SharedPreferences import android.view.Menu import com.huanchengfly.tieba.post.api.models.ProfileBean +import com.huanchengfly.tieba.post.api.models.SubFloorListBean +import com.huanchengfly.tieba.post.api.models.ThreadContentBean import com.huanchengfly.tieba.post.fromJson import com.huanchengfly.tieba.post.plugins.interfaces.IApp import com.huanchengfly.tieba.post.plugins.models.BuiltInPlugins @@ -15,6 +17,8 @@ import kotlin.reflect.KClass object PluginManager { const val MENU_USER_ACTIVITY = "user_activity" + const val MENU_POST_ITEM = "post_item" + const val MENU_SUB_POST_ITEM = "sub_post_item" const val MENU_NONE = "none" lateinit var appInstance: IApp @@ -37,6 +41,8 @@ object PluginManager { registeredPluginMenuItems.clear() listOf( MENU_USER_ACTIVITY, + MENU_POST_ITEM, + MENU_SUB_POST_ITEM, MENU_NONE ).forEach { registeredPluginMenuItems[it] = mutableMapOf() @@ -56,7 +62,7 @@ object PluginManager { fun initPluginMenu(menu: Menu, menuId: String) { val menuItems = registeredPluginMenuItems[menuId]!! menuItems.forEach { - menu.add(0, it.key, 0, it.value.title) + menu.add(0, it.key, 100, it.value.title) } } @@ -196,6 +202,8 @@ fun getMenuByData(dataClass: KClass<*>): String = getMenuByData(dataClass.java) fun getMenuByData(dataClass: Class<*>): String { return when (dataClass.canonicalName) { ProfileBean::class.java.canonicalName -> PluginManager.MENU_USER_ACTIVITY + ThreadContentBean.PostListItemBean::class.java.canonicalName -> PluginManager.MENU_POST_ITEM + SubFloorListBean.PostInfo::class.java.canonicalName -> PluginManager.MENU_SUB_POST_ITEM else -> "none" } } diff --git a/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/MyURLSpan.java b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/MyURLSpan.java new file mode 100644 index 00000000..3edcb8b0 --- /dev/null +++ b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/MyURLSpan.java @@ -0,0 +1,36 @@ +package com.huanchengfly.tieba.post.plugins.asoulcnki; + +import android.content.Context; +import android.text.TextPaint; +import android.text.style.ClickableSpan; +import android.view.View; + +import androidx.annotation.NonNull; + +import com.huanchengfly.tieba.post.R; +import com.huanchengfly.tieba.post.ui.theme.utils.ThemeUtils; +import com.huanchengfly.tieba.post.utils.UtilsKt; + +public class MyURLSpan extends ClickableSpan { + public String url; + private Context context; + + public MyURLSpan(Context context, String url) { + super(); + this.url = url; + this.context = context; + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setColor(ThemeUtils.getColorByAttr(this.context, R.attr.colorAccent)); + ds.setUnderlineText(false); + } + + @Override + public void onClick(@NonNull View view) { + UtilsKt.launchUrl(context, url); + //context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } +} diff --git a/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/PluginAsoulCnki.kt b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/PluginAsoulCnki.kt new file mode 100644 index 00000000..d1aa0b8f --- /dev/null +++ b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/PluginAsoulCnki.kt @@ -0,0 +1,175 @@ +package com.huanchengfly.tieba.post.plugins.asoulcnki + +import android.graphics.Bitmap +import android.text.SpannableStringBuilder +import android.text.Spanned +import android.view.View +import android.widget.ProgressBar +import android.widget.TextView +import com.huanchengfly.tieba.post.R +import com.huanchengfly.tieba.post.api.models.ThreadContentBean +import com.huanchengfly.tieba.post.components.LinkTouchMovementMethod +import com.huanchengfly.tieba.post.components.spans.MyImageSpan +import com.huanchengfly.tieba.post.plugins.IPlugin +import com.huanchengfly.tieba.post.plugins.asoulcnki.api.CheckApi +import com.huanchengfly.tieba.post.plugins.asoulcnki.models.CheckApiBody +import com.huanchengfly.tieba.post.plugins.interfaces.IApp +import com.huanchengfly.tieba.post.plugins.models.PluginManifest +import com.huanchengfly.tieba.post.plugins.registerMenuItem +import com.huanchengfly.tieba.post.toJson +import com.huanchengfly.tieba.post.ui.theme.utils.ThemeUtils +import com.huanchengfly.tieba.post.utils.DisplayUtil +import com.huanchengfly.tieba.post.utils.Util +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.toRequestBody +import java.text.NumberFormat +import java.text.SimpleDateFormat +import java.util.* + +class PluginAsoulCnki(app: IApp, manifest: PluginManifest) : IPlugin(app, manifest) { + override fun onEnable() { + super.onEnable() + registerMenuItem( + "asoul_cnki_check", + context.getString(R.string.plugin_asoul_cnki_check) + ) { data -> + val dialog = app.showLoadingDialog() + val body = CheckApiBody(getPostTextContent(data)).toJson() + launch(Dispatchers.IO + job) { + val result = + CheckApi.instance.checkAsync(body.toRequestBody("application/json, charset=utf-8".toMediaTypeOrNull())) + .await() + launch(Dispatchers.Main + job) { + dialog.cancel() + if (result.code == 0) { + val numberFormatter = NumberFormat.getNumberInstance().apply { + maximumFractionDigits = 2 + minimumFractionDigits = 2 + } + app.showAlertDialog { + setTitle("查重结果") + val percent = "${numberFormatter.format(result.data.rate * 100.0)}%" + val resultForCopy = context.getString( + R.string.plugin_asoul_cnki_result, + formatDateTime("yyyy-MM-dd HH:mm:ss"), + percent, + if (result.data.related.isNotEmpty()) { + context.getString( + R.string.plugin_asoul_cnki_related, + result.data.related[0].replyUrl, + result.data.related[0].reply.mName, + formatDateTime( + "yyyy-MM-dd HH:mm", + result.data.related[0].reply.ctime * 1000L + ) + ) + } else { + "" + } + ) + val view = View.inflate( + context, + R.layout.plugin_asoul_cnki_dialog_check_result, + null + ) + val percentView = view.findViewById(R.id.check_result_percent) + val progress = + view.findViewById(R.id.check_result_progress) + val relatedView = view.findViewById(R.id.check_result_related) + val relatedTitle = + view.findViewById(R.id.check_result_related_title) + val relatedContent = + view.findViewById(R.id.check_result_related_content) + percentView.text = context.getString( + R.string.plugin_asoul_cnki_check_result_percent, + percent + ) + progress.progress = (result.data.rate * 10000).toInt() + if (result.data.related.isNullOrEmpty()) { + relatedView.visibility = View.GONE + } else { + relatedView.visibility = View.VISIBLE + relatedTitle.text = context.getString( + R.string.plugin_asoul_cnki_check_result_related, + result.data.related.size + ) + } + val relatedContentText = SpannableStringBuilder() + result.data.related.forEach { + relatedContentText.appendLink("${it.reply.mName} 的评论", it.replyUrl) + .append("\n") + } + relatedContent.apply { + text = relatedContentText + movementMethod = LinkTouchMovementMethod.getInstance() + } + setView(view) + setPositiveButton(R.string.btn_copy_check_result) { _, _ -> + app.copyText(resultForCopy) + } + setNegativeButton(R.string.btn_close, null) + } + } else { + app.toastShort("查重失败 ${result.code}") + } + } + } + } + } + + private fun SpannableStringBuilder.appendLink( + text: CharSequence, + url: String + ): SpannableStringBuilder { + val spannableStringBuilder = SpannableStringBuilder() + val size = DisplayUtil.sp2px(context, 14f) + val bitmap = Util.tintBitmap( + Bitmap.createScaledBitmap( + Util.getBitmapFromVectorDrawable( + context, + R.drawable.ic_link + ), + size, + size, + true + ), + ThemeUtils.getColorByAttr(context, R.attr.colorAccent) + ) + spannableStringBuilder.append( + "[链接]", + MyImageSpan(context, bitmap), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + spannableStringBuilder.append(" ") + spannableStringBuilder.append( + text, + MyURLSpan(context, url), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + return append(spannableStringBuilder) + } + + fun getPostTextContent(item: ThreadContentBean.PostListItemBean): String { + val stringBuilder = StringBuilder() + for (contentBean in item.content!!) { + when (contentBean.type) { + "2" -> contentBean.setText("#(" + contentBean.c + ")") + "3", "20" -> contentBean.setText("[图片]\n") + "10" -> contentBean.setText("[语音]\n") + } + if (contentBean.text != null) { + stringBuilder.append(contentBean.text) + } + } + return stringBuilder.toString() + } + + private fun formatDateTime( + pattern: String, + timestamp: Long = System.currentTimeMillis() + ): String { + return SimpleDateFormat(pattern, Locale.getDefault()).format(Date(timestamp)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/api/CheckApi.kt b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/api/CheckApi.kt new file mode 100644 index 00000000..68dadaa1 --- /dev/null +++ b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/api/CheckApi.kt @@ -0,0 +1,25 @@ +package com.huanchengfly.tieba.post.plugins.asoulcnki.api + +import com.huanchengfly.tieba.post.api.retrofit.NullOnEmptyConverterFactory +import com.huanchengfly.tieba.post.api.retrofit.adapter.DeferredCallAdapterFactory +import com.huanchengfly.tieba.post.api.retrofit.converter.gson.GsonConverterFactory +import okhttp3.ConnectionPool +import okhttp3.OkHttpClient +import retrofit2.Retrofit + +object CheckApi { + private val connectionPool = ConnectionPool() + + val instance: ICheckApi by lazy { + Retrofit.Builder() + .baseUrl("https://asoulcnki.asia/") + .addCallAdapterFactory(DeferredCallAdapterFactory()) + .addConverterFactory(NullOnEmptyConverterFactory()) + .addConverterFactory(GsonConverterFactory.create()) + .client(OkHttpClient.Builder().apply { + connectionPool(connectionPool) + }.build()) + .build() + .create(ICheckApi::class.java) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/api/CommonHeaderInterceptor.kt b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/api/CommonHeaderInterceptor.kt new file mode 100644 index 00000000..a21e4c31 --- /dev/null +++ b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/api/CommonHeaderInterceptor.kt @@ -0,0 +1,19 @@ +package com.huanchengfly.tieba.post.plugins.asoulcnki.api + +import com.huanchengfly.tieba.post.api.ParamExpression +import com.huanchengfly.tieba.post.api.forEachNonNull +import okhttp3.Interceptor +import okhttp3.Response + +class CommonHeaderInterceptor(private vararg val additionHeaders: ParamExpression) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + val headers = request.headers + + return chain.proceed(request.newBuilder().apply { + additionHeaders.forEachNonNull { name, value -> + if (headers[name] == null) addHeader(name, value) + } + }.build()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/api/ICheckApi.kt b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/api/ICheckApi.kt new file mode 100644 index 00000000..1e7db310 --- /dev/null +++ b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/api/ICheckApi.kt @@ -0,0 +1,16 @@ +package com.huanchengfly.tieba.post.plugins.asoulcnki.api + +import com.huanchengfly.tieba.post.plugins.asoulcnki.models.CheckResult +import kotlinx.coroutines.Deferred +import okhttp3.RequestBody +import retrofit2.http.Body +import retrofit2.http.Headers +import retrofit2.http.POST + +interface ICheckApi { + @POST("/v1/api/check") + @Headers("content-type: application/json;charset=UTF-8") + fun checkAsync( + @Body requestBody: RequestBody + ): Deferred +} \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/models/CheckApiBody.kt b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/models/CheckApiBody.kt new file mode 100644 index 00000000..4ead4034 --- /dev/null +++ b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/models/CheckApiBody.kt @@ -0,0 +1,5 @@ +package com.huanchengfly.tieba.post.plugins.asoulcnki.models + +data class CheckApiBody( + val text: String +) diff --git a/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/models/CheckResult.kt b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/models/CheckResult.kt new file mode 100644 index 00000000..6ef5fe18 --- /dev/null +++ b/app/src/main/java/com/huanchengfly/tieba/post/plugins/asoulcnki/models/CheckResult.kt @@ -0,0 +1,53 @@ +package com.huanchengfly.tieba.post.plugins.asoulcnki.models + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +@Keep +data class CheckResult( + val code: Int, // 0 + val `data`: Data, + val message: String // success +) { + @Keep + data class Data( + @SerializedName("end_time") + val endTime: Int, // 1629010807 + val rate: Double, // 1.0 + val related: List, + @SerializedName("start_time") + val startTime: Int // 1606137506 + ) { + @Keep + data class Related( + val rate: Double, // 1.0 + val reply: Reply, + @SerializedName("reply_url") + val replyUrl: String // https://www.bilibili.com/video/av377092608/#reply5051494613 + ) { + @Keep + data class Reply( + val content: String, // 曾几何时,我也想像asoul的beeeeeeeela一样做幸福滤镜下的事至少在这层滤镜下,beeeeeeeela的一举一动都是随心所欲且浪漫真实的当我看到beeeeeeeela能像个二次元一样和弹幕大谈特谈50音,当我看到beeeeeeeela能够笑着在夜里唱着不知道练了多少遍的云烟成雨,当我看到她可以在失落后得到安抚和拥抱…以往的笑意消散殆尽,剩下的只有我对beeeeeeeela浪漫的感动和一种无中生有的失意了。我也想像她一样。但这是虚假的,每次在烂醉酩酊起来后依然会痛苦,每次在浪费时间的时候都能意识到,你不能感受到我感受到的东西。但就算是这样,没了你我可能就会完蛋了吧。因为我们需要一个梦。 + val ctime: Int, // 1627881576 + @SerializedName("dynamic_id") + val dynamicId: String, // 553473662133564230 + @SerializedName("like_num") + val likeNum: Int, // 9 + @SerializedName("m_name") + val mName: String, // 走出童年 + val mid: Int, // 671239951 + val oid: String, // 377092608 + @SerializedName("origin_rpid") + val originRpid: String, // -1 + val rpid: String, // 5051494613 + @SerializedName("similar_count") + val similarCount: Int, // 1 + @SerializedName("similar_like_sum") + val similarLikeSum: Int, // 424 + @SerializedName("type_id") + val typeId: Int, // 1 + val uid: Int // 672346917 + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/plugins/interfaces/IApp.kt b/app/src/main/java/com/huanchengfly/tieba/post/plugins/interfaces/IApp.kt index 8c67bfeb..692e0aa4 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/plugins/interfaces/IApp.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/plugins/interfaces/IApp.kt @@ -1,9 +1,21 @@ package com.huanchengfly.tieba.post.plugins.interfaces +import android.app.Dialog import android.content.Context +import androidx.appcompat.app.AlertDialog interface IApp { fun getAppContext(): Context + fun getCurrentContext(): Context + fun launchUrl(url: String) + + fun showLoadingDialog(): Dialog + + fun toastShort(text: String) + + fun copyText(text: String) + + fun showAlertDialog(builder: AlertDialog.Builder.() -> Unit): AlertDialog } \ No newline at end of file