mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2026-02-15 12:44:59 +01:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17db393c12 | ||
|
|
275581cfa3 | ||
|
|
d7168ea1ff | ||
|
|
fd2bf08f78 | ||
|
|
98e13c39cf | ||
|
|
e70abeef04 | ||
|
|
194774d6fb | ||
|
|
640d61bab9 | ||
|
|
7f4cf22cc9 | ||
|
|
05a927753f | ||
|
|
0c7b72fb2c | ||
|
|
a869b97502 | ||
|
|
eadd343205 | ||
|
|
1bac986fe0 | ||
|
|
a94be6c2c3 |
21
.github/workflows/analysis.yml
vendored
21
.github/workflows/analysis.yml
vendored
@@ -17,17 +17,28 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: 'stable' # or: 'beta', 'dev' or 'master'
|
||||
channel: 'stable'
|
||||
cache: true
|
||||
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
|
||||
|
||||
- name: Cache pub dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ env.PUB_CACHE }}
|
||||
~/.pub-cache
|
||||
key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pub-
|
||||
|
||||
- name: Install dependencies
|
||||
run: flutter pub get
|
||||
|
||||
# Uncomment this step to verify the use of 'dart format' on each commit.
|
||||
- name: Verify formatting
|
||||
run: dart format --output=none .
|
||||
|
||||
# Consider passing '--fatal-infos' for slightly stricter analysis.
|
||||
- name: Analyze project source
|
||||
run: dart analyze
|
||||
|
||||
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
@@ -9,6 +9,11 @@ on:
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
# Set by fl_build
|
||||
# env:
|
||||
# APP_NAME: ServerBox
|
||||
# BUILD_NUMBER: ${{ github.ref_name }}
|
||||
|
||||
jobs:
|
||||
releaseAndroid:
|
||||
name: Release android
|
||||
@@ -20,7 +25,7 @@ jobs:
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: "3.35.1"
|
||||
flutter-version: "3.35.3"
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: "zulu"
|
||||
@@ -98,16 +103,12 @@ jobs:
|
||||
# uses: actions/checkout@v4
|
||||
# - name: Install Flutter
|
||||
# uses: subosito/flutter-action@v2
|
||||
# with:
|
||||
# channel: 'stable'
|
||||
# flutter-version: '3.32.1'
|
||||
# - name: Build
|
||||
# run: dart run fl_build -p ios,mac
|
||||
# run: dart run fl_build -p ios
|
||||
# - name: Create Release
|
||||
# uses: softprops/action-gh-release@v2
|
||||
# with:
|
||||
# files: |
|
||||
# ${{ env.APP_NAME }}_universal_macos.zip
|
||||
# ${{ env.APP_NAME }}_universal.ipa
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -113,7 +113,7 @@ android.applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
def abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
|
||||
if (abiVersionCode != null) {
|
||||
output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode
|
||||
output.versionCodeOverride = variant.versionCode * 100 + abiVersionCode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package tech.lolli.toolbox
|
||||
|
||||
import android.app.*
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
@@ -16,8 +18,7 @@ class ForegroundService : Service() {
|
||||
var isRunning: Boolean = false
|
||||
}
|
||||
private val chanId = "ForegroundServiceChannel"
|
||||
private val GROUP_KEY = "ssh_sessions_group"
|
||||
private val SUMMARY_ID = 1000
|
||||
private val NOTIFICATION_ID = 1000
|
||||
private val ACTION_STOP_FOREGROUND = "ACTION_STOP_FOREGROUND"
|
||||
private val ACTION_UPDATE_SESSIONS = "tech.lolli.toolbox.ACTION_UPDATE_SESSIONS"
|
||||
private val ACTION_DISCONNECT_SESSION = "tech.lolli.toolbox.ACTION_DISCONNECT_SESSION"
|
||||
@@ -49,19 +50,22 @@ class ForegroundService : Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
try {
|
||||
// Check notification permission for Android 13+
|
||||
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()
|
||||
Log.w("ForegroundService", "Notification permission denied. Stopping service gracefully.")
|
||||
// Don't call stopForegroundService() here as we haven't started foreground yet
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
if (intent == null) {
|
||||
Log.w("ForegroundService", "onStartCommand called with null intent")
|
||||
stopForegroundService()
|
||||
// Don't call stopForegroundService() here as we haven't started foreground yet
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
@@ -70,6 +74,9 @@ class ForegroundService : Service() {
|
||||
|
||||
return when (action) {
|
||||
ACTION_STOP_FOREGROUND -> {
|
||||
// Notify Flutter to stop all connections before stopping service
|
||||
val stopAllIntent = Intent("tech.lolli.toolbox.STOP_ALL_CONNECTIONS")
|
||||
sendBroadcast(stopAllIntent)
|
||||
clearAll()
|
||||
stopForegroundService()
|
||||
START_NOT_STICKY
|
||||
@@ -81,7 +88,7 @@ class ForegroundService : Service() {
|
||||
}
|
||||
else -> {
|
||||
// Default bring up foreground with placeholder
|
||||
ensureForeground(createSummaryNotification(0, emptyList()))
|
||||
ensureForeground(createMergedNotification(0, emptyList(), emptyList()))
|
||||
START_STICKY
|
||||
}
|
||||
}
|
||||
@@ -99,37 +106,67 @@ class ForegroundService : Service() {
|
||||
|
||||
private fun createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val manager = getSystemService(NotificationManager::class.java)
|
||||
if (manager == null) {
|
||||
Log.e("ForegroundService", "Failed to get NotificationManager")
|
||||
return
|
||||
try {
|
||||
val manager = getSystemService(NotificationManager::class.java)
|
||||
if (manager == null) {
|
||||
Log.e("ForegroundService", "Failed to get NotificationManager")
|
||||
return
|
||||
}
|
||||
val serviceChannel = NotificationChannel(
|
||||
chanId,
|
||||
"ForegroundServiceChannel",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
).apply {
|
||||
description = "For foreground service"
|
||||
}
|
||||
manager.createNotificationChannel(serviceChannel)
|
||||
Log.d("ForegroundService", "Notification channel created successfully")
|
||||
} catch (e: Exception) {
|
||||
logError("Failed to create notification channel", e)
|
||||
}
|
||||
val serviceChannel = NotificationChannel(
|
||||
chanId,
|
||||
"ForegroundServiceChannel",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
).apply {
|
||||
description = "For foreground service"
|
||||
}
|
||||
manager.createNotificationChannel(serviceChannel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureForeground(notification: Notification) {
|
||||
try {
|
||||
// Double-check notification permission before starting foreground service
|
||||
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", "Cannot start foreground service without notification permission")
|
||||
stopSelf()
|
||||
return
|
||||
}
|
||||
|
||||
if (!isFgStarted) {
|
||||
startForeground(SUMMARY_ID, notification)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
} else {
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
}
|
||||
isFgStarted = true
|
||||
Log.d("ForegroundService", "Foreground service started successfully")
|
||||
} else {
|
||||
val nm = getSystemService(NotificationManager::class.java)
|
||||
nm?.notify(SUMMARY_ID, notification)
|
||||
if (nm != null) {
|
||||
nm.notify(NOTIFICATION_ID, notification)
|
||||
} else {
|
||||
Log.w("ForegroundService", "NotificationManager is null, cannot update notification")
|
||||
}
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
logError("Security exception when starting foreground service (likely missing permission)", e)
|
||||
stopSelf()
|
||||
} catch (e: Exception) {
|
||||
logError("Failed to start/update foreground", e)
|
||||
// Don't stop the service for other exceptions, just log them
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSummaryNotification(count: Int, lines: List<String>): Notification {
|
||||
|
||||
private fun createMergedNotification(count: Int, lines: List<String>, sessions: List<SessionItem>): Notification {
|
||||
val notificationIntent = Intent(this, MainActivity::class.java)
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE
|
||||
@@ -140,24 +177,66 @@ class ForegroundService : Service() {
|
||||
val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Notification.Builder(this, chanId)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
Notification.Builder(this)
|
||||
}
|
||||
|
||||
val inbox = Notification.InboxStyle()
|
||||
lines.forEach { inbox.addLine(it) }
|
||||
// Use the earliest session's start time for chronometer
|
||||
val earliestStartTime = sessions.minOfOrNull { it.startWhen } ?: System.currentTimeMillis()
|
||||
|
||||
return builder
|
||||
.setContentTitle("SSH sessions: $count active")
|
||||
.setContentText(if (lines.isNotEmpty()) lines.first() else "Running")
|
||||
val title = when (count) {
|
||||
0 -> "Server Box"
|
||||
1 -> sessions.first().title
|
||||
else -> "SSH sessions: $count active"
|
||||
}
|
||||
|
||||
val contentText = when (count) {
|
||||
0 -> "Ready for connections"
|
||||
1 -> {
|
||||
val session = sessions.first()
|
||||
"${session.subtitle} · ${session.status}"
|
||||
}
|
||||
else -> "Multiple SSH connections active"
|
||||
}
|
||||
|
||||
// For multiple sessions, show details in expanded view
|
||||
val style = if (count > 1) {
|
||||
val inbox = Notification.InboxStyle()
|
||||
val maxLines = 5
|
||||
val displayLines = if (lines.size > maxLines) {
|
||||
lines.take(maxLines) + "...and ${lines.size - maxLines} more"
|
||||
} else {
|
||||
lines
|
||||
}
|
||||
displayLines.forEach { inbox.addLine(it) }
|
||||
inbox.setBigContentTitle(title)
|
||||
inbox
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val notification = builder
|
||||
.setContentTitle(title)
|
||||
.setContentText(contentText)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setStyle(inbox)
|
||||
.setWhen(earliestStartTime)
|
||||
.setUsesChronometer(true)
|
||||
.setOngoing(true)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setGroup(GROUP_KEY)
|
||||
.setGroupSummary(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
.addAction(android.R.drawable.ic_delete, "Stop", stopPending)
|
||||
.build()
|
||||
.addAction(
|
||||
Notification.Action.Builder(
|
||||
Icon.createWithResource(this, android.R.drawable.ic_delete),
|
||||
"Stop All",
|
||||
stopPending
|
||||
).build()
|
||||
)
|
||||
|
||||
if (style != null) {
|
||||
notification.setStyle(style)
|
||||
}
|
||||
|
||||
return notification.build()
|
||||
}
|
||||
|
||||
private fun handleUpdateSessions(payload: String) {
|
||||
@@ -192,71 +271,21 @@ class ForegroundService : Service() {
|
||||
return
|
||||
}
|
||||
|
||||
// Build per-session notifications
|
||||
val currentIds = mutableSetOf<Int>()
|
||||
val summaryLines = mutableListOf<String>()
|
||||
sessions.forEach { s ->
|
||||
// Assign a stable, collision-resistant id per session for this service lifecycle
|
||||
val nid = notificationIdMap.getOrPut(s.id) { nextNotificationId.getAndIncrement() }
|
||||
currentIds.add(nid)
|
||||
summaryLines.add("${s.title}: ${s.status}")
|
||||
|
||||
val disconnectIntent = Intent(this, MainActivity::class.java).apply {
|
||||
action = ACTION_DISCONNECT_SESSION
|
||||
putExtra("session_id", s.id)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
}
|
||||
val disconnectPending = PendingIntent.getActivity(
|
||||
this, nid, disconnectIntent, PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Notification.Builder(this, chanId)
|
||||
} else {
|
||||
Notification.Builder(this)
|
||||
}
|
||||
|
||||
val noti = builder
|
||||
.setContentTitle(s.title)
|
||||
.setContentText("${s.subtitle} · ${s.status}")
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setWhen(s.startWhen)
|
||||
.setUsesChronometer(true)
|
||||
.setOngoing(true)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setGroup(GROUP_KEY)
|
||||
.addAction(android.R.drawable.ic_media_pause, "Disconnect", disconnectPending)
|
||||
.build()
|
||||
|
||||
nm.notify(nid, noti)
|
||||
}
|
||||
|
||||
// Cancel stale ones
|
||||
val toCancel = postedIds - currentIds
|
||||
// Cancel any existing individual notifications (we only show merged notification now)
|
||||
val toCancel = postedIds.toSet()
|
||||
toCancel.forEach { nm.cancel(it) }
|
||||
// Clean up id mappings for canceled notifications to prevent growth
|
||||
if (toCancel.isNotEmpty()) {
|
||||
val keysToRemove = notificationIdMap.filterValues { it in toCancel }.keys
|
||||
keysToRemove.forEach { notificationIdMap.remove(it) }
|
||||
}
|
||||
postedIds.clear()
|
||||
postedIds.addAll(currentIds)
|
||||
notificationIdMap.clear()
|
||||
|
||||
// Post/update summary and ensure foreground
|
||||
val maxSummaryLines = 5
|
||||
val truncated = summaryLines.size > maxSummaryLines
|
||||
val displaySummaryLines = if (truncated) {
|
||||
summaryLines.take(maxSummaryLines) + "...and ${summaryLines.size - maxSummaryLines} more"
|
||||
} else {
|
||||
summaryLines
|
||||
}
|
||||
val summary = createSummaryNotification(sessions.size, displaySummaryLines)
|
||||
ensureForeground(summary)
|
||||
// Create merged notification content
|
||||
val summaryLines = sessions.map { "${it.title}: ${it.status}" }
|
||||
val mergedNotification = createMergedNotification(sessions.size, summaryLines, sessions)
|
||||
ensureForeground(mergedNotification)
|
||||
}
|
||||
|
||||
private fun clearAll() {
|
||||
val nm = getSystemService(NotificationManager::class.java)
|
||||
nm?.cancel(SUMMARY_ID)
|
||||
nm?.cancel(NOTIFICATION_ID)
|
||||
postedIds.forEach { id -> nm?.cancel(id) }
|
||||
postedIds.clear()
|
||||
isFgStarted = false
|
||||
@@ -272,7 +301,10 @@ class ForegroundService : Service() {
|
||||
|
||||
private fun stopForegroundService() {
|
||||
try {
|
||||
stopForeground(true)
|
||||
if (isFgStarted) {
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
isFgStarted = false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logError("Error stopping foreground", e)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.Manifest
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.IntentFilter
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import io.flutter.embedding.android.FlutterFragmentActivity
|
||||
@@ -16,6 +19,8 @@ class MainActivity: FlutterFragmentActivity() {
|
||||
private lateinit var channel: MethodChannel
|
||||
private val ACTION_UPDATE_SESSIONS = "tech.lolli.toolbox.ACTION_UPDATE_SESSIONS"
|
||||
private val ACTION_DISCONNECT_SESSION = "tech.lolli.toolbox.ACTION_DISCONNECT_SESSION"
|
||||
private val ACTION_STOP_ALL_CONNECTIONS = "tech.lolli.toolbox.STOP_ALL_CONNECTIONS"
|
||||
private var stopAllReceiver: BroadcastReceiver? = null
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
@@ -92,24 +97,32 @@ class MainActivity: FlutterFragmentActivity() {
|
||||
|
||||
// Handle intent if launched via notification action
|
||||
handleActionIntent(intent)
|
||||
|
||||
// Register broadcast receiver for stop all connections
|
||||
setupStopAllReceiver()
|
||||
}
|
||||
|
||||
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) {
|
||||
try {
|
||||
try {
|
||||
// Check if we already have the permission to avoid unnecessary prompts
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
// Check if we should show rationale
|
||||
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.POST_NOTIFICATIONS)) {
|
||||
android.util.Log.i("MainActivity", "User previously denied notification permission")
|
||||
}
|
||||
|
||||
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}")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Log error but don't crash
|
||||
android.util.Log.e("MainActivity", "Failed to request permissions: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,4 +154,52 @@ class MainActivity: FlutterFragmentActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupStopAllReceiver() {
|
||||
stopAllReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent?.action == ACTION_STOP_ALL_CONNECTIONS && ::channel.isInitialized) {
|
||||
try {
|
||||
channel.invokeMethod("stopAllConnections", null)
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("MainActivity", "Failed to invoke stopAllConnections: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val filter = IntentFilter(ACTION_STOP_ALL_CONNECTIONS)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
ContextCompat.registerReceiver(this, stopAllReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
|
||||
} else {
|
||||
registerReceiver(stopAllReceiver, filter)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == 123) {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
android.util.Log.i("MainActivity", "Notification permission granted")
|
||||
} else {
|
||||
android.util.Log.w("MainActivity", "Notification permission denied")
|
||||
// Optionally inform user about the limitation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
stopAllReceiver?.let {
|
||||
try {
|
||||
unregisterReceiver(it)
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("MainActivity", "Failed to unregister receiver: ${e.message}")
|
||||
}
|
||||
stopAllReceiver = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
flutter_server_box.wiki
Submodule
1
flutter_server_box.wiki
Submodule
Submodule flutter_server_box.wiki added at f440010313
@@ -748,7 +748,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
CURRENT_PROJECT_VERSION = 1256;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -758,7 +758,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1241;
|
||||
MARKETING_VERSION = 1.0.1256;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -884,7 +884,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
CURRENT_PROJECT_VERSION = 1256;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -894,7 +894,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1241;
|
||||
MARKETING_VERSION = 1.0.1256;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -912,7 +912,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
CURRENT_PROJECT_VERSION = 1256;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -922,7 +922,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1241;
|
||||
MARKETING_VERSION = 1.0.1256;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -943,7 +943,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
CURRENT_PROJECT_VERSION = 1256;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -956,7 +956,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1241;
|
||||
MARKETING_VERSION = 1.0.1256;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
@@ -982,7 +982,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
CURRENT_PROJECT_VERSION = 1256;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -995,7 +995,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1241;
|
||||
MARKETING_VERSION = 1.0.1256;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1018,7 +1018,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
CURRENT_PROJECT_VERSION = 1256;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1031,7 +1031,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1241;
|
||||
MARKETING_VERSION = 1.0.1256;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1054,7 +1054,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
CURRENT_PROJECT_VERSION = 1256;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1066,7 +1066,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1241;
|
||||
MARKETING_VERSION = 1.0.1256;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||
@@ -1095,7 +1095,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
CURRENT_PROJECT_VERSION = 1256;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1107,7 +1107,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1241;
|
||||
MARKETING_VERSION = 1.0.1256;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||
PRODUCT_NAME = ServerBox;
|
||||
@@ -1133,7 +1133,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
CURRENT_PROJECT_VERSION = 1256;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1145,7 +1145,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1241;
|
||||
MARKETING_VERSION = 1.0.1256;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||
PRODUCT_NAME = ServerBox;
|
||||
|
||||
@@ -77,8 +77,10 @@ abstract final class MethodChans {
|
||||
}
|
||||
|
||||
/// Register a handler for native -> Flutter callbacks.
|
||||
/// Currently handles: `disconnectSession` with argument map {id: string}
|
||||
static void registerHandler(Future<void> Function(String id) onDisconnect) {
|
||||
/// Currently handles:
|
||||
/// - `disconnectSession` with argument map {id: string}
|
||||
/// - `stopAllConnections` with no arguments
|
||||
static void registerHandler(Future<void> Function(String id) onDisconnect, [VoidCallback? onStopAll]) {
|
||||
_channel.setMethodCallHandler((call) async {
|
||||
switch (call.method) {
|
||||
case 'disconnectSession':
|
||||
@@ -88,6 +90,9 @@ abstract final class MethodChans {
|
||||
await onDisconnect(id);
|
||||
}
|
||||
return;
|
||||
case 'stopAllConnections':
|
||||
onStopAll?.call();
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ class SystemDetector {
|
||||
///
|
||||
/// First checks if a custom system type is configured in [spi].
|
||||
/// If not, attempts to detect the system by running commands:
|
||||
/// 1. 'ver' command to detect Windows
|
||||
/// 2. 'uname -a' command to detect Linux/BSD/Darwin
|
||||
/// 1. 'uname -a' command to detect Linux/BSD/Darwin
|
||||
/// 2. 'ver' command to detect Windows (if uname fails)
|
||||
///
|
||||
/// Returns [SystemType.linux] as default if detection fails.
|
||||
static Future<SystemType> detect(SSHClient client, Spi spi) async {
|
||||
@@ -22,17 +22,8 @@ class SystemDetector {
|
||||
}
|
||||
|
||||
try {
|
||||
// Try to detect Windows systems first (more reliable detection)
|
||||
final powershellResult = await client.run('ver 2>nul').string;
|
||||
if (powershellResult.isNotEmpty &&
|
||||
(powershellResult.contains('Windows') || powershellResult.contains('NT'))) {
|
||||
detectedSystemType = SystemType.windows;
|
||||
dprint('Detected Windows system type for ${spi.oldId}');
|
||||
return detectedSystemType;
|
||||
}
|
||||
|
||||
// Try to detect Unix/Linux/BSD systems
|
||||
final unixResult = await client.run('uname -a').string;
|
||||
// Try to detect Unix/Linux/BSD systems first (more reliable and doesn't create files)
|
||||
final unixResult = await client.run('uname -a 2>/dev/null').string;
|
||||
if (unixResult.contains('Linux')) {
|
||||
detectedSystemType = SystemType.linux;
|
||||
dprint('Detected Linux system type for ${spi.oldId}');
|
||||
@@ -42,6 +33,15 @@ class SystemDetector {
|
||||
dprint('Detected BSD system type for ${spi.oldId}');
|
||||
return detectedSystemType;
|
||||
}
|
||||
|
||||
// If uname fails, try to detect Windows systems
|
||||
final powershellResult = await client.run('ver 2>nul').string;
|
||||
if (powershellResult.isNotEmpty &&
|
||||
(powershellResult.contains('Windows') || powershellResult.contains('NT'))) {
|
||||
detectedSystemType = SystemType.windows;
|
||||
dprint('Detected Windows system type for ${spi.oldId}');
|
||||
return detectedSystemType;
|
||||
}
|
||||
} catch (e) {
|
||||
Loggers.app.warning('System detection failed for ${spi.oldId}: $e');
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ abstract class ServerState with _$ServerState {
|
||||
}
|
||||
|
||||
// Individual server state management
|
||||
@riverpod
|
||||
@Riverpod(keepAlive: true)
|
||||
class ServerNotifier extends _$ServerNotifier {
|
||||
@override
|
||||
ServerState build(String serverId) {
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
|
||||
abstract class BuildData {
|
||||
static const String name = "ServerBox";
|
||||
static const int build = 1241;
|
||||
static const int build = 1256;
|
||||
static const int script = 69;
|
||||
}
|
||||
|
||||
@@ -51,12 +51,31 @@ abstract final class TermSessionManager {
|
||||
|
||||
static void init() {
|
||||
if (isAndroid) {
|
||||
MethodChans.registerHandler((id) async {
|
||||
_entries[id]?.disconnect?.call();
|
||||
});
|
||||
MethodChans.registerHandler(
|
||||
(id) async {
|
||||
_entries[id]?.disconnect?.call();
|
||||
},
|
||||
() {
|
||||
// Stop all connections when notification "Stop All" is pressed
|
||||
stopAllConnections();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when Android notification "Stop All" button is pressed
|
||||
static void stopAllConnections() {
|
||||
// Disconnect all sessions
|
||||
final disconnectCallbacks = _entries.values.map((e) => e.disconnect).where((cb) => cb != null).toList();
|
||||
for (final disconnect in disconnectCallbacks) {
|
||||
disconnect!();
|
||||
}
|
||||
// Clear all entries
|
||||
_entries.clear();
|
||||
_activeId = null;
|
||||
_sync();
|
||||
}
|
||||
|
||||
/// Add a session record and push update to Android.
|
||||
static void add({
|
||||
required String id,
|
||||
|
||||
@@ -1628,6 +1628,12 @@ abstract class AppLocalizations {
|
||||
/// **'Connection Statistics'**
|
||||
String get connectionStats;
|
||||
|
||||
/// No description provided for @connectionStatsDesc.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'View server connection success rate and history'**
|
||||
String get connectionStatsDesc;
|
||||
|
||||
/// No description provided for @noConnectionStatsData.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -1729,6 +1735,12 @@ abstract class AppLocalizations {
|
||||
/// In en, this message translates to:
|
||||
/// **'At least one tab must be selected'**
|
||||
String get atLeastOneTab;
|
||||
|
||||
/// No description provided for @serverTabRequired.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Server tab cannot be removed'**
|
||||
String get serverTabRequired;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
||||
@@ -855,6 +855,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get connectionStats => 'Verbindungsstatistiken';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc =>
|
||||
'Server-Verbindungserfolgsrate und Verlauf anzeigen';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData => 'Keine Verbindungsstatistikdaten';
|
||||
|
||||
@@ -911,4 +915,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get atLeastOneTab => 'Mindestens ein Tab muss ausgewählt sein';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => 'Server-Tab kann nicht entfernt werden';
|
||||
}
|
||||
|
||||
@@ -847,6 +847,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get connectionStats => 'Connection Statistics';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc =>
|
||||
'View server connection success rate and history';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData => 'No connection statistics data';
|
||||
|
||||
@@ -903,4 +907,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get atLeastOneTab => 'At least one tab must be selected';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => 'Server tab cannot be removed';
|
||||
}
|
||||
|
||||
@@ -856,6 +856,10 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
@override
|
||||
String get connectionStats => 'Estadísticas de conexión';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc =>
|
||||
'Ver la tasa de éxito de conexión del servidor e historial';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData =>
|
||||
'No hay datos de estadísticas de conexión';
|
||||
@@ -913,4 +917,7 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get atLeastOneTab => 'Al menos una pestaña debe estar seleccionada';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => 'Server tab cannot be removed';
|
||||
}
|
||||
|
||||
@@ -859,6 +859,10 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
@override
|
||||
String get connectionStats => 'Statistiques de connexion';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc =>
|
||||
'Voir le taux de réussite de connexion du serveur et l\'historique';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData =>
|
||||
'Aucune donnée de statistiques de connexion';
|
||||
@@ -916,4 +920,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get atLeastOneTab => 'Au moins un onglet doit être sélectionné';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => 'Server tab cannot be removed';
|
||||
}
|
||||
|
||||
@@ -847,6 +847,10 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
@override
|
||||
String get connectionStats => 'Statistik Koneksi';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc =>
|
||||
'Lihat tingkat keberhasilan koneksi server dan riwayat';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData => 'Tidak ada data statistik koneksi';
|
||||
|
||||
@@ -903,4 +907,7 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get atLeastOneTab => 'Setidaknya satu tab harus dipilih';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => 'Server tab cannot be removed';
|
||||
}
|
||||
|
||||
@@ -822,6 +822,9 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
@override
|
||||
String get connectionStats => '接続統計';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc => 'サーバー接続成功率と履歴を表示';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData => '接続統計データがありません';
|
||||
|
||||
@@ -876,4 +879,7 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get atLeastOneTab => '少なくとも1つのタブを選択する必要があります';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => 'サーバータブは削除できません';
|
||||
}
|
||||
|
||||
@@ -853,6 +853,10 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
@override
|
||||
String get connectionStats => 'Verbindingsstatistieken';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc =>
|
||||
'Bekijk server verbindingssucces ratio en geschiedenis';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData => 'Geen verbindingsstatistiekgegevens';
|
||||
|
||||
@@ -910,4 +914,7 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
@override
|
||||
String get atLeastOneTab =>
|
||||
'Er moet minimaal één tabblad worden geselecteerd';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => 'Server tab cannot be removed';
|
||||
}
|
||||
|
||||
@@ -850,6 +850,10 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
@override
|
||||
String get connectionStats => 'Estatísticas de conexão';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc =>
|
||||
'Ver taxa de sucesso de conexão do servidor e histórico';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData => 'Não há dados de estatísticas de conexão';
|
||||
|
||||
@@ -906,4 +910,7 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get atLeastOneTab => 'Pelo menos uma aba deve ser selecionada';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => 'Server tab cannot be removed';
|
||||
}
|
||||
|
||||
@@ -852,6 +852,10 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
@override
|
||||
String get connectionStats => 'Статистика соединений';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc =>
|
||||
'Просмотр коэффициента успешности подключения к серверу и истории';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData => 'Нет данных статистики соединений';
|
||||
|
||||
@@ -908,4 +912,7 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get atLeastOneTab => 'Должна быть выбрана хотя бы одна вкладка';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => 'Server tab cannot be removed';
|
||||
}
|
||||
|
||||
@@ -847,6 +847,10 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
@override
|
||||
String get connectionStats => 'Bağlantı İstatistikleri';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc =>
|
||||
'Sunucu bağlantı başarı oranını ve geçmişi görüntüle';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData => 'Bağlantı istatistik verisi yok';
|
||||
|
||||
@@ -903,4 +907,7 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get atLeastOneTab => 'En az bir sekme seçilmelidir';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => 'Server tab cannot be removed';
|
||||
}
|
||||
|
||||
@@ -853,6 +853,10 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||
@override
|
||||
String get connectionStats => 'Статистика з\'єднань';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc =>
|
||||
'Переглянути коефіцієнт успішності підключення до сервера та історію';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData => 'Немає даних статистики з\'єднань';
|
||||
|
||||
@@ -909,4 +913,7 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get atLeastOneTab => 'Потрібно вибрати принаймні одну вкладку';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => 'Server tab cannot be removed';
|
||||
}
|
||||
|
||||
@@ -807,6 +807,9 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
@override
|
||||
String get connectionStats => '连接统计';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc => '查看服务器连接成功率和历史记录';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData => '暂无连接统计数据';
|
||||
|
||||
@@ -861,6 +864,9 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get atLeastOneTab => '至少需要选择一个标签';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => '服务器标签不能被移除';
|
||||
}
|
||||
|
||||
/// The translations for Chinese, as used in Taiwan (`zh_TW`).
|
||||
@@ -1666,6 +1672,9 @@ class AppLocalizationsZhTw extends AppLocalizationsZh {
|
||||
@override
|
||||
String get connectionStats => '連線統計';
|
||||
|
||||
@override
|
||||
String get connectionStatsDesc => '檢視伺服器連線成功率和歷史記錄';
|
||||
|
||||
@override
|
||||
String get noConnectionStatsData => '暫無連線統計資料';
|
||||
|
||||
@@ -1720,4 +1729,7 @@ class AppLocalizationsZhTw extends AppLocalizationsZh {
|
||||
|
||||
@override
|
||||
String get atLeastOneTab => '至少需要選擇一個標籤';
|
||||
|
||||
@override
|
||||
String get serverTabRequired => '服務器標籤不能被移除';
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "Das Schreiben des Skripts ist fehlgeschlagen, möglicherweise aufgrund fehlender Berechtigungen oder das Verzeichnis existiert nicht.",
|
||||
"writeScriptTip": "Nach der Verbindung mit dem Server wird ein Skript in `~/.config/server_box` \n | `/tmp/server_box` geschrieben, um den Systemstatus zu überwachen. Sie können den Skriptinhalt überprüfen.",
|
||||
"connectionStats": "Verbindungsstatistiken",
|
||||
"connectionStatsDesc": "Server-Verbindungserfolgsrate und Verlauf anzeigen",
|
||||
"noConnectionStatsData": "Keine Verbindungsstatistikdaten",
|
||||
"totalAttempts": "Gesamt",
|
||||
"lastSuccess": "Letzter Erfolg",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "Passen Sie an, welche Tabs auf der Startseite angezeigt werden und ihre Reihenfolge",
|
||||
"reset": "Zurücksetzen",
|
||||
"availableTabs": "Verfügbare Tabs",
|
||||
"atLeastOneTab": "Mindestens ein Tab muss ausgewählt sein"
|
||||
}
|
||||
"atLeastOneTab": "Mindestens ein Tab muss ausgewählt sein",
|
||||
"serverTabRequired": "Server-Tab kann nicht entfernt werden"
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "Writing to the script failed, possibly due to lack of permissions or the directory does not exist.",
|
||||
"writeScriptTip": "After connecting to the server, a script will be written to `~/.config/server_box` \n | `/tmp/server_box` to monitor the system status. You can review the script content.",
|
||||
"connectionStats": "Connection Statistics",
|
||||
"connectionStatsDesc": "View server connection success rate and history",
|
||||
"noConnectionStatsData": "No connection statistics data",
|
||||
"totalAttempts": "Total",
|
||||
"lastSuccess": "Last Success",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "Customize which tabs appear on the home page and their order",
|
||||
"reset": "Reset",
|
||||
"availableTabs": "Available Tabs",
|
||||
"atLeastOneTab": "At least one tab must be selected"
|
||||
"atLeastOneTab": "At least one tab must be selected",
|
||||
"serverTabRequired": "Server tab cannot be removed"
|
||||
}
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "La escritura en el script falló, posiblemente por falta de permisos o porque el directorio no existe.",
|
||||
"writeScriptTip": "Después de conectarse al servidor, se escribirá un script en `~/.config/server_box` \n | `/tmp/server_box` para monitorear el estado del sistema. Puedes revisar el contenido del script.",
|
||||
"connectionStats": "Estadísticas de conexión",
|
||||
"connectionStatsDesc": "Ver la tasa de éxito de conexión del servidor e historial",
|
||||
"noConnectionStatsData": "No hay datos de estadísticas de conexión",
|
||||
"totalAttempts": "Total",
|
||||
"lastSuccess": "Último éxito",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "Personaliza qué pestañas aparecen en la página de inicio y su orden",
|
||||
"reset": "Restablecer",
|
||||
"availableTabs": "Pestañas disponibles",
|
||||
"atLeastOneTab": "Al menos una pestaña debe estar seleccionada"
|
||||
}
|
||||
"atLeastOneTab": "Al menos una pestaña debe estar seleccionada",
|
||||
"serverTabRequired": "Server tab cannot be removed"
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "Échec de l'écriture dans le script, probablement en raison d'un manque de permissions ou que le répertoire n'existe pas.",
|
||||
"writeScriptTip": "Après la connexion au serveur, un script sera écrit dans `~/.config/server_box` \n | `/tmp/server_box` pour surveiller l'état du système. Vous pouvez examiner le contenu du script.",
|
||||
"connectionStats": "Statistiques de connexion",
|
||||
"connectionStatsDesc": "Voir le taux de réussite de connexion du serveur et l'historique",
|
||||
"noConnectionStatsData": "Aucune donnée de statistiques de connexion",
|
||||
"totalAttempts": "Total",
|
||||
"lastSuccess": "Dernier succès",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "Personnalisez les onglets qui apparaissent sur la page d'accueil et leur ordre",
|
||||
"reset": "Réinitialiser",
|
||||
"availableTabs": "Onglets disponibles",
|
||||
"atLeastOneTab": "Au moins un onglet doit être sélectionné"
|
||||
}
|
||||
"atLeastOneTab": "Au moins un onglet doit être sélectionné",
|
||||
"serverTabRequired": "Server tab cannot be removed"
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "Penulisan ke skrip gagal, mungkin karena tidak ada izin atau direktori tidak ada.",
|
||||
"writeScriptTip": "Setelah terhubung ke server, sebuah skrip akan ditulis ke `~/.config/server_box` \n | `/tmp/server_box` untuk memantau status sistem. Anda dapat meninjau konten skrip tersebut.",
|
||||
"connectionStats": "Statistik Koneksi",
|
||||
"connectionStatsDesc": "Lihat tingkat keberhasilan koneksi server dan riwayat",
|
||||
"noConnectionStatsData": "Tidak ada data statistik koneksi",
|
||||
"totalAttempts": "Total",
|
||||
"lastSuccess": "Sukses Terakhir",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "Sesuaikan tab mana yang muncul di halaman beranda dan urutannya",
|
||||
"reset": "Reset",
|
||||
"availableTabs": "Tab Tersedia",
|
||||
"atLeastOneTab": "Setidaknya satu tab harus dipilih"
|
||||
}
|
||||
"atLeastOneTab": "Setidaknya satu tab harus dipilih",
|
||||
"serverTabRequired": "Server tab cannot be removed"
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "スクリプトの書き込みに失敗しました。権限がないかディレクトリが存在しない可能性があります。",
|
||||
"writeScriptTip": "サーバーに接続すると、システムの状態を監視するためのスクリプトが `~/.config/server_box` \n | `/tmp/server_box` に書き込まれます。スクリプトの内容を確認できます。",
|
||||
"connectionStats": "接続統計",
|
||||
"connectionStatsDesc": "サーバー接続成功率と履歴を表示",
|
||||
"noConnectionStatsData": "接続統計データがありません",
|
||||
"totalAttempts": "総計",
|
||||
"lastSuccess": "最後の成功",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "ホームページに表示するタブとその順序をカスタマイズします",
|
||||
"reset": "リセット",
|
||||
"availableTabs": "利用可能なタブ",
|
||||
"atLeastOneTab": "少なくとも1つのタブを選択する必要があります"
|
||||
}
|
||||
"atLeastOneTab": "少なくとも1つのタブを選択する必要があります",
|
||||
"serverTabRequired": "サーバータブは削除できません"
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "Het schrijven naar het script is mislukt, mogelijk door gebrek aan rechten of omdat de map niet bestaat.",
|
||||
"writeScriptTip": "Na het verbinden met de server wordt een script geschreven naar `~/.config/server_box` \n | `/tmp/server_box` om de systeemstatus te monitoren. U kunt de inhoud van het script controleren.",
|
||||
"connectionStats": "Verbindingsstatistieken",
|
||||
"connectionStatsDesc": "Bekijk server verbindingssucces ratio en geschiedenis",
|
||||
"noConnectionStatsData": "Geen verbindingsstatistiekgegevens",
|
||||
"totalAttempts": "Totaal",
|
||||
"lastSuccess": "Laatst succesvol",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "Pas aan welke tabbladen op de startpagina worden weergegeven en hun volgorde",
|
||||
"reset": "Resetten",
|
||||
"availableTabs": "Beschikbare tabbladen",
|
||||
"atLeastOneTab": "Er moet minimaal één tabblad worden geselecteerd"
|
||||
}
|
||||
"atLeastOneTab": "Er moet minimaal één tabblad worden geselecteerd",
|
||||
"serverTabRequired": "Server tab cannot be removed"
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "Falha ao escrever no script, possivelmente devido à falta de permissões ou o diretório não existe.",
|
||||
"writeScriptTip": "Após conectar ao servidor, um script será escrito em `~/.config/server_box` \n | `/tmp/server_box` para monitorar o status do sistema. Você pode revisar o conteúdo do script.",
|
||||
"connectionStats": "Estatísticas de conexão",
|
||||
"connectionStatsDesc": "Ver taxa de sucesso de conexão do servidor e histórico",
|
||||
"noConnectionStatsData": "Não há dados de estatísticas de conexão",
|
||||
"totalAttempts": "Total",
|
||||
"lastSuccess": "Último sucesso",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "Personalize quais abas aparecem na página inicial e sua ordem",
|
||||
"reset": "Redefinir",
|
||||
"availableTabs": "Abas disponíveis",
|
||||
"atLeastOneTab": "Pelo menos uma aba deve ser selecionada"
|
||||
}
|
||||
"atLeastOneTab": "Pelo menos uma aba deve ser selecionada",
|
||||
"serverTabRequired": "Server tab cannot be removed"
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "Запись скрипта не удалась, возможно, из-за отсутствия прав или потому что, директории не существует.",
|
||||
"writeScriptTip": "После подключения к серверу скрипт будет записан в `~/.config/server_box` \n | `/tmp/server_box` для мониторинга состояния системы. Вы можете проверить содержимое скрипта.",
|
||||
"connectionStats": "Статистика соединений",
|
||||
"connectionStatsDesc": "Просмотр коэффициента успешности подключения к серверу и истории",
|
||||
"noConnectionStatsData": "Нет данных статистики соединений",
|
||||
"totalAttempts": "Общее",
|
||||
"lastSuccess": "Последний успех",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "Настройте, какие вкладки появляются на главной странице и их порядок",
|
||||
"reset": "Сброс",
|
||||
"availableTabs": "Доступные вкладки",
|
||||
"atLeastOneTab": "Должна быть выбрана хотя бы одна вкладка"
|
||||
}
|
||||
"atLeastOneTab": "Должна быть выбрана хотя бы одна вкладка",
|
||||
"serverTabRequired": "Server tab cannot be removed"
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "Betik yazma başarısız oldu, muhtemelen izin eksikliği veya dizin mevcut değil.",
|
||||
"writeScriptTip": "Sunucuya bağlandıktan sonra, sistem durumunu izlemek için `~/.config/server_box` \n | `/tmp/server_box` dizinine bir betik yazılacak. Betik içeriğini inceleyebilirsiniz.",
|
||||
"connectionStats": "Bağlantı İstatistikleri",
|
||||
"connectionStatsDesc": "Sunucu bağlantı başarı oranını ve geçmişi görüntüle",
|
||||
"noConnectionStatsData": "Bağlantı istatistik verisi yok",
|
||||
"totalAttempts": "Toplam",
|
||||
"lastSuccess": "Son Başarı",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "Ana sayfada görünecek sekmeleri ve sıralarını özelleştirin",
|
||||
"reset": "Sıfırla",
|
||||
"availableTabs": "Mevcut Sekmeler",
|
||||
"atLeastOneTab": "En az bir sekme seçilmelidir"
|
||||
}
|
||||
"atLeastOneTab": "En az bir sekme seçilmelidir",
|
||||
"serverTabRequired": "Server tab cannot be removed"
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "Запис у скрипт не вдався, можливо, через брак дозволів або каталог не існує.",
|
||||
"writeScriptTip": "Після підключення до сервера скрипт буде записано у `~/.config/server_box` \n | `/tmp/server_box` для моніторингу стану системи. Ви можете переглянути вміст скрипта.",
|
||||
"connectionStats": "Статистика з'єднань",
|
||||
"connectionStatsDesc": "Переглянути коефіцієнт успішності підключення до сервера та історію",
|
||||
"noConnectionStatsData": "Немає даних статистики з'єднань",
|
||||
"totalAttempts": "Загальна кількість",
|
||||
"lastSuccess": "Останній успіх",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "Налаштуйте, які вкладки відображаються на головній сторінці та їх порядок",
|
||||
"reset": "Скинути",
|
||||
"availableTabs": "Доступні вкладки",
|
||||
"atLeastOneTab": "Потрібно вибрати принаймні одну вкладку"
|
||||
}
|
||||
"atLeastOneTab": "Потрібно вибрати принаймні одну вкладку",
|
||||
"serverTabRequired": "Server tab cannot be removed"
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "写入脚本失败,可能是没有权限/目录不存在等",
|
||||
"writeScriptTip": "在连接服务器后,会向 `~/.config/server_box` \n | `/tmp/server_box` 写入脚本来监测系统状态,你可以审查脚本内容。",
|
||||
"connectionStats": "连接统计",
|
||||
"connectionStatsDesc": "查看服务器连接成功率和历史记录",
|
||||
"noConnectionStatsData": "暂无连接统计数据",
|
||||
"totalAttempts": "总次数",
|
||||
"lastSuccess": "最后成功",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "自定义主页上显示的标签及其顺序",
|
||||
"reset": "重置",
|
||||
"availableTabs": "可用标签",
|
||||
"atLeastOneTab": "至少需要选择一个标签"
|
||||
"atLeastOneTab": "至少需要选择一个标签",
|
||||
"serverTabRequired": "服务器标签不能被移除"
|
||||
}
|
||||
@@ -251,6 +251,7 @@
|
||||
"writeScriptFailTip": "寫入腳本失敗,可能是沒有權限/目錄不存在等。",
|
||||
"writeScriptTip": "連線到伺服器後,將會在 `~/.config/server_box` \n | `/tmp/server_box` 中寫入一個腳本來監測系統狀態。你可以審查腳本內容。",
|
||||
"connectionStats": "連線統計",
|
||||
"connectionStatsDesc": "檢視伺服器連線成功率和歷史記錄",
|
||||
"noConnectionStatsData": "暫無連線統計資料",
|
||||
"totalAttempts": "總次數",
|
||||
"lastSuccess": "最後成功",
|
||||
@@ -281,5 +282,6 @@
|
||||
"homeTabsCustomizeDesc": "自訂主頁上顯示的標籤及其順序",
|
||||
"reset": "重置",
|
||||
"availableTabs": "可用標籤",
|
||||
"atLeastOneTab": "至少需要選擇一個標籤"
|
||||
}
|
||||
"atLeastOneTab": "至少需要選擇一個標籤",
|
||||
"serverTabRequired": "服務器標籤不能被移除"
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ class _ConnectionStatsPageState extends State<ConnectionStatsPage> {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${libL10n.success}: $successRate%',
|
||||
'${libL10n.success}: $successRate',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: stats.successRate >= 0.8
|
||||
|
||||
@@ -68,7 +68,7 @@ class _HomeTabsConfigPageState extends ConsumerState<HomeTabsConfigPage> {
|
||||
}
|
||||
|
||||
Widget _buildTabItem(AppTab tab, int index, bool isSelected) {
|
||||
final canRemove = _selectedTabs.length > 1;
|
||||
final canRemove = _selectedTabs.length > 1 && tab != AppTab.server;
|
||||
final child = ListTile(
|
||||
leading: tab.navDestination.icon,
|
||||
title: Text(tab.navDestination.label),
|
||||
@@ -77,7 +77,7 @@ class _HomeTabsConfigPageState extends ConsumerState<HomeTabsConfigPage> {
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: canRemove ? () => _removeTab(tab) : null,
|
||||
color: canRemove ? null : Theme.of(context).disabledColor,
|
||||
tooltip: canRemove ? libL10n.delete : l10n.atLeastOneTab,
|
||||
tooltip: canRemove ? libL10n.delete : (tab == AppTab.server ? l10n.serverTabRequired : l10n.atLeastOneTab),
|
||||
)
|
||||
: IconButton(icon: const Icon(Icons.add), onPressed: () => _addTab(tab)),
|
||||
onTap: isSelected && canRemove ? () => _removeTab(tab) : null,
|
||||
@@ -111,6 +111,10 @@ class _HomeTabsConfigPageState extends ConsumerState<HomeTabsConfigPage> {
|
||||
context.showSnackBar(l10n.atLeastOneTab);
|
||||
return;
|
||||
}
|
||||
if (tab == AppTab.server) {
|
||||
context.showSnackBar(l10n.serverTabRequired);
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_selectedTabs.remove(tab);
|
||||
});
|
||||
|
||||
@@ -42,8 +42,8 @@ extension _Server on _AppSettingsPageState {
|
||||
Widget _buildConnectionStats() {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.analytics, size: _kIconSize),
|
||||
title: const Text('连接统计'),
|
||||
subtitle: const Text('查看服务器连接成功率和历史记录'),
|
||||
title: Text(l10n.connectionStats),
|
||||
subtitle: Text(l10n.connectionStatsDesc),
|
||||
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
|
||||
@@ -471,7 +471,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
CURRENT_PROJECT_VERSION = 1256;
|
||||
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.1241;
|
||||
MARKETING_VERSION = 1.0.1256;
|
||||
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 = 1241;
|
||||
CURRENT_PROJECT_VERSION = 1256;
|
||||
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.1241;
|
||||
MARKETING_VERSION = 1.0.1256;
|
||||
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 = 1241;
|
||||
CURRENT_PROJECT_VERSION = 1256;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
@@ -649,11 +649,11 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1.0.1241;
|
||||
MARKETING_VERSION = 1.0.1256;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "Server Box";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = serverbox_lkmm;
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = hj_macmini_for_serverbox;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@@ -18,18 +18,12 @@
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.temporary-exception.files.home-relative-path.read-only</key>
|
||||
<array>
|
||||
<string>.ssh/</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array/>
|
||||
</dict>
|
||||
|
||||
@@ -16,18 +16,12 @@
|
||||
</array>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.temporary-exception.files.home-relative-path.read-only</key>
|
||||
<array>
|
||||
<string>.ssh/</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array/>
|
||||
</dict>
|
||||
|
||||
@@ -480,8 +480,8 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
path: "."
|
||||
ref: "v1.0.51"
|
||||
resolved-ref: "430672b7c7608b68ceda785533ec11e1ac3e9ca7"
|
||||
ref: "v1.0.52"
|
||||
resolved-ref: "38e7d41ccd71bf44e286d86b4ad656f05c5c2548"
|
||||
url: "https://github.com/lppcg/fl_build.git"
|
||||
source: git
|
||||
version: "1.0.0"
|
||||
@@ -1838,8 +1838,8 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: "v4.0.3"
|
||||
resolved-ref: c64183346b924173eb7251800001a64771911185
|
||||
ref: "v4.0.4"
|
||||
resolved-ref: "5747837cdb7b113ef733ce0104e4f2bfa1eb4a36"
|
||||
url: "https://github.com/lollipopkit/xterm.dart"
|
||||
source: git
|
||||
version: "4.0.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: server_box
|
||||
description: server status & toolbox app.
|
||||
publish_to: "none"
|
||||
version: 1.0.1241+1241
|
||||
version: 1.0.1256+1256
|
||||
|
||||
environment:
|
||||
sdk: ">=3.9.0"
|
||||
@@ -47,7 +47,7 @@ dependencies:
|
||||
xterm:
|
||||
git:
|
||||
url: https://github.com/lollipopkit/xterm.dart
|
||||
ref: v4.0.3
|
||||
ref: v4.0.4
|
||||
computer:
|
||||
git:
|
||||
url: https://github.com/lollipopkit/dart_computer
|
||||
@@ -102,7 +102,7 @@ dev_dependencies:
|
||||
fl_build:
|
||||
git:
|
||||
url: https://github.com/lppcg/fl_build.git
|
||||
ref: v1.0.51
|
||||
ref: v1.0.52
|
||||
|
||||
flutter:
|
||||
generate: true
|
||||
|
||||
Reference in New Issue
Block a user