Fix issues around statics.

pull/1/head
Matthew Chen 8 years ago
parent 28a55e2449
commit 99f0b9d3e8

@ -76,7 +76,7 @@
346129DA1FD5B84900532771 /* SAENotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346129D81FD5B84900532771 /* SAENotificationsManager.swift */; }; 346129DA1FD5B84900532771 /* SAENotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346129D81FD5B84900532771 /* SAENotificationsManager.swift */; };
346129DE1FD5C02A00532771 /* LockInteractionController.h in Headers */ = {isa = PBXBuildFile; fileRef = 346129DC1FD5C02900532771 /* LockInteractionController.h */; }; 346129DE1FD5C02A00532771 /* LockInteractionController.h in Headers */ = {isa = PBXBuildFile; fileRef = 346129DC1FD5C02900532771 /* LockInteractionController.h */; };
346129DF1FD5C02A00532771 /* LockInteractionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129DD1FD5C02900532771 /* LockInteractionController.m */; }; 346129DF1FD5C02A00532771 /* LockInteractionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129DD1FD5C02900532771 /* LockInteractionController.m */; };
346129E21FD5C0BE00532771 /* VersionMigrations.h in Headers */ = {isa = PBXBuildFile; fileRef = 346129E01FD5C0BE00532771 /* VersionMigrations.h */; }; 346129E21FD5C0BE00532771 /* VersionMigrations.h in Headers */ = {isa = PBXBuildFile; fileRef = 346129E01FD5C0BE00532771 /* VersionMigrations.h */; settings = {ATTRIBUTES = (Public, ); }; };
346129E31FD5C0BE00532771 /* VersionMigrations.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129E11FD5C0BE00532771 /* VersionMigrations.m */; }; 346129E31FD5C0BE00532771 /* VersionMigrations.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129E11FD5C0BE00532771 /* VersionMigrations.m */; };
346129E61FD5C0C600532771 /* OWSDatabaseMigrationRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129E41FD5C0C600532771 /* OWSDatabaseMigrationRunner.m */; }; 346129E61FD5C0C600532771 /* OWSDatabaseMigrationRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129E41FD5C0C600532771 /* OWSDatabaseMigrationRunner.m */; };
346129E71FD5C0C600532771 /* OWSDatabaseMigrationRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = 346129E51FD5C0C600532771 /* OWSDatabaseMigrationRunner.h */; }; 346129E71FD5C0C600532771 /* OWSDatabaseMigrationRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = 346129E51FD5C0C600532771 /* OWSDatabaseMigrationRunner.h */; };

@ -56,7 +56,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
@interface AppDelegate () @interface AppDelegate ()
@property (nonatomic) UIWindow *screenProtectionWindow; @property (nonatomic) UIWindow *screenProtectionWindow;
@property (nonatomic) OWSContactsSyncing *contactsSyncing;
@property (nonatomic) BOOL hasInitialRootViewController; @property (nonatomic) BOOL hasInitialRootViewController;
@end @end
@ -167,10 +166,7 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
[self prepareScreenProtection]; [self prepareScreenProtection];
self.contactsSyncing = [[OWSContactsSyncing alloc] initWithContactsManager:[Environment current].contactsManager [OWSContactsSyncing sharedManager];
identityManager:[OWSIdentityManager sharedManager]
messageSender:[Environment current].messageSender
profileManager:[OWSProfileManager sharedManager]];
[[NSNotificationCenter defaultCenter] addObserver:self [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(databaseViewRegistrationComplete) selector:@selector(databaseViewRegistrationComplete)

@ -10,7 +10,5 @@
#define test(expressionExpectedToBeTrue) XCTAssert(expressionExpectedToBeTrue, @"") #define test(expressionExpectedToBeTrue) XCTAssert(expressionExpectedToBeTrue, @"")
#define testThrows(expressionExpectedToThrow) XCTAssertThrows(expressionExpectedToThrow, @"") #define testThrows(expressionExpectedToThrow) XCTAssertThrows(expressionExpectedToThrow, @"")
#define testDoesNotThrow(expressionExpectedToNotThrow) expressionExpectedToNotThrow #define testDoesNotThrow(expressionExpectedToNotThrow) expressionExpectedToNotThrow
#define testEnv [Release unitTestEnvironment:@[]]
#define testEnvWith(options) [Release unitTestEnvironment:(@[options])]
#define testChurnUntil(condition, timeout) test(_testChurnHelper(^int{ return condition; }, timeout)) #define testChurnUntil(condition, timeout) test(_testChurnHelper(^int{ return condition; }, timeout))
#define testChurnAndConditionMustStayTrue(condition, timeout) test(!_testChurnHelper(^int{ return !(condition); }, timeout)) #define testChurnAndConditionMustStayTrue(condition, timeout) test(!_testChurnHelper(^int{ return !(condition); }, timeout))

@ -29,3 +29,4 @@ FOUNDATION_EXPORT const unsigned char SignalMessagingVersionString[];
#import <SignalMessaging/UIImage+OWS.h> #import <SignalMessaging/UIImage+OWS.h>
#import <SignalMessaging/UIView+OWS.h> #import <SignalMessaging/UIView+OWS.h>
#import <SignalMessaging/UIViewController+OWS.h> #import <SignalMessaging/UIViewController+OWS.h>
#import <SignalMessaging/VersionMigrations.h>

@ -11,10 +11,9 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSContactsSyncing : NSObject @interface OWSContactsSyncing : NSObject
- (instancetype)initWithContactsManager:(OWSContactsManager *)contactsManager - (instancetype)init NS_UNAVAILABLE;
identityManager:(OWSIdentityManager *)identityManager
messageSender:(OWSMessageSender *)messageSender + (instancetype)sharedManager;
profileManager:(OWSProfileManager *)profileManager;
@end @end

@ -3,6 +3,7 @@
// //
#import "OWSContactsSyncing.h" #import "OWSContactsSyncing.h"
#import "Environment.h"
#import "OWSContactsManager.h" #import "OWSContactsManager.h"
#import "OWSProfileManager.h" #import "OWSProfileManager.h"
#import <SignalServiceKit/DataSource.h> #import <SignalServiceKit/DataSource.h>
@ -33,6 +34,24 @@ NSString *const kTSStorageManagerOWSContactsSyncingLastMessageKey
@implementation OWSContactsSyncing @implementation OWSContactsSyncing
+ (instancetype)sharedManager
{
static OWSContactsSyncing *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] initDefault];
});
return instance;
}
- (instancetype)initDefault
{
return [self initWithContactsManager:Environment.current.contactsManager
identityManager:OWSIdentityManager.sharedManager
messageSender:Environment.current.messageSender
profileManager:OWSProfileManager.sharedManager];
}
- (instancetype)initWithContactsManager:(OWSContactsManager *)contactsManager - (instancetype)initWithContactsManager:(OWSContactsManager *)contactsManager
identityManager:(OWSIdentityManager *)identityManager identityManager:(OWSIdentityManager *)identityManager
messageSender:(OWSMessageSender *)messageSender messageSender:(OWSMessageSender *)messageSender

@ -5,6 +5,7 @@
#import "Environment.h" #import "Environment.h"
#import "DebugLogger.h" #import "DebugLogger.h"
#import "SignalKeyingStorage.h" #import "SignalKeyingStorage.h"
#import <SignalServiceKit/AppContext.h>
#import <SignalServiceKit/ContactsUpdater.h> #import <SignalServiceKit/ContactsUpdater.h>
#import <SignalServiceKit/OWSMessageReceiver.h> #import <SignalServiceKit/OWSMessageReceiver.h>
#import <SignalServiceKit/OWSSignalService.h> #import <SignalServiceKit/OWSSignalService.h>
@ -37,7 +38,7 @@ static Environment *sharedEnvironment = nil;
+ (void)setCurrent:(Environment *)environment + (void)setCurrent:(Environment *)environment
{ {
OWSAssert(!sharedEnvironment); OWSAssert(!sharedEnvironment || !CurrentAppContext().isMainApp);
OWSAssert(environment); OWSAssert(environment);
sharedEnvironment = environment; sharedEnvironment = environment;

@ -9,9 +9,4 @@
/// Connects to actual production infrastructure /// Connects to actual production infrastructure
+ (Environment *)releaseEnvironment; + (Environment *)releaseEnvironment;
+ (Environment *)stagingEnvironment;
/// Fake environment with no logging
+ (Environment *)unitTestEnvironment:(NSArray *)testingAndLegacyOptions;
@end @end

@ -14,56 +14,25 @@
+ (Environment *)releaseEnvironment + (Environment *)releaseEnvironment
{ {
// Order matters here. static Environment *instance = nil;
TSStorageManager *storageManager = [TSStorageManager sharedManager]; static dispatch_once_t onceToken;
TSNetworkManager *networkManager = [TSNetworkManager sharedManager]; dispatch_once(&onceToken, ^{
OWSContactsManager *contactsManager = [OWSContactsManager new]; // Order matters here.
ContactsUpdater *contactsUpdater = [ContactsUpdater sharedUpdater]; TSStorageManager *storageManager = [TSStorageManager sharedManager];
OWSMessageSender *messageSender = [[OWSMessageSender alloc] initWithNetworkManager:networkManager TSNetworkManager *networkManager = [TSNetworkManager sharedManager];
storageManager:storageManager OWSContactsManager *contactsManager = [OWSContactsManager new];
contactsManager:contactsManager ContactsUpdater *contactsUpdater = [ContactsUpdater sharedUpdater];
contactsUpdater:contactsUpdater]; OWSMessageSender *messageSender = [[OWSMessageSender alloc] initWithNetworkManager:networkManager
storageManager:storageManager
return [[Environment alloc] initWithContactsManager:contactsManager contactsManager:contactsManager
contactsUpdater:contactsUpdater contactsUpdater:contactsUpdater];
networkManager:networkManager
messageSender:messageSender]; instance = [[Environment alloc] initWithContactsManager:contactsManager
} contactsUpdater:contactsUpdater
networkManager:networkManager
// TODELETE messageSender:messageSender];
+ (Environment *)stagingEnvironment });
{ return instance;
// Order matters here.
TSStorageManager *storageManager = [TSStorageManager sharedManager];
TSNetworkManager *networkManager = [TSNetworkManager sharedManager];
OWSContactsManager *contactsManager = [OWSContactsManager new];
ContactsUpdater *contactsUpdater = [ContactsUpdater sharedUpdater];
OWSMessageSender *messageSender = [[OWSMessageSender alloc] initWithNetworkManager:networkManager
storageManager:storageManager
contactsManager:contactsManager
contactsUpdater:contactsUpdater];
return [[Environment alloc] initWithContactsManager:contactsManager
contactsUpdater:contactsUpdater
networkManager:networkManager
messageSender:messageSender];
}
// TODELETE
+ (Environment *)unitTestEnvironment:(NSArray *)testingAndLegacyOptions
{
TSNetworkManager *networkManager = [TSNetworkManager sharedManager];
OWSContactsManager *contactsManager = [OWSContactsManager new];
ContactsUpdater *contactsUpdater = [ContactsUpdater sharedUpdater];
OWSMessageSender *messageSender = [[OWSMessageSender alloc] initWithNetworkManager:networkManager
storageManager:[TSStorageManager sharedManager]
contactsManager:contactsManager
contactsUpdater:contactsUpdater];
return [[Environment alloc] initWithContactsManager:nil
contactsUpdater:contactsUpdater
networkManager:networkManager
messageSender:messageSender];
} }
@end @end

@ -3,16 +3,25 @@
// //
#import "TextSecureKitEnv.h" #import "TextSecureKitEnv.h"
#import "AppContext.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
static TextSecureKitEnv *TextSecureKitEnvSharedInstance; static TextSecureKitEnv *sharedTextSecureKitEnv;
@implementation TextSecureKitEnv @interface TextSecureKitEnv ()
@property (nonatomic) id<OWSCallMessageHandler> callMessageHandler;
@property (nonatomic) id<ContactsManagerProtocol> contactsManager;
@property (nonatomic) OWSMessageSender *messageSender;
@property (nonatomic) id<NotificationsProtocol> notificationsManager;
@property (nonatomic) id<ProfileManagerProtocol> profileManager;
@end
#pragma mark -
@synthesize callMessageHandler = _callMessageHandler, contactsManager = _contactsManager, @implementation TextSecureKitEnv
messageSender = _messageSender, notificationsManager = _notificationsManager,
profileManager = _profileManager;
- (instancetype)initWithCallMessageHandler:(id<OWSCallMessageHandler>)callMessageHandler - (instancetype)initWithCallMessageHandler:(id<OWSCallMessageHandler>)callMessageHandler
contactsManager:(id<ContactsManagerProtocol>)contactsManager contactsManager:(id<ContactsManagerProtocol>)contactsManager
@ -25,6 +34,12 @@ static TextSecureKitEnv *TextSecureKitEnvSharedInstance;
return self; return self;
} }
OWSAssert(callMessageHandler);
OWSAssert(contactsManager);
OWSAssert(messageSender);
OWSAssert(notificationsManager);
OWSAssert(profileManager);
_callMessageHandler = callMessageHandler; _callMessageHandler = callMessageHandler;
_contactsManager = contactsManager; _contactsManager = contactsManager;
_messageSender = messageSender; _messageSender = messageSender;
@ -36,48 +51,17 @@ static TextSecureKitEnv *TextSecureKitEnvSharedInstance;
+ (instancetype)sharedEnv + (instancetype)sharedEnv
{ {
NSAssert(TextSecureKitEnvSharedInstance, @"Trying to access shared TextSecureKitEnv before it's been set"); OWSAssert(sharedTextSecureKitEnv);
return TextSecureKitEnvSharedInstance;
}
+ (void)setSharedEnv:(TextSecureKitEnv *)env
{
@synchronized (self) {
NSAssert(TextSecureKitEnvSharedInstance == nil, @"Trying to set shared TextSecureKitEnv which has already been set");
TextSecureKitEnvSharedInstance = env;
}
}
#pragma mark - getters
- (id<OWSCallMessageHandler>)callMessageHandler
{
NSAssert(_callMessageHandler, @"Trying to access the callMessageHandler before it's set.");
return _callMessageHandler;
}
- (id<ContactsManagerProtocol>)contactsManager
{
NSAssert(_contactsManager, @"Trying to access the contactsManager before it's set.");
return _contactsManager;
}
- (OWSMessageSender *)messageSender return sharedTextSecureKitEnv;
{
NSAssert(_messageSender, @"Trying to access the messageSender before it's set.");
return _messageSender;
} }
- (id<NotificationsProtocol>)notificationsManager + (void)setSharedEnv:(TextSecureKitEnv *)env
{ {
NSAssert(_notificationsManager, @"Trying to access the notificationsManager before it's set."); OWSAssert(env);
return _notificationsManager; OWSAssert(!sharedTextSecureKitEnv || !CurrentAppContext().isMainApp);
}
- (id<ProfileManagerProtocol>)profileManager sharedTextSecureKitEnv = env;
{
NSAssert(_profileManager, @"Trying to access the profileManager before it's set.");
return _profileManager;
} }
@end @end

@ -17,7 +17,11 @@ id<AppContext> CurrentAppContext(void)
void SetCurrentAppContext(id<AppContext> appContext) void SetCurrentAppContext(id<AppContext> appContext)
{ {
OWSCAssert(!currentAppContext); // The main app context should only be set once.
//
// App extensions may be opened multiple times in the same process,
// so statics will persist.
OWSCAssert(!currentAppContext || !currentAppContext.isMainApp);
currentAppContext = appContext; currentAppContext = appContext;
} }

@ -4,7 +4,8 @@
import SignalServiceKit import SignalServiceKit
class SAECallMessageHandler: NSObject, OWSCallMessageHandler { @objc
public class SAECallMessageHandler: NSObject, OWSCallMessageHandler {
public func receivedOffer(_ offer: OWSSignalServiceProtosCallMessageOffer, from callerId: String) { public func receivedOffer(_ offer: OWSSignalServiceProtosCallMessageOffer, from callerId: String) {
owsFail("\(self.logTag) in \(#function).") owsFail("\(self.logTag) in \(#function).")

@ -4,7 +4,8 @@
import SignalServiceKit import SignalServiceKit
class SAENotificationsManager: NSObject, NotificationsProtocol { @objc
public class SAENotificationsManager: NSObject, NotificationsProtocol {
public func notifyUser(for incomingMessage: TSIncomingMessage!, in thread: TSThread!, contactsManager: ContactsManagerProtocol!, transaction: YapDatabaseReadTransaction!) { public func notifyUser(for incomingMessage: TSIncomingMessage!, in thread: TSThread!, contactsManager: ContactsManagerProtocol!, transaction: YapDatabaseReadTransaction!) {
owsFail("\(self.logTag) in \(#function).") owsFail("\(self.logTag) in \(#function).")

@ -12,8 +12,6 @@ import PromiseKit
@objc @objc
public class ShareViewController: UINavigationController, SAELoadViewDelegate, SAEFailedViewDelegate { public class ShareViewController: UINavigationController, SAELoadViewDelegate, SAEFailedViewDelegate {
private var contactsSyncing: OWSContactsSyncing?
private var hasInitialRootViewController = false private var hasInitialRootViewController = false
private var isReadyForAppExtensions = false private var isReadyForAppExtensions = false
@ -23,12 +21,13 @@ public class ShareViewController: UINavigationController, SAELoadViewDelegate, S
Logger.debug("\(self.logTag()) \(#function)") Logger.debug("\(self.logTag()) \(#function)")
// This should be the first thing we do. // This should be the first thing we do.
SetCurrentAppContext(ShareAppExtensionContext(rootViewController:self)) let appContext = ShareAppExtensionContext(rootViewController:self)
SetCurrentAppContext(appContext)
DebugLogger.shared().enableTTYLogging() DebugLogger.shared().enableTTYLogging()
if _isDebugAssertConfiguration() { if _isDebugAssertConfiguration() {
DebugLogger.shared().enableFileLogging() DebugLogger.shared().enableFileLogging()
} else if (OWSPreferences.isLoggingEnabled()) { } else if OWSPreferences.isLoggingEnabled() {
DebugLogger.shared().enableFileLogging() DebugLogger.shared().enableFileLogging()
} }
@ -40,7 +39,7 @@ public class ShareViewController: UINavigationController, SAELoadViewDelegate, S
// We don't need to use DeviceSleepManager in the SAE. // We don't need to use DeviceSleepManager in the SAE.
setupEnvironment() appContext.setupEnvironment()
// TODO: // TODO:
// [UIUtil applySignalAppearence]; // [UIUtil applySignalAppearence];
@ -60,7 +59,7 @@ public class ShareViewController: UINavigationController, SAELoadViewDelegate, S
// If we don't have TSSStorageManager, we can't consult TSAccountManager // If we don't have TSSStorageManager, we can't consult TSAccountManager
// for isRegistered, so we use OWSPreferences which is usually-accurate // for isRegistered, so we use OWSPreferences which is usually-accurate
// copy of that state. // copy of that state.
if (OWSPreferences.isRegistered()) { if OWSPreferences.isRegistered() {
showNotReadyView() showNotReadyView()
} else { } else {
showNotRegisteredView() showNotRegisteredView()
@ -78,10 +77,7 @@ public class ShareViewController: UINavigationController, SAELoadViewDelegate, S
// We don't need to use "screen protection" in the SAE. // We don't need to use "screen protection" in the SAE.
contactsSyncing = OWSContactsSyncing(contactsManager:Environment.current().contactsManager, OWSContactsSyncing.sharedManager()
identityManager:OWSIdentityManager.shared(),
messageSender:Environment.current().messageSender,
profileManager:OWSProfileManager.shared())
NotificationCenter.default.addObserver(self, NotificationCenter.default.addObserver(self,
selector: #selector(databaseViewRegistrationComplete), selector: #selector(databaseViewRegistrationComplete),
@ -255,27 +251,6 @@ public class ShareViewController: UINavigationController, SAELoadViewDelegate, S
} }
} }
func setupEnvironment() {
let environment = Release.releaseEnvironment()
Environment.setCurrent(environment)
// Encryption/Decryption mutates session state and must be synchronized on a serial queue.
SessionCipher.setSessionCipherDispatchQueue(OWSDispatch.sessionStoreQueue())
let sharedEnv = TextSecureKitEnv(callMessageHandler:SAECallMessageHandler(),
contactsManager:Environment.current().contactsManager,
messageSender:Environment.current().messageSender,
notificationsManager:SAENotificationsManager(),
profileManager:OWSProfileManager.shared())
TextSecureKitEnv.setShared(sharedEnv)
TSStorageManager.shared().setupDatabase(safeBlockingMigrations: {
VersionMigrations.runSafeBlockingMigrations()
})
Environment.current().contactsManager.startObserving()
}
// MARK: Error Views // MARK: Error Views
private func showNotReadyView() { private func showNotReadyView() {

@ -6,12 +6,15 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
// This is _NOT_ a singleton and will be instantiated each time that the SAE is used.
@interface ShareAppExtensionContext : NSObject <AppContext> @interface ShareAppExtensionContext : NSObject <AppContext>
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController; - (instancetype)initWithRootViewController:(UIViewController *)rootViewController;
- (void)setupEnvironment;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

@ -3,7 +3,15 @@
// //
#import "ShareAppExtensionContext.h" #import "ShareAppExtensionContext.h"
#import "SignalShareExtension-Swift.h"
#import <AxolotlKit/SessionCipher.h>
#import <SignalMessaging/Environment.h>
#import <SignalMessaging/OWSContactsManager.h>
#import <SignalMessaging/OWSProfileManager.h>
#import <SignalMessaging/Release.h>
#import <SignalMessaging/UIViewController+OWS.h> #import <SignalMessaging/UIViewController+OWS.h>
#import <SignalMessaging/VersionMigrations.h>
#import <SignalServiceKit/TextSecureKitEnv.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -29,8 +37,6 @@ NS_ASSUME_NONNULL_BEGIN
_rootViewController = rootViewController; _rootViewController = rootViewController;
OWSSingletonAssert();
return self; return self;
} }
@ -118,6 +124,30 @@ NS_ASSUME_NONNULL_BEGIN
OWSFail(@"%@ called %s.", self.logTag, __PRETTY_FUNCTION__); OWSFail(@"%@ called %s.", self.logTag, __PRETTY_FUNCTION__);
} }
- (void)setupEnvironment
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[Environment setCurrent:[Release releaseEnvironment]];
// Encryption/Decryption mutates session state and must be synchronized on a serial queue.
[SessionCipher setSessionCipherDispatchQueue:[OWSDispatch sessionStoreQueue]];
TextSecureKitEnv *sharedEnv =
[[TextSecureKitEnv alloc] initWithCallMessageHandler:[SAECallMessageHandler new]
contactsManager:[Environment current].contactsManager
messageSender:[Environment current].messageSender
notificationsManager:[SAENotificationsManager new]
profileManager:OWSProfileManager.sharedManager];
[TextSecureKitEnv setSharedEnv:sharedEnv];
[[TSStorageManager sharedManager] setupDatabaseWithSafeBlockingMigrations:^{
[VersionMigrations runSafeBlockingMigrations];
}];
[[Environment current].contactsManager startObserving];
});
}
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

Loading…
Cancel
Save