|  |  |  | @ -577,6 +577,7 @@ typedef enum : NSUInteger { | 
		
	
		
			
				|  |  |  |  | @property (nonatomic) MPMoviePlayerController *videoPlayer; | 
		
	
		
			
				|  |  |  |  | @property (nonatomic) AVAudioRecorder *audioRecorder; | 
		
	
		
			
				|  |  |  |  | @property (nonatomic) OWSAudioAttachmentPlayer *audioAttachmentPlayer; | 
		
	
		
			
				|  |  |  |  | @property (nonatomic) NSUUID *voiceMessageUUID; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | @property (nonatomic) NSTimer *readTimer; | 
		
	
		
			
				|  |  |  |  | @property (nonatomic) UIView *navigationBarTitleView; | 
		
	
	
		
			
				
					|  |  |  | @ -608,6 +609,7 @@ typedef enum : NSUInteger { | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | @property (nonatomic) NSCache *messageAdapterCache; | 
		
	
		
			
				|  |  |  |  | @property (nonatomic) BOOL userHasScrolled; | 
		
	
		
			
				|  |  |  |  | @property (nonatomic) NSDate *lastMessageSentDate; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | @end | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -1547,6 +1549,7 @@ typedef enum : NSUInteger { | 
		
	
		
			
				|  |  |  |  |         } else { | 
		
	
		
			
				|  |  |  |  |             [ThreadUtil sendMessageWithText:text inThread:self.thread messageSender:self.messageSender]; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         self.lastMessageSentDate = [NSDate new]; | 
		
	
		
			
				|  |  |  |  |         if (updateKeyboardState) | 
		
	
		
			
				|  |  |  |  |         { | 
		
	
		
			
				|  |  |  |  |             [self toggleDefaultKeyboard]; | 
		
	
	
		
			
				
					|  |  |  | @ -2871,6 +2874,7 @@ typedef enum : NSUInteger { | 
		
	
		
			
				|  |  |  |  |         (unsigned long)attachment.data.length, | 
		
	
		
			
				|  |  |  |  |         [attachment mimeType]); | 
		
	
		
			
				|  |  |  |  |     [ThreadUtil sendMessageWithAttachment:attachment inThread:self.thread messageSender:self.messageSender]; | 
		
	
		
			
				|  |  |  |  |     self.lastMessageSentDate = [NSDate new]; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | - (NSURL *)videoTempFolder { | 
		
	
	
		
			
				
					|  |  |  | @ -3109,6 +3113,9 @@ typedef enum : NSUInteger { | 
		
	
		
			
				|  |  |  |  | { | 
		
	
		
			
				|  |  |  |  |     OWSAssert([NSThread isMainThread]); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     NSUUID *voiceMessageUUID = [NSUUID UUID]; | 
		
	
		
			
				|  |  |  |  |     self.voiceMessageUUID = voiceMessageUUID; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     __weak typeof(self) weakSelf = self; | 
		
	
		
			
				|  |  |  |  |     [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) { | 
		
	
		
			
				|  |  |  |  |         dispatch_async(dispatch_get_main_queue(), ^{ | 
		
	
	
		
			
				
					|  |  |  | @ -3117,6 +3124,12 @@ typedef enum : NSUInteger { | 
		
	
		
			
				|  |  |  |  |                 return; | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |             if (strongSelf.voiceMessageUUID != voiceMessageUUID) { | 
		
	
		
			
				|  |  |  |  |                 // This voice message recording has been cancelled | 
		
	
		
			
				|  |  |  |  |                 // before recording could begin. | 
		
	
		
			
				|  |  |  |  |                 return; | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |             if (granted) { | 
		
	
		
			
				|  |  |  |  |                 [strongSelf startRecordingVoiceMemo]; | 
		
	
		
			
				|  |  |  |  |             } else { | 
		
	
	
		
			
				
					|  |  |  | @ -3195,9 +3208,12 @@ typedef enum : NSUInteger { | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     DDLogInfo(@"endRecordingVoiceMemo"); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     self.voiceMessageUUID = nil; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     if (!self.audioRecorder) { | 
		
	
		
			
				|  |  |  |  |         // No voice message recording is in progress. | 
		
	
		
			
				|  |  |  |  |         // We may be cancelling before the recording could begin. | 
		
	
		
			
				|  |  |  |  |         DDLogError(@"%@ Missing audioRecorder", self.tag); | 
		
	
		
			
				|  |  |  |  |         OWSAssert(0); | 
		
	
		
			
				|  |  |  |  |         return; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -3210,6 +3226,8 @@ typedef enum : NSUInteger { | 
		
	
		
			
				|  |  |  |  |         DDLogInfo(@"Discarding voice message; too short."); | 
		
	
		
			
				|  |  |  |  |         self.audioRecorder = nil; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |         [self dismissKeyBoard]; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |         [OWSAlerts | 
		
	
		
			
				|  |  |  |  |             showAlertWithTitle: | 
		
	
		
			
				|  |  |  |  |                 NSLocalizedString(@"VOICE_MESSAGE_TOO_SHORT_ALERT_TITLE", | 
		
	
	
		
			
				
					|  |  |  | @ -3263,6 +3281,7 @@ typedef enum : NSUInteger { | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     [self.audioRecorder stop]; | 
		
	
		
			
				|  |  |  |  |     self.audioRecorder = nil; | 
		
	
		
			
				|  |  |  |  |     self.voiceMessageUUID = nil; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | #pragma mark Accessory View | 
		
	
	
		
			
				
					|  |  |  | @ -3573,6 +3592,17 @@ typedef enum : NSUInteger { | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     DDLogInfo(@"voiceMemoGestureDidStart"); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     const CGFloat kIgnoreMessageSendDoubleTapDurationSeconds = 2.f; | 
		
	
		
			
				|  |  |  |  |     if (self.lastMessageSentDate && | 
		
	
		
			
				|  |  |  |  |         [[NSDate new] timeIntervalSinceDate:self.lastMessageSentDate] < kIgnoreMessageSendDoubleTapDurationSeconds) { | 
		
	
		
			
				|  |  |  |  |         // If users double-taps the message send button, the second tap can look like a | 
		
	
		
			
				|  |  |  |  |         // very short voice message gesture.  We want to ignore such gestures. | 
		
	
		
			
				|  |  |  |  |         [((OWSMessagesToolbarContentView *)self.inputToolbar.contentView)cancelVoiceMemoIfNecessary]; | 
		
	
		
			
				|  |  |  |  |         [((OWSMessagesInputToolbar *)self.inputToolbar) hideVoiceMemoUI:NO]; | 
		
	
		
			
				|  |  |  |  |         [self cancelRecordingVoiceMemo]; | 
		
	
		
			
				|  |  |  |  |         return; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     [((OWSMessagesInputToolbar *)self.inputToolbar)showVoiceMemoUI]; | 
		
	
		
			
				|  |  |  |  |     AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); | 
		
	
		
			
				|  |  |  |  |     [self requestRecordingVoiceMemo]; | 
		
	
	
		
			
				
					|  |  |  | 
 |