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