Add the upload statistic

This commit is contained in:
ylecollen 2015-01-09 09:54:31 +01:00
parent 93f2a343c1
commit 1e870e48b6
8 changed files with 215 additions and 67 deletions

View file

@ -10,6 +10,7 @@
71D2E4EC1A49814B000DE015 /* MemberActionsCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 71D2E4EB1A49814B000DE015 /* MemberActionsCell.m */; };
71DB9DC11A495B6400504A09 /* MemberViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 71DB9DC01A495B6400504A09 /* MemberViewController.m */; };
71E94A771A5C4020009F52E5 /* PieChartView.m in Sources */ = {isa = PBXBuildFile; fileRef = 71E94A761A5C4020009F52E5 /* PieChartView.m */; };
71E94A7A1A5FB6D3009F52E5 /* UploadManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 71E94A791A5FB6D3009F52E5 /* UploadManager.m */; };
D648B86A591308736E2D4078 /* libPods-matrixConsole.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8141B1E2401FFCC3C5B99234 /* libPods-matrixConsole.a */; };
F00B5DB91A1B9BCE00EA1C8D /* CustomImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = F00B5DB81A1B9BCE00EA1C8D /* CustomImageView.m */; };
F013EEEC1A40D437002BB093 /* matrixConsole-Defaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = F013EEEB1A40D437002BB093 /* matrixConsole-Defaults.plist */; };
@ -72,6 +73,8 @@
71DB9DC01A495B6400504A09 /* MemberViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MemberViewController.m; sourceTree = "<group>"; };
71E94A751A5C4020009F52E5 /* PieChartView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PieChartView.h; sourceTree = "<group>"; };
71E94A761A5C4020009F52E5 /* PieChartView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PieChartView.m; sourceTree = "<group>"; };
71E94A781A5FB6D3009F52E5 /* UploadManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UploadManager.h; sourceTree = "<group>"; };
71E94A791A5FB6D3009F52E5 /* UploadManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UploadManager.m; sourceTree = "<group>"; };
8141B1E2401FFCC3C5B99234 /* libPods-matrixConsole.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-matrixConsole.a"; sourceTree = BUILT_PRODUCTS_DIR; };
B7EC7E45C718BF2BBCE0CF48 /* Pods-matrixConsole.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-matrixConsole.debug.xcconfig"; path = "Pods/Target Support Files/Pods-matrixConsole/Pods-matrixConsole.debug.xcconfig"; sourceTree = "<group>"; };
F00B5DB71A1B9BCE00EA1C8D /* CustomImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomImageView.h; sourceTree = "<group>"; };
@ -207,6 +210,8 @@
F021FBEE1A5EF57300EA3AE6 /* MediaLoader.m */,
F021FBF01A5F1F8E00EA3AE6 /* MediaManager.h */,
F021FBF11A5F1F8E00EA3AE6 /* MediaManager.m */,
71E94A781A5FB6D3009F52E5 /* UploadManager.h */,
71E94A791A5FB6D3009F52E5 /* UploadManager.m */,
);
path = API;
sourceTree = "<group>";
@ -505,6 +510,7 @@
F03EF5F919F171EB00A0EE52 /* RecentsViewController.m in Sources */,
71E94A771A5C4020009F52E5 /* PieChartView.m in Sources */,
F0465AFA1A251F85003639F9 /* RoomMessage.m in Sources */,
71E94A7A1A5FB6D3009F52E5 /* UploadManager.m in Sources */,
F021FBF21A5F1F8E00EA3AE6 /* MediaManager.m in Sources */,
F04EE51F1A3A01D500C64930 /* APNSHandler.m in Sources */,
F03C47111A02952800E445AB /* CustomAlert.m in Sources */,

View file

@ -143,28 +143,8 @@ NSString *const kMediaLoaderProgressDownloadRateKey = @"kMediaLoaderProgressDown
NSString* progressString = [NSString stringWithFormat:@"%@ / %@", [NSByteCountFormatter stringFromByteCount:downloadData.length countStyle:NSByteCountFormatterCountStyleFile], [NSByteCountFormatter stringFromByteCount:expectedSize countStyle:NSByteCountFormatterCountStyleFile]];
[dict setValue:progressString forKey:kMediaLoaderProgressStringKey];
NSMutableString* remaingTimeStr = [[NSMutableString alloc] init];
if (dataRemainingTime < 1) {
[remaingTimeStr appendString:@"< 1s"];
} else if (dataRemainingTime < 60)
{
[remaingTimeStr appendFormat:@"%ds", (int)dataRemainingTime];
}
else if (dataRemainingTime < 3600)
{
[remaingTimeStr appendFormat:@"%dm %2ds", (int)(dataRemainingTime/60), ((int)dataRemainingTime) % 60];
}
else if (dataRemainingTime >= 3600)
{
[remaingTimeStr appendFormat:@"%dh %dm %ds", (int)(dataRemainingTime / 3600),
((int)(dataRemainingTime) % 3600) / 60,
(int)(dataRemainingTime) % 60];
}
[remaingTimeStr appendString:@" left"];
[dict setValue:remaingTimeStr forKey:kMediaLoaderProgressRemaingTimeKey];
[dict setValue:[MediaManager formatSecondsInterval:dataRemainingTime] forKey:kMediaLoaderProgressRemaingTimeKey];
NSString* downloadRateStr = [NSString stringWithFormat:@"%@/s", [NSByteCountFormatter stringFromByteCount:meanRate * 1024 countStyle:NSByteCountFormatterCountStyleFile]];
[dict setValue:downloadRateStr forKey:kMediaLoaderProgressDownloadRateKey];

View file

@ -28,6 +28,8 @@ extern NSString *const kMediaDownloadDidFailNotification;
+ (id)sharedInstance;
+ (NSString*)formatSecondsInterval:(CGFloat)secondsInterval;
+ (UIImage *)resize:(UIImage *)image toFitInSize:(CGSize)size;
// Load a picture from the local cache (Do not start any remote requests)

View file

@ -39,6 +39,30 @@ static NSMutableDictionary* pendingMediaLoadersByURL = nil;
return sharedMediaManager;
}
+ (NSString*)formatSecondsInterval:(CGFloat)secondsInterval {
NSMutableString* formattedString = [[NSMutableString alloc] init];
if (secondsInterval < 1) {
[formattedString appendString:@"< 1s"];
} else if (secondsInterval < 60)
{
[formattedString appendFormat:@"%ds", (int)secondsInterval];
}
else if (secondsInterval < 3600)
{
[formattedString appendFormat:@"%dm %2ds", (int)(secondsInterval/60), ((int)secondsInterval) % 60];
}
else if (secondsInterval >= 3600)
{
[formattedString appendFormat:@"%dh %dm %ds", (int)(secondsInterval / 3600),
((int)(secondsInterval) % 3600) / 60,
(int)(secondsInterval) % 60];
}
[formattedString appendString:@" left"];
return formattedString;
}
+ (UIImage *)resize:(UIImage *)image toFitInSize:(CGSize)size {
UIImage *resizedImage = image;

View file

@ -0,0 +1,37 @@
/*
Copyright 2014 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 <UIKit/UIKit.h>
@interface UploadManager : NSObject
// trigger the kMediaUploadProgressNotification notification from the upload parameters
// URL : the uploading media URL
// bytesWritten / totalBytesWritten / totalBytesExpectedToWrite : theses parameters are provided by NSURLConnectionDelegate
// initialRange / range: the current upload info could be a subpart of uploads. initialRange defines the global upload progress already did done before this current upload.
// range is the range value of this upload in the global scope.
// e.g. : Upload a media can be split in two parts :
// 1 - upload the thumbnail -> initialRange = 0, range = 0.1 : assume that the thumbnail upload is 10% of the upload process
// 2 - upload the media -> initialRange = 0,1, range = 0.9 : the media upload is 90% of the global upload
+ (void) onUploadProgress:(NSString*)URL bytesWritten:(NSUInteger)bytesWritten totalBytesWritten:(long long)totalBytesWritten totalBytesExpectedToWrite:(long long)totalBytesExpectedToWrite initialRange:(CGFloat)initialRange range:(CGFloat)range;
// returns the stats info with kMediaLoaderProgress... key
+ (NSDictionary*)statsInfoForURL:(NSString*)URL;
// the upload
+ (void)removeURL:(NSString*)URL;
@end

View file

@ -0,0 +1,112 @@
/*
Copyright 2014 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 "UploadManager.h"
#import "MediaManager.h"
NSString *const kUploadManagerUploadStartTimeKey = @"kUploadManagerUploadStartTimeKey";
NSString *const kUploadManagerStatsStartTimeKey = @"kUploadManagerUploadStartTimeKey";
@implementation UploadManager
// Table of stats dictionry by media URL
static NSMutableDictionary* statsByURL = nil;
// trigger the kMediaUploadProgressNotification notification from the upload parameters
// URL : the uploading media URL
// bytesWritten / totalBytesWritten / totalBytesExpectedToWrite : theses parameters are provided by NSURLConnectionDelegate
// initialRange / range: the current upload info could be a subpart of uploads. initialRange defines the global upload progress already did done before this current upload.
// range is the range value of this upload in the global scope.
// e.g. : Upload a media can be split in two parts :
// 1 - upload the thumbnail -> initialRange = 0, range = 0.1 : assume that the thumbnail upload is 10% of the upload process
// 2 - upload the media -> initialRange = 0,1, range = 0.9 : the media upload is 90% of the global upload
+ (void) onUploadProgress:(NSString*)URL bytesWritten:(NSUInteger)bytesWritten totalBytesWritten:(long long)totalBytesWritten totalBytesExpectedToWrite:(long long)totalBytesExpectedToWrite initialRange:(CGFloat)initialRange range:(CGFloat)range {
// sanity check
if (!URL) {
// should never happen
return;
}
if (!statsByURL) {
statsByURL = [[NSMutableDictionary alloc] init];
}
CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent();
NSMutableDictionary* dict = [statsByURL valueForKey:URL];
if (!dict) {
dict = [[NSMutableDictionary alloc] init];
// init the start times
[dict setValue:[NSNumber numberWithDouble:currentTime] forKey:kUploadManagerUploadStartTimeKey];
[dict setValue:[NSNumber numberWithDouble:currentTime] forKey:kUploadManagerStatsStartTimeKey];
[statsByURL setValue:dict forKey:URL];
}
CGFloat progressRate = initialRange + (((float)totalBytesWritten) / ((float)totalBytesExpectedToWrite) * range);
[dict setValue:[NSNumber numberWithFloat:progressRate] forKey:kMediaLoaderProgressRateKey];
CGFloat dataRate = 0;
CFAbsoluteTime statsStartTime = ((NSNumber*)[dict valueForKey:kUploadManagerStatsStartTimeKey]).doubleValue;
if (currentTime != statsStartTime)
{
dataRate = bytesWritten / 1024.0 / (currentTime - statsStartTime);
}
else
{
dataRate = bytesWritten / 1024.0 / 0.001;
}
CGFloat dataRemainingTime = 0;
if (0 != dataRate)
{
dataRemainingTime = (totalBytesExpectedToWrite - totalBytesWritten)/ 1024.0 / dataRate;
}
NSString* progressString = [NSString stringWithFormat:@"%@ / %@", [NSByteCountFormatter stringFromByteCount:totalBytesWritten countStyle:NSByteCountFormatterCountStyleFile], [NSByteCountFormatter stringFromByteCount:totalBytesExpectedToWrite countStyle:NSByteCountFormatterCountStyleFile]];
[dict setValue:progressString forKey:kMediaLoaderProgressStringKey];
[dict setValue:[MediaManager formatSecondsInterval:dataRemainingTime] forKey:kMediaLoaderProgressRemaingTimeKey];
NSString* downloadRateStr = [NSString stringWithFormat:@"%@/s", [NSByteCountFormatter stringFromByteCount:dataRate * 1024 countStyle:NSByteCountFormatterCountStyleFile]];
[dict setValue:downloadRateStr forKey:kMediaLoaderProgressDownloadRateKey];
[[NSNotificationCenter defaultCenter] postNotificationName:kMediaUploadProgressNotification object:URL userInfo:dict];
}
// returns the stats info with kMediaLoaderProgress... key
+ (NSDictionary*)statsInfoForURL:(NSString*)URL {
// sanity check
if (URL) {
return [statsByURL valueForKey:URL];
}
return nil;
}
// the upload
+ (void)removeURL:(NSString*)URL {
if (URL) {
[statsByURL removeObjectForKey:URL];
}
}
@end

View file

@ -17,7 +17,7 @@
#import "RoomMessageTableCell.h"
#import "MediaManager.h"
#import "PieChartView.h"
#import "UploadManager.h"
@implementation RoomMessageTableCell
@ -160,7 +160,15 @@
self.activityIndicator.hidden = NO;
[self.activityIndicator startAnimating];
[self initUploadProgressTo:self.message.uploadProgress];
NSDictionary* uploadDict = [UploadManager statsInfoForURL:self.message.attachmentURL];
if (uploadDict) {
self.activityIndicator.hidden = YES;
[self updateProgressUI:uploadDict];
} else {
self.activityIndicator.hidden = NO;
self.progressView.hidden = YES;
}
}
-(void)stopAnimating {
@ -174,22 +182,16 @@
NSString* url = notif.object;
if ([url isEqualToString:self.message.thumbnailURL] || [url isEqualToString:self.message.attachmentURL]) {
[self updateProgressUI:notif.userInfo];
// the upload is ended
if (self.progressChartView.progress == 1.0) {
self.progressView.hidden = YES;
}
}
}
}
- (void) initUploadProgressTo:(CGFloat)progress {
// nothing to display
if (progress <= 0) {
self.activityIndicator.hidden = NO;
} else {
self.message.uploadProgress = progress;
self.activityIndicator.hidden = YES;
self.progressView.hidden = NO;
self.progressChartView.progress = progress;
}
}
- (void)layoutSubviews {
[super layoutSubviews];

View file

@ -29,6 +29,7 @@
#import "AppSettings.h"
#import "MediaManager.h"
#import "UploadManager.h"
#define ROOMVIEWCONTROLLER_TYPING_TIMEOUT_MS 20000
@ -988,11 +989,14 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
// Create the local event displayed during uploading
MXEvent *localEvent = [self addLocalEchoEventForAttachedImage:thumbnail];
__block NSString* dummyURL = [localEvent.content valueForKey:@"url"];
NSString* dummyURL = [localEvent.content valueForKey:@"url"];
// Upload thumbnail
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
[mxHandler.mxRestClient uploadContent:thumbnailData mimeType:@"image/jpeg" timeout:30 success:^(NSString *url) {
[UploadManager removeURL:dummyURL];
// Prepare content of attached video
NSMutableDictionary *videoContent = [[NSMutableDictionary alloc] init];
NSMutableDictionary *videoInfo = [[NSMutableDictionary alloc] init];
@ -1047,26 +1051,16 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
if (videoData.length < ROOMVIEWCONTROLLER_UPLOAD_FILE_SIZE) {
[videoInfo setValue:[NSNumber numberWithUnsignedInteger:videoData.length] forKey:@"size"];
[mxHandler.mxRestClient uploadContent:videoData mimeType:videoInfo[@"mimetype"] timeout:30 success:^(NSString *url) {
[UploadManager removeURL:dummyURL];
[videoContent setValue:url forKey:@"url"];
[videoContent setValue:videoInfo forKey:@"info"];
[videoContent setValue:@"Video" forKey:@"body"];
[self sendMessage:videoContent withLocalEvent:localEvent];
} failure:^(NSError *error) {
[UploadManager removeURL:dummyURL];
[self handleError:error forLocalEvent:localEvent];
} uploadProgress:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
// only one parameter by now
// but assume more could be expected (like download rates, remaining time...)
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
CGFloat progress = ((float)totalBytesWritten) / ((float)totalBytesExpectedToWrite);
// assume that the thumbnail is about 10% of the video
// the video is about 90 %
[dict setValue:[NSNumber numberWithFloat: 0.1 + (progress * 0.9)] forKey:kMediaLoaderProgressRateKey];
[[NSNotificationCenter defaultCenter] postNotificationName:kMediaUploadProgressNotification object:dummyURL userInfo:dict];
[UploadManager onUploadProgress:dummyURL bytesWritten:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite initialRange:0.1 range:0.9];
}];
} else {
NSLog(@"Video is too large");
@ -1079,6 +1073,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
}
else {
NSLog(@"Video export failed: %d", (int)[exportSession status]);
[UploadManager removeURL:dummyURL];
// remove tmp file (if any)
[[NSFileManager defaultManager] removeItemAtPath:[tmpVideoLocation path] error:nil];
[self handleError:nil forLocalEvent:localEvent];
@ -1086,18 +1081,10 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
}];
} failure:^(NSError *error) {
NSLog(@"Video thumbnail upload failed");
[UploadManager removeURL:dummyURL];
[self handleError:error forLocalEvent:localEvent];
} uploadProgress:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
// only one parameter by now
// but assume more could be expected (like download rates, remaining time...)
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
CGFloat progress = ((float)totalBytesWritten) / ((float)totalBytesExpectedToWrite);
// assume that the thumbnail is about 10% of the video
[dict setValue:[NSNumber numberWithFloat: (progress * 0.1)] forKey:kMediaLoaderProgressRateKey];
[[NSNotificationCenter defaultCenter] postNotificationName:kMediaUploadProgressNotification object:dummyURL userInfo:dict];
[UploadManager onUploadProgress:dummyURL bytesWritten:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite initialRange:0 range:0.1];
}];
}
@ -2279,11 +2266,13 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
NSData *imageData = UIImageJPEGRepresentation(image, 0.8);
[imageInfo setValue:[NSNumber numberWithUnsignedInteger:imageData.length] forKey:@"size"];
__block NSString* dummyURL = [localEvent.content valueForKey:@"url"];
NSString* dummyURL = [localEvent.content valueForKey:@"url"];
// Upload image
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
[mxHandler.mxRestClient uploadContent:imageData mimeType:@"image/jpeg" timeout:30 success:^(NSString *url) {
[UploadManager removeURL:dummyURL];
NSMutableDictionary *imageMessage = [[NSMutableDictionary alloc] init];
[imageMessage setValue:@"m.image" forKey:@"msgtype"];
[imageMessage setValue:url forKey:@"url"];
@ -2292,14 +2281,10 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
// Send message for this attachment
[self sendMessage:imageMessage withLocalEvent:localEvent];
} failure:^(NSError *error) {
[UploadManager removeURL:dummyURL];
[self handleError:error forLocalEvent:localEvent];
} uploadProgress:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
// only one parameter by now
// but assume more could be expected (like download rates, remaining time...)
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
[dict setValue:[NSNumber numberWithFloat: ((float)totalBytesWritten) / ((float)totalBytesExpectedToWrite)] forKey:kMediaLoaderProgressRateKey];
[[NSNotificationCenter defaultCenter] postNotificationName:kMediaUploadProgressNotification object:dummyURL userInfo:dict];
[UploadManager onUploadProgress:dummyURL bytesWritten:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite initialRange:0 range:1.0];
}];
}