update ui

This commit is contained in:
arm64v8a 2023-03-18 13:21:51 +09:00
parent 249511a4c0
commit c2f39c3bbc
84 changed files with 1125 additions and 289 deletions

1
app/.gitignore vendored
View File

@ -1,2 +1 @@
/build
/schemas

View File

@ -31,24 +31,21 @@ dependencies {
implementation(fileTree("libs"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.3")
implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.recyclerview:recyclerview:1.2.1")
implementation("androidx.activity:activity-ktx:1.4.0")
implementation("androidx.fragment:fragment-ktx:1.4.1")
implementation("androidx.browser:browser:1.4.0")
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.recyclerview:recyclerview:1.3.0")
implementation("androidx.activity:activity-ktx:1.6.1")
implementation("androidx.fragment:fragment-ktx:1.5.5")
implementation("androidx.browser:browser:1.5.0")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.navigation:navigation-fragment-ktx:2.4.2")
implementation("androidx.navigation:navigation-ui-ktx:2.4.2")
implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
implementation("androidx.preference:preference-ktx:1.2.0")
implementation("androidx.appcompat:appcompat:1.4.1")
implementation("androidx.work:work-runtime-ktx:2.7.1")
implementation("androidx.work:work-multiprocess:2.7.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.work:work-runtime-ktx:2.8.0")
implementation("androidx.work:work-multiprocess:2.8.0")
implementation(project(":external:preferencex:preferencex"))
implementation(project(":external:preferencex:preferencex-simplemenu"))
implementation("com.google.android.material:material:1.6.0")
implementation("com.google.android.material:material:1.8.0")
implementation("com.google.code.gson:gson:2.8.9")
implementation("com.github.jenly1314:zxing-lite:2.1.1")
@ -71,9 +68,9 @@ dependencies {
exclude(group = "com.google.guava", module = "guava")
}
implementation("androidx.room:room-runtime:2.4.2")
kapt("androidx.room:room-compiler:2.4.2")
implementation("androidx.room:room-ktx:2.4.2")
implementation("androidx.room:room-runtime:2.5.0")
kapt("androidx.room:room-compiler:2.5.0")
implementation("androidx.room:room-ktx:2.5.0")
implementation("com.github.MatrixDev.Roomigrant:RoomigrantLib:0.3.4")
kapt("com.github.MatrixDev.Roomigrant:RoomigrantCompiler:0.3.4")

View File

@ -0,0 +1,330 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "f66fd943df1d9e86d281a2e32c9fdd47",
"entities": [
{
"tableName": "proxy_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ungrouped",
"columnName": "ungrouped",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subscription",
"columnName": "subscription",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "proxy_entities",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `status` INTEGER NOT NULL, `ping` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `error` TEXT, `socksBean` BLOB, `httpBean` BLOB, `ssBean` BLOB, `vmessBean` BLOB, `trojanBean` BLOB, `trojanGoBean` BLOB, `naiveBean` BLOB, `hysteriaBean` BLOB, `tuicBean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `chainBean` BLOB, `nekoBean` BLOB, `configBean` BLOB)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "groupId",
"columnName": "groupId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ping",
"columnName": "ping",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "error",
"columnName": "error",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "socksBean",
"columnName": "socksBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "httpBean",
"columnName": "httpBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "ssBean",
"columnName": "ssBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "vmessBean",
"columnName": "vmessBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanBean",
"columnName": "trojanBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanGoBean",
"columnName": "trojanGoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "naiveBean",
"columnName": "naiveBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "hysteriaBean",
"columnName": "hysteriaBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "tuicBean",
"columnName": "tuicBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "sshBean",
"columnName": "sshBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "wgBean",
"columnName": "wgBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "chainBean",
"columnName": "chainBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "nekoBean",
"columnName": "nekoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "configBean",
"columnName": "configBean",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "groupId",
"unique": false,
"columnNames": [
"groupId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
}
],
"foreignKeys": []
},
{
"tableName": "rules",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `userOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `domains` TEXT NOT NULL, `ip` TEXT NOT NULL, `port` TEXT NOT NULL, `sourcePort` TEXT NOT NULL, `network` TEXT NOT NULL, `source` TEXT NOT NULL, `protocol` TEXT NOT NULL, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "domains",
"columnName": "domains",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ip",
"columnName": "ip",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "port",
"columnName": "port",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourcePort",
"columnName": "sourcePort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "network",
"columnName": "network",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "protocol",
"columnName": "protocol",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "outbound",
"columnName": "outbound",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packages",
"columnName": "packages",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f66fd943df1d9e86d281a2e32c9fdd47')"
]
}
}

View File

@ -0,0 +1,342 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "062104587ad7088bb9926683389e995d",
"entities": [
{
"tableName": "proxy_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL, `isSelector` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ungrouped",
"columnName": "ungrouped",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subscription",
"columnName": "subscription",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isSelector",
"columnName": "isSelector",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "frontProxy",
"columnName": "frontProxy",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "proxy_entities",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `status` INTEGER NOT NULL, `ping` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `error` TEXT, `socksBean` BLOB, `httpBean` BLOB, `ssBean` BLOB, `vmessBean` BLOB, `trojanBean` BLOB, `trojanGoBean` BLOB, `naiveBean` BLOB, `hysteriaBean` BLOB, `tuicBean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `chainBean` BLOB, `nekoBean` BLOB, `configBean` BLOB)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "groupId",
"columnName": "groupId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ping",
"columnName": "ping",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "error",
"columnName": "error",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "socksBean",
"columnName": "socksBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "httpBean",
"columnName": "httpBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "ssBean",
"columnName": "ssBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "vmessBean",
"columnName": "vmessBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanBean",
"columnName": "trojanBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanGoBean",
"columnName": "trojanGoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "naiveBean",
"columnName": "naiveBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "hysteriaBean",
"columnName": "hysteriaBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "tuicBean",
"columnName": "tuicBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "sshBean",
"columnName": "sshBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "wgBean",
"columnName": "wgBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "chainBean",
"columnName": "chainBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "nekoBean",
"columnName": "nekoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "configBean",
"columnName": "configBean",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "groupId",
"unique": false,
"columnNames": [
"groupId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
}
],
"foreignKeys": []
},
{
"tableName": "rules",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `userOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `domains` TEXT NOT NULL, `ip` TEXT NOT NULL, `port` TEXT NOT NULL, `sourcePort` TEXT NOT NULL, `network` TEXT NOT NULL, `source` TEXT NOT NULL, `protocol` TEXT NOT NULL, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "domains",
"columnName": "domains",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ip",
"columnName": "ip",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "port",
"columnName": "port",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourcePort",
"columnName": "sourcePort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "network",
"columnName": "network",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "protocol",
"columnName": "protocol",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "outbound",
"columnName": "outbound",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packages",
"columnName": "packages",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '062104587ad7088bb9926683389e995d')"
]
}
}

View File

@ -0,0 +1,46 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "f1aab1fb633378621635c344dbc8ac7b",
"entities": [
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f1aab1fb633378621635c344dbc8ac7b')"
]
}
}

View File

@ -0,0 +1,46 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "f1aab1fb633378621635c344dbc8ac7b",
"entities": [
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f1aab1fb633378621635c344dbc8ac7b')"
]
}
}

View File

@ -133,6 +133,7 @@ object Key {
const val GROUP_NAME = "groupName"
const val GROUP_TYPE = "groupType"
const val GROUP_ORDER = "groupOrder"
const val GROUP_IS_SELECTOR = "groupIsSelector"
const val GROUP_SUBSCRIPTION = "groupSubscription"
const val SUBSCRIPTION_LINK = "subscriptionLink"

View File

@ -8,7 +8,10 @@ import android.os.IBinder
import android.os.RemoteException
import io.nekohasekai.sagernet.Action
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.aidl.*
import io.nekohasekai.sagernet.aidl.ISagerNetService
import io.nekohasekai.sagernet.aidl.ISagerNetServiceCallback
import io.nekohasekai.sagernet.aidl.SpeedDisplayData
import io.nekohasekai.sagernet.aidl.TrafficData
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.ktx.runOnMainDispatcher

View File

@ -22,9 +22,9 @@ 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.plugin.NekoPluginManager
import moe.matsuri.nb4a.proxy.neko.updateAllConfig
import org.json.JSONObject
import java.io.File

View File

@ -234,6 +234,7 @@ object DataStore : OnPreferenceDataStoreChangeListener {
var groupName by profileCacheStore.string(Key.GROUP_NAME)
var groupType by profileCacheStore.stringToInt(Key.GROUP_TYPE)
var groupOrder by profileCacheStore.stringToInt(Key.GROUP_ORDER)
var groupIsSelector by profileCacheStore.boolean(Key.GROUP_IS_SELECTOR)
var subscriptionLink by profileCacheStore.string(Key.SUBSCRIPTION_LINK)
var subscriptionForceResolve by profileCacheStore.boolean(Key.SUBSCRIPTION_FORCE_RESOLVE)

View File

@ -2,7 +2,6 @@ package io.nekohasekai.sagernet.database
import android.database.sqlite.SQLiteCantOpenDatabaseException
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.aidl.SpeedDisplayData
import io.nekohasekai.sagernet.aidl.TrafficData
import io.nekohasekai.sagernet.fmt.AbstractBean
import io.nekohasekai.sagernet.ktx.Logs

View File

@ -31,13 +31,9 @@ import io.nekohasekai.sagernet.ktx.app
import io.nekohasekai.sagernet.ktx.applyDefaultValues
import io.nekohasekai.sagernet.ui.profile.*
import moe.matsuri.nb4a.Protocols
import moe.matsuri.nb4a.proxy.neko.*
import moe.matsuri.nb4a.proxy.config.ConfigBean
import moe.matsuri.nb4a.proxy.config.ConfigSettingActivity
import moe.matsuri.nb4a.proxy.neko.NekoBean
import moe.matsuri.nb4a.proxy.neko.NekoSettingActivity
import moe.matsuri.nb4a.proxy.neko.haveStandardLink
import moe.matsuri.nb4a.proxy.neko.shareLink
import moe.matsuri.nb4a.proxy.neko.*
@Entity(
tableName = "proxy_entities", indices = [Index("groupId", name = "groupId")]

View File

@ -19,6 +19,8 @@ data class ProxyGroup(
var type: Int = GroupType.BASIC,
var subscription: SubscriptionBean? = null,
var order: Int = GroupOrder.ORIGIN,
var isSelector: Boolean = false,
var frontProxy: Long = 0L
) : Serializable() {
@Transient

View File

@ -15,7 +15,7 @@ import kotlinx.coroutines.launch
@Database(
entities = [ProxyGroup::class, ProxyEntity::class, RuleEntity::class],
version = 1
version = 2
)
@TypeConverters(value = [KryoConverters::class, GsonConverters::class])
@GenerateRoomMigrations

View File

@ -19,7 +19,6 @@ import io.nekohasekai.sagernet.fmt.socks.buildSingBoxOutboundSocksBean
import io.nekohasekai.sagernet.fmt.ssh.SSHBean
import io.nekohasekai.sagernet.fmt.ssh.buildSingBoxOutboundSSHBean
import io.nekohasekai.sagernet.fmt.tuic.TuicBean
import moe.matsuri.nb4a.SingBoxOptions.*
import io.nekohasekai.sagernet.fmt.v2ray.StandardV2RayBean
import io.nekohasekai.sagernet.fmt.v2ray.buildSingBoxOutboundStandardV2RayBean
import io.nekohasekai.sagernet.fmt.wireguard.WireGuardBean
@ -29,8 +28,9 @@ import io.nekohasekai.sagernet.ktx.mkPort
import io.nekohasekai.sagernet.utils.PackageCache
import moe.matsuri.nb4a.DNS.applyDNSNetworkSettings
import moe.matsuri.nb4a.DNS.makeSingBoxRule
import moe.matsuri.nb4a.proxy.config.ConfigBean
import moe.matsuri.nb4a.SingBoxOptions.*
import moe.matsuri.nb4a.plugin.Plugins
import moe.matsuri.nb4a.proxy.config.ConfigBean
import moe.matsuri.nb4a.utils.JavaUtil.gson
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull

View File

@ -24,8 +24,8 @@ import io.nekohasekai.sagernet.fmt.v2ray.VMessBean;
import io.nekohasekai.sagernet.fmt.wireguard.WireGuardBean;
import io.nekohasekai.sagernet.ktx.KryosKt;
import io.nekohasekai.sagernet.ktx.Logs;
import moe.matsuri.nb4a.proxy.neko.NekoBean;
import moe.matsuri.nb4a.proxy.config.ConfigBean;
import moe.matsuri.nb4a.proxy.neko.NekoBean;
import moe.matsuri.nb4a.utils.JavaUtil;
public class KryoConverters {

View File

@ -4,7 +4,6 @@ import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.fmt.LOCALHOST
import io.nekohasekai.sagernet.ktx.*
import moe.matsuri.nb4a.SingBoxOptions
import moe.matsuri.nb4a.utils.Util
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.json.JSONObject
import java.io.File

View File

@ -1,7 +1,7 @@
package io.nekohasekai.sagernet.fmt.shadowsocks
import moe.matsuri.nb4a.SingBoxOptions
import io.nekohasekai.sagernet.ktx.*
import moe.matsuri.nb4a.SingBoxOptions
import moe.matsuri.nb4a.utils.Util
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.json.JSONObject

View File

@ -1,7 +1,10 @@
package io.nekohasekai.sagernet.fmt.socks
import io.nekohasekai.sagernet.ktx.decodeBase64UrlSafe
import io.nekohasekai.sagernet.ktx.toLink
import io.nekohasekai.sagernet.ktx.unUrlSafe
import io.nekohasekai.sagernet.ktx.urlSafe
import moe.matsuri.nb4a.SingBoxOptions
import io.nekohasekai.sagernet.ktx.*
import moe.matsuri.nb4a.utils.NGUtil
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull

View File

@ -7,7 +7,6 @@ import com.esotericsoftware.kryo.io.ByteBufferOutput;
import org.jetbrains.annotations.NotNull;
import io.nekohasekai.sagernet.fmt.AbstractBean;
import io.nekohasekai.sagernet.fmt.KryoConverters;
import io.nekohasekai.sagernet.fmt.v2ray.StandardV2RayBean;

View File

@ -3,8 +3,8 @@ package io.nekohasekai.sagernet.fmt.v2ray
import com.google.gson.Gson
import io.nekohasekai.sagernet.fmt.http.HttpBean
import io.nekohasekai.sagernet.fmt.trojan.TrojanBean
import moe.matsuri.nb4a.SingBoxOptions.*
import io.nekohasekai.sagernet.ktx.*
import moe.matsuri.nb4a.SingBoxOptions.*
import moe.matsuri.nb4a.utils.NGUtil
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl

View File

@ -12,8 +12,8 @@ import io.nekohasekai.sagernet.fmt.socks.parseSOCKS
import io.nekohasekai.sagernet.fmt.trojan.parseTrojan
import io.nekohasekai.sagernet.fmt.trojan_go.parseTrojanGo
import io.nekohasekai.sagernet.fmt.v2ray.parseV2Ray
import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
import moe.matsuri.nb4a.plugin.NekoPluginManager
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

View File

@ -1,7 +1,6 @@
package io.nekohasekai.sagernet.ktx
import android.graphics.Rect
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.nekohasekai.sagernet.database.DataStore

View File

@ -38,9 +38,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.withContext
import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
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

View File

@ -56,9 +56,9 @@ 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.config.ConfigSettingActivity
import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
import moe.matsuri.nb4a.plugin.NekoPluginManager
import moe.matsuri.nb4a.proxy.neko.NekoSettingActivity
import moe.matsuri.nb4a.proxy.neko.canShare
import okhttp3.internal.closeQuietly
@ -1467,14 +1467,14 @@ class ConfigurationFragment @JvmOverloads constructor(
popup.menuInflater.inflate(R.menu.profile_share_menu, popup.menu)
if (proxyEntity.vmessBean == null || proxyEntity.vmessBean!!.isVLESS) {
popup.menu.findItem(R.id.action_group_qr).subMenu.removeItem(R.id.action_v2rayn_qr)
popup.menu.findItem(R.id.action_group_clipboard).subMenu.removeItem(R.id.action_v2rayn_clipboard)
popup.menu.findItem(R.id.action_group_qr).subMenu?.removeItem(R.id.action_v2rayn_qr)
popup.menu.findItem(R.id.action_group_clipboard).subMenu?.removeItem(R.id.action_v2rayn_clipboard)
}
when {
!proxyEntity.haveStandardLink() -> {
popup.menu.findItem(R.id.action_group_qr).subMenu.removeItem(R.id.action_standard_qr)
popup.menu.findItem(R.id.action_group_clipboard).subMenu.removeItem(
popup.menu.findItem(R.id.action_group_qr).subMenu?.removeItem(R.id.action_standard_qr)
popup.menu.findItem(R.id.action_group_clipboard).subMenu?.removeItem(
R.id.action_standard_clipboard
)
}

View File

@ -1,20 +1,21 @@
package io.nekohasekai.sagernet.ui
import android.annotation.SuppressLint
import android.app.Activity
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AlertDialog
import androidx.core.view.ViewCompat
import androidx.preference.*
import com.github.shadowsocks.plugin.Empty
import com.github.shadowsocks.plugin.fragment.AlertDialogFragment
import com.takisoft.preferencex.PreferenceFragmentCompat
import com.takisoft.preferencex.SimpleMenuPreference
import io.nekohasekai.sagernet.GroupType
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
@ -24,8 +25,10 @@ import io.nekohasekai.sagernet.ktx.applyDefaultValues
import io.nekohasekai.sagernet.ktx.onMainDispatcher
import io.nekohasekai.sagernet.ktx.runOnDefaultDispatcher
import io.nekohasekai.sagernet.widget.ListListener
import io.nekohasekai.sagernet.widget.OutboundPreference
import io.nekohasekai.sagernet.widget.UserAgentPreference
import kotlinx.parcelize.Parcelize
import moe.matsuri.nb4a.ui.SimpleMenuPreference
@Suppress("UNCHECKED_CAST")
class GroupSettingsActivity(
@ -33,10 +36,16 @@ class GroupSettingsActivity(
) : ThemedActivity(resId),
OnPreferenceDataStoreChangeListener {
private lateinit var frontProxyPreference: OutboundPreference
fun ProxyGroup.init() {
DataStore.groupName = name ?: ""
DataStore.groupType = type
DataStore.groupOrder = order
DataStore.groupIsSelector = isSelector
DataStore.routeOutboundRule = frontProxy
DataStore.routeOutbound = if (frontProxy >= 0) 3 else 0
val subscription = subscription ?: SubscriptionBean().applyDefaultValues()
DataStore.subscriptionLink = subscription.link
DataStore.subscriptionForceResolve = subscription.forceResolve
@ -51,6 +60,8 @@ class GroupSettingsActivity(
name = DataStore.groupName.takeIf { it.isNotBlank() } ?: "My group"
type = DataStore.groupType
order = DataStore.groupOrder
isSelector = DataStore.groupIsSelector
frontProxy = if (DataStore.routeOutbound == 3) DataStore.routeOutboundRule else -1
val isSubscription = type == GroupType.SUBSCRIPTION
if (isSubscription) {
@ -77,6 +88,22 @@ class GroupSettingsActivity(
) {
addPreferencesFromResource(R.xml.group_preferences)
frontProxyPreference = findPreference(Key.ROUTE_OUTBOUND)!!
frontProxyPreference.apply {
entries = listOf("None", "Select...").toTypedArray()
entryValues = listOf("0", "3").toTypedArray()
setOnPreferenceChangeListener { _, newValue ->
if (newValue.toString() == "3") {
selectProfileForAdd.launch(
Intent(this@GroupSettingsActivity, ProfileSelectActivity::class.java)
)
false
} else {
true
}
}
}
val groupType = findPreference<SimpleMenuPreference>(Key.GROUP_TYPE)!!
val groupSubscription = findPreference<PreferenceCategory>(Key.GROUP_SUBSCRIPTION)!!
val subscriptionUpdate = findPreference<PreferenceCategory>(Key.SUBSCRIPTION_UPDATE)!!
@ -252,7 +279,7 @@ class GroupSettingsActivity(
lateinit var activity: GroupSettingsActivity
override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.preferenceDataStore = DataStore.profileCacheStore
activity.apply {
createPreferences(savedInstanceState, rootKey)
@ -312,4 +339,20 @@ class GroupSettingsActivity(
}
val selectProfileForAdd = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) runOnDefaultDispatcher {
val profile = ProfileManager.getProfile(
it.data!!.getLongExtra(
ProfileSelectActivity.EXTRA_PROFILE_ID, 0
)
) ?: return@runOnDefaultDispatcher
DataStore.routeOutboundRule = profile.id
onMainDispatcher {
frontProxyPreference.value = "3"
}
}
}
}

View File

@ -30,7 +30,6 @@ import io.nekohasekai.sagernet.group.GroupInterfaceAdapter
import io.nekohasekai.sagernet.group.GroupUpdater
import io.nekohasekai.sagernet.ktx.*
import io.nekohasekai.sagernet.widget.ListHolderListener
import kotlinx.coroutines.launch
import libcore.Libcore
import moe.matsuri.nb4a.utils.Util
import java.text.SimpleDateFormat

View File

@ -17,10 +17,10 @@ import androidx.core.view.ViewCompat
import androidx.preference.EditTextPreference
import androidx.preference.Preference
import androidx.preference.PreferenceDataStore
import androidx.preference.PreferenceFragmentCompat
import com.github.shadowsocks.plugin.Empty
import com.github.shadowsocks.plugin.fragment.AlertDialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.takisoft.preferencex.PreferenceFragmentCompat
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore
@ -308,7 +308,7 @@ class RouteSettingsActivity(
lateinit var activity: RouteSettingsActivity
override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.preferenceDataStore = DataStore.profileCacheStore
activity.apply {
createPreferences(savedInstanceState, rootKey)

View File

@ -7,13 +7,8 @@ import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import androidx.core.app.ActivityCompat
import androidx.preference.EditTextPreference
import androidx.preference.MultiSelectListPreference
import androidx.preference.Preference
import androidx.preference.SwitchPreference
import androidx.preference.*
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.takisoft.preferencex.PreferenceFragmentCompat
import com.takisoft.preferencex.SimpleMenuPreference
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.SagerNet
@ -25,8 +20,8 @@ import io.nekohasekai.sagernet.widget.AppListPreference
import moe.matsuri.nb4a.Protocols
import moe.matsuri.nb4a.ui.ColorPickerPreference
import moe.matsuri.nb4a.ui.LongClickMenuPreference
import moe.matsuri.nb4a.ui.LongClickSwitchPreference
import moe.matsuri.nb4a.ui.MTUPreference
import moe.matsuri.nb4a.ui.SimpleMenuPreference
class SettingsPreferenceFragment : PreferenceFragmentCompat() {
@ -44,7 +39,7 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
true
}
override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.preferenceDataStore = DataStore.configurationStore
DataStore.initGlobal()
addPreferencesFromResource(R.xml.global_preferences)

View File

@ -13,11 +13,11 @@ import androidx.activity.result.component1
import androidx.activity.result.component2
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.isVisible
import androidx.preference.PreferenceFragmentCompat
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.takisoft.preferencex.PreferenceFragmentCompat
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.ProfileManager

View File

@ -2,14 +2,14 @@ package io.nekohasekai.sagernet.ui.profile
import android.os.Bundle
import androidx.preference.EditTextPreference
import com.takisoft.preferencex.PreferenceFragmentCompat
import com.takisoft.preferencex.SimpleMenuPreference
import androidx.preference.PreferenceFragmentCompat
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers
import io.nekohasekai.sagernet.fmt.hysteria.HysteriaBean
import io.nekohasekai.sagernet.ktx.applyDefaultValues
import moe.matsuri.nb4a.ui.SimpleMenuPreference
class HysteriaSettingsActivity : ProfileSettingsActivity<HysteriaBean>() {

View File

@ -2,7 +2,7 @@ package io.nekohasekai.sagernet.ui.profile
import android.os.Bundle
import androidx.preference.EditTextPreference
import com.takisoft.preferencex.PreferenceFragmentCompat
import androidx.preference.PreferenceFragmentCompat
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore

View File

@ -21,12 +21,12 @@ import androidx.core.view.isVisible
import androidx.preference.EditTextPreference
import androidx.preference.Preference
import androidx.preference.PreferenceDataStore
import androidx.preference.PreferenceFragmentCompat
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.input.input
import com.github.shadowsocks.plugin.Empty
import com.github.shadowsocks.plugin.fragment.AlertDialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.takisoft.preferencex.PreferenceFragmentCompat
import io.nekohasekai.sagernet.*
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.GroupManager
@ -213,7 +213,7 @@ abstract class ProfileSettingsActivity<T : AbstractBean>(
lateinit var activity: ProfileSettingsActivity<*>
override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.preferenceDataStore = DataStore.profileCacheStore
activity.apply {
createPreferences(savedInstanceState, rootKey)

View File

@ -2,13 +2,13 @@ package io.nekohasekai.sagernet.ui.profile
import android.os.Bundle
import androidx.preference.EditTextPreference
import com.takisoft.preferencex.PreferenceFragmentCompat
import com.takisoft.preferencex.SimpleMenuPreference
import androidx.preference.PreferenceFragmentCompat
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers
import io.nekohasekai.sagernet.fmt.ssh.SSHBean
import moe.matsuri.nb4a.ui.SimpleMenuPreference
class SSHSettingsActivity : ProfileSettingsActivity<SSHBean>() {

View File

@ -2,7 +2,7 @@ package io.nekohasekai.sagernet.ui.profile
import android.os.Bundle
import androidx.preference.EditTextPreference
import com.takisoft.preferencex.PreferenceFragmentCompat
import androidx.preference.PreferenceFragmentCompat
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers

View File

@ -2,14 +2,13 @@ package io.nekohasekai.sagernet.ui.profile
import android.os.Bundle
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceCategory
import com.takisoft.preferencex.PreferenceFragmentCompat
import com.takisoft.preferencex.SimpleMenuPreference
import androidx.preference.PreferenceFragmentCompat
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers
import io.nekohasekai.sagernet.fmt.socks.SOCKSBean
import moe.matsuri.nb4a.ui.SimpleMenuPreference
class SocksSettingsActivity : ProfileSettingsActivity<SOCKSBean>() {
override fun createEntity() = SOCKSBean()

View File

@ -3,8 +3,7 @@ package io.nekohasekai.sagernet.ui.profile
import android.os.Bundle
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceCategory
import com.takisoft.preferencex.PreferenceFragmentCompat
import com.takisoft.preferencex.SimpleMenuPreference
import androidx.preference.PreferenceFragmentCompat
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers
@ -12,10 +11,10 @@ import io.nekohasekai.sagernet.fmt.http.HttpBean
import io.nekohasekai.sagernet.fmt.trojan.TrojanBean
import io.nekohasekai.sagernet.fmt.v2ray.StandardV2RayBean
import io.nekohasekai.sagernet.fmt.v2ray.VMessBean
import io.nekohasekai.sagernet.ktx.Logs
import moe.matsuri.nb4a.proxy.PreferenceBinding
import moe.matsuri.nb4a.proxy.PreferenceBindingManager
import moe.matsuri.nb4a.proxy.Type
import moe.matsuri.nb4a.ui.SimpleMenuPreference
abstract class StandardV2RaySettingsActivity : ProfileSettingsActivity<StandardV2RayBean>() {

View File

@ -3,14 +3,14 @@ package io.nekohasekai.sagernet.ui.profile
import android.os.Bundle
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceCategory
import com.takisoft.preferencex.PreferenceFragmentCompat
import com.takisoft.preferencex.SimpleMenuPreference
import androidx.preference.PreferenceFragmentCompat
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers
import io.nekohasekai.sagernet.fmt.trojan_go.TrojanGoBean
import io.nekohasekai.sagernet.ktx.app
import moe.matsuri.nb4a.ui.SimpleMenuPreference
class TrojanGoSettingsActivity : ProfileSettingsActivity<TrojanGoBean>() {

View File

@ -1,11 +1,9 @@
package io.nekohasekai.sagernet.ui.profile
import android.os.Bundle
import android.widget.Switch
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import com.takisoft.preferencex.PreferenceFragmentCompat
import com.takisoft.preferencex.SimpleMenuPreference
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore

View File

@ -2,7 +2,7 @@ package io.nekohasekai.sagernet.ui.profile
import android.os.Bundle
import androidx.preference.EditTextPreference
import com.takisoft.preferencex.PreferenceFragmentCompat
import androidx.preference.PreferenceFragmentCompat
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers
import io.nekohasekai.sagernet.fmt.wireguard.WireGuardBean

View File

@ -2,20 +2,14 @@ package io.nekohasekai.sagernet.widget
import android.content.Context
import android.util.AttributeSet
import com.takisoft.preferencex.SimpleMenuPreference
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.SagerDatabase
import moe.matsuri.nb4a.ui.SimpleMenuPreference
class GroupPreference : SimpleMenuPreference {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(
context, attrs, defStyle
)
constructor(
context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes)
class GroupPreference
@JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyle: Int = R.attr.dropdownPreferenceStyle
) : SimpleMenuPreference(context, attrs, defStyle, 0) {
init {
val groups = SagerDatabase.groupDao.allGroups()

View File

@ -3,26 +3,26 @@ package io.nekohasekai.sagernet.widget
import android.content.Context
import android.net.Uri
import android.util.AttributeSet
import androidx.core.content.res.TypedArrayUtils
import androidx.core.widget.addTextChangedListener
import androidx.preference.EditTextPreference
import com.google.android.material.textfield.TextInputLayout
import com.takisoft.preferencex.EditTextPreference
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.ktx.app
import io.nekohasekai.sagernet.ktx.readableMessage
import okhttp3.HttpUrl.Companion.toHttpUrl
class LinkOrContentPreference : EditTextPreference {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context, attrs, defStyleAttr
)
class LinkOrContentPreference
@JvmOverloads
constructor(
context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes)
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = TypedArrayUtils.getAttr(
context, R.attr.editTextPreferenceStyle,
android.R.attr.editTextPreferenceStyle
),
defStyleRes: Int = 0
) : EditTextPreference(context, attrs, defStyleAttr, defStyleRes) {
init {
dialogLayoutResource = R.layout.layout_link_dialog

View File

@ -2,50 +2,28 @@ package io.nekohasekai.sagernet.widget
import android.content.Context
import android.util.AttributeSet
import androidx.core.content.res.TypedArrayUtils
import androidx.core.widget.addTextChangedListener
import androidx.preference.EditTextPreference
import com.google.android.material.textfield.TextInputLayout
import com.takisoft.preferencex.EditTextPreference
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.ktx.app
import io.nekohasekai.sagernet.ktx.readableMessage
import okhttp3.HttpUrl.Companion.toHttpUrl
class LinkPreference : EditTextPreference {
var defaultValue: String? = null
constructor(context: Context) : this(context, null)
class LinkPreference
@JvmOverloads
constructor(
context: Context,
attrs: AttributeSet?,
) : this(context, attrs, com.takisoft.preferencex.R.attr.editTextPreferenceStyle)
attrs: AttributeSet? = null,
defStyleAttr: Int = TypedArrayUtils.getAttr(
context, R.attr.editTextPreferenceStyle,
android.R.attr.editTextPreferenceStyle
),
defStyleRes: Int = 0
) : EditTextPreference(context, attrs, defStyleAttr, defStyleRes) {
constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int,
) : this(context, attrs, defStyleAttr, 0)
constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int,
) : super(context, attrs, defStyleAttr, defStyleRes) {
val a = context.obtainStyledAttributes(
attrs, R.styleable.Preference, defStyleAttr, defStyleRes
)
if (a.hasValue(androidx.preference.R.styleable.Preference_defaultValue)) {
defaultValue = onGetDefaultValue(
a, androidx.preference.R.styleable.Preference_defaultValue
)?.toString()
} else if (a.hasValue(androidx.preference.R.styleable.Preference_android_defaultValue)) {
defaultValue = onGetDefaultValue(
a, androidx.preference.R.styleable.Preference_android_defaultValue
)?.toString()
}
}
// var defaultValue: String? = null
init {
dialogLayoutResource = R.layout.layout_link_dialog
@ -79,7 +57,7 @@ class LinkPreference : EditTextPreference {
setOnPreferenceChangeListener { _, newValue ->
if ((newValue as String).isBlank()) {
text = defaultValue
// text = defaultValue
false
} else try {
newValue.toHttpUrl()

View File

@ -2,27 +2,15 @@ package io.nekohasekai.sagernet.widget
import android.content.Context
import android.util.AttributeSet
import com.takisoft.preferencex.SimpleMenuPreference
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.ProfileManager
import moe.matsuri.nb4a.ui.SimpleMenuPreference
class OutboundPreference : SimpleMenuPreference {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(
context,
attrs,
defStyle
)
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes)
class OutboundPreference
@JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyle: Int = R.attr.dropdownPreferenceStyle
) : SimpleMenuPreference(context, attrs, defStyle, 0) {
init {
setEntries(R.array.outbound_entry)

View File

@ -2,20 +2,17 @@ package io.nekohasekai.sagernet.widget
import android.content.Context
import android.util.AttributeSet
import com.takisoft.preferencex.EditTextPreference
import androidx.core.content.res.TypedArrayUtils
import androidx.preference.EditTextPreference
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.ktx.USER_AGENT
class UserAgentPreference : EditTextPreference {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(
context, attrs, defStyle
class UserAgentPreference
@JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyle: Int = TypedArrayUtils.getAttr(
context, R.attr.editTextPreferenceStyle, android.R.attr.editTextPreferenceStyle
)
constructor(
context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes)
) : EditTextPreference(context, attrs, defStyle) {
public override fun notifyChanged() {
super.notifyChanged()

View File

@ -8,7 +8,6 @@ import android.net.Uri
import android.os.Build
import android.widget.Toast
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.plugin.PluginManager.loadString
import io.nekohasekai.sagernet.utils.PackageCache

View File

@ -1,9 +1,7 @@
package moe.matsuri.nb4a.proxy
import androidx.preference.Preference
import com.takisoft.preferencex.EditTextPreference
import com.takisoft.preferencex.PreferenceFragmentCompat
import com.takisoft.preferencex.SimpleMenuPreference
import androidx.preference.PreferenceFragmentCompat
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.ktx.Logs
import io.nekohasekai.sagernet.ktx.readableMessage

View File

@ -1,6 +1,7 @@
package moe.matsuri.nb4a.proxy
import com.takisoft.preferencex.PreferenceFragmentCompat
import androidx.preference.PreferenceFragmentCompat
class PreferenceBindingManager {
val items = mutableListOf<PreferenceBinding>()

View File

@ -1,10 +1,10 @@
package moe.matsuri.nb4a.proxy.config
import android.os.Bundle
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceDataStore
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import com.takisoft.preferencex.EditTextPreference
import com.takisoft.preferencex.PreferenceFragmentCompat
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore

View File

@ -5,7 +5,6 @@ import android.webkit.*
import android.widget.Toast
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import com.takisoft.preferencex.SimpleMenuPreference
import io.nekohasekai.sagernet.BuildConfig
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.database.DataStore
@ -14,6 +13,7 @@ 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

View File

@ -1,17 +1,13 @@
package moe.matsuri.nb4a.proxy.neko
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreference
import com.takisoft.preferencex.EditTextPreference
import com.takisoft.preferencex.PreferenceCategory
import com.takisoft.preferencex.SimpleMenuPreference
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
@ -37,14 +33,24 @@ object NekoPreferenceInflater {
"EditTextPreference" -> {
p = EditTextPreference(context).apply {
when (any.getStr("summaryProvider")) {
null -> summaryProvider = androidx.preference.EditTextPreference.SimpleSummaryProvider.getInstance()
"PasswordSummaryProvider" -> summaryProvider = ProfileSettingsActivity.PasswordSummaryProvider
null -> summaryProvider =
EditTextPreference.SimpleSummaryProvider.getInstance()
"PasswordSummaryProvider" -> summaryProvider =
ProfileSettingsActivity.PasswordSummaryProvider
}
when (any.getStr("EditTextPreferenceModifiers")) {
"Monospace" -> onBindEditTextListener = EditTextPreferenceModifiers.Monospace
"Hosts" -> onBindEditTextListener = EditTextPreferenceModifiers.Hosts
"Port" -> onBindEditTextListener = EditTextPreferenceModifiers.Port
"Number" -> onBindEditTextListener = EditTextPreferenceModifiers.Number
"Monospace" -> setOnBindEditTextListener(
EditTextPreferenceModifiers.Monospace
)
"Hosts" -> setOnBindEditTextListener(
EditTextPreferenceModifiers.Hosts
)
"Port" -> setOnBindEditTextListener(
EditTextPreferenceModifiers.Port
)
"Number" -> setOnBindEditTextListener(
EditTextPreferenceModifiers.Number
)
}
}
}
@ -53,7 +59,6 @@ object NekoPreferenceInflater {
}
"SimpleMenuPreference" -> {
p = SimpleMenuPreference(context).apply {
summaryProvider = androidx.preference.ListPreference.SimpleSummaryProvider.getInstance()
val entries = any.optJSONObject("entries")
if (entries != null) setMenu(this, entries)
}
@ -85,8 +90,8 @@ object NekoPreferenceInflater {
menuEntries.add(b as String)
}
entries.apply {
p.setEntries(menuEntries.toTypedArray())
p.setEntryValues(menuEntryValues.toTypedArray())
p.entries = menuEntries.toTypedArray()
p.entryValues = menuEntryValues.toTypedArray()
}
}
}

View File

@ -4,7 +4,7 @@ import android.os.Bundle
import android.view.View
import androidx.core.view.isVisible
import androidx.preference.PreferenceDataStore
import com.takisoft.preferencex.PreferenceFragmentCompat
import androidx.preference.PreferenceFragmentCompat
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore

View File

@ -4,18 +4,12 @@ import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.preference.PreferenceViewHolder
import com.takisoft.preferencex.SimpleMenuPreference
import io.nekohasekai.sagernet.R
class LongClickMenuPreference
@JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = com.takisoft.preferencex.simplemenu.R.attr.simpleMenuPreferenceStyle,
defStyleRes: Int = R.style.Preference_SimpleMenuPreference
) : SimpleMenuPreference(
context, attrs, defStyleAttr, defStyleRes
) {
context: Context, attrs: AttributeSet? = null, defStyle: Int = R.attr.dropdownPreferenceStyle
) : SimpleMenuPreference(context, attrs, defStyle, 0) {
private var mLongClickListener: View.OnLongClickListener? = null
override fun onBindViewHolder(holder: PreferenceViewHolder) {

View File

@ -5,21 +5,15 @@ import android.util.AttributeSet
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import androidx.core.content.res.TypedArrayUtils
import androidx.preference.PreferenceViewHolder
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.takisoft.preferencex.SimpleMenuPreference
class MTUPreference : SimpleMenuPreference {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context, attrs, defStyleAttr
)
constructor(
context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes)
import io.nekohasekai.sagernet.R
class MTUPreference
@JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyle: Int = R.attr.dropdownPreferenceStyle
) : SimpleMenuPreference(context, attrs, defStyle, 0) {
init {
setSummaryProvider {
value.toString()

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package moe.matsuri.nb4a.ui
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Spinner
import androidx.core.content.ContextCompat
import androidx.preference.DropDownPreference
import androidx.preference.PreferenceViewHolder
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.ktx.getColorAttr
/**
* Bend [DropDownPreference] to support
* [Simple Menus](https://material.google.com/components/menus.html#menus-behavior).
*/
open class SimpleMenuPreference
@JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null,
defStyleAttr: Int = androidx.preference.R.attr.dropdownPreferenceStyle,
defStyleRes: Int = 0
) : DropDownPreference(context!!, attrs, defStyleAttr, defStyleRes) {
private lateinit var mAdapter: SimpleMenuAdapter
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
val mSpinner = holder.itemView.findViewById<Spinner>(R.id.spinner)
mSpinner.layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT
}
override fun createAdapter(): ArrayAdapter<CharSequence?> {
mAdapter = SimpleMenuAdapter(getContext(), R.layout.simple_menu_dropdown_item)
return mAdapter
}
override fun setValue(value: String?) {
super.setValue(value)
if (::mAdapter.isInitialized) {
mAdapter.currentPosition = entryValues.indexOf(value)
mAdapter.notifyDataSetChanged()
}
}
private class SimpleMenuAdapter(context: Context, resource: Int) :
ArrayAdapter<CharSequence?>(context, resource) {
var currentPosition = -1
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
val view: View = super.getDropDownView(position, convertView, parent)
if (position == currentPosition) {
view.setBackgroundColor(context.getColorAttr(R.attr.colorMaterial100))
} else {
view.setBackgroundColor(
ContextCompat.getColor(
context,
R.color.preference_simple_menu_background
)
)
}
return view
}
}
}

View File

@ -2,13 +2,7 @@ package moe.matsuri.nb4a.utils
import android.annotation.SuppressLint
import android.util.Base64
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonParseException
import java.io.ByteArrayOutputStream
import java.lang.reflect.Type
import java.text.NumberFormat
import java.text.SimpleDateFormat
import java.util.*
import java.util.zip.Deflater

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"

View File

@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"

View File

@ -0,0 +1,25 @@
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
style="?android:attr/spinnerDropDownItemStyle"
android:layout_width="match_parent"
android:layout_height="48dp"
android:ellipsize="marquee"
android:gravity="center"
android:minWidth="112dp"
android:paddingEnd="16dp"
android:paddingStart="16dp"
android:singleLine="true" />

View File

@ -1,5 +1,4 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/uninstall_all"
android:title="Uninstall All Plugins" />

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_clear_traffic_statistics"
android:title="@string/clear_traffic_statistics" />

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="fab_color_progress">@color/material_light_white</color>
<color name="preference_simple_menu_background">#303030</color>
</resources>

View File

@ -288,15 +288,6 @@
<color name="color_ng_black_accent">#9E9E9E</color>
<color name="color_ng_black_primary">#2B2B2B</color>
<!-- <color name="colorPrimary">#607D8B</color>-->
<!-- <color name="colorPrimary_dark">#455A64</color>-->
<!-- <color name="colorPrimary_light">#CFD8DC</color>-->
<!-- <color name="accent">#</color>-->
<!-- <color name="colorPrimary_text">#212121</color>-->
<!-- <color name="secondary_text">#757575</color>-->
<!-- <color name="icons">#FFFFFF</color>-->
<!-- <color name="divider">#BDBDBD</color>-->
<!-- <color name="colorPing">#185534</color>-->
<!-- <color name="colorSubscription">#247BA0</color>-->
<color name="preference_simple_menu_background">#FAFAFA</color>
</resources>

View File

@ -1,4 +1,4 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<resources>
<dimen name="qrcode_size">264dp</dimen>
<dimen name="main_list_padding_bottom">88dp</dimen>
<dimen name="bottom_sheet_padding">8dp</dimen>

View File

@ -502,5 +502,7 @@ Anyone can write advanced plugins, which can control NekoBox. please download an
<string name="ads">Ads</string>
<string name="bypass_lan_in_core">Bypass LAN in Core</string>
<string name="need_restart">Restart APP to apply changes</string>
<string name="use_selector">Use selector</string>
<string name="front_proxy">Front proxy</string>
</resources>

View File

@ -1,4 +1,4 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<resources>
<!-- Base application theme. -->
<style name="Theme.SagerNet" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="actionBarStyle">@style/Widget.MaterialComponents.ActionBar.Solid</item>

View File

@ -6,7 +6,7 @@
app:title="@string/profile_name"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="0"
app:entries="@array/balancer_type"
app:entryValues="@array/int_array_2"
@ -15,7 +15,7 @@
app:title="@string/balancer_type"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:entries="@array/balancer_strategy_entry"
app:entryValues="@array/balancer_strategy_value"
app:icon="@drawable/ic_baseline_compare_arrows_24"

View File

@ -1,5 +1,4 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<EditTextPreference
app:icon="@drawable/ic_social_emoji_symbols"

View File

@ -10,7 +10,7 @@
android:title="@string/theme"
app:icon="@drawable/ic_baseline_color_lens_24"
app:key="appTheme" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="0"
app:entries="@array/night_mode"
app:entryValues="@array/int_array_4"
@ -18,7 +18,7 @@
app:key="nightTheme"
app:title="@string/night_mode"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="vpn"
app:entries="@array/service_modes"
app:entryValues="@array/service_mode_values"
@ -26,7 +26,7 @@
app:key="serviceMode"
app:title="@string/service_mode"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="1"
app:entries="@array/tun_implementation"
app:entryValues="@array/int_array_2"
@ -46,7 +46,7 @@
app:key="profileTrafficStatistics"
app:summary="@string/profile_traffic_statistics_summary"
app:title="@string/profile_traffic_statistics" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="1000"
app:entries="@array/notification_entry"
app:entryValues="@array/notification_value"
@ -104,7 +104,7 @@
app:key="resolveDestination"
app:summary="@string/resolve_destination_summary"
app:title="@string/resolve_destination" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="0"
app:entries="@array/ipv6_mode"
app:entryValues="@array/int_array_4"
@ -112,7 +112,7 @@
app:key="ipv6Mode"
app:title="@string/ipv6"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="0"
app:entries="@array/rules_dat_provider"
app:entryValues="@array/int_array_2"
@ -197,7 +197,7 @@
app:key="transproxyPort"
app:title="@string/port_transproxy"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="0"
app:entries="@array/transproxy_mode"
app:entryValues="@array/int_array_2"
@ -234,7 +234,7 @@
app:key="tcpKeepAliveInterval"
app:title="@string/tcp_keep_alive_interval"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="1.2"
app:entries="@array/app_tls_version"
app:entryValues="@array/app_tls_version"

View File

@ -6,7 +6,7 @@
app:title="@string/group_name"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="0"
app:entries="@array/group_types"
app:entryValues="@array/int_array_2"
@ -14,13 +14,16 @@
app:key="groupType"
app:title="@string/group_type"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
app:defaultValue="0"
app:entries="@array/group_orders"
app:entryValues="@array/int_array_3"
app:icon="@drawable/ic_baseline_low_priority_24"
app:key="groupOrder"
app:title="@string/group_order"
<SwitchPreference
app:icon="@drawable/ic_baseline_manage_search_24"
app:key="groupIsSelector"
app:title="@string/use_selector" />
<io.nekohasekai.sagernet.widget.OutboundPreference
app:icon="@drawable/ic_hardware_router"
app:key="routeOutbound"
app:title="@string/front_proxy"
app:useSimpleSummaryProvider="true" />
<PreferenceCategory

View File

@ -24,7 +24,7 @@
app:key="serverObfs"
app:title="@string/hysteria_obfs"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:entries="@array/hysteria_auth_type"
app:entryValues="@array/int_array_3"
app:icon="@drawable/ic_baseline_compare_arrows_24"
@ -36,7 +36,7 @@
app:icon="@drawable/ic_settings_password"
app:key="serverPassword"
app:title="@string/hysteria_auth_payload" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="https"
app:entries="@array/hysteria_protocol"
app:entryValues="@array/int_array_3"

View File

@ -29,7 +29,7 @@
app:icon="@drawable/ic_settings_password"
app:key="serverPassword"
app:title="@string/password_opt" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
android:layout_height="match_parent"
app:defaultValue="https"
app:entries="@array/naive_proto_entry"

View File

@ -1,5 +1,4 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<EditTextPreference
app:icon="@drawable/ic_social_emoji_symbols"

View File

@ -33,7 +33,7 @@
app:key="routeSourcePort"
app:title="sourcePort"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:entries="@array/route_protocol_entry"
app:entryValues="@array/route_protocol_value"
app:icon="@drawable/ic_baseline_compare_arrows_24"

View File

@ -17,7 +17,7 @@
app:key="serverPort"
app:title="@string/server_port"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:entries="@array/ss_enc_method_value"
app:entryValues="@array/ss_enc_method_value"
app:icon="@drawable/ic_notification_enhanced_encryption"
@ -32,7 +32,7 @@
</PreferenceCategory>
<PreferenceCategory app:title="@string/plugin">
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue=""
app:entries="@array/box_shadowsocks_plugins"
app:entryValues="@array/box_shadowsocks_plugins"

View File

@ -7,7 +7,7 @@
app:useSimpleSummaryProvider="true" />
<PreferenceCategory app:title="@string/proxy_cat">
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="2"
app:entries="@array/socks_versions"
app:entryValues="@array/int_array_3"

View File

@ -24,7 +24,7 @@
app:title="@string/username"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:entries="@array/ssh_auth_type"
app:entryValues="@array/int_array_3"
app:icon="@drawable/ic_baseline_compare_arrows_24"

View File

@ -38,21 +38,21 @@
app:key="alterId"
app:title="@string/alter_id"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:entries="@array/vmess_encryption_value"
app:entryValues="@array/vmess_encryption_value"
app:icon="@drawable/ic_notification_enhanced_encryption"
app:key="encryption"
app:title="@string/encryption"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:entries="@array/packet_encoding_entry"
app:entryValues="@array/int_array_3"
app:icon="@drawable/baseline_widgets_24"
app:key="packetEncoding"
app:title="@string/packet_encoding"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:entries="@array/networks_value"
app:entryValues="@array/networks_value"
app:icon="@drawable/ic_baseline_compare_arrows_24"
@ -69,7 +69,7 @@
app:key="path"
app:title="@string/http_path"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:entries="@array/transport_layer_encryption_value"
app:entryValues="@array/transport_layer_encryption_value"
app:icon="@drawable/ic_baseline_layers_24"
@ -118,7 +118,7 @@
app:key="allowInsecure"
app:summary="@string/allow_insecure_sum"
app:title="@string/allow_insecure" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue=""
app:entries="@array/utls_fingerprint_entry"
app:entryValues="@array/utls_fingerprint_entry"

View File

@ -33,7 +33,7 @@
app:key="serverAllowInsecure"
app:summary="@string/allow_insecure_sum"
app:title="@string/allow_insecure" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="none"
app:entries="@array/trojan_go_networks_entry"
app:entryValues="@array/trojan_go_networks_value"
@ -41,7 +41,7 @@
app:key="serverNetwork"
app:title="@string/network"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:entries="@array/trojan_go_security_entry"
app:entryValues="@array/trojan_go_security_value"
app:icon="@drawable/ic_baseline_layers_24"
@ -69,7 +69,7 @@
app:key="serverSsCategory"
app:title="@string/ss_cat">
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="AES-128-GCM"
app:entries="@array/trojan_go_methods"
app:entryValues="@array/trojan_go_methods"

View File

@ -33,7 +33,7 @@
app:key="serverCertificates"
app:title="@string/certificates"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="https"
app:entries="@array/tuic_udp_relay_mode_entry"
app:entryValues="@array/tuic_udp_relay_mode_value"
@ -41,7 +41,7 @@
app:key="serverUDPRelayMode"
app:title="@string/tuic_udp_relay_mode"
app:useSimpleSummaryProvider="true" />
<com.takisoft.preferencex.SimpleMenuPreference
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="https"
app:entries="@array/tuic_congestion_controller_entry"
app:entryValues="@array/tuic_congestion_controller_value"

View File

@ -5,6 +5,8 @@ set -e
#### Download assets
bash buildScript/lib/assets.sh
exit
#### Download "external" from Internet
rm -rf external
mkdir -p external

View File

@ -86,10 +86,10 @@ fun Project.requireTargetAbi(): String {
fun Project.setupCommon() {
android.apply {
buildToolsVersion("30.0.3")
compileSdkVersion(32)
compileSdkVersion(33)
defaultConfig {
minSdk = 21
targetSdk = 32
targetSdk = 33
}
buildTypes {
getByName("release") {

View File

@ -1,5 +1,2 @@
include(":external:preferencex:preferencex")
include(":external:preferencex:preferencex-simplemenu")
include(":app")
rootProject.name = "NB4A"