element-ios/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSourceManager.m

271 lines
8.8 KiB
Objective-C

/*
Copyright 2015 OpenMarket 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 "MXKRoomDataSourceManager.h"
@interface MXKRoomDataSourceManager()
{
MXSession *mxSession;
/**
The list of running roomDataSources.
Each key is a room ID. Each value, the MXKRoomDataSource instance.
*/
NSMutableDictionary *roomDataSources;
/**
Observe UIApplicationDidReceiveMemoryWarningNotification to dispose of any resources that can be recreated.
*/
id UIApplicationDidReceiveMemoryWarningNotificationObserver;
}
@end
static NSMutableDictionary *_roomDataSourceManagers = nil;
static Class _roomDataSourceClass;
@implementation MXKRoomDataSourceManager
+ (MXKRoomDataSourceManager *)sharedManagerForMatrixSession:(MXSession *)mxSession
{
// Manage a pool of managers: one per Matrix session
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_roomDataSourceManagers = [NSMutableDictionary dictionary];
});
MXKRoomDataSourceManager *roomDataSourceManager;
// Compute an id for this mxSession object: its pointer address as a string
NSString *mxSessionId = [NSString stringWithFormat:@"%p", mxSession];
@synchronized(_roomDataSourceManagers)
{
if (_roomDataSourceClass == nil)
{
// Set default class
_roomDataSourceClass = MXKRoomDataSource.class;
}
// If not available yet, create the `MXKRoomDataSourceManager` for this Matrix session
roomDataSourceManager = _roomDataSourceManagers[mxSessionId];
if (!roomDataSourceManager)
{
roomDataSourceManager = [[MXKRoomDataSourceManager alloc]initWithMatrixSession:mxSession];
_roomDataSourceManagers[mxSessionId] = roomDataSourceManager;
}
}
return roomDataSourceManager;
}
+ (void)removeSharedManagerForMatrixSession:(MXSession*)mxSession
{
// Compute the id for this mxSession object: its pointer address as a string
NSString *mxSessionId = [NSString stringWithFormat:@"%p", mxSession];
@synchronized(_roomDataSourceManagers)
{
MXKRoomDataSourceManager *roomDataSourceManager = [_roomDataSourceManagers objectForKey:mxSessionId];
if (roomDataSourceManager)
{
[roomDataSourceManager destroy];
[_roomDataSourceManagers removeObjectForKey:mxSessionId];
}
}
}
+ (void)registerRoomDataSourceClass:(Class)roomDataSourceClass
{
// Sanity check: accept only MXKRoomDataSource classes or sub-classes
NSParameterAssert([roomDataSourceClass isSubclassOfClass:MXKRoomDataSource.class]);
@synchronized(_roomDataSourceManagers)
{
if (roomDataSourceClass !=_roomDataSourceClass)
{
_roomDataSourceClass = roomDataSourceClass;
NSArray *mxSessionIds = _roomDataSourceManagers.allKeys;
for (NSString *mxSessionId in mxSessionIds)
{
MXKRoomDataSourceManager *roomDataSourceManager = [_roomDataSourceManagers objectForKey:mxSessionId];
if (roomDataSourceManager)
{
[roomDataSourceManager destroy];
[_roomDataSourceManagers removeObjectForKey:mxSessionId];
}
}
}
}
}
- (instancetype)initWithMatrixSession:(MXSession *)matrixSession
{
self = [super init];
if (self)
{
mxSession = matrixSession;
roomDataSources = [NSMutableDictionary dictionary];
_releasePolicy = MXKRoomDataSourceManagerReleasePolicyNeverRelease;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didMXSessionDidLeaveRoom:) name:kMXSessionDidLeaveRoomNotification object:nil];
// Observe UIApplicationDidReceiveMemoryWarningNotification
UIApplicationDidReceiveMemoryWarningNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXLogDebug(@"[MXKRoomDataSourceManager] %@: Received memory warning.", self);
// Reload all data sources (except the current used ones) to reduce memory usage.
for (MXKRoomDataSource *roomDataSource in self->roomDataSources.allValues)
{
if (!roomDataSource.delegate)
{
[roomDataSource reload];
}
}
}];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionDidLeaveRoomNotification object:nil];
}
- (void)destroy
{
[self reset];
if (UIApplicationDidReceiveMemoryWarningNotificationObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:UIApplicationDidReceiveMemoryWarningNotificationObserver];
UIApplicationDidReceiveMemoryWarningNotificationObserver = nil;
}
}
#pragma mark
- (BOOL)isServerSyncInProgress
{
// Check first the matrix session state
if (mxSession.state == MXSessionStateSyncInProgress)
{
return YES;
}
// Check all data sources (events process is asynchronous, server sync may not be complete in data source).
for (MXKRoomDataSource *roomDataSource in roomDataSources.allValues)
{
if (roomDataSource.serverSyncEventCount)
{
return YES;
}
}
return NO;
}
#pragma mark
- (void)reset
{
NSArray *roomIds = roomDataSources.allKeys;
for (NSString *roomId in roomIds)
{
[self closeRoomDataSourceWithRoomId:roomId forceClose:YES];
}
}
- (void)roomDataSourceForRoom:(NSString *)roomId create:(BOOL)create onComplete:(void (^)(MXKRoomDataSource *roomDataSource))onComplete
{
NSParameterAssert(roomId);
// If not available yet, create the room data source
MXKRoomDataSource *roomDataSource = roomDataSources[roomId];
if (!roomDataSource && create && roomId)
{
[_roomDataSourceClass loadRoomDataSourceWithRoomId:roomId andMatrixSession:mxSession onComplete:^(id roomDataSource) {
[self addRoomDataSource:roomDataSource];
onComplete(roomDataSource);
}];
}
else
{
onComplete(roomDataSource);
}
}
- (void)addRoomDataSource:(MXKRoomDataSource *)roomDataSource
{
roomDataSources[roomDataSource.roomId] = roomDataSource;
}
- (void)closeRoomDataSourceWithRoomId:(NSString*)roomId forceClose:(BOOL)forceRelease;
{
// Check first whether this roomDataSource is well handled by this manager
if (!roomId || !roomDataSources[roomId])
{
MXLogDebug(@"[MXKRoomDataSourceManager] Failed to close an unknown room id: %@", roomId);
return;
}
MXKRoomDataSource *roomDataSource = roomDataSources[roomId];
// According to the policy, it is interesting to keep the room data source in life: it can keep managing echo messages
// in background for instance
MXKRoomDataSourceManagerReleasePolicy releasePolicy = _releasePolicy;
if (forceRelease)
{
// Act as ReleaseOnClose policy
releasePolicy = MXKRoomDataSourceManagerReleasePolicyReleaseOnClose;
}
switch (releasePolicy)
{
case MXKRoomDataSourceManagerReleasePolicyReleaseOnClose:
// Destroy and forget the instance
[roomDataSource destroy];
[roomDataSources removeObjectForKey:roomDataSource.roomId];
break;
case MXKRoomDataSourceManagerReleasePolicyNeverRelease:
// The close here consists in no more sending actions to the current view controller, the room data source delegate
roomDataSource.delegate = nil;
// Keep the instance for life (reduce memory usage by flushing room data if the number of bubbles is over 30).
[roomDataSource limitMemoryUsage:roomDataSource.maxBackgroundCachedBubblesCount];
break;
default:
break;
}
}
- (void)didMXSessionDidLeaveRoom:(NSNotification *)notif
{
if (mxSession == notif.object)
{
// The room is no more available, remove it from the manager
[self closeRoomDataSourceWithRoomId:notif.userInfo[kMXSessionNotificationRoomIdKey] forceClose:YES];
}
}
@end