Skip to content

Commit ac959bb

Browse files
committed
feat: refresh on wake in desktop platforms
1 parent cafc846 commit ac959bb

5 files changed

Lines changed: 136 additions & 43 deletions

File tree

lib/bloc.dart

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'dart:io';
44

55
import 'package:connectivity_plus/connectivity_plus.dart';
66
import 'package:flutter/material.dart';
7+
import 'package:flutter/services.dart';
78
import 'package:flutter_speedtest/flutter_speedtest.dart';
89
import 'package:http/http.dart' as http;
910
import 'package:http/io_client.dart';
@@ -21,6 +22,8 @@ import 'package:win_toast/win_toast.dart';
2122
import 'utils/kerio.dart';
2223

2324
class AppBloc with AppSystemTray {
25+
static const platform = MethodChannel('ir_net/system_events');
26+
2427
final _latLng = StreamController<LatLng>();
2528
final _ipLookupResult = BehaviorSubject();
2629
final _clearLeakInput = LiveEvent();
@@ -65,7 +68,20 @@ class AppBloc with AppSystemTray {
6568
_runIpCheckInfinitely();
6669
_runKerioCheckInfinitely();
6770
_subscribeConnectivityChange();
71+
_loginKerio(false);
6872
_initializeLeakChecklist();
73+
_initializeNativeChannel();
74+
}
75+
76+
void _initializeNativeChannel() {
77+
platform.setMethodCallHandler((call) async {
78+
if (call.method == 'onMacOsWake'
79+
|| call.method == 'onWindowsWake'
80+
|| call.method == 'onLinuxWake') {
81+
await Future.delayed(const Duration(seconds: 3));
82+
_handleRefresh();
83+
}
84+
});
6985
}
7086

7187
void _initializeLeakChecklist() async {
@@ -93,25 +109,31 @@ class AppBloc with AppSystemTray {
93109
_leakInput = null;
94110
}
95111

96-
void onKerioLoginClick(bool auto, String ip, String username, String password) async {
97-
await AppSharedPreferences.setKerioIP(ip);
98-
await AppSharedPreferences.setKerioUsername(username);
99-
await AppSharedPreferences.setKerioPassword(password);
112+
void onKerioLoginClick() async {
113+
await _loginKerio(true);
114+
_checkKerioBalance();
115+
}
100116

117+
Future<void> _loginKerio(bool manual) async {
118+
var auto = await AppSharedPreferences.kerioAutoLogin;
119+
if (!manual && !auto) {
120+
return Future.value();
121+
}
122+
final ip = await AppSharedPreferences.kerioIP;
123+
final username = await AppSharedPreferences.kerioUsername;
124+
final password = await AppSharedPreferences.kerioPassword;
101125
final url = 'http://$ip/internal/dologin.php';
102-
final response = await http.post(
126+
await http.post(
103127
Uri.parse(url),
104128
body: {
105129
'kerio_username': username,
106130
'kerio_password': password,
107131
},
108132
);
109-
_checkKerioBalance();
110133
}
111134

112135
Future<void> _updateLeakChecklist() async {
113-
_leakChecklist.value =
114-
(await AppSharedPreferences.leakChecklist).map((e) => LeakItem(e)).toList();
136+
_leakChecklist.value = (await AppSharedPreferences.leakChecklist).map((e) => LeakItem(e)).toList();
115137
}
116138

117139
void _verifyLeakedSites() async {
@@ -343,6 +365,7 @@ class AppBloc with AppSystemTray {
343365

344366
void _handleRefresh() async {
345367
await _checkProxySettings();
368+
_loginKerio(false);
346369
_checkPing();
347370
_pingGoogle();
348371
_checkIpLocation();

lib/views/kerio_login.dart

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,47 +26,15 @@ class _KerioLoginViewState extends State<KerioLoginView> {
2626
final ip = await AppSharedPreferences.kerioIP;
2727
final username = await AppSharedPreferences.kerioUsername;
2828
final password = await AppSharedPreferences.kerioPassword;
29-
final enabled = await AppSharedPreferences.kerioAutoLogin;
3029

3130
_ipController.text = ip ?? '';
32-
if (ip != null && username != null && password != null && enabled == true) {
31+
if (ip != null && username != null && password != null) {
3332
_ipController.text = ip;
3433
_usernameController.text = username;
3534
_passwordController.text = password;
36-
_login(true);
3735
}
3836
}
3937

40-
void _login(bool auto) async {
41-
final ip = _ipController.text;
42-
final username = _usernameController.text;
43-
final password = _passwordController.text;
44-
45-
if (ip.isEmpty || username.isEmpty || password.isEmpty) {
46-
_showMessage('Please fill in all fields');
47-
return;
48-
}
49-
if (!auto) {
50-
_showMessage('Login request sent');
51-
}
52-
bloc.onKerioLoginClick(auto, ip, username, password);
53-
}
54-
55-
void _showMessage(String message) {
56-
showDialog(
57-
context: context,
58-
builder: (context) => AlertDialog(
59-
content: Text(message),
60-
actions: [
61-
TextButton(
62-
onPressed: () => Navigator.of(context).pop(),
63-
child: const Text('OK'),
64-
),
65-
],
66-
),
67-
);
68-
}
69-
7038
@override
7139
Widget build(BuildContext context) {
7240
return SizedBox(
@@ -146,7 +114,7 @@ class _KerioLoginViewState extends State<KerioLoginView> {
146114
}
147115
}
148116
},
149-
icon: Image.asset('assets/kerio.dart.png', width: 24, height: 24),
117+
icon: Image.asset('assets/kerio.png', width: 24, height: 24),
150118
),
151119
),
152120
keyboardType: TextInputType.url,
@@ -206,7 +174,7 @@ class _KerioLoginViewState extends State<KerioLoginView> {
206174
Expanded(
207175
flex: 2,
208176
child: ElevatedButton(
209-
onPressed: () => _login(false),
177+
onPressed: () => _login(),
210178
style: ElevatedButton.styleFrom(
211179
shape: const RoundedRectangleBorder(
212180
borderRadius: BorderRadius.all(Radius.circular(8)),
@@ -242,4 +210,35 @@ class _KerioLoginViewState extends State<KerioLoginView> {
242210
},
243211
);
244212
}
213+
214+
void _login() async {
215+
final ip = _ipController.text;
216+
final username = _usernameController.text;
217+
final password = _passwordController.text;
218+
219+
if (ip.isEmpty || username.isEmpty || password.isEmpty) {
220+
_showMessage('Please fill in all fields');
221+
return;
222+
}
223+
await AppSharedPreferences.setKerioIP(ip);
224+
await AppSharedPreferences.setKerioUsername(username);
225+
await AppSharedPreferences.setKerioPassword(password);
226+
_showMessage('Login request sent');
227+
bloc.onKerioLoginClick();
228+
}
229+
230+
void _showMessage(String message) {
231+
showDialog(
232+
context: context,
233+
builder: (context) => AlertDialog(
234+
content: Text(message),
235+
actions: [
236+
TextButton(
237+
onPressed: () => Navigator.of(context).pop(),
238+
child: const Text('OK'),
239+
),
240+
],
241+
),
242+
);
243+
}
245244
}

linux/my_application.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,27 @@
1010
struct _MyApplication {
1111
GtkApplication parent_instance;
1212
char** dart_entrypoint_arguments;
13+
FlMethodChannel* method_channel;
14+
GDBusConnection* system_bus;
1315
};
1416

1517
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
1618

19+
static void system_awake_callback(GDBusConnection* connection,
20+
const gchar* sender_name,
21+
const gchar* object_path,
22+
const gchar* interface_name,
23+
const gchar* signal_name,
24+
GVariant* parameters,
25+
gpointer user_data) {
26+
MyApplication* self = MY_APPLICATION(user_data);
27+
gboolean sleeping;
28+
g_variant_get(parameters, "(b)", &sleeping);
29+
if (!sleeping && self->method_channel) {
30+
fl_method_channel_invoke_method(self->method_channel, "onLinuxWake", nullptr, nullptr, nullptr, nullptr);
31+
}
32+
}
33+
1734
// Implements GApplication::activate.
1835
static void my_application_activate(GApplication* application) {
1936
MyApplication* self = MY_APPLICATION(application);
@@ -59,6 +76,25 @@ static void my_application_activate(GApplication* application) {
5976

6077
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
6178

79+
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
80+
FlEngine* engine = fl_view_get_engine(view);
81+
FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(engine);
82+
self->method_channel = fl_method_channel_new(messenger, "ir_net/system_events", FL_METHOD_CODEC(codec));
83+
84+
self->system_bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr);
85+
if (self->system_bus) {
86+
g_dbus_connection_signal_subscribe(self->system_bus,
87+
"org.freedesktop.login1",
88+
"org.freedesktop.login1.Manager",
89+
"PrepareForSleep",
90+
"/org/freedesktop/login1",
91+
nullptr,
92+
G_DBUS_SIGNAL_FLAGS_NONE,
93+
system_awake_callback,
94+
self,
95+
nullptr);
96+
}
97+
6298
gtk_widget_grab_focus(GTK_WIDGET(view));
6399
}
64100

@@ -85,6 +121,8 @@ static gboolean my_application_local_command_line(GApplication* application, gch
85121
static void my_application_dispose(GObject* object) {
86122
MyApplication* self = MY_APPLICATION(object);
87123
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
124+
g_clear_object(&self->method_channel);
125+
g_clear_object(&self->system_bus);
88126
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
89127
}
90128

macos/Runner/AppDelegate.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,26 @@ import FlutterMacOS
33

44
@main
55
class AppDelegate: FlutterAppDelegate {
6+
var channel: FlutterMethodChannel?
7+
8+
override func applicationDidFinishLaunching(_ aNotification: Notification) {
9+
let controller = mainFlutterWindow?.contentViewController as! FlutterViewController
10+
channel = FlutterMethodChannel(name: "ir_net/system_events",
11+
binaryMessenger: controller.engine.binaryMessenger)
12+
13+
NSWorkspace.shared.notificationCenter.addObserver(
14+
self,
15+
selector: #selector(didWake),
16+
name: NSWorkspace.didWakeNotification,
17+
object: nil
18+
)
19+
super.applicationDidFinishLaunching(aNotification)
20+
}
21+
22+
@objc func didWake(_ notification: Notification) {
23+
channel?.invokeMethod("onMacOsWake", arguments: nil)
24+
}
25+
626
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
727
return false
828
}

windows/runner/flutter_window.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
#include "flutter_window.h"
2+
#include <flutter/event_channel.h>
3+
#include <flutter/event_stream_handler_functions.h>
4+
#include <flutter/method_channel.h>
5+
#include <flutter/standard_method_codec.h>
6+
#include <windows.h>
27

38
#include <optional>
49

@@ -55,6 +60,14 @@ FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
5560
case WM_FONTCHANGE:
5661
flutter_controller_->engine()->ReloadSystemFonts();
5762
break;
63+
case WM_POWERBROADCAST:
64+
if (wparam == PBT_APMRESUMEAUTOMATIC) {
65+
flutter::MethodChannel<> channel(
66+
flutter_controller_->engine()->messenger(), "ir_net/system_events",
67+
&flutter::StandardMethodCodec::GetInstance());
68+
channel.InvokeMethod("onWindowsWake", nullptr);
69+
}
70+
break;
5871
case WM_CLOSE:
5972
ShowWindow(GetHandle(), SW_HIDE);
6073
return 0;

0 commit comments

Comments
 (0)