diff --git a/Bark.xcodeproj/project.pbxproj b/Bark.xcodeproj/project.pbxproj index 538aa18..c953ce6 100644 --- a/Bark.xcodeproj/project.pbxproj +++ b/Bark.xcodeproj/project.pbxproj @@ -71,6 +71,9 @@ 065BE44B2563D8E1002A8CA4 /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065BE44A2563D8E1002A8CA4 /* Reusable.swift */; }; 065BE4502563D939002A8CA4 /* SoundCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065BE44F2563D939002A8CA4 /* SoundCellViewModel.swift */; }; 065BE4552565055F002A8CA4 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065BE4542565055F002A8CA4 /* HomeViewModel.swift */; }; + 0660480B2700550600938904 /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0660480A2700550600938904 /* Intents.framework */; }; + 0660480E2700550600938904 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0660480D2700550600938904 /* IntentHandler.swift */; }; + 066048122700550600938904 /* Intent.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 066048092700550600938904 /* Intent.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 0661A543204FDA4100965E4E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0661A542204FDA4100965E4E /* AppDelegate.swift */; }; 0661A545204FDA4100965E4E /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0661A544204FDA4100965E4E /* HomeViewController.swift */; }; 0661A54A204FDA4100965E4E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0661A549204FDA4100965E4E /* Assets.xcassets */; }; @@ -118,6 +121,13 @@ remoteGlobalIDString = 0632CE1D20EC9098003FDF46; remoteInfo = NotificationContentExtension; }; + 066048102700550600938904 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0661A537204FDA4100965E4E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 066048082700550600938904; + remoteInfo = Intent; + }; 06CF784521C7A50300A052D7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0661A537204FDA4100965E4E /* Project object */; @@ -142,6 +152,7 @@ dstSubfolderSpec = 13; files = ( 0632CE2A20EC9098003FDF46 /* NotificationContentExtension.appex in Embed App Extensions */, + 066048122700550600938904 /* Intent.appex in Embed App Extensions */, 06CF784721C7A50300A052D7 /* NotificationServiceExtension.appex in Embed App Extensions */, ); name = "Embed App Extensions"; @@ -213,6 +224,10 @@ 065BE44A2563D8E1002A8CA4 /* Reusable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; 065BE44F2563D939002A8CA4 /* SoundCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundCellViewModel.swift; sourceTree = ""; }; 065BE4542565055F002A8CA4 /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = ""; }; + 066048092700550600938904 /* Intent.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Intent.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 0660480A2700550600938904 /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; }; + 0660480D2700550600938904 /* IntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentHandler.swift; sourceTree = ""; }; + 0660480F2700550600938904 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0661A53F204FDA4100965E4E /* Bark.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Bark.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0661A542204FDA4100965E4E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 0661A544204FDA4100965E4E /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; }; @@ -273,6 +288,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 066048062700550600938904 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0660480B2700550600938904 /* Intents.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 0661A53C204FDA4100965E4E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -435,6 +458,15 @@ path = Moya; sourceTree = ""; }; + 0660480C2700550600938904 /* Intent */ = { + isa = PBXGroup; + children = ( + 0660480D2700550600938904 /* IntentHandler.swift */, + 0660480F2700550600938904 /* Info.plist */, + ); + path = Intent; + sourceTree = ""; + }; 0661A536204FDA4100965E4E = { isa = PBXGroup; children = ( @@ -447,6 +479,7 @@ 0632CE2120EC9098003FDF46 /* NotificationContentExtension */, 06CF784121C7A50300A052D7 /* NotificationServiceExtension */, 06EE1FD126843E9300586708 /* BarkTests */, + 0660480C2700550600938904 /* Intent */, 0661A540204FDA4100965E4E /* Products */, 9563D0DB71FE280909AB1C63 /* Pods */, 99BD309BDB7F62B5DC0CECE1 /* Frameworks */, @@ -460,6 +493,7 @@ 0632CE1E20EC9098003FDF46 /* NotificationContentExtension.appex */, 06CF784021C7A50300A052D7 /* NotificationServiceExtension.appex */, 06EE1FD026843E9300586708 /* BarkTests.xctest */, + 066048092700550600938904 /* Intent.appex */, ); name = Products; sourceTree = ""; @@ -516,6 +550,7 @@ 14F0F73EC45A35FC2482F967 /* Pods_NotificationContentExtension.framework */, E4EA0C04469BF7B418FF3CB7 /* libPods-Bark.a */, A24A28EB12CA35B953807C99 /* libPods-NotificationServiceExtension.a */, + 0660480A2700550600938904 /* Intents.framework */, ); name = Frameworks; sourceTree = ""; @@ -540,6 +575,23 @@ productReference = 0632CE1E20EC9098003FDF46 /* NotificationContentExtension.appex */; productType = "com.apple.product-type.app-extension"; }; + 066048082700550600938904 /* Intent */ = { + isa = PBXNativeTarget; + buildConfigurationList = 066048132700550600938904 /* Build configuration list for PBXNativeTarget "Intent" */; + buildPhases = ( + 066048052700550600938904 /* Sources */, + 066048062700550600938904 /* Frameworks */, + 066048072700550600938904 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Intent; + productName = Intent; + productReference = 066048092700550600938904 /* Intent.appex */; + productType = "com.apple.product-type.app-extension"; + }; 0661A53E204FDA4100965E4E /* Bark */ = { isa = PBXNativeTarget; buildConfigurationList = 0661A551204FDA4100965E4E /* Build configuration list for PBXNativeTarget "Bark" */; @@ -556,6 +608,7 @@ dependencies = ( 0632CE2920EC9098003FDF46 /* PBXTargetDependency */, 06CF784621C7A50300A052D7 /* PBXTargetDependency */, + 066048112700550600938904 /* PBXTargetDependency */, ); name = Bark; productName = Bark; @@ -604,7 +657,7 @@ 0661A537204FDA4100965E4E /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1250; + LastSwiftUpdateCheck = 1300; LastUpgradeCheck = 0930; ORGANIZATIONNAME = Fin; TargetAttributes = { @@ -612,6 +665,10 @@ CreatedOnToolsVersion = 9.3; ProvisioningStyle = Manual; }; + 066048082700550600938904 = { + CreatedOnToolsVersion = 13.0; + ProvisioningStyle = Automatic; + }; 0661A53E204FDA4100965E4E = { CreatedOnToolsVersion = 9.2; ProvisioningStyle = Manual; @@ -650,6 +707,7 @@ 0632CE1D20EC9098003FDF46 /* NotificationContentExtension */, 06CF783F21C7A50300A052D7 /* NotificationServiceExtension */, 06EE1FCF26843E9300586708 /* BarkTests */, + 066048082700550600938904 /* Intent */, ); }; /* End PBXProject section */ @@ -665,6 +723,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 066048072700550600938904 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 0661A53D204FDA4100965E4E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -799,6 +864,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 066048052700550600938904 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0660480E2700550600938904 /* IntentHandler.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 0661A53B204FDA4100965E4E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -883,6 +956,11 @@ target = 0632CE1D20EC9098003FDF46 /* NotificationContentExtension */; targetProxy = 0632CE2820EC9098003FDF46 /* PBXContainerItemProxy */; }; + 066048112700550600938904 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 066048082700550600938904 /* Intent */; + targetProxy = 066048102700550600938904 /* PBXContainerItemProxy */; + }; 06CF784621C7A50300A052D7 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 06CF783F21C7A50300A052D7 /* NotificationServiceExtension */; @@ -928,7 +1006,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; - CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5U8LBRXG3A; @@ -938,7 +1016,7 @@ MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = me.fin.bark.NotificationContent; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "match AppStore me.fin.bark.NotificationContent"; + PROVISIONING_PROFILE_SPECIFIER = "match Development me.fin.bark.NotificationContent"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; @@ -949,7 +1027,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; - CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5U8LBRXG3A; @@ -959,13 +1037,68 @@ MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = me.fin.bark.NotificationContent; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "match AppStore me.fin.bark.NotificationContent"; + PROVISIONING_PROFILE_SPECIFIER = "match Development me.fin.bark.NotificationContent"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; + 066048142700550600938904 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 5U8LBRXG3A; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Intent/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Intent; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Fin. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = me.fin.bark.Intent; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 066048152700550600938904 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 5U8LBRXG3A; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Intent/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Intent; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Fin. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = me.fin.bark.Intent; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 0661A54F204FDA4100965E4E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1086,7 +1219,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = Bark/Bark.entitlements; - CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5U8LBRXG3A; @@ -1096,7 +1229,7 @@ MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = me.fin.bark; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "match AppStore me.fin.bark"; + PROVISIONING_PROFILE_SPECIFIER = "match Development me.fin.bark"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; @@ -1109,7 +1242,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = Bark/Bark.entitlements; - CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5U8LBRXG3A; @@ -1119,7 +1252,7 @@ MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = me.fin.bark; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "match AppStore me.fin.bark"; + PROVISIONING_PROFILE_SPECIFIER = "match Development me.fin.bark"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; @@ -1131,7 +1264,7 @@ buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; - CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5U8LBRXG3A; @@ -1143,7 +1276,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = me.fin.bark.NotificationServiceExtension; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "match AppStore me.fin.bark.NotificationServiceExtension"; + PROVISIONING_PROFILE_SPECIFIER = "match Development me.fin.bark.NotificationServiceExtension"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1156,7 +1289,7 @@ buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; - CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5U8LBRXG3A; @@ -1167,7 +1300,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = me.fin.bark.NotificationServiceExtension; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "match AppStore me.fin.bark.NotificationServiceExtension"; + PROVISIONING_PROFILE_SPECIFIER = "match Development me.fin.bark.NotificationServiceExtension"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1235,6 +1368,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 066048132700550600938904 /* Build configuration list for PBXNativeTarget "Intent" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 066048142700550600938904 /* Debug */, + 066048152700550600938904 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 0661A53A204FDA4100965E4E /* Build configuration list for PBXProject "Bark" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Bark/Assets.xcassets/icon.imageset/Contents.json b/Bark/Assets.xcassets/icon.imageset/Contents.json new file mode 100644 index 0000000..a5682b9 --- /dev/null +++ b/Bark/Assets.xcassets/icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "IMG_1116 2.jpg", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Bark/Assets.xcassets/icon.imageset/IMG_1116 2.jpg b/Bark/Assets.xcassets/icon.imageset/IMG_1116 2.jpg new file mode 100644 index 0000000..a63445e Binary files /dev/null and b/Bark/Assets.xcassets/icon.imageset/IMG_1116 2.jpg differ diff --git a/Bark/en.lproj/Localizable.strings b/Bark/en.lproj/Localizable.strings index 1ed0e50..db88917 100644 --- a/Bark/en.lproj/Localizable.strings +++ b/Bark/en.lproj/Localizable.strings @@ -94,3 +94,6 @@ other = "Other"; faq = "FAQ"; appSC = "App Source Code"; backendSC = "Backend Source Code"; + +notificationIcon = "Custom Icon"; +notificationIconNotice = "Set a custom icon for the push and the set icon will replace the default Bark icon. Icons are automatically cached locally."; diff --git a/Bark/zh-Hans.lproj/Localizable.strings b/Bark/zh-Hans.lproj/Localizable.strings index cd06ee0..736d13f 100644 --- a/Bark/zh-Hans.lproj/Localizable.strings +++ b/Bark/zh-Hans.lproj/Localizable.strings @@ -96,3 +96,6 @@ other = "其他"; faq = "常见问题"; appSC = "App源代码"; backendSC = "后端源代码"; + +notificationIcon = "自定义推送图标"; +notificationIconNotice = "为推送设置自定义图标,设置的图标将替换默认Bark图标。\图标会自动缓存在本机,相同的图标 URL 仅下载一次。"; diff --git a/Controller/HomeViewModel.swift b/Controller/HomeViewModel.swift index 74b2d59..8de9e90 100644 --- a/Controller/HomeViewModel.swift +++ b/Controller/HomeViewModel.swift @@ -55,6 +55,12 @@ class HomeViewModel: ViewModel, ViewModelType { notice: NSLocalizedString("archiveNotificationMessage"), queryParameter: "isArchive=1" ), + PreviewModel( + body: NSLocalizedString("notificationIcon"), + notice: NSLocalizedString("notificationIconNotice"), + queryParameter: "icon=https://day.app/assets/images/avatar.jpg", + image: UIImage(named: "icon") + ), PreviewModel( body: NSLocalizedString("messageGroup"), notice: NSLocalizedString("groupMessagesNotice"), diff --git a/Intent/Info.plist b/Intent/Info.plist new file mode 100644 index 0000000..bff6b64 --- /dev/null +++ b/Intent/Info.plist @@ -0,0 +1,22 @@ + + + + + NSExtension + + NSExtensionAttributes + + IntentsRestrictedWhileLocked + + IntentsSupported + + INSendMessageIntent + + + NSExtensionPointIdentifier + com.apple.intents-service + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).IntentHandler + + + diff --git a/Intent/IntentHandler.swift b/Intent/IntentHandler.swift new file mode 100644 index 0000000..35c2e86 --- /dev/null +++ b/Intent/IntentHandler.swift @@ -0,0 +1,125 @@ +// +// IntentHandler.swift +// Intent +// +// Created by huangfeng on 2021/9/26. +// Copyright © 2021 Fin. All rights reserved. +// + +import Intents + +// As an example, this class is set up to handle Message intents. +// You will want to replace this or add other intents as appropriate. +// The intents you wish to handle must be declared in the extension's Info.plist. + +// You can test your example integration by saying things to Siri like: +// "Send a message using " +// " John saying hello" +// "Search for messages in " + +class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling { + + override func handler(for intent: INIntent) -> Any { + // This is the default implementation. If you want different objects to handle different intents, + // you can override this and return the handler you want for that particular intent. + + return self + } + + // MARK: - INSendMessageIntentHandling + + // Implement resolution methods to provide additional information about your intent (optional). + func resolveRecipients(for intent: INSendMessageIntent, with completion: @escaping ([INSendMessageRecipientResolutionResult]) -> Void) { + if let recipients = intent.recipients { + + // If no recipients were provided we'll need to prompt for a value. + if recipients.count == 0 { + completion([INSendMessageRecipientResolutionResult.needsValue()]) + return + } + + var resolutionResults = [INSendMessageRecipientResolutionResult]() + for recipient in recipients { + let matchingContacts = [recipient] // Implement your contact matching logic here to create an array of matching contacts + switch matchingContacts.count { + case 2 ... Int.max: + // We need Siri's help to ask user to pick one from the matches. + resolutionResults += [INSendMessageRecipientResolutionResult.disambiguation(with: matchingContacts)] + + case 1: + // We have exactly one matching contact + resolutionResults += [INSendMessageRecipientResolutionResult.success(with: recipient)] + + case 0: + // We have no contacts matching the description provided + resolutionResults += [INSendMessageRecipientResolutionResult.unsupported()] + + default: + break + + } + } + completion(resolutionResults) + } else { + completion([INSendMessageRecipientResolutionResult.needsValue()]) + } + } + + func resolveContent(for intent: INSendMessageIntent, with completion: @escaping (INStringResolutionResult) -> Void) { + if let text = intent.content, !text.isEmpty { + completion(INStringResolutionResult.success(with: text)) + } else { + completion(INStringResolutionResult.needsValue()) + } + } + + // Once resolution is completed, perform validation on the intent and provide confirmation (optional). + + func confirm(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) { + // Verify user is authenticated and your app is ready to send a message. + + let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self)) + let response = INSendMessageIntentResponse(code: .ready, userActivity: userActivity) + completion(response) + } + + // Handle the completed intent (required). + + func handle(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) { + // Implement your application logic to send a message here. + + let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self)) + let response = INSendMessageIntentResponse(code: .success, userActivity: userActivity) + completion(response) + } + + // Implement handlers for each intent you wish to handle. As an example for messages, you may wish to also handle searchForMessages and setMessageAttributes. + + // MARK: - INSearchForMessagesIntentHandling + + func handle(intent: INSearchForMessagesIntent, completion: @escaping (INSearchForMessagesIntentResponse) -> Void) { + // Implement your application logic to find a message that matches the information in the intent. + + let userActivity = NSUserActivity(activityType: NSStringFromClass(INSearchForMessagesIntent.self)) + let response = INSearchForMessagesIntentResponse(code: .success, userActivity: userActivity) + // Initialize with found message's attributes + response.messages = [INMessage( + identifier: "identifier", + content: "I am so excited about SiriKit!", + dateSent: Date(), + sender: INPerson(personHandle: INPersonHandle(value: "sarah@example.com", type: .emailAddress), nameComponents: nil, displayName: "Sarah", image: nil, contactIdentifier: nil, customIdentifier: nil), + recipients: [INPerson(personHandle: INPersonHandle(value: "+1-415-555-5555", type: .phoneNumber), nameComponents: nil, displayName: "John", image: nil, contactIdentifier: nil, customIdentifier: nil)] + )] + completion(response) + } + + // MARK: - INSetMessageAttributeIntentHandling + + func handle(intent: INSetMessageAttributeIntent, completion: @escaping (INSetMessageAttributeIntentResponse) -> Void) { + // Implement your application logic to set the message attribute here. + + let userActivity = NSUserActivity(activityType: NSStringFromClass(INSetMessageAttributeIntent.self)) + let response = INSetMessageAttributeIntentResponse(code: .success, userActivity: userActivity) + completion(response) + } +} diff --git a/NotificationServiceExtension/Info.plist b/NotificationServiceExtension/Info.plist index 92c4aa8..d8fc4ad 100644 --- a/NotificationServiceExtension/Info.plist +++ b/NotificationServiceExtension/Info.plist @@ -22,6 +22,13 @@ $(CURRENT_PROJECT_VERSION) NSExtension + NSExtensionAttributes + + IntentsSupported + + INSendMessageIntent + + NSExtensionPointIdentifier com.apple.usernotifications.service NSExtensionPrincipalClass diff --git a/NotificationServiceExtension/NotificationService.swift b/NotificationServiceExtension/NotificationService.swift index 2668640..8239c31 100644 --- a/NotificationServiceExtension/NotificationService.swift +++ b/NotificationServiceExtension/NotificationService.swift @@ -11,6 +11,7 @@ import UserNotifications import RealmSwift import Kingfisher import MobileCoreServices +import Intents class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? @@ -94,17 +95,56 @@ class NotificationService: UNNotificationServiceExtension { /// - userInfo: 推送参数 /// - bestAttemptContent: 推送content /// - complection: 下载图片完毕后的回调函数 - fileprivate func downloadImage(_ userInfo: [AnyHashable : Any], content bestAttemptContent: UNMutableNotificationContent, complection: @escaping () -> () ) { - - guard let imageUrl = userInfo["image"] as? String, - let groupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.bark"), - let cache = try? ImageCache(name: "shared",cacheDirectoryURL: groupUrl) + fileprivate func downloadImage(_ imageUrl:String, complection: @escaping (_ imageFileUrl:String?) -> () ) { + guard let groupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.bark"), + let cache = try? ImageCache(name: "shared",cacheDirectoryURL: groupUrl), + let imageResource = URL(string: imageUrl) else { + complection(nil) + return + } + + func downloadFinished(){ + let cacheFileUrl = cache.cachePath(forKey: imageResource.cacheKey) + complection(cacheFileUrl) + } + + // 先查看图片缓存 + if cache.diskStorage.isCached(forKey: imageResource.cacheKey) { + downloadFinished() + return + } + + // 下载图片 + Kingfisher.ImageDownloader.default.downloadImage(with: imageResource, options: nil) { result in + guard let result = try? result.get() else { + complection(nil) + return + } + // 缓存图片 + cache.storeToDisk(result.originalData, forKey: imageResource.cacheKey, expiration: StorageExpiration.never) { r in + downloadFinished() + } + } + } + + /// 下载推送图片 + /// - Parameters: + /// - userInfo: 推送参数 + /// - bestAttemptContent: 推送content + /// - complection: 下载图片完毕后的回调函数 + fileprivate func setImage(_ userInfo: [AnyHashable : Any], content bestAttemptContent: UNMutableNotificationContent, complection: @escaping () -> () ) { + + guard let imageUrl = userInfo["image"] as? String else { complection() return } - func finished(imageFileUrl: String){ + func finished(_ imageFileUrl: String?){ + guard let imageFileUrl = imageFileUrl else { + complection() + return + } let copyDestUrl = URL(fileURLWithPath: imageFileUrl).appendingPathExtension(".tmp") // 将图片缓存复制一份,推送使用完后会自动删除,但图片缓存需要留着以后在历史记录里查看 try? FileManager.default.copyItem( @@ -120,33 +160,78 @@ class NotificationService: UNNotificationServiceExtension { complection() } - // 远程图片 - guard let imageResource = URL(string: imageUrl) else { - complection() - return - } - - func downloadFinished(){ - let cacheFileUrl = cache.cachePath(forKey: imageResource.cacheKey) - finished(imageFileUrl: cacheFileUrl) - } - - // 先查看图片缓存 - if cache.diskStorage.isCached(forKey: imageResource.cacheKey) { - downloadFinished() - return - } - - // 下载图片 - Kingfisher.ImageDownloader.default.downloadImage(with: imageResource, options: nil) { result in - guard let result = try? result.get() else { + downloadImage(imageUrl, complection: finished) + } + + fileprivate func setIcon(_ userInfo: [AnyHashable : Any], content bestAttemptContent: UNMutableNotificationContent, complection: @escaping () -> () ) { + if #available(iOSApplicationExtension 15.0, *) { + guard let imageUrl = userInfo["icon"] as? String else { complection() return } - // 缓存图片 - cache.storeToDisk(result.originalData, forKey: imageResource.cacheKey, expiration: StorageExpiration.never) { r in - downloadFinished() + + func finished(_ imageFileUrl: String?){ + guard let imageFileUrl = imageFileUrl else { + complection() + return + } + var personNameComponents = PersonNameComponents() + personNameComponents.nickname = bestAttemptContent.title + + let avatar = INImage(imageData: NSData(contentsOfFile: imageFileUrl)! as Data) + let senderPerson = INPerson( + personHandle: INPersonHandle(value: "", type: .unknown), + nameComponents: personNameComponents, + displayName: personNameComponents.nickname, + image: avatar, + contactIdentifier: nil, + customIdentifier: nil, + isMe: false, + suggestionType: .none + ) + let mePerson = INPerson( + personHandle: INPersonHandle(value: "", type: .unknown), + nameComponents: nil, + displayName: nil, + image: nil, + contactIdentifier: nil, + customIdentifier: nil, + isMe: true, + suggestionType: .none + ) + + let intent = INSendMessageIntent( + recipients: [mePerson], + outgoingMessageType: .outgoingMessageText, + content: bestAttemptContent.body, + speakableGroupName: INSpeakableString(spokenPhrase: personNameComponents.nickname ?? ""), + conversationIdentifier: "sampleConversationIdentifier", + serviceName: nil, + sender: senderPerson, + attachments: nil + ) + + intent.setImage(avatar, forParameterNamed: \.sender) + + let interaction = INInteraction(intent: intent, response: nil) + interaction.direction = .incoming + + interaction.donate(completion: nil) + + do { + let content = try self.bestAttemptContent!.updating(from: intent) as! UNMutableNotificationContent + self.bestAttemptContent = content + } catch { + // Handle error + } + + complection() } + + downloadImage(imageUrl, complection: finished) + } + else{ + complection() } } @@ -162,10 +247,14 @@ class NotificationService: UNNotificationServiceExtension { autoCopy(userInfo, defaultCopy: bestAttemptContent.body) // 保存推送 archive(userInfo) - // 下载图片,设置到推送中 - downloadImage(userInfo, content: bestAttemptContent) { - contentHandler(bestAttemptContent) + // 设置推送图标 + setIcon(userInfo, content: self.bestAttemptContent!) { + // 设置推送图片 + self.setImage(userInfo, content: self.bestAttemptContent!) { + contentHandler(self.bestAttemptContent!) + } } + } else{ contentHandler(bestAttemptContent!)