neoagent 2.3.1-beta.20 → 2.3.1-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/flutter_app/.metadata +42 -0
- package/flutter_app/README.md +21 -0
- package/flutter_app/analysis_options.yaml +32 -0
- package/flutter_app/android/app/build.gradle.kts +109 -0
- package/flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
- package/flutter_app/android/app/src/main/AndroidManifest.xml +147 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +747 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthConnectGateway.kt +280 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncNotifications.kt +113 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncPayload.kt +57 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncScheduler.kt +78 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncWorker.kt +253 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/PermissionsRationaleActivity.kt +46 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +21 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +586 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +78 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +104 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiHomeWidgetProvider.kt +457 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiWidgetStore.kt +194 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/VoiceLaunchWidgetProvider.kt +67 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetConfigActivity.kt +228 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncScheduler.kt +72 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncWorker.kt +186 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetTaskRunWorker.kt +210 -0
- package/flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
- package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_bg.xml +11 -0
- package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_task_bg.xml +8 -0
- package/flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
- package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget.xml +138 -0
- package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget_task_row.xml +52 -0
- package/flutter_app/android/app/src/main/res/layout/neoagent_voice_widget.xml +49 -0
- package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/values/strings.xml +12 -0
- package/flutter_app/android/app/src/main/res/values/styles.xml +18 -0
- package/flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
- package/flutter_app/android/app/src/main/res/xml/file_paths.xml +6 -0
- package/flutter_app/android/app/src/main/res/xml/neoagent_ai_widget_info.xml +12 -0
- package/flutter_app/android/app/src/main/res/xml/neoagent_voice_widget_info.xml +12 -0
- package/flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
- package/flutter_app/android/build.gradle.kts +24 -0
- package/flutter_app/android/ci-release.keystore +0 -0
- package/flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
- package/flutter_app/android/gradle.properties +3 -0
- package/flutter_app/android/key.properties +4 -0
- package/flutter_app/android/settings.gradle.kts +26 -0
- package/flutter_app/assets/branding/app_icon_1024.png +0 -0
- package/flutter_app/assets/branding/app_icon_128.png +0 -0
- package/flutter_app/assets/branding/app_icon_192.png +0 -0
- package/flutter_app/assets/branding/app_icon_256.png +0 -0
- package/flutter_app/assets/branding/app_icon_32.png +0 -0
- package/flutter_app/assets/branding/app_icon_512.png +0 -0
- package/flutter_app/assets/branding/app_icon_64.png +0 -0
- package/flutter_app/assets/branding/tray_icon_template.png +0 -0
- package/flutter_app/lib/features/location/location_service.dart +119 -0
- package/flutter_app/lib/features/notifications/notification_interceptor.dart +97 -0
- package/flutter_app/lib/main.dart +23057 -0
- package/flutter_app/lib/main_app_shell.dart +1682 -0
- package/flutter_app/lib/main_integrations.dart +931 -0
- package/flutter_app/lib/main_launcher.dart +959 -0
- package/flutter_app/lib/main_launcher_entry.dart +5 -0
- package/flutter_app/lib/main_models.dart +3473 -0
- package/flutter_app/lib/main_shared.dart +2861 -0
- package/flutter_app/lib/main_theme.dart +204 -0
- package/flutter_app/lib/main_voice_assistant.dart +831 -0
- package/flutter_app/lib/src/android_apk_drop_zone.dart +32 -0
- package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +16 -0
- package/flutter_app/lib/src/android_apk_drop_zone_web.dart +348 -0
- package/flutter_app/lib/src/android_app_installer.dart +22 -0
- package/flutter_app/lib/src/android_app_installer_io.dart +122 -0
- package/flutter_app/lib/src/android_app_installer_stub.dart +21 -0
- package/flutter_app/lib/src/android_launcher_bridge.dart +239 -0
- package/flutter_app/lib/src/app_launch_bridge.dart +29 -0
- package/flutter_app/lib/src/app_release_updater.dart +511 -0
- package/flutter_app/lib/src/backend_client.dart +1833 -0
- package/flutter_app/lib/src/desktop_companion.dart +2 -0
- package/flutter_app/lib/src/desktop_companion_actions.dart +586 -0
- package/flutter_app/lib/src/desktop_companion_io.dart +538 -0
- package/flutter_app/lib/src/desktop_companion_stub.dart +59 -0
- package/flutter_app/lib/src/desktop_native_bridge.dart +91 -0
- package/flutter_app/lib/src/desktop_screen_capture.dart +21 -0
- package/flutter_app/lib/src/desktop_screen_capture_io.dart +142 -0
- package/flutter_app/lib/src/desktop_screen_capture_stub.dart +12 -0
- package/flutter_app/lib/src/diagnostics_logger.dart +119 -0
- package/flutter_app/lib/src/health_bridge.dart +136 -0
- package/flutter_app/lib/src/live_voice_capture.dart +85 -0
- package/flutter_app/lib/src/messaging_access_summary.dart +46 -0
- package/flutter_app/lib/src/network/app_http_client.dart +53 -0
- package/flutter_app/lib/src/network/app_http_client_factory.dart +6 -0
- package/flutter_app/lib/src/network/app_http_client_io.dart +138 -0
- package/flutter_app/lib/src/network/app_http_client_stub.dart +3 -0
- package/flutter_app/lib/src/network/app_http_client_web.dart +94 -0
- package/flutter_app/lib/src/oauth_launcher.dart +33 -0
- package/flutter_app/lib/src/oauth_launcher_io.dart +77 -0
- package/flutter_app/lib/src/oauth_launcher_stub.dart +33 -0
- package/flutter_app/lib/src/oauth_launcher_web.dart +107 -0
- package/flutter_app/lib/src/recording_bridge.dart +232 -0
- package/flutter_app/lib/src/recording_bridge_io.dart +1019 -0
- package/flutter_app/lib/src/recording_bridge_stub.dart +120 -0
- package/flutter_app/lib/src/recording_bridge_web.dart +689 -0
- package/flutter_app/lib/src/recording_payloads.dart +86 -0
- package/flutter_app/lib/src/theme/palette.dart +81 -0
- package/flutter_app/lib/src/widget_bridge.dart +49 -0
- package/flutter_app/linux/CMakeLists.txt +128 -0
- package/flutter_app/linux/flutter/CMakeLists.txt +88 -0
- package/flutter_app/linux/flutter/generated_plugin_registrant.cc +43 -0
- package/flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
- package/flutter_app/linux/flutter/generated_plugins.cmake +31 -0
- package/flutter_app/linux/runner/CMakeLists.txt +26 -0
- package/flutter_app/linux/runner/main.cc +6 -0
- package/flutter_app/linux/runner/my_application.cc +144 -0
- package/flutter_app/linux/runner/my_application.h +18 -0
- package/flutter_app/linux/runner/resources/app_icon.png +0 -0
- package/flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
- package/flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
- package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +40 -0
- package/flutter_app/macos/Podfile +42 -0
- package/flutter_app/macos/Podfile.lock +87 -0
- package/flutter_app/macos/Runner/AppDelegate.swift +576 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
- package/flutter_app/macos/Runner/Base.lproj/MainMenu.xib +342 -0
- package/flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
- package/flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
- package/flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
- package/flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
- package/flutter_app/macos/Runner/DebugProfile.entitlements +16 -0
- package/flutter_app/macos/Runner/Info.plist +36 -0
- package/flutter_app/macos/Runner/MainFlutterWindow.swift +19 -0
- package/flutter_app/macos/Runner/Release.entitlements +12 -0
- package/flutter_app/macos/Runner.xcodeproj/project.pbxproj +801 -0
- package/flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
- package/flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +10 -0
- package/flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
- package/flutter_app/patch_strings.py +12 -0
- package/flutter_app/pubspec.lock +1088 -0
- package/flutter_app/pubspec.yaml +53 -0
- package/flutter_app/test/messaging_access_summary_test.dart +22 -0
- package/flutter_app/test/recording_payloads_test.dart +53 -0
- package/flutter_app/third_party/desktop_audio_capture/LICENSE +21 -0
- package/flutter_app/third_party/desktop_audio_capture/README.md +262 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +65 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +153 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +110 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +461 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +91 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +106 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +219 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +336 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +101 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +692 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +35 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +36 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +32 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +32 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +878 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +27 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +1172 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +655 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +12 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +30 -0
- package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +87 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +105 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +80 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +31 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +12 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +23 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +25 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +1117 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +115 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +777 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +87 -0
- package/flutter_app/third_party/flutter_secure_storage_linux/linux/CMakeLists.txt +30 -0
- package/flutter_app/third_party/flutter_secure_storage_linux/linux/flutter_secure_storage_linux_plugin.cc +215 -0
- package/flutter_app/third_party/flutter_secure_storage_linux/linux/include/flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h +27 -0
- package/flutter_app/third_party/flutter_secure_storage_linux/pubspec.yaml +20 -0
- package/flutter_app/tool/generate_desktop_branding.py +219 -0
- package/flutter_app/web/favicon.png +0 -0
- package/flutter_app/web/favicon.svg +12 -0
- package/flutter_app/web/icons/Icon-192.png +0 -0
- package/flutter_app/web/icons/Icon-512.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
- package/flutter_app/web/index.html +39 -0
- package/flutter_app/web/manifest.json +35 -0
- package/flutter_app/windows/CMakeLists.txt +108 -0
- package/flutter_app/windows/flutter/CMakeLists.txt +109 -0
- package/flutter_app/windows/flutter/generated_plugin_registrant.cc +47 -0
- package/flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
- package/flutter_app/windows/flutter/generated_plugins.cmake +35 -0
- package/flutter_app/windows/runner/CMakeLists.txt +41 -0
- package/flutter_app/windows/runner/Runner.rc +121 -0
- package/flutter_app/windows/runner/flutter_window.cpp +533 -0
- package/flutter_app/windows/runner/flutter_window.h +37 -0
- package/flutter_app/windows/runner/main.cpp +53 -0
- package/flutter_app/windows/runner/resource.h +16 -0
- package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
- package/flutter_app/windows/runner/runner.exe.manifest +14 -0
- package/flutter_app/windows/runner/utils.cpp +65 -0
- package/flutter_app/windows/runner/utils.h +19 -0
- package/flutter_app/windows/runner/win32_window.cpp +299 -0
- package/flutter_app/windows/runner/win32_window.h +102 -0
- package/package.json +2 -1
- package/server/public/flutter_bootstrap.js +1 -1
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
import Cocoa
|
|
2
|
+
import FlutterMacOS
|
|
3
|
+
import ApplicationServices
|
|
4
|
+
import CoreGraphics
|
|
5
|
+
|
|
6
|
+
@main
|
|
7
|
+
class AppDelegate: FlutterAppDelegate {
|
|
8
|
+
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
|
9
|
+
return false
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
|
|
13
|
+
return true
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
final class DesktopCompanionNativePlugin: NSObject {
|
|
18
|
+
private static let channelName = "neoagent/desktop_companion_native"
|
|
19
|
+
private static var channelAssociationKey: UInt8 = 0
|
|
20
|
+
private static var instanceAssociationKey: UInt8 = 0
|
|
21
|
+
|
|
22
|
+
static func register(with controller: FlutterViewController) {
|
|
23
|
+
let channel = FlutterMethodChannel(
|
|
24
|
+
name: channelName,
|
|
25
|
+
binaryMessenger: controller.engine.binaryMessenger
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
let instance = DesktopCompanionNativePlugin()
|
|
29
|
+
channel.setMethodCallHandler { call, result in
|
|
30
|
+
instance.handle(call, result: result)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
objc_setAssociatedObject(
|
|
34
|
+
controller,
|
|
35
|
+
&channelAssociationKey,
|
|
36
|
+
channel,
|
|
37
|
+
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
|
|
38
|
+
)
|
|
39
|
+
objc_setAssociatedObject(
|
|
40
|
+
controller,
|
|
41
|
+
&instanceAssociationKey,
|
|
42
|
+
instance,
|
|
43
|
+
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
48
|
+
switch call.method {
|
|
49
|
+
case "getStatus":
|
|
50
|
+
result(buildDesktopStatus())
|
|
51
|
+
case "listDisplays":
|
|
52
|
+
result(desktopDisplays())
|
|
53
|
+
case "captureFrame":
|
|
54
|
+
guard preflightScreenCapturePermission() else {
|
|
55
|
+
result(
|
|
56
|
+
FlutterError(
|
|
57
|
+
code: "screen_capture_permission_denied",
|
|
58
|
+
message: "Screen Recording permission is required.",
|
|
59
|
+
details: nil
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
let arguments = call.arguments as? [String: Any]
|
|
65
|
+
let displayId = arguments?["displayId"] as? String
|
|
66
|
+
do {
|
|
67
|
+
result(try captureFrame(displayId: displayId))
|
|
68
|
+
} catch {
|
|
69
|
+
result(
|
|
70
|
+
FlutterError(
|
|
71
|
+
code: "capture_failed",
|
|
72
|
+
message: "Desktop capture failed.",
|
|
73
|
+
details: "\(error)"
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
case "click":
|
|
78
|
+
guard isAccessibilityTrusted() else {
|
|
79
|
+
result(
|
|
80
|
+
FlutterError(
|
|
81
|
+
code: "accessibility_permission_denied",
|
|
82
|
+
message: "Accessibility permission is required for input control.",
|
|
83
|
+
details: nil
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
guard let arguments = call.arguments as? [String: Any],
|
|
89
|
+
let x = arguments["x"] as? NSNumber,
|
|
90
|
+
let y = arguments["y"] as? NSNumber else {
|
|
91
|
+
result(
|
|
92
|
+
FlutterError(
|
|
93
|
+
code: "invalid_arguments",
|
|
94
|
+
message: "Missing click coordinates.",
|
|
95
|
+
details: nil
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
let button = (arguments["button"] as? String) ?? "left"
|
|
101
|
+
let displayId = arguments["displayId"] as? String
|
|
102
|
+
performClick(
|
|
103
|
+
x: x.doubleValue,
|
|
104
|
+
y: y.doubleValue,
|
|
105
|
+
button: button,
|
|
106
|
+
displayId: displayId
|
|
107
|
+
)
|
|
108
|
+
result(nil)
|
|
109
|
+
case "drag":
|
|
110
|
+
guard isAccessibilityTrusted() else {
|
|
111
|
+
result(
|
|
112
|
+
FlutterError(
|
|
113
|
+
code: "accessibility_permission_denied",
|
|
114
|
+
message: "Accessibility permission is required for input control.",
|
|
115
|
+
details: nil
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
guard let arguments = call.arguments as? [String: Any],
|
|
121
|
+
let x1 = arguments["x1"] as? NSNumber,
|
|
122
|
+
let y1 = arguments["y1"] as? NSNumber,
|
|
123
|
+
let x2 = arguments["x2"] as? NSNumber,
|
|
124
|
+
let y2 = arguments["y2"] as? NSNumber else {
|
|
125
|
+
result(
|
|
126
|
+
FlutterError(
|
|
127
|
+
code: "invalid_arguments",
|
|
128
|
+
message: "Missing drag coordinates.",
|
|
129
|
+
details: nil
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
let durationMs = (arguments["durationMs"] as? NSNumber)?.intValue ?? 280
|
|
135
|
+
let displayId = arguments["displayId"] as? String
|
|
136
|
+
performDrag(
|
|
137
|
+
x1: x1.doubleValue,
|
|
138
|
+
y1: y1.doubleValue,
|
|
139
|
+
x2: x2.doubleValue,
|
|
140
|
+
y2: y2.doubleValue,
|
|
141
|
+
durationMs: durationMs,
|
|
142
|
+
displayId: displayId
|
|
143
|
+
)
|
|
144
|
+
result(nil)
|
|
145
|
+
case "scroll":
|
|
146
|
+
guard isAccessibilityTrusted() else {
|
|
147
|
+
result(
|
|
148
|
+
FlutterError(
|
|
149
|
+
code: "accessibility_permission_denied",
|
|
150
|
+
message: "Accessibility permission is required for input control.",
|
|
151
|
+
details: nil
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
guard let arguments = call.arguments as? [String: Any] else {
|
|
157
|
+
result(
|
|
158
|
+
FlutterError(
|
|
159
|
+
code: "invalid_arguments",
|
|
160
|
+
message: "Missing scroll payload.",
|
|
161
|
+
details: nil
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
let deltaX = (arguments["deltaX"] as? NSNumber)?.int32Value ?? 0
|
|
167
|
+
let deltaY = (arguments["deltaY"] as? NSNumber)?.int32Value ?? 0
|
|
168
|
+
let displayId = arguments["displayId"] as? String
|
|
169
|
+
performScroll(
|
|
170
|
+
deltaX: deltaX,
|
|
171
|
+
deltaY: deltaY,
|
|
172
|
+
displayId: displayId
|
|
173
|
+
)
|
|
174
|
+
result(nil)
|
|
175
|
+
case "typeText":
|
|
176
|
+
guard isAccessibilityTrusted() else {
|
|
177
|
+
result(
|
|
178
|
+
FlutterError(
|
|
179
|
+
code: "accessibility_permission_denied",
|
|
180
|
+
message: "Accessibility permission is required for input control.",
|
|
181
|
+
details: nil
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
guard let arguments = call.arguments as? [String: Any] else {
|
|
187
|
+
result(
|
|
188
|
+
FlutterError(
|
|
189
|
+
code: "invalid_arguments",
|
|
190
|
+
message: "Missing text payload.",
|
|
191
|
+
details: nil
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
let text = (arguments["text"] as? String) ?? ""
|
|
197
|
+
let pressEnter = (arguments["pressEnter"] as? Bool) ?? false
|
|
198
|
+
typeText(text, pressEnter: pressEnter)
|
|
199
|
+
result(nil)
|
|
200
|
+
case "pressKey":
|
|
201
|
+
guard isAccessibilityTrusted() else {
|
|
202
|
+
result(
|
|
203
|
+
FlutterError(
|
|
204
|
+
code: "accessibility_permission_denied",
|
|
205
|
+
message: "Accessibility permission is required for input control.",
|
|
206
|
+
details: nil
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
guard let arguments = call.arguments as? [String: Any],
|
|
212
|
+
let key = arguments["key"] as? String else {
|
|
213
|
+
result(
|
|
214
|
+
FlutterError(
|
|
215
|
+
code: "invalid_arguments",
|
|
216
|
+
message: "Missing key payload.",
|
|
217
|
+
details: nil
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
do {
|
|
223
|
+
try pressKey(key)
|
|
224
|
+
result(nil)
|
|
225
|
+
} catch {
|
|
226
|
+
result(
|
|
227
|
+
FlutterError(
|
|
228
|
+
code: "unsupported_key",
|
|
229
|
+
message: "Key is not supported on macOS.",
|
|
230
|
+
details: "\(error)"
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
default:
|
|
235
|
+
result(FlutterMethodNotImplemented)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private func buildDesktopStatus() -> [String: Any] {
|
|
240
|
+
let accessibilityTrusted = isAccessibilityTrusted()
|
|
241
|
+
var status: [String: Any] = [
|
|
242
|
+
"permissions": [
|
|
243
|
+
"screenCapture": preflightScreenCapturePermission() ? "available" : "required",
|
|
244
|
+
"inputControl": accessibilityTrusted ? "available" : "required",
|
|
245
|
+
"accessibility": accessibilityTrusted ? "available" : "required",
|
|
246
|
+
],
|
|
247
|
+
"displays": desktopDisplays(),
|
|
248
|
+
"activeDisplayId": defaultDisplayIdentifier(),
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
if let appName = NSWorkspace.shared.frontmostApplication?.localizedName {
|
|
252
|
+
status["frontmostApp"] = appName
|
|
253
|
+
}
|
|
254
|
+
if let windowTitle = frontmostWindowTitle() {
|
|
255
|
+
status["frontmostWindowTitle"] = windowTitle
|
|
256
|
+
}
|
|
257
|
+
return status
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private func desktopDisplays() -> [[String: Any]] {
|
|
261
|
+
NSScreen.screens.compactMap { screen in
|
|
262
|
+
guard let number = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber else {
|
|
263
|
+
return nil
|
|
264
|
+
}
|
|
265
|
+
let frame = screen.frame
|
|
266
|
+
let displayId = CGDirectDisplayID(number.uint32Value)
|
|
267
|
+
return [
|
|
268
|
+
"id": String(displayId),
|
|
269
|
+
"label": screen.localizedName,
|
|
270
|
+
"x": Int(frame.origin.x),
|
|
271
|
+
"y": Int(frame.origin.y),
|
|
272
|
+
"width": Int(frame.width),
|
|
273
|
+
"height": Int(frame.height),
|
|
274
|
+
"scaleFactor": screen.backingScaleFactor,
|
|
275
|
+
"primary": screen == NSScreen.main,
|
|
276
|
+
]
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private func captureFrame(displayId: String?) throws -> [String: Any] {
|
|
281
|
+
let resolvedDisplayId = resolveDisplayId(displayId)
|
|
282
|
+
guard let image = CGDisplayCreateImage(resolvedDisplayId) else {
|
|
283
|
+
throw NSError(domain: "NeoAgentDesktop", code: 1)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
let rep = NSBitmapImageRep(cgImage: image)
|
|
287
|
+
guard let data = rep.representation(using: .png, properties: [:]) else {
|
|
288
|
+
throw NSError(domain: "NeoAgentDesktop", code: 2)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return [
|
|
292
|
+
"bytes": FlutterStandardTypedData(bytes: data),
|
|
293
|
+
"mimeType": "image/png",
|
|
294
|
+
"width": image.width,
|
|
295
|
+
"height": image.height,
|
|
296
|
+
"displayId": String(resolvedDisplayId),
|
|
297
|
+
"displays": desktopDisplays(),
|
|
298
|
+
"capturedAt": ISO8601DateFormatter().string(from: Date()),
|
|
299
|
+
"frontmostApp": NSWorkspace.shared.frontmostApplication?.localizedName ?? "",
|
|
300
|
+
"frontmostWindowTitle": frontmostWindowTitle() ?? "",
|
|
301
|
+
]
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private func preflightScreenCapturePermission() -> Bool {
|
|
305
|
+
CGPreflightScreenCaptureAccess()
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private func isAccessibilityTrusted() -> Bool {
|
|
309
|
+
AXIsProcessTrusted()
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private func resolveDisplayId(_ raw: String?) -> CGDirectDisplayID {
|
|
313
|
+
if let raw,
|
|
314
|
+
let trimmed = optionalTrimmed(raw),
|
|
315
|
+
let screen = screenForDisplayId(trimmed),
|
|
316
|
+
let number = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber {
|
|
317
|
+
return CGDirectDisplayID(number.uint32Value)
|
|
318
|
+
}
|
|
319
|
+
if let main = NSScreen.main,
|
|
320
|
+
let number = main.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber {
|
|
321
|
+
return CGDirectDisplayID(number.uint32Value)
|
|
322
|
+
}
|
|
323
|
+
return CGMainDisplayID()
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private func screenForDisplayId(_ raw: String?) -> NSScreen? {
|
|
327
|
+
guard let trimmed = optionalTrimmed(raw),
|
|
328
|
+
let value = UInt32(trimmed) else {
|
|
329
|
+
return nil
|
|
330
|
+
}
|
|
331
|
+
return NSScreen.screens.first { screen in
|
|
332
|
+
guard let number = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber else {
|
|
333
|
+
return false
|
|
334
|
+
}
|
|
335
|
+
return number.uint32Value == value
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
private func optionalTrimmed(_ value: String?) -> String? {
|
|
340
|
+
let trimmed = value?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
341
|
+
return trimmed.isEmpty ? nil : trimmed
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private func defaultDisplayIdentifier() -> String {
|
|
345
|
+
String(resolveDisplayId(nil))
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private func frontmostWindowTitle() -> String? {
|
|
349
|
+
guard let app = NSWorkspace.shared.frontmostApplication else {
|
|
350
|
+
return nil
|
|
351
|
+
}
|
|
352
|
+
let appElement = AXUIElementCreateApplication(app.processIdentifier)
|
|
353
|
+
var focusedWindow: CFTypeRef?
|
|
354
|
+
let focusedStatus = AXUIElementCopyAttributeValue(
|
|
355
|
+
appElement,
|
|
356
|
+
kAXFocusedWindowAttribute as CFString,
|
|
357
|
+
&focusedWindow
|
|
358
|
+
)
|
|
359
|
+
guard focusedStatus == .success, let focusedWindow else {
|
|
360
|
+
return nil
|
|
361
|
+
}
|
|
362
|
+
let windowElement = unsafeBitCast(focusedWindow, to: AXUIElement.self)
|
|
363
|
+
|
|
364
|
+
var titleValue: CFTypeRef?
|
|
365
|
+
let titleStatus = AXUIElementCopyAttributeValue(
|
|
366
|
+
windowElement,
|
|
367
|
+
kAXTitleAttribute as CFString,
|
|
368
|
+
&titleValue
|
|
369
|
+
)
|
|
370
|
+
guard titleStatus == .success, let title = titleValue as? String, !title.isEmpty else {
|
|
371
|
+
return nil
|
|
372
|
+
}
|
|
373
|
+
return title
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private func performClick(x: Double, y: Double, button: String, displayId: String?) {
|
|
377
|
+
let point = nativePointForCapturedPixel(x: x, y: y, displayId: displayId)
|
|
378
|
+
let mouseButton = cgMouseButton(button)
|
|
379
|
+
guard let down = CGEvent(
|
|
380
|
+
mouseEventSource: nil,
|
|
381
|
+
mouseType: cgMouseDownType(button),
|
|
382
|
+
mouseCursorPosition: point,
|
|
383
|
+
mouseButton: mouseButton
|
|
384
|
+
),
|
|
385
|
+
let up = CGEvent(
|
|
386
|
+
mouseEventSource: nil,
|
|
387
|
+
mouseType: cgMouseUpType(button),
|
|
388
|
+
mouseCursorPosition: point,
|
|
389
|
+
mouseButton: mouseButton
|
|
390
|
+
) else {
|
|
391
|
+
return
|
|
392
|
+
}
|
|
393
|
+
CGWarpMouseCursorPosition(point)
|
|
394
|
+
down.post(tap: .cghidEventTap)
|
|
395
|
+
usleep(12000)
|
|
396
|
+
up.post(tap: .cghidEventTap)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
private func performDrag(
|
|
400
|
+
x1: Double,
|
|
401
|
+
y1: Double,
|
|
402
|
+
x2: Double,
|
|
403
|
+
y2: Double,
|
|
404
|
+
durationMs: Int,
|
|
405
|
+
displayId: String?
|
|
406
|
+
) {
|
|
407
|
+
let start = nativePointForCapturedPixel(x: x1, y: y1, displayId: displayId)
|
|
408
|
+
let end = nativePointForCapturedPixel(x: x2, y: y2, displayId: displayId)
|
|
409
|
+
let steps = Swift.max(4, Swift.min(90, durationMs / 16))
|
|
410
|
+
|
|
411
|
+
CGWarpMouseCursorPosition(start)
|
|
412
|
+
guard let down = CGEvent(
|
|
413
|
+
mouseEventSource: nil,
|
|
414
|
+
mouseType: .leftMouseDown,
|
|
415
|
+
mouseCursorPosition: start,
|
|
416
|
+
mouseButton: .left
|
|
417
|
+
) else {
|
|
418
|
+
return
|
|
419
|
+
}
|
|
420
|
+
down.post(tap: .cghidEventTap)
|
|
421
|
+
|
|
422
|
+
for step in 1...steps {
|
|
423
|
+
let t = Double(step) / Double(steps)
|
|
424
|
+
let point = CGPoint(
|
|
425
|
+
x: start.x + ((end.x - start.x) * t),
|
|
426
|
+
y: start.y + ((end.y - start.y) * t)
|
|
427
|
+
)
|
|
428
|
+
if let dragged = CGEvent(
|
|
429
|
+
mouseEventSource: nil,
|
|
430
|
+
mouseType: .leftMouseDragged,
|
|
431
|
+
mouseCursorPosition: point,
|
|
432
|
+
mouseButton: .left
|
|
433
|
+
) {
|
|
434
|
+
dragged.post(tap: .cghidEventTap)
|
|
435
|
+
}
|
|
436
|
+
usleep(useconds_t(Swift.max(1, (durationMs * 1000) / Swift.max(1, steps))))
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if let up = CGEvent(
|
|
440
|
+
mouseEventSource: nil,
|
|
441
|
+
mouseType: .leftMouseUp,
|
|
442
|
+
mouseCursorPosition: end,
|
|
443
|
+
mouseButton: .left
|
|
444
|
+
) {
|
|
445
|
+
up.post(tap: .cghidEventTap)
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private func performScroll(deltaX: Int32, deltaY: Int32, displayId: String?) {
|
|
450
|
+
guard let event = CGEvent(
|
|
451
|
+
scrollWheelEvent2Source: nil,
|
|
452
|
+
units: .pixel,
|
|
453
|
+
wheelCount: 2,
|
|
454
|
+
wheel1: -deltaY,
|
|
455
|
+
wheel2: deltaX,
|
|
456
|
+
wheel3: 0
|
|
457
|
+
) else {
|
|
458
|
+
return
|
|
459
|
+
}
|
|
460
|
+
if displayId != nil {
|
|
461
|
+
event.location = anchorPointForDisplay(displayId)
|
|
462
|
+
}
|
|
463
|
+
event.post(tap: .cghidEventTap)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
private func typeText(_ text: String, pressEnter: Bool) {
|
|
467
|
+
for scalar in text.unicodeScalars {
|
|
468
|
+
var unicode = UInt16(scalar.value)
|
|
469
|
+
if let down = CGEvent(keyboardEventSource: nil, virtualKey: 0, keyDown: true),
|
|
470
|
+
let up = CGEvent(keyboardEventSource: nil, virtualKey: 0, keyDown: false) {
|
|
471
|
+
down.keyboardSetUnicodeString(stringLength: 1, unicodeString: &unicode)
|
|
472
|
+
up.keyboardSetUnicodeString(stringLength: 1, unicodeString: &unicode)
|
|
473
|
+
down.post(tap: .cghidEventTap)
|
|
474
|
+
up.post(tap: .cghidEventTap)
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if pressEnter {
|
|
478
|
+
postVirtualKey(36)
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private func pressKey(_ key: String) throws {
|
|
483
|
+
let normalized = key.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
|
484
|
+
let keyCode: CGKeyCode
|
|
485
|
+
switch normalized {
|
|
486
|
+
case "enter", "return":
|
|
487
|
+
keyCode = 36
|
|
488
|
+
case "tab":
|
|
489
|
+
keyCode = 48
|
|
490
|
+
case "space":
|
|
491
|
+
keyCode = 49
|
|
492
|
+
case "backspace", "delete":
|
|
493
|
+
keyCode = 51
|
|
494
|
+
case "escape", "esc":
|
|
495
|
+
keyCode = 53
|
|
496
|
+
case "left":
|
|
497
|
+
keyCode = 123
|
|
498
|
+
case "right":
|
|
499
|
+
keyCode = 124
|
|
500
|
+
case "down":
|
|
501
|
+
keyCode = 125
|
|
502
|
+
case "up":
|
|
503
|
+
keyCode = 126
|
|
504
|
+
default:
|
|
505
|
+
throw NSError(domain: "NeoAgentDesktop", code: 3)
|
|
506
|
+
}
|
|
507
|
+
postVirtualKey(keyCode)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
private func postVirtualKey(_ keyCode: CGKeyCode) {
|
|
511
|
+
let down = CGEvent(keyboardEventSource: nil, virtualKey: keyCode, keyDown: true)
|
|
512
|
+
let up = CGEvent(keyboardEventSource: nil, virtualKey: keyCode, keyDown: false)
|
|
513
|
+
down?.post(tap: .cghidEventTap)
|
|
514
|
+
up?.post(tap: .cghidEventTap)
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
private func cgMouseButton(_ button: String) -> CGMouseButton {
|
|
518
|
+
switch button.lowercased() {
|
|
519
|
+
case "right":
|
|
520
|
+
return .right
|
|
521
|
+
case "middle":
|
|
522
|
+
return .center
|
|
523
|
+
default:
|
|
524
|
+
return .left
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
private func cgMouseDownType(_ button: String) -> CGEventType {
|
|
529
|
+
switch button.lowercased() {
|
|
530
|
+
case "right":
|
|
531
|
+
return .rightMouseDown
|
|
532
|
+
case "middle":
|
|
533
|
+
return .otherMouseDown
|
|
534
|
+
default:
|
|
535
|
+
return .leftMouseDown
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
private func cgMouseUpType(_ button: String) -> CGEventType {
|
|
540
|
+
switch button.lowercased() {
|
|
541
|
+
case "right":
|
|
542
|
+
return .rightMouseUp
|
|
543
|
+
case "middle":
|
|
544
|
+
return .otherMouseUp
|
|
545
|
+
default:
|
|
546
|
+
return .leftMouseUp
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
private func nativePointForCapturedPixel(x: Double, y: Double, displayId: String?) -> CGPoint {
|
|
551
|
+
let resolvedDisplayId = resolveDisplayId(displayId)
|
|
552
|
+
let displayBounds = CGDisplayBounds(resolvedDisplayId)
|
|
553
|
+
let imageWidth = Double(CGDisplayPixelsWide(resolvedDisplayId))
|
|
554
|
+
let imageHeight = Double(CGDisplayPixelsHigh(resolvedDisplayId))
|
|
555
|
+
|
|
556
|
+
guard imageWidth > 0, imageHeight > 0 else {
|
|
557
|
+
return CGPoint(x: x, y: y)
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
let normalizedX = min(max(x / imageWidth, 0), 1)
|
|
561
|
+
let normalizedY = min(max(y / imageHeight, 0), 1)
|
|
562
|
+
return CGPoint(
|
|
563
|
+
x: displayBounds.origin.x + (displayBounds.width * normalizedX),
|
|
564
|
+
y: displayBounds.origin.y + (displayBounds.height * normalizedY)
|
|
565
|
+
)
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
private func anchorPointForDisplay(_ displayId: String?) -> CGPoint {
|
|
569
|
+
let resolvedDisplayId = resolveDisplayId(displayId)
|
|
570
|
+
let displayBounds = CGDisplayBounds(resolvedDisplayId)
|
|
571
|
+
return CGPoint(
|
|
572
|
+
x: displayBounds.midX,
|
|
573
|
+
y: displayBounds.midY
|
|
574
|
+
)
|
|
575
|
+
}
|
|
576
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images" : [
|
|
3
|
+
{
|
|
4
|
+
"size" : "16x16",
|
|
5
|
+
"idiom" : "mac",
|
|
6
|
+
"filename" : "app_icon_16.png",
|
|
7
|
+
"scale" : "1x"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"size" : "16x16",
|
|
11
|
+
"idiom" : "mac",
|
|
12
|
+
"filename" : "app_icon_32.png",
|
|
13
|
+
"scale" : "2x"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"size" : "32x32",
|
|
17
|
+
"idiom" : "mac",
|
|
18
|
+
"filename" : "app_icon_32.png",
|
|
19
|
+
"scale" : "1x"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"size" : "32x32",
|
|
23
|
+
"idiom" : "mac",
|
|
24
|
+
"filename" : "app_icon_64.png",
|
|
25
|
+
"scale" : "2x"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"size" : "128x128",
|
|
29
|
+
"idiom" : "mac",
|
|
30
|
+
"filename" : "app_icon_128.png",
|
|
31
|
+
"scale" : "1x"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"size" : "128x128",
|
|
35
|
+
"idiom" : "mac",
|
|
36
|
+
"filename" : "app_icon_256.png",
|
|
37
|
+
"scale" : "2x"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"size" : "256x256",
|
|
41
|
+
"idiom" : "mac",
|
|
42
|
+
"filename" : "app_icon_256.png",
|
|
43
|
+
"scale" : "1x"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"size" : "256x256",
|
|
47
|
+
"idiom" : "mac",
|
|
48
|
+
"filename" : "app_icon_512.png",
|
|
49
|
+
"scale" : "2x"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"size" : "512x512",
|
|
53
|
+
"idiom" : "mac",
|
|
54
|
+
"filename" : "app_icon_512.png",
|
|
55
|
+
"scale" : "1x"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"size" : "512x512",
|
|
59
|
+
"idiom" : "mac",
|
|
60
|
+
"filename" : "app_icon_1024.png",
|
|
61
|
+
"scale" : "2x"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"info" : {
|
|
65
|
+
"version" : 1,
|
|
66
|
+
"author" : "xcode"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|