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 86ff8bd..4c2b4e0 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
@@ -113,7 +113,7 @@ object DataStore : OnPreferenceDataStoreChangeListener {
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)
+ 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)
@@ -156,7 +156,7 @@ object DataStore : OnPreferenceDataStoreChangeListener {
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.MIXED }
+ 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" }
diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/SagerDatabase.kt b/app/src/main/java/io/nekohasekai/sagernet/database/SagerDatabase.kt
index 486a42c..ececc10 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/database/SagerDatabase.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/database/SagerDatabase.kt
@@ -34,6 +34,7 @@ abstract class SagerDatabase : RoomDatabase() {
SagerNet.application.getDatabasePath(Key.DB_PROFILE).parentFile?.mkdirs()
Room.databaseBuilder(SagerNet.application, SagerDatabase::class.java, Key.DB_PROFILE)
// .addMigrations(*SagerDatabase_Migrations.build())
+ .setJournalMode(JournalMode.TRUNCATE)
.allowMainThreadQueries()
.enableMultiInstanceInvalidation()
.fallbackToDestructiveMigration()
diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/preference/PublicDatabase.kt b/app/src/main/java/io/nekohasekai/sagernet/database/preference/PublicDatabase.kt
index e9296de..d4ebf98 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/database/preference/PublicDatabase.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/database/preference/PublicDatabase.kt
@@ -16,6 +16,7 @@ abstract class PublicDatabase : RoomDatabase() {
val instance by lazy {
SagerNet.application.getDatabasePath(Key.DB_PROFILE).parentFile?.mkdirs()
Room.databaseBuilder(SagerNet.application, PublicDatabase::class.java, Key.DB_PUBLIC)
+ .setJournalMode(JournalMode.TRUNCATE)
.allowMainThreadQueries()
.enableMultiInstanceInvalidation()
.fallbackToDestructiveMigration()
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt b/app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt
index 4023c9b..5b3a0f8 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt
@@ -248,16 +248,19 @@ fun Fragment.needReload() {
fun Fragment.needRestart() {
snackbar(R.string.need_restart).setAction(R.string.apply) {
+ triggerFullRestart(requireContext())
+ }.show()
+}
+
+fun triggerFullRestart(ctx: Context) {
+ runOnDefaultDispatcher {
SagerNet.stopService()
- val ctx = requireContext()
- runOnDefaultDispatcher {
- delay(500)
- SagerDatabase.instance.close()
- PublicDatabase.instance.close()
- Executable.killAll(true)
+ delay(500)
+ Executable.killAll(true)
+ runOnMainDispatcher {
ProcessPhoenix.triggerRebirth(ctx, Intent(ctx, MainActivity::class.java))
}
- }.show()
+ }
}
fun Context.getColour(@ColorRes colorRes: Int): Int {
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 7ed131d..bb921d5 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt
@@ -266,6 +266,7 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) {
}
}
} catch (e: Exception) {
+ Logs.w(e)
runOnMainDispatcher {
Toast.makeText(app, e.readableMessage, Toast.LENGTH_SHORT).show()
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/BackupFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/BackupFragment.kt
index 4816f44..b439225 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/BackupFragment.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/BackupFragment.kt
@@ -16,6 +16,7 @@ import com.jakewharton.processphoenix.ProcessPhoenix
import io.nekohasekai.sagernet.BuildConfig
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.SagerNet
+import io.nekohasekai.sagernet.bg.Executable
import io.nekohasekai.sagernet.database.*
import io.nekohasekai.sagernet.database.preference.KeyValuePair
import io.nekohasekai.sagernet.database.preference.PublicDatabase
@@ -65,19 +66,11 @@ class BackupFragment : NamedFragment(R.layout.layout_backup) {
binding.resetSettings.setOnClickListener {
MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.confirm)
- .setMessage(R.string.reset_settings)
+ .setMessage(R.string.reset_settings_message)
.setNegativeButton(R.string.no, null)
.setPositiveButton(R.string.yes) { _, _ ->
- runOnDefaultDispatcher {
- DataStore.configurationStore.reset()
- delay(500)
- runOnMainDispatcher {
- ProcessPhoenix.triggerRebirth(
- requireContext(),
- Intent(requireContext(), MainActivity::class.java)
- )
- }
- }
+ DataStore.configurationStore.reset()
+ triggerFullRestart(requireContext())
}
.show()
}
@@ -251,9 +244,7 @@ class BackupFragment : NamedFragment(R.layout.layout_backup) {
import.backupRules.isChecked,
import.backupSettings.isChecked
)
- ProcessPhoenix.triggerRebirth(
- requireContext(), Intent(requireContext(), MainActivity::class.java)
- )
+ triggerFullRestart(requireContext())
}.onFailure {
Logs.w(it)
onMainDispatcher {
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/LogcatFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/LogcatFragment.kt
index ef22f30..bb4a082 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/LogcatFragment.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/LogcatFragment.kt
@@ -9,9 +9,10 @@ import android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
import android.text.style.ForegroundColorSpan
import android.view.MenuItem
import android.view.View
-import android.widget.ScrollView
+import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.core.view.ViewCompat
+import androidx.core.view.doOnLayout
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.databinding.LayoutLogcatBinding
import io.nekohasekai.sagernet.ktx.*
@@ -41,7 +42,6 @@ class LogcatFragment : ToolbarFragment(R.layout.layout_logcat),
ViewCompat.setOnApplyWindowInsetsListener(binding.root, ListListener)
reloadSession()
- // TODO new logcat
}
private fun getColorForLine(line: String): ForegroundColorSpan {
@@ -76,8 +76,15 @@ class LogcatFragment : ToolbarFragment(R.layout.layout_logcat),
}
binding.textview.text = span
- binding.scroolview.post {
- binding.scroolview.fullScroll(ScrollView.FOCUS_DOWN)
+ // 阻止自动滚动/焦点干扰
+ binding.scroolview.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS
+ binding.textview.isFocusable = false
+ binding.textview.isFocusableInTouchMode = false
+ binding.textview.clearFocus()
+
+ // 等 textview 完成最终 layout 再滚动到底部
+ binding.textview.doOnLayout {
+ binding.scroolview.scrollTo(0, binding.textview.height)
}
}
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 783c7d8..13399d3 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -493,4 +493,5 @@
当前版本:%1$s\n可升级版本:%2$s\n是否前往下载?
检查成功,但没有更新。
恢复默认设置
+ 恢复默认设置,但节点、分组等数据将保留。如需完全清除数据,请在系统设置中直接清除应用数据。
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 98edb44..aebdd70 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -573,4 +573,5 @@
Current version: %1$s\nAvailable version: %2$s\nDo you want to download it?
Check successful, but no updates.
Restore default settings
+ Restore default settings, but data such as nodes and groups will be retained. To completely clear data, clear application data directly in the system settings.
\ No newline at end of file
diff --git a/app/src/main/res/xml/global_preferences.xml b/app/src/main/res/xml/global_preferences.xml
index d71b680..64f4f79 100644
--- a/app/src/main/res/xml/global_preferences.xml
+++ b/app/src/main/res/xml/global_preferences.xml
@@ -29,7 +29,7 @@
app:title="@string/service_mode"
app:useSimpleSummaryProvider="true" />
= uint32(len(funcs)) {
@@ -317,17 +320,19 @@ func (r *httpRequest) doH3Direct() (HTTPResponse, error) {
mu.Lock()
finalErr = errors.Join(finalErr, fmt.Errorf("%s: %w", t, err))
mu.Unlock()
+ if rsp != nil && rsp.Body != nil {
+ rsp.Body.Close()
+ }
return
}
// 处理 HTTP 状态码
if rsp.StatusCode != http.StatusOK {
hr := &httpResponse{Response: rsp}
- err = errors.Join(finalErr, fmt.Errorf("%s: %s", t, hr.errorString()))
+ err = fmt.Errorf("%s: %s", t, hr.errorString())
mu.Lock()
- finalErr = err
+ finalErr = errors.Join(finalErr, err)
mu.Unlock()
- rsp.Body.Close()
return
}
diff --git a/nb4a.properties b/nb4a.properties
index f25c774..e542a71 100644
--- a/nb4a.properties
+++ b/nb4a.properties
@@ -1,4 +1,4 @@
PACKAGE_NAME=moe.nb4a
VERSION_NAME=1.3.9
-PRE_VERSION_NAME=pre-1.4.0-20250903-1
+PRE_VERSION_NAME=pre-1.4.0-20250904-1
VERSION_CODE=43