diff --git a/flutter/android/app/src/main/AndroidManifest.xml b/flutter/android/app/src/main/AndroidManifest.xml
index 51015f74a..47533612b 100644
--- a/flutter/android/app/src/main/AndroidManifest.xml
+++ b/flutter/android/app/src/main/AndroidManifest.xml
@@ -15,6 +15,13 @@
+
+
+
+
+
+
+
? get subWindowManagerEnableResizeEdges => isWindows
void earlyAssert() {
assert('\1' == '1');
}
+
+void checkUpdate() {
+ if (isDesktop || isAndroid) {
+ if (!bind.isCustomClient()) {
+ platformFFI.registerEventHandler(
+ kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish,
+ (Map evt) async {
+ if (evt['url'] is String) {
+ stateGlobal.updateUrl.value = evt['url'];
+ }
+ });
+ Timer(const Duration(seconds: 1), () async {
+ bind.mainGetSoftwareUpdateUrl();
+ });
+ }
+ }
+}
diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart
index 04a186b84..754efbd5a 100644
--- a/flutter/lib/desktop/pages/desktop_home_page.dart
+++ b/flutter/lib/desktop/pages/desktop_home_page.dart
@@ -14,6 +14,7 @@ import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/server_model.dart';
+import 'package:flutter_hbb/models/state_model.dart';
import 'package:flutter_hbb/plugin/ui_manager.dart';
import 'package:flutter_hbb/utils/multi_window_manager.dart';
import 'package:get/get.dart';
@@ -39,7 +40,6 @@ class _DesktopHomePageState extends State
@override
bool get wantKeepAlive => true;
- var updateUrl = '';
var systemError = '';
StreamSubscription? _uniLinksSubscription;
var svcStopped = false.obs;
@@ -86,7 +86,8 @@ class _DesktopHomePageState extends State
if (!isOutgoingOnly) buildIDBoard(context),
if (!isOutgoingOnly) buildPasswordBoard(context),
FutureBuilder(
- future: buildHelpCards(),
+ future: Future.value(
+ Obx(() => buildHelpCards(stateGlobal.updateUrl.value))),
builder: (_, data) {
if (data.hasData) {
if (isIncomingOnly) {
@@ -415,7 +416,7 @@ class _DesktopHomePageState extends State
);
}
- Future buildHelpCards() async {
+ Widget buildHelpCards(String updateUrl) {
if (!bind.isCustomClient() &&
updateUrl.isNotEmpty &&
!isCardClosed &&
@@ -669,20 +670,6 @@ class _DesktopHomePageState extends State
@override
void initState() {
super.initState();
- if (!bind.isCustomClient()) {
- platformFFI.registerEventHandler(
- kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish,
- (Map evt) async {
- if (evt['url'] is String) {
- setState(() {
- updateUrl = evt['url'];
- });
- }
- });
- Timer(const Duration(seconds: 1), () async {
- bind.mainGetSoftwareUpdateUrl();
- });
- }
_updateTimer = periodic_immediate(const Duration(seconds: 1), () async {
await gFFI.serverModel.fetchID();
final error = await bind.mainGetError();
diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart
index 6f220a35f..44cdbcbb8 100644
--- a/flutter/lib/desktop/pages/desktop_setting_page.dart
+++ b/flutter/lib/desktop/pages/desktop_setting_page.dart
@@ -14,6 +14,7 @@ import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/server_model.dart';
+import 'package:flutter_hbb/models/state_model.dart';
import 'package:flutter_hbb/plugin/manager.dart';
import 'package:flutter_hbb/plugin/widgets/desktop_settings.dart';
import 'package:get/get.dart';
diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart
index 3176bfb86..301a9f25c 100644
--- a/flutter/lib/main.dart
+++ b/flutter/lib/main.dart
@@ -120,6 +120,7 @@ Future initEnv(String appType) async {
void runMainApp(bool startService) async {
// register uni links
await initEnv(kAppTypeMain);
+ checkUpdate();
// trigger connection status updater
await bind.mainCheckConnectStatus();
if (startService) {
@@ -156,6 +157,7 @@ void runMainApp(bool startService) async {
void runMobileApp() async {
await initEnv(kAppTypeMain);
+ checkUpdate();
if (isAndroid) androidChannelInit();
if (isAndroid) platformFFI.syncAndroidServiceAppDirConfigPath();
draggablePositions.load();
diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart
index de68aa510..49e3b2c91 100644
--- a/flutter/lib/mobile/pages/connection_page.dart
+++ b/flutter/lib/mobile/pages/connection_page.dart
@@ -4,6 +4,7 @@ import 'package:auto_size_text_field/auto_size_text_field.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/formatter/id_formatter.dart';
import 'package:flutter_hbb/common/widgets/connection_page_title.dart';
+import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -40,8 +41,6 @@ class _ConnectionPageState extends State {
final _idController = IDTextEditingController();
final RxBool _idEmpty = true.obs;
- /// Update url. If it's not null, means an update is available.
- var _updateUrl = '';
List peers = [];
bool isPeersLoading = false;
@@ -72,22 +71,6 @@ class _ConnectionPageState extends State {
}
});
}
- if (isAndroid) {
- if (!bind.isCustomClient()) {
- platformFFI.registerEventHandler(
- kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish,
- (Map evt) async {
- if (evt['url'] is String) {
- setState(() {
- _updateUrl = evt['url'];
- });
- }
- });
- Timer(const Duration(seconds: 1), () async {
- bind.mainGetSoftwareUpdateUrl();
- });
- }
- }
}
@override
@@ -97,7 +80,8 @@ class _ConnectionPageState extends State {
slivers: [
SliverList(
delegate: SliverChildListDelegate([
- if (!bind.isCustomClient()) _buildUpdateUI(),
+ if (!bind.isCustomClient())
+ Obx(() => _buildUpdateUI(stateGlobal.updateUrl.value)),
_buildRemoteIDTextField(),
])),
SliverFillRemaining(
@@ -116,13 +100,21 @@ class _ConnectionPageState extends State {
}
/// UI for software update.
- /// If [_updateUrl] is not empty, shows a button to update the software.
- Widget _buildUpdateUI() {
- return _updateUrl.isEmpty
+ /// If _updateUrl] is not empty, shows a button to update the software.
+ Widget _buildUpdateUI(String updateUrl) {
+ return updateUrl.isEmpty
? const SizedBox(height: 0)
: InkWell(
onTap: () async {
final url = 'https://rustdesk.com/download';
+ // https://pub.dev/packages/url_launcher#configuration
+ // https://developer.android.com/training/package-visibility/use-cases#open-urls-custom-tabs
+ //
+ // `await launchUrl(Uri.parse(url))` can also run if skip
+ // 1. The following check
+ // 2. `` in AndroidManifest.xml
+ //
+ // But it is better to add the check.
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
}
diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart
index 9e265810f..ede66d78a 100644
--- a/flutter/lib/mobile/pages/settings_page.dart
+++ b/flutter/lib/mobile/pages/settings_page.dart
@@ -5,6 +5,7 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart';
+import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:settings_ui/settings_ui.dart';
@@ -70,6 +71,7 @@ class _SettingsState extends State with WidgetsBindingObserver {
false; //androidVersion >= 26; // remove because not work on every device
var _ignoreBatteryOpt = false;
var _enableStartOnBoot = false;
+ var _checkUpdateOnStartup = false;
var _floatingWindowDisabled = false;
var _keepScreenOn = KeepScreenOn.duringControlled; // relay on floating window
var _enableAbr = false;
@@ -154,6 +156,13 @@ class _SettingsState extends State with WidgetsBindingObserver {
_enableStartOnBoot = enableStartOnBoot;
}
+ var checkUpdateOnStartup =
+ mainGetLocalBoolOptionSync(kOptionEnableCheckUpdate);
+ if (checkUpdateOnStartup != _checkUpdateOnStartup) {
+ update = true;
+ _checkUpdateOnStartup = checkUpdateOnStartup;
+ }
+
var floatingWindowDisabled =
bind.mainGetLocalOption(key: kOptionDisableFloatingWindow) == "Y" ||
!await AndroidPermissionManager.check(kSystemAlertWindow);
@@ -552,6 +561,22 @@ class _SettingsState extends State with WidgetsBindingObserver {
gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, toValue);
}));
+ if (!bind.isCustomClient()) {
+ enhancementsTiles.add(
+ SettingsTile.switchTile(
+ initialValue: _checkUpdateOnStartup,
+ title:
+ Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
+ Text(translate('Check for software update on startup')),
+ ]),
+ onToggle: (bool toValue) async {
+ await mainSetLocalBoolOption(kOptionEnableCheckUpdate, toValue);
+ setState(() => _checkUpdateOnStartup = toValue);
+ },
+ ),
+ );
+ }
+
onFloatingWindowChanged(bool toValue) async {
if (toValue) {
if (!await AndroidPermissionManager.check(kSystemAlertWindow)) {
diff --git a/flutter/lib/models/state_model.dart b/flutter/lib/models/state_model.dart
index f8f06cc3f..f09603649 100644
--- a/flutter/lib/models/state_model.dart
+++ b/flutter/lib/models/state_model.dart
@@ -25,6 +25,8 @@ class StateGlobal {
final isPortrait = false.obs;
+ final updateUrl = ''.obs;
+
String _inputSource = '';
// Use for desktop -> remote toolbar -> resolution
diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs
index 4630ac333..5b6c48b95 100644
--- a/src/flutter_ffi.rs
+++ b/src/flutter_ffi.rs
@@ -1418,7 +1418,8 @@ pub fn main_get_last_remote_id() -> String {
}
pub fn main_get_software_update_url() {
- if get_local_option("enable-check-update".to_string()) != "N" {
+ let opt = get_local_option(config::keys::OPTION_ENABLE_CHECK_UPDATE.to_string());
+ if config::option2bool(config::keys::OPTION_ENABLE_CHECK_UPDATE, &opt) {
crate::common::check_software_update();
}
}