diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n.dart b/.dart_tool/flutter_gen/gen_l10n/l10n.dart index 4d29426f..e9738c88 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n.dart @@ -1466,6 +1466,12 @@ abstract class S { /// **'Please wait for the connection to be established.'** String get waitConnection; + /// No description provided for @watchNotPaired. + /// + /// In en, this message translates to: + /// **'No paired Apple Watch'** + String get watchNotPaired; + /// No description provided for @whenOpenApp. /// /// In en, this message translates to: diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart index e3c1007f..1971042a 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart @@ -734,6 +734,9 @@ class SDe extends S { @override String get waitConnection => 'Bitte warte, bis die Verbindung hergestellt wurde.'; + @override + String get watchNotPaired => 'Keine gekoppelte Apple Watch'; + @override String get whenOpenApp => 'Beim Öffnen der App'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart index e07dd5c1..9e91711a 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart @@ -734,6 +734,9 @@ class SEn extends S { @override String get waitConnection => 'Please wait for the connection to be established.'; + @override + String get watchNotPaired => 'No paired Apple Watch'; + @override String get whenOpenApp => 'When opening the app'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart index 0d359e28..a33a8f12 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart @@ -734,6 +734,9 @@ class SId extends S { @override String get waitConnection => 'Harap tunggu koneksi akan dibuat.'; + @override + String get watchNotPaired => 'Tidak ada Apple Watch yang dipasangkan'; + @override String get whenOpenApp => 'Saat membuka aplikasi'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart index c5b915f0..bacff7e5 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart @@ -734,6 +734,9 @@ class SZh extends S { @override String get waitConnection => '请等待连接建立'; + @override + String get watchNotPaired => '没有已配对的 Apple Watch'; + @override String get whenOpenApp => '当打开 App 时'; @@ -1475,6 +1478,9 @@ class SZhTw extends SZh { @override String get waitConnection => '請等待連接建立'; + @override + String get watchNotPaired => '沒有已配對的 Apple Watch'; + @override String get whenOpenApp => '當打開 App 時'; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 04df8ee7..3d6b1590 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -24,6 +24,8 @@ PODS: - FlutterMacOS - url_launcher_ios (0.0.1): - Flutter + - watch_connectivity (0.0.1): + - Flutter DEPENDENCIES: - countly_flutter (from `.symlinks/plugins/countly_flutter/ios`) @@ -38,6 +40,7 @@ DEPENDENCIES: - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - watch_connectivity (from `.symlinks/plugins/watch_connectivity/ios`) EXTERNAL SOURCES: countly_flutter: @@ -64,6 +67,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" + watch_connectivity: + :path: ".symlinks/plugins/watch_connectivity/ios" SPEC CHECKSUMS: countly_flutter: f153e5547d4f3cdf24be11f6ed4df32c9a421fa3 @@ -78,6 +83,7 @@ SPEC CHECKSUMS: share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028 shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 + watch_connectivity: 715eb484685e05846eab74795348a44bb2809b82 PODFILE CHECKSUM: 7fb15c416f8685fca4966867a8da218ec592ec2e diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 0d5fa2fe..d78300e4 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -25,7 +25,7 @@ E39515C22AB5AC85003602C1 /* Info-Profile.plist in Resources */ = {isa = PBXBuildFile; fileRef = E39515BF2AB5AC85003602C1 /* Info-Profile.plist */; }; E39515CA2AB5AD62003602C1 /* WatchEndApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E39515C92AB5AD62003602C1 /* WatchEndApp.swift */; }; E39515CC2AB5AD62003602C1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E39515CB2AB5AD62003602C1 /* ContentView.swift */; }; - E39515D42AB5AD64003602C1 /* WatchEnd Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = E39515C72AB5AD62003602C1 /* WatchEnd Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + E39515D42AB5AD64003602C1 /* WatchApp.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = E39515C72AB5AD62003602C1 /* WatchApp.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; E39515DB2AB5AE7F003602C1 /* PhoneConnMgr.swift in Sources */ = {isa = PBXBuildFile; fileRef = E39515DA2AB5AE7F003602C1 /* PhoneConnMgr.swift */; }; E39515DD2AB5AE9E003602C1 /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = E39515DC2AB5AE9E003602C1 /* Store.swift */; }; E3DB67ED2A31FE200027B8CB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E3DB67EB2A31FE200027B8CB /* LaunchScreen.storyboard */; }; @@ -76,7 +76,7 @@ dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; dstSubfolderSpec = 16; files = ( - E39515D42AB5AD64003602C1 /* WatchEnd Watch App.app in Embed Watch Content */, + E39515D42AB5AD64003602C1 /* WatchApp.app in Embed Watch Content */, ); name = "Embed Watch Content"; runOnlyForDeploymentPostprocessing = 0; @@ -109,7 +109,7 @@ E39515BD2AB5AC85003602C1 /* Info-Release.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "Info-Release.plist"; path = "../../../server_box_bak/ios/Runner/Info-Release.plist"; sourceTree = ""; }; E39515BE2AB5AC85003602C1 /* Info-Debug.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "Info-Debug.plist"; path = "../../../server_box_bak/ios/Runner/Info-Debug.plist"; sourceTree = ""; }; E39515BF2AB5AC85003602C1 /* Info-Profile.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "Info-Profile.plist"; path = "../../../server_box_bak/ios/Runner/Info-Profile.plist"; sourceTree = ""; }; - E39515C72AB5AD62003602C1 /* WatchEnd Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "WatchEnd Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + E39515C72AB5AD62003602C1 /* WatchApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WatchApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; E39515C92AB5AD62003602C1 /* WatchEndApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchEndApp.swift; sourceTree = ""; }; E39515CB2AB5AD62003602C1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; E39515DA2AB5AE7F003602C1 /* PhoneConnMgr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhoneConnMgr.swift; sourceTree = ""; }; @@ -185,7 +185,7 @@ children = ( 97C146EE1CF9000F007C117D /* Runner.app */, E33A3E352A626DCD009744AB /* StatusWidgetExtension.appex */, - E39515C72AB5AD62003602C1 /* WatchEnd Watch App.app */, + E39515C72AB5AD62003602C1 /* WatchApp.app */, ); name = Products; sourceTree = ""; @@ -286,9 +286,9 @@ productReference = E33A3E352A626DCD009744AB /* StatusWidgetExtension.appex */; productType = "com.apple.product-type.app-extension"; }; - E39515C62AB5AD62003602C1 /* WatchEnd Watch App */ = { + E39515C62AB5AD62003602C1 /* WatchApp */ = { isa = PBXNativeTarget; - buildConfigurationList = E39515D92AB5AD64003602C1 /* Build configuration list for PBXNativeTarget "WatchEnd Watch App" */; + buildConfigurationList = E39515D92AB5AD64003602C1 /* Build configuration list for PBXNativeTarget "WatchApp" */; buildPhases = ( E39515C32AB5AD62003602C1 /* Sources */, E39515C42AB5AD62003602C1 /* Frameworks */, @@ -298,9 +298,9 @@ ); dependencies = ( ); - name = "WatchEnd Watch App"; + name = WatchApp; productName = "WatchEnd Watch App"; - productReference = E39515C72AB5AD62003602C1 /* WatchEnd Watch App.app */; + productReference = E39515C72AB5AD62003602C1 /* WatchApp.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -340,7 +340,7 @@ targets = ( 97C146ED1CF9000F007C117D /* Runner */, E33A3E342A626DCD009744AB /* StatusWidgetExtension */, - E39515C62AB5AD62003602C1 /* WatchEnd Watch App */, + E39515C62AB5AD62003602C1 /* WatchApp */, ); }; /* End PBXProject section */ @@ -491,7 +491,7 @@ }; E39515D32AB5AD64003602C1 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = E39515C62AB5AD62003602C1 /* WatchEnd Watch App */; + target = E39515C62AB5AD62003602C1 /* WatchApp */; targetProxy = E39515D22AB5AD64003602C1 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -1013,7 +1013,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - E39515D92AB5AD64003602C1 /* Build configuration list for PBXNativeTarget "WatchEnd Watch App" */ = { + E39515D92AB5AD64003602C1 /* Build configuration list for PBXNativeTarget "WatchApp" */ = { isa = XCConfigurationList; buildConfigurations = ( E39515D62AB5AD64003602C1 /* Debug */, diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index b52b2e69..a6b826db 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -27,8 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/WatchApp.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/WatchApp.xcscheme new file mode 100644 index 00000000..021f347f --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/WatchApp.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index c7cbe3b7..d3e8c9fa 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -228,6 +228,7 @@ "virtKeyHelpClipboard": "In die Zwischenablage kopieren, wenn das ausgewählte Terminal nicht leer ist, andernfalls den Inhalt der Zwischenablage in das Terminal einfügen.", "virtKeyHelpSFTP": "Aktuelles Verzeichnis in SFTP öffnen.", "waitConnection": "Bitte warte, bis die Verbindung hergestellt wurde.", + "watchNotPaired": "Keine gekoppelte Apple Watch", "whenOpenApp": "Beim Öffnen der App", "willTakEeffectImmediately": "Wird sofort angewendet" } \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 4409bf1f..ad85fd37 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -228,6 +228,7 @@ "virtKeyHelpClipboard": "Copy to the clipboard if terminal selected is not empty, otherwise paste the contents of the clipboard to the terminal.", "virtKeyHelpSFTP": "Open current directory in SFTP.", "waitConnection": "Please wait for the connection to be established.", + "watchNotPaired": "No paired Apple Watch", "whenOpenApp": "When opening the app", "willTakEeffectImmediately": "Will take effect immediately" } \ No newline at end of file diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index ef134fd7..dcecaebc 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -228,6 +228,7 @@ "virtKeyHelpClipboard": "Salin ke clipboard jika terminal yang dipilih tidak kosong, jika tidak, tempel isi clipboard ke terminal.", "virtKeyHelpSFTP": "Buka direktori saat ini di SFTP.", "waitConnection": "Harap tunggu koneksi akan dibuat.", + "watchNotPaired": "Tidak ada Apple Watch yang dipasangkan", "whenOpenApp": "Saat membuka aplikasi", "willTakEeffectImmediately": "Akan segera berlaku" } \ No newline at end of file diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 11e654ae..43a1455a 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -228,6 +228,7 @@ "virtKeyHelpClipboard": "如果终端有选中字符,则复制选中字符至剪切板,否则粘贴剪切板内容至终端。", "virtKeyHelpSFTP": "在 SFTP 中打开当前路径。", "waitConnection": "请等待连接建立", + "watchNotPaired": "没有已配对的 Apple Watch", "whenOpenApp": "当打开 App 时", "willTakEeffectImmediately": "更改将会立即生效" } \ No newline at end of file diff --git a/lib/l10n/app_zh_tw.arb b/lib/l10n/app_zh_tw.arb index 8d83ad2a..79307a0c 100644 --- a/lib/l10n/app_zh_tw.arb +++ b/lib/l10n/app_zh_tw.arb @@ -228,6 +228,7 @@ "virtKeyHelpClipboard": "如果終端有選中字符,則復製選中字符至剪切板,否則粘貼剪切板內容至終端。", "virtKeyHelpSFTP": "在 SFTP 中打開當前路徑。", "waitConnection": "請等待連接建立", + "watchNotPaired": "沒有已配對的 Apple Watch", "whenOpenApp": "當打開 App 時", "willTakEeffectImmediately": "更改將會立即生效" } \ No newline at end of file diff --git a/lib/view/page/home.dart b/lib/view/page/home.dart index c419c792..422d4651 100644 --- a/lib/view/page/home.dart +++ b/lib/view/page/home.dart @@ -362,9 +362,10 @@ class _HomePageState extends State } Future _onLongPressSetting() async { - /// Encode [map] to String with indent `\t` final map = Stores.setting.toJson(); final keys = map.keys; + + /// Encode [map] to String with indent `\t` final text = Miscs.jsonEncoder.convert(map); final result = await AppRoute.editor( text: text, diff --git a/lib/view/page/setting/entry.dart b/lib/view/page/setting/entry.dart index 5d8637ab..a27ce472 100644 --- a/lib/view/page/setting/entry.dart +++ b/lib/view/page/setting/entry.dart @@ -6,6 +6,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_highlight/theme_map.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:toolbox/core/build_mode.dart'; import 'package:toolbox/core/extension/colorx.dart'; import 'package:toolbox/core/extension/context/common.dart'; import 'package:toolbox/core/extension/context/snackbar.dart'; @@ -14,8 +15,11 @@ import 'package:toolbox/core/extension/context/dialog.dart'; import 'package:toolbox/core/extension/stringx.dart'; import 'package:toolbox/core/utils/platform/auth.dart'; import 'package:toolbox/core/utils/platform/base.dart'; +import 'package:toolbox/data/res/logger.dart'; +import 'package:toolbox/data/res/misc.dart'; import 'package:toolbox/data/res/provider.dart'; import 'package:toolbox/data/res/store.dart'; +import 'package:watch_connectivity/watch_connectivity.dart'; import '../../../core/persistant_store.dart'; import '../../../core/route.dart'; @@ -57,6 +61,8 @@ class _SettingPageState extends State { late S _s; late SharedPreferences _sp; + final wc = WatchConnectivity(); + final _selectedColorValue = ValueNotifier(0); final _nightMode = ValueNotifier(0); final _maxRetryCount = ValueNotifier(0); @@ -69,7 +75,6 @@ class _SettingPageState extends State { final _keyboardType = ValueNotifier(0); final _rotateQuarter = ValueNotifier(0); final _netViewType = ValueNotifier(NetViewType.speed); - final _pushToken = ValueNotifier(null); @override @@ -188,13 +193,14 @@ class _SettingPageState extends State { children.add(_buildBgRun()); children.add(_buildAndroidWidgetSharedPreference()); } - if (isIOS) { - children.add(_buildPushToken()); - children.add(_buildAutoUpdateHomeWidget()); - } if (BioAuth.isPlatformSupported) { children.add(_buildBioAuth()); } + if (isIOS) { + if (BuildMode.isRelease) children.add(_buildPushToken()); + children.add(_buildAutoUpdateHomeWidget()); + children.add(_buildWatchApp()); + } return Column( children: children.map((e) => RoundRectCard(e)).toList(), ); @@ -1115,6 +1121,10 @@ class _SettingPageState extends State { success: (can) { return ListTile( title: Text(_s.bioAuth), + subtitle: can + ? null + : const Text('Error: Bio auth is not available', + style: UIs.textGrey), trailing: can ? StoreSwitch( prop: Stores.setting.useBioAuth, @@ -1131,10 +1141,66 @@ class _SettingPageState extends State { } }, ) - : Text(_s.error, style: UIs.textGrey), + : null, ); }, noData: UIs.placeholder, ); } + + Widget _buildWatchApp() { + return FutureWidget?>( + future: () async { + if (!await wc.isPaired) { + return null; + } + return await wc.applicationContext; + }(), + loading: UIs.centerLoading, + error: (e, trace) { + Loggers.app.warning('WatchOS error', e, trace); + return ListTile( + title: const Text('Watch app'), + subtitle: Text('${_s.error}: $e', style: UIs.textGrey), + ); + }, + success: (ctx) { + if (ctx == null) { + return ListTile( + title: const Text('Watch app'), + subtitle: Text(_s.watchNotPaired, style: UIs.textGrey), + ); + } + return ListTile( + title: const Text('Watch app'), + trailing: const Icon(Icons.keyboard_arrow_right), + onTap: () async => _onTapWatchApp(ctx), + ); + }, + noData: UIs.placeholder, + ); + } + + void _onTapWatchApp(Map map) async { + /// Encode [map] to String with indent `\t` + final text = Miscs.jsonEncoder.convert(map); + final result = await AppRoute.editor( + text: text, + langCode: 'json', + title: 'Watch app config', + ).go(context); + if (result == null) { + return; + } + try { + final newCtx = json.decode(result) as Map; + await wc.updateApplicationContext(newCtx); + } catch (e, trace) { + context.showRoundDialog( + title: Text(_s.error), + child: Text('${_s.save}:\n$e'), + ); + Loggers.app.warning('Update watch config failed', e, trace); + } + } } diff --git a/pubspec.lock b/pubspec.lock index b32c45bb..1f259e33 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1051,6 +1051,23 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + watch_connectivity: + dependency: "direct main" + description: + path: "." + ref: master + resolved-ref: "56379d77955db89222f00ee18f5cae80b05ec13f" + url: "https://github.com/lollipopkit/watch_connectivity" + source: git + version: "0.1.5" + watch_connectivity_platform_interface: + dependency: transitive + description: + name: watch_connectivity_platform_interface + sha256: "9074115391bd764c08a17346fcbc4d5c0b555672defbe6928ac648503b54aa9c" + url: "https://pub.dev" + source: hosted + version: "0.1.2" watcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 609152d0..ca0c0a20 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,6 +50,11 @@ dependencies: dynamic_color: ^1.6.6 icloud_storage: ^2.2.0 local_auth: ^2.1.7 + watch_connectivity: + #path: ../watch_connectivity + git: + ref: master + url: https://github.com/lollipopkit/watch_connectivity dev_dependencies: flutter_native_splash: ^2.1.6