diff --git a/Classes/LinphoneUI/UIRecordingCell.h b/Classes/LinphoneUI/UIRecordingCell.h index accb53837..e82954b14 100644 --- a/Classes/LinphoneUI/UIRecordingCell.h +++ b/Classes/LinphoneUI/UIRecordingCell.h @@ -10,10 +10,11 @@ @interface UIRecordingCell : UITableViewCell @property (weak, nonatomic) IBOutlet UILabel *nameLabel; -@property (weak, nonatomic) IBOutlet UILabel *dateLabel; @property(nonatomic, assign) NSString *recording; - (id)initWithIdentifier:(NSString*)identifier; +- (void)updateFrame; + @end diff --git a/Classes/LinphoneUI/UIRecordingCell.m b/Classes/LinphoneUI/UIRecordingCell.m index 7451aa98a..41596d2f8 100644 --- a/Classes/LinphoneUI/UIRecordingCell.m +++ b/Classes/LinphoneUI/UIRecordingCell.m @@ -15,15 +15,15 @@ #pragma mark - Lifecycle Functions - (id)initWithIdentifier:(NSString *)identifier { - if ((self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]) != nil) { + if ((self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier])) { NSArray *arrayOfViews = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil]; // resize cell to match .nib size. It is needed when resized the cell to // correctly adapt its height too - UIView *sub = ((UIView *)[arrayOfViews objectAtIndex:0]); - [self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)]; - [self addSubview:sub]; + UIRecordingCell *sub = [arrayOfViews objectAtIndex:0]; + [self setFrame:CGRectMake(0, 0, sub.frame.size.width, 40)]; + self = sub; self.recording = NULL; } return self; @@ -40,19 +40,15 @@ _recording = arecording; if(_recording) { //TODO: Parse file name to get name of contact and date + NSArray *parsedRecording = [LinphoneUtils parseRecordingName:_recording]; + NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; + [dateFormat setDateFormat:@"HH:mm:ss"]; + _nameLabel.text = [[[parsedRecording objectAtIndex:0] stringByAppendingString:@" @ "] stringByAppendingString:[dateFormat stringFromDate:[parsedRecording objectAtIndex:1]]]; } } #pragma mark - -- (void)touchUp:(id)sender { - [self setHighlighted:true animated:true]; -} - -- (void)touchDown:(id)sender { - [self setHighlighted:false animated:true]; -} - - (NSString *)accessibilityLabel { return _nameLabel.text; } @@ -71,5 +67,19 @@ } } +- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated { + self.selectionStyle = UITableViewCellSelectionStyleNone; +} + +- (void)updateFrame { + CGRect frame = self.frame; + if (!self.selected) { + frame.size.height = 40; + } else { + frame.size.height = 150; + } + [self setFrame:frame]; +} + @end diff --git a/Classes/LinphoneUI/UIRecordingCell.xib b/Classes/LinphoneUI/UIRecordingCell.xib index cc5ad8588..802728a30 100644 --- a/Classes/LinphoneUI/UIRecordingCell.xib +++ b/Classes/LinphoneUI/UIRecordingCell.xib @@ -12,35 +12,33 @@ - - - - - - - - - - - - + + + + + + + + + + + - - + - - + + diff --git a/Classes/RecordingsListTableView.h b/Classes/RecordingsListTableView.h index d8c003927..45e32626b 100644 --- a/Classes/RecordingsListTableView.h +++ b/Classes/RecordingsListTableView.h @@ -9,14 +9,13 @@ #import "UICheckBoxTableView.h" -#import "OrderedDictionary.h" - @interface RecordingsListTableView : UICheckBoxTableView { @private - OrderedDictionary *recordings; + NSMutableDictionary *recordings; //This has sub arrays indexed with the date of the recordings, themselves containings the recordings. + NSString *writablePath; + //This is the path to the folder where we write the recordings to. We should probably define it in LinphoneManager though. } -@property(nonatomic) BOOL ongoing; - (void)loadData; - (void)removeAllRecordings; diff --git a/Classes/RecordingsListTableView.m b/Classes/RecordingsListTableView.m index b90dafbd7..66f22450b 100644 --- a/Classes/RecordingsListTableView.m +++ b/Classes/RecordingsListTableView.m @@ -12,13 +12,14 @@ #import "Utils.h" @implementation RecordingsListTableView -NSArray *sortedRecordings; #pragma mark - Lifecycle Functions - (void)initRecordingsTableViewController { - recordings = [[OrderedDictionary alloc] init]; - sortedRecordings = [[NSArray alloc] init]; + recordings = [NSMutableDictionary dictionary]; + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); + writablePath = [paths objectAtIndex:0]; + writablePath = [writablePath stringByAppendingString:@"/"]; } - (void)viewWillAppear:(BOOL)animated { @@ -26,6 +27,7 @@ NSArray *sortedRecordings; if (![self selectFirstRow]) { //TODO: Make first cell expand to show player } + [self loadData]; } - (id)init { @@ -33,7 +35,6 @@ NSArray *sortedRecordings; if (self) { [self initRecordingsTableViewController]; } - _ongoing = FALSE; return self; } @@ -60,7 +61,6 @@ NSArray *sortedRecordings; - (void)loadData { - _ongoing = TRUE; LOGI(@"====>>>> Load recording list - Start"); //Clear recording cells @@ -70,18 +70,52 @@ NSArray *sortedRecordings; [[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:j]] setRecording:nil]; } } + recordings = [NSMutableDictionary dictionary]; + NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:writablePath error:NULL]; + for (NSString *file in directoryContent) { + if (![file hasPrefix:@"recording_"]) { + continue; + } + NSArray *parsedName = [LinphoneUtils parseRecordingName:file]; + NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; + [dateFormat setDateFormat:@"EEEE, MMM d, yyyy"]; + NSString *dayPretty = [dateFormat stringFromDate:[parsedName objectAtIndex:1]]; + NSMutableArray *recOfDay = [recordings objectForKey:dayPretty]; + if (recOfDay) { + // Loop through the object until a later object, then insert it right before + int i; + for (i = 0; i < [recOfDay count]; ++i) { + NSString *fileAtIndex = [recOfDay objectAtIndex:i]; + NSArray *parsedNameAtIndex = [LinphoneUtils parseRecordingName:fileAtIndex]; + if ([[parsedName objectAtIndex:1] compare:[parsedNameAtIndex objectAtIndex:1]] == NSOrderedDescending) { + break; + } + } + [recOfDay insertObject:[writablePath stringByAppendingString:file] atIndex:i]; + } else { + recOfDay = [NSMutableArray arrayWithObjects:[writablePath stringByAppendingString:file], nil]; + [recordings setObject:recOfDay forKey:dayPretty]; + } + } - //TODO: Fill the recordings dictionnary with the recording names, keys are dates LOGI(@"====>>>> Load recording list - End"); [super loadData]; - _ongoing = FALSE; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + NSIndexPath *selectedRow = [self.tableView indexPathForSelectedRow]; + if (selectedRow && [selectedRow compare:indexPath] == NSOrderedSame) { + return 150; + } else { + return 40; + } } #pragma mark - UITableViewDataSource Functions - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { - return [recordings allKeys]; + return nil; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { @@ -89,7 +123,8 @@ NSArray *sortedRecordings; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return [(OrderedDictionary *)[recordings objectForKey:[recordings keyAtIndex:section]] count]; + NSArray *sortedKey = [self getSortedKeys]; + return [(NSArray *)[recordings objectForKey:[sortedKey objectAtIndex:section]] count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { @@ -98,11 +133,14 @@ NSArray *sortedRecordings; if (cell == nil) { cell = [[UIRecordingCell alloc] initWithIdentifier:kCellId]; } - NSString *recording = @""; - //TODO: Set recording to the path of the recording - [cell setRecording:recording]; + NSString *date = [[self getSortedKeys] objectAtIndex:[indexPath section]]; + NSMutableArray *subAr = [recordings objectForKey:date]; + NSString *recordingPath = subAr[indexPath.row]; + [cell setRecording:recordingPath]; [super accessoryForCell:cell atPath:indexPath]; - + //accessoryForCell set it to gray but we don't want it + cell.selectionStyle = UITableViewCellSelectionStyleNone; + [cell updateFrame]; return cell; } @@ -114,7 +152,7 @@ NSArray *sortedRecordings; UILabel *tempLabel = [[UILabel alloc] initWithFrame:frame]; tempLabel.backgroundColor = [UIColor clearColor]; tempLabel.textColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"color_A.png"]]; - tempLabel.text = [recordings keyAtIndex:section]; + tempLabel.text = [[self getSortedKeys] objectAtIndex:section]; tempLabel.textAlignment = NSTextAlignmentCenter; tempLabel.font = [UIFont boldSystemFontOfSize:17]; tempLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; @@ -126,7 +164,9 @@ NSArray *sortedRecordings; - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [super tableView:tableView didSelectRowAtIndexPath:indexPath]; if (![self isEditing]) { - //TODO: Expand selected cell to display player + [tableView beginUpdates]; + [(UIRecordingCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath] updateFrame]; + [tableView endUpdates]; } } @@ -138,9 +178,9 @@ forRowAtIndexPath:(NSIndexPath *)indexPath { [tableView beginUpdates]; - NSString *date = [recordings keyAtIndex:[indexPath section]]; + NSString *date = [[self getSortedKeys] objectAtIndex:[indexPath section]]; NSMutableArray *subAr = [recordings objectForKey:date]; - //NSString *recording = subAr[indexPath.row]; + NSString *recordingPath = subAr[indexPath.row]; [subAr removeObjectAtIndex:indexPath.row]; if (subAr.count == 0) { [recordings removeObjectForKey:date]; @@ -150,7 +190,9 @@ forRowAtIndexPath:(NSIndexPath *)indexPath { UIRecordingCell* cell = [self.tableView cellForRowAtIndexPath:indexPath]; [cell setRecording:NULL]; - //TODO: Delete recording file here + + remove([recordingPath cStringUsingEncoding:NSUTF8StringEncoding]); + [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView endUpdates]; @@ -162,16 +204,28 @@ forRowAtIndexPath:(NSIndexPath *)indexPath { [super removeSelectionUsing:^(NSIndexPath *indexPath) { [NSNotificationCenter.defaultCenter removeObserver:self]; - NSString *date = [recordings keyAtIndex:[indexPath section]]; + NSString *date = [[self getSortedKeys] objectAtIndex:[indexPath section]]; NSMutableArray *subAr = [recordings objectForKey:date]; - //NSString *recording = subAr[indexPath.row]; + NSString *recordingPath = subAr[indexPath.row]; [subAr removeObjectAtIndex:indexPath.row]; if (subAr.count == 0) { [recordings removeObjectForKey:date]; } UIRecordingCell* cell = [self.tableView cellForRowAtIndexPath:indexPath]; [cell setRecording:NULL]; - //TODO: Delete recording file here + remove([recordingPath cStringUsingEncoding:NSUTF8StringEncoding]); + }]; +} + +#pragma mark - Utilities + +- (NSArray *)getSortedKeys { + return [[recordings allKeys] sortedArrayUsingComparator:^NSComparisonResult(NSString *day2, NSString *day1){ + NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; + [dateFormat setDateFormat:@"EEEE, MMM d, yyyy"]; + NSDate *date1 = [dateFormat dateFromString:day1]; + NSDate *date2 = [dateFormat dateFromString:day2]; + return [date1 compare:date2]; }]; } diff --git a/Classes/RecordingsListView.m b/Classes/RecordingsListView.m index 14c4e4d4a..9b91c5675 100644 --- a/Classes/RecordingsListView.m +++ b/Classes/RecordingsListView.m @@ -53,6 +53,7 @@ static UICompositeViewDescription *compositeDescription = nil; - (void)viewDidLoad { [super viewDidLoad]; tableController.tableView.accessibilityIdentifier = @"Recordings table"; + tableController.tableView.tableFooterView = [[UIView alloc] init]; } - (void)viewWillAppear:(BOOL)animated { diff --git a/Classes/RecordingsListView.xib b/Classes/RecordingsListView.xib index bee02a58e..a508993d9 100644 --- a/Classes/RecordingsListView.xib +++ b/Classes/RecordingsListView.xib @@ -86,13 +86,12 @@ - - + @@ -101,6 +100,14 @@ + @@ -111,6 +118,7 @@ + diff --git a/Classes/Utils/Utils.h b/Classes/Utils/Utils.h index e010ce711..1f03d9fb4 100644 --- a/Classes/Utils/Utils.h +++ b/Classes/Utils/Utils.h @@ -52,6 +52,7 @@ typedef enum { + (NSMutableDictionary *)photoAssetsDictionary; + (NSString *)recordingFilePathFromCall:(const LinphoneAddress *)iaddr; ++ (NSArray *)parseRecordingName:(NSString *)filename; @end diff --git a/Classes/Utils/Utils.m b/Classes/Utils/Utils.m index f895b8cef..039f64248 100644 --- a/Classes/Utils/Utils.m +++ b/Classes/Utils/Utils.m @@ -510,7 +510,6 @@ return addr; } - + (NSString *)recordingFilePathFromCall:(const LinphoneAddress *)iaddr { NSString *filepath = @"recording_"; const char *address = linphone_address_get_username(iaddr); @@ -530,10 +529,25 @@ writablePath = [writablePath stringByAppendingString:filepath]; LOGD(@"file path is: %@\n", writablePath); return writablePath; - //file name is contact-name_dayName-day-monthName-year-hour-minutes-seconds + //file name is recording_contact-name_dayName-day-monthName-year-hour-minutes-seconds + //The recording prefix is used to identify recordings in the cache directory. //We will use name_dayName-day-monthName-year to separate recordings by days, then hour-minutes-seconds to order them in each day. } ++ (NSArray *)parseRecordingName:(NSString *)filename { + NSString *rec = @"recording_"; //key that helps find recordings + NSString *subName = [filename substringFromIndex:[filename rangeOfString:rec].location]; //We remove the parent folders if they exist in the filename + NSArray *splitString = [subName componentsSeparatedByString:@"_"]; + //splitString: first element is the 'recording' prefix, last element is the date with the "E-d-MMM-yyyy-HH-mm-ss" format. + NSString *name = [[splitString subarrayWithRange:NSMakeRange(1, [splitString count] -2)] componentsJoinedByString:@""]; + NSDateFormatter *format = [[NSDateFormatter alloc] init]; + [format setDateFormat:@"E-d-MMM-yyyy-HH-mm-ss"]; + NSString *dateWithMkv = [splitString objectAtIndex:[splitString count]-1]; //this will be in the form "E-d-MMM-yyyy-HH-mm-ss.mkv", we have to delete the extension + NSDate *date = [format dateFromString:[dateWithMkv substringToIndex:[dateWithMkv length] - 4]]; + NSArray *res = [NSArray arrayWithObjects:name, date, nil]; + return res; +} + @end @implementation NSNumber (HumanReadableSize)