From 53bb87dafa03475cc4e8c7cff2d4d4bf1b0afb3e Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 22 May 2020 14:27:30 +0300 Subject: [PATCH 01/27] Create notification service extension Signed-off-by: ismailgulek --- Podfile | 4 + Riot.xcodeproj/project.pbxproj | 196 +++++++ Riot/AppDelegate.m | 46 +- Riot/Assets/Riot-Defaults.plist | 4 + Riot/Generated/RiotDefaults.swift | 2 + .../PushNotificationService.h | 14 +- .../PushNotificationService.m | 210 +++++--- Riot/Managers/Settings/RiotSettings.swift | 70 ++- .../Modules/Settings/SettingsViewController.m | 81 +-- RiotNSE/Info.plist | 31 ++ RiotNSE/NotificationService.swift | 482 ++++++++++++++++++ RiotNSE/RiotNSE.entitlements | 10 + 12 files changed, 1005 insertions(+), 145 deletions(-) create mode 100644 RiotNSE/Info.plist create mode 100644 RiotNSE/NotificationService.swift create mode 100644 RiotNSE/RiotNSE.entitlements diff --git a/Podfile b/Podfile index 39bcd86341..de4288b5f2 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 5a8db780fd..acdc89c8b8 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 7c58c08077..18054d7a23 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -273,6 +273,28 @@ - (void)registerForRemoteNotificationsWithCompletion:(nullable void (^)(NSError [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 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [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 @@ - (void)initMatrixSessions 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 @@ - (void)initMatrixSessions 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 @@ - (void)setupUserDefaults 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 7bc0ce4f78..c7f887560c 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 6d6431104d..3cb6f869a6 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 c91c4544e6..fe76e70b38 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 7948a03d4d..ab36c401e6 100644 --- a/Riot/Managers/PushNotification/PushNotificationService.m +++ b/Riot/Managers/PushNotification/PushNotificationService.m @@ -46,7 +46,7 @@ @interface PushNotificationService() NSMutableDictionary *notificationListenerBlocks; } -@property (nonatomic, strong) PKPushRegistry *pushRegistry; +//@property (nonatomic, strong) PKPushRegistry *pushRegistry; @property (nonatomic) NSMutableDictionary *> *incomingPushEventIds; @@ -110,16 +110,83 @@ - (void)registerUserNotificationSettings - (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 @@ - (void)handleSessionStateChangesInBackgroundFor:(MXSession *)mxSession } } -#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 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 - 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 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 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNoti } // 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 @@ - (NSDictionary*)notificationUserInfoForEvent:(MXEvent*)event andUserId:(NSStrin } // 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 b1ab989975..bc9322c318 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 5321c03829..de1601f300 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -87,9 +87,9 @@ 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 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N } 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 @@ - (void)togglePushNotifications:(id)sender 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 @@ - (void)togglePushNotifications:(id)sender } 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 0000000000..5ce763e053 --- /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 0000000000..afa438b567 --- /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 0000000000..e540aaaec1 --- /dev/null +++ b/RiotNSE/RiotNSE.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.im.vector + + + From a3890c65d664664f107454e88cb50eba42ff7b42 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 25 May 2020 13:27:20 +0300 Subject: [PATCH 02/27] update fastlane commands for new target Signed-off-by: ismailgulek --- fastlane/.env.default | 5 +++++ fastlane/Fastfile | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/fastlane/.env.default b/fastlane/.env.default index 10a9ac2baa..e30f9cbff3 100644 --- a/fastlane/.env.default +++ b/fastlane/.env.default @@ -6,10 +6,12 @@ SCHEME=Riot MAIN_TARGET=Riot SHARE_EXTENSION_TARGET=RiotShareExtension SIRI_INTENTS_EXTENSION_TARGET=SiriIntents +NSE_TARGET=RiotNSE MAIN_BUNDLE_ID=im.vector.app SHARE_EXTENSION_BUNDLE_ID=im.vector.app.shareExtension SIRI_INTENTS_EXTENSION_BUNDLE_ID=im.vector.app.SiriIntents +NSE_BUNDLE_ID=im.vector.app.nse ## Build configuration @@ -26,6 +28,7 @@ PROVISIONING_PROFILES_PATH=./provisioning_profiles/ MAIN_PROVISIONING_PROFILE_FILENAME=main.mobileprovision SHARE_EXTENSION_PROVISIONING_PROFILE_FILENAME=share_extension.mobileprovision SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_FILENAME=siri_intents.mobileprovision +NSE_PROVISIONING_PROFILE_FILENAME=nse.mobileprovision ## App Store code signing @@ -35,12 +38,14 @@ APPSTORE_SIGNING_CERTIFICATE="iPhone Distribution: Vector Creations Limited (7J4 APPSTORE_MAIN_PROVISIONING_PROFILE_SPECIFIER="Vector App Store" APPSTORE_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Share Extension: App Store" APPSTORE_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Siri Intents: App Store" +APPSTORE_NSE_PROVISIONING_PROFILE_SPECIFIER="Vector NSE: App Store" ## Ad-Hoc code signing ADHOC_MAIN_PROVISIONING_PROFILE_SPECIFIER="Vector Ad Hoc" ADHOC_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Share Extension: Ad Hoc ADHOC_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Siri Intents: Ad Hoc" +ADHOC_NSE_PROVISIONING_PROFILE_SPECIFIER="Vector NSE: Ad Hoc" ## Account information diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 350e21eac5..4d944c3bf9 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -101,11 +101,13 @@ platform :ios do main_provisioning_profile = ENV["ADHOC_MAIN_PROVISIONING_PROFILE_SPECIFIER"] share_extension_provisioning_profile = ENV["ADHOC_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] siri_intents_provisioning_profile = ENV["ADHOC_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] + nse_provisioning_profile = ENV["ADHOC_NSE_PROVISIONING_PROFILE_SPECIFIER"] else export_method = "app-store" main_provisioning_profile = ENV["APPSTORE_MAIN_PROVISIONING_PROFILE_SPECIFIER"] share_extension_provisioning_profile = ENV["APPSTORE_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] siri_intents_provisioning_profile = ENV["APPSTORE_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] + nse_provisioning_profile = ENV["APPSTORE_NSE_PROVISIONING_PROFILE_SPECIFIER"] end # Build app and create ipa @@ -130,6 +132,7 @@ platform :ios do ENV["MAIN_BUNDLE_ID"] => main_provisioning_profile, ENV["SHARE_EXTENSION_BUNDLE_ID"] => share_extension_provisioning_profile, ENV["SIRI_INTENTS_EXTENSION_BUNDLE_ID"] => siri_intents_provisioning_profile + ENV["NSE_BUNDLE_ID"] => nse_provisioning_profile }, iCloudContainerEnvironment: 'Production' } @@ -172,6 +175,15 @@ platform :ios do filename: ENV["SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_FILENAME"], readonly: true ) + # NSE + get_provisioning_profile( + app_identifier: ENV["NSE_BUNDLE_ID"], + adhoc: adhoc, + skip_certificate_verification: skip_certificate_verification, + output_path: output_path, + filename: ENV["NSE_PROVISIONING_PROFILE_FILENAME"], + readonly: true + ) end desc "Update provisioning profiles for each target" @@ -201,6 +213,13 @@ platform :ios do target_filter: ENV["SIRI_INTENTS_EXTENSION_TARGET"], build_configuration: build_configuration ) + # NSE + update_project_provisioning( + xcodeproj: xcodeproj, + profile: "#{provisioning_profiles_path}#{ENV["NSE_PROVISIONING_PROFILE_FILENAME"]}", + target_filter: ENV["NSE_TARGET"], + build_configuration: build_configuration + ) end desc "Update application build number for all targets" @@ -222,6 +241,11 @@ platform :ios do build_number: build_number, target: ENV["SIRI_INTENTS_EXTENSION_TARGET"] ) + # NSE + increment_build_number_in_plist( + build_number: build_number, + target: ENV["NSE_TARGET"] + ) end desc "Returns version identifiers hash to inject in GCC_PREPROCESSOR_DEFINITIONS for release builds" From 73356822de6678c9deafcaf78231ae88a1871762 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 25 May 2020 13:36:42 +0300 Subject: [PATCH 03/27] Configure log files correctly Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index afa438b567..642f34f8f3 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -73,8 +73,8 @@ class NotificationService: UNNotificationServiceExtension { Bundle.mxk_customizeLocalizedStringTableName("Vector") if isatty(STDERR_FILENO) == 0 { -// MXLogger.setSubLogName("nse") -// MXLogger.redirectNSLog(toFiles: true) + MXLogger.setSubLogName("nse") + MXLogger.redirectNSLog(toFiles: true) } userAccount = MXKAccountManager.shared()?.activeAccounts.first From 9475cd7a104326ad7a7893a365df212ced339b90 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 25 May 2020 15:50:26 +0300 Subject: [PATCH 04/27] Fix Manu's comments Signed-off-by: ismailgulek --- .../PushNotificationService.m | 2 +- RiotNSE/NotificationService.swift | 68 +++++++++---------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/Riot/Managers/PushNotification/PushNotificationService.m b/Riot/Managers/PushNotification/PushNotificationService.m index ab36c401e6..68fa3ebcee 100644 --- a/Riot/Managers/PushNotification/PushNotificationService.m +++ b/Riot/Managers/PushNotification/PushNotificationService.m @@ -153,7 +153,7 @@ - (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error - (void)didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { -// NSLog(@"[PushNotificationService][Push] didReceiveRemoteNotification: applicationState: %tu - payload: %@", [UIApplication sharedApplication].applicationState, userInfo); + 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) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 642f34f8f3..bdc635c319 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -79,71 +79,69 @@ class NotificationService: UNNotificationServiceExtension { userAccount = MXKAccountManager.shared()?.activeAccounts.first - if let theUserAccount = userAccount { - store = MXFileStore(credentials: theUserAccount.mxCredentials) + if let userAccount = userAccount { + store = MXFileStore(credentials: userAccount.mxCredentials) - if theUserAccount.mxSession == nil { - theUserAccount.openSession(with: store!) + if userAccount.mxSession == nil { + userAccount.openSession(with: store!) } } } func fetchEvent() { - if let content = originalContent, let theUserAccount = self.userAccount { + if let content = originalContent, let userAccount = 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 { + guard let roomId = userInfo["room_id"] as? String, let eventId = userInfo["event_id"] as? String 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 { + userAccount.mxSession.event(withEventId: eventId, inRoom: roomId, success: { [weak self] (event) in + guard let self = self else { NSLog("[NotificationService] Fallback case 9") return } - guard let theEvent = event else { + guard let event = event else { return } - if theEvent.isEncrypted { + if event.isEncrypted { // encrypted - if strongSelf.showDecryptedContentInNotifications { + if self.showDecryptedContentInNotifications { // should show decrypted content in notification - if theEvent.clear == nil { + if event.clear == nil { // should decrypt it first - if theUserAccount.mxSession.decryptEvent(theEvent, inTimeline: nil) { + if userAccount.mxSession.decryptEvent(event, inTimeline: nil) { // decryption succeeded - strongSelf.processEvent(theEvent) + self.processEvent(event) } 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() + self.launchBackgroundSync() } } else { // already decrypted - strongSelf.processEvent(theEvent) + self.processEvent(event) } } else { // do not show decrypted content in notification - strongSelf.fallbackToOriginalContent() + self.fallbackToOriginalContent() } } else { // not encrypted, go on - strongSelf.processEvent(theEvent) + self.processEvent(event) } }) { [weak self] (error) in - guard let strongSelf = self else { + guard let self = self else { NSLog("[NotificationService] Fallback case 10") return } NSLog("[NotificationService] Fallback case 3") - strongSelf.fallbackToOriginalContent() + self.fallbackToOriginalContent() } } else { // there is something wrong, do not change the content @@ -153,26 +151,26 @@ class NotificationService: UNNotificationServiceExtension { } func launchBackgroundSync() { - guard let theUserAccount = userAccount else { return } - guard let theFileStore = store else { return } - if theUserAccount.mxSession == nil { - theUserAccount.openSession(with: theFileStore) + guard let userAccount = userAccount else { return } + guard let store = store else { return } + if userAccount.mxSession == nil { + userAccount.openSession(with: store) } - let sessionState = theUserAccount.mxSession.state + let sessionState = userAccount.mxSession.state if sessionState == MXSessionStateInitialised || sessionState == MXSessionStatePaused { - theUserAccount.initialBackgroundSync(20000, success: { [weak self] in - guard let strongSelf = self else { + userAccount.initialBackgroundSync(20000, success: { [weak self] in + guard let self = self else { NSLog("[NotificationService] Fallback case 12") return } - strongSelf.fetchEvent() + self.fetchEvent() }) { [weak self] (error) in - guard let strongSelf = self else { + guard let self = self else { NSLog("[NotificationService] Fallback case 11") return } NSLog("[NotificationService] Fallback case 6") - strongSelf.fallbackToOriginalContent() + self.fallbackToOriginalContent() } } else { NSLog("[NotificationService] Fallback case 8") @@ -181,15 +179,15 @@ class NotificationService: UNNotificationServiceExtension { } func processEvent(_ event: MXEvent) { - if let content = originalContent, let theUserAccount = userAccount { + if let content = originalContent, let userAccount = userAccount, let requestIdentifier = requestIdentifier { - self.notificationContent(forEvent: event, inAccount: theUserAccount) { (notificationContent) in + self.notificationContent(forEvent: event, inAccount: userAccount) { (notificationContent) in self.store?.close() // Modify the notification content here... guard let newContent = notificationContent else { // remove - UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [self.requestIdentifier!]) + UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [requestIdentifier]) return } From f85aee0a564d50913716d7feb189abd40d2762fb Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 25 May 2020 17:52:29 +0300 Subject: [PATCH 05/27] Implement a workaround to delete some notifications afterwards Signed-off-by: ismailgulek --- Riot.xcodeproj/project.pbxproj | 12 +++++++ .../Categories/UNUserNotificationCenter.swift | 32 ++++++++++++++++++ .../PushNotificationService.m | 2 ++ Riot/Utils/Constants.swift | 23 +++++++++++++ RiotNSE/NotificationService.swift | 33 +++++++++---------- 5 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 Riot/Categories/UNUserNotificationCenter.swift create mode 100644 Riot/Utils/Constants.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index acdc89c8b8..5439f998d1 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -710,6 +710,10 @@ 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 */; }; + EC85D751247C0E8F002C44C9 /* UNUserNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC85D750247C0E8F002C44C9 /* UNUserNotificationCenter.swift */; }; + EC85D752247C0F52002C44C9 /* UNUserNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC85D750247C0E8F002C44C9 /* UNUserNotificationCenter.swift */; }; + EC85D754247C0F5B002C44C9 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC85D753247C0F5B002C44C9 /* Constants.swift */; }; + EC85D755247C0F84002C44C9 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC85D753247C0F5B002C44C9 /* Constants.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 */; }; @@ -1719,6 +1723,8 @@ 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 = ""; }; + EC85D750247C0E8F002C44C9 /* UNUserNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNUserNotificationCenter.swift; sourceTree = ""; }; + EC85D753247C0F5B002C44C9 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; 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 = ""; }; @@ -4378,6 +4384,7 @@ 926FA53D1F4C132000F826C2 /* MXSession+Riot.h */, 926FA53E1F4C132000F826C2 /* MXSession+Riot.m */, 32242F0821E8B05F00725742 /* UIColor.swift */, + EC85D750247C0E8F002C44C9 /* UNUserNotificationCenter.swift */, B1CA3A2621EF6913000D1D89 /* UIViewController.swift */, B1CA3A2821EF692B000D1D89 /* UIView.swift */, B140B4A121F87F7100E3F5FE /* OperationQueue.swift */, @@ -4420,6 +4427,7 @@ F083BC0F1E7009EC00A9B29C /* Utils */ = { isa = PBXGroup; children = ( + EC85D753247C0F5B002C44C9 /* Constants.swift */, ECB101342477D00700CF8C11 /* UniversalLink.h */, ECB101352477D00700CF8C11 /* UniversalLink.m */, F083BC101E7009EC00A9B29C /* AvatarGenerator.h */, @@ -5099,6 +5107,8 @@ files = ( EC85D74F2477E8EB002C44C9 /* RiotSettings.swift in Sources */, EC85D7462477E5F7002C44C9 /* NotificationService.swift in Sources */, + EC85D752247C0F52002C44C9 /* UNUserNotificationCenter.swift in Sources */, + EC85D755247C0F84002C44C9 /* Constants.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -5397,6 +5407,7 @@ 32A6001D22C661100042C1D9 /* EditHistoryCoordinatorType.swift in Sources */, F083BDFA1E7009ED00A9B29C /* RoomPreviewData.m in Sources */, B1B557B420EF5AEF00210D55 /* EventDetailsView.m in Sources */, + EC85D751247C0E8F002C44C9 /* UNUserNotificationCenter.swift in Sources */, B1BEE73823DF44A60003A4CB /* UserVerificationSessionsStatusViewAction.swift in Sources */, B1B5577E20EE84BF00210D55 /* IncomingCallView.m in Sources */, B1CE83E22422817200D07506 /* KeyVerificationVerifyBySASViewState.swift in Sources */, @@ -5500,6 +5511,7 @@ B1B558C820EF768F00210D55 /* RoomIncomingEncryptedAttachmentBubbleCell.m in Sources */, B1B557C620EF5CD400210D55 /* DirectoryServerDetailTableViewCell.m in Sources */, EC85D71A2477DCD7002C44C9 /* KeyVerificationScanConfirmationCoordinator.swift in Sources */, + EC85D754247C0F5B002C44C9 /* Constants.swift in Sources */, B1B336C4242B933700F95EC4 /* KeyVerificationSelfVerifyStartViewAction.swift in Sources */, B1B5590920EF768F00210D55 /* RoomEmptyBubbleCell.m in Sources */, 324A2054225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift in Sources */, diff --git a/Riot/Categories/UNUserNotificationCenter.swift b/Riot/Categories/UNUserNotificationCenter.swift new file mode 100644 index 0000000000..7dbd0f43c8 --- /dev/null +++ b/Riot/Categories/UNUserNotificationCenter.swift @@ -0,0 +1,32 @@ +/* + 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 Foundation +import UserNotifications + +@objc extension UNUserNotificationCenter { + + func removeUnwantedNotifications() { + UNUserNotificationCenter.current().getDeliveredNotifications { (notifications) in + // get identifiers of notifications whose category identifiers are "TO_BE_REMOVED" + let identifiersToBeRemoved = notifications.compactMap({ $0.request.content.categoryIdentifier == Constants.toBeRemovedNotificationCategoryIdentifier ? $0.request.identifier : nil }) + + // remove the notifications with these id's + UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: identifiersToBeRemoved) + } + } + +} diff --git a/Riot/Managers/PushNotification/PushNotificationService.m b/Riot/Managers/PushNotification/PushNotificationService.m index 68fa3ebcee..20cbe5ba09 100644 --- a/Riot/Managers/PushNotification/PushNotificationService.m +++ b/Riot/Managers/PushNotification/PushNotificationService.m @@ -198,6 +198,8 @@ - (void)applicationWillEnterForeground [array removeAllObjects]; } [incomingPushPayloads removeAllObjects]; + + [[UNUserNotificationCenter currentNotificationCenter] removeUnwantedNotifications]; } - (void)addMatrixSession:(MXSession *)mxSession diff --git a/Riot/Utils/Constants.swift b/Riot/Utils/Constants.swift new file mode 100644 index 0000000000..6d5cc885e2 --- /dev/null +++ b/Riot/Utils/Constants.swift @@ -0,0 +1,23 @@ +/* + 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 Foundation + +struct Constants { + + static let toBeRemovedNotificationCategoryIdentifier = "TO_BE_REMOVED" + +} diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index bdc635c319..a5bfdb8da2 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -19,7 +19,6 @@ import MatrixKit class NotificationService: UNNotificationServiceExtension { - var requestIdentifier: String? var contentHandler: ((UNNotificationContent) -> Void)? var originalContent: UNMutableNotificationContent? @@ -31,17 +30,18 @@ class NotificationService: UNNotificationServiceExtension { 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 + UNUserNotificationCenter.current().removeUnwantedNotifications() + // check if this is a Matrix notification if let content = originalContent { let userInfo = content.userInfo - NSLog("[NotificationService] Payload came: \(userInfo) with identifier: \(requestIdentifier!)") + NSLog("[NotificationService] Payload came: \(userInfo)") 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") @@ -179,26 +179,25 @@ class NotificationService: UNNotificationServiceExtension { } func processEvent(_ event: MXEvent) { - if let content = originalContent, let userAccount = userAccount, let requestIdentifier = requestIdentifier { + if let content = originalContent, let userAccount = userAccount { self.notificationContent(forEvent: event, inAccount: userAccount) { (notificationContent) in self.store?.close() // Modify the notification content here... - guard let newContent = notificationContent else { - // remove - UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [requestIdentifier]) - return + if let newContent = notificationContent { + 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 + } else { + // this is an unwanted notification, mark as to be deleted when app is foregrounded again OR a new push came + content.categoryIdentifier = Constants.toBeRemovedNotificationCategoryIdentifier } - 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) } } From 87e702a70c2bc7cb0cd40f9786a19a572feacad3 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 25 May 2020 21:16:08 +0300 Subject: [PATCH 06/27] Swiftify some ifs Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index a5bfdb8da2..89e86aa48e 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -36,18 +36,20 @@ class NotificationService: UNNotificationServiceExtension { UNUserNotificationCenter.current().removeUnwantedNotifications() // check if this is a Matrix notification - if let content = originalContent { - let userInfo = content.userInfo - NSLog("[NotificationService] Payload came: \(userInfo)") - let roomId = userInfo["room_id"] as? String - let eventId = userInfo["event_id"] as? String + guard let content = originalContent else { + return + } + + let userInfo = content.userInfo + NSLog("[NotificationService] Payload came: \(userInfo)") + 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 - } + 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 From 23203af216a74579338895b8f7a311d17768824d Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 25 May 2020 21:18:37 +0300 Subject: [PATCH 07/27] Swiftify some ifs Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 40 ++++++++++++++++--------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 89e86aa48e..dce238a785 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -181,27 +181,29 @@ class NotificationService: UNNotificationServiceExtension { } func processEvent(_ event: MXEvent) { - if let content = originalContent, let userAccount = userAccount { + guard let content = originalContent, let userAccount = userAccount else { + return + } + + self.notificationContent(forEvent: event, inAccount: userAccount) { (notificationContent) in + // close store + self.store?.close() - self.notificationContent(forEvent: event, inAccount: userAccount) { (notificationContent) in - self.store?.close() - - // Modify the notification content here... - if let newContent = notificationContent { - 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 - } else { - // this is an unwanted notification, mark as to be deleted when app is foregrounded again OR a new push came - content.categoryIdentifier = Constants.toBeRemovedNotificationCategoryIdentifier - } - - self.contentHandler?(content) + // Modify the notification content here... + if let newContent = notificationContent { + 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 + } else { + // this is an unwanted notification, mark as to be deleted when app is foregrounded again OR a new push came + content.categoryIdentifier = Constants.toBeRemovedNotificationCategoryIdentifier } + + self.contentHandler?(content) } } From db01577903a75c90260e5df6fa4e2d8a4a5124da Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 25 May 2020 21:19:18 +0300 Subject: [PATCH 08/27] Swiftify some ifs Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 107 +++++++++++++++--------------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index dce238a785..355929404c 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -91,64 +91,67 @@ class NotificationService: UNNotificationServiceExtension { } func fetchEvent() { - if let content = originalContent, let userAccount = self.userAccount { - let userInfo = content.userInfo + guard let content = originalContent, let userAccount = self.userAccount else { + // there is something wrong, do not change the content + NSLog("[NotificationService] Fallback case 4") + fallbackToOriginalContent() + return + } + let userInfo = content.userInfo + + guard let roomId = userInfo["room_id"] as? String, let eventId = userInfo["event_id"] as? String else { + // it's not a Matrix notification, do not change the content + NSLog("[NotificationService] Fallback case 1") + contentHandler?(content) + return + } + + userAccount.mxSession.event(withEventId: eventId, inRoom: roomId, success: { [weak self] (event) in + guard let self = self else { + NSLog("[NotificationService] Fallback case 9") + return + } - guard let roomId = userInfo["room_id"] as? String, let eventId = userInfo["event_id"] as? String else { - // it's not a Matrix notification, do not change the content - NSLog("[NotificationService] Fallback case 1") - contentHandler?(content) + guard let event = event else { return } - userAccount.mxSession.event(withEventId: eventId, inRoom: roomId, success: { [weak self] (event) in - guard let self = self else { - NSLog("[NotificationService] Fallback case 9") - return - } - - guard let event = event else { - return - } - - if event.isEncrypted { - // encrypted - if self.showDecryptedContentInNotifications { - // should show decrypted content in notification - if event.clear == nil { - // should decrypt it first - if userAccount.mxSession.decryptEvent(event, inTimeline: nil) { - // decryption succeeded - self.processEvent(event) - } else { - // decryption failed - NSLog("[NotificationService] Event needs to be decrpyted, but we don't have the keys to decrypt it. Launching a background sync.") - self.launchBackgroundSync() - } - } else { - // already decrypted - self.processEvent(event) - } - } else { - // do not show decrypted content in notification - self.fallbackToOriginalContent() - } - } else { - // not encrypted, go on - self.processEvent(event) - } - }) { [weak self] (error) in - guard let self = self else { - NSLog("[NotificationService] Fallback case 10") - return - } - NSLog("[NotificationService] Fallback case 3") + guard event.isEncrypted else { + // not encrypted, go on processing + self.processEvent(event) + return + } + + // encrypted + guard self.showDecryptedContentInNotifications else { + // do not show decrypted content in notification self.fallbackToOriginalContent() + return } - } else { - // there is something wrong, do not change the content - NSLog("[NotificationService] Fallback case 4") - fallbackToOriginalContent() + + // should show decrypted content in notification + guard event.clear == nil else { + // already decrypted + self.processEvent(event) + return + } + + // should decrypt it first + if userAccount.mxSession.decryptEvent(event, inTimeline: nil) { + // decryption succeeded + self.processEvent(event) + } else { + // decryption failed + NSLog("[NotificationService] Event needs to be decrpyted, but we don't have the keys to decrypt it. Launching a background sync.") + self.launchBackgroundSync() + } + }) { [weak self] (error) in + guard let self = self else { + NSLog("[NotificationService] Fallback case 10") + return + } + NSLog("[NotificationService] Fallback case 3") + self.fallbackToOriginalContent() } } From 4e253234f3953786814feba73ae0a79570e72f26 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 25 May 2020 21:20:31 +0300 Subject: [PATCH 09/27] Swiftify code, ignore session state Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 32 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 355929404c..383e32879f 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -161,25 +161,21 @@ class NotificationService: UNNotificationServiceExtension { if userAccount.mxSession == nil { userAccount.openSession(with: store) } - let sessionState = userAccount.mxSession.state - if sessionState == MXSessionStateInitialised || sessionState == MXSessionStatePaused { - userAccount.initialBackgroundSync(20000, success: { [weak self] in - guard let self = self else { - NSLog("[NotificationService] Fallback case 12") - return - } - self.fetchEvent() - }) { [weak self] (error) in - guard let self = self else { - NSLog("[NotificationService] Fallback case 11") - return - } - NSLog("[NotificationService] Fallback case 6") - self.fallbackToOriginalContent() + + // launch an initial background sync + userAccount.initialBackgroundSync(20000, success: { [weak self] in + guard let self = self else { + NSLog("[NotificationService] Fallback case 12") + return } - } else { - NSLog("[NotificationService] Fallback case 8") - fallbackToOriginalContent() + self.fetchEvent() + }) { [weak self] (error) in + guard let self = self else { + NSLog("[NotificationService] Fallback case 11") + return + } + NSLog("[NotificationService] Fallback case 6") + self.fallbackToOriginalContent() } } From 09353f488fc9caba987731f41426f83e2a62296a Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 25 May 2020 21:22:40 +0300 Subject: [PATCH 10/27] Swiftify content function, use switch for ifs when possible Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 147 ++++++++++++++++-------------- 1 file changed, 77 insertions(+), 70 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 383e32879f..ba16f769dc 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -216,7 +216,7 @@ class NotificationService: UNNotificationServiceExtension { } func notificationContent(forEvent event: MXEvent, inAccount account: MXKAccount, onComplete: @escaping (UNNotificationContent?) -> Void) { - if event.content == nil || event.content!.count == 0 { + guard let content = event.content, content.count > 0 else { NSLog("[NotificationService][Push] notificationContentForEvent: empty event content") onComplete(nil) return @@ -229,7 +229,7 @@ class NotificationService: UNNotificationServiceExtension { } let pushRule = room.getRoomPushRule() - room.state({ (roomState:MXRoomState!) in + room.state({ (roomState: MXRoomState!) in var notificationTitle: String? var notificationBody: String? @@ -238,27 +238,26 @@ class NotificationService: UNNotificationServiceExtension { let eventSenderName = roomState.members.memberName(event.sender) let currentUserId = account.mxCredentials.userId - if event.eventType == .roomMessage || event.eventType == .roomEncrypted { + switch event.eventType { + case .roomMessage, .roomEncrypted: if room.isMentionsOnly { // A local notification will be displayed only for highlighted notification. - var isHighlighted:Bool = false + var isHighlighted = 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 - } - } + guard action.actionType == MXPushRuleActionTypeSetTweak else { continue } + guard action.parameters["set_tweak"] as? String == "highlight" else { continue } + // 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 { + guard isHighlighted else { // Ignore this notif. NSLog("[NotificationService][Push] notificationBodyForEvent: Ignore non highlighted notif in mentions only room") onComplete(nil) @@ -275,105 +274,113 @@ class NotificationService: UNNotificationServiceExtension { } let roomDisplayName = room.summary.displayname - - let myUserId:String! = account.mxSession.myUser.userId - let isIncomingEvent:Bool = !(event.sender == myUserId) + let myUserId = account.mxSession.myUser.userId + let isIncomingEvent = 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 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") { + if msgType == kMXMessageTypeText { 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) { + } else if msgType == kMXMessageTypeEmote { + notificationBody = NSString.localizedUserNotificationString(forKey: "ACTION_FROM_USER", arguments: [eventSenderName as Any, messageContent as Any]) + } else if msgType == kMXMessageTypeImage { + 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 { + guard let request = keyVerification.request, request.state == MXKeyVerificationRequestStatePending else { onComplete(nil) - } + return + } + // TODO: Add accept and decline actions to notification + let body = NSString.localizedUserNotificationString(forKey: "KEY_VERIFICATION_REQUEST_FROM_USER", arguments: [eventSenderName as Any]) + + let notificationContent = self.notificationContent(withTitle: notificationTitle, + body: body, + threadIdentifier: threadIdentifier, + userId: currentUserId, + event: event, + pushRule: pushRule) + + onComplete(notificationContent) }, 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]) + notificationBody = NSString.localizedUserNotificationString(forKey: "MSG_FROM_USER", arguments: [eventSenderName as Any]) } } else { notificationTitle = eventSenderName - if (msgType == "m.text") { + switch msgType { + case kMXMessageTypeText: 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 { + break + case kMXMessageTypeEmote: + notificationBody = NSString.localizedUserNotificationString(forKey: "ACTION_FROM_USER", arguments: [eventSenderName as Any, messageContent as Any]) + break + case kMXMessageTypeImage: + notificationBody = NSString.localizedUserNotificationString(forKey: "IMAGE_FROM_USER", arguments: [eventSenderName as Any, messageContent as Any]) + break + default: // Encrypted messages falls here - notificationBody = NSString.localizedUserNotificationString(forKey: "MSG_FROM_USER", arguments:[eventSenderName as Any]) + notificationBody = NSString.localizedUserNotificationString(forKey: "MSG_FROM_USER", arguments: [eventSenderName as Any]) + break } } - } else if event.eventType == .callInvite { + break + case .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]) + 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]) + 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 + case .roomMember: + let roomDisplayName = room.summary.displayname - if roomDisplayName != nil && !(roomDisplayName == eventSenderName) { - notificationBody = NSString.localizedUserNotificationString(forKey: "USER_INVITE_TO_NAMED_ROOM", arguments:[eventSenderName as Any, roomDisplayName as Any]) + 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]) + notificationBody = NSString.localizedUserNotificationString(forKey: "USER_INVITE_TO_CHAT", arguments: [eventSenderName as Any]) } - } else if event.eventType == .sticker { - let roomDisplayName:String! = room.summary.displayname + case .sticker: + let roomDisplayName = 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]) + 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]) + notificationBody = NSString.localizedUserNotificationString(forKey: "STICKER_FROM_USER", arguments: [eventSenderName as Any]) + default: + break } - if (notificationBody != nil) { - let notificationContent = self.notificationContent(withTitle: notificationTitle, - body: notificationBody, - threadIdentifier: threadIdentifier, - userId: currentUserId, - event: event, - pushRule: pushRule) - - onComplete(notificationContent) - } else { + guard (notificationBody != nil) else { onComplete(nil) + return } + + let notificationContent = self.notificationContent(withTitle: notificationTitle, + body: notificationBody, + threadIdentifier: threadIdentifier, + userId: currentUserId, + event: event, + pushRule: pushRule) + + onComplete(notificationContent) }) } From f25e3b6c6bac04b97e107b2842a58d2e39766fd4 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 25 May 2020 21:23:36 +0300 Subject: [PATCH 11/27] Swiftify code, style changes Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 38 ++++++++++++++++++------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index ba16f769dc..ecf05c6274 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -208,11 +208,13 @@ class NotificationService: UNNotificationServiceExtension { func fallbackToOriginalContent() { store?.close() - if let content = originalContent { - contentHandler?(content) - } else { + guard let content = originalContent else { NSLog("[NotificationService] Fallback case 13") + return } + + // call contentHandler + contentHandler?(content) } func notificationContent(forEvent event: MXEvent, inAccount account: MXKAccount, onComplete: @escaping (UNNotificationContent?) -> Void) { @@ -384,7 +386,12 @@ class NotificationService: UNNotificationServiceExtension { }) } - func notificationContent(withTitle title: String?, body: String?, threadIdentifier: String?, userId: String?, event: MXEvent, pushRule: MXPushRule?) -> UNNotificationContent { + func notificationContent(withTitle title: String?, + body: String?, + threadIdentifier: String?, + userId: String?, + event: MXEvent, + pushRule: MXPushRule?) -> UNNotificationContent { let notificationContent = UNMutableNotificationContent() if let title = title { @@ -427,14 +434,11 @@ class NotificationService: UNNotificationServiceExtension { // 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" - } - } + guard action.actionType == MXPushRuleActionTypeSetTweak else { continue } + guard action.parameters["set_tweak"] as? String == "sound" else { continue } + soundName = action.parameters["value"] as? String + if soundName == "default" { + soundName = "message.caf" } } @@ -444,13 +448,15 @@ class NotificationService: UNNotificationServiceExtension { func notificationCategoryIdentifier(forEvent event: MXEvent) -> String? { let isNotificationContentShown = !event.isEncrypted || self.showDecryptedContentInNotifications - var categoryIdentifier: String? + guard isNotificationContentShown else { + return nil + } - if (event.eventType == .roomMessage || event.eventType == .roomEncrypted) && isNotificationContentShown { - categoryIdentifier = "QUICK_REPLY" + guard event.eventType == .roomMessage || event.eventType == .roomEncrypted else { + return nil } - return categoryIdentifier + return "QUICK_REPLY" } } From 7f39581ee2b9414b0c55542fcd033186016f398a Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 25 May 2020 21:24:08 +0300 Subject: [PATCH 12/27] Swiftify extension methods Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 32 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index ecf05c6274..94f34f83a9 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -464,14 +464,16 @@ class NotificationService: UNNotificationServiceExtension { 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 - } + guard let rules = self.mxSession.notificationCenter.rules.global.room else { + return nil + } + + 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 } } @@ -480,12 +482,14 @@ extension MXRoom { 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 - } + guard let rule = self.getRoomPushRule() else { + return false + } + + for ruleAction in rule.actions { + guard let action = ruleAction as? MXPushRuleAction else { continue } + if action.actionType == MXPushRuleActionTypeDontNotify { + return rule.enabled } } From 63d4a16d51ac995ae5161342a68c4810edbdb938 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 26 May 2020 17:51:15 +0300 Subject: [PATCH 13/27] Update Riot/Utils/Constants.swift Co-authored-by: manuroe --- Riot/Utils/Constants.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Utils/Constants.swift b/Riot/Utils/Constants.swift index 6d5cc885e2..200c52cdd5 100644 --- a/Riot/Utils/Constants.swift +++ b/Riot/Utils/Constants.swift @@ -16,7 +16,7 @@ import Foundation -struct Constants { +enum Constants { static let toBeRemovedNotificationCategoryIdentifier = "TO_BE_REMOVED" From db8b5a4b4560f2a60729e3c21c658910f51b9e8d Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 26 May 2020 17:52:26 +0300 Subject: [PATCH 14/27] Update RiotNSE/NotificationService.swift Co-authored-by: manuroe --- RiotNSE/NotificationService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 94f34f83a9..455047b010 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -142,7 +142,7 @@ class NotificationService: UNNotificationServiceExtension { self.processEvent(event) } else { // decryption failed - NSLog("[NotificationService] Event needs to be decrpyted, but we don't have the keys to decrypt it. Launching a background sync.") + NSLog("[NotificationService] fetchEvent: Event needs to be decrpyted, but we don't have the keys to decrypt it. Launching a background sync.") self.launchBackgroundSync() } }) { [weak self] (error) in From ba50f47c6be3812164070d18516a52c5256e3ab0 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 26 May 2020 18:04:18 +0300 Subject: [PATCH 15/27] Remove some commented code Signed-off-by: ismailgulek --- .../PushNotificationService.m | 97 +------------------ .../Modules/Settings/SettingsViewController.m | 35 ------- 2 files changed, 1 insertion(+), 131 deletions(-) diff --git a/Riot/Managers/PushNotification/PushNotificationService.m b/Riot/Managers/PushNotification/PushNotificationService.m index 20cbe5ba09..85e97ece8c 100644 --- a/Riot/Managers/PushNotification/PushNotificationService.m +++ b/Riot/Managers/PushNotification/PushNotificationService.m @@ -154,34 +154,7 @@ - (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); } @@ -328,65 +301,6 @@ - (void)handleSessionStateChangesInBackgroundFor:(MXSession *)mxSession } } -//#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 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 // iOS 10+, see application:handleActionWithIdentifier:forLocalNotification:withResponseInfo:completionHandler: @@ -442,15 +356,6 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNoti } } -// 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); -// completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert); -//} - #pragma mark - Other Methods - (void)launchBackgroundSync diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index de1601f300..9ec816b3bf 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -87,8 +87,6 @@ enum { - //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 +1729,6 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N } 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) { MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; From b1a654dd0d601b50892a86ff5f22a7923f9cd66f Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 26 May 2020 18:15:44 +0300 Subject: [PATCH 16/27] Change logs for fallback cases Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 455047b010..acd9c066c9 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -47,7 +47,7 @@ class NotificationService: UNNotificationServiceExtension { guard roomId != nil, eventId != nil else { // it's not a Matrix notification, do not change the content - NSLog("[NotificationService] Fallback case 7") + NSLog("[NotificationService] didReceiveRequest: This is not a Matrix notification.") contentHandler(content) return } @@ -62,7 +62,7 @@ class NotificationService: UNNotificationServiceExtension { 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") + NSLog("[NotificationService] serviceExtensionTimeWillExpire") fallbackToOriginalContent() } @@ -93,7 +93,7 @@ class NotificationService: UNNotificationServiceExtension { func fetchEvent() { guard let content = originalContent, let userAccount = self.userAccount else { // there is something wrong, do not change the content - NSLog("[NotificationService] Fallback case 4") + NSLog("[NotificationService] fetchEvent: Either originalContent or userAccount is missing.") fallbackToOriginalContent() return } @@ -101,14 +101,14 @@ class NotificationService: UNNotificationServiceExtension { guard let roomId = userInfo["room_id"] as? String, let eventId = userInfo["event_id"] as? String else { // it's not a Matrix notification, do not change the content - NSLog("[NotificationService] Fallback case 1") + NSLog("[NotificationService] fetchEvent: This is not a Matrix notification.") contentHandler?(content) return } userAccount.mxSession.event(withEventId: eventId, inRoom: roomId, success: { [weak self] (event) in guard let self = self else { - NSLog("[NotificationService] Fallback case 9") + NSLog("[NotificationService] fetchEvent: MXSession.event method returned too late successfully.") return } @@ -147,10 +147,10 @@ class NotificationService: UNNotificationServiceExtension { } }) { [weak self] (error) in guard let self = self else { - NSLog("[NotificationService] Fallback case 10") + NSLog("[NotificationService] fetchEvent: MXSession.event method returned too late with error: \(String(describing: error))"); return } - NSLog("[NotificationService] Fallback case 3") + NSLog("[NotificationService] fetchEvent: MXSession.event method returned error: \(String(describing: error))"); self.fallbackToOriginalContent() } } @@ -165,16 +165,16 @@ class NotificationService: UNNotificationServiceExtension { // launch an initial background sync userAccount.initialBackgroundSync(20000, success: { [weak self] in guard let self = self else { - NSLog("[NotificationService] Fallback case 12") + NSLog("[NotificationService] launchBackgroundSync: MXKAccount.initialBackgroundSync returned too late successfully") return } self.fetchEvent() }) { [weak self] (error) in guard let self = self else { - NSLog("[NotificationService] Fallback case 11") + NSLog("[NotificationService] launchBackgroundSync: MXKAccount.initialBackgroundSync returned too late with error: \(String(describing: error))") return } - NSLog("[NotificationService] Fallback case 6") + NSLog("[NotificationService] launchBackgroundSync: MXKAccount.initialBackgroundSync returned with error: \(String(describing: error))") self.fallbackToOriginalContent() } } @@ -209,7 +209,7 @@ class NotificationService: UNNotificationServiceExtension { func fallbackToOriginalContent() { store?.close() guard let content = originalContent else { - NSLog("[NotificationService] Fallback case 13") + NSLog("[NotificationService] fallbackToOriginalContent: Original content is missing.") return } From 69d9b5bd9a69c49a5078894b7061d3eb6d54c39b Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 26 May 2020 18:21:30 +0300 Subject: [PATCH 17/27] Add fallback method calls on some points Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index acd9c066c9..9ae7da8adc 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -113,6 +113,7 @@ class NotificationService: UNNotificationServiceExtension { } guard let event = event else { + self.fallbackToOriginalContent() return } @@ -156,8 +157,14 @@ class NotificationService: UNNotificationServiceExtension { } func launchBackgroundSync() { - guard let userAccount = userAccount else { return } - guard let store = store else { return } + guard let userAccount = userAccount else { + self.fallbackToOriginalContent() + return + } + guard let store = store else { + self.fallbackToOriginalContent() + return + } if userAccount.mxSession == nil { userAccount.openSession(with: store) } @@ -181,6 +188,7 @@ class NotificationService: UNNotificationServiceExtension { func processEvent(_ event: MXEvent) { guard let content = originalContent, let userAccount = userAccount else { + self.fallbackToOriginalContent() return } From c4bdf33d8df60fd63a87e2eaab0384bb622ca32f Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 26 May 2020 18:22:05 +0300 Subject: [PATCH 18/27] Remove some redundant semicolons Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 9ae7da8adc..0714f5a1cb 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -148,10 +148,10 @@ class NotificationService: UNNotificationServiceExtension { } }) { [weak self] (error) in guard let self = self else { - NSLog("[NotificationService] fetchEvent: MXSession.event method returned too late with error: \(String(describing: error))"); + NSLog("[NotificationService] fetchEvent: MXSession.event method returned too late with error: \(String(describing: error))") return } - NSLog("[NotificationService] fetchEvent: MXSession.event method returned error: \(String(describing: error))"); + NSLog("[NotificationService] fetchEvent: MXSession.event method returned error: \(String(describing: error))") self.fallbackToOriginalContent() } } From fa44dae328322437bf506e0d9ed576d93aaff839 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 26 May 2020 18:23:47 +0300 Subject: [PATCH 19/27] Update RiotNSE/NotificationService.swift Co-authored-by: manuroe --- RiotNSE/NotificationService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 0714f5a1cb..5263a48933 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -117,7 +117,7 @@ class NotificationService: UNNotificationServiceExtension { return } - guard event.isEncrypted else { + if !event.isEncrypted { // not encrypted, go on processing self.processEvent(event) return From a0e95407fa074fd696e6f31ec5f7ea47531ce091 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 26 May 2020 18:24:21 +0300 Subject: [PATCH 20/27] Update RiotNSE/NotificationService.swift Co-authored-by: manuroe --- RiotNSE/NotificationService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 5263a48933..41adda53f8 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -124,7 +124,7 @@ class NotificationService: UNNotificationServiceExtension { } // encrypted - guard self.showDecryptedContentInNotifications else { + if !self.showDecryptedContentInNotifications { // do not show decrypted content in notification self.fallbackToOriginalContent() return From f5324aed9cfe939eb99ee0a1057432e577686bb4 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 26 May 2020 18:26:00 +0300 Subject: [PATCH 21/27] Update RiotNSE/NotificationService.swift Co-authored-by: manuroe --- RiotNSE/NotificationService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 41adda53f8..15b5bfecd8 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -267,7 +267,7 @@ class NotificationService: UNNotificationServiceExtension { } } - guard isHighlighted else { + if !isHighlighted { // Ignore this notif. NSLog("[NotificationService][Push] notificationBodyForEvent: Ignore non highlighted notif in mentions only room") onComplete(nil) From 3bd9e6bb3a6463ebcad9cdd0c246f25440116601 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 26 May 2020 18:28:08 +0300 Subject: [PATCH 22/27] Change some guarding to if check Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 15b5bfecd8..04a671a6e1 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -131,7 +131,7 @@ class NotificationService: UNNotificationServiceExtension { } // should show decrypted content in notification - guard event.clear == nil else { + if event.clear != nil { // already decrypted self.processEvent(event) return From bcd96835115550b3b0f2f82b9094ce303d223a12 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 27 May 2020 18:34:04 +0300 Subject: [PATCH 23/27] Implement a memory store specific to NSE Signed-off-by: ismailgulek --- Riot.xcodeproj/project.pbxproj | 4 ++ RiotNSE/NSEMemoryStore.swift | 83 ++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 RiotNSE/NSEMemoryStore.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 5439f998d1..eadc3739aa 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -714,6 +714,7 @@ EC85D752247C0F52002C44C9 /* UNUserNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC85D750247C0E8F002C44C9 /* UNUserNotificationCenter.swift */; }; EC85D754247C0F5B002C44C9 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC85D753247C0F5B002C44C9 /* Constants.swift */; }; EC85D755247C0F84002C44C9 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC85D753247C0F5B002C44C9 /* Constants.swift */; }; + EC85D757247E700F002C44C9 /* NSEMemoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC85D756247E700F002C44C9 /* NSEMemoryStore.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 */; }; @@ -1725,6 +1726,7 @@ EC85D74E2477E614002C44C9 /* RiotNSE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RiotNSE.entitlements; sourceTree = ""; }; EC85D750247C0E8F002C44C9 /* UNUserNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNUserNotificationCenter.swift; sourceTree = ""; }; EC85D753247C0F5B002C44C9 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + EC85D756247E700F002C44C9 /* NSEMemoryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEMemoryStore.swift; 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 = ""; }; @@ -4288,6 +4290,7 @@ children = ( EC85D74E2477E614002C44C9 /* RiotNSE.entitlements */, EC85D7452477E5F7002C44C9 /* NotificationService.swift */, + EC85D756247E700F002C44C9 /* NSEMemoryStore.swift */, EC85D7472477E5F7002C44C9 /* Info.plist */, ); path = RiotNSE; @@ -5106,6 +5109,7 @@ buildActionMask = 2147483647; files = ( EC85D74F2477E8EB002C44C9 /* RiotSettings.swift in Sources */, + EC85D757247E700F002C44C9 /* NSEMemoryStore.swift in Sources */, EC85D7462477E5F7002C44C9 /* NotificationService.swift in Sources */, EC85D752247C0F52002C44C9 /* UNUserNotificationCenter.swift in Sources */, EC85D755247C0F84002C44C9 /* Constants.swift in Sources */, diff --git a/RiotNSE/NSEMemoryStore.swift b/RiotNSE/NSEMemoryStore.swift new file mode 100644 index 0000000000..f5610d200a --- /dev/null +++ b/RiotNSE/NSEMemoryStore.swift @@ -0,0 +1,83 @@ +/* + 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 Foundation +import MatrixSDK + +class NSEMemoryStore: MXMemoryStore { + + private var credentials: MXCredentials + private var fileStore: MXFileStore + + init(withCredentials credentials: MXCredentials) { + self.credentials = credentials + fileStore = MXFileStore(credentials: credentials) + fileStore.loadMetaData() + } + + // Return real eventStreamToken, to be able to launch a meaningful background sync + override var eventStreamToken: String? { + get { + return fileStore.eventStreamToken + } set { + // no-op + } + } + + // Return real userAccountData, to be able to use push rules + override var userAccountData: [AnyHashable : Any]? { + get { + return fileStore.userAccountData + } set { + // no-op + } + } + + // This store should act like as a permanent one + override var isPermanent: Bool { + return true + } + + // Some mandatory methods to implement to be permanent + override func storeState(forRoom roomId: String, stateEvents: [MXEvent]) { + + } + + override func state(ofRoom roomId: String, success: @escaping ([MXEvent]) -> Void, failure: ((Error) -> Void)? = nil) { + + } + + override func summary(ofRoom roomId: String) -> MXRoomSummary? { + return fileStore.summary(ofRoom: roomId) + } + + override func accountData(ofRoom roomId: String) -> MXRoomAccountData? { + return fileStore.accountData(ofRoom: roomId) + } + + // Override and return a user to be stored on session.myUser + override func user(withUserId userId: String) -> MXUser? { + if userId == credentials.userId { + return MXMyUser(userId: userId) + } + return MXUser(userId: userId) + } + + override func close() { + fileStore.close() + } + +} From d859ce8a43f71af1bc25d1913fa5df1a6ea6a9ad Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 27 May 2020 18:54:24 +0300 Subject: [PATCH 24/27] Drop MXKAccount and MXFileStore usages Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 123 +++++++++++++++++------------- 1 file changed, 69 insertions(+), 54 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 04a671a6e1..922fcfbbe9 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -22,8 +22,8 @@ class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var originalContent: UNMutableNotificationContent? - var userAccount: MXKAccount? - var store: MXFileStore? + var mxSession: MXSession? + var store: NSEMemoryStore! var showDecryptedContentInNotifications: Bool { return RiotSettings.shared.showDecryptedContentInNotifications } @@ -42,10 +42,8 @@ class NotificationService: UNNotificationServiceExtension { let userInfo = content.userInfo NSLog("[NotificationService] Payload came: \(userInfo)") - let roomId = userInfo["room_id"] as? String - let eventId = userInfo["event_id"] as? String - guard roomId != nil, eventId != nil else { + guard let roomId = userInfo["room_id"] as? String, let _ = userInfo["event_id"] as? String else { // it's not a Matrix notification, do not change the content NSLog("[NotificationService] didReceiveRequest: This is not a Matrix notification.") contentHandler(content) @@ -53,10 +51,10 @@ class NotificationService: UNNotificationServiceExtension { } // setup user account - setup() - - // fetch the event first - fetchEvent() + setup(withRoomId: roomId) { + // fetch the event first + self.fetchEvent() + } } override func serviceExtensionTimeWillExpire() { @@ -66,7 +64,7 @@ class NotificationService: UNNotificationServiceExtension { fallbackToOriginalContent() } - func setup() { + func setup(withRoomId roomId: String, completion: @escaping () -> Void) { let sdkOptions = MXSDKOptions.sharedInstance() sdkOptions.applicationGroupIdentifier = "group.im.vector" sdkOptions.disableIdenticonUseForUserAvatar = true @@ -79,21 +77,30 @@ class NotificationService: UNNotificationServiceExtension { MXLogger.redirectNSLog(toFiles: true) } - userAccount = MXKAccountManager.shared()?.activeAccounts.first - - if let userAccount = userAccount { - store = MXFileStore(credentials: userAccount.mxCredentials) + if let userAccount = MXKAccountManager.shared()?.activeAccounts.first { + store = NSEMemoryStore(withCredentials: userAccount.mxCredentials) + store.open(with: userAccount.mxCredentials, onComplete: nil, failure: nil) + store.getOrCreateRoomStore(roomId) - if userAccount.mxSession == nil { - userAccount.openSession(with: store!) - } + mxSession = MXSession(matrixRestClient: MXRestClient(credentials: userAccount.mxCredentials, unrecognizedCertificateHandler: nil)) + mxSession?.setStore(store, completion: { (response) in + switch response { + case .success: + completion() + break + case .failure(let error): + NSLog("[NotificationService] setup: MXSession.setStore method returned error: \(String(describing: error))") + self.fallbackToOriginalContent() + break + } + }) } } func fetchEvent() { - guard let content = originalContent, let userAccount = self.userAccount else { + guard let content = originalContent, let mxSession = mxSession else { // there is something wrong, do not change the content - NSLog("[NotificationService] fetchEvent: Either originalContent or userAccount is missing.") + NSLog("[NotificationService] fetchEvent: Either originalContent or mxSession is missing.") fallbackToOriginalContent() return } @@ -106,7 +113,7 @@ class NotificationService: UNNotificationServiceExtension { return } - userAccount.mxSession.event(withEventId: eventId, inRoom: roomId, success: { [weak self] (event) in + mxSession.event(withEventId: eventId, inRoom: roomId, success: { [weak self] (event) in guard let self = self else { NSLog("[NotificationService] fetchEvent: MXSession.event method returned too late successfully.") return @@ -138,7 +145,7 @@ class NotificationService: UNNotificationServiceExtension { } // should decrypt it first - if userAccount.mxSession.decryptEvent(event, inTimeline: nil) { + if mxSession.decryptEvent(event, inTimeline: nil) { // decryption succeeded self.processEvent(event) } else { @@ -157,44 +164,43 @@ class NotificationService: UNNotificationServiceExtension { } func launchBackgroundSync() { - guard let userAccount = userAccount else { - self.fallbackToOriginalContent() - return - } - guard let store = store else { + guard let mxSession = mxSession else { + NSLog("[NotificationService] launchBackgroundSync: mxSession is missing.") self.fallbackToOriginalContent() return } - if userAccount.mxSession == nil { - userAccount.openSession(with: store) - } // launch an initial background sync - userAccount.initialBackgroundSync(20000, success: { [weak self] in - guard let self = self else { - NSLog("[NotificationService] launchBackgroundSync: MXKAccount.initialBackgroundSync returned too late successfully") - return - } - self.fetchEvent() - }) { [weak self] (error) in - guard let self = self else { - NSLog("[NotificationService] launchBackgroundSync: MXKAccount.initialBackgroundSync returned too late with error: \(String(describing: error))") - return + mxSession.initialBackgroundSync(withTimeout: 20000) { [weak self] (response) in + switch response { + case .success: + guard let self = self else { + NSLog("[NotificationService] launchBackgroundSync: MXSession.initialBackgroundSync returned too late successfully") + return + } + self.fetchEvent() + break + case .failure(let error): + guard let self = self else { + NSLog("[NotificationService] launchBackgroundSync: MXSession.initialBackgroundSync returned too late with error: \(String(describing: error))") + return + } + NSLog("[NotificationService] launchBackgroundSync: MXSession.initialBackgroundSync returned with error: \(String(describing: error))") + self.fallbackToOriginalContent() + break } - NSLog("[NotificationService] launchBackgroundSync: MXKAccount.initialBackgroundSync returned with error: \(String(describing: error))") - self.fallbackToOriginalContent() } } func processEvent(_ event: MXEvent) { - guard let content = originalContent, let userAccount = userAccount else { + guard let content = originalContent, let mxSession = mxSession else { self.fallbackToOriginalContent() return } - self.notificationContent(forEvent: event, inAccount: userAccount) { (notificationContent) in + self.notificationContent(forEvent: event, inSession: mxSession) { (notificationContent) in // close store - self.store?.close() + self.store.close() // Modify the notification content here... if let newContent = notificationContent { @@ -215,7 +221,7 @@ class NotificationService: UNNotificationServiceExtension { } func fallbackToOriginalContent() { - store?.close() + store.close() guard let content = originalContent else { NSLog("[NotificationService] fallbackToOriginalContent: Original content is missing.") return @@ -225,28 +231,37 @@ class NotificationService: UNNotificationServiceExtension { contentHandler?(content) } - func notificationContent(forEvent event: MXEvent, inAccount account: MXKAccount, onComplete: @escaping (UNNotificationContent?) -> Void) { + func notificationContent(forEvent event: MXEvent, inSession session: MXSession, onComplete: @escaping (UNNotificationContent?) -> Void) { guard let content = event.content, content.count > 0 else { NSLog("[NotificationService][Push] notificationContentForEvent: empty event content") onComplete(nil) return } - guard let room = account.mxSession.room(withRoomId: event.roomId) else { + guard let room = MXRoom(roomId: event.roomId, matrixSession: session, andStore: store) else { NSLog("[NotificationService][Push] notificationBodyForEvent: Unknown room") onComplete(nil) return } + let pushRule = room.getRoomPushRule() - room.state({ (roomState: MXRoomState!) in - + // initialize a temporary file store, just to load the room state + let fileStore = MXFileStore(credentials: session.credentials) + + MXRoomState.load(from: fileStore, withRoomId: event.roomId, matrixSession: session) { (roomState) in + guard let roomState = roomState else { + NSLog("[NotificationService] notificationContentForEvent: Could not load the room state") + onComplete(nil) + return + } + var notificationTitle: String? var notificationBody: String? var threadIdentifier = room.roomId let eventSenderName = roomState.members.memberName(event.sender) - let currentUserId = account.mxCredentials.userId + let currentUserId = session.credentials.userId switch event.eventType { case .roomMessage, .roomEncrypted: @@ -284,7 +299,7 @@ class NotificationService: UNNotificationServiceExtension { } let roomDisplayName = room.summary.displayname - let myUserId = account.mxSession.myUser.userId + let myUserId = session.myUser.userId let isIncomingEvent = event.sender != myUserId // Display the room name only if it is different than the sender name @@ -298,8 +313,8 @@ class NotificationService: UNNotificationServiceExtension { } else if msgType == kMXMessageTypeImage { 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 + session.crypto.keyVerificationManager.keyVerification(fromKeyVerificationEvent: event, + success:{ (keyVerification) in guard let request = keyVerification.request, request.state == MXKeyVerificationRequestStatePending else { onComplete(nil) return @@ -391,7 +406,7 @@ class NotificationService: UNNotificationServiceExtension { pushRule: pushRule) onComplete(notificationContent) - }) + } } func notificationContent(withTitle title: String?, From 845363b404d82b005fc24d1b7e0a1f60159eea9d Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 29 May 2020 09:55:58 +0300 Subject: [PATCH 25/27] Fix Manu's comments Signed-off-by: ismailgulek --- RiotNSE/NSEMemoryStore.swift | 2 +- RiotNSE/NotificationService.swift | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/RiotNSE/NSEMemoryStore.swift b/RiotNSE/NSEMemoryStore.swift index f5610d200a..66322e50af 100644 --- a/RiotNSE/NSEMemoryStore.swift +++ b/RiotNSE/NSEMemoryStore.swift @@ -57,7 +57,7 @@ class NSEMemoryStore: MXMemoryStore { } override func state(ofRoom roomId: String, success: @escaping ([MXEvent]) -> Void, failure: ((Error) -> Void)? = nil) { - + fileStore.state(ofRoom: roomId, success: success, failure: failure) } override func summary(ofRoom roomId: String) -> MXRoomSummary? { diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 922fcfbbe9..fb9805eb88 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -79,7 +79,9 @@ class NotificationService: UNNotificationServiceExtension { if let userAccount = MXKAccountManager.shared()?.activeAccounts.first { store = NSEMemoryStore(withCredentials: userAccount.mxCredentials) - store.open(with: userAccount.mxCredentials, onComplete: nil, failure: nil) + // Fake roomStores in memory store. This is for both -[MXMemoryStore rooms] and -[MXSession rooms] to return some rooms. + // Also roomsSummaries will be filled with the room summary for this roomId. + // They all will be used afterwards. store.getOrCreateRoomStore(roomId) mxSession = MXSession(matrixRestClient: MXRestClient(credentials: userAccount.mxCredentials, unrecognizedCertificateHandler: nil)) @@ -237,8 +239,7 @@ class NotificationService: UNNotificationServiceExtension { onComplete(nil) return } - - guard let room = MXRoom(roomId: event.roomId, matrixSession: session, andStore: store) else { + guard let room = session.room(withRoomId: event.roomId) else { NSLog("[NotificationService][Push] notificationBodyForEvent: Unknown room") onComplete(nil) return @@ -246,10 +247,7 @@ class NotificationService: UNNotificationServiceExtension { let pushRule = room.getRoomPushRule() - // initialize a temporary file store, just to load the room state - let fileStore = MXFileStore(credentials: session.credentials) - - MXRoomState.load(from: fileStore, withRoomId: event.roomId, matrixSession: session) { (roomState) in + room.state { (roomState) in guard let roomState = roomState else { NSLog("[NotificationService] notificationContentForEvent: Could not load the room state") onComplete(nil) From 4ef5e178c26370bfbb61acbb632c06c2564d6a3d Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 29 May 2020 09:56:44 +0300 Subject: [PATCH 26/27] Add some comments Signed-off-by: ismailgulek --- RiotNSE/NSEMemoryStore.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/RiotNSE/NSEMemoryStore.swift b/RiotNSE/NSEMemoryStore.swift index 66322e50af..8bbe923dc7 100644 --- a/RiotNSE/NSEMemoryStore.swift +++ b/RiotNSE/NSEMemoryStore.swift @@ -17,14 +17,17 @@ import Foundation import MatrixSDK +/// Fake memory store implementation. Uses some real values from an MXFileStore instance. class NSEMemoryStore: MXMemoryStore { private var credentials: MXCredentials + // real store private var fileStore: MXFileStore init(withCredentials credentials: MXCredentials) { self.credentials = credentials fileStore = MXFileStore(credentials: credentials) + // load real eventStreamToken fileStore.loadMetaData() } @@ -53,17 +56,20 @@ class NSEMemoryStore: MXMemoryStore { // Some mandatory methods to implement to be permanent override func storeState(forRoom roomId: String, stateEvents: [MXEvent]) { - + // no-op } + // Fetch real room state override func state(ofRoom roomId: String, success: @escaping ([MXEvent]) -> Void, failure: ((Error) -> Void)? = nil) { fileStore.state(ofRoom: roomId, success: success, failure: failure) } + // Fetch real soom summary override func summary(ofRoom roomId: String) -> MXRoomSummary? { return fileStore.summary(ofRoom: roomId) } + // Fetch real room account data override func accountData(ofRoom roomId: String) -> MXRoomAccountData? { return fileStore.accountData(ofRoom: roomId) } @@ -77,6 +83,7 @@ class NSEMemoryStore: MXMemoryStore { } override func close() { + // close real store fileStore.close() } From 0b639e8f814f6b08f68a4ebce0a1329e9bf01d35 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 29 May 2020 11:26:43 +0300 Subject: [PATCH 27/27] Adapt to new method, change timeout to seconds Signed-off-by: ismailgulek --- RiotNSE/NotificationService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index fb9805eb88..e70fd31920 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -173,7 +173,7 @@ class NotificationService: UNNotificationServiceExtension { } // launch an initial background sync - mxSession.initialBackgroundSync(withTimeout: 20000) { [weak self] (response) in + mxSession.backgroundSync(withTimeout: 20, ignoreSessionState: true) { [weak self] (response) in switch response { case .success: guard let self = self else {