Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Build NekoBox Android Release

on:
schedule:
- cron: '0 0 * * 0'
workflow_dispatch:
inputs:
ref:
description: "Git tag/branch to build (a tagged release is recommended, main can be unstable)"
required: false
default: "1.4.2"

jobs:
libcore:
name: Native Build (LibCore)
runs-on: ubuntu-latest
steps:
# مستقیماً از مخزن رسمی و روی یک تگ ریلیز مشخص چک‌اوت می‌شود
# (فورک شما تگ‌های آپ‌استریم را به‌صورت خودکار sync نمی‌کند)
- name: Checkout
uses: actions/checkout@v4
with:
repository: MatsuriDayo/NekoBoxForAndroid
ref: ${{ github.event.inputs.ref || '1.4.2' }}

- name: Golang Status
run: find buildScript libcore/*.sh | xargs cat | sha1sum > golang_status

- name: Libcore Status
run: git ls-files libcore | xargs cat | sha1sum > libcore_status

- name: LibCore Cache
id: cache
uses: actions/cache@v4
with:
path: app/libs/libcore.aar
key: ${{ hashFiles('.github/workflows/*', 'golang_status', 'libcore_status') }}

- name: Install Golang
if: steps.cache.outputs.cache-hit != 'true'
uses: actions/setup-go@v5
with:
go-version: ^1.25

# ساخت libcore.aar باید فقط از طریق دیسپچر رسمی پروژه انجام شود؛
# این اسکریپت خودش gomobile و ابزارهای لازم را نصب/تنظیم می‌کند.
- name: Native Build
if: steps.cache.outputs.cache-hit != 'true'
run: ./run lib core

build:
name: Build Release APK
runs-on: ubuntu-latest
needs: libcore
steps:
- name: Checkout
uses: actions/checkout@v4
with:
repository: MatsuriDayo/NekoBoxForAndroid
ref: ${{ github.event.inputs.ref || '1.4.2' }}

- name: Golang Status
run: find buildScript libcore/*.sh | xargs cat | sha1sum > golang_status

- name: Libcore Status
run: git ls-files libcore | xargs cat | sha1sum > libcore_status

- name: LibCore Cache
uses: actions/cache@v4
with:
path: app/libs/libcore.aar
key: ${{ hashFiles('.github/workflows/*', 'golang_status', 'libcore_status') }}

- name: Gradle cache
uses: actions/cache@v4
with:
path: ~/.gradle
key: gradle-oss-${{ hashFiles('**/*.gradle.kts') }}

- name: Gradle Build
env:
BUILD_PLUGIN: none
run: |
echo "sdk.dir=${ANDROID_HOME}" > local.properties
echo "ndk.dir=${ANDROID_HOME}/ndk/25.0.8775105" >> local.properties

# آماده‌سازی محیط گریدل توسط اسکریپت رسمی پروژه
./run init action gradle

chmod +x gradlew
# فقط flavor عمومی "oss" ساخته می‌شود، نه هر سه flavor (oss/fdroid/play)
./gradlew app:assembleOssRelease --no-daemon

- name: Upload Release APK Artifact
uses: actions/upload-artifact@v4
with:
name: NekoBox-Release-APK
path: app/build/outputs/apk/**/*.apk
retention-days: 14
31 changes: 13 additions & 18 deletions app/src/main/java/io/nekohasekai/sagernet/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ package io.nekohasekai.sagernet
const val CONNECTION_TEST_URL = "http://cp.cloudflare.com/"

object Key {

const val DB_PUBLIC = "configuration.db"
const val DB_PROFILE = "sager_net.db"

const val PERSIST_ACROSS_REBOOT = "isAutoConnect"

const val APP_EXPERT = "isExpert"
const val APP_THEME = "appTheme"
const val NIGHT_THEME = "nightTheme"

const val SERVICE_MODE = "serviceMode"
const val MODE_VPN = "vpn"
const val MODE_PROXY = "proxy"
Expand All @@ -38,29 +37,30 @@ object Key {

const val MIXED_PORT = "mixedPort"
const val ALLOW_ACCESS = "allowAccess"

const val SPEED_INTERVAL = "speedInterval"
const val SHOW_DIRECT_SPEED = "showDirectSpeed"

const val APPEND_HTTP_PROXY = "appendHttpProxy"

const val CONNECTION_TEST_URL = "connectionTestURL"

const val NETWORK_CHANGE_RESET_CONNECTIONS = "networkChangeResetConnections"
const val WAKE_RESET_CONNECTIONS = "wakeResetConnections"

const val RULES_PROVIDER = "rulesProvider"

const val LOG_LEVEL = "logLevel"
const val LOG_BUF_SIZE = "logBufSize"

const val MTU = "mtu"

const val ALWAYS_SHOW_ADDRESS = "alwaysShowAddress"

// Protocol Settings
const val GLOBAL_ALLOW_INSECURE = "globalAllowInsecure"

const val ACQUIRE_WAKE_LOCK = "acquireWakeLock"
const val SHOW_BOTTOM_BAR = "showBottomBar"

const val ALLOW_INSECURE_ON_REQUEST = "allowInsecureOnRequest"

const val TUN_IMPLEMENTATION = "tunImplementation"
const val PROFILE_TRAFFIC_STATISTICS = "profileTrafficStatistics"

Expand All @@ -76,12 +76,9 @@ object Key {
const val SERVER_PASSWORD = "serverPassword"
const val SERVER_METHOD = "serverMethod"
const val SERVER_PASSWORD1 = "serverPassword1"

const val PROTOCOL_VERSION = "protocolVersion"

const val SERVER_PROTOCOL = "serverProtocol"
const val SERVER_OBFS = "serverObfs"

const val SERVER_NETWORK = "serverNetwork"
const val SERVER_HOST = "serverHost"
const val SERVER_PATH = "serverPath"
Expand All @@ -90,7 +87,6 @@ object Key {
const val SERVER_ALPN = "serverALPN"
const val SERVER_CERTIFICATES = "serverCertificates"
const val SERVER_MTU = "serverMTU"

const val SERVER_CONFIG = "serverConfig"
const val SERVER_CUSTOM = "serverCustom"
const val SERVER_CUSTOM_OUTBOUND = "serverCustomOutbound"
Expand All @@ -102,18 +98,15 @@ object Key {
const val SERVER_SS_CATEGORY = "serverSsCategory"
const val SERVER_HEADERS = "serverHeaders"
const val SERVER_ALLOW_INSECURE = "serverAllowInsecure"

const val SERVER_AUTH_TYPE = "serverAuthType"
const val SERVER_UPLOAD_SPEED = "serverUploadSpeed"
const val SERVER_DOWNLOAD_SPEED = "serverDownloadSpeed"
const val SERVER_STREAM_RECEIVE_WINDOW = "serverStreamReceiveWindow"
const val SERVER_CONNECTION_RECEIVE_WINDOW = "serverConnectionReceiveWindow"
const val SERVER_DISABLE_MTU_DISCOVERY = "serverDisableMtuDiscovery"
const val SERVER_HOP_INTERVAL = "hopInterval"

const val SERVER_PRIVATE_KEY = "serverPrivateKey"
const val SERVER_INSECURE_CONCURRENCY = "serverInsecureConcurrency"

const val SERVER_UDP_RELAY_MODE = "serverUDPRelayMode"
const val SERVER_CONGESTION_CONTROLLER = "serverCongestionController"
const val SERVER_DISABLE_SNI = "serverDisableSNI"
Expand All @@ -136,8 +129,8 @@ object Key {
const val GROUP_IS_SELECTOR = "groupIsSelector"
const val GROUP_FRONT_PROXY = "groupFrontProxy"
const val GROUP_LANDING_PROXY = "groupLandingProxy"

const val GROUP_SUBSCRIPTION = "groupSubscription"

const val SUBSCRIPTION_LINK = "subscriptionLink"
const val SUBSCRIPTION_FORCE_RESOLVE = "subscriptionForceResolve"
const val SUBSCRIPTION_DEDUPLICATION = "subscriptionDeduplication"
Expand All @@ -147,10 +140,13 @@ object Key {
const val SUBSCRIPTION_AUTO_UPDATE = "subscriptionAutoUpdate"
const val SUBSCRIPTION_AUTO_UPDATE_DELAY = "subscriptionAutoUpdateDelay"

//

const val APP_TLS_VERSION = "appTLSVersion"
// const val APP_TLS_VERSION = "appTLSVersion"
const val ENABLE_CLASH_API = "enableClashAPI"

// ==================== جدید: کلیدهای تنظیمات دکوریشن ====================
const val ENABLE_FLAG_EMOJI = "enableFlagEmoji"
const val ENABLE_GEMINI_MARKER = "enableGeminiMarker"
// ====================================================================
}

object TunImplementation {
Expand Down Expand Up @@ -181,7 +177,6 @@ object Action {
const val SERVICE = "io.nekohasekai.sagernet.SERVICE"
const val CLOSE = "io.nekohasekai.sagernet.CLOSE"
const val RELOAD = "io.nekohasekai.sagernet.RELOAD"

// const val SWITCH_WAKE_LOCK = "io.nekohasekai.sagernet.SWITCH_WAKELOCK"
const val RESET_UPSTREAM_CONNECTIONS = "moe.nb4a.RESET_UPSTREAM_CONNECTIONS"
}
41 changes: 10 additions & 31 deletions app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,15 @@ object DataStore : OnPreferenceDataStoreChangeListener {

// last used, but may not be running
var currentProfile by configurationStore.long(Key.PROFILE_CURRENT)

var selectedProxy by configurationStore.long(Key.PROFILE_ID)
var selectedGroup by configurationStore.long(Key.PROFILE_GROUP) { currentGroupId() } // "ungrouped" group id = 1
var selectedGroup by configurationStore.long(Key.PROFILE_GROUP) { currentGroupId() }

// "ungrouped" group id = 1
// only in bg process
var vpnService: VpnService? = null
var baseService: BaseService.Interface? = null

// main

var runningTest = false

fun currentGroupId(): Long {
Expand Down Expand Up @@ -87,44 +86,34 @@ object DataStore : OnPreferenceDataStoreChangeListener {
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)

var allowInsecureOnRequest by configurationStore.boolean(Key.ALLOW_INSECURE_ON_REQUEST)
var networkChangeResetConnections by configurationStore.boolean(Key.NETWORK_CHANGE_RESET_CONNECTIONS) { true }
var wakeResetConnections by configurationStore.boolean(Key.WAKE_RESET_CONNECTIONS)

//

var isExpert by configurationStore.boolean(Key.APP_EXPERT)
// var isExpert by configurationStore.boolean(Key.APP_EXPERT)
var appTheme by configurationStore.int(Key.APP_THEME)
var nightTheme by configurationStore.stringToInt(Key.NIGHT_THEME)
var serviceMode by configurationStore.string(Key.SERVICE_MODE) { Key.MODE_VPN }

var trafficSniffing by configurationStore.stringToInt(Key.TRAFFIC_SNIFFING) { 1 }
var resolveDestination by configurationStore.boolean(Key.RESOLVE_DESTINATION)

var mtu by configurationStore.stringToInt(Key.MTU) { 9000 }

var bypassLan by configurationStore.boolean(Key.BYPASS_LAN)
var bypassLanInCore by configurationStore.boolean(Key.BYPASS_LAN_IN_CORE)

var allowAccess by configurationStore.boolean(Key.ALLOW_ACCESS)
var speedInterval by configurationStore.stringToInt(Key.SPEED_INTERVAL)
var showGroupInNotification by configurationStore.boolean("showGroupInNotification")

var globalCustomConfig by configurationStore.string(Key.GLOBAL_CUSTOM_CONFIG) { "" }

var remoteDns by configurationStore.string(Key.REMOTE_DNS) { "https://dns.google/dns-query" }
var directDns by configurationStore.string(Key.DIRECT_DNS) { "https://223.5.5.5/dns-query" }
var enableDnsRouting by configurationStore.boolean(Key.ENABLE_DNS_ROUTING) { true }
var enableFakeDns by configurationStore.boolean(Key.ENABLE_FAKEDNS) { true }

var rulesProvider by configurationStore.stringToInt(Key.RULES_PROVIDER)
var logLevel by configurationStore.stringToInt(Key.LOG_LEVEL)
var logBufSize by configurationStore.int(Key.LOG_BUF_SIZE) { 0 }
var acquireWakeLock by configurationStore.boolean(Key.ACQUIRE_WAKE_LOCK)

// hopefully hashCode = mHandle doesn't change, currently this is true from KitKat to Nougat
private val userIndex by lazy { Binder.getCallingUserHandle().hashCode() }

var mixedPort: Int
get() = getLocalPort(Key.MIXED_PORT, 2080)
set(value) = saveLocalPort(Key.MIXED_PORT, value)
Expand All @@ -135,7 +124,6 @@ object DataStore : OnPreferenceDataStoreChangeListener {
}
}


private fun getLocalPort(key: String, default: Int): Int {
return parsePort(configurationStore.getString(key), default + userIndex)
}
Expand All @@ -145,31 +133,24 @@ object DataStore : OnPreferenceDataStoreChangeListener {
}

var ipv6Mode by configurationStore.stringToInt(Key.IPV6_MODE) { IPv6Mode.DISABLE }

var meteredNetwork by configurationStore.boolean(Key.METERED_NETWORK)
var proxyApps by configurationStore.boolean(Key.PROXY_APPS)
var bypass by configurationStore.boolean(Key.BYPASS_MODE) { true }
var individual by configurationStore.string(Key.INDIVIDUAL)
var showDirectSpeed by configurationStore.boolean(Key.SHOW_DIRECT_SPEED) { true }

val persistAcrossReboot by configurationStore.boolean(Key.PERSIST_ACROSS_REBOOT) { false }

var appendHttpProxy by configurationStore.boolean(Key.APPEND_HTTP_PROXY)
var connectionTestURL by configurationStore.string(Key.CONNECTION_TEST_URL) { CONNECTION_TEST_URL }
var connectionTestConcurrent by configurationStore.int("connectionTestConcurrent") { 5 }
var alwaysShowAddress by configurationStore.boolean(Key.ALWAYS_SHOW_ADDRESS)

var tunImplementation by configurationStore.stringToInt(Key.TUN_IMPLEMENTATION) { TunImplementation.GVISOR }
var profileTrafficStatistics by configurationStore.boolean(Key.PROFILE_TRAFFIC_STATISTICS) { true }

var yacdURL by configurationStore.string("yacdURL") { "http://127.0.0.1:9090/ui" }

// protocol

var globalAllowInsecure by configurationStore.boolean(Key.GLOBAL_ALLOW_INSECURE) { false }

// old cache, DO NOT ADD

var dirty by profileCacheStore.boolean(Key.PROFILE_DIRTY)
var editingId by profileCacheStore.long(Key.PROFILE_ID)
var editingGroup by profileCacheStore.long(Key.PROFILE_GROUP)
Expand All @@ -181,12 +162,9 @@ object DataStore : OnPreferenceDataStoreChangeListener {
var serverPassword by profileCacheStore.string(Key.SERVER_PASSWORD)
var serverPassword1 by profileCacheStore.string(Key.SERVER_PASSWORD1)
var serverMethod by profileCacheStore.string(Key.SERVER_METHOD)

var sharedStorage by profileCacheStore.string("sharedStorage")

var serverProtocol by profileCacheStore.string(Key.SERVER_PROTOCOL)
var serverObfs by profileCacheStore.string(Key.SERVER_OBFS)

var serverNetwork by profileCacheStore.string(Key.SERVER_NETWORK)
var serverHost by profileCacheStore.string(Key.SERVER_HOST)
var serverPath by profileCacheStore.string(Key.SERVER_PATH)
Expand All @@ -197,21 +175,17 @@ object DataStore : OnPreferenceDataStoreChangeListener {
var serverMTU by profileCacheStore.stringToInt(Key.SERVER_MTU)
var serverHeaders by profileCacheStore.string(Key.SERVER_HEADERS)
var serverAllowInsecure by profileCacheStore.boolean(Key.SERVER_ALLOW_INSECURE)

var serverAuthType by profileCacheStore.stringToInt(Key.SERVER_AUTH_TYPE)
var serverUploadSpeed by profileCacheStore.stringToInt(Key.SERVER_UPLOAD_SPEED)
var serverDownloadSpeed by profileCacheStore.stringToInt(Key.SERVER_DOWNLOAD_SPEED)
var serverStreamReceiveWindow by profileCacheStore.stringToIntIfExists(Key.SERVER_STREAM_RECEIVE_WINDOW)
var serverConnectionReceiveWindow by profileCacheStore.stringToIntIfExists(Key.SERVER_CONNECTION_RECEIVE_WINDOW)
var serverDisableMtuDiscovery by profileCacheStore.boolean(Key.SERVER_DISABLE_MTU_DISCOVERY)
var serverHopInterval by profileCacheStore.stringToInt(Key.SERVER_HOP_INTERVAL) { 10 }

var protocolVersion by profileCacheStore.stringToInt(Key.PROTOCOL_VERSION) { 2 } // default is SOCKS5

var serverProtocolInt by profileCacheStore.stringToInt(Key.SERVER_PROTOCOL)
var serverPrivateKey by profileCacheStore.string(Key.SERVER_PRIVATE_KEY)
var serverInsecureConcurrency by profileCacheStore.stringToInt(Key.SERVER_INSECURE_CONCURRENCY)

var serverUDPRelayMode by profileCacheStore.string(Key.SERVER_UDP_RELAY_MODE)
var serverCongestionController by profileCacheStore.string(Key.SERVER_CONGESTION_CONTROLLER)
var serverDisableSNI by profileCacheStore.boolean(Key.SERVER_DISABLE_SNI)
Expand Down Expand Up @@ -253,6 +227,11 @@ object DataStore : OnPreferenceDataStoreChangeListener {

var rulesFirstCreate by profileCacheStore.boolean("rulesFirstCreate")

// ==================== جدید: دسترسی به تنظیمات دکوریشن ====================
var enableFlagEmoji by configurationStore.boolean(Key.ENABLE_FLAG_EMOJI) { false }
var enableGeminiMarker by configurationStore.boolean(Key.ENABLE_GEMINI_MARKER) { false }
// ========================================================================

override fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String) {
}
}
}
Loading