Merge branch 'release/1.1.3/master'

This commit is contained in:
ismailgulek 2020-12-16 19:00:51 +03:00
commit d2dec60dfd
10 changed files with 227 additions and 520 deletions

View file

@ -1,3 +1,30 @@
Changes in 1.1.3 (2020-12-16)
=================================================
✨ Features
*
🙌 Improvements
* AuthVC: Update SSO button wording.
🐛 Bugfix
* Refresh account details on NSE runs (#3719).
⚠️ API Changes
*
🗣 Translations
*
🧱 Build
*
Others
*
Improvements:
* Upgrade MatrixKit version ([v0.13.3](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.13.3)).
Changes in 1.1.2 (2020-12-02)
=================================================
@ -7,6 +34,7 @@ Changes in 1.1.2 (2020-12-02)
🙌 Improvements
* Room History: Remove the report option for outgoing messages.
* Empty views: Add empty screen when there is nothing to display on home, people, favourites and rooms screen (#3836).
* BuildSettings.messageDetailsAllowShare now hide /show action button in document preview (#3864).
🐛 Bugfix
* Restore the modular widget events in the rooms histories.
@ -105,6 +133,7 @@ Changes in 1.0.18 (2020-10-27)
* Update MatomoTracker to 7.2.2 (#3570).
* Update SwiftGen to 6.3.0 (#3570).
* Update SwiftLint to 0.40.3 (#3570).
* NSE: Utilize MXBackgroundService on pushes, to make messages available when the app is foregrounded (#3579).
🐛 Bugfix
* Fix typos in UI
@ -133,7 +162,7 @@ Changes in 1.0.17 (2020-10-14)
🙌 Improvements
* Device verification: Do not check for existing key backup after SSSS & Cross-Signing reset.
* Cross-signing: Detect when cross-signing keys have been changed.
* Make copying & pasting media configurable.
* Make copying & pasting media configurable.
🐛 Bugfix
*

View file

@ -11,7 +11,7 @@ use_frameworks!
# - `{ {kit spec hash} => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for each repo. Used by Fastfile during CI
#
# Warning: our internal tooling depends on the name of this variable name, so be sure not to change it
$matrixKitVersion = '= 0.13.2'
$matrixKitVersion = '= 0.13.3'
# $matrixKitVersion = :local
# $matrixKitVersion = {'develop' => 'develop'}
@ -35,7 +35,6 @@ end
# Method to import the right MatrixKit flavour
def import_MatrixKit
pod 'MatrixSDK', $matrixSDKVersionSpec
pod 'MatrixSDK/SwiftSupport', $matrixSDKVersionSpec
pod 'MatrixSDK/JingleCallStack', $matrixSDKVersionSpec
pod 'MatrixKit', $matrixKitVersionSpec
end
@ -43,7 +42,6 @@ end
# Method to import the right MatrixKit/AppExtension flavour
def import_MatrixKitAppExtension
pod 'MatrixSDK', $matrixSDKVersionSpec
pod 'MatrixSDK/SwiftSupport', $matrixSDKVersionSpec
pod 'MatrixKit/AppExtension', $matrixKitVersionSpec
end

View file

@ -60,39 +60,37 @@ PODS:
- MatomoTracker (7.2.2):
- MatomoTracker/Core (= 7.2.2)
- MatomoTracker/Core (7.2.2)
- MatrixKit (0.13.2):
- MatrixKit (0.13.3):
- Down (~> 0.9.3)
- DTCoreText (~> 1.6.23)
- HPGrowingTextView (~> 1.1)
- libPhoneNumber-iOS (~> 0.9.13)
- MatrixKit/Core (= 0.13.2)
- MatrixSDK (= 0.17.4)
- MatrixKit/AppExtension (0.13.2):
- MatrixKit/Core (= 0.13.3)
- MatrixSDK (= 0.17.5)
- MatrixKit/AppExtension (0.13.3):
- Down (~> 0.9.3)
- DTCoreText (~> 1.6.23)
- DTCoreText/Extension
- HPGrowingTextView (~> 1.1)
- libPhoneNumber-iOS (~> 0.9.13)
- MatrixSDK (= 0.17.4)
- MatrixKit/Core (0.13.2):
- MatrixSDK (= 0.17.5)
- MatrixKit/Core (0.13.3):
- Down (~> 0.9.3)
- DTCoreText (~> 1.6.23)
- HPGrowingTextView (~> 1.1)
- libPhoneNumber-iOS (~> 0.9.13)
- MatrixSDK (= 0.17.4)
- MatrixSDK (0.17.4):
- MatrixSDK/Core (= 0.17.4)
- MatrixSDK/Core (0.17.4):
- MatrixSDK (= 0.17.5)
- MatrixSDK (0.17.5):
- MatrixSDK/Core (= 0.17.5)
- MatrixSDK/Core (0.17.5):
- AFNetworking (~> 4.0.0)
- GZIP (~> 1.3.0)
- libbase58 (~> 0.1.4)
- OLMKit (~> 3.1.0)
- Realm (= 10.1.4)
- MatrixSDK/JingleCallStack (0.17.4):
- MatrixSDK/JingleCallStack (0.17.5):
- JitsiMeetSDK (= 2.11.0)
- MatrixSDK/Core
- MatrixSDK/SwiftSupport (0.17.4):
- MatrixSDK/Core
- OLMKit (3.1.0):
- OLMKit/olmc (= 3.1.0)
- OLMKit/olmcpp (= 3.1.0)
@ -129,11 +127,10 @@ DEPENDENCIES:
- KeychainAccess (~> 4.2.1)
- KTCenterFlowLayout (~> 1.3.1)
- MatomoTracker (~> 7.2.2)
- MatrixKit (= 0.13.2)
- MatrixKit/AppExtension (= 0.13.2)
- MatrixKit (= 0.13.3)
- MatrixKit/AppExtension (= 0.13.3)
- MatrixSDK
- MatrixSDK/JingleCallStack
- MatrixSDK/SwiftSupport
- OLMKit
- ReadMoreTextView (~> 3.0.1)
- Reusable (~> 4.1)
@ -202,8 +199,8 @@ SPEC CHECKSUMS:
LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d
Logging: beeb016c9c80cf77042d62e83495816847ef108b
MatomoTracker: a59ec4da0f580be57bdc6baa708a71a86532a832
MatrixKit: 411348d4690b414e18958a0437c13edc21054a18
MatrixSDK: 39282219213aebf621326f6335073b5a2b1e9113
MatrixKit: 86f5239fab112ab0d3714fd1f366916ebd4d2b25
MatrixSDK: aadf483438804d9e93d5d46ad3e5e832b7bb8848
OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
Realm: 80f4fb2971ccb9adc27a47d0955ae8e533a7030b
@ -215,6 +212,6 @@ SPEC CHECKSUMS:
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
PODFILE CHECKSUM: 5f40ae970fc6a939c548fedf78849358a895c315
PODFILE CHECKSUM: 22fcc5047435925034265c61cd821507736bf492
COCOAPODS: 1.10.0

View file

@ -895,7 +895,6 @@
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 */; };
EC9A3EC524E1616900A8CFAE /* PushNotificationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC9A3EC424E1616900A8CFAE /* PushNotificationStore.swift */; };
EC9A3EC624E1632C00A8CFAE /* PushNotificationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC9A3EC424E1616900A8CFAE /* PushNotificationStore.swift */; };
EC9A3EC724E1634100A8CFAE /* KeyValueStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1CA87124C823E700DE9EBF /* KeyValueStore.swift */; };
@ -2137,7 +2136,6 @@
EC85D74E2477E614002C44C9 /* RiotNSE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RiotNSE.entitlements; sourceTree = "<group>"; };
EC85D750247C0E8F002C44C9 /* UNUserNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNUserNotificationCenter.swift; sourceTree = "<group>"; };
EC85D753247C0F5B002C44C9 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
EC85D756247E700F002C44C9 /* NSEMemoryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEMemoryStore.swift; sourceTree = "<group>"; };
EC9A3EC424E1616900A8CFAE /* PushNotificationStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushNotificationStore.swift; sourceTree = "<group>"; };
ECAE7AE424EC0E01002FA813 /* TableViewSections.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewSections.swift; sourceTree = "<group>"; };
ECAE7AE624EC15F7002FA813 /* Section.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = "<group>"; };
@ -5187,7 +5185,6 @@
children = (
EC85D74E2477E614002C44C9 /* RiotNSE.entitlements */,
EC85D7452477E5F7002C44C9 /* NotificationService.swift */,
EC85D756247E700F002C44C9 /* NSEMemoryStore.swift */,
EC85D7472477E5F7002C44C9 /* Info.plist */,
EC1CA8D924D811B400DE9EBF /* NSE-Common.xcconfig */,
EC1CA8BC24D1B4CF00DE9EBF /* NSE-Debug.xcconfig */,
@ -6213,7 +6210,6 @@
files = (
EC85D74F2477E8EB002C44C9 /* RiotSettings.swift in Sources */,
32FD756824D2AD5100BA7B37 /* BuildSettings.swift in Sources */,
EC85D757247E700F002C44C9 /* NSEMemoryStore.swift in Sources */,
EC2B4EF224A1EF34005EB739 /* DataProtectionHelper.swift in Sources */,
EC85D7462477E5F7002C44C9 /* NotificationService.swift in Sources */,
32FD755424D074C700BA7B37 /* CommonConfiguration.swift in Sources */,
@ -7351,7 +7347,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.1.2;
CURRENT_PROJECT_VERSION = 1.1.3;
DEFINES_MODULE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@ -7371,7 +7367,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 1.1.2;
MARKETING_VERSION = 1.1.3;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -7410,7 +7406,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
COPY_PHASE_STRIP = YES;
CURRENT_PROJECT_VERSION = 1.1.2;
CURRENT_PROJECT_VERSION = 1.1.3;
DEFINES_MODULE = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@ -7423,7 +7419,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 1.1.2;
MARKETING_VERSION = 1.1.3;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;

View file

@ -72,7 +72,7 @@
"auth_register" = "Register";
"auth_submit" = "Submit";
"auth_skip" = "Skip";
"auth_login_single_sign_on" = "Sign in with single sign-on";
"auth_login_single_sign_on" = "Sign In";
"auth_send_reset_email" = "Send Reset Email";
"auth_return_to_login" = "Return to login screen";
"auth_user_id_placeholder" = "Email or user name";

View file

@ -114,7 +114,7 @@ internal enum VectorL10n {
internal static var authLogin: String {
return VectorL10n.tr("Vector", "auth_login")
}
/// Sign in with single sign-on
/// Sign In
internal static var authLoginSingleSignOn: String {
return VectorL10n.tr("Vector", "auth_login_single_sign_on")
}

View file

@ -617,8 +617,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
}
_isAppForeground = YES;
[self configurePinCodeScreenFor:application createIfRequired:NO];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
@ -1109,9 +1107,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
- (void)pushNotificationService:(PushNotificationService *)pushNotificationService shouldNavigateToRoomWithId:(NSString *)roomId
{
[MXSDKOptions.sharedInstance.profiler startMeasuringTaskWithName:AnalyticsNoficationsTimeToDisplayContent
category:AnalyticsNoficationsCategory];
_lastNavigatedRoomIdFromPush = roomId;
[self navigateToRoomById:roomId];
}

View file

@ -294,6 +294,9 @@
// Listen to the event sent state changes
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidChangeSentState:) name:kMXEventDidChangeSentStateNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidChangeIdentifier:) name:kMXEventDidChangeIdentifierNotification object:nil];
// Show / hide actions button in document preview according BuildSettings
self.allowActionsInDocumentPreview = BuildSettings.messageDetailsAllowShare;
}
- (void)viewDidLoad

View file

@ -1,133 +0,0 @@
/*
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
// error domain
let NSEMemoryStoreErrorDomain: String = "NSEMemoryStoreErrorDomain"
// error codes
enum NSEMemoryStoreErrorCode: Int {
case userIDMissing = 1001 // User ID is missing in credentials
}
/// Fake memory store implementation. Uses some real values from an MXFileStore instance.
class NSEMemoryStore: MXMemoryStore {
// In-memory value for eventStreamToken. Will be used as eventStreamToken if provided.
private var lastStoredEventStreamToken: String?
private var credentials: MXCredentials
// real store
private var fileStore: MXFileStore!
private var myUser: MXUser?
init(withCredentials credentials: MXCredentials) {
self.credentials = credentials
if fileStore == nil {
fileStore = MXFileStore(credentials: credentials)
// load real eventStreamToken
fileStore.loadMetaData()
}
}
override func open(with credentials: MXCredentials, onComplete: (() -> Void)?, failure: ((Error?) -> Void)? = nil) {
super.open(with: credentials, onComplete: {
guard let userId = credentials.userId else {
failure?(NSError(domain: NSEMemoryStoreErrorDomain,
code: NSEMemoryStoreErrorCode.userIDMissing.rawValue,
userInfo: nil))
return
}
// load session user before calling onComplete
self.fileStore.asyncUsers(withUserIds: [userId], success: { (users) in
if let user = users.first {
self.myUser = user
}
onComplete?()
}, failure: failure)
}, failure: failure)
}
// Return real eventStreamToken, to be able to launch a meaningful background sync
override var eventStreamToken: String? {
get {
// if more up-to-date token exists, use it
if let token = lastStoredEventStreamToken {
return token
}
return fileStore.eventStreamToken
} set {
// store new token values in memory, and return these values in future reads
lastStoredEventStreamToken = newValue
}
}
// 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]) {
// 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)
}
// Override and return a user to be stored on session.myUser
override func user(withUserId userId: String) -> MXUser? {
if userId == credentials.userId, let myUser = myUser {
// if asking for session user and myUser is set, return that
return myUser
}
return MXUser(userId: userId)
}
override var syncFilterId: String? {
get {
let filter = MXFilterJSONModel()
filter.room = MXRoomFilter()
filter.room.rooms = []
return filter.jsonString()
} set {
// no-op
}
}
}

View file

@ -21,21 +21,21 @@ import MatrixSDK
class NotificationService: UNNotificationServiceExtension {
/// Content handlers. Keys are eventId's
var contentHandlers: [String: ((UNNotificationContent) -> Void)] = [:]
private var contentHandlers: [String: ((UNNotificationContent) -> Void)] = [:]
private var userAccount: MXKAccount?
/// Best attempt contents. Will be updated incrementally, if something fails during the process, this best attempt content will be showed as notification. Keys are eventId's
var bestAttemptContents: [String: UNMutableNotificationContent] = [:]
private var bestAttemptContents: [String: UNMutableNotificationContent] = [:]
/// Cached events. Keys are eventId's
var cachedEvents: [String: MXEvent] = [:]
static var mxSession: MXSession?
var showDecryptedContentInNotifications: Bool {
private static var backgroundSyncService: MXBackgroundSyncService!
private var showDecryptedContentInNotifications: Bool {
return RiotSettings.shared.showDecryptedContentInNotifications
}
lazy var configuration: Configurable = {
private lazy var configuration: Configurable = {
return CommonConfiguration()
}()
static var isLoggerInitialized: Bool = false
private static var isLoggerInitialized: Bool = false
private lazy var pushGatewayRestClient: MXPushGatewayRestClient = {
let url = URL(string: BuildSettings.serverConfigSygnalAPIUrlString)!
return MXPushGatewayRestClient(pushGateway: url.scheme! + "://" + url.host!, andOnUnrecognizedCertificateBlock: nil)
@ -110,25 +110,13 @@ class NotificationService: UNNotificationServiceExtension {
}
func setup(withRoomId roomId: String, eventId: String, completion: @escaping () -> Void) {
if let userAccount = MXKAccountManager.shared()?.activeAccounts.first {
if NotificationService.mxSession == nil {
let store = NSEMemoryStore(withCredentials: userAccount.mxCredentials)
NotificationService.mxSession = MXSession(matrixRestClient: MXRestClient(credentials: userAccount.mxCredentials, unrecognizedCertificateHandler: nil))
NotificationService.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.fallbackToBestAttemptContent(forEventId: eventId)
break
}
})
} else {
NSLog("[NotificationService] Instance: Reusing session")
completion()
MXKAccountManager.shared()?.forceReloadAccounts()
self.userAccount = MXKAccountManager.shared()?.activeAccounts.first
if let userAccount = userAccount {
if NotificationService.backgroundSyncService == nil {
NotificationService.backgroundSyncService = MXBackgroundSyncService(withCredentials: userAccount.mxCredentials)
}
completion()
} else {
NSLog("[NotificationService] setup: No active accounts")
fallbackToBestAttemptContent(forEventId: eventId)
@ -144,10 +132,9 @@ class NotificationService: UNNotificationServiceExtension {
NSLog("[NotificationService] preprocessPayload: Do not preprocess because app protection is set")
return
}
guard let session = NotificationService.mxSession else { return }
guard let roomDisplayName = session.store.summary?(ofRoom: roomId)?.displayname else { return }
let isDirect = session.directUserId(inRoom: roomId) != nil
if isDirect {
guard let roomSummary = NotificationService.backgroundSyncService.roomSummary(forRoomId: roomId) else { return }
guard let roomDisplayName = roomSummary.displayname else { return }
if roomSummary.isDirect == true {
bestAttemptContents[eventId]?.body = NSString.localizedUserNotificationString(forKey: "MESSAGE_FROM_X", arguments: [roomDisplayName as Any])
} else {
bestAttemptContents[eventId]?.body = NSString.localizedUserNotificationString(forKey: "MESSAGE_IN_X", arguments: [roomDisplayName as Any])
@ -155,133 +142,29 @@ class NotificationService: UNNotificationServiceExtension {
}
func fetchEvent(withEventId eventId: String, roomId: String, allowSync: Bool = true) {
guard let mxSession = NotificationService.mxSession else {
// there is something wrong, do not change the content
NSLog("[NotificationService] fetchEvent: Either originalContent or mxSession is missing.")
fallbackToBestAttemptContent(forEventId: eventId)
return
}
NSLog("[NotificationService] fetchEvent")
/// Inline function to handle decryption failure
func handleDecryptionFailure() {
if allowSync {
NSLog("[NotificationService] fetchEvent: Launch a background sync.")
self.launchBackgroundSync(forEventId: eventId, roomId: roomId)
} else {
NSLog("[NotificationService] fetchEvent: Do not sync anymore.")
self.fallbackToBestAttemptContent(forEventId: eventId)
}
}
/// Inline function to handle encryption for event, either from cache or from the backend
/// - Parameter event: The event to be handled
func handleEncryption(forEvent event: MXEvent) {
if !event.isEncrypted {
// not encrypted, go on processing
NSLog("[NotificationService] fetchEvent: Event not encrypted.")
self.processEvent(event)
return
}
// encrypted
if event.clear != nil {
// already decrypted
NSLog("[NotificationService] fetchEvent: Event already decrypted.")
self.processEvent(event)
return
}
// should decrypt it first
if mxSession.crypto.hasKeys(toDecryptEvent: event) {
// we have keys to decrypt the event
NSLog("[NotificationService] fetchEvent: Event needs to be decrpyted, and we have the keys to decrypt it.")
if mxSession.decryptEvent(event, inTimeline: nil) {
// decryption succeeded
NSLog("[NotificationService] fetchEvent: Event decrypted successfully.")
self.processEvent(event)
} else {
// decryption failed
NSLog("[NotificationService] fetchEvent: Decryption failed even crypto claimed it has the keys.")
handleDecryptionFailure()
}
} else {
// we don't have keys to decrypt the event
NSLog("[NotificationService] fetchEvent: Event needs to be decrpyted, but we don't have the keys to decrypt it.")
handleDecryptionFailure()
}
}
// check if we've fetched the event before
if let cachedEvent = self.cachedEvents[eventId] {
// use cached event
handleEncryption(forEvent: cachedEvent)
} else {
// attempt to fetch the event
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
}
guard let event = event else {
NSLog("[NotificationService] fetchEvent: MXSession.event method returned successfully with no event.")
self.fallbackToBestAttemptContent(forEventId: eventId)
return
}
// cache this event
self.cachedEvents[eventId] = event
// handle encryption for this event
handleEncryption(forEvent: event)
}) { [weak self] (error) in
guard let self = self else {
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))")
self.fallbackToBestAttemptContent(forEventId: eventId)
}
}
NotificationService.backgroundSyncService.event(withEventId: eventId,
inRoom: roomId,
completion: { (response) in
switch response {
case .success(let event):
NSLog("[NotificationService] fetchEvent: Event fetched successfully")
self.processEvent(event)
case .failure(let error):
NSLog("[NotificationService] fetchEvent: error: \(error)")
self.fallbackToBestAttemptContent(forEventId: eventId)
}
})
}
func launchBackgroundSync(forEventId eventId: String, roomId: String) {
guard let mxSession = NotificationService.mxSession else {
NSLog("[NotificationService] launchBackgroundSync: mxSession is missing.")
self.fallbackToBestAttemptContent(forEventId: eventId)
return
}
// launch an initial background sync
mxSession.backgroundSync(withTimeout: 20, ignoreSessionState: true) { [weak self] (response) in
switch response {
case .success:
guard let self = self else {
NSLog("[NotificationService] launchBackgroundSync: MXSession.initialBackgroundSync returned too late successfully")
return
}
// do not allow to sync anymore
self.fetchEvent(withEventId: eventId, roomId: roomId, allowSync: false)
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.fallbackToBestAttemptContent(forEventId: eventId)
break
}
}
}
func processEvent(_ event: MXEvent) {
guard let content = bestAttemptContents[event.eventId], let mxSession = NotificationService.mxSession else {
private func processEvent(_ event: MXEvent) {
guard let content = bestAttemptContents[event.eventId], let userAccount = userAccount else {
self.fallbackToBestAttemptContent(forEventId: event.eventId)
return
}
self.notificationContent(forEvent: event, inSession: mxSession) { (notificationContent) in
self.notificationContent(forEvent: event, forAccount: userAccount) { (notificationContent) in
var isUnwantedNotification = false
// Modify the notification content here...
@ -301,10 +184,13 @@ class NotificationService: UNNotificationServiceExtension {
NSLog("[NotificationService] processEvent: Calling content handler for: \(String(describing: event.eventId)), isUnwanted: \(isUnwantedNotification)")
self.contentHandlers[event.eventId]?(content)
// clear maps
self.contentHandlers.removeValue(forKey: event.eventId)
self.bestAttemptContents.removeValue(forKey: event.eventId)
}
}
func fallbackToBestAttemptContent(forEventId eventId: String) {
private func fallbackToBestAttemptContent(forEventId eventId: String) {
NSLog("[NotificationService] fallbackToBestAttemptContent: method called.")
guard let content = bestAttemptContents[eventId] else {
@ -314,190 +200,163 @@ class NotificationService: UNNotificationServiceExtension {
// call contentHandler
contentHandlers[eventId]?(content)
// clear maps
contentHandlers.removeValue(forKey: eventId)
bestAttemptContents.removeValue(forKey: eventId)
}
func notificationContent(forEvent event: MXEvent, inSession session: MXSession, onComplete: @escaping (UNNotificationContent?) -> Void) {
private func notificationContent(forEvent event: MXEvent, forAccount account: MXKAccount, onComplete: @escaping (UNNotificationContent?) -> Void) {
guard let content = event.content, content.count > 0 else {
NSLog("[NotificationService] notificationContentForEvent: empty event content")
onComplete(nil)
return
}
guard let room = MXRoom.load(from: session.store, withRoomId: event.roomId, matrixSession: session) as? MXRoom else {
NSLog("[NotificationService] notificationContentForEvent: Unknown room")
onComplete(nil)
return
}
let roomId = event.roomId!
let isRoomMentionsOnly = NotificationService.backgroundSyncService.isRoomMentionsOnly(roomId)
let roomSummary = NotificationService.backgroundSyncService.roomSummary(forRoomId: roomId)
NSLog("[NotificationService] notificationContentForEvent: Attempt to fetch the room state")
room.state { (roomState) in
guard let roomState = roomState else {
NSLog("[NotificationService] notificationContentForEvent: Could not fetch 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 = session.credentials.userId
let pushRule = session.notificationCenter.rule(matching: event, roomState: roomState)
switch event.eventType {
case .callInvite:
let offer = event.content["offer"] as? [AnyHashable: Any]
let sdp = offer?["sdp"] as? String
let isVideoCall = sdp?.contains("m=video") ?? false
NotificationService.backgroundSyncService.roomState(forRoomId: roomId, completion: { (response) in
switch response {
case .success(let roomState):
var notificationTitle: String?
var notificationBody: String?
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])
}
var threadIdentifier: String? = roomId
let eventSenderName = roomState.members.memberName(event.sender)
let currentUserId = account.mxCredentials.userId
let roomDisplayName = roomSummary?.displayname
let pushRule = NotificationService.backgroundSyncService.pushRule(matching: event, roomState: roomState)
// call notifications should stand out from normal messages, so we don't stack them
threadIdentifier = nil
self.sendVoipPush(forEvent: event)
case .roomMessage, .roomEncrypted:
if room.isMentionsOnly {
// A local notification will be displayed only for highlighted notification.
var isHighlighted = false
switch event.eventType {
case .callInvite:
let offer = event.content["offer"] as? [AnyHashable: Any]
let sdp = offer?["sdp"] as? String
let isVideoCall = sdp?.contains("m=video") ?? false
// Check whether is there an highlight tweak on it
for ruleAction in pushRule?.actions ?? [] {
guard let action = ruleAction as? MXPushRuleAction else { continue }
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 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
self.sendVoipPush(forEvent: event)
case .roomMessage, .roomEncrypted:
if isRoomMentionsOnly {
// A local notification will be displayed only for highlighted notification.
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 }
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 {
// Ignore this notif.
NSLog("[NotificationService] notificationContentForEvent: Ignore non highlighted notif in mentions only room")
onComplete(nil)
return
}
}
if !isHighlighted {
// Ignore this notif.
NSLog("[NotificationService] notificationContentForEvent: 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 = session.store.summary?(ofRoom: room.roomId)?.displayname
let myUserId = session.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])
var msgType = event.content["msgtype"] as? String
let messageContent = event.content["body"] as? String
if msgType == kMXMessageTypeText {
notificationBody = messageContent
} 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 {
session.crypto.keyVerificationManager.keyVerification(fromKeyVerificationEvent: event,
success:{ (keyVerification) in
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] notificationContentForEvent: failed to fetch key verification with error: \(error)")
onComplete(nil)
})
if event.isEncrypted && !self.showDecryptedContentInNotifications {
// Hide the content
msgType = nil
}
// 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 == kMXMessageTypeText {
notificationBody = messageContent
} 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 {
// Encrypted messages falls here
notificationBody = NSString.localizedUserNotificationString(forKey: "MESSAGE", arguments: [])
}
} else {
// Encrypted messages falls here
notificationBody = NSString.localizedUserNotificationString(forKey: "MESSAGE", arguments: [])
notificationTitle = eventSenderName
switch msgType {
case kMXMessageTypeText:
notificationBody = messageContent
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: "MESSAGE", arguments: [])
break
}
}
break
case .roomMember:
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])
}
case .sticker:
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
}
} else {
notificationTitle = eventSenderName
switch msgType {
case kMXMessageTypeText:
notificationBody = messageContent
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: "MESSAGE", arguments: [])
break
}
}
break
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])
} else {
notificationBody = NSString.localizedUserNotificationString(forKey: "USER_INVITE_TO_CHAT", arguments: [eventSenderName as Any])
}
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])
} else {
notificationTitle = eventSenderName
notificationBody = NSString.localizedUserNotificationString(forKey: "STICKER_FROM_USER", arguments: [eventSenderName as Any])
default:
break
}
notificationBody = NSString.localizedUserNotificationString(forKey: "STICKER_FROM_USER", arguments: [eventSenderName as Any])
default:
break
}
if self.localAuthenticationService.isProtectionSet {
NSLog("[NotificationService] notificationContentForEvent: Resetting title and body because app protection is set")
notificationBody = NSString.localizedUserNotificationString(forKey: "MESSAGE_PROTECTED", arguments: [])
notificationTitle = nil
}
guard notificationBody != nil else {
NSLog("[NotificationService] notificationContentForEvent: notificationBody is nil")
if self.localAuthenticationService.isProtectionSet {
NSLog("[NotificationService] notificationContentForEvent: Resetting title and body because app protection is set")
notificationBody = NSString.localizedUserNotificationString(forKey: "MESSAGE_PROTECTED", arguments: [])
notificationTitle = nil
}
guard notificationBody != nil else {
NSLog("[NotificationService] notificationContentForEvent: notificationBody is nil")
onComplete(nil)
return
}
let notificationContent = self.notificationContent(withTitle: notificationTitle,
body: notificationBody,
threadIdentifier: threadIdentifier,
userId: currentUserId,
event: event,
pushRule: pushRule)
NSLog("[NotificationService] notificationContentForEvent: Calling onComplete.")
onComplete(notificationContent)
case .failure(let error):
NSLog("[NotificationService] notificationContentForEvent: error: \(error)")
onComplete(nil)
return
}
let notificationContent = self.notificationContent(withTitle: notificationTitle,
body: notificationBody,
threadIdentifier: threadIdentifier,
userId: currentUserId,
event: event,
pushRule: pushRule)
NSLog("[NotificationService] notificationContentForEvent: Calling onComplete.")
onComplete(notificationContent)
}
})
}
func notificationContent(withTitle title: String?,
@ -597,40 +456,3 @@ class NotificationService: UNNotificationServiceExtension {
}
}
extension MXRoom {
func getRoomPushRule() -> MXPushRule? {
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
}
}
return nil
}
var isMentionsOnly: Bool {
// Check push rules at room level
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
}
}
return false
}
}