From 7f58237589caf37b1524054f5463912f28d981fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?lollipopkit=F0=9F=8F=B3=EF=B8=8F=E2=80=8D=E2=9A=A7?=
=?UTF-8?q?=EF=B8=8F?= <10864310+lollipopkit@users.noreply.github.com>
Date: Sat, 4 Jan 2025 16:22:20 +0800
Subject: [PATCH] fix: catch crash of fg service (#669)
---
.../tech/lolli/toolbox/ForegroundService.kt | 140 +++++---
.../kotlin/tech/lolli/toolbox/MainActivity.kt | 36 +-
.../tech/lolli/toolbox/widget/HomeWidget.kt | 10 +
.../app/src/main/res/layout/home_widget.xml | 9 +-
ios/Podfile.lock | 24 +-
ios/Runner.xcodeproj/project.pbxproj | 36 +-
lib/core/chan.dart | 9 +-
lib/data/res/build_data.dart | 2 +-
lib/data/store/setting.dart | 3 +
lib/generated/l10n/l10n.dart | 12 +
lib/generated/l10n/l10n_de.dart | 6 +
lib/generated/l10n/l10n_en.dart | 6 +
lib/generated/l10n/l10n_es.dart | 6 +
lib/generated/l10n/l10n_fr.dart | 6 +
lib/generated/l10n/l10n_id.dart | 6 +
lib/generated/l10n/l10n_ja.dart | 6 +
lib/generated/l10n/l10n_nl.dart | 6 +
lib/generated/l10n/l10n_pt.dart | 6 +
lib/generated/l10n/l10n_ru.dart | 6 +
lib/generated/l10n/l10n_tr.dart | 6 +
lib/generated/l10n/l10n_uk.dart | 6 +
lib/generated/l10n/l10n_zh.dart | 12 +
lib/l10n/app_de.arb | 2 +
lib/l10n/app_en.arb | 2 +
lib/l10n/app_es.arb | 2 +
lib/l10n/app_fr.arb | 2 +
lib/l10n/app_id.arb | 2 +
lib/l10n/app_ja.arb | 2 +
lib/l10n/app_nl.arb | 2 +
lib/l10n/app_pt.arb | 2 +
lib/l10n/app_ru.arb | 2 +
lib/l10n/app_tr.arb | 2 +
lib/l10n/app_uk.arb | 2 +
lib/l10n/app_zh.arb | 2 +
lib/l10n/app_zh_tw.arb | 2 +
lib/view/page/setting/platform/android.dart | 8 +
lib/view/page/ssh/page.dart | 308 +++++++++---------
lib/view/page/ssh/tab.dart | 54 +--
macos/Runner.xcodeproj/project.pbxproj | 12 +-
pubspec.yaml | 2 +-
40 files changed, 497 insertions(+), 272 deletions(-)
diff --git a/android/app/src/main/kotlin/tech/lolli/toolbox/ForegroundService.kt b/android/app/src/main/kotlin/tech/lolli/toolbox/ForegroundService.kt
index 83dde45b..3b14e287 100644
--- a/android/app/src/main/kotlin/tech/lolli/toolbox/ForegroundService.kt
+++ b/android/app/src/main/kotlin/tech/lolli/toolbox/ForegroundService.kt
@@ -5,10 +5,24 @@ import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.util.Log
+import java.io.File
+import java.util.*
class ForegroundService : Service() {
private val chanId = "ForegroundServiceChannel"
+ private fun logError(message: String, error: Throwable? = null) {
+ Log.e("ForegroundService", message, error)
+ try {
+ val logFile = File(getExternalFilesDir(null), "server_box.log")
+ val timestamp = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).format(Date())
+ val logMessage = "$timestamp [ForegroundService] ERROR: $message\n${error?.stackTraceToString() ?: ""}\n"
+ logFile.appendText(logMessage)
+ } catch (e: Exception) {
+ Log.e("ForegroundService", "Failed to write log", e)
+ }
+ }
+
override fun onCreate() {
super.onCreate()
Log.d("ForegroundService", "Service onCreate")
@@ -16,25 +30,52 @@ class ForegroundService : Service() {
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- if (intent == null) {
- Log.w("ForegroundService", "onStartCommand called with null intent")
- stopForegroundService()
- return START_NOT_STICKY
- }
-
- val action = intent.action
- Log.d("ForegroundService", "onStartCommand action=$action")
-
- return when (action) {
- "ACTION_STOP_FOREGROUND" -> {
+ try {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
+ androidx.core.content.ContextCompat.checkSelfPermission(
+ this, android.Manifest.permission.POST_NOTIFICATIONS
+ ) != android.content.pm.PackageManager.PERMISSION_GRANTED
+ ) {
+ Log.w("ForegroundService", "Notification permission denied. Stopping service.")
stopForegroundService()
- START_NOT_STICKY
+ return START_NOT_STICKY
}
- else -> {
- val notification = createNotification()
+
+ if (intent == null) {
+ Log.w("ForegroundService", "onStartCommand called with null intent")
+ stopForegroundService()
+ return START_NOT_STICKY
+ }
+
+ val action = intent.action
+ Log.d("ForegroundService", "onStartCommand action=$action")
+
+ // Create notification before starting foreground
+ val notification = createNotification()
+
+ // Use try-catch for startForeground
+ try {
startForeground(1, notification)
- START_STICKY
+ } catch (e: Exception) {
+ logError("Failed to start foreground", e)
+ stopSelf()
+ return START_NOT_STICKY
}
+
+ return when (action) {
+ "ACTION_STOP_FOREGROUND" -> {
+ stopForegroundService()
+ START_NOT_STICKY
+ }
+ else -> {
+ START_STICKY
+ }
+ }
+
+ } catch (e: Exception) {
+ logError("Error in onStartCommand", e)
+ stopSelf()
+ return START_NOT_STICKY
}
}
@@ -61,44 +102,53 @@ class ForegroundService : Service() {
}
private fun createNotification(): Notification {
- val notificationIntent = Intent(this, MainActivity::class.java)
- val pendingIntent = PendingIntent.getActivity(
- this,
- 0,
- notificationIntent,
- PendingIntent.FLAG_IMMUTABLE
- )
+ try {
+ val notificationIntent = Intent(this, MainActivity::class.java)
+ val pendingIntent = PendingIntent.getActivity(
+ this,
+ 0,
+ notificationIntent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
- val deleteIntent = Intent(this, ForegroundService::class.java).apply {
- action = "ACTION_STOP_FOREGROUND"
+ val deleteIntent = Intent(this, ForegroundService::class.java).apply {
+ action = "ACTION_STOP_FOREGROUND"
+ }
+ val deletePendingIntent = PendingIntent.getService(
+ this,
+ 0,
+ deleteIntent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
+
+ val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ Notification.Builder(this, chanId)
+ } else {
+ Notification.Builder(this)
+ }
+
+ return builder
+ .setContentTitle("Server Box")
+ .setContentText("Running in background")
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setContentIntent(pendingIntent)
+ .addAction(android.R.drawable.ic_delete, "Stop", deletePendingIntent)
+ .build()
+ } catch (e: Exception) {
+ logError("Error creating notification", e)
+ // Return a basic notification as fallback
+ return Notification.Builder(this)
+ .setContentTitle("Server Box")
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .build()
}
- val deletePendingIntent = PendingIntent.getService(
- this,
- 0,
- deleteIntent,
- PendingIntent.FLAG_IMMUTABLE
- )
-
- val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- Notification.Builder(this, chanId)
- } else {
- Notification.Builder(this)
- }
-
- return builder
- .setContentTitle("Server Box")
- .setContentText("Running in background")
- .setSmallIcon(R.mipmap.ic_launcher)
- .setContentIntent(pendingIntent)
- .addAction(android.R.drawable.ic_delete, "Stop", deletePendingIntent)
- .build()
}
private fun stopForegroundService() {
try {
stopForeground(true)
} catch (e: Exception) {
- Log.e("ForegroundService", "Error stopping foreground: ${e.message}")
+ logError("Error stopping foreground", e)
}
stopSelf()
Log.d("ForegroundService", "ForegroundService stopped")
diff --git a/android/app/src/main/kotlin/tech/lolli/toolbox/MainActivity.kt b/android/app/src/main/kotlin/tech/lolli/toolbox/MainActivity.kt
index dbb0a99d..3dd4ed07 100644
--- a/android/app/src/main/kotlin/tech/lolli/toolbox/MainActivity.kt
+++ b/android/app/src/main/kotlin/tech/lolli/toolbox/MainActivity.kt
@@ -25,12 +25,19 @@ class MainActivity: FlutterFragmentActivity() {
result.success(null)
}
"startService" -> {
- reqPerm()
- val serviceIntent = Intent(this@MainActivity, ForegroundService::class.java)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- startForegroundService(serviceIntent)
- } else {
- startService(serviceIntent)
+ try {
+ reqPerm()
+ val serviceIntent = Intent(this@MainActivity, ForegroundService::class.java)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForegroundService(serviceIntent)
+ } else {
+ startService(serviceIntent)
+ }
+ result.success(null)
+ } catch (e: Exception) {
+ // Log error but don't crash
+ android.util.Log.e("MainActivity", "Failed to start service: ${e.message}")
+ result.error("SERVICE_ERROR", e.message, null)
}
}
"stopService" -> {
@@ -54,13 +61,20 @@ class MainActivity: FlutterFragmentActivity() {
private fun reqPerm() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
+
+ // Check if we already have the permission to avoid unnecessary prompts
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(
- this,
- arrayOf(Manifest.permission.POST_NOTIFICATIONS),
- 123,
- )
+ try {
+ ActivityCompat.requestPermissions(
+ this,
+ arrayOf(Manifest.permission.POST_NOTIFICATIONS),
+ 123,
+ )
+ } catch (e: Exception) {
+ // Log error but don't crash
+ android.util.Log.e("MainActivity", "Failed to request permissions: ${e.message}")
+ }
}
}
}
diff --git a/android/app/src/main/kotlin/tech/lolli/toolbox/widget/HomeWidget.kt b/android/app/src/main/kotlin/tech/lolli/toolbox/widget/HomeWidget.kt
index 59c57e10..5a5b2c8c 100644
--- a/android/app/src/main/kotlin/tech/lolli/toolbox/widget/HomeWidget.kt
+++ b/android/app/src/main/kotlin/tech/lolli/toolbox/widget/HomeWidget.kt
@@ -65,6 +65,8 @@ class HomeWidget : AppWidgetProvider() {
views.setViewVisibility(R.id.error_message, View.VISIBLE)
views.setTextViewText(R.id.error_message, "Please configure the widget URL.")
views.setViewVisibility(R.id.widget_content, View.GONE)
+ views.setFloat(R.id.widget_name, "setAlpha", 1f)
+ views.setFloat(R.id.error_message, "setAlpha", 1f)
appWidgetManager.updateAppWidget(appWidgetId, views)
return
} else {
@@ -100,6 +102,12 @@ class HomeWidget : AppWidgetProvider() {
views.setTextViewText(R.id.widget_net, net)
val timeStr = android.text.format.DateFormat.format("HH:mm", java.util.Date()).toString()
views.setTextViewText(R.id.widget_time, timeStr)
+ views.setFloat(R.id.widget_name, "setAlpha", 1f)
+ views.setFloat(R.id.widget_cpu_label, "setAlpha", 1f)
+ views.setFloat(R.id.widget_mem_label, "setAlpha", 1f)
+ views.setFloat(R.id.widget_disk_label, "setAlpha", 1f)
+ views.setFloat(R.id.widget_net_label, "setAlpha", 1f)
+ views.setFloat(R.id.widget_time, "setAlpha", 1f)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
} else {
@@ -113,6 +121,8 @@ class HomeWidget : AppWidgetProvider() {
views.setViewVisibility(R.id.error_message, View.VISIBLE)
views.setTextViewText(R.id.error_message, "Failed to retrieve data.")
views.setViewVisibility(R.id.widget_content, View.GONE)
+ views.setFloat(R.id.widget_name, "setAlpha", 1f)
+ views.setFloat(R.id.error_message, "setAlpha", 1f)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
diff --git a/android/app/src/main/res/layout/home_widget.xml b/android/app/src/main/res/layout/home_widget.xml
index 615a320e..af31dbde 100644
--- a/android/app/src/main/res/layout/home_widget.xml
+++ b/android/app/src/main/res/layout/home_widget.xml
@@ -16,6 +16,8 @@
android:textSize="23sp"
android:textStyle="bold"
android:maxLines="1"
+ android:alpha="0"
+ android:animateLayoutChanges="true"
tools:text="Server Name" />
@@ -32,7 +34,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
- android:paddingTop="13dp">
+ android:paddingTop="13dp"
+ android:animateLayoutChanges="true">
\ No newline at end of file
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 7900b8f0..8cb6f725 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -6,6 +6,13 @@ PODS:
- file_picker (0.0.1):
- Flutter
- Flutter (1.0.0)
+ - flutter_inappwebview_ios (0.0.1):
+ - Flutter
+ - flutter_inappwebview_ios/Core (= 0.0.1)
+ - OrderedSet (~> 6.0.3)
+ - flutter_inappwebview_ios/Core (0.0.1):
+ - Flutter
+ - OrderedSet (~> 6.0.3)
- flutter_native_splash (2.4.3):
- Flutter
- icloud_storage (0.0.1):
@@ -13,6 +20,7 @@ PODS:
- local_auth_darwin (0.0.1):
- Flutter
- FlutterMacOS
+ - OrderedSet (6.0.3)
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
@@ -31,15 +39,13 @@ PODS:
- Flutter
- watch_connectivity (0.0.1):
- Flutter
- - webview_flutter_wkwebview (0.0.1):
- - Flutter
- - FlutterMacOS
DEPENDENCIES:
- app_links (from `.symlinks/plugins/app_links/ios`)
- camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
+ - flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- icloud_storage (from `.symlinks/plugins/icloud_storage/ios`)
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
@@ -51,7 +57,10 @@ DEPENDENCIES:
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
- watch_connectivity (from `.symlinks/plugins/watch_connectivity/ios`)
- - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
+
+SPEC REPOS:
+ trunk:
+ - OrderedSet
EXTERNAL SOURCES:
app_links:
@@ -62,6 +71,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/file_picker/ios"
Flutter:
:path: Flutter
+ flutter_inappwebview_ios:
+ :path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
flutter_native_splash:
:path: ".symlinks/plugins/flutter_native_splash/ios"
icloud_storage:
@@ -84,17 +95,17 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/wakelock_plus/ios"
watch_connectivity:
:path: ".symlinks/plugins/watch_connectivity/ios"
- webview_flutter_wkwebview:
- :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
SPEC CHECKSUMS:
app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0
camera_avfoundation: dd002b0330f4981e1bbcb46ae9b62829237459a4
file_picker: c79185e70b9b45728cde2a8d8da454e0cb43f287
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
+ flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
flutter_native_splash: e8a1e01082d97a8099d973f919f57904c925008a
icloud_storage: d9ac7a33ced81df08ba7ea1bf3099cc0ee58f60a
local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
+ OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
plain_notification_token: b36467dc91939a7b6754267c701bbaca14996ee1
@@ -103,7 +114,6 @@ SPEC CHECKSUMS:
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
watch_connectivity: 715eb484685e05846eab74795348a44bb2809b82
- webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4
PODFILE CHECKSUM: ec6ef69056f066e8b21a3391082f23b5ad2d37f8
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 425f715a..03e3f21a 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -672,7 +672,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CURRENT_PROJECT_VERSION = 1117;
+ CURRENT_PROJECT_VERSION = 1124;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -682,7 +682,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.1117;
+ MARKETING_VERSION = 1.0.1124;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -808,7 +808,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CURRENT_PROJECT_VERSION = 1117;
+ CURRENT_PROJECT_VERSION = 1124;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -818,7 +818,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.1117;
+ MARKETING_VERSION = 1.0.1124;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -836,7 +836,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CURRENT_PROJECT_VERSION = 1117;
+ CURRENT_PROJECT_VERSION = 1124;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -846,7 +846,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.1117;
+ MARKETING_VERSION = 1.0.1124;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -867,7 +867,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1117;
+ CURRENT_PROJECT_VERSION = 1124;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -880,7 +880,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 1.0.1117;
+ MARKETING_VERSION = 1.0.1124;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
@@ -906,7 +906,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1117;
+ CURRENT_PROJECT_VERSION = 1124;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -919,7 +919,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 1.0.1117;
+ MARKETING_VERSION = 1.0.1124;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -942,7 +942,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1117;
+ CURRENT_PROJECT_VERSION = 1124;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -955,7 +955,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 1.0.1117;
+ MARKETING_VERSION = 1.0.1124;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -978,7 +978,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1117;
+ CURRENT_PROJECT_VERSION = 1124;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -990,7 +990,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.1117;
+ MARKETING_VERSION = 1.0.1124;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
@@ -1019,7 +1019,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1117;
+ CURRENT_PROJECT_VERSION = 1124;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -1031,7 +1031,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.1117;
+ MARKETING_VERSION = 1.0.1124;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox;
@@ -1057,7 +1057,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1117;
+ CURRENT_PROJECT_VERSION = 1124;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -1069,7 +1069,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.1117;
+ MARKETING_VERSION = 1.0.1124;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox;
diff --git a/lib/core/chan.dart b/lib/core/chan.dart
index d55e4aca..16f09ab1 100644
--- a/lib/core/chan.dart
+++ b/lib/core/chan.dart
@@ -1,5 +1,6 @@
import 'package:flutter/services.dart';
import 'package:server_box/data/res/misc.dart';
+import 'package:server_box/data/res/store.dart';
abstract final class MethodChans {
static const _channel = MethodChannel('${Miscs.pkgName}/main_chan');
@@ -8,14 +9,14 @@ abstract final class MethodChans {
_channel.invokeMethod('sendToBackground');
}
- /// TODO: try fix the fn, then uncomment it and [stopService]
- /// Issues #639
static void startService() {
- // _channel.invokeMethod('startService');
+ if (Stores.setting.fgService.fetch() != true) return;
+ _channel.invokeMethod('startService');
}
static void stopService() {
- // _channel.invokeMethod('stopService');
+ if (Stores.setting.fgService.fetch() != true) return;
+ _channel.invokeMethod('stopService');
}
static void updateHomeWidget() async {
diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart
index 0178ad5e..19e36e18 100644
--- a/lib/data/res/build_data.dart
+++ b/lib/data/res/build_data.dart
@@ -3,6 +3,6 @@
abstract class BuildData {
static const String name = "ServerBox";
- static const int build = 1117;
+ static const int build = 1124;
static const int script = 59;
}
diff --git a/lib/data/store/setting.dart b/lib/data/store/setting.dart
index f9a5b998..a1d7160a 100644
--- a/lib/data/store/setting.dart
+++ b/lib/data/store/setting.dart
@@ -235,6 +235,9 @@ class SettingStore extends HiveStore {
/// Set it empty to use local editor GUI.
late final sftpEditor = propertyDefault('sftpEditor', '');
+ /// Run foreground service on Android, if the SSH terminal is running
+ late final fgService = propertyDefault('fgService', false);
+
// Never show these settings for users
//
// ------BEGIN------
diff --git a/lib/generated/l10n/l10n.dart b/lib/generated/l10n/l10n.dart
index c6c099a9..28361fed 100644
--- a/lib/generated/l10n/l10n.dart
+++ b/lib/generated/l10n/l10n.dart
@@ -434,6 +434,18 @@ abstract class AppLocalizations {
/// **'If you downloaded this app from F-Droid, it is recommended to turn off this option.'**
String get fdroidReleaseTip;
+ /// No description provided for @fgService.
+ ///
+ /// In en, this message translates to:
+ /// **'Foreground Service'**
+ String get fgService;
+
+ /// No description provided for @fgServiceTip.
+ ///
+ /// In en, this message translates to:
+ /// **'After enabling, some device models may crash. Disabling it may cause some models to be unable to maintain SSH connections in the background. Please allow ServerBox notification permissions, background running, and self-wake-up in system settings.'**
+ String get fgServiceTip;
+
/// No description provided for @fileTooLarge.
///
/// In en, this message translates to:
diff --git a/lib/generated/l10n/l10n_de.dart b/lib/generated/l10n/l10n_de.dart
index 093bb0c8..b37de365 100644
--- a/lib/generated/l10n/l10n_de.dart
+++ b/lib/generated/l10n/l10n_de.dart
@@ -173,6 +173,12 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get fdroidReleaseTip => 'Wenn Sie diese App von F-Droid heruntergeladen haben, wird empfohlen, diese Option zu deaktivieren.';
+ @override
+ String get fgService => 'Vordergrund-Dienst';
+
+ @override
+ String get fgServiceTip => 'Nach dem Einschalten kann es bei einigen Gerätemodellen zu Abstürzen kommen. Das Ausschalten kann bei einigen Modellen dazu führen, dass SSH-Verbindungen im Hintergrund nicht aufrechterhalten werden können. Bitte erlauben Sie ServerBox in den Systemeinstellungen Benachrichtigungsrechte, Hintergrundausführung und Selbstaktivierung.';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return 'Datei \'$file\' ist zu groß $size, max $sizeMax';
diff --git a/lib/generated/l10n/l10n_en.dart b/lib/generated/l10n/l10n_en.dart
index 264e2fb9..4703b278 100644
--- a/lib/generated/l10n/l10n_en.dart
+++ b/lib/generated/l10n/l10n_en.dart
@@ -173,6 +173,12 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get fdroidReleaseTip => 'If you downloaded this app from F-Droid, it is recommended to turn off this option.';
+ @override
+ String get fgService => 'Foreground Service';
+
+ @override
+ String get fgServiceTip => 'After enabling, some device models may crash. Disabling it may cause some models to be unable to maintain SSH connections in the background. Please allow ServerBox notification permissions, background running, and self-wake-up in system settings.';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return 'File \'$file\' too large $size, max $sizeMax';
diff --git a/lib/generated/l10n/l10n_es.dart b/lib/generated/l10n/l10n_es.dart
index 8a536cb2..338c67ed 100644
--- a/lib/generated/l10n/l10n_es.dart
+++ b/lib/generated/l10n/l10n_es.dart
@@ -173,6 +173,12 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get fdroidReleaseTip => 'Si descargaste esta aplicación desde F-Droid, se recomienda desactivar esta opción.';
+ @override
+ String get fgService => 'Servicio en primer plano';
+
+ @override
+ String get fgServiceTip => 'Después de activarlo, algunos modelos de dispositivos pueden bloquearse. Desactivarlo puede hacer que algunos modelos no puedan mantener las conexiones SSH en segundo plano. Por favor, permita los permisos de notificación de ServerBox, la ejecución en segundo plano y el auto-despertar en la configuración del sistema.';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return 'El archivo \'$file\' es demasiado grande \'$size\', supera el $sizeMax';
diff --git a/lib/generated/l10n/l10n_fr.dart b/lib/generated/l10n/l10n_fr.dart
index 0a81b0f5..5e0e294e 100644
--- a/lib/generated/l10n/l10n_fr.dart
+++ b/lib/generated/l10n/l10n_fr.dart
@@ -173,6 +173,12 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get fdroidReleaseTip => 'Si vous avez téléchargé cette application depuis F-Droid, il est recommandé de désactiver cette option.';
+ @override
+ String get fgService => 'Service de premier plan';
+
+ @override
+ String get fgServiceTip => 'Après l\'activation, certains modèles d\'appareils peuvent planter. La désactivation peut empêcher certains modèles de maintenir les connexions SSH en arrière-plan. Veuillez autoriser les permissions de notification ServerBox, l\'exécution en arrière-plan et l\'auto-réveil dans les paramètres système.';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return 'Fichier \'$file\' trop volumineux $size, max $sizeMax';
diff --git a/lib/generated/l10n/l10n_id.dart b/lib/generated/l10n/l10n_id.dart
index e010d125..127ca3bd 100644
--- a/lib/generated/l10n/l10n_id.dart
+++ b/lib/generated/l10n/l10n_id.dart
@@ -173,6 +173,12 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get fdroidReleaseTip => 'Jika Anda mengunduh aplikasi ini dari F-Droid, disarankan untuk mematikan opsi ini.';
+ @override
+ String get fgService => 'Layanan Latar Depan';
+
+ @override
+ String get fgServiceTip => 'Setelah diaktifkan, beberapa model perangkat mungkin crash. Menonaktifkannya dapat menyebabkan beberapa model tidak dapat mempertahankan koneksi SSH di latar belakang. Harap izinkan perizinan notifikasi ServerBox, menjalankan di latar belakang, dan bangun mandiri di pengaturan sistem.';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return 'File \'$file\' terlalu besar $size, max $sizeMax';
diff --git a/lib/generated/l10n/l10n_ja.dart b/lib/generated/l10n/l10n_ja.dart
index f7755770..52739c6c 100644
--- a/lib/generated/l10n/l10n_ja.dart
+++ b/lib/generated/l10n/l10n_ja.dart
@@ -173,6 +173,12 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get fdroidReleaseTip => 'このアプリをF-Droidからダウンロードした場合、このオプションをオフにすることをお勧めします。';
+ @override
+ String get fgService => 'フォアグラウンドサービス';
+
+ @override
+ String get fgServiceTip => '有効にすると、一部の機種でクラッシュする可能性があります。無効にすると、一部の機種でバックグラウンドでのSSH接続を維持できなくなる可能性があります。システム設定でServerBoxの通知権限、バックグラウンド実行、自己起動を許可してください。';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return 'ファイル \'$file\' は大きすぎます \'$size\'、$sizeMax を超えています';
diff --git a/lib/generated/l10n/l10n_nl.dart b/lib/generated/l10n/l10n_nl.dart
index 4fe062f9..e32d5ace 100644
--- a/lib/generated/l10n/l10n_nl.dart
+++ b/lib/generated/l10n/l10n_nl.dart
@@ -173,6 +173,12 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get fdroidReleaseTip => 'Als u deze app van F-Droid heeft gedownload, wordt aanbevolen deze optie uit te schakelen.';
+ @override
+ String get fgService => 'Voorgrondservice';
+
+ @override
+ String get fgServiceTip => 'Na het inschakelen kunnen sommige apparaatmodellen crashen. Uitschakelen kan ertoe leiden dat sommige modellen SSH-verbindingen niet op de achtergrond kunnen behouden. Sta ServerBox notificatierechten, achtergronduitvoering en zelf-ontwaken toe in systeeminstellingen.';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return 'Bestand \'$file\' te groot $size, max $sizeMax';
diff --git a/lib/generated/l10n/l10n_pt.dart b/lib/generated/l10n/l10n_pt.dart
index 53fd28af..0f6bb792 100644
--- a/lib/generated/l10n/l10n_pt.dart
+++ b/lib/generated/l10n/l10n_pt.dart
@@ -173,6 +173,12 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get fdroidReleaseTip => 'Se você baixou este aplicativo do F-Droid, é recomendado desativar esta opção.';
+ @override
+ String get fgService => 'Serviço em primeiro plano';
+
+ @override
+ String get fgServiceTip => 'Após ativar, alguns modelos de dispositivos podem travar. Desativar pode fazer com que alguns modelos não consigam manter conexões SSH em segundo plano. Por favor, permita as permissões de notificação do ServerBox, execução em segundo plano e auto-despertar nas configurações do sistema.';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return 'Arquivo \'$file\' muito grande \'$size\', excedendo $sizeMax';
diff --git a/lib/generated/l10n/l10n_ru.dart b/lib/generated/l10n/l10n_ru.dart
index d1bcee72..7bf19cca 100644
--- a/lib/generated/l10n/l10n_ru.dart
+++ b/lib/generated/l10n/l10n_ru.dart
@@ -173,6 +173,12 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get fdroidReleaseTip => 'Если вы скачали это приложение с F-Droid, рекомендуется отключить эту опцию.';
+ @override
+ String get fgService => 'Сервис переднего плана';
+
+ @override
+ String get fgServiceTip => 'После включения некоторые модели устройств могут вылетать. Отключение может привести к тому, что некоторые модели не смогут поддерживать SSH-соединения в фоновом режиме. Пожалуйста, разрешите ServerBox права на уведомления, фоновую работу и самопробуждение в системных настройках.';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return 'Файл \'$file\' слишком большой \'$size\', превышает $sizeMax';
diff --git a/lib/generated/l10n/l10n_tr.dart b/lib/generated/l10n/l10n_tr.dart
index 7ac689d2..be193930 100644
--- a/lib/generated/l10n/l10n_tr.dart
+++ b/lib/generated/l10n/l10n_tr.dart
@@ -173,6 +173,12 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get fdroidReleaseTip => 'Bu uygulamayı F-Droid\'den indirdiyseniz, bu seçeneği kapatmanız önerilir.';
+ @override
+ String get fgService => 'Ön plan hizmeti';
+
+ @override
+ String get fgServiceTip => 'Etkinleştirdikten sonra, bazı cihaz modellerinde çökme olabilir. Devre dışı bırakmak, bazı modellerin SSH bağlantılarını arka planda sürdürememesine neden olabilir. Lütfen sistem ayarlarında ServerBox bildirim izinlerine, arka planda çalışmaya ve kendiliğinden uyanmaya izin verin.';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return '\'$file\' dosyası çok büyük $size, maksimum $sizeMax';
diff --git a/lib/generated/l10n/l10n_uk.dart b/lib/generated/l10n/l10n_uk.dart
index 5a71b1b3..6fd83b4a 100644
--- a/lib/generated/l10n/l10n_uk.dart
+++ b/lib/generated/l10n/l10n_uk.dart
@@ -173,6 +173,12 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get fdroidReleaseTip => 'Якщо ви завантажили цей застосунок з F-Droid, рекомендується відключити цю опцію.';
+ @override
+ String get fgService => 'Служба переднього плану';
+
+ @override
+ String get fgServiceTip => 'Після увімкнення деякі моделі пристроїв можуть вилітати. Вимкнення може призвести до того, що деякі моделі не зможуть підтримувати SSH-з\'єднання у фоновому режимі. Будь ласка, дозвольте ServerBox права на сповіщення, фонову роботу та самопробудження в системних налаштуваннях.';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return 'Файл \'$file\' занадто великий ($size), макс $sizeMax';
diff --git a/lib/generated/l10n/l10n_zh.dart b/lib/generated/l10n/l10n_zh.dart
index f99de8f5..7d758dac 100644
--- a/lib/generated/l10n/l10n_zh.dart
+++ b/lib/generated/l10n/l10n_zh.dart
@@ -173,6 +173,12 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get fdroidReleaseTip => '如果你是从 F-Droid 下载的本应用,推荐关闭此选项';
+ @override
+ String get fgService => '前台服务';
+
+ @override
+ String get fgServiceTip => '开启后,可能会导致部分机型闪退。关闭可能导致部分机型无法后台保持 SSH 连接。请在系统设置内允许 ServerBox 通知权限、后台运行、自我唤醒。';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return '文件 \'$file\' 过大 \'$size\',超过了 $sizeMax';
@@ -869,6 +875,12 @@ class AppLocalizationsZhTw extends AppLocalizationsZh {
@override
String get fdroidReleaseTip => '如果你是從 F-Droid 下載的本應用,推薦關閉此選項';
+ @override
+ String get fgService => '前台服務';
+
+ @override
+ String get fgServiceTip => '開啟後,可能會導致部分機型閃退。關閉可能導致部分機型無法後台保持 SSH 連接。請在系統設置內允許 ServerBox 通知權限、後台運行、自我喚醒。';
+
@override
String fileTooLarge(Object file, Object size, Object sizeMax) {
return '文件 \'$file\' 過大 \'$size\',超過了 $sizeMax';
diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb
index b63e5607..4c5ce99d 100644
--- a/lib/l10n/app_de.arb
+++ b/lib/l10n/app_de.arb
@@ -53,6 +53,8 @@
"extraArgs": "Extra args",
"fallbackSshDest": "SSH-Fallback-Ziel",
"fdroidReleaseTip": "Wenn Sie diese App von F-Droid heruntergeladen haben, wird empfohlen, diese Option zu deaktivieren.",
+ "fgService": "Vordergrund-Dienst",
+ "fgServiceTip": "Nach dem Einschalten kann es bei einigen Gerätemodellen zu Abstürzen kommen. Das Ausschalten kann bei einigen Modellen dazu führen, dass SSH-Verbindungen im Hintergrund nicht aufrechterhalten werden können. Bitte erlauben Sie ServerBox in den Systemeinstellungen Benachrichtigungsrechte, Hintergrundausführung und Selbstaktivierung.",
"fileTooLarge": "Datei '{file}' ist zu groß {size}, max {sizeMax}",
"followSystem": "System verfolgen",
"font": "Schriftarten",
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index 205edde5..a818067f 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -53,6 +53,8 @@
"extraArgs": "Extra arguments",
"fallbackSshDest": "Fallback SSH destination",
"fdroidReleaseTip": "If you downloaded this app from F-Droid, it is recommended to turn off this option.",
+ "fgService": "Foreground Service",
+ "fgServiceTip": "After enabling, some device models may crash. Disabling it may cause some models to be unable to maintain SSH connections in the background. Please allow ServerBox notification permissions, background running, and self-wake-up in system settings.",
"fileTooLarge": "File '{file}' too large {size}, max {sizeMax}",
"followSystem": "Follow system",
"font": "Font",
diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb
index 169dceff..d01fc937 100644
--- a/lib/l10n/app_es.arb
+++ b/lib/l10n/app_es.arb
@@ -53,6 +53,8 @@
"extraArgs": "Argumentos extra",
"fallbackSshDest": "Destino SSH alternativo",
"fdroidReleaseTip": "Si descargaste esta aplicación desde F-Droid, se recomienda desactivar esta opción.",
+ "fgService": "Servicio en primer plano",
+ "fgServiceTip": "Después de activarlo, algunos modelos de dispositivos pueden bloquearse. Desactivarlo puede hacer que algunos modelos no puedan mantener las conexiones SSH en segundo plano. Por favor, permita los permisos de notificación de ServerBox, la ejecución en segundo plano y el auto-despertar en la configuración del sistema.",
"fileTooLarge": "El archivo '{file}' es demasiado grande '{size}', supera el {sizeMax}",
"followSystem": "Seguir al sistema",
"font": "Fuente",
diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb
index 7065bfc7..4eb9415c 100644
--- a/lib/l10n/app_fr.arb
+++ b/lib/l10n/app_fr.arb
@@ -53,6 +53,8 @@
"extraArgs": "Arguments supplémentaires",
"fallbackSshDest": "Destino SSH alternativo",
"fdroidReleaseTip": "Si vous avez téléchargé cette application depuis F-Droid, il est recommandé de désactiver cette option.",
+ "fgService": "Service de premier plan",
+ "fgServiceTip": "Après l'activation, certains modèles d'appareils peuvent planter. La désactivation peut empêcher certains modèles de maintenir les connexions SSH en arrière-plan. Veuillez autoriser les permissions de notification ServerBox, l'exécution en arrière-plan et l'auto-réveil dans les paramètres système.",
"fileTooLarge": "Fichier '{file}' trop volumineux {size}, max {sizeMax}",
"followSystem": "Suivre le système",
"font": "Police",
diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb
index e1590634..507804da 100644
--- a/lib/l10n/app_id.arb
+++ b/lib/l10n/app_id.arb
@@ -53,6 +53,8 @@
"extraArgs": "Args ekstra",
"fallbackSshDest": "Tujuan SSH mundur",
"fdroidReleaseTip": "Jika Anda mengunduh aplikasi ini dari F-Droid, disarankan untuk mematikan opsi ini.",
+ "fgService": "Layanan Latar Depan",
+ "fgServiceTip": "Setelah diaktifkan, beberapa model perangkat mungkin crash. Menonaktifkannya dapat menyebabkan beberapa model tidak dapat mempertahankan koneksi SSH di latar belakang. Harap izinkan perizinan notifikasi ServerBox, menjalankan di latar belakang, dan bangun mandiri di pengaturan sistem.",
"fileTooLarge": "File '{file}' terlalu besar {size}, max {sizeMax}",
"followSystem": "Ikuti sistem",
"font": "Font",
diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb
index ce939652..c30b9bb2 100644
--- a/lib/l10n/app_ja.arb
+++ b/lib/l10n/app_ja.arb
@@ -53,6 +53,8 @@
"extraArgs": "追加引数",
"fallbackSshDest": "フォールバックSSH宛先",
"fdroidReleaseTip": "このアプリをF-Droidからダウンロードした場合、このオプションをオフにすることをお勧めします。",
+ "fgService": "フォアグラウンドサービス",
+ "fgServiceTip": "有効にすると、一部の機種でクラッシュする可能性があります。無効にすると、一部の機種でバックグラウンドでのSSH接続を維持できなくなる可能性があります。システム設定でServerBoxの通知権限、バックグラウンド実行、自己起動を許可してください。",
"fileTooLarge": "ファイル '{file}' は大きすぎます '{size}'、{sizeMax} を超えています",
"followSystem": "システムに従う",
"font": "フォント",
diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb
index 21e6df81..a2558360 100644
--- a/lib/l10n/app_nl.arb
+++ b/lib/l10n/app_nl.arb
@@ -53,6 +53,8 @@
"extraArgs": "Extra argumenten",
"fallbackSshDest": "Fallback SSH-bestemming",
"fdroidReleaseTip": "Als u deze app van F-Droid heeft gedownload, wordt aanbevolen deze optie uit te schakelen.",
+ "fgService": "Voorgrondservice",
+ "fgServiceTip": "Na het inschakelen kunnen sommige apparaatmodellen crashen. Uitschakelen kan ertoe leiden dat sommige modellen SSH-verbindingen niet op de achtergrond kunnen behouden. Sta ServerBox notificatierechten, achtergronduitvoering en zelf-ontwaken toe in systeeminstellingen.",
"fileTooLarge": "Bestand '{file}' te groot {size}, max {sizeMax}",
"followSystem": "Volg systeem",
"font": "Lettertype",
diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb
index 082a8d58..82c56865 100644
--- a/lib/l10n/app_pt.arb
+++ b/lib/l10n/app_pt.arb
@@ -53,6 +53,8 @@
"extraArgs": "Argumentos extras",
"fallbackSshDest": "Destino SSH de fallback",
"fdroidReleaseTip": "Se você baixou este aplicativo do F-Droid, é recomendado desativar esta opção.",
+ "fgService": "Serviço em primeiro plano",
+ "fgServiceTip": "Após ativar, alguns modelos de dispositivos podem travar. Desativar pode fazer com que alguns modelos não consigam manter conexões SSH em segundo plano. Por favor, permita as permissões de notificação do ServerBox, execução em segundo plano e auto-despertar nas configurações do sistema.",
"fileTooLarge": "Arquivo '{file}' muito grande '{size}', excedendo {sizeMax}",
"followSystem": "Seguir sistema",
"font": "Fonte",
diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb
index 39556f39..9fad74ce 100644
--- a/lib/l10n/app_ru.arb
+++ b/lib/l10n/app_ru.arb
@@ -53,6 +53,8 @@
"extraArgs": "Дополнительные аргументы",
"fallbackSshDest": "Резервное место назначения SSH",
"fdroidReleaseTip": "Если вы скачали это приложение с F-Droid, рекомендуется отключить эту опцию.",
+ "fgService": "Сервис переднего плана",
+ "fgServiceTip": "После включения некоторые модели устройств могут вылетать. Отключение может привести к тому, что некоторые модели не смогут поддерживать SSH-соединения в фоновом режиме. Пожалуйста, разрешите ServerBox права на уведомления, фоновую работу и самопробуждение в системных настройках.",
"fileTooLarge": "Файл '{file}' слишком большой '{size}', превышает {sizeMax}",
"followSystem": "Следовать за системой",
"font": "Шрифт",
diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb
index 1433b829..5b39834a 100644
--- a/lib/l10n/app_tr.arb
+++ b/lib/l10n/app_tr.arb
@@ -53,6 +53,8 @@
"extraArgs": "Ek argümanlar",
"fallbackSshDest": "Yedek SSH hedefi",
"fdroidReleaseTip": "Bu uygulamayı F-Droid'den indirdiyseniz, bu seçeneği kapatmanız önerilir.",
+ "fgService": "Ön plan hizmeti",
+ "fgServiceTip": "Etkinleştirdikten sonra, bazı cihaz modellerinde çökme olabilir. Devre dışı bırakmak, bazı modellerin SSH bağlantılarını arka planda sürdürememesine neden olabilir. Lütfen sistem ayarlarında ServerBox bildirim izinlerine, arka planda çalışmaya ve kendiliğinden uyanmaya izin verin.",
"fileTooLarge": "'{file}' dosyası çok büyük {size}, maksimum {sizeMax}",
"followSystem": "Sistemi takip et",
"font": "Yazı tipi",
diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb
index 4e281320..19207cf4 100644
--- a/lib/l10n/app_uk.arb
+++ b/lib/l10n/app_uk.arb
@@ -53,6 +53,8 @@
"extraArgs": "Додаткові аргументи",
"fallbackSshDest": "Резервна SSH адреса",
"fdroidReleaseTip": "Якщо ви завантажили цей застосунок з F-Droid, рекомендується відключити цю опцію.",
+ "fgService": "Служба переднього плану",
+ "fgServiceTip": "Після увімкнення деякі моделі пристроїв можуть вилітати. Вимкнення може призвести до того, що деякі моделі не зможуть підтримувати SSH-з'єднання у фоновому режимі. Будь ласка, дозвольте ServerBox права на сповіщення, фонову роботу та самопробудження в системних налаштуваннях.",
"fileTooLarge": "Файл '{file}' занадто великий ({size}), макс {sizeMax}",
"followSystem": "Слідувати системі",
"font": "Шрифт",
diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb
index 1156a865..300dfc49 100644
--- a/lib/l10n/app_zh.arb
+++ b/lib/l10n/app_zh.arb
@@ -53,6 +53,8 @@
"extraArgs": "额外参数",
"fallbackSshDest": "备选 SSH 目标",
"fdroidReleaseTip": "如果你是从 F-Droid 下载的本应用,推荐关闭此选项",
+ "fgService": "前台服务",
+ "fgServiceTip": "开启后,可能会导致部分机型闪退。关闭可能导致部分机型无法后台保持 SSH 连接。请在系统设置内允许 ServerBox 通知权限、后台运行、自我唤醒。",
"fileTooLarge": "文件 '{file}' 过大 '{size}',超过了 {sizeMax}",
"followSystem": "跟随系统",
"font": "字体",
diff --git a/lib/l10n/app_zh_tw.arb b/lib/l10n/app_zh_tw.arb
index d83bb0b5..09b37da1 100644
--- a/lib/l10n/app_zh_tw.arb
+++ b/lib/l10n/app_zh_tw.arb
@@ -53,6 +53,8 @@
"extraArgs": "額外參數",
"fallbackSshDest": "備選 SSH 目標",
"fdroidReleaseTip": "如果你是從 F-Droid 下載的本應用,推薦關閉此選項",
+ "fgService": "前台服務",
+ "fgServiceTip": "開啟後,可能會導致部分機型閃退。關閉可能導致部分機型無法後台保持 SSH 連接。請在系統設置內允許 ServerBox 通知權限、後台運行、自我喚醒。",
"fileTooLarge": "文件 '{file}' 過大 '{size}',超過了 {sizeMax}",
"followSystem": "跟隨系統",
"font": "字型",
diff --git a/lib/view/page/setting/platform/android.dart b/lib/view/page/setting/platform/android.dart
index ea9d927a..b264fdca 100644
--- a/lib/view/page/setting/platform/android.dart
+++ b/lib/view/page/setting/platform/android.dart
@@ -21,6 +21,7 @@ class _AndroidSettingsPageState extends State {
body: ListView(
padding: const EdgeInsets.symmetric(horizontal: 17),
children: [
+ _buildFgService(),
_buildBgRun(),
_buildAndroidWidgetSharedPreference(),
if (BioAuth.isPlatformSupported)
@@ -115,4 +116,11 @@ class _AndroidSettingsPageState extends State {
// },
// );
// }
+
+ Widget _buildFgService() {
+ return ListTile(
+ title: TipText(l10n.fgService, l10n.fgServiceTip),
+ trailing: StoreSwitch(prop: Stores.setting.fgService),
+ );
+ }
}
diff --git a/lib/view/page/ssh/page.dart b/lib/view/page/ssh/page.dart
index cc485fc9..36e8796e 100644
--- a/lib/view/page/ssh/page.dart
+++ b/lib/view/page/ssh/page.dart
@@ -76,7 +76,6 @@ class SSHPageState extends State
@override
void dispose() {
- super.dispose();
_virtKeyLongPressTimer?.cancel();
_terminalController.dispose();
_discontinuityTimer?.cancel();
@@ -87,6 +86,7 @@ class SSHPageState extends State
MethodChans.stopService();
}
}
+ super.dispose();
}
@override
@@ -270,6 +270,161 @@ class SSHPageState extends State
);
}
+ @override
+ bool get wantKeepAlive => true;
+
+ void _initStoredCfg() {
+ final fontFamilly = Stores.setting.fontPath.fetch().getFileName();
+ final textSize = Stores.setting.termFontSize.fetch();
+ final textStyle = TextStyle(
+ fontFamily: fontFamilly,
+ fontSize: textSize,
+ );
+
+ _terminalStyle = TerminalStyle.fromTextStyle(textStyle);
+ }
+
+ Future _showHelp() async {
+ if (Stores.setting.sshTermHelpShown.fetch()) return;
+
+ return await context.showRoundDialog(
+ title: libL10n.doc,
+ child: Text(l10n.sshTermHelp),
+ actions: [
+ TextButton(
+ onPressed: () {
+ Stores.setting.sshTermHelpShown.put(true);
+ context.pop();
+ },
+ child: Text(l10n.noPromptAgain),
+ ),
+ ],
+ );
+ }
+
+ @override
+ FutureOr afterFirstLayout(BuildContext context) async {
+ await _showHelp();
+ await _initTerminal();
+
+ if (Stores.setting.sshWakeLock.fetch()) WakelockPlus.enable();
+ }
+}
+
+extension _Init on SSHPageState {
+ Future _initTerminal() async {
+ _writeLn(l10n.waitConnection);
+ _client ??= await genClient(
+ widget.spi,
+ onStatus: (p0) {
+ _writeLn(p0.toString());
+ },
+ onKeyboardInteractive: _onKeyboardInteractive,
+ );
+
+ _writeLn('${libL10n.execute}: Shell');
+ final session = await _client?.shell(
+ pty: SSHPtyConfig(
+ width: _terminal.viewWidth,
+ height: _terminal.viewHeight,
+ ),
+ environment: widget.spi.envs,
+ );
+
+ //_setupDiscontinuityTimer();
+
+ if (session == null) {
+ _writeLn(libL10n.fail);
+ return;
+ }
+
+ _terminal.buffer.clear();
+ _terminal.buffer.setCursor(0, 0);
+
+ _terminal.onOutput = (data) {
+ session.write(utf8.encode(data));
+ };
+ _terminal.onResize = (width, height, pixelWidth, pixelHeight) {
+ session.resizeTerminal(width, height);
+ };
+
+ _listen(session.stdout);
+ _listen(session.stderr);
+
+ for (final snippet in SnippetProvider.snippets.value) {
+ if (snippet.autoRunOn?.contains(widget.spi.id) == true) {
+ snippet.runInTerm(_terminal, widget.spi);
+ }
+ }
+
+ if (widget.initCmd != null) {
+ _terminal.textInput(widget.initCmd!);
+ _terminal.keyInput(TerminalKey.enter);
+ }
+
+ if (widget.initSnippet != null) {
+ widget.initSnippet!.runInTerm(_terminal, widget.spi);
+ }
+
+ widget.focusNode?.requestFocus();
+
+ await session.done;
+ if (mounted && widget.notFromTab) {
+ context.pop();
+ }
+ widget.onSessionEnd?.call();
+ }
+
+ void _listen(Stream? stream) {
+ if (stream == null) {
+ return;
+ }
+ stream
+ .cast>()
+ .transform(const Utf8Decoder())
+ .listen(_terminal.write);
+ }
+
+ void _setupDiscontinuityTimer() {
+ _discontinuityTimer = Timer.periodic(
+ const Duration(seconds: 5),
+ (_) async {
+ var throwTimeout = true;
+ Future.delayed(const Duration(seconds: 3), () {
+ if (throwTimeout) {
+ _catchTimeout();
+ }
+ });
+ await _client?.ping();
+ throwTimeout = false;
+ },
+ );
+ }
+
+ void _catchTimeout() {
+ _discontinuityTimer?.cancel();
+ if (!mounted) return;
+ _writeLn('\n\nConnection lost\r\n');
+ context.showRoundDialog(
+ title: libL10n.attention,
+ child: Text('${l10n.disconnected}\n${l10n.goBackQ}'),
+ barrierDismiss: false,
+ actions: Btn.ok(
+ onTap: () {
+ if (mounted) {
+ context.pop();
+ }
+ },
+ ).toList,
+ );
+ }
+
+ void _writeLn(String p0) {
+ _terminal.write('$p0\r\n');
+ }
+}
+
+extension _VirtKey on SSHPageState {
void _doVirtualKey(VirtKey item) {
if (item.func != null) {
HapticFeedback.mediumImpact();
@@ -380,10 +535,6 @@ class SSHPageState extends State
return _terminal.buffer.getText(range);
}
- void _writeLn(String p0) {
- _terminal.write('$p0\r\n');
- }
-
void _initVirtKeys() {
final virtKeys = VirtKeyX.loadFromStore();
for (int len = 0; len < virtKeys.length; len += 7) {
@@ -398,151 +549,4 @@ class SSHPageState extends State
FutureOr?> _onKeyboardInteractive(SSHUserInfoRequest req) {
return KeybordInteractive.defaultHandle(widget.spi, ctx: context);
}
-
- Future _initTerminal() async {
- _writeLn(l10n.waitConnection);
- _client ??= await genClient(
- widget.spi,
- onStatus: (p0) {
- _writeLn(p0.toString());
- },
- onKeyboardInteractive: _onKeyboardInteractive,
- );
-
- _writeLn('${libL10n.execute}: Shell');
- final session = await _client?.shell(
- pty: SSHPtyConfig(
- width: _terminal.viewWidth,
- height: _terminal.viewHeight,
- ),
- environment: widget.spi.envs,
- );
-
- //_setupDiscontinuityTimer();
-
- if (session == null) {
- _writeLn(libL10n.fail);
- return;
- }
-
- _terminal.buffer.clear();
- _terminal.buffer.setCursor(0, 0);
-
- _terminal.onOutput = (data) {
- session.write(utf8.encode(data));
- };
- _terminal.onResize = (width, height, pixelWidth, pixelHeight) {
- session.resizeTerminal(width, height);
- };
-
- _listen(session.stdout);
- _listen(session.stderr);
-
- for (final snippet in SnippetProvider.snippets.value) {
- if (snippet.autoRunOn?.contains(widget.spi.id) == true) {
- snippet.runInTerm(_terminal, widget.spi);
- }
- }
-
- if (widget.initCmd != null) {
- _terminal.textInput(widget.initCmd!);
- _terminal.keyInput(TerminalKey.enter);
- }
-
- if (widget.initSnippet != null) {
- widget.initSnippet!.runInTerm(_terminal, widget.spi);
- }
-
- widget.focusNode?.requestFocus();
-
- await session.done;
- if (mounted && widget.notFromTab) {
- context.pop();
- }
- widget.onSessionEnd?.call();
- }
-
- void _listen(Stream? stream) {
- if (stream == null) {
- return;
- }
- stream
- .cast>()
- .transform(const Utf8Decoder())
- .listen(_terminal.write);
- }
-
- void _setupDiscontinuityTimer() {
- _discontinuityTimer = Timer.periodic(
- const Duration(seconds: 5),
- (_) async {
- var throwTimeout = true;
- Future.delayed(const Duration(seconds: 3), () {
- if (throwTimeout) {
- _catchTimeout();
- }
- });
- await _client?.ping();
- throwTimeout = false;
- },
- );
- }
-
- void _catchTimeout() {
- _discontinuityTimer?.cancel();
- if (!mounted) return;
- _writeLn('\n\nConnection lost\r\n');
- context.showRoundDialog(
- title: libL10n.attention,
- child: Text('${l10n.disconnected}\n${l10n.goBackQ}'),
- barrierDismiss: false,
- actions: Btn.ok(
- onTap: () {
- if (mounted) {
- context.pop();
- }
- },
- ).toList,
- );
- }
-
- @override
- bool get wantKeepAlive => true;
-
- void _initStoredCfg() {
- final fontFamilly = Stores.setting.fontPath.fetch().getFileName();
- final textSize = Stores.setting.termFontSize.fetch();
- final textStyle = TextStyle(
- fontFamily: fontFamilly,
- fontSize: textSize,
- );
-
- _terminalStyle = TerminalStyle.fromTextStyle(textStyle);
- }
-
- Future _showHelp() async {
- if (Stores.setting.sshTermHelpShown.fetch()) return;
-
- return await context.showRoundDialog(
- title: libL10n.doc,
- child: Text(l10n.sshTermHelp),
- actions: [
- TextButton(
- onPressed: () {
- Stores.setting.sshTermHelpShown.put(true);
- context.pop();
- },
- child: Text(l10n.noPromptAgain),
- ),
- ],
- );
- }
-
- @override
- FutureOr afterFirstLayout(BuildContext context) async {
- await _showHelp();
- await _initTerminal();
-
- if (Stores.setting.sshWakeLock.fetch()) WakelockPlus.enable();
- }
}
diff --git a/lib/view/page/ssh/tab.dart b/lib/view/page/ssh/tab.dart
index 6ef12be0..98b92441 100644
--- a/lib/view/page/ssh/tab.dart
+++ b/lib/view/page/ssh/tab.dart
@@ -67,30 +67,6 @@ class _SSHTabPageState extends State
);
}
- void _onTapTab(int idx) async {
- await _toPage(idx);
- }
-
- void _onTapClose(String name) async {
- final confirm = await showDialog(
- context: context,
- builder: (context) {
- return AlertDialog(
- title: Text(libL10n.attention),
- content: Text('${libL10n.close} SSH ${l10n.conn}($name) ?'),
- actions: Btnx.okReds,
- );
- },
- );
- Future.delayed(Durations.short1, FocusScope.of(context).unfocus);
- if (confirm != true) return;
-
- _tabMap.remove(name);
- _tabRN.notify();
- _pageCtrl.previousPage(
- duration: Durations.medium1, curve: Curves.fastEaseInToSlowEaseOut);
- }
-
Widget _buildBody() {
return ListenBuilder(
listenable: _tabRN,
@@ -109,6 +85,11 @@ class _SSHTabPageState extends State
);
}
+ @override
+ bool get wantKeepAlive => true;
+}
+
+extension on _SSHTabPageState {
void _onTapInitCard(Spi spi) async {
final name = () {
final reg = RegExp('${spi.name}\\((\\d+)\\)');
@@ -155,8 +136,29 @@ class _SSHTabPageState extends State
}
}
- @override
- bool get wantKeepAlive => true;
+ void _onTapTab(int idx) async {
+ await _toPage(idx);
+ }
+
+ void _onTapClose(String name) async {
+ final confirm = await showDialog(
+ context: context,
+ builder: (context) {
+ return AlertDialog(
+ title: Text(libL10n.attention),
+ content: Text('${libL10n.close} SSH ${l10n.conn}($name) ?'),
+ actions: Btnx.okReds,
+ );
+ },
+ );
+ Future.delayed(Durations.short1, FocusScope.of(context).unfocus);
+ if (confirm != true) return;
+
+ _tabMap.remove(name);
+ _tabRN.notify();
+ _pageCtrl.previousPage(
+ duration: Durations.medium1, curve: Curves.fastEaseInToSlowEaseOut);
+ }
}
final class _TabBar extends StatelessWidget implements PreferredSizeWidget {
diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj
index aea36655..30b6d1be 100644
--- a/macos/Runner.xcodeproj/project.pbxproj
+++ b/macos/Runner.xcodeproj/project.pbxproj
@@ -471,7 +471,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- CURRENT_PROJECT_VERSION = 1117;
+ CURRENT_PROJECT_VERSION = 1124;
DEVELOPMENT_TEAM = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
@@ -481,7 +481,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
- MARKETING_VERSION = 1.0.1117;
+ MARKETING_VERSION = 1.0.1124;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -608,7 +608,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- CURRENT_PROJECT_VERSION = 1117;
+ CURRENT_PROJECT_VERSION = 1124;
DEVELOPMENT_TEAM = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
@@ -618,7 +618,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
- MARKETING_VERSION = 1.0.1117;
+ MARKETING_VERSION = 1.0.1124;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -638,7 +638,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
- CURRENT_PROJECT_VERSION = 1117;
+ CURRENT_PROJECT_VERSION = 1124;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist;
@@ -649,7 +649,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
- MARKETING_VERSION = 1.0.1117;
+ MARKETING_VERSION = 1.0.1124;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = "";
diff --git a/pubspec.yaml b/pubspec.yaml
index 347afb6b..51dcba9b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,7 +1,7 @@
name: server_box
description: server status & toolbox app.
publish_to: 'none'
-version: 1.0.1117+1117
+version: 1.0.1124+1124
environment:
sdk: ">=3.0.0"