diff --git a/Classes/AppManager.swift b/Classes/AppManager.swift
index d1987e746..9a86a02e9 100644
--- a/Classes/AppManager.swift
+++ b/Classes/AppManager.swift
@@ -64,4 +64,16 @@ enum NetworkType: Int {
//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.
}
+
+ @objc static func removeFile(file: String) {
+ let fileManager = FileManager.default
+ do {
+ try fileManager.removeItem(atPath: file)
+ Log.directLog(BCTBX_LOG_MESSAGE, text: "File :\(file) removed")
+
+ } catch {
+ print("Could not remove file : \(file) \(error)")
+ }
+ }
+
}
diff --git a/Classes/Base.lproj/ChatConversationView.xib b/Classes/Base.lproj/ChatConversationView.xib
index e321384a1..62255dabe 100644
--- a/Classes/Base.lproj/ChatConversationView.xib
+++ b/Classes/Base.lproj/ChatConversationView.xib
@@ -34,9 +34,18 @@
+
+
+
+
+
+
+
+
+
@@ -209,7 +218,7 @@
-
+
@@ -221,7 +230,7 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -551,8 +625,21 @@
+
-
+
@@ -637,10 +724,14 @@
-
+
+
+
+
+
diff --git a/Classes/CallManager.swift b/Classes/CallManager.swift
index 68c0869eb..a9546176a 100644
--- a/Classes/CallManager.swift
+++ b/Classes/CallManager.swift
@@ -639,6 +639,25 @@ import AVFoundation
AnyHashable("message"): message
])
}
+
+ @objc func activateAudioSession() {
+ lc?.activateAudioSession(actived: true)
+ }
+
+ @objc func getSpeakerSoundCard() -> String? {
+ var speakerCard: String? = nil
+ var earpieceCard: String? = nil
+ lc?.audioDevices.forEach { device in
+ if (device.hasCapability(capability: .CapabilityPlay)) {
+ if (device.type == .Speaker) {
+ speakerCard = device.id
+ } else if (device.type == .Earpiece) {
+ earpieceCard = device.id
+ }
+ }
+ }
+ return speakerCard != nil ? speakerCard : earpieceCard
+ }
}
diff --git a/Classes/ChatConversationTableView.h b/Classes/ChatConversationTableView.h
index 3fb5e83be..1a3f783d7 100644
--- a/Classes/ChatConversationTableView.h
+++ b/Classes/ChatConversationTableView.h
@@ -39,10 +39,10 @@
@protocol ChatConversationDelegate
-- (BOOL)resendMultiFiles:(FileContext *)newFileContext message:(NSString *)message;
-- (BOOL)resendFile:(NSData *)data withName:(NSString *)name type:(NSString *)type key:(NSString *)key message:(NSString *)message;
+- (BOOL)resendMultiFiles:(FileContext *)newFileContext message:(NSString *)message voiceContent:(LinphoneContent *)voiceContent;
+- (BOOL)resendFile:(NSData *)data withName:(NSString *)name type:(NSString *)type key:(NSString *)key message:(NSString *)message voiceContent:(LinphoneContent *)voiceContent;
- (BOOL)startFileUpload:(NSData *)data withName:(NSString *)name;
-- (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url;
+- (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url voiceContent:(LinphoneContent *)voiceContent;
- (void)tableViewIsScrolling;
@end
diff --git a/Classes/ChatConversationView.h b/Classes/ChatConversationView.h
index 06bb89c17..1a8d77309 100644
--- a/Classes/ChatConversationView.h
+++ b/Classes/ChatConversationView.h
@@ -92,6 +92,28 @@
@property (weak, nonatomic) IBOutlet UIInterfaceStyleButton *toggleMenuButton;
@property (weak, nonatomic) IBOutlet UIImageView *ephemeralndicator;
+
+// Voice recording
+@property (strong, nonatomic) IBOutlet UIView *vrView;
+@property (weak, nonatomic) IBOutlet UIView *vrInnerView;
+@property (weak, nonatomic) IBOutlet UIButton *vrDeleteButton;
+@property (weak, nonatomic) IBOutlet UIButton *vrPlayButton;
+@property (weak, nonatomic) IBOutlet UIImageView *vrWave;
+@property (weak, nonatomic) IBOutlet UIView *vrWaveMask;
+@property (weak, nonatomic) IBOutlet UIView *vrWaveMaskPlayer;
+@property (weak, nonatomic) IBOutlet UILabel *vrDurationLabel;
+@property NSTimer *vrRecordTimer;
+@property NSTimer *vrPlayerTimer;
+@property (weak, nonatomic) IBOutlet UIButton *toggleRecord;
+@property BOOL isVoiceRecording;
+@property BOOL isPendingVoiceRecord;
+@property BOOL isPlayingVoiceRecording;
+@property LinphoneRecorder *voiceRecorder;
+@property LinphonePlayer *sharedVoicePlayer;
+@property BOOL showVoiceRecorderView;
+@property BOOL preservePendingRecording;
+
+
+ (void)markAsRead:(LinphoneChatRoom *)chatRoom;
+ (void)autoDownload:(LinphoneChatMessage *)message;
+(NSString *)getKeyFromFileType:(NSString *)fileType fileName:(NSString *)name;
@@ -123,4 +145,8 @@
- (NSURL *)getICloudFileUrl:(NSString *)name;
- (void)removeCallBacks;
+-(void) startSharedPlayer:(const char *)path;
+-(void) stopSharedPlayer;
+-(BOOL) sharedPlayedIsPlaying:(const char *)path;
+
@end
diff --git a/Classes/ChatConversationView.m b/Classes/ChatConversationView.m
index bfdca00f3..05ec3b14c 100644
--- a/Classes/ChatConversationView.m
+++ b/Classes/ChatConversationView.m
@@ -120,6 +120,7 @@
[NSNumber numberWithFloat:0.5], NSLocalizedString(@"Average", nil),
[NSNumber numberWithFloat:0.0], NSLocalizedString(@"Minimum", nil), nil];
composingVisible = false;
+ [self initSharedPlayer];
}
return self;
}
@@ -187,6 +188,12 @@ static UICompositeViewDescription *compositeDescription = nil;
[_imagesCollectionView registerClass:[UIImageViewDeletable class] forCellWithReuseIdentifier:NSStringFromClass([UIImageViewDeletable class])];
[_imagesCollectionView setDataSource:self];
[_toggleSelectionButton setImage:[UIImage imageNamed:@"select_all_default.png"] forState:UIControlStateSelected];
+
+ _vrInnerView.layer.cornerRadius = 5.0f;
+ _vrInnerView.layer.masksToBounds = YES;
+ _vrWaveMaskPlayer.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"color_L"]]; // rgba(1,88,7,0.2);
+ _showVoiceRecorderView = false;
+
}
- (void)refreshData {
@@ -202,6 +209,10 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
+ [NSNotificationCenter.defaultCenter addObserver:self
+ selector:@selector(applicationWillEnterBackground)
+ name:UIApplicationDidEnterBackgroundNotification
+ object:nil];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
@@ -222,6 +233,16 @@ static UICompositeViewDescription *compositeDescription = nil;
selector:@selector(onLinphoneCoreReady:)
name:kLinphoneGlobalStateUpdate
object:nil];
+
+ [NSNotificationCenter.defaultCenter addObserver:self
+ selector:@selector(endVoicePlayingIfDoingSO:)
+ name:kLinphoneVoiceMessagePlayerLostFocus
+ object:nil];
+
+ [NSNotificationCenter.defaultCenter addObserver:self
+ selector:@selector(endVoicePlayingIfDoingSO:)
+ name:kLinphoneVoiceMessagePlayerEOF
+ object:nil];
if ([_fileContext count] > 0) {
[UIView animateWithDuration:0
delay:0
@@ -236,6 +257,7 @@ static UICompositeViewDescription *compositeDescription = nil;
CGRect tableViewFrame = [_tableController.tableView frame];
tableViewFrame.size.height -= 100;
[_tableController.tableView setFrame:tableViewFrame];
+ [self updateFramesInclRecordingView];
}
completion:nil];
}
@@ -245,10 +267,20 @@ static UICompositeViewDescription *compositeDescription = nil;
CGRect popupFrame = _popupMenu.frame;
popupFrame.size.height = 44 * [_popupMenu numberOfRowsInSection:0];
_popupMenu.frame = popupFrame;
+
+ // Voice recording
+ _vrView.hidden = true;
+ _preservePendingRecording = false;
}
- (void)viewWillDisappear:(BOOL)animated {
+
+ if (!_preservePendingRecording)
+ [self cancelVoiceRecording];
+ else if (_isVoiceRecording)
+ [self stopVoiceRecording];
+
[super viewWillDisappear:animated];
[self removeCallBacks];
@@ -256,7 +288,9 @@ static UICompositeViewDescription *compositeDescription = nil;
[_messageField resignFirstResponder];
[self setComposingVisible:false withDelay:0]; // will hide the "user is composing.." message
-
+
+ [self stopAllPlays];
+
[NSNotificationCenter.defaultCenter removeObserver:self];
PhoneMainView.instance.currentRoom = NULL;
}
@@ -287,10 +321,20 @@ static UICompositeViewDescription *compositeDescription = nil;
_backButton.hidden = _tableController.isEditing;
[_tableController scrollToBottom:true];
[self refreshImageDrawer];
+ [self stopAllPlays];
+
}
#pragma mark -
+- (void)applicationWillEnterBackground{
+ if (!_preservePendingRecording)
+ [self cancelVoiceRecording];
+ else if (_isVoiceRecording)
+ [self stopVoiceRecording];
+}
+
+
- (void)configureForRoom:(BOOL)editing {
if (!_chatRoom) {
_chatView.hidden = YES;
@@ -456,16 +500,24 @@ static UICompositeViewDescription *compositeDescription = nil;
}
}
-- (BOOL)sendMessage:(NSString *)message withExterlBodyUrl:(NSURL *)externalUrl {
+- (BOOL)sendMessage:(NSString *)message withExterlBodyUrl:(NSURL *)externalUrl andVoiceContent:(LinphoneContent *)voiceContent {
if (_chatRoom == NULL) {
LOGW(@"Cannot send message: No chatroom");
return FALSE;
}
- LinphoneChatMessage *msg = linphone_chat_room_create_message(_chatRoom, [message UTF8String]);
+ LinphoneChatMessage *msg = linphone_chat_room_create_empty_message(_chatRoom);
+ if (message && message.length > 0)
+ linphone_chat_message_add_utf8_text_content(msg, message.UTF8String);
+
if (externalUrl) {
linphone_chat_message_set_external_body_url(msg, [[externalUrl absoluteString] UTF8String]);
}
+
+ // Voice recording
+
+ if (voiceContent)
+ linphone_chat_message_add_content(msg, voiceContent);
// we must ref & unref message because in case of error, it will be destroy otherwise
linphone_chat_message_send(msg);
@@ -512,10 +564,10 @@ static UICompositeViewDescription *compositeDescription = nil;
[sheet addButtonWithTitle:NSLocalizedString(@"Send to this friend", nil)
block:^() {
if (![[self.messageField text] isEqualToString:@""]) {
- [self sendMessageInMessageField];
+ [self sendMessageInMessageFieldWithVoiceContent:nil];
}
if (url)
- [self sendMessage:url withExterlBodyUrl:nil];
+ [self sendMessage:url withExterlBodyUrl:nil andVoiceContent:nil];
else
[self startFileUpload:data withName:fileName];
}];
@@ -600,8 +652,8 @@ static UICompositeViewDescription *compositeDescription = nil;
_addressLabel.frame = frame;
}
-- (void)sendMessageInMessageField {
- if ([self sendMessage:[_messageField text] withExterlBodyUrl:nil]) {
+- (void)sendMessageInMessageFieldWithVoiceContent:(LinphoneContent *)voiceContent {
+ if ([self sendMessage:[_messageField text] withExterlBodyUrl:nil andVoiceContent:voiceContent]) {
scrollOnGrowingEnabled = FALSE;
[_messageField setText:@""];
scrollOnGrowingEnabled = TRUE;
@@ -657,6 +709,7 @@ static UICompositeViewDescription *compositeDescription = nil;
CGRect tableRect = [_tableController.view frame];
tableRect.size.height -= diff;
[_tableController.view setFrame:tableRect];
+ [self updateFramesInclRecordingView];
// if we're showing the compose message, update it position
if (![_composeLabel isHidden]) {
@@ -681,28 +734,40 @@ static UICompositeViewDescription *compositeDescription = nil;
}
- (IBAction)onSendClick:(id)event {
+ LinphoneContent *voiceContent = nil;
+ if (_isPendingVoiceRecord && _voiceRecorder && linphone_recorder_get_file(_voiceRecorder)) {
+ voiceContent = linphone_recorder_create_content(_voiceRecorder);
+ _isPendingVoiceRecord = false;
+ [self cancelVoiceRecording];
+ [self stopVoiceRecordPlayer];
+ }
+
+ if (!linphone_core_is_network_reachable(LC)) {
+ [PhoneMainView.instance presentViewController:[LinphoneUtils networkErrorView:@"send a message"] animated:YES completion:nil];
+ //return;
+ }
if ([_fileContext count] > 0) {
if (linphone_chat_room_get_capabilities(_chatRoom) & LinphoneChatRoomCapabilitiesConference) {
- [self startMultiFilesUpload];
+ [self startMultiFilesUploadWithVoiceContent:voiceContent];
} else {
int i = 0;
for (i = 0; i < [_fileContext count]-1; ++i) {
- [self startUploadData:[_fileContext.datasArray objectAtIndex:i] withType:[_fileContext.typesArray objectAtIndex:i] withName:[_fileContext.namesArray objectAtIndex:i] andMessage:NULL];
+ [self startUploadData:[_fileContext.datasArray objectAtIndex:i] withType:[_fileContext.typesArray objectAtIndex:i] withName:[_fileContext.namesArray objectAtIndex:i] andMessage:NULL voiceContent:voiceContent];
}
if (isOneToOne) {
- [self startUploadData:[_fileContext.datasArray objectAtIndex:i] withType:[_fileContext.typesArray objectAtIndex:i] withName:[_fileContext.namesArray objectAtIndex:i] andMessage:NULL];
+ [self startUploadData:[_fileContext.datasArray objectAtIndex:i] withType:[_fileContext.typesArray objectAtIndex:i] withName:[_fileContext.namesArray objectAtIndex:i] andMessage:NULL voiceContent:voiceContent];
if (![[self.messageField text] isEqualToString:@""]) {
- [self sendMessage:[_messageField text] withExterlBodyUrl:nil];
+ [self sendMessage:[_messageField text] withExterlBodyUrl:nil andVoiceContent:voiceContent];
}
} else {
- [self startUploadData:[_fileContext.datasArray objectAtIndex:i] withType:[_fileContext.typesArray objectAtIndex:i] withName:[_fileContext.namesArray objectAtIndex:i] andMessage:[self.messageField text]];
+ [self startUploadData:[_fileContext.datasArray objectAtIndex:i] withType:[_fileContext.typesArray objectAtIndex:i] withName:[_fileContext.namesArray objectAtIndex:i] andMessage:[self.messageField text] voiceContent:voiceContent];
}
}
[self clearMessageView];
return;
}
- [self sendMessageInMessageField];
+ [self sendMessageInMessageFieldWithVoiceContent:voiceContent];
}
- (IBAction)onListTap:(id)sender {
@@ -759,14 +824,11 @@ static UICompositeViewDescription *compositeDescription = nil;
}
- (IBAction)onMessageChange:(id)sender {
- if ([[_messageField text] length] > 0) {
- [_sendButton setEnabled:TRUE];
- } else {
- [_sendButton setEnabled:FALSE];
- }
+ [self setSendButtonState];
}
- (IBAction)onPictureClick:(id)event {
+ _preservePendingRecording = true;
[_messageField resignFirstResponder];
[ImagePickerView SelectImageFromDevice:self atPosition:_pictureButton inView:self.view withDocumentMenuDelegate:self];
@@ -800,15 +862,15 @@ static UICompositeViewDescription *compositeDescription = nil;
#pragma mark ChatRoomDelegate
-- (BOOL)startMultiFilesUpload {
+- (BOOL)startMultiFilesUploadWithVoiceContent:(LinphoneContent *)voiceContent {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
[fileTransfer setText:[self.messageField text]];
- [fileTransfer uploadFileContent:_fileContext forChatRoom:_chatRoom];
+ [fileTransfer uploadFileContent:_fileContext forChatRoom:_chatRoom andVoiceContent:voiceContent];
[_tableController scrollToBottom:true];
return TRUE;
}
-- (BOOL)startUploadData:(NSData *)data withType:(NSString*)type withName:(NSString *)name andMessage:(NSString *)message {
+- (BOOL)startUploadData:(NSData *)data withType:(NSString*)type withName:(NSString *)name andMessage:(NSString *)message voiceContent:(LinphoneContent *)voiceContent {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
if (message)
[fileTransfer setText:message];
@@ -818,7 +880,7 @@ static UICompositeViewDescription *compositeDescription = nil;
} else if ([type isEqualToString:@"image"]) {
key = @"localimage";
}
- [fileTransfer uploadData:data forChatRoom:_chatRoom type:type subtype:type name:name key:key];
+ [fileTransfer uploadData:data forChatRoom:_chatRoom type:type subtype:type name:name key:key voiceContent:voiceContent];
[_tableController scrollToBottom:true];
return TRUE;
}
@@ -830,26 +892,26 @@ static UICompositeViewDescription *compositeDescription = nil;
return TRUE;
}
-- (BOOL)resendMultiFiles:(FileContext *)newFileContext message:(NSString *)message {
+- (BOOL)resendMultiFiles:(FileContext *)newFileContext message:(NSString *)message voiceContent:(LinphoneContent *)voiceContent {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
if (message)
[fileTransfer setText:message];
- [fileTransfer uploadFileContent:newFileContext forChatRoom:_chatRoom];
+ [fileTransfer uploadFileContent:newFileContext forChatRoom:_chatRoom andVoiceContent:voiceContent];
[_tableController scrollToBottom:true];
return TRUE;
}
-- (BOOL)resendFile: (NSData *)data withName:(NSString *)name type:(NSString *)type key:(NSString *)key message:(NSString *)message {
+- (BOOL)resendFile: (NSData *)data withName:(NSString *)name type:(NSString *)type key:(NSString *)key message:(NSString *)message voiceContent:(LinphoneContent *)voiceContent{
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
if (message)
[fileTransfer setText:message];
- [fileTransfer uploadData:data forChatRoom:_chatRoom type:type subtype:type name:name key:key];
+ [fileTransfer uploadData:data forChatRoom:_chatRoom type:type subtype:type name:name key:key voiceContent:voiceContent];
[_tableController scrollToBottom:true];
return TRUE;
}
-- (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url {
- [self sendMessage:message withExterlBodyUrl:[NSURL URLWithString:url]];
+- (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url voiceContent:(LinphoneContent *)voiceContent {
+ [self sendMessage:message withExterlBodyUrl:[NSURL URLWithString:url] andVoiceContent:voiceContent];
}
#pragma mark ImagePickerDelegate
@@ -1077,6 +1139,7 @@ static UICompositeViewDescription *compositeDescription = nil;
[_messageView frame].origin.y - tableFrame.origin.y - composeIndicatorCompensation;
[_tableController.view setFrame:tableFrame];
+
// Scroll to bottom
NSInteger lastSection = [_tableController.tableView numberOfSections] - 1;
if (lastSection >= 0) {
@@ -1102,9 +1165,12 @@ static UICompositeViewDescription *compositeDescription = nil;
tableViewFrame.size.height = imagesFrame.origin.y - tableViewFrame.origin.y;
[_tableController.tableView setFrame:tableViewFrame];
}
+ if (_showVoiceRecorderView)
+ _vrView.hidden = true;
+ [self updateFramesInclRecordingView];
+
}
completion:^(BOOL finished){
-
}];
}
@@ -1158,6 +1224,7 @@ static UICompositeViewDescription *compositeDescription = nil;
tableFrame.size.height =
[_messageView frame].origin.y - tableFrame.origin.y - composeIndicatorCompensation;
[_tableController.view setFrame:tableFrame];
+
}
if ([_fileContext count] > 0){
@@ -1170,6 +1237,7 @@ static UICompositeViewDescription *compositeDescription = nil;
CGRect tableViewFrame = [_tableController.tableView frame];
tableViewFrame.size.height = imagesFrame.origin.y - tableViewFrame.origin.y;
[_tableController.tableView setFrame:tableViewFrame];
+
}
// Scroll
@@ -1183,10 +1251,13 @@ static UICompositeViewDescription *compositeDescription = nil;
animated:FALSE];
}
}
+ if (_showVoiceRecorderView)
+ _vrView.hidden = true;
+ [self updateFramesInclRecordingView];
+
}
completion:^(BOOL finished){
-
}];
}
@@ -1395,7 +1466,7 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog
[imgView setUuid:[_fileContext.uuidsArray objectAtIndex:[indexPath item]]];
[imgView setDeleteDelegate:self];
[imgView setFrame:imgFrame];
- [_sendButton setEnabled:TRUE];
+ [self setSendButtonState];
return imgView;
}
@@ -1416,10 +1487,10 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog
CGRect tableViewFrame = [_tableController.tableView frame];
tableViewFrame.size.height = imagesFrame.origin.y - tableViewFrame.origin.y;
[_tableController.tableView setFrame:tableViewFrame];
+ [self updateFramesInclRecordingView];
}
completion:nil];
- if ([_messageField.text isEqualToString:@""])
- [_sendButton setEnabled:FALSE];
+ [self setSendButtonState];
} else {
// resizing imagesView
CGRect imagesFrame = [_imagesView frame];
@@ -1430,6 +1501,7 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog
CGRect tableViewFrame = [_tableController.tableView frame];
tableViewFrame.size.height = imagesFrame.origin.y - tableViewFrame.origin.y;
[_tableController.tableView setFrame:tableViewFrame];
+ [self updateFramesInclRecordingView];
[_imagesCollectionView reloadData];
}
}
@@ -1592,4 +1664,268 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog
}
+// Voice redcording
+
+
+- (IBAction)onVrDelete:(id)sender {
+ [self cancelVoiceRecording];
+ [self stopVoiceRecordPlayer];
+}
+
+- (IBAction)onvrPlayPauseStop:(id)sender {
+ if (_isVoiceRecording) {
+ [self stopVoiceRecording];
+ } else {
+ if (_isPlayingVoiceRecording)
+ [self stopVoiceRecordPlayer];
+ else
+ [self playRecordedMessage];
+ }
+}
+
+- (IBAction)onVrStart:(id)sender {
+ if (_isVoiceRecording) {
+ [self stopVoiceRecording];
+ } else {
+ [self startVoiceRecording];
+ }
+}
+
+-(void) createVoiceRecorder {
+ LinphoneRecorderParams *p = linphone_core_create_recorder_params(LC);
+ linphone_recorder_params_set_file_format(p, LinphoneRecorderFileFormatWav);
+ _voiceRecorder = linphone_core_create_recorder(LC, p);
+ [CallManager.instance activateAudioSession];
+}
+
+-(void) cancelVoiceRecording {
+ _showVoiceRecorderView = false;
+ _toggleRecord.selected = false;
+ [self updateFramesInclRecordingView];
+ _isPendingVoiceRecord = false;
+ _isVoiceRecording = false;
+ if (_voiceRecorder && linphone_recorder_get_state(_voiceRecorder) != LinphoneRecorderClosed) {
+ linphone_recorder_close(_voiceRecorder);
+ const char *recordingFile = linphone_recorder_get_file(_voiceRecorder);
+ if (recordingFile) {
+ [AppManager removeFileWithFile:[NSString stringWithUTF8String:recordingFile]];
+ }
+ }
+ [self setSendButtonState];
+}
+
+-(void) stopVoiceRecording {
+ if (_voiceRecorder && linphone_recorder_get_state(_voiceRecorder) == LinphoneRecorderRunning) {
+ LOGI(@"[Chat Message Sending] Pausing / closing voice recorder");
+ linphone_recorder_pause(_voiceRecorder);
+ linphone_recorder_close(_voiceRecorder);
+ _vrDurationLabel.text = [self formattedDuration:linphone_recorder_get_duration(_voiceRecorder)];
+ }
+ _isVoiceRecording = false;
+ if ([LinphoneManager.instance lpConfigBoolForKey:@"voice_recording_send_right_away" withDefault:false]) {
+ [self onSendClick:nil];
+ }
+ [_vrPlayButton setImage:[UIImage imageNamed:@"vr_play"] forState:UIControlStateNormal];
+ _toggleRecord.selected = false;
+ _vrWaveMask.frame = CGRectZero;
+ [_vrRecordTimer invalidate];
+ _isPendingVoiceRecord = linphone_recorder_get_duration(_voiceRecorder) > 0;
+ [self setSendButtonState];
+
+}
+
+-(void) startVoiceRecording {
+
+ if (!_voiceRecorder)
+ [self createVoiceRecorder];
+
+ _toggleRecord.selected = true;
+ [_vrPlayButton setImage:[UIImage imageNamed:@"vr_stop"] forState:UIControlStateNormal];
+
+
+ _showVoiceRecorderView = true;
+ [self updateFramesInclRecordingView];
+ _isVoiceRecording = true;
+ _vrWaveMaskPlayer.frame = CGRectZero;
+
+ switch (linphone_recorder_get_state(_voiceRecorder)) {
+ case LinphoneRecorderClosed: {
+ NSString *filename = [NSString stringWithFormat:@"%@/voice-recording-%@.wav",[LinphoneManager imagesDirectory], [NSUUID UUID].UUIDString];
+ linphone_recorder_open(_voiceRecorder, filename.UTF8String);
+ linphone_recorder_start(_voiceRecorder);
+ LOGW(@"[Chat Message Sending] Recorder is closed opening it with %@",filename);
+ break;
+ };
+ case LinphoneRecorderRunning: {
+ LOGW(@"[Chat Message Sending] Recorder is already recording");
+ break;
+ }
+ case LinphoneRecorderPaused: {
+ LOGW(@"[Chat Message Sending] Recorder isn't closed, resuming recording");
+ linphone_recorder_start(_voiceRecorder);
+ }
+ }
+ _vrWaveMask.frame = _vrWave.frame;
+ _vrDurationLabel.text = [self formattedDuration:linphone_recorder_get_duration(_voiceRecorder)];
+ _vrRecordTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
+ target:self
+ selector:@selector(voiceRecordTimerUpdate)
+ userInfo:nil
+ repeats:YES];
+
+
+}
+
+-(void) voiceRecordTimerUpdate {
+ int recorderDuration = linphone_recorder_get_duration(_voiceRecorder);
+ if (recorderDuration > [LinphoneManager.instance lpConfigIntForKey:@"voice_recording_max_duration" withDefault:60000]) {
+ LOGW(@"[Chat Message Sending] Max duration for voice recording exceeded, stopping. (max = %d)",[LinphoneManager.instance lpConfigIntForKey:@"voice_recording_max_duration" withDefault:60000]);
+ [self stopVoiceRecording];
+ } else {
+ _vrDurationLabel.text = [self formattedDuration:linphone_recorder_get_duration(_voiceRecorder)];
+ CGRect r = _vrWaveMask.frame;
+ r.origin.x += 30;
+ r.size.width -= 30;
+ if (r.origin.x > _vrWave.frame.size.width) {
+ r = _vrWave.frame;
+ _vrWaveMask.frame = r;
+ } else {
+ [UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
+ _vrWaveMask.frame = r;
+ }completion:^(BOOL finished) {}];
+ }
+ }
+}
+
+// Playback Shared Player (new recording & chat bubble)
+
+- (void) initSharedPlayer {
+ LOGI(@"[Voice Message] Creating shared player");
+ _sharedVoicePlayer = linphone_core_create_local_player(LC, [CallManager.instance getSpeakerSoundCard].UTF8String, nil, nil);
+ LinphonePlayerCbs *cbs = linphone_factory_create_player_cbs(linphone_factory_get());
+ linphone_player_cbs_set_eof_reached(cbs, on_shared_player_eof_reached);
+ linphone_player_cbs_set_user_data(cbs, (__bridge void*)self);
+ linphone_player_add_callbacks(_sharedVoicePlayer, cbs);
+}
+
+-(void) startSharedPlayer:(const char *)path {
+ LOGI(@"[Voice Message] Starting shared player path = %s",path);
+ if (linphone_player_get_user_data(_sharedVoicePlayer)) {
+ LOGI(@"[Voice Message] a play was requested (%s), but there is already one going (%s)",path,(const char *)linphone_player_get_user_data(_sharedVoicePlayer) );
+ NSDictionary* userInfo = @{@"path": [NSString stringWithUTF8String:linphone_player_get_user_data(_sharedVoicePlayer)]};
+ [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneVoiceMessagePlayerLostFocus object:nil userInfo:userInfo];
+ }
+ [CallManager.instance changeRouteToSpeaker];
+ linphone_player_set_user_data(_sharedVoicePlayer, (void *)path);
+ linphone_player_open(_sharedVoicePlayer, path);
+ linphone_player_start(_sharedVoicePlayer);
+}
+
+-(void) stopSharedPlayer {
+ LOGI(@"[Voice Message] Stopping shared player path = %s",linphone_player_get_user_data(_sharedVoicePlayer) ? (const char *)linphone_player_get_user_data(_sharedVoicePlayer) : "nil");
+ linphone_player_pause(_sharedVoicePlayer);
+ linphone_player_seek(_sharedVoicePlayer,0);
+ linphone_player_close(_sharedVoicePlayer);
+ linphone_player_set_user_data(_sharedVoicePlayer, nil);
+}
+
+-(BOOL) sharedPlayedIsPlaying:(const char *)path {
+ return path && linphone_player_get_user_data(_sharedVoicePlayer) && !strcmp(path,linphone_player_get_user_data(_sharedVoicePlayer));
+}
+
+void on_shared_player_eof_reached(LinphonePlayer *p) {
+ LOGI(@"[Voice Message] End of file reached for player");
+ const char * currentPlayedFile = (const char *) linphone_player_get_user_data(p);
+ if (currentPlayedFile) {
+ NSDictionary* userInfo = @{@"path": [NSString stringWithUTF8String:currentPlayedFile]};
+ [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneVoiceMessagePlayerEOF object:nil userInfo:userInfo];
+ }
+
+// ChatConversationView *view = (__bridge ChatConversationView *)linphone_player_cbs_get_user_data(linphone_player_get_current_callbacks(p));
+// [view stopVoiceRecordPlayer];
+}
+
+// Playback of new recordings
+
+-(void) playRecordedMessage {
+ [_vrPlayButton setImage:[UIImage imageNamed:@"vr_stop"] forState:UIControlStateNormal];
+ _vrDurationLabel.text = [self formattedDuration:linphone_player_get_duration(_sharedVoicePlayer)];
+ _vrWaveMask.frame = CGRectZero;
+ CGRect r = CGRectZero;
+ r.size.height = _vrInnerView.frame.size.height;
+ _vrWaveMaskPlayer.frame = r;
+ _vrPlayerTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
+ target:self
+ selector:@selector(voicePlayTimerUpdate)
+ userInfo:nil
+ repeats:YES];
+ [self startSharedPlayer:linphone_recorder_get_file(_voiceRecorder)];
+ [self animPlayerOnce];
+ _isPlayingVoiceRecording = true;
+}
+
+-(void) voicePlayTimerUpdate {
+ _vrDurationLabel.text = [self formattedDuration:linphone_player_get_duration(_sharedVoicePlayer)];
+ [self animPlayerOnce];
+}
+
+-(void) animPlayerOnce {
+ CGRect r = _vrWaveMaskPlayer.frame;
+ r.size.width += _vrInnerView.frame.size.width / ((linphone_player_get_duration(_sharedVoicePlayer) / 1000)+1) ;
+ if (r.size.width > _vrInnerView.frame.size.width) {
+ r.size.width = _vrInnerView.frame.size.width;
+ }
+ [UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
+ _vrWaveMaskPlayer.frame = r;
+ }completion:^(BOOL finished) {}];
+}
+
+-(void) endVoicePlayingIfDoingSO:(NSNotification *)notif {
+ if (_isPlayingVoiceRecording)
+ [self stopVoiceRecordPlayer];
+}
+
+-(void) stopVoiceRecordPlayer {
+ [self stopSharedPlayer];
+ [_vrPlayButton setImage:[UIImage imageNamed:@"vr_play"] forState:UIControlStateNormal];
+ _isPlayingVoiceRecording = false;
+ [_vrPlayerTimer invalidate];
+ _vrWaveMaskPlayer.frame = CGRectZero;
+}
+
+-(NSString *)formattedDuration:(long)valueMs {
+ return [NSString stringWithFormat:@"%02ld:%02ld", valueMs/ 60000, (valueMs % 60000) / 1000 ];
+}
+
+-(void) updateFramesInclRecordingView { // place below the messages table.
+ BOOL showHide = _showVoiceRecorderView != !_vrView.hidden;
+ if (showHide)
+ _vrView.hidden = !_showVoiceRecorderView;
+
+ CGRect vrFrame = _vrView.frame;
+ CGRect tableFrame = _tableController.tableView.frame;
+ if (showHide) {
+ tableFrame.size.height = _showVoiceRecorderView ? tableFrame.size.height - vrFrame.size.height : tableFrame.size.height + vrFrame.size.height;
+ _tableController.tableView.frame = tableFrame;
+ [_tableController.tableView reloadData];
+ }
+ vrFrame.origin.y = tableFrame.origin.y+tableFrame.size.height;
+ _vrView.frame = vrFrame;
+}
+
+-(void) stopAllPlays {
+ if (linphone_player_get_user_data(_sharedVoicePlayer)) {
+ NSDictionary* userInfo = @{@"path": [NSString stringWithUTF8String:linphone_player_get_user_data(_sharedVoicePlayer)]};
+ [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneVoiceMessagePlayerLostFocus object:nil userInfo:userInfo];
+ }
+}
+
+// send button state
+
+-(void) setSendButtonState {
+ _sendButton.enabled = !_isVoiceRecording && ((_isPendingVoiceRecord && linphone_recorder_get_duration(_voiceRecorder) > 0) || [[_messageField text] length] > 0 || _fileContext.count > 0);
+}
+
+
+
@end
diff --git a/Classes/FirstLoginView.m b/Classes/FirstLoginView.m
index f00b1182d..a0d10d0a6 100644
--- a/Classes/FirstLoginView.m
+++ b/Classes/FirstLoginView.m
@@ -200,7 +200,7 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)onLoginClick:(id)sender {
if (!linphone_core_is_network_reachable(LC)) {
- [PhoneMainView.instance presentViewController:[LinphoneUtils networkErrorView] animated:YES completion:nil];
+ [PhoneMainView.instance presentViewController:[LinphoneUtils networkErrorView:@"configure an account"] animated:YES completion:nil];
return;
}
diff --git a/Classes/LinphoneManager.h b/Classes/LinphoneManager.h
index f168ada5a..1caf894f7 100644
--- a/Classes/LinphoneManager.h
+++ b/Classes/LinphoneManager.h
@@ -60,6 +60,8 @@ extern NSString *const kLinphoneFileTransferRecvUpdate;
extern NSString *const kLinphoneQRCodeFound;
extern NSString *const kLinphoneChatCreateViewChange;
extern NSString *const kLinphoneEphemeralMessageDeletedInRoom;
+extern NSString *const kLinphoneVoiceMessagePlayerEOF;
+extern NSString *const kLinphoneVoiceMessagePlayerLostFocus;
extern NSString *const kLinphoneMsgNotificationAppGroupId;
diff --git a/Classes/LinphoneManager.m b/Classes/LinphoneManager.m
index 64bb1b296..47b288df4 100644
--- a/Classes/LinphoneManager.m
+++ b/Classes/LinphoneManager.m
@@ -75,6 +75,8 @@ NSString *const kLinphoneFileTransferRecvUpdate = @"LinphoneFileTransferRecvUpda
NSString *const kLinphoneQRCodeFound = @"LinphoneQRCodeFound";
NSString *const kLinphoneChatCreateViewChange = @"LinphoneChatCreateViewChange";
NSString *const kLinphoneEphemeralMessageDeletedInRoom = @"LinphoneEphemeralMessageDeletedInRoom";
+NSString *const kLinphoneVoiceMessagePlayerEOF = @"LinphoneVoiceMessagePlayerEOF";
+NSString *const kLinphoneVoiceMessagePlayerLostFocus = @"LinphoneVoiceMessagePlayerLostFocus";
NSString *const kLinphoneMsgNotificationAppGroupId = @"group.org.linphone.phone.msgNotification";
@@ -1784,7 +1786,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
- (void)call:(const LinphoneAddress *)iaddr {
// First verify that network is available, abort otherwise.
if (!linphone_core_is_network_reachable(theLinphoneCore)) {
- [PhoneMainView.instance presentViewController:[LinphoneUtils networkErrorView] animated:YES completion:nil];
+ [PhoneMainView.instance presentViewController:[LinphoneUtils networkErrorView:@"place a call"] animated:YES completion:nil];
return;
}
diff --git a/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib b/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib
index d320d6580..b987f08b4 100644
--- a/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib
+++ b/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib
@@ -33,11 +33,16 @@
+
+
+
+
+
-
+
@@ -57,11 +62,11 @@
-
+
-
+
@@ -158,28 +163,60 @@
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -217,5 +254,7 @@
+
+
diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.h b/Classes/LinphoneUI/UIChatBubblePhotoCell.h
index 43b6b6eed..c1530c413 100644
--- a/Classes/LinphoneUI/UIChatBubblePhotoCell.h
+++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.h
@@ -44,6 +44,16 @@
@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *plusLongGestureRecognizer;
@property(strong, nonatomic) NSMutableArray *contentViews;
+// Video recordings
+@property (weak, nonatomic) IBOutlet UIView *vrView;
+@property (weak, nonatomic) IBOutlet UIButton *vrPlayPause;
+@property (weak, nonatomic) IBOutlet UILabel *vrTimerLabel;
+@property (weak, nonatomic) IBOutlet UIImageView *vrWave;
+@property (weak, nonatomic) IBOutlet UIView *vrWaveMaskPlayback;
+@property NSTimer *vrPlayerTimer;
+@property NSString *voiceRecordingFile;
+
+
- (void)setEvent:(LinphoneEventLog *)event;
- (void)setChatMessage:(LinphoneChatMessage *)message;
diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.m b/Classes/LinphoneUI/UIChatBubblePhotoCell.m
index d96ae3cb2..4e6766faa 100644
--- a/Classes/LinphoneUI/UIChatBubblePhotoCell.m
+++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.m
@@ -26,6 +26,11 @@
#import
#import
+#define voicePlayer VIEW(ChatConversationView).sharedVoicePlayer
+#define chatView VIEW(ChatConversationView)
+
+
+
@implementation UIChatBubblePhotoCell {
FileTransferDelegate *_ftd;
CGSize imageSize, bubbleSize, videoDefaultSize;
@@ -54,6 +59,8 @@
assetIsLoaded = FALSE;
self.contentView.userInteractionEnabled = NO;
_contentViews = [[NSMutableArray alloc] init];
+ self.vrWaveMaskPlayback.layer.cornerRadius = 10.0f;
+ self.vrWaveMaskPlayback.layer.masksToBounds = YES;
}
return self;
}
@@ -151,32 +158,55 @@
});
}
+
- (void)update {
if (self.message == nil) {
LOGW(@"Cannot update message room cell: NULL message");
return;
}
[super update];
-
+
+ NSMutableDictionary *encrptedFilePaths = NULL;
+ if ([VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId]) {
+ encrptedFilePaths = [LinphoneManager getMessageAppDataForKey:@"encryptedfiles" inMessage:self.message];
+ if (!encrptedFilePaths) {
+ encrptedFilePaths = [NSMutableDictionary dictionary];
+ }
+ }
+
+ _voiceRecordingFile = nil;
+ LinphoneContent *voiceContent = [UIChatBubbleTextCell voiceContent:self.message];
+ if (voiceContent) {
+ _voiceRecordingFile = [NSString stringWithUTF8String:[VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId] ? linphone_content_get_plain_file_path(voiceContent) : linphone_content_get_file_path(voiceContent)];
+ if ([VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId])
+ [encrptedFilePaths setValue:_voiceRecordingFile forKey:[NSString stringWithUTF8String:linphone_content_get_name(voiceContent)]];
+ [self setVoiceMessageDuration];
+ _vrWaveMaskPlayback.frame = CGRectZero;
+ _vrWaveMaskPlayback.backgroundColor = linphone_chat_message_is_outgoing(self.message) ? UIColor.orangeColor : UIColor.grayColor;
+ }
+
const bctbx_list_t *contents = linphone_chat_message_get_contents(self.message);
+
+ size_t contentCount = bctbx_list_size(contents);
+ if (voiceContent)
+ contentCount--;
BOOL multiParts = ((linphone_chat_message_get_text_content(self.message) != NULL) ? bctbx_list_size(contents) > 2 : bctbx_list_size(contents) > 1);
+ if (voiceContent && !multiParts) {
+ _cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = _playButton.hidden = _fileName.hidden = _fileView.hidden = _fileButton.hidden = YES;
+ return;
+ }
+
if (multiParts) {
if (!assetIsLoaded) {
- NSMutableDictionary *encrptedFilePaths = NULL;
- if ([VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId]) {
- encrptedFilePaths = [LinphoneManager getMessageAppDataForKey:@"encryptedfiles" inMessage:self.message];
- if (!encrptedFilePaths) {
- encrptedFilePaths = [NSMutableDictionary dictionary];
- }
- }
-
_imageGestureRecognizer.enabled = NO;
_cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = _playButton.hidden = _fileName.hidden = _fileView.hidden = _fileButton.hidden = YES;
-
const bctbx_list_t *it = contents;
int i;
for (it = contents, i=0; it != NULL; it=bctbx_list_next(it)){
LinphoneContent *content = (LinphoneContent *)it->data;
+ if (linphone_content_is_voice_recording(content)) { // Handled elsewhere
+ continue;
+ }
if (linphone_content_is_file_transfer(content) || linphone_content_is_file(content)){
UIChatContentView *contentView = [[UIChatContentView alloc] initWithFrame: CGRectMake(0,0,0,0)];
if([VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId] && (linphone_chat_message_is_outgoing(self.message) || linphone_content_is_file(content))) {
@@ -214,8 +244,6 @@
return;
}
-
-
const char *url = linphone_chat_message_get_external_body_url(self.message);
BOOL is_external =
(url && (strstr(url, "http") == url)) || linphone_chat_message_get_file_transfer_information(self.message);
@@ -713,16 +741,95 @@
textFrame.origin = CGPointMake(textFrame.origin.x, self.finalAssetView.frame.origin.y + self.finalAssetView.frame.size.height);
else
// When image hasn't be download
- textFrame.origin = CGPointMake(textFrame.origin.x, _imageSubView.frame.size.height + _imageSubView.frame.origin.y - 10);
+ textFrame.origin = CGPointMake(textFrame.origin.x, _voiceRecordingFile ? _fileView.frame.origin.y : _imageSubView.frame.size.height + _imageSubView.frame.origin.y - 10);
if (!utf8Text) {
textFrame.size.height = 0;
} else {
textFrame.size.height = bubbleFrame.size.height - 90;//textFrame.origin.x;
}
+
+ if (_voiceRecordingFile) {
+ CGRect vrFrame = _vrView.frame;
+ vrFrame.origin.y = _contentViews.count == 0 && !utf8Text ? _fileView.frame.origin.y : textFrame.origin.y;
+ _vrView.frame = vrFrame;
+ textFrame.origin.y += VOICE_RECORDING_PLAYER_HEIGHT;
+ _vrView.hidden = NO;
+ } else {
+ _vrView.hidden = YES;
+ }
self.messageText.frame = textFrame;
}
+// Voice messages
+
+static AVAudioPlayer* utilityPlayer;
+
+-(void) setVoiceMessageDuration {
+ NSError *error = nil;
+ AVAudioPlayer* utilityPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL URLWithString:_voiceRecordingFile] error:&error]; // Workaround as opening multiple linphone_players at the same time can cause crash (here for example layout refreshed whilst a voice memo is playing
+ _vrTimerLabel.text = [self formattedDuration:utilityPlayer.duration];
+ utilityPlayer = nil;
+}
+
+-(void) voicePlayTimerUpdate {
+ CGRect r = _vrWaveMaskPlayback.frame;
+ r.size.width += _vrView.frame.size.width / ((linphone_player_get_duration(voicePlayer) / 500)) ;
+ if (r.size.width > _vrView.frame.size.width) {
+ r.size.width = _vrView.frame.size.width;
+ }
+ [UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
+ _vrWaveMaskPlayback.frame = r;
+ }completion:^(BOOL finished) {}];
+}
+
+
+-(void) stopPlayer {
+ [NSNotificationCenter.defaultCenter removeObserver:self];
+ [chatView stopSharedPlayer];
+ [_vrPlayPause setImage:[UIImage imageNamed:@"vr_play"] forState:UIControlStateNormal];
+ [_vrPlayerTimer invalidate];
+ _vrWaveMaskPlayback.frame = CGRectZero;
+}
+
+-(NSString *)formattedDuration:(long)valueMs {
+ return [NSString stringWithFormat:@"%02ld:%02ld", valueMs/ 60, (valueMs % 60) ];
+}
+
+-(void) startPlayer {
+ [chatView startSharedPlayer:_voiceRecordingFile.UTF8String];
+ [NSNotificationCenter.defaultCenter addObserver:self
+ selector:@selector(stopPlayer)
+ name:kLinphoneVoiceMessagePlayerLostFocus
+ object:nil];
+
+ [NSNotificationCenter.defaultCenter addObserver:self
+ selector:@selector(stopPlayer)
+ name:kLinphoneVoiceMessagePlayerEOF
+ object:nil];
+
+ [_vrPlayPause setImage:[UIImage imageNamed:@"vr_stop"] forState:UIControlStateNormal];
+ CGRect r = CGRectZero;
+ r.size.height = _vrView.frame.size.height - 14;
+ r.origin.y = 7;
+ _vrWaveMaskPlayback.frame = r;
+ _vrPlayerTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
+ target:self
+ selector:@selector(voicePlayTimerUpdate)
+ userInfo:nil
+ repeats:YES];
+ [self voicePlayTimerUpdate];
+
+}
+
+- (IBAction)onVRPlayPauseClick:(id)sender {
+ if ([chatView sharedPlayedIsPlaying:_voiceRecordingFile.UTF8String])
+ [self stopPlayer];
+ else {
+ [self startPlayer];
+ }
+}
+
@end
diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.h b/Classes/LinphoneUI/UIChatBubbleTextCell.h
index 6778a8143..062164d5f 100644
--- a/Classes/LinphoneUI/UIChatBubbleTextCell.h
+++ b/Classes/LinphoneUI/UIChatBubbleTextCell.h
@@ -26,6 +26,9 @@
#define CELL_IMAGE_X_MARGIN 100
#define IMAGE_DEFAULT_WIDTH 120
#define IMAGE_DEFAULT_MARGIN 5
+#define VOICE_RECORDING_PLAYER_HEIGHT 60
+#define VOICE_RECORDING_PLAYER_WIDTH 300
+
@interface UIChatBubbleTextCell : UITableViewCell
@@ -72,5 +75,6 @@
+ (NSString *)TextMessageForChat:(LinphoneChatMessage *)message;
+ (CGSize)computeBoundingBox:(NSString *)text size:(CGSize)size font:(UIFont *)font;
+ (NSString *)ContactDateForChat:(LinphoneChatMessage *)message;
++(LinphoneContent *) voiceContent:(LinphoneChatMessage *)message;
@end
diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.m b/Classes/LinphoneUI/UIChatBubbleTextCell.m
index 808cb20b1..fc3371774 100644
--- a/Classes/LinphoneUI/UIChatBubbleTextCell.m
+++ b/Classes/LinphoneUI/UIChatBubbleTextCell.m
@@ -306,6 +306,12 @@
}
- (void)onResend {
+
+ if (!linphone_core_is_network_reachable(LC)) {
+ [PhoneMainView.instance presentViewController:[LinphoneUtils networkErrorView:@"send a message"] animated:YES completion:nil];
+ //return;
+ }
+
if (_message == nil || !linphone_chat_message_is_outgoing(_message))
return;
@@ -314,7 +320,13 @@
return;
const bctbx_list_t *contents = linphone_chat_message_get_contents(_message);
- BOOL multiParts = ((linphone_chat_message_get_text_content(self.message) != NULL) ? bctbx_list_size(contents) > 2 : bctbx_list_size(contents) > 1);
+ LinphoneContent *voiceContent = [UIChatBubbleTextCell voiceContent:_message];
+ size_t contentCount = bctbx_list_size(contents);
+ if (voiceContent)
+ contentCount--;
+
+ BOOL multiParts = ((linphone_chat_message_get_text_content(_message) != NULL) ? contentCount > 2 : contentCount > 1);
+
if (multiParts) {
FileContext *newfileContext = [[FileContext alloc] init];
[newfileContext clear];
@@ -323,6 +335,9 @@
const bctbx_list_t *it;
for (it = contents, i=0; it != NULL; it=bctbx_list_next(it)){
LinphoneContent *content = (LinphoneContent *)it->data;
+ if (linphone_content_is_voice_recording(content)) {
+ continue;
+ }
if (linphone_content_is_file_transfer(content) || linphone_content_is_file(content)){
NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)];
NSString *filePath = [encrptedFilePaths valueForKey:name];
@@ -335,11 +350,11 @@
[self onDelete];
dispatch_async(dispatch_get_main_queue(), ^ {
const char *text = linphone_chat_message_get_text_content(_message);
- [_chatRoomDelegate resendMultiFiles:newfileContext message: text? [NSString stringWithUTF8String:text]: NULL];
+ [_chatRoomDelegate resendMultiFiles:newfileContext message: text? [NSString stringWithUTF8String:text]: NULL voiceContent:voiceContent];
});
return;
}
- if (linphone_chat_message_get_file_transfer_information(_message) != NULL) {
+ if (!voiceContent && contentCount == 1 && linphone_chat_message_get_file_transfer_information(_message) != NULL) {
NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:_message];
NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:_message];
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:_message];
@@ -354,11 +369,11 @@
const char *text = linphone_chat_message_get_text_content(_message);
NSString *str = text ? [NSString stringWithUTF8String:text] : NULL;
if (localImage) {
- [_chatRoomDelegate resendFile: (data?:[ChatConversationView getFileData:localImage]) withName:localImage type:@"image" key:@"localimage" message:str];
+ [_chatRoomDelegate resendFile: (data?:[ChatConversationView getFileData:localImage]) withName:localImage type:@"image" key:@"localimage" message:str voiceContent:voiceContent];
} else if (localVideo) {
- [_chatRoomDelegate resendFile:(data?:[ChatConversationView getFileData:localVideo]) withName:localVideo type:@"video" key:@"localvideo" message:str];
+ [_chatRoomDelegate resendFile:(data?:[ChatConversationView getFileData:localVideo]) withName:localVideo type:@"video" key:@"localvideo" message:str voiceContent:voiceContent];
} else {
- [_chatRoomDelegate resendFile:(data?:[ChatConversationView getFileData:localFile]) withName:localFile type:@"image" key:@"localfile" message:str];
+ [_chatRoomDelegate resendFile:(data?:[ChatConversationView getFileData:localFile]) withName:localFile type:@"image" key:@"localfile" message:str voiceContent:voiceContent];
}
});
} else {
@@ -366,7 +381,10 @@
double delayInSeconds = 0.4;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
- [_chatRoomDelegate resendChat:self.textMessage withExternalUrl:nil];
+ NSString *text = self.textMessage;
+ if (voiceContent && [text isEqualToString:@"🗻"])
+ text = nil;
+ [_chatRoomDelegate resendChat:text withExternalUrl:nil voiceContent:voiceContent];
});
}
}
@@ -457,6 +475,20 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 44;
return image;
}
++(LinphoneContent *) voiceContent:(LinphoneChatMessage *)message {
+ for (const bctbx_list_t *it = linphone_chat_message_get_contents(message); it != NULL; it=bctbx_list_next(it)){
+ LinphoneContent *content = (LinphoneContent *)it->data;
+ if (linphone_content_is_voice_recording(content))
+ return content;
+ }
+ return nil;
+}
+
+
++(CGSize) addVoicePlayerToSize:(CGSize)size withMargins:(BOOL)margins {
+ return CGSizeMake(MAX(size.width,VOICE_RECORDING_PLAYER_WIDTH + (margins ? CELL_MESSAGE_X_MARGIN: 0)), size.height + VOICE_RECORDING_PLAYER_HEIGHT+(margins ? CELL_MESSAGE_Y_MARGIN: 0));
+
+}
+ (CGSize)ViewHeightForMessageText:(LinphoneChatMessage *)chat withWidth:(int)width textForImdn:(NSString *)imdnText {
NSString *messageText = [UIChatBubbleTextCell TextMessageForChat:chat];
@@ -484,14 +516,51 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 44;
CGFloat imagesh=0;
CGFloat max_imagesw=0;
CGFloat max_imagesh=0;
+ LinphoneContent *voiceContent = [self voiceContent:chat];
const bctbx_list_t *contents = linphone_chat_message_get_contents(chat);
- BOOL multiParts = ((linphone_chat_message_get_text_content(chat) != NULL) ? bctbx_list_size(contents) > 2 : bctbx_list_size(contents) > 1);
+ size_t contentCount = bctbx_list_size(contents);
+ if (voiceContent)
+ contentCount--;
+
+ BOOL multiParts = ((linphone_chat_message_get_text_content(chat) != NULL) ? contentCount > 2 : contentCount > 1);
+
+ if (voiceContent && contentCount == 0) {
+ size = CGSizeMake(VOICE_RECORDING_PLAYER_WIDTH, VOICE_RECORDING_PLAYER_HEIGHT);
+ CGSize textSize = CGSizeMake(0, 0);
+ if (![messageText isEqualToString:@"🗻"]) {
+ textSize = [self computeBoundingBox:messageText
+ size:CGSizeMake(max_imagesw , CGFLOAT_MAX)
+ font:messageFont];
+ }
+
+ // add size for message text
+ size.height += textSize.height;
+ size.width = MAX(textSize.width, size.width);
+ size.width = MAX(size.width + CELL_MESSAGE_X_MARGIN, CELL_MIN_WIDTH);
+ size.height = MAX(size.height + CELL_MESSAGE_Y_MARGIN, CELL_MIN_HEIGHT) ;
+ return size;
+ }
+
if (multiParts) {
const bctbx_list_t *it = contents;
NSMutableDictionary *encrptedFilePaths = [LinphoneManager getMessageAppDataForKey:@"encryptedfiles" inMessage:chat];
for (it = contents; it != NULL; it=bctbx_list_next(it)){
LinphoneContent *content = (LinphoneContent *)it->data;
+ if (linphone_content_is_voice_recording(content)) {
+ CGSize sSize = CGSizeMake(VOICE_RECORDING_PLAYER_WIDTH, VOICE_RECORDING_PLAYER_HEIGHT);
+ imagesw += sSize.width;
+ if (imagesw > width) {
+ imagesw = sSize.width;
+ max_imagesw = MAX(max_imagesw, imagesw);
+ max_imagesh += imagesh;
+ imagesh = sSize.height;
+ } else {
+ max_imagesw = MAX(max_imagesw, imagesw);
+ imagesh = MAX(imagesh, sSize.height);
+ }
+ continue;
+ }
UIImage *image;
if(!linphone_chat_message_is_outgoing(chat) && linphone_content_is_file_transfer(content)) {
// not yet downloaded
@@ -538,14 +607,15 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 44;
size.height = MAX(size.height + CELL_MESSAGE_Y_MARGIN, CELL_MIN_HEIGHT) ;
return size;
}
+
-
- LinphoneContent *fileContent = linphone_chat_message_get_file_transfer_information(chat);
+ LinphoneContent *fileContent = linphone_chat_message_get_utf8_text(chat) ? nil : linphone_chat_message_get_file_transfer_information(chat);
if (url == nil && fileContent == NULL) {
size = [self computeBoundingBox:messageText
size:CGSizeMake(width - CELL_MESSAGE_X_MARGIN - 4, CGFLOAT_MAX)
font:messageFont];
} else {
+
NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:chat];
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:chat];
NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:chat];
@@ -583,27 +653,43 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 44;
image = [[UIImage alloc] initWithData:data];
}
} else {
- return [self ViewHeightForFile:width];
+ CGSize fileSize = [self ViewHeightForFile:width];
+ if (voiceContent) {
+ fileSize = [self addVoicePlayerToSize:fileSize withMargins:true];
+ }
+ return fileSize;
}
originalImageSize = image.size;
} else {
if (!localImage && !localVideo) {
//We are loading the image
- return CGSizeMake(CELL_MIN_WIDTH + CELL_MESSAGE_X_MARGIN, CELL_MIN_HEIGHT + CELL_MESSAGE_Y_MARGIN + textSize.height + 20);
+ CGSize baseSize = CGSizeMake(CELL_MIN_WIDTH + CELL_MESSAGE_X_MARGIN, CELL_MIN_HEIGHT + CELL_MESSAGE_Y_MARGIN + textSize.height + 20);
+ if (voiceContent) {
+ baseSize = [self addVoicePlayerToSize:baseSize withMargins:true];
+ }
+ return baseSize;
}
if (localImage && [[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSData* data = [NSData dataWithContentsOfFile:filePath];
UIImage *image = [[UIImage alloc] initWithData:data];
if (!image) {
- return [self ViewHeightForFile:width];
+ CGSize fileSize = [self ViewHeightForFile:width];
+ if (voiceContent) {
+ fileSize = [self addVoicePlayerToSize:fileSize withMargins:true];
+ }
+ return fileSize;
}
originalImageSize = image.size;
} else if (localVideo && [[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
UIImage *image = [UIChatBubbleTextCell getImageFromVideoUrl:[NSURL fileURLWithPath:filePath]];
if (!image) {
- return [self ViewHeightForFile:width];
+ CGSize fileSize = [self ViewHeightForFile:width];
+ if (voiceContent) {
+ fileSize = [self addVoicePlayerToSize:fileSize withMargins:true];
+ }
+ return fileSize;
}
originalImageSize = image.size;
} else {
@@ -615,7 +701,11 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 44;
assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localVideo] options:nil];
if (![assets firstObject]) {
- return CGSizeMake(CELL_MIN_WIDTH, CELL_MIN_WIDTH + CELL_MESSAGE_Y_MARGIN + textSize.height);
+ CGSize baseSize = CGSizeMake(CELL_MIN_WIDTH, CELL_MIN_WIDTH + CELL_MESSAGE_Y_MARGIN + textSize.height);
+ if (voiceContent) {
+ baseSize = [self addVoicePlayerToSize:baseSize withMargins:true];
+ }
+ return baseSize;
} else {
PHAsset *asset = [assets firstObject];
originalImageSize = CGSizeMake([asset pixelWidth], [asset pixelHeight]);
@@ -627,6 +717,11 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 44;
size.height += textSize.height;
size.width = MAX(textSize.width, size.width);
}
+
+ if (voiceContent) {
+ size.width = MAX(size.width,VOICE_RECORDING_PLAYER_WIDTH);
+ size.height += VOICE_RECORDING_PLAYER_HEIGHT;
+ }
size.width = MAX(size.width + CELL_MESSAGE_X_MARGIN, CELL_MIN_WIDTH);
size.height = MAX(size.height + CELL_MESSAGE_Y_MARGIN, CELL_MIN_HEIGHT);
diff --git a/Classes/PhoneMainView.m b/Classes/PhoneMainView.m
index c8d78cd4f..680712541 100644
--- a/Classes/PhoneMainView.m
+++ b/Classes/PhoneMainView.m
@@ -830,7 +830,7 @@ static RootViewManager *rootViewManagerInstance = nil;
}
if (!linphone_core_is_network_reachable(LC)) {
- [PhoneMainView.instance presentViewController:[LinphoneUtils networkErrorView] animated:YES completion:nil];
+ [PhoneMainView.instance presentViewController:[LinphoneUtils networkErrorView:@"send a message"] animated:YES completion:nil];
return;
}
diff --git a/Classes/Utils/FileTransferDelegate.h b/Classes/Utils/FileTransferDelegate.h
index 3aa3d22db..21ff1d605 100644
--- a/Classes/Utils/FileTransferDelegate.h
+++ b/Classes/Utils/FileTransferDelegate.h
@@ -24,8 +24,8 @@
@interface FileTransferDelegate : NSObject
-- (void)uploadFileContent: (FileContext *)context forChatRoom:(LinphoneChatRoom *)chatRoom;
-- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key;
+- (void)uploadFileContent: (FileContext *)context forChatRoom:(LinphoneChatRoom *)chatRoom andVoiceContent:(LinphoneContent *)voiceContent;
+- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key voiceContent:(LinphoneContent *)voiceContent;
- (void)uploadImage:(UIImage *)image forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality;
- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name;
- (void)uploadVideo:(NSData *)data withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom;
diff --git a/Classes/Utils/FileTransferDelegate.m b/Classes/Utils/FileTransferDelegate.m
index d8d20670b..9ad65457e 100644
--- a/Classes/Utils/FileTransferDelegate.m
+++ b/Classes/Utils/FileTransferDelegate.m
@@ -102,7 +102,7 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *message,
}
}
-- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key{
+- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key voiceContent:(LinphoneContent *)voiceContent{
if ([[LinphoneManager.instance fileTransferDelegates] containsObject:self]) {
LOGW(@"fileTransferDelegates has already added %p", self);
return;
@@ -124,12 +124,15 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *message,
linphone_chat_message_cbs_set_file_transfer_progress_indication(linphone_chat_message_get_callbacks(_message), file_transfer_progress_indication_send);
[LinphoneManager setValueInMessageAppData:name forKey:key inMessage:_message];
+
+ if (voiceContent)
+ linphone_chat_message_add_content(_message, voiceContent);
LOGI(@"%p Uploading content from message %p", self, _message);
linphone_chat_message_send(_message);
}
-- (void)uploadFileContent: (FileContext *)context forChatRoom:(LinphoneChatRoom *)chatRoom {
+- (void)uploadFileContent: (FileContext *)context forChatRoom:(LinphoneChatRoom *)chatRoom andVoiceContent:(LinphoneContent *)voiceContent{
[LinphoneManager.instance.fileTransferDelegates addObject:self];
_message = linphone_chat_room_create_empty_message(chatRoom);
@@ -162,6 +165,8 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *message,
// todo indication progress
[LinphoneManager setValueInMessageAppData:names forKey:@"multiparts" inMessage:_message];
[LinphoneManager setValueInMessageAppData:types forKey:@"multipartstypes" inMessage:_message];
+ if (voiceContent)
+ linphone_chat_message_add_content(_message, voiceContent);
LOGI(@"%p Uploading content from message %p", self, _message);
linphone_chat_message_send(_message);
}
@@ -170,12 +175,12 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *message,
- (void)uploadImage:(UIImage *)image forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality {
NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]];
NSData *data = UIImageJPEGRepresentation(image, quality);
- [self uploadData:data forChatRoom:chatRoom type:@"image" subtype:@"jpg" name:name key:@"localimage"];
+ [self uploadData:data forChatRoom:chatRoom type:@"image" subtype:@"jpg" name:name key:@"localimage" voiceContent:nil];
}
- (void)uploadVideo:(NSData *)data withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom {
NSString *name = [NSString stringWithFormat:@"IMG-%f.MOV", [NSDate timeIntervalSinceReferenceDate]];
- [self uploadData:data forChatRoom:chatRoom type:@"video" subtype:@"mov" name:name key:@"localvideo"];
+ [self uploadData:data forChatRoom:chatRoom type:@"video" subtype:@"mov" name:name key:@"localvideo" voiceContent:nil];
}
- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name {
@@ -184,7 +189,7 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *message,
NSString *fileType = [[asset tracksWithMediaType:AVMediaTypeVideo] count] > 0 ? @"video" : @"file";
NSString *key = [ChatConversationView getKeyFromFileType:fileType fileName:name];
- [self uploadData:data forChatRoom:chatRoom type:fileType subtype:name.lastPathComponent name:name key:key];
+ [self uploadData:data forChatRoom:chatRoom type:fileType subtype:name.lastPathComponent name:name key:key voiceContent:nil];
}
- (BOOL)download:(LinphoneChatMessage *)message {
diff --git a/Classes/Utils/Utils.h b/Classes/Utils/Utils.h
index 7289bbe83..d515926af 100644
--- a/Classes/Utils/Utils.h
+++ b/Classes/Utils/Utils.h
@@ -39,7 +39,7 @@
+ (UIImage *)resizeImage:(UIImage *)imageToResize newSize:(CGSize)newSize;
+ (LinphoneAddress *)normalizeSipOrPhoneAddress:(NSString *)addr;
-+ (UIAlertController *)networkErrorView;
++ (UIAlertController *)networkErrorView:(NSString *)action;
typedef enum {
LinphoneDateHistoryList,
diff --git a/Classes/Utils/Utils.m b/Classes/Utils/Utils.m
index 4f6d1249d..7ae1fedca 100644
--- a/Classes/Utils/Utils.m
+++ b/Classes/Utils/Utils.m
@@ -516,12 +516,10 @@
return res;
}
-+ (UIAlertController *)networkErrorView {
++ (UIAlertController *)networkErrorView:(NSString *)action {
UIAlertController *errView =
[UIAlertController alertControllerWithTitle:NSLocalizedString(@"Network Error", nil)
- message:NSLocalizedString(@"There is no network connection available, "
- @"enable WIFI or WWAN prior to place a call",
- nil)
+ message:NSLocalizedString([@"There is no network connection available, enable WIFI or WWAN prior to " stringByAppendingString:action],nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil)
diff --git a/Resources/en.lproj/Localizable.strings b/Resources/en.lproj/Localizable.strings
index a69217772..76f33d13e 100644
Binary files a/Resources/en.lproj/Localizable.strings and b/Resources/en.lproj/Localizable.strings differ
diff --git a/Resources/fr.lproj/Localizable.strings b/Resources/fr.lproj/Localizable.strings
index b119be4b7..318063fa6 100644
Binary files a/Resources/fr.lproj/Localizable.strings and b/Resources/fr.lproj/Localizable.strings differ
diff --git a/Resources/images/vr_off.png b/Resources/images/vr_off.png
new file mode 100644
index 000000000..5f319e5c8
Binary files /dev/null and b/Resources/images/vr_off.png differ
diff --git a/Resources/images/vr_on.png b/Resources/images/vr_on.png
new file mode 100644
index 000000000..9fd24b67c
Binary files /dev/null and b/Resources/images/vr_on.png differ
diff --git a/Resources/images/vr_pause.png b/Resources/images/vr_pause.png
new file mode 100644
index 000000000..af2d89acc
Binary files /dev/null and b/Resources/images/vr_pause.png differ
diff --git a/Resources/images/vr_play.png b/Resources/images/vr_play.png
new file mode 100644
index 000000000..6de2686f0
Binary files /dev/null and b/Resources/images/vr_play.png differ
diff --git a/Resources/images/vr_stop.png b/Resources/images/vr_stop.png
new file mode 100644
index 000000000..6574a7d56
Binary files /dev/null and b/Resources/images/vr_stop.png differ
diff --git a/Resources/images/vr_wave.png b/Resources/images/vr_wave.png
new file mode 100644
index 000000000..26d7f1be7
Binary files /dev/null and b/Resources/images/vr_wave.png differ
diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj
index c1a97bd18..035b2db19 100644
--- a/linphone.xcodeproj/project.pbxproj
+++ b/linphone.xcodeproj/project.pbxproj
@@ -109,7 +109,7 @@
61AE364F20C00B370089D9D3 /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61AE364E20C00B370089D9D3 /* ShareViewController.m */; };
61AE365220C00B370089D9D3 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61AE365020C00B370089D9D3 /* MainInterface.storyboard */; };
61AE365620C00B370089D9D3 /* linphoneExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 61AE364B20C00B370089D9D3 /* linphoneExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
- 61AEBEA321906AFC00F35E7F /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
+ 61AEBEA321906AFC00F35E7F /* (null) in Frameworks */ = {isa = PBXBuildFile; };
61AEBEBD2191990A00F35E7F /* DevicesListView.m in Sources */ = {isa = PBXBuildFile; fileRef = 61AEBEBC2191990A00F35E7F /* DevicesListView.m */; };
61AEBEBF2191991F00F35E7F /* DevicesListView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 61AEBEBE2191991F00F35E7F /* DevicesListView.xib */; };
61AEBEC62191E47500F35E7F /* chevron_list_close.png in Resources */ = {isa = PBXBuildFile; fileRef = 61AEBEC52191E47500F35E7F /* chevron_list_close.png */; };
@@ -622,7 +622,7 @@
63E27A321C4FECD000D332AE /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 63E27A311C4FECD000D332AE /* LaunchScreen.xib */; };
63E27A521C50EDB000D332AE /* hold.mkv in Resources */ = {isa = PBXBuildFile; fileRef = 63E27A511C50EB2700D332AE /* hold.mkv */; };
63E59A3F1ADE70D900646FB3 /* InAppProductsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E59A3E1ADE70D900646FB3 /* InAppProductsManager.m */; };
- 63E802DB1C625AEF000D5509 /* BuildFile in Resources */ = {isa = PBXBuildFile; };
+ 63E802DB1C625AEF000D5509 /* (null) in Resources */ = {isa = PBXBuildFile; };
63EC8D391D7438660066547B /* AssistantLinkView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 63EC8D3B1D7438660066547B /* AssistantLinkView.xib */; };
63F1DF441BCE618E00EDED90 /* UIAddressTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F1DF431BCE618E00EDED90 /* UIAddressTextField.m */; };
63F1DF4B1BCE983200EDED90 /* CallConferenceTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F1DF4A1BCE983200EDED90 /* CallConferenceTableView.m */; };
@@ -675,6 +675,12 @@
C61B1BF22667D075001A4E4A /* menu_security_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C61B1BF12667D075001A4E4A /* menu_security_default.png */; };
C61B1BF42667D202001A4E4A /* more_menu_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C61B1BF32667D202001A4E4A /* more_menu_default.png */; };
C61B1BF72667EC6B001A4E4A /* ephemeral_messages_color_A.png in Resources */ = {isa = PBXBuildFile; fileRef = C61B1BF62667EC6B001A4E4A /* ephemeral_messages_color_A.png */; };
+ C622E3EF26A81290004F5434 /* vr_stop.png in Resources */ = {isa = PBXBuildFile; fileRef = C622E3E926A8128F004F5434 /* vr_stop.png */; };
+ C622E3F026A81290004F5434 /* vr_wave.png in Resources */ = {isa = PBXBuildFile; fileRef = C622E3EA26A8128F004F5434 /* vr_wave.png */; };
+ C622E3F126A81290004F5434 /* vr_on.png in Resources */ = {isa = PBXBuildFile; fileRef = C622E3EB26A8128F004F5434 /* vr_on.png */; };
+ C622E3F226A81290004F5434 /* vr_off.png in Resources */ = {isa = PBXBuildFile; fileRef = C622E3EC26A8128F004F5434 /* vr_off.png */; };
+ C622E3F326A81290004F5434 /* vr_pause.png in Resources */ = {isa = PBXBuildFile; fileRef = C622E3ED26A8128F004F5434 /* vr_pause.png */; };
+ C622E3F426A81290004F5434 /* vr_play.png in Resources */ = {isa = PBXBuildFile; fileRef = C622E3EE26A81290004F5434 /* vr_play.png */; };
C64A854E2667B67200252AD2 /* EphemeralSettingsView.m in Sources */ = {isa = PBXBuildFile; fileRef = C64A854D2667B67200252AD2 /* EphemeralSettingsView.m */; };
C64A85502667B67A00252AD2 /* EphemeralSettingsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C64A854F2667B67A00252AD2 /* EphemeralSettingsView.xib */; };
C64A85522667B74100252AD2 /* ephemeral_messages_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C64A85512667B74100252AD2 /* ephemeral_messages_default.png */; };
@@ -1714,6 +1720,12 @@
C61B1BF12667D075001A4E4A /* menu_security_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_security_default.png; sourceTree = ""; };
C61B1BF32667D202001A4E4A /* more_menu_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = more_menu_default.png; sourceTree = ""; };
C61B1BF62667EC6B001A4E4A /* ephemeral_messages_color_A.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ephemeral_messages_color_A.png; sourceTree = ""; };
+ C622E3E926A8128F004F5434 /* vr_stop.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = vr_stop.png; sourceTree = ""; };
+ C622E3EA26A8128F004F5434 /* vr_wave.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = vr_wave.png; sourceTree = ""; };
+ C622E3EB26A8128F004F5434 /* vr_on.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = vr_on.png; sourceTree = ""; };
+ C622E3EC26A8128F004F5434 /* vr_off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = vr_off.png; sourceTree = ""; };
+ C622E3ED26A8128F004F5434 /* vr_pause.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = vr_pause.png; sourceTree = ""; };
+ C622E3EE26A81290004F5434 /* vr_play.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = vr_play.png; sourceTree = ""; };
C64A854C2667B66900252AD2 /* EphemeralSettingsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EphemeralSettingsView.h; sourceTree = ""; };
C64A854D2667B67200252AD2 /* EphemeralSettingsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EphemeralSettingsView.m; sourceTree = ""; };
C64A854F2667B67A00252AD2 /* EphemeralSettingsView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EphemeralSettingsView.xib; sourceTree = ""; };
@@ -1951,7 +1963,7 @@
files = (
61DD7E1F2372E88F001BDD01 /* CoreLocation.framework in Frameworks */,
6180D6FE21EE41A800AD9CB6 /* QuickLook.framework in Frameworks */,
- 61AEBEA321906AFC00F35E7F /* BuildFile in Frameworks */,
+ 61AEBEA321906AFC00F35E7F /* (null) in Frameworks */,
D37DC7181594AF3400B2A5EB /* MessageUI.framework in Frameworks */,
61F1997520C6B1D5006B069A /* AVKit.framework in Frameworks */,
249660951FD6A35F001D55AA /* Photos.framework in Frameworks */,
@@ -2285,7 +2297,7 @@
path = LinphoneUI;
sourceTree = "";
};
- 29B97314FDCFA39411CA2CEA = {
+ 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
isa = PBXGroup;
children = (
8C23BCB71D82AAC3005F19BB /* linphone.entitlements */,
@@ -2447,6 +2459,12 @@
633FEBE11D3CD5570014B822 /* images */ = {
isa = PBXGroup;
children = (
+ C622E3EC26A8128F004F5434 /* vr_off.png */,
+ C622E3EB26A8128F004F5434 /* vr_on.png */,
+ C622E3ED26A8128F004F5434 /* vr_pause.png */,
+ C622E3EE26A81290004F5434 /* vr_play.png */,
+ C622E3E926A8128F004F5434 /* vr_stop.png */,
+ C622E3EA26A8128F004F5434 /* vr_wave.png */,
C61B1BF62667EC6B001A4E4A /* ephemeral_messages_color_A.png */,
C61B1BF32667D202001A4E4A /* more_menu_default.png */,
C61B1BF12667D075001A4E4A /* menu_security_default.png */,
@@ -3357,7 +3375,7 @@
fr,
hu,
);
- mainGroup = 29B97314FDCFA39411CA2CEA;
+ mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */;
productRefGroup = 19C28FACFE9D520D11CA2CBB /* Products */;
projectDirPath = "";
projectRoot = "";
@@ -3408,6 +3426,7 @@
615A2817217F280C0060F920 /* chat_list_indicator.png in Resources */,
633FEEFF1D3CD55A0014B822 /* options_add_call_disabled@2x.png in Resources */,
633FEF091D3CD55A0014B822 /* options_start_conference_disabled@2x.png in Resources */,
+ C622E3F326A81290004F5434 /* vr_pause.png in Resources */,
633FEE051D3CD5590014B822 /* cancel_edit_disabled@2x.png in Resources */,
633FEE5F1D3CD5590014B822 /* edit_list_default@2x.png in Resources */,
633FEEB61D3CD55A0014B822 /* numpad_3_over@2x.png in Resources */,
@@ -3418,7 +3437,7 @@
633FEED41D3CD55A0014B822 /* numpad_7_default@2x.png in Resources */,
633FEEE01D3CD55A0014B822 /* numpad_8_over~ipad@2x.png in Resources */,
633FEDDC1D3CD5590014B822 /* call_start_body_disabled~ipad.png in Resources */,
- 63E802DB1C625AEF000D5509 /* BuildFile in Resources */,
+ 63E802DB1C625AEF000D5509 /* (null) in Resources */,
633FEE2E1D3CD5590014B822 /* color_F.png in Resources */,
633FEDC51D3CD5590014B822 /* call_hangup_disabled@2x.png in Resources */,
633FEEDF1D3CD55A0014B822 /* numpad_8_over~ipad.png in Resources */,
@@ -3453,6 +3472,7 @@
633FEDD91D3CD5590014B822 /* call_start_body_default~ipad@2x.png in Resources */,
633FEE401D3CD5590014B822 /* contacts_all_selected.png in Resources */,
633FEE0C1D3CD5590014B822 /* chat_attachment_disabled.png in Resources */,
+ C622E3EF26A81290004F5434 /* vr_stop.png in Resources */,
633FEF001D3CD55A0014B822 /* options_default.png in Resources */,
CF15F21F20E4F9A3008B1DE6 /* UIImageViewDeletable.xib in Resources */,
633FEE951D3CD55A0014B822 /* micro_default@2x.png in Resources */,
@@ -3478,6 +3498,7 @@
633FEE7A1D3CD5590014B822 /* history_missed_default.png in Resources */,
633FEF121D3CD55A0014B822 /* pause_big_over_selected.png in Resources */,
633FED9D1D3CD5590014B822 /* add_field_default@2x.png in Resources */,
+ C622E3F426A81290004F5434 /* vr_play.png in Resources */,
639E9CB01C0DB83000019A75 /* SideMenuView.xib in Resources */,
633FEDBB1D3CD5590014B822 /* call_audio_start_default@2x.png in Resources */,
633FEF1A1D3CD55A0014B822 /* presence_away.png in Resources */,
@@ -3505,6 +3526,7 @@
615A283E2180A2560060F920 /* invite_linphone.png in Resources */,
633FEF281D3CD55A0014B822 /* route_earpiece_default.png in Resources */,
633FEE4F1D3CD5590014B822 /* delete_field_over@2x.png in Resources */,
+ C622E3F226A81290004F5434 /* vr_off.png in Resources */,
633FEE531D3CD5590014B822 /* dialer_alt_back@2x.png in Resources */,
633FEE3E1D3CD5590014B822 /* contacts_all_disabled.png in Resources */,
633FEEF31D3CD55A0014B822 /* numpad_over_background.png in Resources */,
@@ -3740,6 +3762,7 @@
633FEE191D3CD5590014B822 /* chat_send_over@2x.png in Resources */,
633FEF181D3CD55A0014B822 /* pause_small_over_selected.png in Resources */,
633FEE001D3CD5590014B822 /* camera_switch_over.png in Resources */,
+ C622E3F126A81290004F5434 /* vr_on.png in Resources */,
633FEF401D3CD55A0014B822 /* select_all_default.png in Resources */,
633FEDF01D3CD5590014B822 /* call_transfer_disabled.png in Resources */,
633FEE351D3CD5590014B822 /* conference_exit_default@2x.png in Resources */,
@@ -3772,6 +3795,7 @@
633FEEFA1D3CD55A0014B822 /* numpad_star~ipad.png in Resources */,
D38187B915FE342200C3EDCA /* ContactDetailsView.xib in Resources */,
633FEE921D3CD55A0014B822 /* menu.png in Resources */,
+ C622E3F026A81290004F5434 /* vr_wave.png in Resources */,
633FEDE41D3CD5590014B822 /* call_status_incoming~ipad.png in Resources */,
633FEE4C1D3CD5590014B822 /* delete_field_default.png in Resources */,
633FEE391D3CD5590014B822 /* contact_add_default@2x.png in Resources */,