From fed500a2127c6ff2f6d43b0b08726488d303b3be Mon Sep 17 00:00:00 2001 From: Brian Date: Wed, 14 May 2025 03:24:09 +0700 Subject: [PATCH] feat: open privacy and security for macOs --- CHANGELOG.md | 4 + example/lib/main.dart | 26 +++++- example/macos/Podfile.lock | 8 +- .../macos/Runner.xcodeproj/project.pbxproj | 42 +++++----- .../xcshareddata/xcschemes/Runner.xcscheme | 20 ++++- example/macos/Runner/AppDelegate.swift | 6 +- example/pubspec.lock | 76 +++++++++--------- lib/app_settings_method_channel.dart | 7 ++ lib/app_settings_platform_interface.dart | 6 ++ lib/src/app_settings.dart | 4 + lib/src/app_settings_type.dart | 68 +++++++++++++++- macos/Classes/AppSettingsPlugin.swift | 22 +++++ macos/Classes/PrivacyType.swift | 40 ++++++++++ .../app_settings/AppSettingsEnums.swift | 5 ++ .../app_settings/AppSettingsPlugin.swift | 80 ++++++++++++++++--- .../Sources/app_settings/PrivacyType.swift | 40 ++++++++++ 16 files changed, 370 insertions(+), 84 deletions(-) create mode 100644 macos/Classes/PrivacyType.swift create mode 100644 macos/app_settings/Sources/app_settings/AppSettingsEnums.swift create mode 100644 macos/app_settings/Sources/app_settings/PrivacyType.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index f01f68d..2599d03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.2.0 +- Added openPrivacySession() to access platform specific 'Privacy and Security' settings (Only MacOS) . + + ## 6.1.0 - Added openAppSettings() to access platform specific 'camera' settings (Only MacOS) . diff --git a/example/lib/main.dart b/example/lib/main.dart index edad661..2da03df 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -102,13 +102,18 @@ class _MyAppState extends State { title: const Text("Subscriptions"), onTap: () => AppSettings.openAppSettings(type: AppSettingsType.subscriptions, asAnotherTask: true), ), - ListTile( - title: const Text("Camera"), - onTap: () => AppSettings.openAppSettings(type: AppSettingsType.camera, asAnotherTask: true), - ), ]; } + List getOpenPrivacyActions() { + return PrivacyType.values.map((type) { + return ListTile( + title: Text(type.name), + onTap: () => AppSettings.openPrivateSecurity(type), + ); + }).toList(); + } + List getOpenAppSettingsPanelActions() { return [ ListTile( @@ -135,6 +140,7 @@ class _MyAppState extends State { Widget build(BuildContext context) { final appSettingsActions = getOpenAppSettingsActions(); final appSettingsPanelActions = getOpenAppSettingsPanelActions(); + final getPrivacyActions = getOpenPrivacyActions(); return MaterialApp( home: Scaffold( @@ -155,6 +161,18 @@ class _MyAppState extends State { SliverList( delegate: SliverChildListDelegate.fixed(appSettingsActions), ), + const SliverToBoxAdapter( + child: Padding( + padding: EdgeInsets.all(8.0), + child: Text( + 'getPrivacyActions() options', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + ), + ), + SliverList( + delegate: SliverChildListDelegate.fixed(getPrivacyActions), + ), const SliverToBoxAdapter( child: Padding( padding: EdgeInsets.all(8.0), diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index a30c3ed..2c0f961 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -1,22 +1,16 @@ PODS: - - app_settings (5.1.1): - - FlutterMacOS - FlutterMacOS (1.0.0) DEPENDENCIES: - - app_settings (from `Flutter/ephemeral/.symlinks/plugins/app_settings/macos`) - FlutterMacOS (from `Flutter/ephemeral`) EXTERNAL SOURCES: - app_settings: - :path: Flutter/ephemeral/.symlinks/plugins/app_settings/macos FlutterMacOS: :path: Flutter/ephemeral SPEC CHECKSUMS: - app_settings: 5947582424ca323cb28382631e7df2ec99a33992 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 -COCOAPODS: 1.14.2 +COCOAPODS: 1.16.2 diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index 24a9ddb..11d43f5 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; FB26C8F1414D8D7D26B18C73 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 050E7E66FF82AE26A1D6D4D1 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ @@ -80,6 +81,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, FB26C8F1414D8D7D26B18C73 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -184,7 +186,6 @@ 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 0C99BD244592654D1B94E509 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -192,6 +193,9 @@ 33CC11202044C79F0003C045 /* PBXTargetDependency */, ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 33CC10ED2044A3C60003C045 /* app_settings_example.app */; productType = "com.apple.product-type.application"; @@ -203,7 +207,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -231,6 +235,9 @@ Base, ); mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -254,23 +261,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0C99BD244592654D1B94E509 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 166114DE890DE63AD111B9AA /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -628,6 +618,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index b5d80a1..dafd5a3 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/example/pubspec.lock b/example/pubspec.lock index ac98aaf..edc522d 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,55 +7,55 @@ packages: path: ".." relative: true source: path - version: "5.2.0" + version: "6.1.0" async: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.19.0" fake_async: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -78,18 +78,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -110,10 +110,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -126,18 +126,18 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" path: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" plugin_platform_interface: dependency: transitive description: @@ -155,50 +155,50 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.12.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.3.0" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.3" vector_math: dependency: transitive description: @@ -211,10 +211,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.3.0" sdks: - dart: ">=3.7.0-0 <4.0.0" + dart: ">=3.4.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/lib/app_settings_method_channel.dart b/lib/app_settings_method_channel.dart index ab2487e..7e2b9a5 100644 --- a/lib/app_settings_method_channel.dart +++ b/lib/app_settings_method_channel.dart @@ -27,4 +27,11 @@ class MethodChannelAppSettings extends AppSettingsPlatform { 'type': type.name, }); } + + @override + Future openPrivacySecuritySession(PrivacyType type) { + return methodChannel.invokeMethod('openPrivacySecuritySession', { + 'type': type.getRawValue(), + }); + } } diff --git a/lib/app_settings_platform_interface.dart b/lib/app_settings_platform_interface.dart index e6088c7..aba106a 100644 --- a/lib/app_settings_platform_interface.dart +++ b/lib/app_settings_platform_interface.dart @@ -35,4 +35,10 @@ abstract class AppSettingsPlatform extends PlatformInterface { Future openAppSettingsPanel(AppSettingsPanelType type) { throw UnimplementedError('openAppSettingsPanel() has not yet been implemented.'); } + + Future openPrivacySecuritySession(PrivacyType type) { + throw UnimplementedError('openPrivateSecuritySession() has not yet been implemented.'); + } + + } diff --git a/lib/src/app_settings.dart b/lib/src/app_settings.dart index eeb9017..cb48bac 100644 --- a/lib/src/app_settings.dart +++ b/lib/src/app_settings.dart @@ -22,4 +22,8 @@ class AppSettings { static Future openAppSettingsPanel(AppSettingsPanelType type) { return AppSettingsPlatform.instance.openAppSettingsPanel(type); } + + static Future openPrivateSecurity(PrivacyType type) { + return AppSettingsPlatform.instance.openPrivacySecuritySession(type); + } } diff --git a/lib/src/app_settings_type.dart b/lib/src/app_settings_type.dart index 0b5a3d4..e89b9f3 100644 --- a/lib/src/app_settings_type.dart +++ b/lib/src/app_settings_type.dart @@ -126,7 +126,71 @@ enum AppSettingsType { /// Only supported on Android. wireless, - /// Only supported on MacOS - camera, } +enum PrivacyType { + location, + contacts, + calendar, + reminders, + photos, + bluetooth, + microphone, + camera, + motion, + homeKit, + speechRecognition, + mediaAndAppleMusic, + filesAndFolders, + fullDiskAccess, + focus, + accessibility, + inputMonitoring, + screenRecording, + passkeysAccess, + automation, + appManagement, + developerTools, + sensitiveContentWarning, + analyticsAndImprovements, + appleAdvertising, + fileVault, + lockdownMode, + advanced, + extensions, + profiles, + legacyProfiles; + + String getRawValue() { + return { + PrivacyType.location: "Privacy_LocationServices", + PrivacyType.contacts: "Privacy_Contacts", + PrivacyType.calendar: "Privacy_Calendars", + PrivacyType.reminders: "Privacy_Reminders", + PrivacyType.photos: "Privacy_Photos", + PrivacyType.bluetooth: "Privacy_Bluetooth", + PrivacyType.microphone: "Privacy_Microphone", + PrivacyType.camera: "Privacy_Camera", + PrivacyType.motion: "Privacy_Motion", + PrivacyType.homeKit: "Privacy_HomeKit", + PrivacyType.speechRecognition: "Privacy_SpeechRecognition", + PrivacyType.mediaAndAppleMusic: "Privacy_Media", + PrivacyType.filesAndFolders: "Privacy_FilesAndFolders", + PrivacyType.fullDiskAccess: "Privacy_AllFiles", + PrivacyType.focus: "Privacy_Focus", + PrivacyType.accessibility: "Privacy_Accessibility", + PrivacyType.inputMonitoring: "Privacy_ListenEvent", + PrivacyType.screenRecording: "Privacy_ScreenCapture", + PrivacyType.passkeysAccess: "Privacy_PasskeyAccess", + PrivacyType.automation: "Privacy_Automation", + PrivacyType.appManagement: "Privacy_AppBundles", + PrivacyType.developerTools: "Privacy_DevTools", + PrivacyType.sensitiveContentWarning: "Privacy_NudityDetection", + PrivacyType.analyticsAndImprovements: "Privacy_Analytics", + PrivacyType.appleAdvertising: "Privacy_Advertising", + PrivacyType.fileVault: "FileVault", + PrivacyType.lockdownMode: "LockdownMode", + PrivacyType.advanced: "Advanced", + }[this]!; + } +} \ No newline at end of file diff --git a/macos/Classes/AppSettingsPlugin.swift b/macos/Classes/AppSettingsPlugin.swift index def0045..e580bba 100644 --- a/macos/Classes/AppSettingsPlugin.swift +++ b/macos/Classes/AppSettingsPlugin.swift @@ -15,6 +15,9 @@ public class AppSettingsPlugin: NSObject, FlutterPlugin { case "openSettings": handleOpenSettings(call: call, result: result) break + case "openPrivacySecuritySession": + handlePrivateSecuritySession(call: call, result: result) + break default: result(FlutterMethodNotImplemented) break @@ -43,6 +46,25 @@ public class AppSettingsPlugin: NSObject, FlutterPlugin { } } + private func handlePrivateSecuritySession(call: FlutterMethodCall, result: @escaping FlutterResult) { + let arguments = call.arguments as! Dictionary + let type = arguments["type"] as! String + let privacyType = PrivacyType(rawValue: type) + print("Opening settings for privacy type: \(type) \(privacyType)") + if let privacyType = privacyType { + // Open the specific privacy settings URL. + print("Opening settings for privacy type: \(privacyType)") + openSettings(settingsUrl: privacyType.url()) + } + else{ + // Show the default settings as fallback. +// openSettings(settingsUrl: UIApplication.openSettingsURLString) + print("Opening ") + result(nil) + } + } + + private func openSettings(settingsUrl: String) { guard let url = URL(string: settingsUrl) else { return diff --git a/macos/Classes/PrivacyType.swift b/macos/Classes/PrivacyType.swift new file mode 100644 index 0000000..b0df17e --- /dev/null +++ b/macos/Classes/PrivacyType.swift @@ -0,0 +1,40 @@ +enum PrivacyType: String { + case location = "Privacy_LocationServices" + case contacts = "Privacy_Contacts" + case calendar = "Privacy_Calendars" + case reminders = "Privacy_Reminders" + case photos = "Privacy_Photos" + case bluetooth = "Privacy_Bluetooth" + case microphone = "Privacy_Microphone" + case camera = "Privacy_Camera" + case motion = "Privacy_Motion" + case homeKit = "Privacy_HomeKit" + case speechRecognition = "Privacy_SpeechRecognition" + case mediaAndAppleMusic = "Privacy_Media" + case filesAndFolders = "Privacy_FilesAndFolders" + case fullDiskAccess = "Privacy_AllFiles" + case focus = "Privacy_Focus" + case accessibility = "Privacy_Accessibility" + case inputMonitoring = "Privacy_ListenEvent" + case screenRecording = "Privacy_ScreenCapture" + case passkeysAccess = "Privacy_PasskeyAccess" + case automation = "Privacy_Automation" + case appManagement = "Privacy_AppBundles" + case developerTools = "Privacy_DevTools" + case sensitiveContentWarning = "Privacy_NudityDetection" + case analyticsAndImprovements = "Privacy_Analytics" + case appleAdvertising = "Privacy_Advertising" + case fileVault = "FileVault" + case lockdownMode = "LockdownMode" + case advanced = "Advanced" + case extensions = "com.apple.ExtensionsPreferences" + case profiles = "com.apple.Profiles-Settings.extension" + case legacyProfiles = "com.apple.preferences.configurationprofiles" + + func url() -> String { + if self == .extensions || self == .profiles || self == .legacyProfiles { + return "x-apple.systempreferences:\(self.rawValue)" + } + return "x-apple.systempreferences:com.apple.preference.security?\(self.rawValue)" + } +} \ No newline at end of file diff --git a/macos/app_settings/Sources/app_settings/AppSettingsEnums.swift b/macos/app_settings/Sources/app_settings/AppSettingsEnums.swift new file mode 100644 index 0000000..1de3686 --- /dev/null +++ b/macos/app_settings/Sources/app_settings/AppSettingsEnums.swift @@ -0,0 +1,5 @@ +enum SettingsGeneral: String { + case preference = "x-apple.systempreferences:com.apple.preference" + case notifications = "x-apple.systempreferences:com.apple.Notifications-Settings.extension" + case camera = "x-apple.systempreferences:com.apple.preference.security?Privacy_Camera" +} diff --git a/macos/app_settings/Sources/app_settings/AppSettingsPlugin.swift b/macos/app_settings/Sources/app_settings/AppSettingsPlugin.swift index e243070..e580bba 100644 --- a/macos/app_settings/Sources/app_settings/AppSettingsPlugin.swift +++ b/macos/app_settings/Sources/app_settings/AppSettingsPlugin.swift @@ -1,19 +1,75 @@ import Cocoa import FlutterMacOS +import AppKit +import StoreKit public class AppSettingsPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "app_settings", binaryMessenger: registrar.messenger) - let instance = AppSettingsPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "com.spencerccf.app_settings/methods", binaryMessenger: registrar.messenger) + + registrar.addMethodCallDelegate(AppSettingsPlugin(), channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch(call.method) { + case "openSettings": + handleOpenSettings(call: call, result: result) + break + case "openPrivacySecuritySession": + handlePrivateSecuritySession(call: call, result: result) + break + default: + result(FlutterMethodNotImplemented) + break + } + } + + /// Handle the 'openSettings' method call. + private func handleOpenSettings(call: FlutterMethodCall, result: @escaping FlutterResult) { + let arguments = call.arguments as! Dictionary + let type = arguments["type"] as! String + + switch(type) { + case "notification": + openSettings(settingsUrl: SettingsGeneral.notifications.rawValue) + result(nil) + break + case "camera": + openSettings(settingsUrl: SettingsGeneral.camera.rawValue) + result(nil) + break + default: + // Show the default settings as fallback. + openSettings(settingsUrl: SettingsGeneral.preference.rawValue) + result(nil) + break + } + } + + private func handlePrivateSecuritySession(call: FlutterMethodCall, result: @escaping FlutterResult) { + let arguments = call.arguments as! Dictionary + let type = arguments["type"] as! String + let privacyType = PrivacyType(rawValue: type) + print("Opening settings for privacy type: \(type) \(privacyType)") + if let privacyType = privacyType { + // Open the specific privacy settings URL. + print("Opening settings for privacy type: \(privacyType)") + openSettings(settingsUrl: privacyType.url()) + } + else{ + // Show the default settings as fallback. +// openSettings(settingsUrl: UIApplication.openSettingsURLString) + print("Opening ") + result(nil) + } + } + - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "getPlatformVersion": - result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString) - default: - result(FlutterMethodNotImplemented) + private func openSettings(settingsUrl: String) { + guard let url = URL(string: settingsUrl) else { + return + } + + NSWorkspace.shared.open(url) } - } } diff --git a/macos/app_settings/Sources/app_settings/PrivacyType.swift b/macos/app_settings/Sources/app_settings/PrivacyType.swift new file mode 100644 index 0000000..b0df17e --- /dev/null +++ b/macos/app_settings/Sources/app_settings/PrivacyType.swift @@ -0,0 +1,40 @@ +enum PrivacyType: String { + case location = "Privacy_LocationServices" + case contacts = "Privacy_Contacts" + case calendar = "Privacy_Calendars" + case reminders = "Privacy_Reminders" + case photos = "Privacy_Photos" + case bluetooth = "Privacy_Bluetooth" + case microphone = "Privacy_Microphone" + case camera = "Privacy_Camera" + case motion = "Privacy_Motion" + case homeKit = "Privacy_HomeKit" + case speechRecognition = "Privacy_SpeechRecognition" + case mediaAndAppleMusic = "Privacy_Media" + case filesAndFolders = "Privacy_FilesAndFolders" + case fullDiskAccess = "Privacy_AllFiles" + case focus = "Privacy_Focus" + case accessibility = "Privacy_Accessibility" + case inputMonitoring = "Privacy_ListenEvent" + case screenRecording = "Privacy_ScreenCapture" + case passkeysAccess = "Privacy_PasskeyAccess" + case automation = "Privacy_Automation" + case appManagement = "Privacy_AppBundles" + case developerTools = "Privacy_DevTools" + case sensitiveContentWarning = "Privacy_NudityDetection" + case analyticsAndImprovements = "Privacy_Analytics" + case appleAdvertising = "Privacy_Advertising" + case fileVault = "FileVault" + case lockdownMode = "LockdownMode" + case advanced = "Advanced" + case extensions = "com.apple.ExtensionsPreferences" + case profiles = "com.apple.Profiles-Settings.extension" + case legacyProfiles = "com.apple.preferences.configurationprofiles" + + func url() -> String { + if self == .extensions || self == .profiles || self == .legacyProfiles { + return "x-apple.systempreferences:\(self.rawValue)" + } + return "x-apple.systempreferences:com.apple.preference.security?\(self.rawValue)" + } +} \ No newline at end of file