mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Merge branch 'develop' into element_3655
This commit is contained in:
commit
3429b60227
12 changed files with 173 additions and 177 deletions
|
@ -8,13 +8,17 @@ Changes to be released in next version
|
|||
* Upgrade to Xcode 12 (#3712).
|
||||
* Xcode 12: Make Xcode 12 and fastlane(xcodebuild) happy while some pods are not updated.
|
||||
* Update Gemfile.lock.
|
||||
* MXAnalyticsDelegate: Make it fully agnostic on tracked data.
|
||||
* KeyValueStore improvements.
|
||||
* Jitsi: Support authenticated Jitsi widgets (#3655).
|
||||
|
||||
🐛 Bugfix
|
||||
*
|
||||
* Fix analytics in order to track performance improvements.
|
||||
* Fix long placeholder cropping in room input toolbar. Prevent long placeholder to be displayed on small devices (#3790).
|
||||
|
||||
⚠️ API Changes
|
||||
* Xcode 12 is now mandatory to build the project.
|
||||
* Remove MXDecryptionFailureDelegate in flavor of agnostic MXAnalyticsDelegate.
|
||||
|
||||
🗣 Translations
|
||||
*
|
||||
|
|
|
@ -905,6 +905,8 @@
|
|||
ECB101322477CFDB00CF8C11 /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB1012E2477CFDB00CF8C11 /* UIDevice.swift */; };
|
||||
ECB101332477CFDB00CF8C11 /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB1012F2477CFDB00CF8C11 /* UITableViewCell.swift */; };
|
||||
ECB101362477D00700CF8C11 /* UniversalLink.m in Sources */ = {isa = PBXBuildFile; fileRef = ECB101352477D00700CF8C11 /* UniversalLink.m */; };
|
||||
ECB5D98F255420F8000AD89C /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB5D98E255420F8000AD89C /* Keychain.swift */; };
|
||||
ECB5D9902554221F000AD89C /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB5D98E255420F8000AD89C /* Keychain.swift */; };
|
||||
ECDC15F224AF41D2003437CF /* FormattedBodyParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECDC15F124AF41D2003437CF /* FormattedBodyParser.swift */; };
|
||||
ECF57A4425090C23004BBF9D /* CreateRoomCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF57A3825090C23004BBF9D /* CreateRoomCoordinatorBridgePresenter.swift */; };
|
||||
ECF57A4525090C23004BBF9D /* CreateRoomCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF57A3925090C23004BBF9D /* CreateRoomCoordinatorType.swift */; };
|
||||
|
@ -2139,6 +2141,7 @@
|
|||
ECB1012F2477CFDB00CF8C11 /* UITableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = "<group>"; };
|
||||
ECB101342477D00700CF8C11 /* UniversalLink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UniversalLink.h; sourceTree = "<group>"; };
|
||||
ECB101352477D00700CF8C11 /* UniversalLink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UniversalLink.m; sourceTree = "<group>"; };
|
||||
ECB5D98E255420F8000AD89C /* Keychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = "<group>"; };
|
||||
ECDC15F124AF41D2003437CF /* FormattedBodyParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormattedBodyParser.swift; sourceTree = "<group>"; };
|
||||
ECF57A3825090C23004BBF9D /* CreateRoomCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateRoomCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
|
||||
ECF57A3925090C23004BBF9D /* CreateRoomCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateRoomCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
|
@ -4871,6 +4874,7 @@
|
|||
EC1CA87124C823E700DE9EBF /* KeyValueStore.swift */,
|
||||
EC1CA87424C8259700DE9EBF /* KeychainStore.swift */,
|
||||
EC1CA87624C82D0E00DE9EBF /* MemoryStore.swift */,
|
||||
ECB5D98D255420EA000AD89C /* Extensions */,
|
||||
);
|
||||
path = KeyValueStorage;
|
||||
sourceTree = "<group>";
|
||||
|
@ -5190,6 +5194,14 @@
|
|||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
ECB5D98D255420EA000AD89C /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ECB5D98E255420F8000AD89C /* Keychain.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
ECF57A3725090C23004BBF9D /* CreateRoom */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -6195,6 +6207,7 @@
|
|||
EC9A3EC624E1632C00A8CFAE /* PushNotificationStore.swift in Sources */,
|
||||
EC31F0952521FC4600D407DA /* PinCodePreferences.swift in Sources */,
|
||||
32FD757424D2BEF700BA7B37 /* InfoPlist.swift in Sources */,
|
||||
ECB5D9902554221F000AD89C /* Keychain.swift in Sources */,
|
||||
EC31F09C2524AE1400D407DA /* BiometricsAuthenticationPresenter.swift in Sources */,
|
||||
EC85D752247C0F52002C44C9 /* UNUserNotificationCenter.swift in Sources */,
|
||||
EC9A3EC724E1634100A8CFAE /* KeyValueStore.swift in Sources */,
|
||||
|
@ -6774,6 +6787,7 @@
|
|||
ECF57A87250A6872004BBF9D /* TextViewTableViewCell.swift in Sources */,
|
||||
B1C562E1228C7C8C0037F12A /* RoomContextualMenuToolbarView.swift in Sources */,
|
||||
B1CE83DF2422817200D07506 /* KeyVerificationVerifyBySASCoordinatorType.swift in Sources */,
|
||||
ECB5D98F255420F8000AD89C /* Keychain.swift in Sources */,
|
||||
B1B557BF20EF5B4500210D55 /* DisabledRoomInputToolbarView.m in Sources */,
|
||||
B1B5578620EF564900210D55 /* GroupTableViewCellWithSwitch.m in Sources */,
|
||||
B1098BE821ECFE52000DDA48 /* Coordinator.swift in Sources */,
|
||||
|
|
|
@ -17,12 +17,11 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <MatrixSDK/MatrixSDK.h>
|
||||
#import "DecryptionFailureTracker.h"
|
||||
|
||||
/**
|
||||
`Analytics` sends analytics to an analytics tool.
|
||||
*/
|
||||
@interface Analytics : NSObject <MXAnalyticsDelegate, MXDecryptionFailureDelegate>
|
||||
@interface Analytics : NSObject <MXAnalyticsDelegate>
|
||||
|
||||
/**
|
||||
Returns the shared Analytics manager.
|
||||
|
@ -53,11 +52,4 @@
|
|||
*/
|
||||
- (void)dispatch;
|
||||
|
||||
/**
|
||||
Track how long the launch screen has been displayed to the end user.
|
||||
|
||||
@param seconds the duration in seconds.
|
||||
*/
|
||||
- (void)trackLaunchScreenDisplayDuration: (NSTimeInterval)seconds;
|
||||
|
||||
@end
|
||||
|
|
|
@ -18,42 +18,21 @@
|
|||
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
// All metrics are store under a Piwik category called "Metrics".
|
||||
// Then, there are 2 Piwik actions: "iOS.startup" and "iOS.stats" (these actions
|
||||
// are namespaced by plaform to have a nice rendering on the Piwik website).
|
||||
// Then, we use constants defined by the Matrix SDK as Piwik Names (ex:"mountData")
|
||||
NSString *const kAnalyticsMetricsCategory = @"Metrics";
|
||||
NSString *const kAnalyticsMetricsActionPattern = @"iOS.%@";
|
||||
|
||||
// E2E telemetry is stored under a Piwik category called "E2E".
|
||||
NSString *const kAnalyticsE2eCategory = @"E2E";
|
||||
NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure";
|
||||
// Duration data will be visible under the Piwik category called "Performance".
|
||||
// Other values will be visible in "Metrics".
|
||||
// Some Matomo screenshots are available at https://github.com/vector-im/element-ios/pull/3789.
|
||||
NSString *const kAnalyticsPerformanceCategory = @"Performance";
|
||||
NSString *const kAnalyticsMetricsCategory = @"Metrics";
|
||||
|
||||
|
||||
@import MatomoTracker;
|
||||
|
||||
@interface MatomoTracker (MatomoTrackerMigration)
|
||||
+ (MatomoTracker *)shared;
|
||||
|
||||
- (void)migrateFromFourPointFourSharedInstance;
|
||||
@end
|
||||
|
||||
@implementation MatomoTracker (MatomoTrackerMigration)
|
||||
+ (MatomoTracker *)shared
|
||||
@interface Analytics ()
|
||||
{
|
||||
MatomoTracker *matomoTracker = [[MatomoTracker alloc] initWithSiteId:BuildSettings.analyticsAppId
|
||||
baseURL:BuildSettings.analyticsServerUrl
|
||||
userAgent:@"iOSMatomoTracker"];
|
||||
[matomoTracker migrateFromFourPointFourSharedInstance];
|
||||
return matomoTracker;
|
||||
MatomoTracker *matomoTracker;
|
||||
}
|
||||
|
||||
- (void)migrateFromFourPointFourSharedInstance
|
||||
{
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"migratedFromFourPointFourSharedInstance"]) return;
|
||||
[self copyFromOldSharedInstance];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:true forKey:@"migratedFromFourPointFourSharedInstance"];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation Analytics
|
||||
|
@ -70,26 +49,46 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure";
|
|||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
matomoTracker = [[MatomoTracker alloc] initWithSiteId:BuildSettings.analyticsAppId
|
||||
baseURL:BuildSettings.analyticsServerUrl
|
||||
userAgent:@"iOSMatomoTracker"];
|
||||
[self migrateFromFourPointFourSharedInstance];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)migrateFromFourPointFourSharedInstance
|
||||
{
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"migratedFromFourPointFourSharedInstance"]) return;
|
||||
[matomoTracker copyFromOldSharedInstance];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:true forKey:@"migratedFromFourPointFourSharedInstance"];
|
||||
}
|
||||
|
||||
- (void)start
|
||||
{
|
||||
// Check whether the user has enabled the sending of crash reports.
|
||||
if (RiotSettings.shared.enableCrashReport)
|
||||
{
|
||||
[MatomoTracker shared].isOptedOut = NO;
|
||||
matomoTracker.isOptedOut = NO;
|
||||
|
||||
[[MatomoTracker shared] setCustomVariableWithIndex:1 name:@"App Platform" value:@"iOS Platform"];
|
||||
[[MatomoTracker shared] setCustomVariableWithIndex:2 name:@"App Version" value:[AppDelegate theDelegate].appVersion];
|
||||
[matomoTracker setCustomVariableWithIndex:1 name:@"App Platform" value:@"iOS Platform"];
|
||||
[matomoTracker setCustomVariableWithIndex:2 name:@"App Version" value:[AppDelegate theDelegate].appVersion];
|
||||
|
||||
// The language is either the one selected by the user within the app
|
||||
// or, else, the one configured by the OS
|
||||
NSString *language = [NSBundle mxk_language] ? [NSBundle mxk_language] : [[NSBundle mainBundle] preferredLocalizations][0];
|
||||
[[MatomoTracker shared] setCustomVariableWithIndex:4 name:@"Chosen Language" value:language];
|
||||
[matomoTracker setCustomVariableWithIndex:4 name:@"Chosen Language" value:language];
|
||||
|
||||
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
|
||||
if (account)
|
||||
{
|
||||
[[MatomoTracker shared] setCustomVariableWithIndex:7 name:@"Homeserver URL" value:account.mxCredentials.homeServer];
|
||||
[[MatomoTracker shared] setCustomVariableWithIndex:8 name:@"Identity Server URL" value:account.identityServerURL];
|
||||
[matomoTracker setCustomVariableWithIndex:7 name:@"Homeserver URL" value:account.mxCredentials.homeServer];
|
||||
[matomoTracker setCustomVariableWithIndex:8 name:@"Identity Server URL" value:account.identityServerURL];
|
||||
}
|
||||
|
||||
// TODO: We should also track device and os version
|
||||
|
@ -101,20 +100,20 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure";
|
|||
|
||||
#ifdef DEBUG
|
||||
// Disable analytics in debug as it pollutes stats
|
||||
[MatomoTracker shared].isOptedOut = YES;
|
||||
matomoTracker.isOptedOut = YES;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[AppDelegate] The user decided to not send analytics");
|
||||
[MatomoTracker shared].isOptedOut = YES;
|
||||
matomoTracker.isOptedOut = YES;
|
||||
[MXLogger logCrashes:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stop
|
||||
{
|
||||
[MatomoTracker shared].isOptedOut = YES;
|
||||
matomoTracker.isOptedOut = YES;
|
||||
[MXLogger logCrashes:NO];
|
||||
}
|
||||
|
||||
|
@ -124,84 +123,35 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure";
|
|||
NSString *appName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"];
|
||||
NSString *appVersion = [AppDelegate theDelegate].appVersion;
|
||||
|
||||
[[MatomoTracker shared] trackWithView:@[@"ios", appName, appVersion, screenName]
|
||||
[matomoTracker trackWithView:@[@"ios", appName, appVersion, screenName]
|
||||
url:nil];
|
||||
}
|
||||
|
||||
- (void)dispatch
|
||||
{
|
||||
[[MatomoTracker shared] dispatch];
|
||||
}
|
||||
|
||||
- (void)trackLaunchScreenDisplayDuration:(NSTimeInterval)seconds
|
||||
{
|
||||
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory];
|
||||
|
||||
[[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
|
||||
action:action
|
||||
name:kMXAnalyticsStartupLaunchScreen
|
||||
number:@(seconds * 1000)
|
||||
url:nil];
|
||||
[matomoTracker dispatch];
|
||||
}
|
||||
|
||||
#pragma mark - MXAnalyticsDelegate
|
||||
|
||||
- (void)trackStartupStorePreloadDuration: (NSTimeInterval)seconds
|
||||
- (void)trackDuration:(NSTimeInterval)seconds category:(NSString*)category name:(NSString*)name
|
||||
{
|
||||
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory];
|
||||
|
||||
[[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
|
||||
action:action
|
||||
name:kMXAnalyticsStartupStorePreload
|
||||
number:@(seconds * 1000)
|
||||
url:nil];
|
||||
// Report time in ms to make figures look better in Matomo
|
||||
NSNumber *value = @(seconds * 1000);
|
||||
[matomoTracker trackWithEventWithCategory:kAnalyticsPerformanceCategory
|
||||
action:category
|
||||
name:name
|
||||
number:value
|
||||
url:nil];
|
||||
}
|
||||
|
||||
- (void)trackStartupMountDataDuration: (NSTimeInterval)seconds
|
||||
- (void)trackValue:(NSNumber*)value category:(NSString*)category name:(NSString*)name
|
||||
{
|
||||
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory];
|
||||
|
||||
[[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
|
||||
action:action
|
||||
name:kMXAnalyticsStartupMountData
|
||||
number:@(seconds * 1000)
|
||||
url:nil];
|
||||
}
|
||||
|
||||
- (void)trackStartupSyncDuration: (NSTimeInterval)seconds isInitial: (BOOL)isInitial
|
||||
{
|
||||
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory];
|
||||
|
||||
[[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
|
||||
action:action
|
||||
name:isInitial ? kMXAnalyticsStartupInititialSync : kMXAnalyticsStartupIncrementalSync
|
||||
number:@(seconds * 1000)
|
||||
url:nil];
|
||||
}
|
||||
|
||||
- (void)trackRoomCount: (NSUInteger)roomCount
|
||||
{
|
||||
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStatsCategory];
|
||||
|
||||
[[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
|
||||
action:action
|
||||
name:kMXAnalyticsStatsRooms
|
||||
number:@(roomCount)
|
||||
url:nil];
|
||||
}
|
||||
|
||||
#pragma mark - MXDecryptionFailureDelegate
|
||||
|
||||
- (void)trackFailures:(NSDictionary<NSString *,NSNumber *> *)failuresCounts
|
||||
{
|
||||
for (NSString *reason in failuresCounts)
|
||||
{
|
||||
[[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsE2eCategory
|
||||
action:kAnalyticsE2eDecryptionFailureAction
|
||||
name:reason
|
||||
number:failuresCounts[reason]
|
||||
url:nil];
|
||||
}
|
||||
[matomoTracker trackWithEventWithCategory:kAnalyticsMetricsCategory
|
||||
action:category
|
||||
name:name
|
||||
number:value
|
||||
url:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
|
||||
@import MatrixSDK;
|
||||
|
||||
@protocol MXDecryptionFailureDelegate;
|
||||
|
||||
@interface DecryptionFailureTracker : NSObject
|
||||
|
||||
/**
|
||||
|
@ -34,7 +32,7 @@
|
|||
/**
|
||||
The delegate object to receive analytics events.
|
||||
*/
|
||||
@property (nonatomic) id<MXDecryptionFailureDelegate> delegate;
|
||||
@property (nonatomic) id<MXAnalyticsDelegate> delegate;
|
||||
|
||||
/**
|
||||
Report an event unable to decrypt.
|
||||
|
@ -54,18 +52,3 @@
|
|||
- (void)dispatch;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
The `MXDecryptionFailureDelegate` protocol receives some stats computed by
|
||||
`DecryptionFailureTracker`.
|
||||
*/
|
||||
@protocol MXDecryptionFailureDelegate <NSObject>
|
||||
|
||||
/**
|
||||
Stats for decryption failures.
|
||||
|
||||
@param failuresCounts the number of errors per failure reason.
|
||||
*/
|
||||
- (void)trackFailures:(NSDictionary<NSString*, NSNumber*> *)failuresCounts;
|
||||
|
||||
@end
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
// and reporting them as failures
|
||||
#define GRACE_PERIOD 60
|
||||
|
||||
// E2E failures analytics category.
|
||||
NSString *const kDecryptionFailureTrackerAnalyticsCategory = @"e2e.failure";
|
||||
|
||||
@interface DecryptionFailureTracker()
|
||||
{
|
||||
|
@ -128,6 +130,11 @@
|
|||
*/
|
||||
- (void)checkFailures
|
||||
{
|
||||
if (!_delegate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NSTimeInterval tsNow = [NSDate date].timeIntervalSince1970;
|
||||
|
||||
NSMutableArray *failuresToTrack = [NSMutableArray array];
|
||||
|
@ -142,7 +149,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (_delegate && failuresToTrack.count)
|
||||
if (failuresToTrack.count)
|
||||
{
|
||||
// Sort failures by error reason
|
||||
NSMutableDictionary<NSString*, NSNumber*> *failuresCounts = [NSMutableDictionary dictionary];
|
||||
|
@ -152,8 +159,11 @@
|
|||
}
|
||||
|
||||
NSLog(@"[DecryptionFailureTracker] trackFailures: %@", failuresCounts);
|
||||
|
||||
[_delegate trackFailures:failuresCounts];
|
||||
|
||||
for (NSString *reason in failuresCounts)
|
||||
{
|
||||
[_delegate trackValue:failuresCounts[reason] category:kDecryptionFailureTrackerAnalyticsCategory name:reason];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
35
Riot/Managers/KeyValueStorage/Extensions/Keychain.swift
Normal file
35
Riot/Managers/KeyValueStorage/Extensions/Keychain.swift
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// 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 KeychainAccess
|
||||
|
||||
/// Extension on Keychain to get/set booleans
|
||||
extension Keychain {
|
||||
|
||||
public func set(_ value: Bool, key: String, ignoringAttributeSynchronizable: Bool = true) throws {
|
||||
try set(value.description, key: key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable)
|
||||
}
|
||||
|
||||
public func getBool(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> Bool? {
|
||||
guard let value = try getString(key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) else {
|
||||
return nil
|
||||
}
|
||||
guard value == true.description || value == false.description else { return nil }
|
||||
return value == true.description
|
||||
}
|
||||
|
||||
}
|
|
@ -24,12 +24,17 @@ protocol KeyValueStore {
|
|||
func set(_ value: String?, forKey key: KeyValueStoreKey) throws
|
||||
func set(_ value: Bool?, forKey key: KeyValueStoreKey) throws
|
||||
func set(_ value: Int?, forKey key: KeyValueStoreKey) throws
|
||||
func set(_ value: UInt?, forKey key: KeyValueStoreKey) throws
|
||||
|
||||
// getters
|
||||
func data(forKey key: KeyValueStoreKey) throws -> Data?
|
||||
func string(forKey key: KeyValueStoreKey) throws -> String?
|
||||
func bool(forKey key: KeyValueStoreKey) throws -> Bool?
|
||||
func integer(forKey key: KeyValueStoreKey) throws -> Int?
|
||||
func unsignedInteger(forKey key: KeyValueStoreKey) throws -> UInt?
|
||||
|
||||
// checkers
|
||||
func containsObject(forKey key: KeyValueStoreKey) -> Bool
|
||||
|
||||
// remove
|
||||
func removeObject(forKey key: KeyValueStoreKey) throws
|
||||
|
|
|
@ -17,23 +17,6 @@
|
|||
import Foundation
|
||||
import KeychainAccess
|
||||
|
||||
/// Extension on Keychain to get/set booleans
|
||||
extension Keychain {
|
||||
|
||||
public func set(_ value: Bool, key: String, ignoringAttributeSynchronizable: Bool = true) throws {
|
||||
try set(value.description, key: key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable)
|
||||
}
|
||||
|
||||
public func getBool(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> Bool? {
|
||||
guard let value = try getString(key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) else {
|
||||
return nil
|
||||
}
|
||||
guard value == true.description || value == false.description else { return nil }
|
||||
return value == true.description
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class KeychainStore {
|
||||
|
||||
private var keychain: Keychain
|
||||
|
@ -85,6 +68,15 @@ extension KeychainStore: KeyValueStore {
|
|||
try keychain.set(String(value), key: key)
|
||||
}
|
||||
|
||||
func set(_ value: UInt?, forKey key: KeyValueStoreKey) throws {
|
||||
guard let value = value else {
|
||||
try removeObject(forKey: key)
|
||||
return
|
||||
}
|
||||
|
||||
try keychain.set(String(value), key: key)
|
||||
}
|
||||
|
||||
// getters
|
||||
func data(forKey key: KeyValueStoreKey) throws -> Data? {
|
||||
return try keychain.getData(key)
|
||||
|
@ -105,6 +97,18 @@ extension KeychainStore: KeyValueStore {
|
|||
return Int(stringValue)
|
||||
}
|
||||
|
||||
func unsignedInteger(forKey key: KeyValueStoreKey) throws -> UInt? {
|
||||
guard let stringValue = try keychain.getString(key) else {
|
||||
return nil
|
||||
}
|
||||
return UInt(stringValue)
|
||||
}
|
||||
|
||||
// checkers
|
||||
func containsObject(forKey key: KeyValueStoreKey) -> Bool {
|
||||
return (try? keychain.contains(key)) ?? false
|
||||
}
|
||||
|
||||
// remove
|
||||
func removeObject(forKey key: KeyValueStoreKey) throws {
|
||||
try keychain.remove(key)
|
||||
|
|
|
@ -18,13 +18,13 @@ import Foundation
|
|||
|
||||
class MemoryStore {
|
||||
|
||||
private var map: Dictionary<KeyValueStoreKey, Any> = [:]
|
||||
private(set) var map: [KeyValueStoreKey: Any] = [:]
|
||||
|
||||
private func setObject(_ value: Any?, forKey key: KeyValueStoreKey) {
|
||||
if let value = value {
|
||||
map[key] = value
|
||||
} else {
|
||||
removeObject(forKey: key)
|
||||
try? removeObject(forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,10 @@ class MemoryStore {
|
|||
return map[key]
|
||||
}
|
||||
|
||||
init(withMap map: [KeyValueStoreKey: Any] = [:]) {
|
||||
self.map = map
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension MemoryStore: KeyValueStore {
|
||||
|
@ -53,6 +57,10 @@ extension MemoryStore: KeyValueStore {
|
|||
setObject(value, forKey: key)
|
||||
}
|
||||
|
||||
func set(_ value: UInt?, forKey key: KeyValueStoreKey) throws {
|
||||
setObject(value, forKey: key)
|
||||
}
|
||||
|
||||
// getters
|
||||
func data(forKey key: KeyValueStoreKey) throws -> Data? {
|
||||
return object(forKey: key) as? Data
|
||||
|
@ -70,8 +78,17 @@ extension MemoryStore: KeyValueStore {
|
|||
return object(forKey: key) as? Int
|
||||
}
|
||||
|
||||
func unsignedInteger(forKey key: KeyValueStoreKey) throws -> UInt? {
|
||||
return object(forKey: key) as? UInt
|
||||
}
|
||||
|
||||
// checkers
|
||||
func containsObject(forKey key: KeyValueStoreKey) -> Bool {
|
||||
return object(forKey: key) != nil
|
||||
}
|
||||
|
||||
// remove
|
||||
func removeObject(forKey key: KeyValueStoreKey) {
|
||||
func removeObject(forKey key: KeyValueStoreKey) throws {
|
||||
map.removeValue(forKey: key)
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#import "BugReportViewController.h"
|
||||
#import "RoomKeyRequestViewController.h"
|
||||
#import "DecryptionFailureTracker.h"
|
||||
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
|
||||
|
@ -2418,7 +2419,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
NSLog(@"[AppDelegate] hideLaunchAnimation: LaunchAnimation was shown for %.3fms", duration * 1000);
|
||||
|
||||
// Track it on our analytics
|
||||
[[Analytics sharedInstance] trackLaunchScreenDisplayDuration:duration];
|
||||
[[MXSDKOptions sharedInstance].analyticsDelegate trackDuration:duration
|
||||
category:kMXAnalyticsStartupCategory
|
||||
name:kMXAnalyticsStartupLaunchScreen];
|
||||
|
||||
// TODO: Send durationMs to Piwik
|
||||
// Such information should be the same on all platforms
|
||||
|
|
|
@ -132,28 +132,7 @@
|
|||
{
|
||||
_isEncryptionEnabled = isEncryptionEnabled;
|
||||
|
||||
// Consider the default placeholder
|
||||
NSString *placeholder= NSLocalizedStringFromTable(@"room_message_short_placeholder", @"Vector", nil);
|
||||
|
||||
if (_isEncryptionEnabled)
|
||||
{
|
||||
// Check the device screen size before using large placeholder
|
||||
if ([GBDeviceInfo deviceInfo].family == GBDeviceFamilyiPad || [GBDeviceInfo deviceInfo].displayInfo.display >= GBDeviceDisplay4p7Inch)
|
||||
{
|
||||
placeholder = NSLocalizedStringFromTable(@"encrypted_room_message_placeholder", @"Vector", nil);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check the device screen size before using large placeholder
|
||||
if ([GBDeviceInfo deviceInfo].family == GBDeviceFamilyiPad || [GBDeviceInfo deviceInfo].displayInfo.display >= GBDeviceDisplay4p7Inch)
|
||||
{
|
||||
placeholder = NSLocalizedStringFromTable(@"room_message_placeholder", @"Vector", nil);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
self.placeholder = placeholder;
|
||||
[self updatePlaceholder];
|
||||
}
|
||||
|
||||
- (void)setSendMode:(RoomInputToolbarViewSendMode)sendMode
|
||||
|
@ -192,7 +171,7 @@
|
|||
NSString *placeholder;
|
||||
|
||||
// Check the device screen size before using large placeholder
|
||||
BOOL shouldDisplayLargePlaceholder = [GBDeviceInfo deviceInfo].family == GBDeviceFamilyiPad || [GBDeviceInfo deviceInfo].displayInfo.display >= GBDeviceDisplay4p7Inch;
|
||||
BOOL shouldDisplayLargePlaceholder = [GBDeviceInfo deviceInfo].family == GBDeviceFamilyiPad || [GBDeviceInfo deviceInfo].displayInfo.display >= GBDeviceDisplay5p8Inch;
|
||||
|
||||
if (!shouldDisplayLargePlaceholder)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue