Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 20 additions & 7 deletions app/src/main/java/com/github/kr328/clash/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ import java.util.concurrent.TimeUnit
import com.github.kr328.clash.design.R as DesignR

class MainActivity : BaseActivity<MainDesign>() {

companion object {
@Volatile
@JvmStatic
var pendingExternalRequest: Boolean = false
private set
}

override suspend fun main() {
val design = MainDesign(this)

Expand Down Expand Up @@ -129,13 +137,18 @@ class MainActivity : BaseActivity<MainDesign>() {

try {
if (vpnRequest != null) {
val result = startActivityForResult(
ActivityResultContracts.StartActivityForResult(),
vpnRequest
)

if (result.resultCode == RESULT_OK)
startClashService()
pendingExternalRequest = true
try {
val result = startActivityForResult(
ActivityResultContracts.StartActivityForResult(),
vpnRequest
)

if (result.resultCode == RESULT_OK)
startClashService()
} finally {
pendingExternalRequest = false
}
}
} catch (e: Exception) {
design?.showToast(DesignR.string.unable_to_start_vpn, ToastDuration.Long)
Expand Down
52 changes: 52 additions & 0 deletions app/src/main/java/com/github/kr328/clash/MainApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ package com.github.kr328.clash

import android.app.Application
import android.content.Context
import android.os.Process
import com.github.kr328.clash.common.Global
import com.github.kr328.clash.common.compat.currentProcessName
import com.github.kr328.clash.common.log.Log
import com.github.kr328.clash.design.store.UiStore
import com.github.kr328.clash.remote.Remote
import com.github.kr328.clash.service.util.sendServiceRecreated
import com.github.kr328.clash.util.ApplicationObserver
import com.github.kr328.clash.util.clashDir
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.File
import java.io.FileOutputStream

Expand All @@ -29,6 +35,52 @@ class MainApplication : Application() {
Log.d("Process $processName started")

if (processName == packageName) {
// Setup auto destroy UI observer (must register before Remote.launch
// so our callback is in the listener list before any visibility event)
val uiStore = UiStore(this)
var autoDestroyJob: Job? = null

// 含未保存编辑或正在等待外部结果(文件选择器、VPN 授权等)的 Activity,
// 存活时不允许杀 UI 进程
val protectedActivities: Set<Class<*>> = setOf(
NewProfileActivity::class.java,
FilesActivity::class.java,
LogcatActivity::class.java,
MetaFeatureSettingsActivity::class.java,
AccessControlActivity::class.java,
OverrideSettingsActivity::class.java,
PropertiesActivity::class.java,
)

ApplicationObserver.onVisibleChanged { visible ->
autoDestroyJob?.cancel()
autoDestroyJob = null
if (!visible && uiStore.autoDestroyUI) {
autoDestroyJob = Global.launch {
// 持续监测:有等待回调/编辑中则等待,无保护条件持续 3 秒才杀
var idleMs = 0
while (idleMs < 3000) {
delay(500)
val protectedNow = LogcatService.running ||
ApplicationObserver.createdActivities.any { it.javaClass in protectedActivities } ||
MainActivity.pendingExternalRequest
if (protectedNow) {
idleMs = 0
} else {
idleMs += 500
}
}
Log.d("AutoDestroyUI: Killing UI process to free memory")
try {
File(clashDir, "auto_destroy.log").appendText(
"${java.util.Date()} AutoDestroyUI: Killing UI process\n"
)
} catch (e: Exception) { }
Process.killProcess(Process.myPid())
}
}
}

Remote.launch()
} else {
sendServiceRecreated()
Expand Down
8 changes: 5 additions & 3 deletions app/src/main/java/com/github/kr328/clash/util/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ object ApplicationObserver {
private val _createdActivities: MutableSet<Activity> = mutableSetOf()
private val _visibleActivities: MutableSet<Activity> = mutableSetOf()

private var visibleChanged: (Boolean) -> Unit = {}
private val visibleChangedListeners: MutableList<(Boolean) -> Unit> = mutableListOf()

private var appVisible = false
private set(value) {
if (field != value) {
field = value

visibleChanged(value)
visibleChangedListeners.forEach { listener ->
listener(value)
}
}
}

Expand Down Expand Up @@ -55,7 +57,7 @@ object ApplicationObserver {
}

fun onVisibleChanged(visibleChanged: (Boolean) -> Unit) {
this.visibleChanged = visibleChanged
visibleChangedListeners.add(visibleChanged)
}

fun attach(application: Application) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ class AppSettingsDesign(
}
}

switch(
value = uiStore::autoDestroyUI,
icon = R.drawable.ic_baseline_close,
title = R.string.auto_destroy_ui_title,
summary = R.string.auto_destroy_ui_desc,
)

category(R.string.service)

switch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class UiStore(context: Context) {
defaultValue = false,
)

var autoDestroyUI: Boolean by store.boolean(
key = "auto_destroy_ui",
defaultValue = false,
)

var proxyExcludeNotSelectable by store.boolean(
key = "proxy_exclude_not_selectable",
defaultValue = false,
Expand Down
2 changes: 2 additions & 0 deletions design/src/main/res/values-ja-rJP/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -264,4 +264,6 @@
<string name="override_destination">Override Destination</string>
<string name="import_from_qr_no_permission">カメラのアクセスが制限されています。設定から有効にしてください。</string>
<string name="import_from_qr_exception">システムで予期しない例外が発生しました。</string>
<string name="auto_destroy_ui_title">UI自動破棄</string>
<string name="auto_destroy_ui_desc">アプリがバックグラウンドに移行して3秒後にメモリ節約のためUIを自動的に破棄します</string>
</resources>
2 changes: 2 additions & 0 deletions design/src/main/res/values-ko-rKR/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -264,4 +264,6 @@
<string name="override_destination">Override Destination</string>
<string name="import_from_qr_no_permission">카메라 접근이 제한되었습니다. 설정에서 허용해 주세요.</string>
<string name="import_from_qr_exception">처리되지 않은 시스템 예외가 발생했습니다.</string>
<string name="auto_destroy_ui_title">UI 자동 종료</string>
<string name="auto_destroy_ui_desc">앱이 백그라운드로 전환된 후 3초 후 메모리 절약을 위해 UI를 자동으로 종료합니다</string>
</resources>
2 changes: 2 additions & 0 deletions design/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -328,4 +328,6 @@
<string name="override_destination">Override Destination</string>
<string name="import_from_qr_no_permission">Доступ к камере ограничен. Разрешите его в настройках.</string>
<string name="import_from_qr_exception">Произошла не обрабатываемая системная ошибка.</string>
<string name="auto_destroy_ui_title">Автоуничтожение UI</string>
<string name="auto_destroy_ui_desc">Автоматически уничтожать UI через 3 секунды после перехода приложения в фон для экономии памяти</string>
</resources>
2 changes: 2 additions & 0 deletions design/src/main/res/values-vi/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,6 @@
<string name="import_from_qr">Nhập từ Mã QR</string>
<string name="import_from_qr_no_permission">Quyền truy cập camera bị hạn chế. Vui lòng bật trong Cài đặt.</string>
<string name="import_from_qr_exception">Đã xảy ra ngoại lệ hệ thống không xử lý được.</string>
<string name="auto_destroy_ui_title">Tự động hủy UI</string>
<string name="auto_destroy_ui_desc">Tự động hủy UI sau 3 giây khi ứng dụng chuyển sang nền để tiết kiệm bộ nhớ</string>
</resources>
2 changes: 2 additions & 0 deletions design/src/main/res/values-zh-rHK/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -261,4 +261,6 @@
<string name="override_destination">Override Destination</string>
<string name="import_from_qr_no_permission">相機權限受限,請前往設定開啟。</string>
<string name="import_from_qr_exception">發生系統未知異常,操作失敗。</string>
<string name="auto_destroy_ui_title">自動銷毀介面</string>
<string name="auto_destroy_ui_desc">應用程式進入背景3秒後自動銷毀介面以節省記憶體</string>
</resources>
2 changes: 2 additions & 0 deletions design/src/main/res/values-zh-rTW/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -261,4 +261,6 @@
<string name="override_destination">Override Destination</string>
<string name="import_from_qr_no_permission">相機權限受限,請前往設定開啟。</string>
<string name="import_from_qr_exception">發生系統未知異常,操作失敗。</string>
<string name="auto_destroy_ui_title">自動銷毀介面</string>
<string name="auto_destroy_ui_desc">應用程式進入背景3秒後自動銷毀介面以節省記憶體</string>
</resources>
2 changes: 2 additions & 0 deletions design/src/main/res/values-zh/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
<string name="hide_app_icon_desc">可以在拨号盘输入 *#*#252746382#*#* 打开应用</string>
<string name="hide_from_recents_title">从最近任务隐藏</string>
<string name="hide_from_recents_desc">在最近任务中隐藏应用</string>
<string name="auto_destroy_ui_title">自动销毁界面</string>
<string name="auto_destroy_ui_desc">应用进入后台3秒后自动销毁界面以节省内存</string>
<string name="history">历史</string>
<string name="import_from_file">从文件导入</string>
<string name="import_from_url">从 URL 导入</string>
Expand Down
2 changes: 2 additions & 0 deletions design/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@
<string name="hide_app_icon_desc">You can dial *#*#252746382#*#* to open this App</string>
<string name="hide_from_recents_title">Hide from Recents</string>
<string name="hide_from_recents_desc">Hide app from the Recent apps screen</string>
<string name="auto_destroy_ui_title">Auto Destroy UI</string>
<string name="auto_destroy_ui_desc">Automatically destroy UI after 3 seconds when app goes to background</string>
<string name="import_from_qr_no_permission">Camera access is restricted. Please enable it in Settings.</string>
<string name="import_from_qr_exception">An unhandled system exception occurred.</string>

Expand Down