From 912a0665a5d522caeb531a61323e80e348408b0c Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Wed, 9 Apr 2025 13:36:35 +0900
Subject: [PATCH] Remove advanced plugin
---
app/src/main/AndroidManifest.xml | 3 -
.../java/io/nekohasekai/sagernet/Constants.kt | 1 -
.../io/nekohasekai/sagernet/bg/VpnService.kt | 5 +-
.../sagernet/bg/proto/BoxInstance.kt | 55 ---
.../sagernet/database/DataStore.kt | 1 -
.../sagernet/database/ProxyEntity.kt | 5 +-
.../sagernet/fmt/v2ray/StandardV2RayBean.java | 36 +-
.../sagernet/fmt/v2ray/VMessBean.java | 7 +-
.../io/nekohasekai/sagernet/ktx/Formats.kt | 22 +-
.../nekohasekai/sagernet/ui/AboutFragment.kt | 32 +-
.../sagernet/ui/AppListActivity.kt | 85 +---
.../sagernet/ui/ConfigurationFragment.kt | 105 ++---
.../sagernet/ui/SettingsPreferenceFragment.kt | 15 -
.../sagernet/utils/PackageCache.kt | 2 +-
.../matsuri/nb4a/plugin/NekoPluginManager.kt | 153 -------
.../java/moe/matsuri/nb4a/plugin/Plugins.kt | 4 +-
.../moe/matsuri/nb4a/proxy/neko/NekoBean.java | 23 +-
.../moe/matsuri/nb4a/proxy/neko/NekoFmt.kt | 123 ------
.../nb4a/proxy/neko/NekoJSInterface.kt | 388 ------------------
.../nb4a/proxy/neko/NekoPreferenceInflater.kt | 97 -----
.../nb4a/proxy/neko/NekoSettingActivity.kt | 102 -----
app/src/main/res/layout/layout_app_list.xml | 55 ---
app/src/main/res/layout/layout_apps_item.xml | 9 -
app/src/main/res/menu/add_profile_menu.xml | 3 -
app/src/main/res/values-es/strings.xml | 4 +-
app/src/main/res/values-fa/strings.xml | 3 -
app/src/main/res/values-ru/strings.xml | 4 -
app/src/main/res/values-zh-rCN/strings.xml | 5 -
app/src/main/res/values/strings.xml | 6 -
app/src/main/res/xml/global_preferences.xml | 4 -
30 files changed, 111 insertions(+), 1246 deletions(-)
delete mode 100644 app/src/main/java/moe/matsuri/nb4a/plugin/NekoPluginManager.kt
delete mode 100644 app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoFmt.kt
delete mode 100644 app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoJSInterface.kt
delete mode 100644 app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoPreferenceInflater.kt
delete mode 100644 app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoSettingActivity.kt
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9e7a740..f969dea 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -191,9 +191,6 @@
-
diff --git a/app/src/main/java/io/nekohasekai/sagernet/Constants.kt b/app/src/main/java/io/nekohasekai/sagernet/Constants.kt
index a2fece3..d44a3b6 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/Constants.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/Constants.kt
@@ -147,7 +147,6 @@ object Key {
//
- const val NEKO_PLUGIN_MANAGED = "nekoPlugins"
const val APP_TLS_VERSION = "appTLSVersion"
const val ENABLE_CLASH_API = "enableClashAPI"
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/VpnService.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/VpnService.kt
index c43b250..751c149 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/bg/VpnService.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/bg/VpnService.kt
@@ -16,9 +16,6 @@ import io.nekohasekai.sagernet.fmt.hysteria.HysteriaBean
import io.nekohasekai.sagernet.ktx.*
import io.nekohasekai.sagernet.ui.VpnRequestActivity
import io.nekohasekai.sagernet.utils.Subnet
-import libcore.*
-import moe.matsuri.nb4a.net.LocalResolverImpl
-import moe.matsuri.nb4a.proxy.neko.needBypassRootUid
import android.net.VpnService as BaseVpnService
class VpnService : BaseVpnService(),
@@ -135,7 +132,7 @@ class VpnService : BaseVpnService(),
var bypass = DataStore.bypass
val workaroundSYSTEM = false /* DataStore.tunImplementation == TunImplementation.SYSTEM */
val needBypassRootUid = workaroundSYSTEM || data.proxy!!.config.trafficMap.values.any {
- it[0].nekoBean?.needBypassRootUid() == true || it[0].hysteriaBean?.protocol == HysteriaBean.PROTOCOL_FAKETCP
+ it[0].hysteriaBean?.protocol == HysteriaBean.PROTOCOL_FAKETCP
}
if (proxyApps || needBypassRootUid) {
diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
index bca90d0..84586bc 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
@@ -21,11 +21,6 @@ import io.nekohasekai.sagernet.plugin.PluginManager
import kotlinx.coroutines.*
import libcore.BoxInstance
import libcore.Libcore
-import moe.matsuri.nb4a.plugin.NekoPluginManager
-import moe.matsuri.nb4a.proxy.neko.NekoBean
-import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
-import moe.matsuri.nb4a.proxy.neko.updateAllConfig
-import org.json.JSONObject
import java.io.File
abstract class BoxInstance(
@@ -53,7 +48,6 @@ abstract class BoxInstance(
}
protected open suspend fun loadConfig() {
- NekoJSInterface.Default.destroyAllJsi()
box = Libcore.newSingBoxInstance(config.config)
}
@@ -88,17 +82,6 @@ abstract class BoxInstance(
}
}
}
-
- is NekoBean -> {
- // check if plugin binary can be loaded
- initPlugin(bean.plgId)
-
- // build config and check if succeed
- bean.updateAllConfig(port)
- if (bean.allConfig == null) {
- throw NekoPluginManager.PluginInternalException(bean.protocolId)
- }
- }
}
}
}
@@ -211,44 +194,6 @@ abstract class BoxInstance(
processes.start(commands)
}
-
- bean is NekoBean -> {
- // config built from JS
- val nekoRunConfigs = bean.allConfig.optJSONArray("nekoRunConfigs")
- val configs = mutableMapOf()
-
- nekoRunConfigs?.forEach { _, any ->
- any as JSONObject
-
- val name = any.getString("name")
- val configFile = File(cacheDir, name)
- configFile.parentFile?.mkdirs()
- val content = any.getString("content")
- configFile.writeText(content)
-
- cacheFiles.add(configFile)
- configs[name] = configFile.absolutePath
-
- Logs.d(name + "\n\n" + content)
- }
-
- val nekoCommands = bean.allConfig.getJSONArray("nekoCommands")
- val commands = mutableListOf()
-
- nekoCommands.forEach { _, any ->
- if (any is String) {
- if (configs.containsKey(any)) {
- commands.add(configs[any]!!)
- } else if (any == "%exe%") {
- commands.add(initPlugin(bean.plgId).path)
- } else {
- commands.add(any)
- }
- }
- }
-
- processes.start(commands)
- }
}
}
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
index 8c95911..df6f06a 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
@@ -81,7 +81,6 @@ object DataStore : OnPreferenceDataStoreChangeListener {
return groups.find { it.type == GroupType.BASIC }!!.id
}
- var nekoPlugins by configurationStore.string(Key.NEKO_PLUGIN_MANAGED)
var appTLSVersion by configurationStore.string(Key.APP_TLS_VERSION)
var enableClashAPI by configurationStore.boolean(Key.ENABLE_CLASH_API)
var showBottomBar by configurationStore.boolean(Key.SHOW_BOTTOM_BAR)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/ProxyEntity.kt b/app/src/main/java/io/nekohasekai/sagernet/database/ProxyEntity.kt
index b53343d..b975695 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/database/ProxyEntity.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/database/ProxyEntity.kt
@@ -239,7 +239,7 @@ data class ProxyEntity(
is SSHBean -> false
is WireGuardBean -> false
is ShadowTLSBean -> false
- is NekoBean -> nekoBean!!.haveStandardLink()
+ is NekoBean -> false
is ConfigBean -> false
else -> true
}
@@ -257,7 +257,7 @@ data class ProxyEntity(
is HysteriaBean -> toUri()
is TuicBean -> toUri()
is AnyTLSBean -> toUri()
- is NekoBean -> shareLink()
+ is NekoBean -> ""
else -> toUniversalLink()
}
}
@@ -470,7 +470,6 @@ data class ProxyEntity(
TYPE_SHADOWTLS -> ShadowTLSSettingsActivity::class.java
TYPE_ANYTLS -> AnyTLSSettingsActivity::class.java
TYPE_CHAIN -> ChainSettingsActivity::class.java
- TYPE_NEKO -> NekoSettingActivity::class.java
TYPE_CONFIG -> ConfigSettingActivity::class.java
else -> throw IllegalArgumentException()
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java b/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
index b3cbcc4..0e76e72 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
@@ -54,11 +54,6 @@ public abstract class StandardV2RayBean extends AbstractBean {
public String echConfig;
- // sing-box 不再使用
- public Boolean enablePqSignature;
-
- public Boolean disabledDRS;
-
// --------------------------------------- Mux
public Boolean enableMux;
@@ -108,8 +103,6 @@ public abstract class StandardV2RayBean extends AbstractBean {
if (enableECH == null) enableECH = false;
if (JavaUtil.isNullOrBlank(echConfig)) echConfig = "";
- if (enablePqSignature == null) enablePqSignature = false;
- if (disabledDRS == null) disabledDRS = false;
if (enableMux == null) enableMux = false;
if (muxPadding == null) muxPadding = false;
@@ -119,7 +112,7 @@ public abstract class StandardV2RayBean extends AbstractBean {
@Override
public void serialize(ByteBufferOutput output) {
- output.writeInt(2);
+ output.writeInt(3);
super.serialize(output);
output.writeString(uuid);
output.writeString(encryption);
@@ -167,11 +160,7 @@ public abstract class StandardV2RayBean extends AbstractBean {
}
output.writeBoolean(enableECH);
- if (enableECH) {
- output.writeBoolean(enablePqSignature);
- output.writeBoolean(disabledDRS);
- output.writeString(echConfig);
- }
+ output.writeString(echConfig);
output.writeInt(packetEncoding);
@@ -229,16 +218,18 @@ public abstract class StandardV2RayBean extends AbstractBean {
realityShortId = input.readString();
}
- if (version >= 1) { // 从老版本升级上来
+ if (version >= 1) {
enableECH = input.readBoolean();
- if (enableECH) {
- enablePqSignature = input.readBoolean();
- disabledDRS = input.readBoolean();
+ if (version >= 3) {
echConfig = input.readString();
+ } else {
+ if (enableECH) {
+ input.readBoolean();
+ input.readBoolean();
+ echConfig = input.readString();
+ }
}
- }
-
- if (version == 0) {
+ } else if (version == 0) {
// 从老版本升级上来但是 version == 0, 可能有 enableECH 也可能没有,需要做判断
int position = input.getByteBuffer().position(); // 当前位置
@@ -250,8 +241,8 @@ public abstract class StandardV2RayBean extends AbstractBean {
if (tmpPacketEncoding != 1 && tmpPacketEncoding != 2) {
enableECH = tmpEnableECH;
if (enableECH) {
- enablePqSignature = input.readBoolean();
- disabledDRS = input.readBoolean();
+ input.readBoolean();
+ input.readBoolean();
echConfig = input.readString();
}
} // 否则后一位就是 packetEncoding
@@ -275,7 +266,6 @@ public abstract class StandardV2RayBean extends AbstractBean {
bean.utlsFingerprint = utlsFingerprint;
bean.packetEncoding = packetEncoding;
bean.enableECH = enableECH;
- bean.disabledDRS = disabledDRS;
bean.echConfig = echConfig;
bean.enableMux = enableMux;
bean.muxPadding = muxPadding;
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/VMessBean.java b/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/VMessBean.java
index 84044da..2b7e8ae 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/VMessBean.java
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/VMessBean.java
@@ -16,7 +16,12 @@ public class VMessBean extends StandardV2RayBean {
super.initializeDefaultValues();
alterId = alterId != null ? alterId : 0;
- encryption = JavaUtil.isNotBlank(encryption) ? encryption : "auto";
+
+ if (alterId == -1) {
+ encryption = JavaUtil.isNotBlank(encryption) ? encryption : "";
+ } else {
+ encryption = JavaUtil.isNotBlank(encryption) ? encryption : "auto";
+ }
}
@NotNull
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ktx/Formats.kt b/app/src/main/java/io/nekohasekai/sagernet/ktx/Formats.kt
index 3fc8573..3774719 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ktx/Formats.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ktx/Formats.kt
@@ -14,10 +14,7 @@ import io.nekohasekai.sagernet.fmt.trojan.parseTrojan
import io.nekohasekai.sagernet.fmt.tuic.parseTuic
import io.nekohasekai.sagernet.fmt.trojan_go.parseTrojanGo
import io.nekohasekai.sagernet.fmt.v2ray.parseV2Ray
-import moe.matsuri.nb4a.plugin.NekoPluginManager
import moe.matsuri.nb4a.proxy.anytls.parseAnytls
-import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
-import moe.matsuri.nb4a.proxy.neko.parseShareLink
import moe.matsuri.nb4a.utils.JavaUtil.gson
import moe.matsuri.nb4a.utils.Util
import org.json.JSONArray
@@ -112,7 +109,7 @@ suspend fun parseProxies(text: String): List {
val entities = ArrayList()
val entitiesByLine = ArrayList()
- suspend fun String.parseLink(entities: ArrayList) {
+ fun String.parseLink(entities: ArrayList) {
if (startsWith("clash://install-config?") || startsWith("sn://subscription?")) {
throw SubscriptionFoundException(this)
}
@@ -211,22 +208,6 @@ suspend fun parseProxies(text: String): List {
}.onFailure {
Logs.w(it)
}
- } else { // Neko Plugins
- NekoPluginManager.getProtocols().forEach { obj ->
- obj.protocolConfig.optJSONArray("links")?.forEach { _, any ->
- if (any is String && startsWith(any)) {
- runCatching {
- entities.add(
- parseShareLink(
- obj.plgId, obj.protocolId, this@parseLink
- )
- )
- }.onFailure {
- Logs.w(it)
- }
- }
- }
- }
}
}
@@ -246,7 +227,6 @@ suspend fun parseProxies(text: String): List {
}
}
}
- NekoJSInterface.Default.destroyAllJsi()
return if (entities.size > entitiesByLine.size) entities else entitiesByLine
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt
index ba70fc3..54ed72a 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt
@@ -75,9 +75,11 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) {
}
return MaterialAboutList.Builder()
- .addCard(MaterialAboutCard.Builder()
+ .addCard(
+ MaterialAboutCard.Builder()
.outline(false)
- .addItem(MaterialAboutActionItem.Builder()
+ .addItem(
+ MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_baseline_update_24)
.text(R.string.app_version)
.subText(versionName)
@@ -87,13 +89,15 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) {
)
}
.build())
- .addItem(MaterialAboutActionItem.Builder()
+ .addItem(
+ MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_baseline_layers_24)
.text(getString(R.string.version_x, "sing-box"))
.subText(Libcore.versionBox())
.setOnClickAction { }
.build())
- .addItem(MaterialAboutActionItem.Builder()
+ .addItem(
+ MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_baseline_card_giftcard_24)
.text(R.string.donate)
.subText(R.string.donate_info)
@@ -107,9 +111,11 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) {
PackageCache.awaitLoadSync()
for ((_, pkg) in PackageCache.installedPluginPackages) {
try {
- val pluginId = pkg.providers?.get(0)?.loadString(Plugins.METADATA_KEY_ID)
- if (pluginId.isNullOrBlank() || pluginId.startsWith(Plugins.AUTHORITIES_PREFIX_NEKO_PLUGIN)) continue
- addItem(MaterialAboutActionItem.Builder()
+ val pluginId =
+ pkg.providers?.get(0)?.loadString(Plugins.METADATA_KEY_ID)
+ if (pluginId.isNullOrBlank()) continue
+ addItem(
+ MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_baseline_nfc_24)
.text(
getString(
@@ -137,7 +143,8 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val pm = app.getSystemService(Context.POWER_SERVICE) as PowerManager
if (!pm.isIgnoringBatteryOptimizations(app.packageName)) {
- addItem(MaterialAboutActionItem.Builder()
+ addItem(
+ MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_baseline_running_with_errors_24)
.text(R.string.ignore_battery_optimizations)
.subText(R.string.ignore_battery_optimizations_sum)
@@ -154,10 +161,12 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) {
}
}
.build())
- .addCard(MaterialAboutCard.Builder()
+ .addCard(
+ MaterialAboutCard.Builder()
.outline(false)
.title(R.string.project)
- .addItem(MaterialAboutActionItem.Builder()
+ .addItem(
+ MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_baseline_sanitizer_24)
.text(R.string.github)
.setOnClickAction {
@@ -167,7 +176,8 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) {
)
}
.build())
- .addItem(MaterialAboutActionItem.Builder()
+ .addItem(
+ MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_qu_shadowsocks_foreground)
.text(R.string.telegram)
.setOnClickAction {
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt
index 6c96bc1..436151a 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt
@@ -15,12 +15,9 @@ import android.view.ViewGroup
import android.widget.Filter
import android.widget.Filterable
import androidx.annotation.UiThread
-import androidx.appcompat.content.res.AppCompatResources
import androidx.core.util.contains
import androidx.core.util.set
import androidx.core.view.ViewCompat
-import androidx.core.view.isGone
-import androidx.core.view.isVisible
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DefaultItemAnimator
@@ -29,27 +26,20 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
import io.nekohasekai.sagernet.BuildConfig
-import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.databinding.LayoutAppListBinding
import io.nekohasekai.sagernet.databinding.LayoutAppsItemBinding
import io.nekohasekai.sagernet.ktx.crossFadeFrom
-import io.nekohasekai.sagernet.ktx.launchCustomTab
import io.nekohasekai.sagernet.ktx.onMainDispatcher
import io.nekohasekai.sagernet.ktx.runOnDefaultDispatcher
-import io.nekohasekai.sagernet.ktx.runOnIoDispatcher
import io.nekohasekai.sagernet.utils.PackageCache
import io.nekohasekai.sagernet.widget.ListListener
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.withContext
-import moe.matsuri.nb4a.plugin.NekoPluginManager
-import moe.matsuri.nb4a.plugin.Plugins
-import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
-import moe.matsuri.nb4a.ui.Dialogs
import kotlin.coroutines.coroutineContext
class AppListActivity : ThemedActivity() {
@@ -81,35 +71,7 @@ class AppListActivity : ThemedActivity() {
item = app
binding.itemicon.setImageDrawable(app.icon)
binding.title.text = app.name
- if (forNeko) {
- val packageName = app.packageName
- val ver = getCachedApps()[packageName]?.versionName ?: ""
- binding.desc.text = "$packageName ($ver)"
- //
- binding.button.isVisible = true
- binding.button.setImageDrawable(
- AppCompatResources.getDrawable(
- this@AppListActivity,
- R.drawable.ic_baseline_info_24
- )
- )
- binding.button.setOnClickListener {
- runOnIoDispatcher {
- val jsi = NekoJSInterface(packageName)
- jsi.init()
- val about = jsi.getAbout()
- jsi.destorySuspend()
- Dialogs.message(
- this@AppListActivity, app.name as String,
- "PackageName: ${packageName}\n" +
- "Version: ${ver}\n" +
- "--------\n" + about
- )
- }
- }
- } else {
- binding.desc.text = "${app.packageName} (${app.uid})"
- }
+ binding.desc.text = "${app.packageName} (${app.uid})"
handlePayload(listOf(SWITCH))
}
@@ -117,7 +79,6 @@ class AppListActivity : ThemedActivity() {
if (payloads.contains(SWITCH)) {
val selected = isProxiedApp(item)
binding.itemcheck.isChecked = selected
- binding.button.isVisible = forNeko && selected
}
}
@@ -125,23 +86,6 @@ class AppListActivity : ThemedActivity() {
if (isProxiedApp(item)) proxiedUids.delete(item.uid) else proxiedUids[item.uid] = true
DataStore.routePackages = apps.filter { isProxiedApp(it) }
.joinToString("\n") { it.packageName }
-
- if (forNeko) {
- if (isProxiedApp(item)) {
- runOnIoDispatcher {
- try {
- NekoPluginManager.installPlugin(item.packageName)
- } catch (e: Exception) {
- // failed UI
- runOnUiThread { onClick(v) }
- Dialogs.logExceptionAndShow(this@AppListActivity, e) { }
- }
- }
- } else {
- NekoPluginManager.removeManagedPlugin(item.packageName)
- }
- }
-
appsAdapter.notifyItemRangeChanged(0, appsAdapter.itemCount, SWITCH)
}
}
@@ -234,11 +178,8 @@ class AppListActivity : ThemedActivity() {
}
}
- private var forNeko = false
-
fun getCachedApps(): MutableMap {
- val packages =
- if (forNeko) PackageCache.installedPluginPackages else PackageCache.installedPackages
+ val packages = PackageCache.installedPackages
return packages.toMutableMap().apply {
remove(BuildConfig.APPLICATION_ID)
}
@@ -246,7 +187,6 @@ class AppListActivity : ThemedActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- forNeko = intent?.hasExtra(Key.NEKO_PLUGIN_MANAGED) == true
binding = LayoutAppListBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -275,28 +215,13 @@ class AppListActivity : ThemedActivity() {
appsAdapter.filter.filter(binding.search.text?.toString() ?: "")
}
- if (forNeko) {
- DataStore.routePackages = DataStore.nekoPlugins
- binding.search.setText(Plugins.AUTHORITIES_PREFIX_NEKO_PLUGIN)
- }
-
- binding.searchLayout.isGone = forNeko
- binding.hintNekoPlugin.isGone = !forNeko
- binding.actionLearnMore.setOnClickListener {
- launchCustomTab("https://matsuridayo.github.io/m-plugin/")
- }
-
loadApps()
}
private var sysApps = false
override fun onCreateOptionsMenu(menu: Menu): Boolean {
- if (forNeko) {
- menuInflater.inflate(R.menu.app_list_neko_menu, menu)
- } else {
- menuInflater.inflate(R.menu.app_list_menu, menu)
- }
+ menuInflater.inflate(R.menu.app_list_menu, menu)
return true
}
@@ -368,9 +293,6 @@ class AppListActivity : ThemedActivity() {
proxiedUids.clear()
DataStore.routePackages = ""
apps = apps.sortedWith(compareBy({ !isProxiedApp(it) }, { it.name.toString() }))
- NekoPluginManager.plugins.forEach {
- NekoPluginManager.removeManagedPlugin(it)
- }
onMainDispatcher {
appsAdapter.notifyItemRangeChanged(0, appsAdapter.itemCount, SWITCH)
}
@@ -394,7 +316,6 @@ class AppListActivity : ThemedActivity() {
override fun onDestroy() {
loader?.cancel()
- if (forNeko) DataStore.nekoPlugins = DataStore.routePackages
super.onDestroy()
}
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt
index f82edad..d915244 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt
@@ -10,12 +10,15 @@ import android.text.SpannableStringBuilder
import android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
import android.text.format.Formatter
import android.text.style.ForegroundColorSpan
-import android.view.*
+import android.view.KeyEvent
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar
@@ -32,43 +35,82 @@ import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
-import io.nekohasekai.sagernet.*
+import io.nekohasekai.sagernet.GroupOrder
+import io.nekohasekai.sagernet.GroupType
+import io.nekohasekai.sagernet.Key
+import io.nekohasekai.sagernet.R
+import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.aidl.TrafficData
import io.nekohasekai.sagernet.bg.BaseService
import io.nekohasekai.sagernet.bg.proto.UrlTest
-import io.nekohasekai.sagernet.database.*
+import io.nekohasekai.sagernet.database.DataStore
+import io.nekohasekai.sagernet.database.GroupManager
+import io.nekohasekai.sagernet.database.ProfileManager
+import io.nekohasekai.sagernet.database.ProxyEntity
+import io.nekohasekai.sagernet.database.ProxyGroup
+import io.nekohasekai.sagernet.database.SagerDatabase
import io.nekohasekai.sagernet.database.preference.OnPreferenceDataStoreChangeListener
-import io.nekohasekai.sagernet.databinding.LayoutAppsItemBinding
import io.nekohasekai.sagernet.databinding.LayoutProfileListBinding
import io.nekohasekai.sagernet.databinding.LayoutProgressListBinding
import io.nekohasekai.sagernet.fmt.AbstractBean
import io.nekohasekai.sagernet.fmt.toUniversalLink
import io.nekohasekai.sagernet.group.GroupUpdater
import io.nekohasekai.sagernet.group.RawUpdater
-import io.nekohasekai.sagernet.ktx.*
+import io.nekohasekai.sagernet.ktx.FixedLinearLayoutManager
+import io.nekohasekai.sagernet.ktx.Logs
+import io.nekohasekai.sagernet.ktx.SubscriptionFoundException
+import io.nekohasekai.sagernet.ktx.alert
+import io.nekohasekai.sagernet.ktx.app
+import io.nekohasekai.sagernet.ktx.dp2px
+import io.nekohasekai.sagernet.ktx.getColorAttr
+import io.nekohasekai.sagernet.ktx.getColour
+import io.nekohasekai.sagernet.ktx.isIpAddress
+import io.nekohasekai.sagernet.ktx.onMainDispatcher
+import io.nekohasekai.sagernet.ktx.readableMessage
+import io.nekohasekai.sagernet.ktx.runOnDefaultDispatcher
+import io.nekohasekai.sagernet.ktx.runOnLifecycleDispatcher
+import io.nekohasekai.sagernet.ktx.runOnMainDispatcher
+import io.nekohasekai.sagernet.ktx.scrollTo
+import io.nekohasekai.sagernet.ktx.showAllowingStateLoss
+import io.nekohasekai.sagernet.ktx.snackbar
+import io.nekohasekai.sagernet.ktx.startFilesForResult
+import io.nekohasekai.sagernet.ktx.tryToShow
import io.nekohasekai.sagernet.plugin.PluginManager
-import io.nekohasekai.sagernet.ui.profile.*
-import io.nekohasekai.sagernet.utils.PackageCache
+import io.nekohasekai.sagernet.ui.profile.ChainSettingsActivity
+import io.nekohasekai.sagernet.ui.profile.HttpSettingsActivity
+import io.nekohasekai.sagernet.ui.profile.HysteriaSettingsActivity
+import io.nekohasekai.sagernet.ui.profile.MieruSettingsActivity
+import io.nekohasekai.sagernet.ui.profile.NaiveSettingsActivity
+import io.nekohasekai.sagernet.ui.profile.SSHSettingsActivity
+import io.nekohasekai.sagernet.ui.profile.ShadowsocksSettingsActivity
+import io.nekohasekai.sagernet.ui.profile.SocksSettingsActivity
+import io.nekohasekai.sagernet.ui.profile.TrojanGoSettingsActivity
+import io.nekohasekai.sagernet.ui.profile.TrojanSettingsActivity
+import io.nekohasekai.sagernet.ui.profile.TuicSettingsActivity
+import io.nekohasekai.sagernet.ui.profile.VMessSettingsActivity
+import io.nekohasekai.sagernet.ui.profile.WireGuardSettingsActivity
import io.nekohasekai.sagernet.widget.QRCodeDialog
import io.nekohasekai.sagernet.widget.UndoSnackbarManager
-import kotlinx.coroutines.*
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.joinAll
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.newFixedThreadPoolContext
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import moe.matsuri.nb4a.Protocols
import moe.matsuri.nb4a.Protocols.getProtocolColor
-import moe.matsuri.nb4a.plugin.NekoPluginManager
import moe.matsuri.nb4a.proxy.anytls.AnyTLSSettingsActivity
import moe.matsuri.nb4a.proxy.config.ConfigSettingActivity
-import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
-import moe.matsuri.nb4a.proxy.neko.NekoSettingActivity
-import moe.matsuri.nb4a.proxy.neko.canShare
import moe.matsuri.nb4a.proxy.shadowtls.ShadowTLSSettingsActivity
import okhttp3.internal.closeQuietly
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.Socket
import java.net.UnknownHostException
-import java.util.*
+import java.util.Collections
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicInteger
import java.util.zip.ZipInputStream
@@ -403,38 +445,6 @@ class ConfigurationFragment @JvmOverloads constructor(
startActivity(Intent(requireActivity(), ChainSettingsActivity::class.java))
}
- R.id.action_new_neko -> {
- val context = requireContext()
- lateinit var dialog: AlertDialog
- val linearLayout = LinearLayout(context).apply {
- orientation = LinearLayout.VERTICAL
-
- NekoPluginManager.getProtocols().forEach { obj ->
- LayoutAppsItemBinding.inflate(layoutInflater, this, true).apply {
- itemcheck.isGone = true
- button.isGone = false
- itemicon.setImageDrawable(
- PackageCache.installedApps[obj.plgId]?.loadIcon(
- context.packageManager
- )
- )
- title.text = obj.protocolId
- desc.text = obj.plgId
- button.setOnClickListener {
- dialog.dismiss()
- val intent = Intent(context, NekoSettingActivity::class.java)
- intent.putExtra("plgId", obj.plgId)
- intent.putExtra("protocolId", obj.protocolId)
- startActivity(intent)
- }
- }
- }
- }
- dialog = MaterialAlertDialogBuilder(context).setTitle(R.string.neko_plugin)
- .setView(linearLayout)
- .show()
- }
-
R.id.action_update_subscription -> {
val group = DataStore.currentGroup()
if (group.type != GroupType.SUBSCRIPTION) {
@@ -870,7 +880,6 @@ class ConfigurationFragment @JvmOverloads constructor(
}
}
GroupManager.postReload(DataStore.currentGroupId())
- NekoJSInterface.Default.destroyAllJsi()
mainJob.cancel()
testJobs.forEach { it.cancel() }
}
@@ -1591,7 +1600,7 @@ class ConfigurationFragment @JvmOverloads constructor(
removeButton.isGone = select
proxyEntity.nekoBean?.apply {
- shareLayout.isGone = !canShare()
+ shareLayout.isGone = true
}
runOnDefaultDispatcher {
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt
index 4de4242..5c91718 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt
@@ -16,13 +16,11 @@ import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers
import io.nekohasekai.sagernet.ktx.*
import io.nekohasekai.sagernet.utils.Theme
-import io.nekohasekai.sagernet.widget.AppListPreference
import moe.matsuri.nb4a.ui.*
class SettingsPreferenceFragment : PreferenceFragmentCompat() {
private lateinit var isProxyApps: SwitchPreference
- private lateinit var nekoPlugins: AppListPreference
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -40,16 +38,6 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
DataStore.initGlobal()
addPreferencesFromResource(R.xml.global_preferences)
- DataStore.routePackages = DataStore.nekoPlugins
- nekoPlugins = findPreference(Key.NEKO_PLUGIN_MANAGED)!!
- nekoPlugins.setOnPreferenceClickListener {
- // borrow from route app settings
- startActivity(Intent(
- context, AppListActivity::class.java
- ).apply { putExtra(Key.NEKO_PLUGIN_MANAGED, true) })
- true
- }
-
val appTheme = findPreference(Key.APP_THEME)!!
appTheme.setOnPreferenceChangeListener { _, newTheme ->
if (DataStore.serviceState.started) {
@@ -183,9 +171,6 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
if (::isProxyApps.isInitialized) {
isProxyApps.isChecked = DataStore.proxyApps
}
- if (::nekoPlugins.isInitialized) {
- nekoPlugins.postUpdate()
- }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/io/nekohasekai/sagernet/utils/PackageCache.kt b/app/src/main/java/io/nekohasekai/sagernet/utils/PackageCache.kt
index 2ffaef8..a2e09ef 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/utils/PackageCache.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/utils/PackageCache.kt
@@ -51,7 +51,7 @@ object PackageCache {
}.associateBy { it.packageName }
installedPluginPackages = rawPackageInfo.filter {
- Plugins.isExeOrPlugin(it)
+ Plugins.isExe(it)
}.associateBy { it.packageName }
val installed = app.packageManager.getInstalledApplications(PackageManager.GET_META_DATA)
diff --git a/app/src/main/java/moe/matsuri/nb4a/plugin/NekoPluginManager.kt b/app/src/main/java/moe/matsuri/nb4a/plugin/NekoPluginManager.kt
deleted file mode 100644
index d7ae50e..0000000
--- a/app/src/main/java/moe/matsuri/nb4a/plugin/NekoPluginManager.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-package moe.matsuri.nb4a.plugin
-
-import io.nekohasekai.sagernet.R
-import io.nekohasekai.sagernet.SagerNet
-import io.nekohasekai.sagernet.bg.BaseService
-import io.nekohasekai.sagernet.database.DataStore
-import io.nekohasekai.sagernet.ktx.forEach
-import io.nekohasekai.sagernet.utils.PackageCache
-import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
-import okhttp3.internal.closeQuietly
-import org.json.JSONObject
-import java.io.File
-import java.util.zip.CRC32
-import java.util.zip.ZipFile
-
-object NekoPluginManager {
- const val managerVersion = 2
-
- val plugins get() = DataStore.nekoPlugins.split("\n").filter { it.isNotBlank() }
-
- // plgID to plgConfig object
- fun getManagedPlugins(): Map {
- val ret = mutableMapOf()
- plugins.forEach {
- tryGetPlgConfig(it)?.apply {
- ret[it] = this
- }
- }
- return ret
- }
-
- class Protocol(
- val protocolId: String, val plgId: String, val protocolConfig: JSONObject
- )
-
- fun getProtocols(): List {
- val ret = mutableListOf()
- getManagedPlugins().forEach { (t, u) ->
- u.optJSONArray("protocols")?.forEach { _, any ->
- if (any is JSONObject) {
- val name = any.optString("protocolId")
- ret.add(Protocol(name, t, any))
- }
- }
- }
- return ret
- }
-
- fun findProtocol(protocolId: String): Protocol? {
- getManagedPlugins().forEach { (t, u) ->
- u.optJSONArray("protocols")?.forEach { _, any ->
- if (any is JSONObject) {
- if (protocolId == any.optString("protocolId")) {
- return Protocol(protocolId, t, any)
- }
- }
- }
- }
- return null
- }
-
- fun removeManagedPlugin(plgId: String) {
- DataStore.configurationStore.remove(plgId)
- val dir = File(SagerNet.application.filesDir.absolutePath + "/plugins/" + plgId)
- if (dir.exists()) {
- dir.deleteRecursively()
- }
- }
-
- fun extractPlugin(plgId: String, install: Boolean) {
- val app = PackageCache.installedApps[plgId] ?: return
- val apk = File(app.publicSourceDir)
- if (!apk.exists()) {
- return
- }
- if (!install && !plugins.contains(plgId)) {
- return
- }
-
- val zipFile = ZipFile(apk)
- val unzipDir = File(SagerNet.application.filesDir.absolutePath + "/plugins/" + plgId)
- unzipDir.mkdirs()
- for (entry in zipFile.entries()) {
- if (entry.name.startsWith("assets/")) {
- val relativePath = entry.name.removePrefix("assets/")
- val outFile = File(unzipDir, relativePath)
- if (entry.isDirectory) {
- outFile.mkdirs()
- continue
- }
-
- if (outFile.isDirectory) {
- outFile.delete()
- } else if (outFile.exists()) {
- val checksum = CRC32()
- checksum.update(outFile.readBytes())
- if (checksum.value == entry.crc) {
- continue
- }
- }
-
- val input = zipFile.getInputStream(entry)
- outFile.outputStream().use {
- input.copyTo(it)
- }
- }
- }
- zipFile.closeQuietly()
- }
-
- suspend fun installPlugin(plgId: String) {
- if (plgId == "moe.matsuri.plugin.singbox" || plgId == "moe.matsuri.plugin.xray") {
- throw Exception("This plugin is deprecated")
- }
- extractPlugin(plgId, true)
- NekoJSInterface.Default.destroyJsi(plgId)
- NekoJSInterface.Default.requireJsi(plgId).init()
- NekoJSInterface.Default.destroyJsi(plgId)
- }
-
- const val PLUGIN_APP_VERSION = "_v_vc"
- const val PLUGIN_APP_VERSION_NAME = "_v_vn"
-
- // Return null if not managed
- fun tryGetPlgConfig(plgId: String): JSONObject? {
- return try {
- JSONObject(DataStore.configurationStore.getString(plgId)!!)
- } catch (e: Exception) {
- null
- }
- }
-
- fun updatePlgConfig(plgId: String, plgConfig: JSONObject) {
- PackageCache.installedPluginPackages[plgId]?.apply {
- // longVersionCode requires API 28
-// plgConfig.put(PLUGIN_APP_VERSION, versionCode)
- plgConfig.put(PLUGIN_APP_VERSION_NAME, versionName)
- }
- DataStore.configurationStore.putString(plgId, plgConfig.toString())
- }
-
- fun htmlPath(plgId: String): String {
- val htmlFile = File(SagerNet.application.filesDir.absolutePath + "/plugins/" + plgId)
- return htmlFile.absolutePath
- }
-
- class PluginInternalException(val protocolId: String) : Exception(),
- BaseService.ExpectedException {
- override fun getLocalizedMessage() =
- SagerNet.application.getString(R.string.neko_plugin_internal_error, protocolId)
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/moe/matsuri/nb4a/plugin/Plugins.kt b/app/src/main/java/moe/matsuri/nb4a/plugin/Plugins.kt
index 43b80bb..a584cad 100644
--- a/app/src/main/java/moe/matsuri/nb4a/plugin/Plugins.kt
+++ b/app/src/main/java/moe/matsuri/nb4a/plugin/Plugins.kt
@@ -14,20 +14,18 @@ import io.nekohasekai.sagernet.utils.PackageCache
object Plugins {
const val AUTHORITIES_PREFIX_SEKAI_EXE = "io.nekohasekai.sagernet.plugin."
const val AUTHORITIES_PREFIX_NEKO_EXE = "moe.matsuri.exe."
- const val AUTHORITIES_PREFIX_NEKO_PLUGIN = "moe.matsuri.plugin."
const val ACTION_NATIVE_PLUGIN = "io.nekohasekai.sagernet.plugin.ACTION_NATIVE_PLUGIN"
const val METADATA_KEY_ID = "io.nekohasekai.sagernet.plugin.id"
const val METADATA_KEY_EXECUTABLE_PATH = "io.nekohasekai.sagernet.plugin.executable_path"
- fun isExeOrPlugin(pkg: PackageInfo): Boolean {
+ fun isExe(pkg: PackageInfo): Boolean {
if (pkg.providers?.isEmpty() == true) return false
val provider = pkg.providers?.get(0) ?: return false
val auth = provider.authority ?: return false
return auth.startsWith(AUTHORITIES_PREFIX_SEKAI_EXE)
|| auth.startsWith(AUTHORITIES_PREFIX_NEKO_EXE)
- || auth.startsWith(AUTHORITIES_PREFIX_NEKO_PLUGIN)
}
fun preferExePrefix(): String {
diff --git a/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoBean.java b/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoBean.java
index 3a86b7f..434d549 100644
--- a/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoBean.java
+++ b/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoBean.java
@@ -8,18 +8,12 @@ import com.esotericsoftware.kryo.io.ByteBufferOutput;
import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;
-import io.nekohasekai.sagernet.R;
-import io.nekohasekai.sagernet.SagerNet;
import io.nekohasekai.sagernet.fmt.AbstractBean;
import io.nekohasekai.sagernet.fmt.KryoConverters;
import io.nekohasekai.sagernet.ktx.Logs;
-import moe.matsuri.nb4a.plugin.NekoPluginManager;
public class NekoBean extends AbstractBean {
- // BoxInstance use this
- public JSONObject allConfig = null;
-
public String plgId;
public String protocolId;
public JSONObject sharedStorage = new JSONObject();
@@ -62,31 +56,22 @@ public class NekoBean extends AbstractBean {
}
public String displayType() {
- NekoPluginManager.Protocol p = NekoPluginManager.INSTANCE.findProtocol(protocolId);
- String neko = SagerNet.application.getResources().getString(R.string.neko_plugin);
- if (p == null) return neko;
- return p.getProtocolId();
+ return "invalid";
}
@Override
public boolean canMapping() {
- NekoPluginManager.Protocol p = NekoPluginManager.INSTANCE.findProtocol(protocolId);
- if (p == null) return false;
- return p.getProtocolConfig().optBoolean("canMapping");
+ return false;
}
@Override
public boolean canICMPing() {
- NekoPluginManager.Protocol p = NekoPluginManager.INSTANCE.findProtocol(protocolId);
- if (p == null) return false;
- return p.getProtocolConfig().optBoolean("canICMPing");
+ return false;
}
@Override
public boolean canTCPing() {
- NekoPluginManager.Protocol p = NekoPluginManager.INSTANCE.findProtocol(protocolId);
- if (p == null) return false;
- return p.getProtocolConfig().optBoolean("canTCPing");
+ return false;
}
@NotNull
diff --git a/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoFmt.kt b/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoFmt.kt
deleted file mode 100644
index eb29e53..0000000
--- a/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoFmt.kt
+++ /dev/null
@@ -1,123 +0,0 @@
-package moe.matsuri.nb4a.proxy.neko
-
-import io.nekohasekai.sagernet.database.DataStore
-import io.nekohasekai.sagernet.ktx.Logs
-import io.nekohasekai.sagernet.ktx.getStr
-import io.nekohasekai.sagernet.ktx.runOnIoDispatcher
-import libcore.Libcore
-import moe.matsuri.nb4a.Protocols
-import moe.matsuri.nb4a.plugin.NekoPluginManager
-import org.json.JSONObject
-import kotlin.coroutines.resume
-import kotlin.coroutines.suspendCoroutine
-
-suspend fun parseShareLink(plgId: String, protocolId: String, link: String): NekoBean =
- suspendCoroutine {
- runOnIoDispatcher {
- val jsi = NekoJSInterface.Default.requireJsi(plgId)
- jsi.lock()
-
- try {
- jsi.init()
-
- val jsip = jsi.switchProtocol(protocolId)
- val sharedStorage = jsip.parseShareLink(link)
-
- // NekoBean from link
- val bean = NekoBean()
- bean.plgId = plgId
- bean.protocolId = protocolId
- bean.sharedStorage = NekoBean.tryParseJSON(sharedStorage)
- bean.onSharedStorageSet()
-
- it.resume(bean)
- } catch (e: Exception) {
- Logs.e(e)
- it.resume(NekoBean().apply {
- this.plgId = plgId
- this.protocolId = protocolId
- })
- }
-
- jsi.unlock()
- // destroy when all link parsed
- }
- }
-
-fun NekoBean.shareLink(): String {
- return sharedStorage.optString("shareLink")
-}
-
-// Only run in bg process
-// seems no concurrent
-suspend fun NekoBean.updateAllConfig(port: Int) = suspendCoroutine {
- allConfig = null
-
- runOnIoDispatcher {
- val jsi = NekoJSInterface.Default.requireJsi(plgId)
- jsi.lock()
-
- try {
- jsi.init()
- val jsip = jsi.switchProtocol(protocolId)
-
- // runtime arguments
- val otherArgs = mutableMapOf()
- otherArgs["finalAddress"] = finalAddress
- otherArgs["finalPort"] = finalPort
-// otherArgs["muxEnabled"] = Protocols.shouldEnableMux(protocolId)
-// otherArgs["muxConcurrency"] = DataStore.muxConcurrency
-
- val ret = jsip.buildAllConfig(port, this@updateAllConfig, otherArgs)
-
- // result
- allConfig = JSONObject(ret)
- } catch (e: Exception) {
- Logs.e(e)
- }
-
- jsi.unlock()
- it.resume(Unit)
- // destroy when config generated / all tests finished
- }
-}
-
-fun NekoBean.cacheGet(id: String): String? {
- return DataStore.profileCacheStore.getString("neko_${hash()}_$id")
-}
-
-fun NekoBean.cacheSet(id: String, value: String) {
- DataStore.profileCacheStore.putString("neko_${hash()}_$id", value)
-}
-
-fun NekoBean.hash(): String {
- var a = plgId
- a += protocolId
- a += sharedStorage.toString()
- return Libcore.sha256Hex(a.toByteArray())
-}
-
-// must call it to update something like serverAddress
-fun NekoBean.onSharedStorageSet() {
- serverAddress = sharedStorage.getStr("serverAddress")
- serverPort = sharedStorage.getStr("serverPort")?.toInt() ?: 1080
- if (serverAddress == null || serverAddress.isBlank()) {
- serverAddress = "127.0.0.1"
- }
- name = sharedStorage.optString("name")
-}
-
-fun NekoBean.needBypassRootUid(): Boolean {
- val p = NekoPluginManager.findProtocol(protocolId) ?: return false
- return p.protocolConfig.optBoolean("needBypassRootUid")
-}
-
-fun NekoBean.haveStandardLink(): Boolean {
- val p = NekoPluginManager.findProtocol(protocolId) ?: return false
- return p.protocolConfig.optBoolean("haveStandardLink")
-}
-
-fun NekoBean.canShare(): Boolean {
- val p = NekoPluginManager.findProtocol(protocolId) ?: return false
- return p.protocolConfig.optBoolean("canShare")
-}
diff --git a/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoJSInterface.kt b/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoJSInterface.kt
deleted file mode 100644
index df165c7..0000000
--- a/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoJSInterface.kt
+++ /dev/null
@@ -1,388 +0,0 @@
-package moe.matsuri.nb4a.proxy.neko
-
-import android.annotation.SuppressLint
-import android.webkit.*
-import android.widget.Toast
-import androidx.preference.Preference
-import androidx.preference.PreferenceScreen
-import io.nekohasekai.sagernet.BuildConfig
-import io.nekohasekai.sagernet.SagerNet
-import io.nekohasekai.sagernet.database.DataStore
-import io.nekohasekai.sagernet.ktx.*
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.withContext
-import moe.matsuri.nb4a.plugin.NekoPluginManager
-import moe.matsuri.nb4a.ui.SimpleMenuPreference
-import moe.matsuri.nb4a.utils.JavaUtil
-import moe.matsuri.nb4a.utils.Util
-import moe.matsuri.nb4a.utils.WebViewUtil
-import org.json.JSONObject
-import java.io.File
-import java.io.FileInputStream
-import java.util.*
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.atomic.AtomicBoolean
-import kotlin.coroutines.resume
-import kotlin.coroutines.resumeWithException
-import kotlin.coroutines.suspendCoroutine
-
-class NekoJSInterface(val plgId: String) {
-
- private val mutex = Mutex()
- private var webView: WebView? = null
- val jsObject = JsObject()
- var plgConfig: JSONObject? = null
- var plgConfigException: Exception? = null
- val protocols = mutableMapOf()
- val loaded = AtomicBoolean()
-
- suspend fun lock() {
- mutex.lock(null)
- }
-
- fun unlock() {
- mutex.unlock(null)
- }
-
- // load webview and js
- // Return immediately when already loaded
- // Return plgConfig or throw exception
- suspend fun init() = withContext(Dispatchers.Main) {
- initInternal()
- }
-
- @SuppressLint("SetJavaScriptEnabled")
- private suspend fun initInternal() = suspendCoroutine {
- if (loaded.get()) {
- plgConfig?.apply {
- it.resume(this)
- return@suspendCoroutine
- }
- plgConfigException?.apply {
- it.resumeWithException(this)
- return@suspendCoroutine
- }
- it.resumeWithException(Exception("wtf"))
- return@suspendCoroutine
- }
-
- WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
- NekoPluginManager.extractPlugin(plgId, false)
-
- webView = WebView(SagerNet.application.applicationContext)
- webView!!.settings.javaScriptEnabled = true
- webView!!.addJavascriptInterface(jsObject, "neko")
- webView!!.webViewClient = object : WebViewClient() {
- // provide files
- override fun shouldInterceptRequest(
- view: WebView?, request: WebResourceRequest?
- ): WebResourceResponse {
- return WebViewUtil.interceptRequest(
- { res ->
- val f = File(NekoPluginManager.htmlPath(plgId), res)
- if (f.exists()) {
- FileInputStream(f)
- } else {
- null
- }
- },
- view,
- request
- )
- }
-
- override fun onReceivedError(
- view: WebView?, request: WebResourceRequest?, error: WebResourceError?
- ) {
- WebViewUtil.onReceivedError(view, request, error)
- }
-
- override fun onPageFinished(view: WebView?, url: String?) {
- super.onPageFinished(view, url)
- if (loaded.getAndSet(true)) return
-
- runOnIoDispatcher {
- // Process nekoInit
- var ret = ""
- try {
- ret = nekoInit()
- val obj = JSONObject(ret)
- if (!obj.getBoolean("ok")) {
- throw Exception("plugin refuse to run: ${obj.optString("reason")}")
- }
- val min = obj.getInt("minVersion")
- if (min > NekoPluginManager.managerVersion) {
- throw Exception("manager version ${NekoPluginManager.managerVersion} too old, this plugin requires >= $min")
- }
- plgConfig = obj
- NekoPluginManager.updatePlgConfig(plgId, obj)
- it.resume(obj)
- } catch (e: Exception) {
- val e2 = Exception("nekoInit: " + e.readableMessage + "\n\n" + ret)
- plgConfigException = e2
- it.resumeWithException(e2)
- }
- }
- }
- }
- webView!!.loadUrl("http://$plgId/plugin.html")
- }
-
- // Android call JS
-
- private suspend fun callJS(script: String): String = suspendCoroutine {
- val jsLatch = CountDownLatch(1)
- var jsReceivedValue = ""
-
- runOnMainDispatcher {
- if (webView != null) {
- webView!!.evaluateJavascript(script) { value ->
- jsReceivedValue = value
- jsLatch.countDown()
- }
- } else {
- jsReceivedValue = "webView is null"
- jsLatch.countDown()
- }
- }
-
- jsLatch.await(5, TimeUnit.SECONDS)
-
- // evaluateJavascript escapes Javascript's String
- jsReceivedValue = JavaUtil.unescapeString(jsReceivedValue.removeSurrounding("\""))
- if (BuildConfig.DEBUG) Logs.d("$script: $jsReceivedValue")
- it.resume(jsReceivedValue)
- }
-
- // call once
- private suspend fun nekoInit(): String {
- val sendData = JSONObject()
- sendData.put("lang", Locale.getDefault().toLanguageTag())
- sendData.put("plgId", plgId)
- sendData.put("managerVersion", NekoPluginManager.managerVersion)
-
- return callJS(
- "nekoInit(\"${
- Util.b64EncodeUrlSafe(
- sendData.toString().toByteArray()
- )
- }\")"
- )
- }
-
- fun switchProtocol(id: String): NekoProtocol {
- lateinit var p: NekoProtocol
- if (protocols.containsKey(id)) {
- p = protocols[id]!!
- } else {
- p = NekoProtocol(id) { callJS(it) }
- protocols[id] = p
- }
- jsObject.protocol = p
- return p
- }
-
- suspend fun getAbout(): String {
- return callJS("nekoAbout()")
- }
-
- inner class NekoProtocol(val protocolId: String, val callJS: suspend (String) -> String) {
- private suspend fun callProtocol(method: String, b64Str: String?): String {
- var arg = ""
- if (b64Str != null) {
- arg = "\"" + b64Str + "\""
- }
- return callJS("nekoProtocol(\"$protocolId\").$method($arg)")
- }
-
- suspend fun buildAllConfig(
- port: Int, bean: NekoBean, otherArgs: Map?
- ): String {
- val sendData = JSONObject()
- sendData.put("port", port)
- sendData.put(
- "sharedStorage",
- Util.b64EncodeUrlSafe(bean.sharedStorage.toString().toByteArray())
- )
- otherArgs?.forEach { (t, u) -> sendData.put(t, u) }
-
- return callProtocol(
- "buildAllConfig", Util.b64EncodeUrlSafe(sendData.toString().toByteArray())
- )
- }
-
- suspend fun parseShareLink(shareLink: String): String {
- val sendData = JSONObject()
- sendData.put("shareLink", shareLink)
-
- return callProtocol(
- "parseShareLink", Util.b64EncodeUrlSafe(sendData.toString().toByteArray())
- )
- }
-
- // UI Interface
-
- suspend fun setSharedStorage(sharedStorage: String) {
- callProtocol(
- "setSharedStorage",
- Util.b64EncodeUrlSafe(sharedStorage.toByteArray())
- )
- }
-
- suspend fun requireSetProfileCache() {
- callProtocol("requireSetProfileCache", null)
- }
-
- suspend fun requirePreferenceScreenConfig(): String {
- return callProtocol("requirePreferenceScreenConfig", null)
- }
-
- suspend fun sharedStorageFromProfileCache(): String {
- return callProtocol("sharedStorageFromProfileCache", null)
- }
-
- suspend fun onPreferenceCreated() {
- callProtocol("onPreferenceCreated", null)
- }
-
- suspend fun onPreferenceChanged(key: String, v: Any) {
- val sendData = JSONObject()
- sendData.put("key", key)
- sendData.put("newValue", v)
-
- callProtocol(
- "onPreferenceChanged",
- Util.b64EncodeUrlSafe(sendData.toString().toByteArray())
- )
- }
-
- }
-
- inner class JsObject {
- var preferenceScreen: PreferenceScreen? = null
- var protocol: NekoProtocol? = null
-
- // JS call Android
-
- @JavascriptInterface
- fun toast(s: String) {
- Toast.makeText(SagerNet.application.applicationContext, s, Toast.LENGTH_SHORT).show()
- }
-
- @JavascriptInterface
- fun logError(s: String) {
- Logs.e("logError: $s")
- }
-
- @JavascriptInterface
- fun setPreferenceVisibility(key: String, isVisible: Boolean) {
- runBlockingOnMainDispatcher {
- preferenceScreen?.findPreference(key)?.isVisible = isVisible
- }
- }
-
- @JavascriptInterface
- fun setPreferenceTitle(key: String, title: String) {
- runBlockingOnMainDispatcher {
- preferenceScreen?.findPreference(key)?.title = title
- }
- }
-
- @JavascriptInterface
- fun setMenu(key: String, entries: String) {
- runBlockingOnMainDispatcher {
- preferenceScreen?.findPreference(key)?.apply {
- NekoPreferenceInflater.setMenu(this, JSONObject(entries))
- }
- }
- }
-
- @JavascriptInterface
- fun listenOnPreferenceChanged(key: String) {
- preferenceScreen?.findPreference(key)
- ?.setOnPreferenceChangeListener { preference, newValue ->
- runOnIoDispatcher {
- protocol?.onPreferenceChanged(preference.key, newValue)
- }
- true
- }
- }
-
- @JavascriptInterface
- fun setKV(type: Int, key: String, jsonStr: String) {
- try {
- val v = JSONObject(jsonStr)
- when (type) {
- 0 -> DataStore.profileCacheStore.putBoolean(key, v.getBoolean("v"))
- 1 -> DataStore.profileCacheStore.putFloat(key, v.getDouble("v").toFloat())
- 2 -> DataStore.profileCacheStore.putInt(key, v.getInt("v"))
- 3 -> DataStore.profileCacheStore.putLong(key, v.getLong("v"))
- 4 -> DataStore.profileCacheStore.putString(key, v.getString("v"))
- }
- } catch (e: Exception) {
- Logs.e("setKV: $e")
- }
- }
-
- @JavascriptInterface
- fun getKV(type: Int, key: String): String {
- val v = JSONObject()
- try {
- when (type) {
- 0 -> v.put("v", DataStore.profileCacheStore.getBoolean(key))
- 1 -> v.put("v", DataStore.profileCacheStore.getFloat(key))
- 2 -> v.put("v", DataStore.profileCacheStore.getInt(key))
- 3 -> v.put("v", DataStore.profileCacheStore.getLong(key))
- 4 -> v.put("v", DataStore.profileCacheStore.getString(key))
- }
- } catch (e: Exception) {
- Logs.e("getKV: $e")
- }
- return v.toString()
- }
-
- }
-
- fun destroy() {
- webView?.onPause()
- webView?.removeAllViews()
- webView?.destroy()
- webView = null
- }
-
- suspend fun destorySuspend() = withContext(Dispatchers.Main) {
- destroy()
- }
-
- object Default {
- val map = mutableMapOf()
-
- suspend fun destroyJsi(plgId: String) = withContext(Dispatchers.Main) {
- if (map.containsKey(plgId)) {
- map[plgId]!!.destroy()
- map.remove(plgId)
- }
- }
-
- // now it's manually managed
- suspend fun destroyAllJsi() = withContext(Dispatchers.Main) {
- map.forEach { (t, u) ->
- u.destroy()
- map.remove(t)
- }
- }
-
- suspend fun requireJsi(plgId: String): NekoJSInterface = withContext(Dispatchers.Main) {
- lateinit var jsi: NekoJSInterface
- if (map.containsKey(plgId)) {
- jsi = map[plgId]!!
- } else {
- jsi = NekoJSInterface(plgId)
- map[plgId] = jsi
- }
- return@withContext jsi
- }
- }
-}
diff --git a/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoPreferenceInflater.kt b/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoPreferenceInflater.kt
deleted file mode 100644
index cd2fdff..0000000
--- a/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoPreferenceInflater.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-package moe.matsuri.nb4a.proxy.neko
-
-import androidx.preference.*
-import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers
-import io.nekohasekai.sagernet.ktx.forEach
-import io.nekohasekai.sagernet.ktx.getStr
-import io.nekohasekai.sagernet.ui.profile.ProfileSettingsActivity
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
-import moe.matsuri.nb4a.ui.SimpleMenuPreference
-import moe.matsuri.nb4a.utils.getDrawableByName
-import org.json.JSONArray
-import org.json.JSONObject
-
-object NekoPreferenceInflater {
- suspend fun inflate(pref: JSONArray, preferenceScreen: PreferenceScreen) =
- withContext(Dispatchers.Main) {
- val context = preferenceScreen.context
- pref.forEach { _, category ->
- category as JSONObject
-
- val preferenceCategory = PreferenceCategory(context)
- preferenceScreen.addPreference(preferenceCategory)
-
- category.getStr("key")?.apply { preferenceCategory.key = this }
- category.getStr("title")?.apply { preferenceCategory.title = this }
-
- category.optJSONArray("preferences")?.forEach { _, any ->
- if (any is JSONObject) {
- lateinit var p: Preference
- // Create Preference
- when (any.getStr("type")) {
- "EditTextPreference" -> {
- p = EditTextPreference(context).apply {
- when (any.getStr("summaryProvider")) {
- null -> summaryProvider =
- EditTextPreference.SimpleSummaryProvider.getInstance()
- "PasswordSummaryProvider" -> summaryProvider =
- ProfileSettingsActivity.PasswordSummaryProvider
- }
- when (any.getStr("EditTextPreferenceModifiers")) {
- "Monospace" -> setOnBindEditTextListener(
- EditTextPreferenceModifiers.Monospace
- )
- "Hosts" -> setOnBindEditTextListener(
- EditTextPreferenceModifiers.Hosts
- )
- "Port" -> setOnBindEditTextListener(
- EditTextPreferenceModifiers.Port
- )
- "Number" -> setOnBindEditTextListener(
- EditTextPreferenceModifiers.Number
- )
- }
- }
- }
- "SwitchPreference" -> {
- p = SwitchPreference(context)
- }
- "SimpleMenuPreference" -> {
- p = SimpleMenuPreference(context).apply {
- val entries = any.optJSONObject("entries")
- if (entries != null) setMenu(this, entries)
- }
- }
- }
- // Set key & title
- p.key = any.getString("key")
- any.getStr("title")?.apply { p.title = this }
- // Set icon
- any.getStr("icon")?.apply {
- p.icon = context.getDrawableByName(this)
- }
- // Set summary
- any.getStr("summary")?.apply {
- p.summary = this
- }
- // Add to category
- preferenceCategory.addPreference(p)
- }
- }
- }
- }
-
- fun setMenu(p: SimpleMenuPreference, entries: JSONObject) {
- val menuEntries = mutableListOf()
- val menuEntryValues = mutableListOf()
- entries.forEach { s, b ->
- menuEntryValues.add(s)
- menuEntries.add(b as String)
- }
- entries.apply {
- p.entries = menuEntries.toTypedArray()
- p.entryValues = menuEntryValues.toTypedArray()
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoSettingActivity.kt b/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoSettingActivity.kt
deleted file mode 100644
index e167fd6..0000000
--- a/app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoSettingActivity.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package moe.matsuri.nb4a.proxy.neko
-
-import android.os.Bundle
-import android.view.View
-import androidx.core.view.isVisible
-import androidx.preference.PreferenceDataStore
-import androidx.preference.PreferenceFragmentCompat
-import io.nekohasekai.sagernet.Key
-import io.nekohasekai.sagernet.R
-import io.nekohasekai.sagernet.database.DataStore
-import io.nekohasekai.sagernet.ktx.runOnIoDispatcher
-import io.nekohasekai.sagernet.ui.profile.ProfileSettingsActivity
-import moe.matsuri.nb4a.ui.Dialogs
-import org.json.JSONArray
-
-class NekoSettingActivity : ProfileSettingsActivity() {
-
- lateinit var jsi: NekoJSInterface
- lateinit var jsip: NekoJSInterface.NekoProtocol
- lateinit var plgId: String
- lateinit var protocolId: String
- var loaded = false
-
- override fun createEntity() = NekoBean()
-
- override fun NekoBean.init() {
- if (!this@NekoSettingActivity::plgId.isInitialized) this@NekoSettingActivity.plgId = plgId
- if (!this@NekoSettingActivity::protocolId.isInitialized) this@NekoSettingActivity.protocolId = protocolId
- DataStore.profileCacheStore.putString("name", name)
- DataStore.sharedStorage = sharedStorage.toString()
- }
-
- override fun NekoBean.serialize() {
- // NekoBean from input
- plgId = this@NekoSettingActivity.plgId
- protocolId = this@NekoSettingActivity.protocolId
-
- sharedStorage = NekoBean.tryParseJSON(DataStore.sharedStorage)
- onSharedStorageSet()
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- intent?.getStringExtra("plgId")?.apply { plgId = this }
- intent?.getStringExtra("protocolId")?.apply { protocolId = this }
- super.onCreate(savedInstanceState)
- }
-
- override fun PreferenceFragmentCompat.viewCreated(view: View, savedInstanceState: Bundle?) {
- listView.isVisible = false
- }
-
- override fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String) {
- if (loaded && key != Key.PROFILE_DIRTY) {
- DataStore.dirty = true
- }
- }
-
- override fun PreferenceFragmentCompat.createPreferences(
- savedInstanceState: Bundle?,
- rootKey: String?,
- ) {
- addPreferencesFromResource(R.xml.neko_preferences)
-
- // Create a jsi
- jsi = NekoJSInterface(plgId)
- runOnIoDispatcher {
- try {
- jsi.init()
- jsip = jsi.switchProtocol(protocolId)
- jsi.jsObject.preferenceScreen = preferenceScreen
-
- // Because of the Preference problem, first require the KV and then inflate the UI
- jsip.setSharedStorage(DataStore.sharedStorage)
- jsip.requireSetProfileCache()
-
- val config = jsip.requirePreferenceScreenConfig()
- val pref = JSONArray(config)
-
- NekoPreferenceInflater.inflate(pref, preferenceScreen)
- jsip.onPreferenceCreated()
-
- runOnUiThread {
- loaded = true
- listView.isVisible = true
- }
- } catch (e: Exception) {
- Dialogs.logExceptionAndShow(this@NekoSettingActivity, e) { finish() }
- }
- }
- }
-
- override suspend fun saveAndExit() {
- DataStore.sharedStorage = jsip.sharedStorageFromProfileCache()
- super.saveAndExit() // serialize & finish
- }
-
- override fun onDestroy() {
- jsi.destroy()
- super.onDestroy()
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_app_list.xml b/app/src/main/res/layout/layout_app_list.xml
index f013d4d..45432fa 100644
--- a/app/src/main/res/layout/layout_app_list.xml
+++ b/app/src/main/res/layout/layout_app_list.xml
@@ -82,61 +82,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/layout_apps_item.xml b/app/src/main/res/layout/layout_apps_item.xml
index 1736784..055d836 100644
--- a/app/src/main/res/layout/layout_apps_item.xml
+++ b/app/src/main/res/layout/layout_apps_item.xml
@@ -58,15 +58,6 @@
-
-
-
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 5544121..e0301bc 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -446,7 +446,5 @@
Cambiar
Borrado no disponible
- Complemento avanzado
- %s error interno
-
+
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index 46c01df..bb9291c 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -477,9 +477,6 @@
نسخه (%s) برنامه شما بسیار قدیمی شده است و در %s متوقف میشود. لطفاً به نسخه جدیدتر بهروزرسانی کنید
برنامه شما خیلی قدیمی شده است (%s). و در %s کار نمیکند. باید نسخه جدید را دانلود کنید!
پاککردن غیرقابلدسترسها
- افزونه پیشرفته
- افزونههای پیشرفته میتوانند پروتکلهایی را ارائه دهند که به صورت پیشفرض در این برنامه پشتیبانی نشدهاند.\n\n هر کسی میتواند افزونههای پیشرفته بنویسد که میتوانند NekoBox را کنترل کنند. لطفا از منابع معتبر دانلود و نصب کنید.
- مشکل داخلی %s
حرکت
ارائهدهنده برگزیده افزونهها
ساخت میانبر
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 46cc8c1..07a38d9 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -259,10 +259,6 @@
Если придётся использовать данную функцию, попробуйте N=2, чтобы проверить, решит ли это проблему. Настоятельно не рекомендуется использовать более 4 соединений.\""
Перезагрузите прокси-сервис, чтобы применить изменения
Перезапустите приложение для применения изменений
-Дополнительный плагин
-"\"Дополнительные плагины могут предоставлять протоколы, которые изначально не поддерживаются.
-
- Любой может написать дополнительные плагины, которые могут управлять Nekobox. Загружайте и устанавливайте их из надежных источников.\""
Сеть
Сбрасывать исходящие соединения при изменении сети
Ночной режим
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index c08ee1f..2dbb97f 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -425,16 +425,11 @@
您的 APP (%s) 太旧了喵,%s 再不更新就没的用了喵~
您的 APP (%s) 版本过旧,已于 %s 停止工作,请立即升级。
清理不可用配置
- 高级插件
- 高级插件可以提供原本不支持的协议。\n\n
- 任何人都可以编写高级插件,开启相当于给予其控制 NekoBox 的权限,请从信任的来源下载安装。\n\n
- 普通插件在关于页面显示,无需手动开启。
包编码
切换
获取唤醒锁
释放唤醒锁
保持 CPU 开启
- %s 内部错误
移动
过期: %s
更新所有订阅
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 96471ae..1899df1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -509,12 +509,6 @@
Your APP is too old (%s). And has been stopped working at %s.
Please update!
Clear unavailable
- Advanced plugin
- Advanced plugins can provide protocols that are not
- originally supported.\n\n
- Anyone can write advanced plugins, which can control NekoBox. please download and install
- from trusted sources.
- %s internal error
Move
Plugin Preferred Provider
Create Shortcut
diff --git a/app/src/main/res/xml/global_preferences.xml b/app/src/main/res/xml/global_preferences.xml
index eef631d..038587c 100644
--- a/app/src/main/res/xml/global_preferences.xml
+++ b/app/src/main/res/xml/global_preferences.xml
@@ -89,10 +89,6 @@
app:key="logLevel"
app:title="@string/log_level"
app:useSimpleSummaryProvider="true" />
-