element-ios/RiotShareExtension/Shared/ShareManager.m

297 lines
11 KiB
Objective-C

/*
Copyright 2017 Aram Sargsyan
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 MobileCoreServices;
#import <mach/mach.h>
#import "ShareManager.h"
#import "ShareViewController.h"
#import "ShareDataSource.h"
#import "ShareItemSenderProtocol.h"
#import "GeneratedInterface-Swift.h"
@interface ShareManager () <ShareViewControllerDelegate, ShareItemSenderDelegate>
@property (nonatomic, strong, readonly) id<ShareItemSenderProtocol> shareItemSender;
@property (nonatomic, strong, readonly) ShareViewController *shareViewController;
@property (nonatomic) BOOL useCustomSession;
@property (nonatomic, strong) MXSession* session;
@property (nonatomic, strong) MXKAccount *userAccount;
@property (nonatomic, strong) MXFileStore *fileStore;
/**
An array of rooms that the item is being shared to. This is to maintain a strong ref
to all necessary `MXRoom`s until sharing has completed.
*/
@property (nonatomic, strong) NSMutableArray<MXRoom *> *selectedRooms;
@end
@implementation ShareManager
/// A fake matrix session used to provide summaries with a REST client to handle room avatars.
/// The session is stored statically to prevent new ones from being created for each share.
static MXSession *fakeSession;
- (instancetype)initWithShareItemSender:(id<ShareItemSenderProtocol>)itemSender
type:(ShareManagerType)type
session:(MXSession*)session
{
if (self = [super init])
{
_shareItemSender = itemSender;
_shareItemSender.delegate = self;
_session = session;
_useCustomSession = _session == nil;
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(checkUserAccount) name:kMXKAccountManagerDidRemoveAccountNotification object:nil];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(checkUserAccount) name:NSExtensionHostWillEnterForegroundNotification object:nil];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
_shareViewController = [[ShareViewController alloc] initWithType:(type == ShareManagerTypeForward ? ShareViewControllerTypeForward : ShareViewControllerTypeSend)
currentState:ShareViewControllerAccountStateNotConfigured];
[_shareViewController setDelegate:self];
// Set up runtime language on each context update.
NSUserDefaults *sharedUserDefaults = [MXKAppSettings standardAppSettings].sharedUserDefaults;
NSString *language = [sharedUserDefaults objectForKey:@"appLanguage"];
[NSBundle mxk_setLanguage:language];
[NSBundle mxk_setFallbackLanguage:@"en"];
if (!_useCustomSession)
{
// If we don't use a custom session, we can initialize the shareViewController with our existing session
self.userAccount = [MXKAccountManager sharedManager].activeAccounts.firstObject;
ShareDataSource *roomDataSource = [[ShareDataSource alloc] initWithFileStore:_session.store
session:_session];
[self.shareViewController configureWithState:ShareViewControllerAccountStateConfigured
roomDataSource:roomDataSource];
}
else
{
[self checkUserAccount];
}
}
return self;
}
#pragma mark - Public
- (UIViewController *)mainViewController
{
return self.shareViewController;
}
#pragma mark - ShareViewControllerDelegate
- (void)shareViewController:(ShareViewController *)shareViewController didRequestShareForRoomIdentifiers:(NSSet<NSString *> *)roomIdentifiers
{
MXWeakify(self);
MXRestClient *restClient = [[MXRestClient alloc] initWithCredentials:self.userAccount.mxCredentials andOnUnrecognizedCertificateBlock:nil andPersistentTokenDataHandler:^(void (^handler)(NSArray<MXCredentials *> *credentials, void (^completion)(BOOL didUpdateCredentials))) {
[[MXKAccountManager sharedManager] readAndWriteCredentials:handler];
} andUnauthenticatedHandler:^(MXError *error, BOOL isSoftLogout, BOOL isRefreshTokenAuth, void (^completion)(void)) {
MXStrongifyAndReturnIfNil(self);
[self.userAccount handleUnauthenticatedWithError:error isSoftLogout:isSoftLogout isRefreshTokenAuth:isRefreshTokenAuth andCompletion:completion];
}];
if (self.useCustomSession || !self.session)
{
MXSession* session = [[MXSession alloc] initWithMatrixRestClient:restClient];
[MXFileStore setPreloadOptions:0];
MXWeakify(session);
[session setStore:self.fileStore success:^{
MXStrongifyAndReturnIfNil(session);
[self shareForRoomIdentifiers:roomIdentifiers usingSession:session];
} failure:^(NSError *error) {
MXLogError(@"[ShareManager] Failed preparing matrix session");
}];
}
else
{
[self shareForRoomIdentifiers:roomIdentifiers usingSession:self.session];
}
}
- (void)shareViewControllerDidRequestDismissal:(ShareViewController *)shareViewController
{
self.completionCallback(ShareManagerResultCancelled);
}
#pragma mark - ShareItemSenderDelegate
- (void)shareItemSenderDidStartSending:(id<ShareItemSenderProtocol>)shareItemSender
{
[self.shareViewController showProgressIndicator];
}
- (void)shareItemSender:(id<ShareItemSenderProtocol>)shareItemSender didUpdateProgress:(CGFloat)progress
{
[self.shareViewController setProgress:progress];
}
#pragma mark - Private
- (void)shareForRoomIdentifiers:(NSSet<NSString *> *)roomIdentifiers usingSession:(MXSession*)session
{
self.selectedRooms = [NSMutableArray array];
for (NSString *roomIdentifier in roomIdentifiers) {
MXRoom *room = [MXRoom loadRoomFromStore:session.store withRoomId:roomIdentifier matrixSession:session];
if (room) {
[self.selectedRooms addObject:room];
}
}
[self.shareItemSender sendItemsToRooms:self.selectedRooms success:^{
self.selectedRooms = nil;
self.completionCallback(ShareManagerResultFinished);
} failure:^(NSArray<NSError *> *errors) {
self.selectedRooms = nil;
[self showFailureAlert:[VectorL10n roomEventFailedToSend]];
}];
}
- (void)showFailureAlert:(NSString *)title
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
MXWeakify(self);
UIAlertAction *okAction = [UIAlertAction actionWithTitle:[VectorL10n ok] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
MXStrongifyAndReturnIfNil(self);
if (self.completionCallback)
{
self.completionCallback(ShareManagerResultFailed);
}
}];
[alertController addAction:okAction];
[self.mainViewController presentViewController:alertController animated:YES completion:nil];
}
- (void)checkUserAccount
{
// Force account manager to reload account from the local storage.
[MXKAccountManager sharedManagerWithReload:YES];
if (self.userAccount)
{
// Check whether the used account is still the first active one
MXKAccount *firstAccount = [MXKAccountManager sharedManager].activeAccounts.firstObject;
// Compare the access token
if (!firstAccount || ![self.userAccount.mxCredentials.accessToken isEqualToString:firstAccount.mxCredentials.accessToken])
{
// Remove this account
self.userAccount = nil;
}
}
if (!self.userAccount)
{
// We consider the first enabled account.
// TODO: Handle multiple accounts
self.userAccount = [MXKAccountManager sharedManager].activeAccounts.firstObject;
[self checkFakeSession];
}
// Reset the file store to reload the room data.
if (_fileStore)
{
[_fileStore close];
_fileStore = nil;
}
if (self.userAccount && fakeSession)
{
_fileStore = [[MXFileStore alloc] initWithCredentials:self.userAccount.mxCredentials];
ShareDataSource *roomDataSource = [[ShareDataSource alloc] initWithFileStore:_fileStore
session:fakeSession];
[self.shareViewController configureWithState:ShareViewControllerAccountStateConfigured
roomDataSource:roomDataSource];
} else {
[self.shareViewController configureWithState:ShareViewControllerAccountStateNotConfigured
roomDataSource:nil];
}
}
- (void)checkFakeSession
{
if (!self.userAccount)
{
return;
}
if (fakeSession && [fakeSession.credentials.userId isEqualToString:self.userAccount.mxCredentials.userId])
{
return;
}
MXRestClient *mxRestClient = [[MXRestClient alloc] initWithCredentials:self.userAccount.mxCredentials
andOnUnrecognizedCertificateBlock:nil
andPersistentTokenDataHandler:^(void (^handler)(NSArray<MXCredentials *> *credentials, void (^completion)(BOOL didUpdateCredentials))) {
[[MXKAccountManager sharedManager] readAndWriteCredentials:handler];
} andUnauthenticatedHandler:nil];
fakeSession = [[MXSession alloc] initWithMatrixRestClient:mxRestClient];
}
- (void)didStartSending
{
[self.shareViewController showProgressIndicator];
}
- (void)didReceiveMemoryWarning:(NSNotification*)notification
{
MXLogDebug(@"[ShareManager] Did receive memory warning");
[self logMemoryUsage];
}
// Log memory usage.
// NOTE: This result may not be reliable for all iOS versions (see https://forums.developer.apple.com/thread/64665 for more information).
- (void)logMemoryUsage
{
struct task_basic_info basicInfo;
mach_msg_type_number_t size = TASK_BASIC_INFO_COUNT;
kern_return_t kerr = task_info(mach_task_self(),
TASK_BASIC_INFO,
(task_info_t)&basicInfo,
&size);
vm_size_t memoryUsedInBytes = basicInfo.resident_size;
CGFloat memoryUsedInMegabytes = memoryUsedInBytes / (1024*1024);
if (kerr == KERN_SUCCESS)
{
MXLogDebug(@"[ShareManager] Memory in use (in MB): %f", memoryUsedInMegabytes);
}
else
{
MXLogDebug(@"[ShareManager] Error with task_info(): %s", mach_error_string(kerr));
}
}
@end