mirror of
https://github.com/chenasraf/mudblock.git
synced 2026-05-17 17:48:05 +00:00
feat: bg notifications/service
No principal matched. wip: bg notification
This commit is contained in:
@@ -45,8 +45,9 @@ android {
|
||||
applicationId "dev.casraf.mudblock"
|
||||
// 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.
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
minSdkVersion 21
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
compileSdkVersion 34
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application
|
||||
android:label="mudblock"
|
||||
android:label="Mudblock"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
@@ -34,4 +34,8 @@
|
||||
<!-- Permissions -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
|
||||
</manifest>
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -42,6 +42,8 @@
|
||||
/* Begin PBXFileReference section */
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
@@ -53,8 +55,6 @@
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -68,6 +68,14 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
331C8082294A63A400263BE5 /* RunnerTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
331C807B294A618700263BE5 /* RunnerTests.swift */,
|
||||
);
|
||||
path = RunnerTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -79,14 +87,6 @@
|
||||
name = Flutter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
331C8082294A63A400263BE5 /* RunnerTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
331C807B294A618700263BE5 /* RunnerTests.swift */,
|
||||
);
|
||||
path = RunnerTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146E51CF9000F007C117D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -129,7 +129,6 @@
|
||||
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||
buildPhases = (
|
||||
331C807D294A63A400263BE5 /* Sources */,
|
||||
331C807E294A63A400263BE5 /* Frameworks */,
|
||||
331C807F294A63A400263BE5 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
@@ -361,6 +360,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = S56NDKQB4D;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@@ -377,7 +377,6 @@
|
||||
};
|
||||
331C8088294A63A400263BE5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
@@ -395,7 +394,6 @@
|
||||
};
|
||||
331C8089294A63A400263BE5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
@@ -411,7 +409,6 @@
|
||||
};
|
||||
331C808A294A63A400263BE5 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
@@ -539,6 +536,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = S56NDKQB4D;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@@ -561,6 +559,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = S56NDKQB4D;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
||||
@@ -8,6 +8,15 @@ import Flutter
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
|
||||
// This function registers the desired plugins to be used within a notification background action
|
||||
SwiftAwesomeNotificationsPlugin.setPluginRegistrantCallback { registry in
|
||||
SwiftAwesomeNotificationsPlugin.register(
|
||||
with: registry.registrar(forPlugin: "io.flutter.plugins.awesomenotifications.AwesomeNotificationsPlugin")!)
|
||||
FLTSharedPreferencesPlugin.register(
|
||||
with: registry.registrar(forPlugin: "io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin")!)
|
||||
}
|
||||
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<true/>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>dev.flutter.background.refresh</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
68
lib/core/background_service.dart
Normal file
68
lib/core/background_service.dart
Normal file
@@ -0,0 +1,68 @@
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:awesome_notifications/awesome_notifications.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_background_service/flutter_background_service.dart';
|
||||
|
||||
final backgroundService = FlutterBackgroundService();
|
||||
final notifs = AwesomeNotifications();
|
||||
|
||||
const notificationGroupId = 'mudblock';
|
||||
const notificationChannelId = 'mudblock_game';
|
||||
// final notificationId = Random().nextInt(1000);
|
||||
const notificationId = 1337;
|
||||
|
||||
final channel = NotificationChannel(
|
||||
channelGroupKey: notificationGroupId,
|
||||
channelKey: notificationChannelId,
|
||||
channelName: 'Active Game Session',
|
||||
channelDescription: 'Primary notification channel for keeping game active',
|
||||
defaultColor: const Color(0xFF9D50DD),
|
||||
ledColor: Colors.white,
|
||||
importance: NotificationImportance.High,
|
||||
// locked: true,
|
||||
// criticalAlerts: true,
|
||||
);
|
||||
|
||||
Future<void> initBackgroundService() async {
|
||||
if (!Platform.isAndroid && !Platform.isIOS) {
|
||||
return;
|
||||
}
|
||||
notifs.initialize(
|
||||
null,
|
||||
// 'resource://drawable/res_app_icon',
|
||||
[channel],
|
||||
channelGroups: [
|
||||
NotificationChannelGroup(
|
||||
channelGroupKey: notificationGroupId,
|
||||
channelGroupName: 'Mudblock',
|
||||
)
|
||||
],
|
||||
debug: true,
|
||||
);
|
||||
|
||||
await backgroundService.configure(
|
||||
androidConfiguration: AndroidConfiguration(
|
||||
onStart: onStart,
|
||||
|
||||
// auto start service
|
||||
autoStart: false,
|
||||
isForegroundMode: true,
|
||||
|
||||
foregroundServiceNotificationId: notificationId,
|
||||
notificationChannelId: notificationChannelId,
|
||||
initialNotificationTitle: 'Mudblock Background Service',
|
||||
initialNotificationContent: 'Initializing',
|
||||
),
|
||||
iosConfiguration: IosConfiguration(
|
||||
// TODO
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onStart(ServiceInstance service) async {
|
||||
// Only available for flutter 3.0.0 and later
|
||||
DartPluginRegistrant.ensureInitialized();
|
||||
}
|
||||
|
||||
37
lib/core/notification_controller.dart
Normal file
37
lib/core/notification_controller.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
import 'package:awesome_notifications/awesome_notifications.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:mudblock/core/store.dart';
|
||||
|
||||
class NotificationController {
|
||||
/// Use this method to detect when a new notification or a schedule is created
|
||||
@pragma("vm:entry-point")
|
||||
static Future<void> onNotificationCreatedMethod(
|
||||
ReceivedNotification receivedNotification) async {
|
||||
debugPrint('Notification created: ${receivedNotification.toString()}');
|
||||
}
|
||||
|
||||
/// Use this method to detect every time that a new notification is displayed
|
||||
@pragma("vm:entry-point")
|
||||
static Future<void> onNotificationDisplayedMethod(
|
||||
ReceivedNotification receivedNotification) async {
|
||||
debugPrint('Notification displayed: ${receivedNotification.toString()}');
|
||||
// NotificationContent().fromMap(receivedNotification.toMap());
|
||||
}
|
||||
|
||||
/// Use this method to detect if the user dismissed a notification
|
||||
@pragma("vm:entry-point")
|
||||
static Future<void> onDismissActionReceivedMethod(
|
||||
ReceivedAction receivedAction) async {
|
||||
debugPrint('Notification dismissed: ${receivedAction.toString()}');
|
||||
gameStore.showGameNotification();
|
||||
}
|
||||
|
||||
/// Use this method to detect when the user taps on a notification or action button
|
||||
@pragma("vm:entry-point")
|
||||
static Future<void> onActionReceivedMethod(
|
||||
ReceivedAction receivedAction) async {
|
||||
debugPrint('Action received: ${receivedAction.toString()}');
|
||||
gameStore.showGameNotification();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,21 +3,25 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:awesome_notifications/awesome_notifications.dart';
|
||||
import 'package:ctelnet/ctelnet.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../core/features/settings.dart';
|
||||
import '../core/profile_presets.dart';
|
||||
import '../core/storage.dart';
|
||||
import 'background_service.dart';
|
||||
import 'consts.dart';
|
||||
import 'features/action.dart';
|
||||
import 'features/alias.dart';
|
||||
import 'features/builtin_command.dart';
|
||||
import 'features/profile.dart';
|
||||
import 'features/trigger.dart';
|
||||
import 'platform_utils.dart';
|
||||
import 'routes.dart';
|
||||
|
||||
const maxLines = 2000;
|
||||
@@ -70,10 +74,7 @@ class GameStore extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void selectProfileAndConnect(BuildContext context) async {
|
||||
final profile = await Navigator.pushNamed(
|
||||
context,
|
||||
Paths.selectProfile,
|
||||
);
|
||||
final profile = await Navigator.pushNamed(context, Paths.selectProfile);
|
||||
if (profile == null) {
|
||||
return;
|
||||
}
|
||||
@@ -97,6 +98,8 @@ class GameStore extends ChangeNotifier {
|
||||
echoError('Failed to connect');
|
||||
return;
|
||||
}
|
||||
await startBackgroundService();
|
||||
showGameNotification();
|
||||
_subscription = stream.listen(onData);
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -108,11 +111,15 @@ class GameStore extends ChangeNotifier {
|
||||
currentProfile.username.isNotEmpty &&
|
||||
currentProfile.password.isNotEmpty) {
|
||||
debugPrint('Sending username and password');
|
||||
if (currentProfile.authMethod == AuthMethod.diku) {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
send(currentProfile.username);
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
send(currentProfile.password);
|
||||
switch (currentProfile.authMethod) {
|
||||
case AuthMethod.diku:
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
send(currentProfile.username);
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
send(currentProfile.password);
|
||||
break;
|
||||
case AuthMethod.none:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +134,7 @@ class GameStore extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void onRawData(List<int> bytes) {
|
||||
debugPrint('Received Raw Data: ${bytes.length}');
|
||||
debugPrint('Received Raw Data: ${bytes.length} bytes');
|
||||
try {
|
||||
final data = Message(bytes);
|
||||
handleSpecialMessages(data);
|
||||
@@ -149,7 +156,7 @@ class GameStore extends ChangeNotifier {
|
||||
_rawStreamController.add(data.bytes);
|
||||
return;
|
||||
}
|
||||
debugPrint('Received data: ${data.bytes.length}');
|
||||
debugPrint('Received data: ${data.bytes.length} bytes');
|
||||
handleSpecialMessages(data);
|
||||
if (isCompressed) {
|
||||
return;
|
||||
@@ -210,8 +217,7 @@ class GameStore extends ChangeNotifier {
|
||||
terminalSub.isNotEmpty &&
|
||||
terminalSub.single == 1) {
|
||||
debugPrint('Received terminal type SEND request');
|
||||
final tt = const AsciiEncoder().convert('Mublock');
|
||||
final ttBytes = [0, ...tt];
|
||||
List<int> ttBytes = _clientNamesAsBytes();
|
||||
builder.addSubnegotiation(Symbols.terminalType, ttBytes);
|
||||
debugPrint('Sending terminal type response: $ttBytes');
|
||||
}
|
||||
@@ -256,6 +262,12 @@ class GameStore extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
List<int> _clientNamesAsBytes() {
|
||||
final tt = const AsciiEncoder().convert('Mublock');
|
||||
final ttBytes = [0, ...tt];
|
||||
return ttBytes;
|
||||
}
|
||||
|
||||
void disableCompression() {
|
||||
isCompressed = false;
|
||||
_decodedSub.cancel();
|
||||
@@ -531,6 +543,77 @@ class GameStore extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void showGameNotification() async {
|
||||
if (!PlatformUtils.isMobile) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await backgroundService.isRunning())) {
|
||||
debugPrint('Background service not running');
|
||||
return;
|
||||
}
|
||||
final notifAllowed = await requestNotificationPermissions();
|
||||
final alarmAllowed = await requestSchedulePermissions();
|
||||
if (!notifAllowed || !alarmAllowed) {
|
||||
return;
|
||||
}
|
||||
final content = NotificationContent(
|
||||
id: notificationId,
|
||||
channelKey: notificationChannelId,
|
||||
groupKey: notificationGroupId,
|
||||
category: NotificationCategory.Status,
|
||||
// notificationLayout: NotificationLayout.BigText,
|
||||
title: 'Mudblock - Connected to ${currentProfile.name}',
|
||||
body:
|
||||
'${currentProfile.host}:${currentProfile.port} - Game is running in the background. Tap to open',
|
||||
locked: true,
|
||||
// criticalAlert: true,
|
||||
// payload: {
|
||||
// 'profile': jsonEncode(currentProfile.toJson()),
|
||||
// 'locked': 'true',
|
||||
// },
|
||||
displayOnForeground: true,
|
||||
displayOnBackground: true,
|
||||
// autoDismissible: false,
|
||||
);
|
||||
debugPrint(
|
||||
'Showing notification: ${const JsonEncoder.withIndent(' ').convert(content.toMap())}');
|
||||
notifs.createNotification(content: content);
|
||||
}
|
||||
|
||||
startBackgroundService() async {
|
||||
if (!PlatformUtils.isMobile) {
|
||||
return;
|
||||
}
|
||||
await backgroundService.startService();
|
||||
}
|
||||
|
||||
Future<bool> requestNotificationPermissions() async {
|
||||
if (!PlatformUtils.isMobile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var isAllowed = await AwesomeNotifications().isNotificationAllowed();
|
||||
debugPrint('Notification allowed? $isAllowed');
|
||||
if (!isAllowed) {
|
||||
// This is just a basic example. For real apps, you must show some
|
||||
// friendly dialog box before call the request method.
|
||||
// This is very important to not harm the user experience
|
||||
isAllowed =
|
||||
await AwesomeNotifications().requestPermissionToSendNotifications();
|
||||
debugPrint('Notification allowed (after asking)? $isAllowed');
|
||||
}
|
||||
return isAllowed;
|
||||
}
|
||||
|
||||
Future<bool> requestSchedulePermissions() async {
|
||||
if (!PlatformUtils.isMobile) {
|
||||
return false;
|
||||
}
|
||||
final permission = await Permission.scheduleExactAlarm.request();
|
||||
return permission.isGranted;
|
||||
}
|
||||
|
||||
static GameStore of(BuildContext context) {
|
||||
return Provider.of<GameStore>(context, listen: false);
|
||||
}
|
||||
|
||||
@@ -10,11 +10,10 @@ Future<void> windowInit() async {
|
||||
|
||||
final w = prefs.getInt('windowWidth') ?? 1000;
|
||||
final h = prefs.getInt('windowHeight') ?? 900;
|
||||
final size = Size(w.toDouble(), h.toDouble());
|
||||
|
||||
final x = prefs.getInt('windowX') ?? 0;
|
||||
final y = prefs.getInt('windowY') ?? 0;
|
||||
final position = Offset(x.toDouble(), y.toDouble());
|
||||
final size = Size(w.toDouble(), h.toDouble());
|
||||
|
||||
WindowOptions windowOptions = WindowOptions(
|
||||
size: size,
|
||||
@@ -25,8 +24,8 @@ Future<void> windowInit() async {
|
||||
);
|
||||
windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||
await windowManager.show();
|
||||
await windowManager.focus();
|
||||
await windowManager.setPosition(position);
|
||||
await windowManager.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mudblock/core/background_service.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import 'core/platform_utils.dart';
|
||||
import 'core/routes.dart';
|
||||
@@ -10,6 +12,7 @@ import 'core/window_manager.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
initBackgroundService();
|
||||
await getPrefs();
|
||||
await gameStore.init();
|
||||
await windowInit();
|
||||
@@ -19,6 +22,9 @@ void main() async {
|
||||
class MudblockApp extends StatelessWidget {
|
||||
const MudblockApp({super.key});
|
||||
|
||||
static final GlobalKey<NavigatorState> navigatorKey =
|
||||
GlobalKey<NavigatorState>();
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -43,6 +49,7 @@ class MudblockApp extends StatelessWidget {
|
||||
return MaterialApp(
|
||||
title: 'Mudblock',
|
||||
theme: darkTheme,
|
||||
navigatorKey: navigatorKey,
|
||||
builder: (context, child) {
|
||||
return GameStore.provider(
|
||||
child: Container(
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:awesome_notifications/awesome_notifications.dart';
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mudblock/core/color_utils.dart';
|
||||
import 'package:mudblock/core/storage/shared_prefs.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import '../core/consts.dart';
|
||||
import '../core/color_utils.dart';
|
||||
import '../core/features/game_button_set.dart';
|
||||
import '../core/features/keyboard_shortcuts.dart';
|
||||
import '../core/notification_controller.dart';
|
||||
import '../core/platform_utils.dart';
|
||||
import '../core/storage/shared_prefs.dart';
|
||||
import '../core/store.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
@@ -26,6 +28,23 @@ class HomePageState extends State<HomePage>
|
||||
super.initState();
|
||||
windowManager.addListener(this);
|
||||
Future.delayed(Duration.zero, () => store.appStart(context));
|
||||
|
||||
_initNotifications();
|
||||
}
|
||||
|
||||
void _initNotifications() {
|
||||
if (!PlatformUtils.isMobile) {
|
||||
return;
|
||||
}
|
||||
AwesomeNotifications().setListeners(
|
||||
onActionReceivedMethod: NotificationController.onActionReceivedMethod,
|
||||
onNotificationCreatedMethod:
|
||||
NotificationController.onNotificationCreatedMethod,
|
||||
onNotificationDisplayedMethod:
|
||||
NotificationController.onNotificationDisplayedMethod,
|
||||
onDismissActionReceivedMethod:
|
||||
NotificationController.onDismissActionReceivedMethod,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -6,11 +6,19 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <awesome_notifications/awesome_notifications_plugin.h>
|
||||
#include <awesome_notifications_core/awesome_notifications_core_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) awesome_notifications_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "AwesomeNotificationsPlugin");
|
||||
awesome_notifications_plugin_register_with_registrar(awesome_notifications_registrar);
|
||||
g_autoptr(FlPluginRegistrar) awesome_notifications_core_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "AwesomeNotificationsCorePlugin");
|
||||
awesome_notifications_core_plugin_register_with_registrar(awesome_notifications_core_registrar);
|
||||
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
awesome_notifications
|
||||
awesome_notifications_core
|
||||
screen_retriever
|
||||
url_launcher_linux
|
||||
window_manager
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import awesome_notifications
|
||||
import awesome_notifications_core
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import screen_retriever
|
||||
@@ -13,6 +15,8 @@ import url_launcher_macos
|
||||
import window_manager
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AwesomeNotificationsPlugin.register(with: registry.registrar(forPlugin: "AwesomeNotificationsPlugin"))
|
||||
AwesomeNotificationsCorePlugin.register(with: registry.registrar(forPlugin: "AwesomeNotificationsCorePlugin"))
|
||||
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
PODS:
|
||||
- awesome_notifications (0.0.1):
|
||||
- FlutterMacOS
|
||||
- awesome_notifications_core (0.0.1):
|
||||
- FlutterMacOS
|
||||
- FlutterMacOS (1.0.0)
|
||||
- package_info_plus (0.0.1):
|
||||
- FlutterMacOS
|
||||
@@ -16,6 +20,8 @@ PODS:
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
- awesome_notifications (from `Flutter/ephemeral/.symlinks/plugins/awesome_notifications/macos`)
|
||||
- awesome_notifications_core (from `Flutter/ephemeral/.symlinks/plugins/awesome_notifications_core/macos`)
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
@@ -25,6 +31,10 @@ DEPENDENCIES:
|
||||
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
awesome_notifications:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/awesome_notifications/macos
|
||||
awesome_notifications_core:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/awesome_notifications_core/macos
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
package_info_plus:
|
||||
@@ -41,6 +51,8 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
awesome_notifications: 428f5c15a700b117418aed09e29c21c5806fcf69
|
||||
awesome_notifications_core: 43438f2e1b0e1dd9da81aaeb1b4af5ec0447af1a
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
|
||||
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||
|
||||
108
pubspec.lock
108
pubspec.lock
@@ -33,6 +33,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
awesome_notifications:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: awesome_notifications
|
||||
sha256: "65f730f9c0e73a346039ef746384bcff1773f9f03821b859705a7ab8db977b23"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.2"
|
||||
awesome_notifications_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: awesome_notifications_core
|
||||
sha256: "7090d11eaf343bc6f354ad999b82d8c25f41a2089111936199670357c86b46ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -103,7 +119,7 @@ packages:
|
||||
path: "../ctelnet"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.1.6"
|
||||
version: "0.2.0"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -157,6 +173,38 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_background_service:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_background_service
|
||||
sha256: "94d9a143852729140e17254a53769383b03738cd92b6e588a8762003e6cd9dd9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.5"
|
||||
flutter_background_service_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_background_service_android
|
||||
sha256: "30863ebafd8214b8e76d5e5c9f27887dc5cc303fcf3e89f71534f621fc486782"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.2"
|
||||
flutter_background_service_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_background_service_ios
|
||||
sha256: ab73657535876e16abc89e40f924df3e92ad3dee83f64d187081417e824709ed
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
flutter_background_service_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_background_service_platform_interface
|
||||
sha256: cd5720ff5b051d551a4734fae16683aace779bd0425e8d3f15d84a0cdcc2d8d9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -207,6 +255,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.3"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.1"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -359,6 +415,54 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
permission_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.3.1"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.0.5"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.4.4"
|
||||
permission_handler_html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_html
|
||||
sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.1"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_windows
|
||||
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -685,4 +789,4 @@ packages:
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.2.3 <4.0.0"
|
||||
flutter: ">=3.13.0"
|
||||
flutter: ">=3.16.0"
|
||||
|
||||
@@ -28,7 +28,7 @@ environment:
|
||||
# the latest version available on pub.dev. To see which dependencies have newer
|
||||
# versions available, run `flutter pub outdated`.
|
||||
dependencies:
|
||||
# ctelnet: ^0.1.4
|
||||
# ctelnet: ^0.1.6
|
||||
ctelnet:
|
||||
path: ../ctelnet
|
||||
flutter:
|
||||
@@ -50,6 +50,10 @@ dependencies:
|
||||
path_provider: ^2.1.1
|
||||
package_info_plus: ^4.1.0
|
||||
url_launcher: ^6.1.14
|
||||
flutter_background_service: ^5.0.1
|
||||
awesome_notifications_core: 0.8.0
|
||||
awesome_notifications: any
|
||||
permission_handler: ^11.0.1
|
||||
# permission_handler: ^11.0.0
|
||||
|
||||
dev_dependencies:
|
||||
|
||||
@@ -6,11 +6,20 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <awesome_notifications/awesome_notifications_plugin_c_api.h>
|
||||
#include <awesome_notifications_core/awesome_notifications_core_plugin_c_api.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
AwesomeNotificationsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AwesomeNotificationsPluginCApi"));
|
||||
AwesomeNotificationsCorePluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AwesomeNotificationsCorePluginCApi"));
|
||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
awesome_notifications
|
||||
awesome_notifications_core
|
||||
permission_handler_windows
|
||||
screen_retriever
|
||||
url_launcher_windows
|
||||
window_manager
|
||||
|
||||
Reference in New Issue
Block a user