diff --git a/Podfile b/Podfile index 39bcd8634..de4288b5f 100644 --- a/Podfile +++ b/Podfile @@ -93,6 +93,10 @@ abstract_target 'RiotPods' do import_MatrixKitAppExtension end + target "RiotNSE" do + import_MatrixKitAppExtension + end + end diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 5a8db780f..acdc89c8b 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -684,6 +684,7 @@ B1E5368D21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E5368C21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift */; }; B1E5368F21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1E5368E21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard */; }; B1FDF56021F5FE5500BA3834 /* KeyBackupSetupPassphraseViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1FDF55F21F5FE5500BA3834 /* KeyBackupSetupPassphraseViewAction.swift */; }; + DB1392A2332C3CAF6C9962EF /* Pods_RiotPods_RiotNSE.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4D418D054E4032F2CFA8B51 /* Pods_RiotPods_RiotNSE.framework */; }; EC85D6AE2477DC89002C44C9 /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC85D6AD2477DC89002C44C9 /* RoundedButton.swift */; }; EC85D7142477DCD7002C44C9 /* KeyVerificationScanConfirmationViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC85D70C2477DCD7002C44C9 /* KeyVerificationScanConfirmationViewState.swift */; }; EC85D7152477DCD7002C44C9 /* KeyVerificationScanConfirmationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC85D70D2477DCD7002C44C9 /* KeyVerificationScanConfirmationViewController.swift */; }; @@ -706,6 +707,9 @@ EC85D7372477DD97002C44C9 /* LocalContactsSectionHeaderContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = EC85D7352477DD97002C44C9 /* LocalContactsSectionHeaderContainerView.m */; }; EC85D73A2477DDB8002C44C9 /* DirectorySectionHeaderContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = EC85D7392477DDB8002C44C9 /* DirectorySectionHeaderContainerView.m */; }; EC85D73E2477DDD0002C44C9 /* PushNotificationService.m in Sources */ = {isa = PBXBuildFile; fileRef = EC85D73D2477DDD0002C44C9 /* PushNotificationService.m */; }; + EC85D7462477E5F7002C44C9 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC85D7452477E5F7002C44C9 /* NotificationService.swift */; }; + EC85D74A2477E5F7002C44C9 /* RiotNSE.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = EC85D7432477E5F7002C44C9 /* RiotNSE.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + EC85D74F2477E8EB002C44C9 /* RiotSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B5597F20EFC3DF00210D55 /* RiotSettings.swift */; }; ECB101302477CFDB00CF8C11 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB1012C2477CFDB00CF8C11 /* UITableView.swift */; }; ECB101312477CFDB00CF8C11 /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB1012D2477CFDB00CF8C11 /* UILabel.swift */; }; ECB101322477CFDB00CF8C11 /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB1012E2477CFDB00CF8C11 /* UIDevice.swift */; }; @@ -751,6 +755,13 @@ remoteGlobalIDString = 92726A421F58737A004AD26F; remoteInfo = SiriIntents; }; + EC85D7482477E5F7002C44C9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F094A99A1B78D8F000B1FBBF /* Project object */; + proxyType = 1; + remoteGlobalIDString = EC85D7422477E5F7002C44C9; + remoteInfo = RiotNSE; + }; F094A9BF1B78D8F000B1FBBF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F094A99A1B78D8F000B1FBBF /* Project object */; @@ -769,6 +780,7 @@ files = ( 24CBEC591F0EAD310093EABB /* RiotShareExtension.appex in Embed App Extensions */, 92726A4B1F58737A004AD26F /* SiriIntents.appex in Embed App Extensions */, + EC85D74A2477E5F7002C44C9 /* RiotNSE.appex in Embed App Extensions */, ); name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -779,6 +791,7 @@ 129EB7E27E7E4AC3F5F098F5 /* Pods_RiotTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1ACF09217ADF1D7E7A35BC02 /* Pods_RiotPods_Riot.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotPods_Riot.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 24CBEC4E1F0EAD310093EABB /* RiotShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = RiotShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 2B582BE9B2A98BCF5F740873 /* Pods-RiotPods-RiotNSE.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-RiotNSE.debug.xcconfig"; path = "Target Support Files/Pods-RiotPods-RiotNSE/Pods-RiotPods-RiotNSE.debug.xcconfig"; sourceTree = ""; }; 3209451121F1C1430088CAA2 /* BlackTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlackTheme.swift; sourceTree = ""; }; 32242F0821E8B05F00725742 /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; 32242F0C21E8FBA900725742 /* ThemeService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThemeService.m; sourceTree = ""; }; @@ -912,6 +925,7 @@ 43C2962BE367F59220F517FA /* Pods-RiotPods-Riot.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-Riot.debug.xcconfig"; path = "Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot.debug.xcconfig"; sourceTree = ""; }; 4FC6A5D63FAD1B27C2F57AFA /* Pods-RiotPods-RiotShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-RiotShareExtension.release.xcconfig"; path = "Target Support Files/Pods-RiotPods-RiotShareExtension/Pods-RiotPods-RiotShareExtension.release.xcconfig"; sourceTree = ""; }; 51187E952D5CECF6D6F5A28E /* Pods_RiotPods_SiriIntents.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotPods_SiriIntents.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8A61E94F88EA96AFE1CFD9D3 /* Pods-RiotPods-RiotNSE.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-RiotNSE.release.xcconfig"; path = "Target Support Files/Pods-RiotPods-RiotNSE/Pods-RiotPods-RiotNSE.release.xcconfig"; sourceTree = ""; }; 926FA53D1F4C132000F826C2 /* MXSession+Riot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MXSession+Riot.h"; sourceTree = ""; }; 926FA53E1F4C132000F826C2 /* MXSession+Riot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MXSession+Riot.m"; sourceTree = ""; }; 92726A431F58737A004AD26F /* SiriIntents.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SiriIntents.appex; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1673,6 +1687,7 @@ B43DC75D1590BB8A4243BD4D /* Pods-RiotPods-Riot.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-Riot.release.xcconfig"; path = "Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot.release.xcconfig"; sourceTree = ""; }; BABB6681FBD79219B1213D6C /* Pods-RiotTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotTests.debug.xcconfig"; path = "Target Support Files/Pods-RiotTests/Pods-RiotTests.debug.xcconfig"; sourceTree = ""; }; E2599D0ECB8DD206624E450B /* Pods-RiotPods-SiriIntents.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-SiriIntents.release.xcconfig"; path = "Target Support Files/Pods-RiotPods-SiriIntents/Pods-RiotPods-SiriIntents.release.xcconfig"; sourceTree = ""; }; + E4D418D054E4032F2CFA8B51 /* Pods_RiotPods_RiotNSE.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotPods_RiotNSE.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EC85D6AD2477DC89002C44C9 /* RoundedButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; EC85D70C2477DCD7002C44C9 /* KeyVerificationScanConfirmationViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationScanConfirmationViewState.swift; sourceTree = ""; }; EC85D70D2477DCD7002C44C9 /* KeyVerificationScanConfirmationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationScanConfirmationViewController.swift; sourceTree = ""; }; @@ -1700,6 +1715,10 @@ EC85D7392477DDB8002C44C9 /* DirectorySectionHeaderContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DirectorySectionHeaderContainerView.m; sourceTree = ""; }; EC85D73C2477DDD0002C44C9 /* PushNotificationService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PushNotificationService.h; sourceTree = ""; }; EC85D73D2477DDD0002C44C9 /* PushNotificationService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PushNotificationService.m; sourceTree = ""; }; + EC85D7432477E5F7002C44C9 /* RiotNSE.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = RiotNSE.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + EC85D7452477E5F7002C44C9 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; + EC85D7472477E5F7002C44C9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + EC85D74E2477E614002C44C9 /* RiotNSE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RiotNSE.entitlements; sourceTree = ""; }; ECB1012C2477CFDB00CF8C11 /* UITableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableView.swift; sourceTree = ""; }; ECB1012D2477CFDB00CF8C11 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = ""; }; ECB1012E2477CFDB00CF8C11 /* UIDevice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = ""; }; @@ -1763,6 +1782,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + EC85D7402477E5F7002C44C9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DB1392A2332C3CAF6C9962EF /* Pods_RiotPods_RiotNSE.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F094A99F1B78D8F000B1FBBF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -2111,6 +2138,8 @@ E2599D0ECB8DD206624E450B /* Pods-RiotPods-SiriIntents.release.xcconfig */, BABB6681FBD79219B1213D6C /* Pods-RiotTests.debug.xcconfig */, AC34BF67FD21A9D01C16AE5D /* Pods-RiotTests.release.xcconfig */, + 2B582BE9B2A98BCF5F740873 /* Pods-RiotPods-RiotNSE.debug.xcconfig */, + 8A61E94F88EA96AFE1CFD9D3 /* Pods-RiotPods-RiotNSE.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -2123,6 +2152,7 @@ 97151D7F0F892081250D50A3 /* Pods_RiotPods_RiotShareExtension.framework */, 51187E952D5CECF6D6F5A28E /* Pods_RiotPods_SiriIntents.framework */, 129EB7E27E7E4AC3F5F098F5 /* Pods_RiotTests.framework */, + E4D418D054E4032F2CFA8B51 /* Pods_RiotPods_RiotNSE.framework */, ); name = Frameworks; sourceTree = ""; @@ -4247,6 +4277,16 @@ path = PushNotification; sourceTree = ""; }; + EC85D7442477E5F7002C44C9 /* RiotNSE */ = { + isa = PBXGroup; + children = ( + EC85D74E2477E614002C44C9 /* RiotNSE.entitlements */, + EC85D7452477E5F7002C44C9 /* NotificationService.swift */, + EC85D7472477E5F7002C44C9 /* Info.plist */, + ); + path = RiotNSE; + sourceTree = ""; + }; F083BB021E7005FD00A9B29C /* RiotTests */ = { isa = PBXGroup; children = ( @@ -4407,6 +4447,7 @@ F083BB021E7005FD00A9B29C /* RiotTests */, 24CBEC4F1F0EAD310093EABB /* RiotShareExtension */, 92726A441F58737A004AD26F /* SiriIntents */, + EC85D7442477E5F7002C44C9 /* RiotNSE */, F094A9A31B78D8F000B1FBBF /* Products */, 5FC42FA41F5186AFFB6A2404 /* Frameworks */, 3232AAFE22564D9100AD6A5C /* Tools */, @@ -4422,6 +4463,7 @@ F094A9BE1B78D8F000B1FBBF /* RiotTests.xctest */, 24CBEC4E1F0EAD310093EABB /* RiotShareExtension.appex */, 92726A431F58737A004AD26F /* SiriIntents.appex */, + EC85D7432477E5F7002C44C9 /* RiotNSE.appex */, ); name = Products; sourceTree = ""; @@ -4465,6 +4507,24 @@ productReference = 92726A431F58737A004AD26F /* SiriIntents.appex */; productType = "com.apple.product-type.app-extension"; }; + EC85D7422477E5F7002C44C9 /* RiotNSE */ = { + isa = PBXNativeTarget; + buildConfigurationList = EC85D74D2477E5F7002C44C9 /* Build configuration list for PBXNativeTarget "RiotNSE" */; + buildPhases = ( + E96A5016582B740FB3EABBB3 /* [CP] Check Pods Manifest.lock */, + EC85D73F2477E5F7002C44C9 /* Sources */, + EC85D7402477E5F7002C44C9 /* Frameworks */, + EC85D7412477E5F7002C44C9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RiotNSE; + productName = RiotNSE; + productReference = EC85D7432477E5F7002C44C9 /* RiotNSE.appex */; + productType = "com.apple.product-type.app-extension"; + }; F094A9A11B78D8F000B1FBBF /* Riot */ = { isa = PBXNativeTarget; buildConfigurationList = F094A9C81B78D8F000B1FBBF /* Build configuration list for PBXNativeTarget "Riot" */; @@ -4483,6 +4543,7 @@ dependencies = ( 242661F61F12B1BA00D3FC08 /* PBXTargetDependency */, 92726A4A1F58737A004AD26F /* PBXTargetDependency */, + EC85D7492477E5F7002C44C9 /* PBXTargetDependency */, ); name = Riot; productName = Vector; @@ -4514,6 +4575,8 @@ F094A99A1B78D8F000B1FBBF /* Project object */ = { isa = PBXProject; attributes = { + DefaultBuildSystemTypeForWorkspace = Original; + LastSwiftUpdateCheck = 1130; LastUpgradeCheck = 0940; ORGANIZATIONNAME = matrix.org; TargetAttributes = { @@ -4538,6 +4601,9 @@ }; }; }; + EC85D7422477E5F7002C44C9 = { + CreatedOnToolsVersion = 11.3.1; + }; F094A9A11B78D8F000B1FBBF = { CreatedOnToolsVersion = 6.2; DevelopmentTeam = 7J4U792NQT; @@ -4599,6 +4665,7 @@ F094A9BD1B78D8F000B1FBBF /* RiotTests */, 24CBEC4D1F0EAD310093EABB /* RiotShareExtension */, 92726A421F58737A004AD26F /* SiriIntents */, + EC85D7422477E5F7002C44C9 /* RiotNSE */, ); }; /* End PBXProject section */ @@ -4625,6 +4692,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + EC85D7412477E5F7002C44C9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; F094A9A01B78D8F000B1FBBF /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -4934,6 +5008,28 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + E96A5016582B740FB3EABBB3 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RiotPods-RiotNSE-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; FF06981FDA0DB688B8C52A41 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -4997,6 +5093,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + EC85D73F2477E5F7002C44C9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EC85D74F2477E8EB002C44C9 /* RiotSettings.swift in Sources */, + EC85D7462477E5F7002C44C9 /* NotificationService.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F094A99E1B78D8F000B1FBBF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -5540,6 +5645,11 @@ target = 92726A421F58737A004AD26F /* SiriIntents */; targetProxy = 92726A491F58737A004AD26F /* PBXContainerItemProxy */; }; + EC85D7492477E5F7002C44C9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = EC85D7422477E5F7002C44C9 /* RiotNSE */; + targetProxy = EC85D7482477E5F7002C44C9 /* PBXContainerItemProxy */; + }; F094A9C01B78D8F000B1FBBF /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F094A9A11B78D8F000B1FBBF /* Riot */; @@ -5791,6 +5901,83 @@ }; name = Release; }; + EC85D74B2477E5F7002C44C9 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2B582BE9B2A98BCF5F740873 /* Pods-RiotPods-RiotNSE.debug.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = RiotNSE/RiotNSE.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0.11.5; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 7J4U792NQT; + ENABLE_BITCODE = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RiotNSE/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 0.11.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = im.vector.app.nse; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + EC85D74C2477E5F7002C44C9 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8A61E94F88EA96AFE1CFD9D3 /* Pods-RiotPods-RiotNSE.release.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = RiotNSE/RiotNSE.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 0.11.5; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 7J4U792NQT; + ENABLE_BITCODE = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RiotNSE/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 0.11.5; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = im.vector.app.nse; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; F094A9C61B78D8F000B1FBBF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -6041,6 +6228,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + EC85D74D2477E5F7002C44C9 /* Build configuration list for PBXNativeTarget "RiotNSE" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EC85D74B2477E5F7002C44C9 /* Debug */, + EC85D74C2477E5F7002C44C9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F094A99D1B78D8F000B1FBBF /* Build configuration list for PBXProject "Riot" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 7c58c0807..18054d7a2 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -273,6 +273,28 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni [self.pushNotificationService registerForRemoteNotificationsWithCompletion:completion]; } +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken +{ + [self.pushNotificationService didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; + + NSString * deviceTokenString = [[[[deviceToken description] + stringByReplacingOccurrencesOfString: @"<" withString: @""] + stringByReplacingOccurrencesOfString: @">" withString: @""] + stringByReplacingOccurrencesOfString: @" " withString: @""]; + + NSLog(@"The generated device token string is : %@",deviceTokenString); +} + +- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error +{ + [self.pushNotificationService didFailToRegisterForRemoteNotificationsWithError:error]; +} + +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler +{ + [self.pushNotificationService didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; +} + #pragma mark - - (NSString*)appVersion @@ -484,6 +506,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni [MXSDKOptions sharedInstance].analyticsDelegate = [Analytics sharedInstance]; [DecryptionFailureTracker sharedInstance].delegate = [Analytics sharedInstance]; [[Analytics sharedInstance] start]; + + // Disable CallKit + [MXKAppSettings standardAppSettings].enableCallKit = NO; self.pushNotificationService = [PushNotificationService new]; self.pushNotificationService.delegate = self; @@ -1953,7 +1978,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni if (isPushRegistered) { // Enable push notifications by default on new added account - [account enablePushKitNotifications:YES success:nil failure:nil]; + [account enablePushNotifications:YES success:nil failure:nil]; } else { @@ -2034,13 +2059,13 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni accountManager.storeClass = [MXFileStore class]; // Disable APNS use. - if (accountManager.apnsDeviceToken) - { - // We use now Pushkit, unregister for all remote notifications received via Apple Push Notification service. - [[UIApplication sharedApplication] unregisterForRemoteNotifications]; - [accountManager setApnsDeviceToken:nil]; - } - +// if (accountManager.apnsDeviceToken) +// { +// // We use now Pushkit, unregister for all remote notifications received via Apple Push Notification service. +// [[UIApplication sharedApplication] unregisterForRemoteNotifications]; +// [accountManager setApnsDeviceToken:nil]; +// } + // Observers have been defined, we can start a matrix session for each enabled accounts. NSLog(@"[AppDelegate] initMatrixSessions: prepareSessionForActiveAccounts (app state: %tu)", [[UIApplication sharedApplication] applicationState]); [accountManager prepareSessionForActiveAccounts]; @@ -4541,6 +4566,11 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni NSDictionary *defaults = [NSDictionary dictionaryWithContentsOfFile:defaultsPathFromApp]; [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; + if (!RiotSettings.shared.isUserDefaultsMigrated) + { + [RiotSettings.shared migrate]; + } + // Now use RiotSettings and NSUserDefaults to store `showDecryptedContentInNotifications` setting option // Migrate this information from main MXKAccount to RiotSettings, if value is not in UserDefaults diff --git a/Riot/Assets/Riot-Defaults.plist b/Riot/Assets/Riot-Defaults.plist index 7bc0ce4f7..c7f887560 100644 --- a/Riot/Assets/Riot-Defaults.plist +++ b/Riot/Assets/Riot-Defaults.plist @@ -12,6 +12,10 @@ im.vector.app.ios.voip.prod pushKitAppIdDev im.vector.app.ios.voip.dev + pusherAppIdDev + im.vector.app.ios.dev + pusherAppIdProd + im.vector.app.ios.prod identityserverurl https://vector.im homeserverurl diff --git a/Riot/Generated/RiotDefaults.swift b/Riot/Generated/RiotDefaults.swift index 6d6431104..3cb6f869a 100644 --- a/Riot/Generated/RiotDefaults.swift +++ b/Riot/Generated/RiotDefaults.swift @@ -34,6 +34,8 @@ internal enum RiotDefaults { internal static let pushGatewayURL: String = _document["pushGatewayURL"] internal static let pushKitAppIdDev: String = _document["pushKitAppIdDev"] internal static let pushKitAppIdProd: String = _document["pushKitAppIdProd"] + internal static let pusherAppIdDev: String = _document["pusherAppIdDev"] + internal static let pusherAppIdProd: String = _document["pusherAppIdProd"] internal static let roomDirectoryServers: [String: Any] = _document["roomDirectoryServers"] internal static let showAllEventsInRoomHistory: Bool = _document["showAllEventsInRoomHistory"] internal static let showLeftMembersInRoomMemberList: Bool = _document["showLeftMembersInRoomMemberList"] diff --git a/Riot/Managers/PushNotification/PushNotificationService.h b/Riot/Managers/PushNotification/PushNotificationService.h index c91c4544e..fe76e70b3 100644 --- a/Riot/Managers/PushNotification/PushNotificationService.h +++ b/Riot/Managers/PushNotification/PushNotificationService.h @@ -15,16 +15,19 @@ limitations under the License. */ +#import #import -#import #import @class MXSession; +@class MXEvent; +@class MXPushRule; +@class MXKAccount; @protocol PushNotificationServiceDelegate; NS_ASSUME_NONNULL_BEGIN -@interface PushNotificationService : NSObject +@interface PushNotificationService : NSObject /** Is push really registered. @@ -48,6 +51,13 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)registerForRemoteNotificationsWithCompletion:(nullable void (^)(NSError *))completion; +- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; + +- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; + +- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; + /** Perform deregistration for remote notifications. */ diff --git a/Riot/Managers/PushNotification/PushNotificationService.m b/Riot/Managers/PushNotification/PushNotificationService.m index 7948a03d4..ab36c401e 100644 --- a/Riot/Managers/PushNotification/PushNotificationService.m +++ b/Riot/Managers/PushNotification/PushNotificationService.m @@ -46,7 +46,7 @@ NSMutableDictionary *notificationListenerBlocks; } -@property (nonatomic, strong) PKPushRegistry *pushRegistry; +//@property (nonatomic, strong) PKPushRegistry *pushRegistry; @property (nonatomic) NSMutableDictionary *> *incomingPushEventIds; @@ -110,16 +110,83 @@ - (void)registerForRemoteNotificationsWithCompletion:(nullable void (^)(NSError *))completion { - self.pushRegistry = [[PKPushRegistry alloc] initWithQueue:nil]; - self.pushRegistry.delegate = self; - self.pushRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP]; - self.registrationForRemoteNotificationsCompletion = completion; + + dispatch_async(dispatch_get_main_queue(), ^{ + [[UIApplication sharedApplication] registerForRemoteNotifications]; + }); +} + +- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken +{ + MXKAccountManager* accountManager = [MXKAccountManager sharedManager]; + [accountManager setApnsDeviceToken:deviceToken]; + // remove PushKit pusher if exists + if (accountManager.pushDeviceToken) + { + [accountManager setPushDeviceToken:nil withPushOptions:nil]; + } + // Sanity check: Make sure the Pushkit push token is deleted + NSParameterAssert(!accountManager.isPushAvailable); + NSParameterAssert(!accountManager.pushDeviceToken); + + _isPushRegistered = YES; + + if (self.registrationForRemoteNotificationsCompletion) + { + self.registrationForRemoteNotificationsCompletion(nil); + self.registrationForRemoteNotificationsCompletion = nil; + } +} + +- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error +{ + [self clearPushNotificationToken]; + + if (self.registrationForRemoteNotificationsCompletion) + { + self.registrationForRemoteNotificationsCompletion(error); + self.registrationForRemoteNotificationsCompletion = nil; + } +} + +- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler +{ +// NSLog(@"[PushNotificationService][Push] didReceiveRemoteNotification: applicationState: %tu - payload: %@", [UIApplication sharedApplication].applicationState, userInfo); +// +// // Display local notifications only when the app is running in background. +// if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) +// { +// NSLog(@"[PushNotificationService][Push] didReceiveRemoteNotification while app is in background"); +// +// // Check whether an event id is provided. +// NSString *eventId = userInfo[@"event_id"]; +// if (eventId) +// { +// // Add this event identifier in the pending push array for each session. +// for (NSMutableArray *array in self.incomingPushEventIds.allValues) +// { +// [array addObject:eventId]; +// } +// +// // Cache payload for further usage +// incomingPushPayloads[eventId] = userInfo; +// } +// else +// { +// NSLog(@"[PushNotificationService][Push] didReceiveRemoteNotification - Unexpected payload %@", userInfo); +// } +// +// // Trigger a background sync to handle notifications. +// [self launchBackgroundSync]; +// } +// + completionHandler(UIBackgroundFetchResultNewData); } - (void)deregisterRemoteNotifications { - self.pushRegistry = nil; _isPushRegistered = NO; } @@ -259,64 +326,64 @@ } } -#pragma mark - PKPushRegistryDelegate +//#pragma mark - PKPushRegistryDelegate +// +//- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type +//{ +// NSData *token = credentials.token; +// +// NSLog(@"[PushNotificationService][Push] didUpdatePushCredentials: Got Push token: %@. Type: %@", [MXKTools logForPushToken:token], type); +// +// MXKAccountManager* accountManager = [MXKAccountManager sharedManager]; +// [accountManager setPushDeviceToken:token withPushOptions:@{@"format": @"event_id_only"}]; +// +// _isPushRegistered = YES; +// +// if (self.registrationForRemoteNotificationsCompletion) +// { +// self.registrationForRemoteNotificationsCompletion(nil); +// self.registrationForRemoteNotificationsCompletion = nil; +// } +//} +// +//- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type +//{ +// NSLog(@"[PushNotificationService][Push] didInvalidatePushTokenForType: Type: %@", type); +// +// [self clearPushNotificationToken]; +//} -- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type -{ - NSData *token = credentials.token; - - NSLog(@"[PushNotificationService][Push] didUpdatePushCredentials: Got Push token: %@. Type: %@", [MXKTools logForPushToken:token], type); - - MXKAccountManager* accountManager = [MXKAccountManager sharedManager]; - [accountManager setPushDeviceToken:token withPushOptions:@{@"format": @"event_id_only"}]; - - _isPushRegistered = YES; - - if (self.registrationForRemoteNotificationsCompletion) - { - self.registrationForRemoteNotificationsCompletion(nil); - self.registrationForRemoteNotificationsCompletion = nil; - } -} - -- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type -{ - NSLog(@"[PushNotificationService][Push] didInvalidatePushTokenForType: Type: %@", type); - - [self clearPushNotificationToken]; -} - -- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type -{ - NSLog(@"[PushNotificationService][Push] didReceiveIncomingPushWithPayload: applicationState: %tu - type: %@ - payload: %@", [UIApplication sharedApplication].applicationState, payload.type, payload.dictionaryPayload); - - // Display local notifications only when the app is running in background. - if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) - { - NSLog(@"[PushNotificationService][Push] didReceiveIncomingPushWithPayload while app is in background"); - - // Check whether an event id is provided. - NSString *eventId = payload.dictionaryPayload[@"event_id"]; - if (eventId) - { - // Add this event identifier in the pending push array for each session. - for (NSMutableArray *array in self.incomingPushEventIds.allValues) - { - [array addObject:eventId]; - } - - // Cache payload for further usage - incomingPushPayloads[eventId] = payload.dictionaryPayload; - } - else - { - NSLog(@"[PushNotificationService][Push] didReceiveIncomingPushWithPayload - Unexpected payload %@", payload.dictionaryPayload); - } - - // Trigger a background sync to handle notifications. - [self launchBackgroundSync]; - } -} +//- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type +//{ +// NSLog(@"[PushNotificationService][Push] didReceiveIncomingPushWithPayload: applicationState: %tu - type: %@ - payload: %@", [UIApplication sharedApplication].applicationState, payload.type, payload.dictionaryPayload); +// +// // Display local notifications only when the app is running in background. +// if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) +// { +// NSLog(@"[PushNotificationService][Push] didReceiveIncomingPushWithPayload while app is in background"); +// +// // Check whether an event id is provided. +// NSString *eventId = payload.dictionaryPayload[@"event_id"]; +// if (eventId) +// { +// // Add this event identifier in the pending push array for each session. +// for (NSMutableArray *array in self.incomingPushEventIds.allValues) +// { +// [array addObject:eventId]; +// } +// +// // Cache payload for further usage +// incomingPushPayloads[eventId] = payload.dictionaryPayload; +// } +// else +// { +// NSLog(@"[PushNotificationService][Push] didReceiveIncomingPushWithPayload - Unexpected payload %@", payload.dictionaryPayload); +// } +// +// // Trigger a background sync to handle notifications. +// [self launchBackgroundSync]; +// } +//} #pragma mark - UNUserNotificationCenterDelegate @@ -374,12 +441,13 @@ } // iOS 10+, this is called when a notification is about to display in foreground. -- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler -{ - NSLog(@"[PushNotificationService][Push] willPresentNotification: applicationState: %@", @([UIApplication sharedApplication].applicationState)); - - completionHandler(UNNotificationPresentationOptionNone); -} +//- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler +//{ +// NSLog(@"[PushNotificationService][Push] willPresentNotification: applicationState: %@", @([UIApplication sharedApplication].applicationState)); +// +//// completionHandler(UNNotificationPresentationOptionNone); +// completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert); +//} #pragma mark - Other Methods @@ -607,7 +675,7 @@ } // iOS 10+, does the same thing as notificationBodyForEvent:pushRule:inAccount:onComplete:, except with more features -- (void)notificationContentForEvent:(MXEvent *)event pushRule:(MXPushRule *)rule inAccount:(MXKAccount *)account onComplete:(void (^)(UNNotificationContent * _Nullable notificationContent))onComplete; +- (void)notificationContentForEvent:(MXEvent *)event pushRule:(MXPushRule *)rule inAccount:(MXKAccount *)account onComplete:(void (^)(UNNotificationContent * _Nullable notificationContent))onComplete { if (!event.content || !event.content.count) { diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index b1ab98997..bc9322c31 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -38,39 +38,61 @@ final class RiotSettings: NSObject { static let shared = RiotSettings() + /// UserDefaults to be used on reads and writes. + private lazy var defaults: UserDefaults = { + return UserDefaults(suiteName: "group.im.vector")! + }() + // MARK: - Public // MARK: Notifications /// Indicate if `showDecryptedContentInNotifications` settings has been set once. var isShowDecryptedContentInNotificationsHasBeenSetOnce: Bool { - return UserDefaults.standard.object(forKey: UserDefaultsKeys.notificationsShowDecryptedContent) != nil + return defaults.object(forKey: UserDefaultsKeys.notificationsShowDecryptedContent) != nil + } + + /// Indicate if UserDefaults suite has been migrated once. + var isUserDefaultsMigrated: Bool { + return defaults.object(forKey: UserDefaultsKeys.notificationsShowDecryptedContent) != nil + } + + func migrate() { + // read all values from standard + let dictionary = UserDefaults.standard.dictionaryRepresentation() + + // write values to suite + // remove redundant values from standard + for (key, value) in dictionary { + defaults.set(value, forKey: key) + UserDefaults.standard.removeObject(forKey: key) + } } /// Indicate if encrypted messages content should be displayed in notifications. var showDecryptedContentInNotifications: Bool { get { - return UserDefaults.standard.bool(forKey: UserDefaultsKeys.notificationsShowDecryptedContent) + return defaults.bool(forKey: UserDefaultsKeys.notificationsShowDecryptedContent) } set { - UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.notificationsShowDecryptedContent) + defaults.set(newValue, forKey: UserDefaultsKeys.notificationsShowDecryptedContent) } } /// Indicate if rooms with missed notifications should be displayed first on home screen. var pinRoomsWithMissedNotificationsOnHome: Bool { get { - return UserDefaults.standard.bool(forKey: UserDefaultsKeys.pinRoomsWithMissedNotifications) + return defaults.bool(forKey: UserDefaultsKeys.pinRoomsWithMissedNotifications) } set { - UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.pinRoomsWithMissedNotifications) + defaults.set(newValue, forKey: UserDefaultsKeys.pinRoomsWithMissedNotifications) } } /// Indicate if rooms with unread messages should be displayed first on home screen. var pinRoomsWithUnreadMessagesOnHome: Bool { get { - return UserDefaults.standard.bool(forKey: UserDefaultsKeys.pinRoomsWithUnreadMessages) + return defaults.bool(forKey: UserDefaultsKeys.pinRoomsWithUnreadMessages) } set { - UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.pinRoomsWithUnreadMessages) + defaults.set(newValue, forKey: UserDefaultsKeys.pinRoomsWithUnreadMessages) } } @@ -78,9 +100,9 @@ final class RiotSettings: NSObject { var userInterfaceTheme: String? { get { - return UserDefaults.standard.string(forKey: UserDefaultsKeys.userInterfaceTheme) + return defaults.string(forKey: UserDefaultsKeys.userInterfaceTheme) } set { - UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.userInterfaceTheme) + defaults.set(newValue, forKey: UserDefaultsKeys.userInterfaceTheme) } } @@ -88,22 +110,22 @@ final class RiotSettings: NSObject { /// Indicate if `enableCrashReport` settings has been set once. var isEnableCrashReportHasBeenSetOnce: Bool { - return UserDefaults.standard.object(forKey: UserDefaultsKeys.enableCrashReport) != nil + return defaults.object(forKey: UserDefaultsKeys.enableCrashReport) != nil } var enableCrashReport: Bool { get { - return UserDefaults.standard.bool(forKey: UserDefaultsKeys.enableCrashReport) + return defaults.bool(forKey: UserDefaultsKeys.enableCrashReport) } set { - UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.enableCrashReport) + defaults.set(newValue, forKey: UserDefaultsKeys.enableCrashReport) } } var enableRageShake: Bool { get { - return UserDefaults.standard.bool(forKey: UserDefaultsKeys.enableRageShake) + return defaults.bool(forKey: UserDefaultsKeys.enableRageShake) } set { - UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.enableRageShake) + defaults.set(newValue, forKey: UserDefaultsKeys.enableRageShake) } } @@ -111,9 +133,9 @@ final class RiotSettings: NSObject { var createConferenceCallsWithJitsi: Bool { get { - return UserDefaults.standard.bool(forKey: UserDefaultsKeys.createConferenceCallsWithJitsi) + return defaults.bool(forKey: UserDefaultsKeys.createConferenceCallsWithJitsi) } set { - UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.createConferenceCallsWithJitsi) + defaults.set(newValue, forKey: UserDefaultsKeys.createConferenceCallsWithJitsi) } } @@ -121,36 +143,36 @@ final class RiotSettings: NSObject { /// Indicate if `allowStunServerFallback` settings has been set once. var isAllowStunServerFallbackHasBeenSetOnce: Bool { - return UserDefaults.standard.object(forKey: UserDefaultsKeys.allowStunServerFallback) != nil + return defaults.object(forKey: UserDefaultsKeys.allowStunServerFallback) != nil } var allowStunServerFallback: Bool { get { - return UserDefaults.standard.bool(forKey: UserDefaultsKeys.allowStunServerFallback) + return defaults.bool(forKey: UserDefaultsKeys.allowStunServerFallback) } set { - UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.allowStunServerFallback) + defaults.set(newValue, forKey: UserDefaultsKeys.allowStunServerFallback) } } var stunServerFallback: String? { - return UserDefaults.standard.string(forKey: UserDefaultsKeys.stunServerFallback) + return defaults.string(forKey: UserDefaultsKeys.stunServerFallback) } // MARK: Key verification var hideVerifyThisSessionAlert: Bool { get { - return UserDefaults.standard.bool(forKey: UserDefaultsKeys.hideVerifyThisSessionAlert) + return defaults.bool(forKey: UserDefaultsKeys.hideVerifyThisSessionAlert) } set { - UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.hideVerifyThisSessionAlert) + defaults.set(newValue, forKey: UserDefaultsKeys.hideVerifyThisSessionAlert) } } var hideReviewSessionsAlert: Bool { get { - return UserDefaults.standard.bool(forKey: UserDefaultsKeys.hideReviewSessionsAlert) + return defaults.bool(forKey: UserDefaultsKeys.hideReviewSessionsAlert) } set { - UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.hideReviewSessionsAlert) + defaults.set(newValue, forKey: UserDefaultsKeys.hideReviewSessionsAlert) } } } diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 5321c0382..de1601f30 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -87,9 +87,9 @@ enum enum { - CALLS_ENABLE_CALLKIT_INDEX = 0, - CALLS_CALLKIT_DESCRIPTION_INDEX, - CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX, + //CALLS_ENABLE_CALLKIT_INDEX = 0, + //CALLS_CALLKIT_DESCRIPTION_INDEX, + CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX=0, CALLS_STUN_SERVER_FALLBACK_DESCRIPTION_INDEX, CALLS_COUNT }; @@ -1731,39 +1731,40 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate> } else if (section == SETTINGS_SECTION_CALLS_INDEX) { - if (row == CALLS_ENABLE_CALLKIT_INDEX) - { - MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; - labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_enable_callkit", @"Vector", nil); - labelAndSwitchCell.mxkSwitch.on = [MXKAppSettings standardAppSettings].isCallKitEnabled; - labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; - labelAndSwitchCell.mxkSwitch.enabled = YES; - [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleCallKit:) forControlEvents:UIControlEventTouchUpInside]; - - if (![MXCallKitAdapter callKitAvailable]) - { - labelAndSwitchCell.mxkSwitch.on = NO; - labelAndSwitchCell.mxkSwitch.enabled = NO; - labelAndSwitchCell.mxkLabel.enabled = NO; - } - - cell = labelAndSwitchCell; - } - else if (row == CALLS_CALLKIT_DESCRIPTION_INDEX) - { - MXKTableViewCell *globalInfoCell = [self getDefaultTableViewCell:tableView]; - globalInfoCell.textLabel.text = NSLocalizedStringFromTable(@"settings_callkit_info", @"Vector", nil); - globalInfoCell.textLabel.numberOfLines = 0; - globalInfoCell.selectionStyle = UITableViewCellSelectionStyleNone; - - if (![MXCallKitAdapter callKitAvailable]) - { - globalInfoCell.textLabel.enabled = NO; - } - - cell = globalInfoCell; - } - else if (row == CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX) +// if (row == CALLS_ENABLE_CALLKIT_INDEX) +// { +// MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; +// labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_enable_callkit", @"Vector", nil); +// labelAndSwitchCell.mxkSwitch.on = [MXKAppSettings standardAppSettings].isCallKitEnabled; +// labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; +// labelAndSwitchCell.mxkSwitch.enabled = YES; +// [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleCallKit:) forControlEvents:UIControlEventTouchUpInside]; +// +// if (![MXCallKitAdapter callKitAvailable]) +// { +// labelAndSwitchCell.mxkSwitch.on = NO; +// labelAndSwitchCell.mxkSwitch.enabled = NO; +// labelAndSwitchCell.mxkLabel.enabled = NO; +// } +// +// cell = labelAndSwitchCell; +// } +// else if (row == CALLS_CALLKIT_DESCRIPTION_INDEX) +// { +// MXKTableViewCell *globalInfoCell = [self getDefaultTableViewCell:tableView]; +// globalInfoCell.textLabel.text = NSLocalizedStringFromTable(@"settings_callkit_info", @"Vector", nil); +// globalInfoCell.textLabel.numberOfLines = 0; +// globalInfoCell.selectionStyle = UITableViewCellSelectionStyleNone; +// +// if (![MXCallKitAdapter callKitAvailable]) +// { +// globalInfoCell.textLabel.enabled = NO; +// } +// +// cell = globalInfoCell; +// } +// else + if (row == CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX) { MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_calls_stun_server_fallback_button", @"Vector", nil); @@ -2848,10 +2849,10 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate> MXKAccountManager *accountManager = [MXKAccountManager sharedManager]; MXKAccount* account = accountManager.activeAccounts.firstObject; - - if (accountManager.pushDeviceToken) + + if (accountManager.apnsDeviceToken) { - [account enablePushKitNotifications:!account.isPushKitNotificationActive success:^{ + [account enablePushNotifications:!account.pushNotificationServiceIsActive success:^{ [self stopActivityIndicator]; } failure:^(NSError *error) { [self stopActivityIndicator]; @@ -2868,7 +2869,7 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate> } else { - [account enablePushKitNotifications:YES success:^{ + [account enablePushNotifications:YES success:^{ [self stopActivityIndicator]; } failure:^(NSError *error) { [self stopActivityIndicator]; diff --git a/RiotNSE/Info.plist b/RiotNSE/Info.plist new file mode 100644 index 000000000..5ce763e05 --- /dev/null +++ b/RiotNSE/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + RiotNSE + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSExtension + + NSExtensionPointIdentifier + com.apple.usernotifications.service + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).NotificationService + + + diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift new file mode 100644 index 000000000..afa438b56 --- /dev/null +++ b/RiotNSE/NotificationService.swift @@ -0,0 +1,482 @@ +/* + Copyright 2020 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UserNotifications +import MatrixKit + +class NotificationService: UNNotificationServiceExtension { + + var requestIdentifier: String? + var contentHandler: ((UNNotificationContent) -> Void)? + var originalContent: UNMutableNotificationContent? + + var userAccount: MXKAccount? + var store: MXFileStore? + var showDecryptedContentInNotifications: Bool { + return RiotSettings.shared.showDecryptedContentInNotifications + } + + override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { + self.contentHandler = contentHandler + requestIdentifier = request.identifier + // save this content as fallback content + originalContent = request.content.mutableCopy() as? UNMutableNotificationContent + + // check if this is a Matrix notification + if let content = originalContent { + let userInfo = content.userInfo + NSLog("[NotificationService] Payload came: \(userInfo) with identifier: \(requestIdentifier!)") + let roomId = userInfo["room_id"] as? String + let eventId = userInfo["event_id"] as? String + + guard roomId != nil, eventId != nil else { + // it's not a Matrix notification, do not change the content + NSLog("[NotificationService] Fallback case 7") + contentHandler(content) + return + } + } + + // setup user account + setup() + + // fetch the event first + fetchEvent() + } + + override func serviceExtensionTimeWillExpire() { + // Called just before the extension will be terminated by the system. + // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. + NSLog("[NotificationService] Fallback case 5") + fallbackToOriginalContent() + } + + func setup() { + let sdkOptions = MXSDKOptions.sharedInstance() + sdkOptions.applicationGroupIdentifier = "group.im.vector" + sdkOptions.disableIdenticonUseForUserAvatar = true + sdkOptions.enableCryptoWhenStartingMXSession = true + sdkOptions.backgroundModeHandler = MXUIKitBackgroundModeHandler() + Bundle.mxk_customizeLocalizedStringTableName("Vector") + + if isatty(STDERR_FILENO) == 0 { +// MXLogger.setSubLogName("nse") +// MXLogger.redirectNSLog(toFiles: true) + } + + userAccount = MXKAccountManager.shared()?.activeAccounts.first + + if let theUserAccount = userAccount { + store = MXFileStore(credentials: theUserAccount.mxCredentials) + + if theUserAccount.mxSession == nil { + theUserAccount.openSession(with: store!) + } + } + } + + func fetchEvent() { + if let content = originalContent, let theUserAccount = self.userAccount { + let userInfo = content.userInfo + let roomId = userInfo["room_id"] as? String + let eventId = userInfo["event_id"] as? String + + guard let theRoomId = roomId, let theEventId = eventId else { + // it's not a Matrix notification, do not change the content + NSLog("[NotificationService] Fallback case 1") + contentHandler?(content) + return + } + + theUserAccount.mxSession.event(withEventId: theEventId, inRoom: theRoomId, success: { [weak self] (event) in + guard let strongSelf = self else { + NSLog("[NotificationService] Fallback case 9") + return + } + + guard let theEvent = event else { + return + } + + if theEvent.isEncrypted { + // encrypted + if strongSelf.showDecryptedContentInNotifications { + // should show decrypted content in notification + if theEvent.clear == nil { + // should decrypt it first + if theUserAccount.mxSession.decryptEvent(theEvent, inTimeline: nil) { + // decryption succeeded + strongSelf.processEvent(theEvent) + } else { + // decryption failed + NSLog("[NotificationService] Event needs to be decrpyted, but we don't have the keys to decrypt it. Launching a background sync.") + strongSelf.launchBackgroundSync() + } + } else { + // already decrypted + strongSelf.processEvent(theEvent) + } + } else { + // do not show decrypted content in notification + strongSelf.fallbackToOriginalContent() + } + } else { + // not encrypted, go on + strongSelf.processEvent(theEvent) + } + }) { [weak self] (error) in + guard let strongSelf = self else { + NSLog("[NotificationService] Fallback case 10") + return + } + NSLog("[NotificationService] Fallback case 3") + strongSelf.fallbackToOriginalContent() + } + } else { + // there is something wrong, do not change the content + NSLog("[NotificationService] Fallback case 4") + fallbackToOriginalContent() + } + } + + func launchBackgroundSync() { + guard let theUserAccount = userAccount else { return } + guard let theFileStore = store else { return } + if theUserAccount.mxSession == nil { + theUserAccount.openSession(with: theFileStore) + } + let sessionState = theUserAccount.mxSession.state + if sessionState == MXSessionStateInitialised || sessionState == MXSessionStatePaused { + theUserAccount.initialBackgroundSync(20000, success: { [weak self] in + guard let strongSelf = self else { + NSLog("[NotificationService] Fallback case 12") + return + } + strongSelf.fetchEvent() + }) { [weak self] (error) in + guard let strongSelf = self else { + NSLog("[NotificationService] Fallback case 11") + return + } + NSLog("[NotificationService] Fallback case 6") + strongSelf.fallbackToOriginalContent() + } + } else { + NSLog("[NotificationService] Fallback case 8") + fallbackToOriginalContent() + } + } + + func processEvent(_ event: MXEvent) { + if let content = originalContent, let theUserAccount = userAccount { + + self.notificationContent(forEvent: event, inAccount: theUserAccount) { (notificationContent) in + self.store?.close() + + // Modify the notification content here... + guard let newContent = notificationContent else { + // remove + UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [self.requestIdentifier!]) + return + } + + content.title = newContent.title + content.subtitle = newContent.subtitle + content.body = newContent.body + content.threadIdentifier = newContent.threadIdentifier + content.categoryIdentifier = newContent.categoryIdentifier + content.userInfo = newContent.userInfo + content.sound = newContent.sound + + self.contentHandler?(content) + } + } + } + + func fallbackToOriginalContent() { + store?.close() + if let content = originalContent { + contentHandler?(content) + } else { + NSLog("[NotificationService] Fallback case 13") + } + } + + func notificationContent(forEvent event: MXEvent, inAccount account: MXKAccount, onComplete: @escaping (UNNotificationContent?) -> Void) { + if event.content == nil || event.content!.count == 0 { + NSLog("[NotificationService][Push] notificationContentForEvent: empty event content") + onComplete(nil) + return + } + + guard let room = account.mxSession.room(withRoomId: event.roomId) else { + NSLog("[NotificationService][Push] notificationBodyForEvent: Unknown room") + onComplete(nil) + return + } + let pushRule = room.getRoomPushRule() + + room.state({ (roomState:MXRoomState!) in + + var notificationTitle: String? + var notificationBody: String? + + var threadIdentifier = room.roomId + let eventSenderName = roomState.members.memberName(event.sender) + let currentUserId = account.mxCredentials.userId + + if event.eventType == .roomMessage || event.eventType == .roomEncrypted { + if room.isMentionsOnly { + // A local notification will be displayed only for highlighted notification. + var isHighlighted:Bool = false + + // Check whether is there an highlight tweak on it + for ruleAction in pushRule?.actions ?? [] { + guard let action = ruleAction as? MXPushRuleAction else { continue } + if action.actionType == MXPushRuleActionTypeSetTweak { + if action.parameters["set_tweak"] as? String == "highlight" { + // Check the highlight tweak "value" + // If not present, highlight. Else check its value before highlighting + if nil == action.parameters["value"] || true == (action.parameters["value"] as? Bool) { + isHighlighted = true + break + } + } + } + } + + if !isHighlighted { + // Ignore this notif. + NSLog("[NotificationService][Push] notificationBodyForEvent: Ignore non highlighted notif in mentions only room") + onComplete(nil) + return + } + } + + var msgType = event.content["msgtype"] as? String + let messageContent = event.content["body"] as? String + + if event.isEncrypted && !self.showDecryptedContentInNotifications { + // Hide the content + msgType = nil + } + + let roomDisplayName = room.summary.displayname + + let myUserId:String! = account.mxSession.myUser.userId + let isIncomingEvent:Bool = !(event.sender == myUserId) + + // Display the room name only if it is different than the sender name + if roomDisplayName != nil && !(roomDisplayName == eventSenderName) { + notificationTitle = NSString.localizedUserNotificationString(forKey: "MSG_FROM_USER_IN_ROOM_TITLE", arguments:[eventSenderName as Any, roomDisplayName as Any]) + + if (msgType == "m.text") { + notificationBody = messageContent + } else if (msgType == "m.emote") { + notificationBody = NSString.localizedUserNotificationString(forKey: "ACTION_FROM_USER", arguments:[eventSenderName as Any, messageContent as Any]) + } else if (msgType == "m.image") { + notificationBody = NSString.localizedUserNotificationString(forKey: "IMAGE_FROM_USER", arguments:[eventSenderName as Any, messageContent as Any]) + } else if room.isDirect && isIncomingEvent && (msgType == kMXMessageTypeKeyVerificationRequest) { + account.mxSession.crypto.keyVerificationManager.keyVerification(fromKeyVerificationEvent: event, + success:{ (keyVerification) in + if keyVerification.request != nil && keyVerification.request!.state == MXKeyVerificationRequestStatePending { + // TODO: Add accept and decline actions to notification + let body:String! = NSString.localizedUserNotificationString(forKey: "KEY_VERIFICATION_REQUEST_FROM_USER", arguments:[eventSenderName as Any]) + + let notificationContent:UNNotificationContent! = self.notificationContent(withTitle: notificationTitle, + body: body, + threadIdentifier: threadIdentifier, + userId: currentUserId, + event: event, + pushRule: pushRule) + + onComplete(notificationContent) + } else { + onComplete(nil) + } + }, failure:{ (error) in + NSLog("[NotificationService][Push] notificationContentForEvent: failed to fetch key verification with error: \(error)") + onComplete(nil) + }) + } else { + // Encrypted messages falls here + notificationBody = NSString.localizedUserNotificationString(forKey: "MSG_FROM_USER", arguments:[eventSenderName as Any]) + } + } else { + notificationTitle = eventSenderName + + if (msgType == "m.text") { + notificationBody = messageContent + } else if (msgType == "m.emote") { + notificationBody = NSString.localizedUserNotificationString(forKey: "ACTION_FROM_USER", arguments:[eventSenderName as Any, messageContent as Any]) + } else if (msgType == "m.image") { + notificationBody = NSString.localizedUserNotificationString(forKey: "IMAGE_FROM_USER", arguments:[eventSenderName as Any, messageContent as Any]) + } else { + // Encrypted messages falls here + notificationBody = NSString.localizedUserNotificationString(forKey: "MSG_FROM_USER", arguments:[eventSenderName as Any]) + } + } + } else if event.eventType == .callInvite { + let offer = event.content["offer"] as? [AnyHashable: Any] + let sdp = offer?["sdp"] as? String + let isVideoCall = sdp?.contains("m=video") ?? false + + if isVideoCall { + notificationBody = NSString.localizedUserNotificationString(forKey: "VIDEO_CALL_FROM_USER", arguments:[eventSenderName as Any]) + } else { + notificationBody = NSString.localizedUserNotificationString(forKey: "VOICE_CALL_FROM_USER", arguments:[eventSenderName as Any]) + } + + // call notifications should stand out from normal messages, so we don't stack them + threadIdentifier = nil + } else if event.eventType == .roomMember { + let roomDisplayName:String! = room.summary.displayname + + if roomDisplayName != nil && !(roomDisplayName == eventSenderName) { + notificationBody = NSString.localizedUserNotificationString(forKey: "USER_INVITE_TO_NAMED_ROOM", arguments:[eventSenderName as Any, roomDisplayName as Any]) + } else { + notificationBody = NSString.localizedUserNotificationString(forKey: "USER_INVITE_TO_CHAT", arguments:[eventSenderName as Any]) + } + } else if event.eventType == .sticker { + let roomDisplayName:String! = room.summary.displayname + + if roomDisplayName != nil && !(roomDisplayName == eventSenderName) { + notificationTitle = NSString.localizedUserNotificationString(forKey: "MSG_FROM_USER_IN_ROOM_TITLE", arguments:[eventSenderName as Any, roomDisplayName as Any]) + } else { + notificationTitle = eventSenderName + } + + notificationBody = NSString.localizedUserNotificationString(forKey: "STICKER_FROM_USER", arguments:[eventSenderName as Any]) + } + + if (notificationBody != nil) { + let notificationContent = self.notificationContent(withTitle: notificationTitle, + body: notificationBody, + threadIdentifier: threadIdentifier, + userId: currentUserId, + event: event, + pushRule: pushRule) + + onComplete(notificationContent) + } else { + onComplete(nil) + } + }) + } + + func notificationContent(withTitle title: String?, body: String?, threadIdentifier: String?, userId: String?, event: MXEvent, pushRule: MXPushRule?) -> UNNotificationContent { + let notificationContent = UNMutableNotificationContent() + + if let title = title { + notificationContent.title = title + } + if let body = body { + notificationContent.body = body + } + if let threadIdentifier = threadIdentifier { + notificationContent.threadIdentifier = threadIdentifier + } + if let categoryIdentifier = self.notificationCategoryIdentifier(forEvent: event) { + notificationContent.categoryIdentifier = categoryIdentifier + } + if let soundName = notificationSoundName(fromPushRule: pushRule) { + notificationContent.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: soundName)) + } else { + notificationContent.sound = UNNotificationSound.default + } + notificationContent.userInfo = notificationUserInfo(forEvent: event, andUserId: userId) + + return notificationContent + } + + func notificationUserInfo(forEvent event: MXEvent, andUserId userId: String?) -> [AnyHashable: Any] { + var notificationUserInfo: [AnyHashable: Any] = [ + "type": "full", + "room_id": event.roomId as Any, + "event_id": event.eventId as Any + ] + if let userId = userId { + notificationUserInfo["user_id"] = userId + } + return notificationUserInfo + } + + func notificationSoundName(fromPushRule pushRule: MXPushRule?) -> String? { + var soundName: String? + + // Set sound name based on the value provided in action of MXPushRule + for ruleAction in pushRule?.actions ?? [] { + guard let action = ruleAction as? MXPushRuleAction else { continue } + + if action.actionType == MXPushRuleActionTypeSetTweak { + if (action.parameters["set_tweak"] as? String == "sound") { + soundName = action.parameters["value"] as? String + if (soundName == "default") { + soundName = "message.caf" + } + } + } + } + + return soundName + } + + func notificationCategoryIdentifier(forEvent event: MXEvent) -> String? { + let isNotificationContentShown = !event.isEncrypted || self.showDecryptedContentInNotifications + + var categoryIdentifier: String? + + if (event.eventType == .roomMessage || event.eventType == .roomEncrypted) && isNotificationContentShown { + categoryIdentifier = "QUICK_REPLY" + } + + return categoryIdentifier + } + +} + +extension MXRoom { + + func getRoomPushRule() -> MXPushRule? { + if let rules = self.mxSession.notificationCenter.rules.global.room { + for rule in rules { + guard let pushRule = rule as? MXPushRule else { continue } + // the rule id is the room Id + // it is the server trick to avoid duplicated rule on the same room. + if (pushRule.ruleId == self.roomId) { + return pushRule + } + } + } + + return nil + } + + var isMentionsOnly: Bool { + // Check push rules at room level + if let rule = self.getRoomPushRule() { + for ruleAction in rule.actions { + guard let action = ruleAction as? MXPushRuleAction else { continue } + if action.actionType == MXPushRuleActionTypeDontNotify { + return rule.enabled + } + } + } + + return false + } + +} diff --git a/RiotNSE/RiotNSE.entitlements b/RiotNSE/RiotNSE.entitlements new file mode 100644 index 000000000..e540aaaec --- /dev/null +++ b/RiotNSE/RiotNSE.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.im.vector + + +