feat: 插件菜单入口

This commit is contained in:
HuanChengFly 2021-08-15 18:30:51 +08:00
parent c306846e7e
commit c2af149d1d
7 changed files with 80 additions and 30 deletions

View File

@ -1,9 +1,11 @@
[ {
{ "plugins": [
"id": "CommentLookup", {
"name": "发言查询", "id": "CommentLookup",
"desc": "使用第三方工具箱查询用户过往发言", "name": "发言查询",
"version": "1.0", "desc": "使用第三方工具箱查询用户过往发言",
"mainClass": "com.huanchengfly.tieba.post.plugins.PluginCommentLookup" "version": "1.0",
} "main_class": "com.huanchengfly.tieba.post.plugins.PluginCommentLookup"
] }
]
}

View File

@ -11,7 +11,6 @@ import android.graphics.Color
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils
import android.view.View import android.view.View
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
@ -41,26 +40,25 @@ class BaseApplication : Application(), IApp {
instance = this instance = this
super.onCreate() super.onCreate()
ThemeUtils.init(ThemeDelegate) ThemeUtils.init(ThemeDelegate)
PluginManager.init(this)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
LitePal.initialize(this) LitePal.initialize(this)
FlurryAgent.Builder() FlurryAgent.Builder()
.withCaptureUncaughtExceptions(true) .withCaptureUncaughtExceptions(true)
.build(this, "ZMRX6W76WNF95ZHT857X") .build(this, "ZMRX6W76WNF95ZHT857X")
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
private var clipBoardHash: String? = null private var clipBoardHash: Int = 0
private fun updateClipBoardHashCode() { private fun updateClipBoardHashCode() {
clipBoardHash = getClipBoardHash() clipBoardHash = getClipBoardHash()
} }
private fun getClipBoardHash(): String? { private fun getClipBoardHash(): Int {
val cm = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val cm = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val data = cm.primaryClip val data = cm.primaryClip
if (data != null) { if (data != null) {
val item = data.getItemAt(0) val item = data.getItemAt(0)
return item.toString().toMD5() return item.hashCode()
} }
return null return 0
} }
private val clipBoard: String private val clipBoard: String
@ -117,8 +115,9 @@ class BaseApplication : Application(), IApp {
} }
override fun onActivityResumed(activity: Activity) { override fun onActivityResumed(activity: Activity) {
if (!TextUtils.equals(clipBoardHash, getClipBoardHash())) { if (clipBoardHash != getClipBoardHash()) {
@RegExp val regex = "((http|https)://)(([a-zA-Z0-9._-]+\\.[a-zA-Z]{2,6})|([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9&%_./-~-]*)?" @RegExp val regex =
"((http|https)://)(([a-zA-Z0-9._-]+\\.[a-zA-Z]{2,6})|([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9&%_./-~-]*)?"
val pattern = Pattern.compile(regex) val pattern = Pattern.compile(regex)
val matcher = pattern.matcher(clipBoard) val matcher = pattern.matcher(clipBoard)
if (matcher.find()) { if (matcher.find()) {
@ -127,7 +126,8 @@ class BaseApplication : Application(), IApp {
if (isTiebaDomain(uri.host)) { if (isTiebaDomain(uri.host)) {
val previewView = Util.inflate(activity, R.layout.preview_url) val previewView = Util.inflate(activity, R.layout.preview_url)
if (isForumUrl(uri)) { if (isForumUrl(uri)) {
updatePreviewView(activity, previewView, PreviewInfo() updatePreviewView(
activity, previewView, PreviewInfo()
.setIconRes(R.drawable.ic_round_forum) .setIconRes(R.drawable.ic_round_forum)
.setTitle(activity.getString(R.string.title_forum, getForumName(uri))) .setTitle(activity.getString(R.string.title_forum, getForumName(uri)))
.setSubtitle(activity.getString(R.string.text_loading)) .setSubtitle(activity.getString(R.string.text_loading))
@ -174,6 +174,7 @@ class BaseApplication : Application(), IApp {
override fun onActivityDestroyed(activity: Activity) {} override fun onActivityDestroyed(activity: Activity) {}
}) })
CrashUtil.CrashHandler.getInstance().init(this) CrashUtil.CrashHandler.getInstance().init(this)
PluginManager.init(this)
} }
/** /**

View File

@ -30,6 +30,7 @@ import com.huanchengfly.tieba.post.fragments.UserPostFragment
import com.huanchengfly.tieba.post.goToActivity import com.huanchengfly.tieba.post.goToActivity
import com.huanchengfly.tieba.post.models.PhotoViewBean import com.huanchengfly.tieba.post.models.PhotoViewBean
import com.huanchengfly.tieba.post.models.database.Block import com.huanchengfly.tieba.post.models.database.Block
import com.huanchengfly.tieba.post.plugins.PluginManager
import com.huanchengfly.tieba.post.utils.AccountUtil import com.huanchengfly.tieba.post.utils.AccountUtil
import com.huanchengfly.tieba.post.utils.ImageUtil import com.huanchengfly.tieba.post.utils.ImageUtil
import com.huanchengfly.tieba.post.utils.StatusBarUtil import com.huanchengfly.tieba.post.utils.StatusBarUtil
@ -187,6 +188,7 @@ class UserActivity : BaseActivity() {
menu.findItem(R.id.menu_block).isVisible = true menu.findItem(R.id.menu_block).isVisible = true
menu.findItem(R.id.menu_edit_info).isVisible = false menu.findItem(R.id.menu_edit_info).isVisible = false
} }
PluginManager.initPluginMenu(menu, PluginManager.MENU_USER_ACTIVITY)
return super.onCreateOptionsMenu(menu) return super.onCreateOptionsMenu(menu)
} }
@ -205,7 +207,8 @@ class UserActivity : BaseActivity() {
.saveAsync() .saveAsync()
.listen { success: Boolean -> .listen { success: Boolean ->
if (success) { if (success) {
Toast.makeText(this, R.string.toast_add_success, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.toast_add_success, Toast.LENGTH_SHORT)
.show()
} }
} }
return true return true
@ -215,7 +218,17 @@ class UserActivity : BaseActivity() {
return true return true
} }
} }
return super.onOptionsItemSelected(item) return if (PluginManager.performPluginMenuClick(
this,
PluginManager.MENU_USER_ACTIVITY,
item.itemId,
profileBean
)
) {
true
} else {
super.onOptionsItemSelected(item)
}
} }
@OnClick(R.id.user_center_action_btn) @OnClick(R.id.user_center_action_btn)

View File

@ -33,11 +33,11 @@ inline fun <reified Data> IPlugin.registerMenuItem(
inline fun <reified Data> IPlugin.registerMenuItem( inline fun <reified Data> IPlugin.registerMenuItem(
id: String, id: String,
title: String, title: String,
crossinline callback: (Data) -> Unit crossinline callback: (Context, Data) -> Unit
) { ) {
registerMenuItem(id, title, object : ClickCallback<Data> { registerMenuItem(id, title, object : ClickCallback<Data> {
override fun onClick(data: Data) { override fun onClick(context: Context, data: Data) {
callback.invoke(data) callback.invoke(context, data)
} }
}) })
} }

View File

@ -4,6 +4,7 @@ import com.huanchengfly.tieba.post.R
import com.huanchengfly.tieba.post.api.models.ProfileBean import com.huanchengfly.tieba.post.api.models.ProfileBean
import com.huanchengfly.tieba.post.plugins.interfaces.IApp import com.huanchengfly.tieba.post.plugins.interfaces.IApp
import com.huanchengfly.tieba.post.plugins.models.PluginManifest import com.huanchengfly.tieba.post.plugins.models.PluginManifest
import com.huanchengfly.tieba.post.utils.launchUrl
class PluginCommentLookup(app: IApp, manifest: PluginManifest) : IPlugin(app, manifest) { class PluginCommentLookup(app: IApp, manifest: PluginManifest) : IPlugin(app, manifest) {
override fun onEnable() { override fun onEnable() {
@ -11,8 +12,8 @@ class PluginCommentLookup(app: IApp, manifest: PluginManifest) : IPlugin(app, ma
registerMenuItem<ProfileBean>( registerMenuItem<ProfileBean>(
"lookup_comment", "lookup_comment",
context.getString(R.string.plugin_comment_lookup_menu) context.getString(R.string.plugin_comment_lookup_menu)
) { ) { context, data ->
app.launchUrl("https://www.82cat.com/tieba/reply/${it.user?.name}/1") launchUrl(context, "https://www.82cat.com/tieba/reply/${data.user?.name}/1")
} }
} }
} }

View File

@ -2,9 +2,11 @@ package com.huanchengfly.tieba.post.plugins
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.view.Menu
import com.huanchengfly.tieba.post.api.models.ProfileBean import com.huanchengfly.tieba.post.api.models.ProfileBean
import com.huanchengfly.tieba.post.fromJson import com.huanchengfly.tieba.post.fromJson
import com.huanchengfly.tieba.post.plugins.interfaces.IApp import com.huanchengfly.tieba.post.plugins.interfaces.IApp
import com.huanchengfly.tieba.post.plugins.models.BuiltInPlugins
import com.huanchengfly.tieba.post.plugins.models.PluginManifest import com.huanchengfly.tieba.post.plugins.models.PluginManifest
import com.huanchengfly.tieba.post.utils.AssetUtil import com.huanchengfly.tieba.post.utils.AssetUtil
import com.huanchengfly.tieba.post.utils.SharedPreferencesUtil import com.huanchengfly.tieba.post.utils.SharedPreferencesUtil
@ -19,7 +21,7 @@ object PluginManager {
val pluginManifests: MutableList<PluginManifest> = mutableListOf() val pluginManifests: MutableList<PluginManifest> = mutableListOf()
val pluginInstances: MutableList<IPlugin> = mutableListOf() val pluginInstances: MutableList<IPlugin> = mutableListOf()
val registeredPluginMenuItems: MutableMap<String, MutableMap<String, PluginMenuItem<*>>> = val registeredPluginMenuItems: MutableMap<String, MutableMap<Int, PluginMenuItem<*>>> =
mutableMapOf() mutableMapOf()
val context: Context val context: Context
@ -37,7 +39,7 @@ object PluginManager {
} }
fun <Data> registerMenuItem(pluginInstance: IPlugin, menuItem: PluginMenuItem<Data>) { fun <Data> registerMenuItem(pluginInstance: IPlugin, menuItem: PluginMenuItem<Data>) {
registeredPluginMenuItems[menuItem.menuId]!!["${pluginInstance.manifest.id}_${menuItem.id}"] = registeredPluginMenuItems[menuItem.menuId]!!["${pluginInstance.manifest.id}_${menuItem.id}".hashCode()] =
menuItem menuItem
} }
@ -46,6 +48,32 @@ object PluginManager {
reloadPlugins() reloadPlugins()
} }
fun initPluginMenu(menu: Menu, menuId: String) {
val menuItems = registeredPluginMenuItems[menuId]!!
menuItems.forEach {
menu.add(0, it.key, 0, it.value.title)
}
}
fun <Data> performPluginMenuClick(
context: Context,
menuId: String,
itemId: Int,
data: Data
): Boolean {
val menuItems = registeredPluginMenuItems[menuId]!!
val item = menuItems[itemId]
if (item?.callback == null) {
return false
}
return try {
(item as PluginMenuItem<Data>).callback!!.onClick(context, data)
true
} catch (e: Exception) {
false
}
}
fun enablePlugin(id: String) { fun enablePlugin(id: String) {
pluginInstances.filter { it.manifest.id == id }.forEach { enablePlugin(it) } pluginInstances.filter { it.manifest.id == id }.forEach { enablePlugin(it) }
} }
@ -94,7 +122,7 @@ object PluginManager {
fun reloadPluginManifests() { fun reloadPluginManifests() {
pluginManifests.clear() pluginManifests.clear()
pluginManifests.addAll( pluginManifests.addAll(
AssetUtil.getStringFromAsset(context, "plugins.json").fromJson<List<PluginManifest>>() AssetUtil.getStringFromAsset(context, "plugins.json").fromJson<BuiltInPlugins>().plugins
) )
} }
@ -107,7 +135,7 @@ object PluginManager {
reloadPluginManifests() reloadPluginManifests()
pluginManifests.forEach { pluginManifests.forEach {
try { try {
if (it.pluginCreated && preferences.getBoolean("${it.id}_enabled", true)) { if (!it.pluginCreated && preferences.getBoolean("${it.id}_enabled", true)) {
val pluginInstance = createPlugin(it) val pluginInstance = createPlugin(it)
if (pluginInstance != null) { if (pluginInstance != null) {
pluginInstances.add(pluginInstance) pluginInstances.add(pluginInstance)
@ -135,7 +163,7 @@ class PluginMenuItem<Data>(
val callback: ClickCallback<Data>? = null val callback: ClickCallback<Data>? = null
) { ) {
interface ClickCallback<Data> { interface ClickCallback<Data> {
fun onClick(data: Data) fun onClick(context: Context, data: Data)
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {

View File

@ -0,0 +1,5 @@
package com.huanchengfly.tieba.post.plugins.models
data class BuiltInPlugins(
val plugins: List<PluginManifest>
)