Skip to content

Commit

Permalink
Merge pull request #568 from lollipopkit/lollipopkit/issue564
Browse files Browse the repository at this point in the history
  • Loading branch information
lollipopkit authored Aug 31, 2024
2 parents c18732d + b33d0bb commit 7f0dc65
Show file tree
Hide file tree
Showing 18 changed files with 344 additions and 202 deletions.
1 change: 0 additions & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ android {
}

defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "tech.lolli.toolbox"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
Expand Down
34 changes: 18 additions & 16 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Expand All @@ -15,7 +16,8 @@
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:hasFragileUserData="true"
android:restoreAnyVersion="true">
android:restoreAnyVersion="true"
tools:targetApi="q">
<activity
android:name=".MainActivity"
android:exported="true"
Expand All @@ -29,12 +31,12 @@
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
Expand All @@ -43,11 +45,6 @@
android:name="flutterEmbedding"
android:value="2" />

<service
android:name="id.flutter.flutter_background_service.BackgroundService"
android:foregroundServiceType="dataSync"
/>

<receiver
android:name=".widget.HomeWidget"
android:exported="false"
Expand All @@ -67,7 +64,12 @@
android:resource="@xml/home_widget" />
</receiver>

<service android:name=".KeepAliveService"/>
<service
android:name=".ForegroundService"
android:enabled="true"
android:foregroundServiceType="dataSync"
android:exported="false" />

</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility?hl=en and
Expand All @@ -76,8 +78,8 @@
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
<action android:name="android.intent.action.PROCESS_TEXT" />
<data android:mimeType="text/plain" />
</intent>
</queries>
</manifest>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package tech.lolli.toolbox

import android.app.*
import android.content.Intent
import android.os.Build
import android.os.IBinder

class ForegroundService : Service() {
private val chanId = "ForegroundServiceChannel"

override fun onCreate() {
super.onCreate()
createNotificationChannel()
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = createNotification()
startForeground(1, notification)

// Exec your code here

return START_STICKY
}

override fun onBind(intent: Intent): IBinder? {
return null
}

private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val serviceChannel = NotificationChannel(
chanId,
chanId,
NotificationManager.IMPORTANCE_DEFAULT
)
val manager = getSystemService(NotificationManager::class.java)
manager.createNotificationChannel(serviceChannel)
}
}

private fun createNotification(): Notification {
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
0,
notificationIntent,
PendingIntent.FLAG_IMMUTABLE
)

return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder(this, chanId)
.setContentTitle("App is running")
.setContentText("Click to open the app")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build()
} else {
Notification.Builder(this)
.setContentTitle("App is running")
.setContentText("Click to open the app")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build()
}
}
}
18 changes: 0 additions & 18 deletions android/app/src/main/kotlin/tech/lolli/toolbox/KeepAliveService.kt

This file was deleted.

26 changes: 24 additions & 2 deletions android/app/src/main/kotlin/tech/lolli/toolbox/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package tech.lolli.toolbox

import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.Manifest
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
Expand All @@ -18,8 +23,13 @@ class MainActivity: FlutterFragmentActivity() {
result.success(null)
}
"startService" -> {
val intent = Intent(this@MainActivity, KeepAliveService::class.java)
startService(intent)
reqPerm()
val serviceIntent = Intent(this@MainActivity, ForegroundService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
}
else -> {
result.notImplemented()
Expand All @@ -28,4 +38,16 @@ class MainActivity: FlutterFragmentActivity() {
}
}
}

private fun reqPerm() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
123,
)
}
}
}
5 changes: 5 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:logging/logging.dart';
import 'package:server_box/app.dart';
import 'package:server_box/core/channel/bg_run.dart';
import 'package:server_box/core/utils/sync/icloud.dart';
import 'package:server_box/core/utils/sync/webdav.dart';
import 'package:server_box/data/model/app/menu/server_func.dart';
Expand Down Expand Up @@ -109,6 +110,10 @@ void _doPlatformRelated() async {
if (isAndroid) {
// try switch to highest refresh rate
FlutterDisplayMode.setHighRefreshRate();
if (Stores.setting.bgRun.fetch()) {
Loggers.app.info('Start foreground service');
BgRunMC.startService();
}
}

final serversCount = Stores.server.box.keys.length;
Expand Down
2 changes: 1 addition & 1 deletion lib/view/page/backup.dart
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class BackupPage extends StatelessWidget {
trailing: ListenableBuilder(
listenable: webdavLoading,
builder: (_, __) {
if (webdavLoading.value) return SizedLoading.centerSmall;
if (webdavLoading.value) return SizedLoading.small;

return Row(
mainAxisSize: MainAxisSize.min,
Expand Down
2 changes: 1 addition & 1 deletion lib/view/page/private_key/edit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {
return;
}
FocusScope.of(context).unfocus();
_loading.value = SizedLoading.centerMedium;
_loading.value = SizedLoading.medium;
try {
final decrypted = await Computer.shared.start(decyptPem, [key, pwd]);
final pki = PrivateKeyInfo(id: name, key: decrypted);
Expand Down
95 changes: 35 additions & 60 deletions lib/view/page/ssh/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import 'package:dartssh2/dartssh2.dart';
import 'package:fl_lib/fl_lib.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:provider/provider.dart';
import 'package:server_box/core/extension/context/locale.dart';
import 'package:server_box/core/utils/ssh_auth.dart';
Expand Down Expand Up @@ -85,6 +84,7 @@ class SSHPageState extends State<SSHPage>
_terminalController.dispose();
_discontinuityTimer?.cancel();
if (!Stores.setting.generalWakeLock.fetch()) WakelockPlus.disable();
_setupDiscontinuityTimer();
}

@override
Expand All @@ -95,7 +95,7 @@ class SSHPageState extends State<SSHPage>
2 => true,
_ => context.isDark,
};
_media = MediaQuery.of(context);
_media = context.media;

_terminalTheme = _isDark ? TerminalThemes.dark : TerminalThemes.light;
_terminalTheme = _terminalTheme.copyWith(selectionCursor: UIs.primaryColor);
Expand Down Expand Up @@ -415,8 +415,6 @@ class SSHPageState extends State<SSHPage>
_listen(session.stdout);
_listen(session.stderr);

_initService();

for (final snippet in SnippetProvider.snippets.value) {
if (snippet.autoRunOn?.contains(widget.spi.id) == true) {
snippet.runInTerm(_terminal, widget.spi);
Expand Down Expand Up @@ -451,64 +449,43 @@ class SSHPageState extends State<SSHPage>
.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: Text(l10n.attention),
// child: Text('${l10n.disconnected}\n${l10n.goBackQ}'),
// barrierDismiss: false,
// actions: [
// TextButton(
// onPressed: () {
// if (mounted) {
// context.pop();
// if (widget.pop) {
// context.pop();
// }
// }
// },
// child: Text(l10n.ok),
// ),
// ],
// );
// }

@override
bool get wantKeepAlive => true;

Future<void> _initService() async {
if (!isAndroid) return;
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;
},
);
}

await FlutterBackgroundService().configure(
androidConfiguration: AndroidConfiguration(
onStart: _onStart,
autoStart: true,
isForegroundMode: true,
initialNotificationTitle: 'SSH',
initialNotificationContent: l10n.bgRun,
),
iosConfiguration: IosConfiguration(),
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();
Expand Down Expand Up @@ -546,5 +523,3 @@ class SSHPageState extends State<SSHPage>
if (Stores.setting.sshWakeLock.fetch()) WakelockPlus.enable();
}
}

Future<void> _onStart(ServiceInstance service) async {}
Loading

0 comments on commit 7f0dc65

Please sign in to comment.