From 415446e3bb39cb1508d0759ecce7cdf116f542d8 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 8 Dec 2024 21:24:06 +0800 Subject: [PATCH] :sparkles: Add iOS notification services :bug: Fix didn't request notification permission --- ios/Runner.xcodeproj/project.pbxproj | 215 ++++++++++++++++++ ios/Runner/Info.plist | 4 + ios/SolarNotifyService/Info.plist | 18 ++ .../NotificationService.swift | 143 ++++++++++++ lib/providers/notification.dart | 12 +- macos/Runner/DebugProfile.entitlements | 4 + macos/Runner/Info.plist | 2 + macos/Runner/Release.entitlements | 4 + pubspec.yaml | 2 +- 9 files changed, 402 insertions(+), 2 deletions(-) create mode 100644 ios/SolarNotifyService/Info.plist create mode 100644 ios/SolarNotifyService/NotificationService.swift diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index bc9cc71..d5776ee 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 73DA8A012D05C7620024A03E /* SolarNotifyService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73DA89FA2D05C7620024A03E /* SolarNotifyService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 8CD0929C27BC410DD5056EAB /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = A2C24C5238FAC44EA2CCF738 /* GoogleService-Info.plist */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -27,9 +28,27 @@ remoteGlobalIDString = 97C146ED1CF9000F007C117D; remoteInfo = Runner; }; + 73DA89FF2D05C7620024A03E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 73DA89F92D05C7620024A03E; + remoteInfo = SolarNotifyService; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 73DA8A022D05C7620024A03E /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 73DA8A012D05C7620024A03E /* SolarNotifyService.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -55,6 +74,7 @@ 64FBE78F9C282712818D6D95 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 72E9279EFA6DAC00BBAC493C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 73111C212CEE3D5E004CF4B3 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + 73DA89FA2D05C7620024A03E /* SolarNotifyService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SolarNotifyService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -70,7 +90,28 @@ EDF483E994343CDFBF9BA347 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 73DA8A062D05C7620024A03E /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 73DA89F92D05C7620024A03E /* SolarNotifyService */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 73DA89FB2D05C7620024A03E /* SolarNotifyService */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (73DA8A062D05C7620024A03E /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = SolarNotifyService; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ + 73DA89F72D05C7620024A03E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -123,6 +164,7 @@ children = ( 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, + 73DA89FB2D05C7620024A03E /* SolarNotifyService */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, F5165E3BD1F2519F85CD4BE2 /* Pods */, @@ -136,6 +178,7 @@ children = ( 97C146EE1CF9000F007C117D /* Runner.app */, 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + 73DA89FA2D05C7620024A03E /* SolarNotifyService.appex */, ); name = Products; sourceTree = ""; @@ -191,6 +234,28 @@ productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 73DA89F92D05C7620024A03E /* SolarNotifyService */ = { + isa = PBXNativeTarget; + buildConfigurationList = 73DA8A072D05C7620024A03E /* Build configuration list for PBXNativeTarget "SolarNotifyService" */; + buildPhases = ( + 73DA89F62D05C7620024A03E /* Sources */, + 73DA89F72D05C7620024A03E /* Frameworks */, + 73DA89F82D05C7620024A03E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 73DA89FB2D05C7620024A03E /* SolarNotifyService */, + ); + name = SolarNotifyService; + packageProductDependencies = ( + ); + productName = SolarNotifyService; + productReference = 73DA89FA2D05C7620024A03E /* SolarNotifyService.appex */; + productType = "com.apple.product-type.app-extension"; + }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; @@ -200,6 +265,7 @@ 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, + 73DA8A022D05C7620024A03E /* Embed Foundation Extensions */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, FC4815D44D909666EB1FA614 /* [CP] Embed Pods Frameworks */, @@ -209,6 +275,7 @@ buildRules = ( ); dependencies = ( + 73DA8A002D05C7620024A03E /* PBXTargetDependency */, ); name = Runner; productName = Runner; @@ -222,6 +289,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 1610; LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { @@ -229,6 +297,9 @@ CreatedOnToolsVersion = 14.0; TestTargetID = 97C146ED1CF9000F007C117D; }; + 73DA89F92D05C7620024A03E = { + CreatedOnToolsVersion = 16.1; + }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 1100; @@ -250,6 +321,7 @@ targets = ( 97C146ED1CF9000F007C117D /* Runner */, 331C8080294A63A400263BE5 /* RunnerTests */, + 73DA89F92D05C7620024A03E /* SolarNotifyService */, ); }; /* End PBXProject section */ @@ -262,6 +334,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 73DA89F82D05C7620024A03E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -415,6 +494,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 73DA89F62D05C7620024A03E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -432,6 +518,11 @@ target = 97C146ED1CF9000F007C117D /* Runner */; targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; }; + 73DA8A002D05C7620024A03E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 73DA89F92D05C7620024A03E /* SolarNotifyService */; + targetProxy = 73DA89FF2D05C7620024A03E /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -581,6 +672,120 @@ }; name = Profile; }; + 73DA8A032D05C7620024A03E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = W7HPZ53V6B; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = SolarNotifyService/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = SolarNotifyService; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolarNotifyService; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 73DA8A042D05C7620024A03E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = W7HPZ53V6B; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = SolarNotifyService/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = SolarNotifyService; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolarNotifyService; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 73DA8A052D05C7620024A03E /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = W7HPZ53V6B; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = SolarNotifyService/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = SolarNotifyService; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolarNotifyService; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Profile; + }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -756,6 +961,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 73DA8A072D05C7620024A03E /* Build configuration list for PBXNativeTarget "SolarNotifyService" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 73DA8A032D05C7620024A03E /* Debug */, + 73DA8A042D05C7620024A03E /* Release */, + 73DA8A052D05C7620024A03E /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index c860a2d..45b98b0 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -62,6 +62,10 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + NSUserActivityTypes + + INSendMessageIntent + UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait diff --git a/ios/SolarNotifyService/Info.plist b/ios/SolarNotifyService/Info.plist new file mode 100644 index 0000000..a9e56d5 --- /dev/null +++ b/ios/SolarNotifyService/Info.plist @@ -0,0 +1,18 @@ + + + + + NSUserActivityTypes + + INStartCallIntent + INSendMessageIntent + + NSExtension + + NSExtensionPointIdentifier + com.apple.usernotifications.service + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).NotificationService + + + diff --git a/ios/SolarNotifyService/NotificationService.swift b/ios/SolarNotifyService/NotificationService.swift new file mode 100644 index 0000000..a78c80e --- /dev/null +++ b/ios/SolarNotifyService/NotificationService.swift @@ -0,0 +1,143 @@ +// +// NotificationService.swift +// SolarNotifyService +// +// Created by LittleSheep on 2024/12/8. +// + +import UserNotifications +import Intents + +enum ParseNotificationPayloadError: Error { + case missingMetadata(String) + case missingAvatarUrl(String) +} + +class NotificationService: UNNotificationServiceExtension { + + private var contentHandler: ((UNNotificationContent) -> Void)? + private var bestAttemptContent: UNMutableNotificationContent? + private let serverBaseUrl = "https://api.sn.solsynth.dev" + + private func getAttachmentUrl(for identifier: String) -> String { + identifier.starts(with: "http") ? identifier : "\(serverBaseUrl)/cgi/uc/attachments/\(identifier)" + } + + override func didReceive( + _ request: UNNotificationRequest, + withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void + ) { + self.contentHandler = contentHandler + guard let bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent else { + contentHandler(request.content) + return + } + self.bestAttemptContent = bestAttemptContent + + do { + try processNotification(request: request, content: bestAttemptContent) + } catch { + contentHandler(bestAttemptContent) + } + } + + override func serviceExtensionTimeWillExpire() { + if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { + contentHandler(bestAttemptContent) + } + } + + private func processNotification(request: UNNotificationRequest, content: UNMutableNotificationContent) throws { + switch content.categoryIdentifier { + case "messaging.message", "messaging.callStart": + try handleMessagingNotification(request: request, content: content) + default: + try handleDefaultNotification(content: content) + } + } + + private func handleMessagingNotification(request: UNNotificationRequest, content: UNMutableNotificationContent) throws { + guard let metadata = content.userInfo["metadata"] as? [AnyHashable: Any] else { + throw ParseNotificationPayloadError.missingMetadata("The notification has no metadata.") + } + + guard var avatarUrl = metadata["avatar"] as? String else { + throw ParseNotificationPayloadError.missingAvatarUrl("The notification has no avatar.") + } + avatarUrl = getAttachmentUrl(for: avatarUrl) + + let handle = INPersonHandle(value: "\(metadata["user_id"] ?? "")", type: .unknown) + let avatar = INImage(url: URL(string: avatarUrl)!) + let sender = INPerson( + personHandle: handle, + nameComponents: nil, + displayName: content.title, + image: avatar, + contactIdentifier: nil, + customIdentifier: nil + ) + + if content.categoryIdentifier == "messaging.callStart" { + let intent = createCallIntent(with: sender) + donateInteraction(for: intent) + let updatedContent = try request.content.updating(from: intent) + contentHandler?(updatedContent) + } else { + let intent = createMessageIntent(with: sender, metadata: metadata, body: content.body) + donateInteraction(for: intent) + let updatedContent = try request.content.updating(from: intent) + contentHandler?(updatedContent) + } + } + + private func handleDefaultNotification(content: UNMutableNotificationContent) throws { + guard let metadata = content.userInfo["metadata"] as? [AnyHashable: Any] else { + throw ParseNotificationPayloadError.missingMetadata("The notification has no metadata.") + } + + if let imageIdentifier = metadata["image"] as? String { + attachMedia(to: content, withIdentifier: imageIdentifier) + } else if let avatarIdentifier = metadata["avatar"] as? String { + attachMedia(to: content, withIdentifier: avatarIdentifier) + } + + contentHandler?(content) + } + + private func attachMedia(to content: UNMutableNotificationContent, withIdentifier identifier: String) { + let attachmentUrl = getAttachmentUrl(for: identifier) + if let url = URL(string: attachmentUrl), let attachment = try? UNNotificationAttachment(identifier: identifier, url: url) { + content.attachments = [attachment] + } + } + + private func createCallIntent(with sender: INPerson) -> INStartCallIntent { + INStartCallIntent( + callRecordFilter: nil, + callRecordToCallBack: nil, + audioRoute: .unknown, + destinationType: .normal, + contacts: [sender], + callCapability: .unknown + ) + } + + private func createMessageIntent(with sender: INPerson, metadata: [AnyHashable: Any], body: String) -> INSendMessageIntent { + INSendMessageIntent( + recipients: nil, + outgoingMessageType: .outgoingMessageText, + content: body, + speakableGroupName: nil, + conversationIdentifier: "\(metadata["channel_id"] ?? "")", + serviceName: nil, + sender: sender, + attachments: nil + ) + } + + private func donateInteraction(for intent: INIntent) { + let interaction = INInteraction(intent: intent, response: nil) + interaction.direction = .incoming + interaction.donate(completion: nil) + } +} diff --git a/lib/providers/notification.dart b/lib/providers/notification.dart index d2b177c..0d966cc 100644 --- a/lib/providers/notification.dart +++ b/lib/providers/notification.dart @@ -27,9 +27,19 @@ class NotificationProvider extends ChangeNotifier { } Future registerPushNotifications() async { - if (kIsWeb) return; + if (kIsWeb || Platform.isWindows || Platform.isLinux) return; if (!_ua.isAuthorized) return; + await FirebaseMessaging.instance.requestPermission( + alert: true, + announcement: true, + badge: true, + carPlay: false, + criticalAlert: false, + provisional: false, + sound: true, + ); + late final String? token; late final String provider; var deviceUuid = await FlutterUdid.consistentUdid; diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index d8dadb4..31f36d7 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -12,6 +12,10 @@ com.apple.security.device.bluetooth + com.apple.security.device.camera + + com.apple.security.files.downloads.read-write + com.apple.security.files.user-selected.read-write com.apple.security.network.client diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist index 905acf9..fc14816 100644 --- a/macos/Runner/Info.plist +++ b/macos/Runner/Info.plist @@ -38,6 +38,8 @@ Grant access to Photo Library will allow Solian upload photo or video for your post. CFBundleDisplayName $(PRODUCT_NAME) + ITSAppUsesNonExemptEncryption + NSPhotoLibraryAddUsageDescription Grant access to Photo Library will allow Solian download photo to album for you. diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index c804233..762b4c9 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -10,6 +10,10 @@ com.apple.security.device.bluetooth + com.apple.security.device.camera + + com.apple.security.files.downloads.read-write + com.apple.security.files.user-selected.read-write com.apple.security.network.client diff --git a/pubspec.yaml b/pubspec.yaml index fd3c8bb..823c906 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 2.0.0+17 +version: 2.0.0+18 environment: sdk: ^3.5.4