Re-style voice memo recording view
This commit is contained in:
parent
a978e453bc
commit
3a74bc21b2
@ -785,6 +785,7 @@
|
||||
88928A45263A2781009C9B30 /* state-change_confirm-up.caf in Resources */ = {isa = PBXBuildFile; fileRef = 88928A43263A2781009C9B30 /* state-change_confirm-up.caf */; };
|
||||
88928A71264099EC009C9B30 /* ConversationViewController+VoiceMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88928A70264099EC009C9B30 /* ConversationViewController+VoiceMessage.swift */; };
|
||||
88928A732640DC9B009C9B30 /* VoiceMessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88928A722640DC9B009C9B30 /* VoiceMessageModel.swift */; };
|
||||
88928A7526418904009C9B30 /* VoiceMessageTooltip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88928A7426418904009C9B30 /* VoiceMessageTooltip.swift */; };
|
||||
889C8A8D248831AC00C3D3D8 /* BlurTooltip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 889C8A8C248831AC00C3D3D8 /* BlurTooltip.swift */; };
|
||||
88A357B923639384009D6B9A /* MemberActionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88A357B823639384009D6B9A /* MemberActionSheet.swift */; };
|
||||
88A4CC09246CB0960082211F /* OnboardingTransferChoiceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88A4CC08246CB0960082211F /* OnboardingTransferChoiceViewController.swift */; };
|
||||
@ -1928,6 +1929,7 @@
|
||||
88928A5A263F0DEC009C9B30 /* hi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hi; path = translations/hi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
88928A70264099EC009C9B30 /* ConversationViewController+VoiceMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConversationViewController+VoiceMessage.swift"; sourceTree = "<group>"; };
|
||||
88928A722640DC9B009C9B30 /* VoiceMessageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageModel.swift; sourceTree = "<group>"; };
|
||||
88928A7426418904009C9B30 /* VoiceMessageTooltip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageTooltip.swift; sourceTree = "<group>"; };
|
||||
889C8A8C248831AC00C3D3D8 /* BlurTooltip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurTooltip.swift; sourceTree = "<group>"; };
|
||||
88A357B823639384009D6B9A /* MemberActionSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberActionSheet.swift; sourceTree = "<group>"; };
|
||||
88A358252363FF63009D6B9A /* mr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mr; path = translations/mr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
@ -3442,6 +3444,7 @@
|
||||
34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */,
|
||||
32AC5CE6255B51E900829BD8 /* JoinGroupCallPill.swift */,
|
||||
4C043929220A9EC800BAEA63 /* VoiceNoteLock.swift */,
|
||||
88928A7426418904009C9B30 /* VoiceMessageTooltip.swift */,
|
||||
);
|
||||
name = Views;
|
||||
path = views;
|
||||
@ -5176,6 +5179,7 @@
|
||||
45CD81EF1DC030E7004C9430 /* SyncPushTokensJob.swift in Sources */,
|
||||
88ABB8BD25352B8300229EAA /* GroupCallMemberSheet.swift in Sources */,
|
||||
88A4CC0E246CD6CC0082211F /* OnboardingTransferProgressViewController.swift in Sources */,
|
||||
88928A7526418904009C9B30 /* VoiceMessageTooltip.swift in Sources */,
|
||||
4C0C36F8226647FE0083F19A /* ThreadMapping.swift in Sources */,
|
||||
34B14D8D24F02A9600CC3A9A /* GroupLinkViewController.swift in Sources */,
|
||||
34D2CCE0206939B400CB1A14 /* DebugUIMessagesAssetLoader.m in Sources */,
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "mic-outline-64@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "mic-outline-64@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "mic-outline-64@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.6 KiB |
12
Signal/Images.xcassets/mic-solid-36.imageset/Contents.json
vendored
Normal file
12
Signal/Images.xcassets/mic-solid-36.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "mic-solid-36.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
94
Signal/Images.xcassets/mic-solid-36.imageset/mic-solid-36.pdf
vendored
Normal file
94
Signal/Images.xcassets/mic-solid-36.imageset/mic-solid-36.pdf
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 6.000000 1.500000 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
12.000000 10.500000 m
|
||||
10.209788 10.500000 8.492900 11.211159 7.227030 12.477030 c
|
||||
5.961159 13.742901 5.250000 15.459789 5.250000 17.250000 c
|
||||
5.250000 26.250000 l
|
||||
5.250000 28.040211 5.961159 29.757101 7.227030 31.022970 c
|
||||
8.492900 32.288841 10.209788 33.000000 12.000000 33.000000 c
|
||||
12.000000 33.000000 l
|
||||
13.790212 33.000000 15.507100 32.288841 16.772970 31.022970 c
|
||||
18.038841 29.757101 18.750000 28.040211 18.750000 26.250000 c
|
||||
18.750000 17.250000 l
|
||||
18.750000 15.459789 18.038841 13.742901 16.772970 12.477030 c
|
||||
15.507100 11.211159 13.790212 10.500000 12.000000 10.500000 c
|
||||
12.000000 10.500000 l
|
||||
h
|
||||
24.000000 17.250000 m
|
||||
21.750000 17.250000 l
|
||||
21.750000 14.664139 20.722771 12.184189 18.894291 10.355709 c
|
||||
17.065811 8.527229 14.585861 7.500000 12.000000 7.500000 c
|
||||
9.414139 7.500000 6.934188 8.527229 5.105709 10.355709 c
|
||||
3.277229 12.184189 2.250000 14.664139 2.250000 17.250000 c
|
||||
0.000000 17.250000 l
|
||||
0.004423 14.263853 1.120677 11.386356 3.131173 9.178417 c
|
||||
5.141669 6.970476 7.902320 5.590326 10.875000 5.306999 c
|
||||
10.875000 0.000000 l
|
||||
13.125000 0.000000 l
|
||||
13.125000 5.306999 l
|
||||
16.097679 5.590326 18.858332 6.970476 20.868828 9.178417 c
|
||||
22.879324 11.386356 23.995577 14.263853 24.000000 17.250000 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
1326
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 36.000000 36.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Type /Catalog
|
||||
/Pages 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000001416 00000 n
|
||||
0000001439 00000 n
|
||||
0000001612 00000 n
|
||||
0000001686 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1745
|
||||
%%EOF
|
||||
@ -117,6 +117,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (void)cancelVoiceMemoIfNecessary;
|
||||
|
||||
- (void)showVoiceMemoTooltip;
|
||||
|
||||
- (void)removeVoiceMemoTooltip;
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@property (nonatomic, nullable) OWSQuotedReplyModel *quotedReply;
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_CLOSED_ENUM(NSUInteger, VoiceMemoRecordingState){
|
||||
typedef NS_CLOSED_ENUM(NSUInteger, VoiceMemoRecordingState) {
|
||||
VoiceMemoRecordingState_Idle,
|
||||
VoiceMemoRecordingState_RecordingHeld,
|
||||
VoiceMemoRecordingState_RecordingLocked
|
||||
@ -78,9 +78,10 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
|
||||
#pragma mark - Voice Memo Recording UI
|
||||
|
||||
@property (nonatomic, nullable) UIView *voiceMemoUI;
|
||||
@property (nonatomic, nullable) VoiceMemoLockView *voiceMemoLockView;
|
||||
@property (nonatomic, nullable) UIView *voiceMemoContentView;
|
||||
@property (nonatomic, readonly) UIView *voiceMemoContentViewLeftSpacer;
|
||||
@property (nonatomic, readonly) UIView *voiceMemoContentViewRightSpacer;
|
||||
@property (nonatomic, readonly) UIView *voiceMemoContentView;
|
||||
@property (nonatomic) NSDate *voiceMemoStartTime;
|
||||
@property (nonatomic, nullable) NSTimer *voiceMemoUpdateTimer;
|
||||
@property (nonatomic) UIGestureRecognizer *voiceMemoGestureRecognizer;
|
||||
@ -90,6 +91,7 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
@property (nonatomic, readonly) BOOL isRecordingVoiceMemo;
|
||||
@property (nonatomic) VoiceMemoRecordingState voiceMemoRecordingState;
|
||||
@property (nonatomic) CGPoint voiceMemoGestureStartLocation;
|
||||
@property (nonatomic, nullable, weak) UIView *voiceMemoTooltip;
|
||||
@property (nonatomic, nullable) NSArray<NSLayoutConstraint *> *layoutContraints;
|
||||
@property (nonatomic) UIEdgeInsets receivedSafeAreaInsets;
|
||||
@property (nonatomic, nullable) InputLinkPreview *inputLinkPreview;
|
||||
@ -248,6 +250,8 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
_voiceMemoButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
self.voiceMemoButton.accessibilityLabel = NSLocalizedString(@"INPUT_TOOLBAR_VOICE_MEMO_BUTTON_ACCESSIBILITY_LABEL",
|
||||
@"accessibility label for the button which records voice memos");
|
||||
self.voiceMemoButton.accessibilityHint = NSLocalizedString(@"INPUT_TOOLBAR_VOICE_MEMO_BUTTON_ACCESSIBILITY_HINT",
|
||||
@"accessibility hint for the button which records voice memos");
|
||||
UIImage *micIcon = [Theme iconImage:ThemeIconMicButton];
|
||||
[self.voiceMemoButton setTemplateImage:micIcon tintColor:Theme.primaryIconColor];
|
||||
[self.voiceMemoButton autoSetDimensionsToSize:CGSizeMake(40, kMinToolbarItemHeight)];
|
||||
@ -292,6 +296,23 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
self.linkPreviewWrapper.backgroundColor = Theme.conversationInputBackgroundColor;
|
||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, _linkPreviewWrapper);
|
||||
|
||||
_voiceMemoContentView = [UIView containerView];
|
||||
self.voiceMemoContentView.hidden = YES;
|
||||
[self.voiceMemoContentView setContentHuggingHorizontalLow];
|
||||
[self.voiceMemoContentView setCompressionResistanceHorizontalLow];
|
||||
[self.voiceMemoContentView autoSetDimension:ALDimensionHeight toSize:kMinTextViewHeight];
|
||||
self.voiceMemoContentView.backgroundColor = Theme.conversationInputBackgroundColor;
|
||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, _voiceMemoContentView);
|
||||
|
||||
_voiceMemoContentViewLeftSpacer = [UIView containerView];
|
||||
self.voiceMemoContentViewLeftSpacer.hidden = YES;
|
||||
[self.voiceMemoContentViewLeftSpacer autoSetDimension:ALDimensionHeight toSize:kMinToolbarItemHeight];
|
||||
[self.voiceMemoContentViewLeftSpacer autoSetDimension:ALDimensionWidth toSize:16];
|
||||
_voiceMemoContentViewRightSpacer = [UIView containerView];
|
||||
self.voiceMemoContentViewRightSpacer.hidden = YES;
|
||||
[self.voiceMemoContentViewRightSpacer autoSetDimension:ALDimensionHeight toSize:kMinToolbarItemHeight];
|
||||
[self.voiceMemoContentViewRightSpacer autoSetDimension:ALDimensionWidth toSize:16];
|
||||
|
||||
// V Stack
|
||||
UIStackView *vStack = [[UIStackView alloc]
|
||||
initWithArrangedSubviews:@[ self.quotedReplyWrapper, self.linkPreviewWrapper, self.inputTextView ]];
|
||||
@ -300,6 +321,9 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
[vStack setContentHuggingHorizontalLow];
|
||||
[vStack setCompressionResistanceHorizontalLow];
|
||||
|
||||
[vStack addSubview:self.voiceMemoContentView];
|
||||
[self.voiceMemoContentView autoPinEdgesToEdgesOfView:self.inputTextView];
|
||||
|
||||
for (UIView *button in
|
||||
@[ self.cameraButton, self.attachmentButton, self.stickerButton, self.voiceMemoButton, self.sendButton ]) {
|
||||
[button setContentHuggingHorizontalHigh];
|
||||
@ -326,6 +350,7 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
|
||||
// Media Stack
|
||||
UIStackView *mediaAndSendStack = [[UIStackView alloc] initWithArrangedSubviews:@[
|
||||
self.voiceMemoContentViewRightSpacer,
|
||||
self.sendButton,
|
||||
self.cameraButton,
|
||||
self.voiceMemoButton,
|
||||
@ -339,6 +364,7 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
|
||||
// H Stack
|
||||
UIStackView *hStack = [[UIStackView alloc] initWithArrangedSubviews:@[
|
||||
self.voiceMemoContentViewLeftSpacer,
|
||||
self.attachmentButton,
|
||||
vStackRoundingOffsetView,
|
||||
mediaAndSendStack,
|
||||
@ -608,18 +634,42 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
// NOTE: We use untrimmedText, so that the sticker button disappears
|
||||
// even if the user just enters whitespace.
|
||||
BOOL hasTextInput = self.inputTextView.untrimmedText.length > 0;
|
||||
BOOL isRecordingVoiceMemo = self.isRecordingVoiceMemo;
|
||||
|
||||
// We used trimmed text for determining all the other button visibility.
|
||||
BOOL hasNonWhitespaceTextInput = self.inputTextView.trimmedText.length > 0;
|
||||
ensureViewHiddenState(self.attachmentButton, NO);
|
||||
if (hasNonWhitespaceTextInput) {
|
||||
if (isRecordingVoiceMemo) {
|
||||
BOOL hideSendButton = self.voiceMemoRecordingState == VoiceMemoRecordingState_RecordingHeld;
|
||||
ensureViewHiddenState(self.linkPreviewWrapper, YES);
|
||||
ensureViewHiddenState(self.quotedReplyWrapper, YES);
|
||||
ensureViewHiddenState(self.voiceMemoContentView, NO);
|
||||
ensureViewHiddenState(self.voiceMemoContentViewLeftSpacer, NO);
|
||||
ensureViewHiddenState(self.voiceMemoContentViewRightSpacer, !hideSendButton);
|
||||
ensureViewHiddenState(self.cameraButton, YES);
|
||||
ensureViewHiddenState(self.voiceMemoButton, YES);
|
||||
ensureViewHiddenState(self.sendButton, hideSendButton);
|
||||
ensureViewHiddenState(self.attachmentButton, YES);
|
||||
} else if (hasNonWhitespaceTextInput) {
|
||||
ensureViewHiddenState(self.linkPreviewWrapper, NO);
|
||||
ensureViewHiddenState(self.quotedReplyWrapper, NO);
|
||||
ensureViewHiddenState(self.voiceMemoContentView, YES);
|
||||
ensureViewHiddenState(self.voiceMemoContentViewLeftSpacer, YES);
|
||||
ensureViewHiddenState(self.voiceMemoContentViewRightSpacer, YES);
|
||||
ensureViewHiddenState(self.cameraButton, YES);
|
||||
ensureViewHiddenState(self.voiceMemoButton, YES);
|
||||
ensureViewHiddenState(self.sendButton, NO);
|
||||
ensureViewHiddenState(self.attachmentButton, NO);
|
||||
} else {
|
||||
ensureViewHiddenState(self.linkPreviewWrapper, NO);
|
||||
ensureViewHiddenState(self.quotedReplyWrapper, NO);
|
||||
ensureViewHiddenState(self.voiceMemoContentView, YES);
|
||||
ensureViewHiddenState(self.voiceMemoContentViewLeftSpacer, YES);
|
||||
ensureViewHiddenState(self.voiceMemoContentViewRightSpacer, YES);
|
||||
ensureViewHiddenState(self.cameraButton, NO);
|
||||
ensureViewHiddenState(self.voiceMemoButton, NO);
|
||||
ensureViewHiddenState(self.sendButton, YES);
|
||||
ensureViewHiddenState(self.attachmentButton, NO);
|
||||
}
|
||||
|
||||
// If the layout has changed, update the layout
|
||||
@ -632,7 +682,7 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
}
|
||||
|
||||
void (^updateBlock)(void) = ^{
|
||||
BOOL hideStickerButton = hasTextInput || self.quotedReply != nil;
|
||||
BOOL hideStickerButton = hasTextInput || isRecordingVoiceMemo || self.quotedReply != nil;
|
||||
ensureViewHiddenState(self.stickerButton, hideStickerButton);
|
||||
if (!hideStickerButton) {
|
||||
self.stickerButton.imageView.tintColor
|
||||
@ -783,6 +833,14 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setVoiceMemoRecordingState:(VoiceMemoRecordingState)voiceMemoRecordingState
|
||||
{
|
||||
if (voiceMemoRecordingState != _voiceMemoRecordingState) {
|
||||
_voiceMemoRecordingState = voiceMemoRecordingState;
|
||||
[self ensureButtonVisibilityWithIsAnimated:YES doLayout:YES];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Voice Memo
|
||||
|
||||
- (BOOL)isRecordingVoiceMemo
|
||||
@ -800,37 +858,24 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
[self removeVoiceMemoTooltip];
|
||||
|
||||
self.voiceMemoStartTime = [NSDate date];
|
||||
|
||||
[self.voiceMemoUI removeFromSuperview];
|
||||
[self.voiceMemoRedRecordingCircle removeFromSuperview];
|
||||
[self.voiceMemoLockView removeFromSuperview];
|
||||
|
||||
self.voiceMemoUI = [UIView new];
|
||||
self.voiceMemoUI.backgroundColor = Theme.toolbarBackgroundColor;
|
||||
[self addSubview:self.voiceMemoUI];
|
||||
[self.voiceMemoUI autoPinEdgesToSuperviewEdges];
|
||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, _voiceMemoUI);
|
||||
|
||||
self.voiceMemoContentView = [UIView new];
|
||||
[self.voiceMemoUI addSubview:self.voiceMemoContentView];
|
||||
[self.voiceMemoContentView autoPinEdgesToSuperviewMargins];
|
||||
[self.voiceMemoContentView removeAllSubviews];
|
||||
|
||||
self.recordingLabel = [UILabel new];
|
||||
self.recordingLabel.textColor = UIColor.ows_accentRedColor;
|
||||
self.recordingLabel.font = [UIFont ows_semiboldFontWithSize:14.f];
|
||||
self.recordingLabel.textColor = Theme.primaryTextColor;
|
||||
self.recordingLabel.font = UIFont.ows_dynamicTypeBodyClampedFont.ows_medium.ows_monospaced;
|
||||
[self.voiceMemoContentView addSubview:self.recordingLabel];
|
||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, _recordingLabel);
|
||||
|
||||
VoiceMemoLockView *voiceMemoLockView = [VoiceMemoLockView new];
|
||||
self.voiceMemoLockView = voiceMemoLockView;
|
||||
[self addSubview:voiceMemoLockView];
|
||||
[voiceMemoLockView autoPinEdgeToSuperviewMargin:ALEdgeRight];
|
||||
[voiceMemoLockView autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:self.voiceMemoContentView];
|
||||
[voiceMemoLockView setCompressionResistanceHigh];
|
||||
|
||||
[self updateVoiceMemo];
|
||||
|
||||
UIImage *icon = [UIImage imageNamed:@"mic-outline-24"];
|
||||
UIImage *icon = [UIImage imageNamed:@"mic-solid-24"];
|
||||
OWSAssertDebug(icon);
|
||||
UIImageView *imageView =
|
||||
[[UIImageView alloc] initWithImage:[icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];
|
||||
@ -840,70 +885,59 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
|
||||
NSMutableAttributedString *cancelString = [NSMutableAttributedString new];
|
||||
const CGFloat cancelArrowFontSize = ScaleFromIPhone5To7Plus(18.4, 20.f);
|
||||
const CGFloat cancelFontSize = ScaleFromIPhone5To7Plus(14.f, 16.f);
|
||||
NSString *arrowHead = @"\uf104";
|
||||
[cancelString append:arrowHead
|
||||
attributes:@{
|
||||
NSFontAttributeName : [UIFont ows_fontAwesomeFont:cancelArrowFontSize],
|
||||
NSForegroundColorAttributeName : UIColor.ows_accentRedColor,
|
||||
NSForegroundColorAttributeName : Theme.secondaryTextAndIconColor,
|
||||
NSBaselineOffsetAttributeName : @(-1.f),
|
||||
}];
|
||||
[cancelString append:@" "
|
||||
attributes:@{
|
||||
NSFontAttributeName : [UIFont ows_fontAwesomeFont:cancelArrowFontSize],
|
||||
NSForegroundColorAttributeName : UIColor.ows_accentRedColor,
|
||||
NSBaselineOffsetAttributeName : @(-1.f),
|
||||
}];
|
||||
[cancelString append:NSLocalizedString(@"VOICE_MESSAGE_CANCEL_INSTRUCTIONS",
|
||||
@"Indicates how to cancel a voice message.")
|
||||
attributes:@{
|
||||
NSFontAttributeName : [UIFont ows_semiboldFontWithSize:cancelFontSize],
|
||||
NSForegroundColorAttributeName : UIColor.ows_accentRedColor,
|
||||
}];
|
||||
[cancelString append:@" "
|
||||
attributes:@{
|
||||
NSFontAttributeName : [UIFont ows_fontAwesomeFont:cancelArrowFontSize],
|
||||
NSForegroundColorAttributeName : UIColor.ows_accentRedColor,
|
||||
NSBaselineOffsetAttributeName : @(-1.f),
|
||||
}];
|
||||
[cancelString append:arrowHead
|
||||
attributes:@{
|
||||
NSFontAttributeName : [UIFont ows_fontAwesomeFont:cancelArrowFontSize],
|
||||
NSForegroundColorAttributeName : UIColor.ows_accentRedColor,
|
||||
NSForegroundColorAttributeName : Theme.secondaryTextAndIconColor,
|
||||
NSBaselineOffsetAttributeName : @(-1.f),
|
||||
}];
|
||||
[cancelString
|
||||
append:NSLocalizedString(@"VOICE_MESSAGE_CANCEL_INSTRUCTIONS", @"Indicates how to cancel a voice message.")
|
||||
attributes:@{
|
||||
NSFontAttributeName : [UIFont ows_dynamicTypeSubheadlineClampedFont],
|
||||
NSForegroundColorAttributeName : Theme.secondaryTextAndIconColor,
|
||||
}];
|
||||
UILabel *cancelLabel = [UILabel new];
|
||||
cancelLabel.textAlignment = NSTextAlignmentRight;
|
||||
self.voiceMemoCancelLabel = cancelLabel;
|
||||
cancelLabel.attributedText = cancelString;
|
||||
[self.voiceMemoContentView addSubview:cancelLabel];
|
||||
|
||||
const CGFloat kRedCircleSize = 100.f;
|
||||
const CGFloat kRedCircleSize = 80.f;
|
||||
UIView *redCircleView = [[OWSCircleView alloc] initWithDiameter:kRedCircleSize];
|
||||
self.voiceMemoRedRecordingCircle = redCircleView;
|
||||
redCircleView.backgroundColor = UIColor.ows_accentRedColor;
|
||||
[self.voiceMemoContentView addSubview:redCircleView];
|
||||
[redCircleView autoAlignAxis:ALAxisHorizontal toSameAxisOfView:self.voiceMemoButton];
|
||||
[redCircleView autoAlignAxis:ALAxisVertical toSameAxisOfView:self.voiceMemoButton];
|
||||
[self addSubview:redCircleView];
|
||||
[redCircleView autoAlignAxis:ALAxisHorizontal toSameAxisOfView:self.voiceMemoContentView];
|
||||
[redCircleView autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:12];
|
||||
|
||||
UIImage *whiteIcon = [UIImage imageNamed:@"mic-outline-64"];
|
||||
UIImage *whiteIcon = [UIImage imageNamed:@"mic-solid-36"];
|
||||
OWSAssertDebug(whiteIcon);
|
||||
UIImageView *whiteIconView = [[UIImageView alloc] initWithImage:whiteIcon];
|
||||
[redCircleView addSubview:whiteIconView];
|
||||
[whiteIconView autoCenterInSuperview];
|
||||
|
||||
[imageView autoVCenterInSuperview];
|
||||
[imageView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:10.f];
|
||||
[imageView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:12.f];
|
||||
[self.recordingLabel autoVCenterInSuperview];
|
||||
[self.recordingLabel autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:imageView withOffset:5.f];
|
||||
[self.recordingLabel autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:imageView withOffset:8.f];
|
||||
[cancelLabel autoVCenterInSuperview];
|
||||
[cancelLabel autoHCenterInSuperview];
|
||||
[self.voiceMemoUI layoutIfNeeded];
|
||||
[cancelLabel autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:72.f];
|
||||
[cancelLabel autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:self.recordingLabel];
|
||||
|
||||
// Slide in the "slide to cancel" label.
|
||||
CGRect cancelLabelStartFrame = cancelLabel.frame;
|
||||
CGRect cancelLabelEndFrame = cancelLabel.frame;
|
||||
cancelLabelStartFrame.origin.x = self.voiceMemoUI.bounds.size.width;
|
||||
cancelLabel.frame = cancelLabelStartFrame;
|
||||
VoiceMemoLockView *voiceMemoLockView = [VoiceMemoLockView new];
|
||||
self.voiceMemoLockView = voiceMemoLockView;
|
||||
[self addSubview:voiceMemoLockView];
|
||||
[voiceMemoLockView autoAlignAxis:ALAxisVertical toSameAxisOfView:redCircleView];
|
||||
[voiceMemoLockView autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:redCircleView];
|
||||
[voiceMemoLockView setCompressionResistanceHigh];
|
||||
|
||||
voiceMemoLockView.transform = CGAffineTransformMakeScale(0.0, 0.0);
|
||||
[voiceMemoLockView layoutIfNeeded];
|
||||
@ -915,36 +949,18 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
}
|
||||
completion:nil];
|
||||
|
||||
[UIView animateWithDuration:0.35f
|
||||
delay:0.f
|
||||
options:UIViewAnimationOptionCurveEaseOut
|
||||
animations:^{
|
||||
cancelLabel.frame = cancelLabelEndFrame;
|
||||
}
|
||||
completion:nil];
|
||||
redCircleView.transform = CGAffineTransformMakeScale(0.0, 0.0);
|
||||
[UIView animateWithDuration:0.1f animations:^{ redCircleView.transform = CGAffineTransformIdentity; }];
|
||||
|
||||
// Pulse the icon.
|
||||
imageView.layer.opacity = 1.f;
|
||||
imageView.alpha = 1.f;
|
||||
[UIView animateWithDuration:0.5f
|
||||
delay:0.2f
|
||||
options:UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse
|
||||
| UIViewAnimationOptionCurveEaseIn
|
||||
animations:^{
|
||||
imageView.layer.opacity = 0.f;
|
||||
}
|
||||
animations:^{ imageView.alpha = 0.f; }
|
||||
completion:nil];
|
||||
|
||||
// Fade in the view.
|
||||
self.voiceMemoUI.layer.opacity = 0.f;
|
||||
[UIView animateWithDuration:0.2f
|
||||
animations:^{
|
||||
self.voiceMemoUI.layer.opacity = 1.f;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
self.voiceMemoUI.layer.opacity = 1.f;
|
||||
}
|
||||
}];
|
||||
|
||||
[self.voiceMemoUpdateTimer invalidate];
|
||||
self.voiceMemoUpdateTimer = [NSTimer weakScheduledTimerWithTimeInterval:0.1f
|
||||
@ -960,33 +976,29 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
|
||||
self.voiceMemoRecordingState = VoiceMemoRecordingState_Idle;
|
||||
|
||||
UIView *oldVoiceMemoUI = self.voiceMemoUI;
|
||||
UIView *oldVoiceMemoRedRecordingCircle = self.voiceMemoRedRecordingCircle;
|
||||
UIView *oldVoiceMemoLockView = self.voiceMemoLockView;
|
||||
|
||||
self.voiceMemoUI = nil;
|
||||
self.voiceMemoCancelLabel = nil;
|
||||
self.voiceMemoRedRecordingCircle = nil;
|
||||
self.voiceMemoContentView = nil;
|
||||
self.voiceMemoLockView = nil;
|
||||
self.recordingLabel = nil;
|
||||
|
||||
[self.voiceMemoUpdateTimer invalidate];
|
||||
self.voiceMemoUpdateTimer = nil;
|
||||
|
||||
[oldVoiceMemoUI.layer removeAllAnimations];
|
||||
|
||||
if (animated) {
|
||||
[UIView animateWithDuration:0.35f
|
||||
[UIView animateWithDuration:0.2f
|
||||
animations:^{
|
||||
oldVoiceMemoUI.layer.opacity = 0.f;
|
||||
oldVoiceMemoLockView.layer.opacity = 0.f;
|
||||
oldVoiceMemoRedRecordingCircle.alpha = 0.f;
|
||||
oldVoiceMemoLockView.alpha = 0.f;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
[oldVoiceMemoUI removeFromSuperview];
|
||||
[oldVoiceMemoRedRecordingCircle removeFromSuperview];
|
||||
[oldVoiceMemoLockView removeFromSuperview];
|
||||
}];
|
||||
} else {
|
||||
[oldVoiceMemoUI removeFromSuperview];
|
||||
[oldVoiceMemoRedRecordingCircle removeFromSuperview];
|
||||
[oldVoiceMemoLockView removeFromSuperview];
|
||||
}
|
||||
}
|
||||
@ -995,18 +1007,7 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
{
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
|
||||
UIButton *sendVoiceMemoButton = [[OWSButton alloc] initWithBlock:^{
|
||||
[weakSelf.inputToolbarDelegate voiceMemoGestureDidComplete];
|
||||
}];
|
||||
[sendVoiceMemoButton setTitle:MessageStrings.sendButton forState:UIControlStateNormal];
|
||||
[sendVoiceMemoButton setTitleColor:UIColor.ows_accentBlueColor forState:UIControlStateNormal];
|
||||
sendVoiceMemoButton.alpha = 0;
|
||||
[self.voiceMemoContentView addSubview:sendVoiceMemoButton];
|
||||
[sendVoiceMemoButton autoPinEdgeToSuperviewMargin:ALEdgeRight withInset:10.f];
|
||||
[sendVoiceMemoButton autoVCenterInSuperview];
|
||||
[sendVoiceMemoButton setCompressionResistanceHigh];
|
||||
[sendVoiceMemoButton setContentHuggingHigh];
|
||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, sendVoiceMemoButton);
|
||||
[ImpactHapticFeedback impactOccuredWithStyle:UIImpactFeedbackStyleMedium];
|
||||
|
||||
UIButton *cancelButton = [[OWSButton alloc] initWithBlock:^{
|
||||
[weakSelf.inputToolbarDelegate voiceMemoGestureDidCancel];
|
||||
@ -1014,7 +1015,8 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
[cancelButton setTitle:CommonStrings.cancelButton forState:UIControlStateNormal];
|
||||
[cancelButton setTitleColor:UIColor.ows_accentRedColor forState:UIControlStateNormal];
|
||||
cancelButton.alpha = 0;
|
||||
cancelButton.titleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
cancelButton.titleLabel.textAlignment = NSTextAlignmentRight;
|
||||
cancelButton.titleLabel.font = UIFont.ows_dynamicTypeBodyClampedFont.ows_medium;
|
||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, cancelButton);
|
||||
|
||||
[self.voiceMemoContentView addSubview:cancelButton];
|
||||
@ -1025,29 +1027,23 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
forConstraints:^{
|
||||
[cancelButton autoHCenterInSuperview];
|
||||
}];
|
||||
[cancelButton autoPinEdgeToSuperviewMargin:ALEdgeRight withInset:20];
|
||||
[cancelButton autoPinEdge:ALEdgeLeft
|
||||
toEdge:ALEdgeRight
|
||||
ofView:self.recordingLabel
|
||||
withOffset:4
|
||||
relation:NSLayoutRelationGreaterThanOrEqual];
|
||||
[cancelButton autoPinEdge:ALEdgeLeft
|
||||
toEdge:ALEdgeRight
|
||||
ofView:sendVoiceMemoButton
|
||||
withOffset:-4
|
||||
relation:NSLayoutRelationLessThanOrEqual];
|
||||
[cancelButton autoVCenterInSuperview];
|
||||
|
||||
[self.voiceMemoCancelLabel removeFromSuperview];
|
||||
[self.voiceMemoContentView layoutIfNeeded];
|
||||
[UIView animateWithDuration:0.35
|
||||
[UIView animateWithDuration:0.2f
|
||||
animations:^{
|
||||
self.voiceMemoCancelLabel.alpha = 0;
|
||||
self.voiceMemoRedRecordingCircle.alpha = 0;
|
||||
self.voiceMemoLockView.transform = CGAffineTransformMakeScale(0, 0);
|
||||
self.voiceMemoLockView.alpha = 0;
|
||||
cancelButton.alpha = 1.0;
|
||||
sendVoiceMemoButton.alpha = 1.0;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
[self.voiceMemoCancelLabel removeFromSuperview];
|
||||
[self.voiceMemoRedRecordingCircle removeFromSuperview];
|
||||
[self.voiceMemoLockView removeFromSuperview];
|
||||
}];
|
||||
@ -1059,7 +1055,7 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
|
||||
// Fade out the voice message views as the cancel gesture
|
||||
// proceeds as feedback.
|
||||
self.voiceMemoContentView.layer.opacity = MAX(0.f, MIN(1.f, 1.f - (float)cancelAlpha));
|
||||
self.voiceMemoCancelLabel.alpha = MAX(0.f, MIN(1.f, 1.f - (float)cancelAlpha));
|
||||
}
|
||||
|
||||
- (void)updateVoiceMemo
|
||||
@ -1078,13 +1074,46 @@ const CGFloat kMaxIPadTextViewHeight = 142;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showVoiceMemoTooltip
|
||||
{
|
||||
if (self.voiceMemoTooltip) {
|
||||
return;
|
||||
}
|
||||
|
||||
__weak ConversationInputToolbar *weakSelf = self;
|
||||
UIView *tooltip = [VoiceMessageTooltip presentFromView:self
|
||||
widthReferenceView:self
|
||||
tailReferenceView:self.voiceMemoButton
|
||||
wasTappedBlock:^{ [weakSelf removeVoiceMemoTooltip]; }];
|
||||
self.voiceMemoTooltip = tooltip;
|
||||
|
||||
const CGFloat tooltipDurationSeconds = 3.f;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(tooltipDurationSeconds * NSEC_PER_SEC)),
|
||||
dispatch_get_main_queue(),
|
||||
^{ [weakSelf removeVoiceMemoTooltip]; });
|
||||
}
|
||||
|
||||
- (void)removeVoiceMemoTooltip
|
||||
{
|
||||
UIView *voiceMemoTooltip = self.voiceMemoTooltip;
|
||||
self.voiceMemoTooltip = nil;
|
||||
|
||||
[UIView animateWithDuration:0.2
|
||||
animations:^{ voiceMemoTooltip.alpha = 0; }
|
||||
completion:^(BOOL finished) { [voiceMemoTooltip removeFromSuperview]; }];
|
||||
}
|
||||
|
||||
#pragma mark - Event Handlers
|
||||
|
||||
- (void)sendButtonPressed
|
||||
{
|
||||
OWSAssertDebug(self.inputToolbarDelegate);
|
||||
|
||||
[self.inputToolbarDelegate sendButtonPressed];
|
||||
if (self.isRecordingVoiceMemo) {
|
||||
[self.inputToolbarDelegate voiceMemoGestureDidComplete];
|
||||
} else {
|
||||
[self.inputToolbarDelegate sendButtonPressed];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cameraButtonPressed
|
||||
|
||||
@ -12,7 +12,6 @@ extension ConversationViewController {
|
||||
// Cancel any ongoing audio playback.
|
||||
cvAudioPlayer.stopAll()
|
||||
|
||||
SelectionHapticFeedback().selectionChanged()
|
||||
inputToolbar?.showVoiceMemoUI()
|
||||
|
||||
let voiceMessageModel = VoiceMessageModel(thread: thread)
|
||||
@ -38,6 +37,8 @@ extension ConversationViewController {
|
||||
return owsFailDebug("Unexpectedly missing voice message model")
|
||||
}
|
||||
|
||||
ImpactHapticFeedback.impactOccured(style: .light)
|
||||
|
||||
do {
|
||||
try voiceMessageModel.startRecording()
|
||||
} catch {
|
||||
@ -50,7 +51,13 @@ extension ConversationViewController {
|
||||
func cancelRecordingVoiceMessage() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
viewState.currentVoiceMessageModel?.stopRecording()
|
||||
defer { viewState.currentVoiceMessageModel = nil }
|
||||
guard let voiceMessageModel = viewState.currentVoiceMessageModel else { return }
|
||||
|
||||
voiceMessageModel.stopRecording()
|
||||
|
||||
NotificationHapticFeedback().notificationOccurred(.warning)
|
||||
|
||||
clearVoiceMessageDraft()
|
||||
viewState.currentVoiceMessageModel = nil
|
||||
inputToolbar?.cancelVoiceMemoIfNecessary()
|
||||
@ -66,17 +73,14 @@ extension ConversationViewController {
|
||||
|
||||
let duration = voiceMessageModel.stopRecording()
|
||||
|
||||
SelectionHapticFeedback().selectionChanged()
|
||||
|
||||
guard duration >= Self.minimumVoiceMessageDuration else {
|
||||
presentToast(text: NSLocalizedString(
|
||||
"VOICE_MESSAGE_TOO_SHORT_ALERT_MESSAGE",
|
||||
comment: "Message for the alert indicating the 'voice message' needs to be held to be held down to record."
|
||||
))
|
||||
inputToolbar?.showVoiceMemoTooltip()
|
||||
cancelRecordingVoiceMessage()
|
||||
return
|
||||
}
|
||||
|
||||
ImpactHapticFeedback.impactOccured(style: .medium)
|
||||
|
||||
if sendImmediately {
|
||||
inputToolbar?.hideVoiceMemoUI(true)
|
||||
sendVoiceMessageModel(voiceMessageModel)
|
||||
|
||||
@ -92,7 +92,8 @@ typedef NS_CLOSED_ENUM(NSUInteger, ConversationUIMode) {
|
||||
@property (nonatomic, readonly) ConversationViewLayout *layout;
|
||||
@property (nonatomic, readonly) CVViewState *viewState;
|
||||
|
||||
- (void)tryToSendAttachments:(NSArray<SignalAttachment *> *)attachments messageBody:(MessageBody *_Nullable)messageBody NS_SWIFT_NAME(tryToSendAttachments(_:messageBody:));
|
||||
- (void)tryToSendAttachments:(NSArray<SignalAttachment *> *)attachments
|
||||
messageBody:(MessageBody *_Nullable)messageBody NS_SWIFT_NAME(tryToSendAttachments(_:messageBody:));
|
||||
|
||||
- (void)updateBarButtonItems;
|
||||
- (void)ensureBannerState;
|
||||
|
||||
33
Signal/src/views/VoiceMessageTooltip.swift
Normal file
33
Signal/src/views/VoiceMessageTooltip.swift
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc
|
||||
public class VoiceMessageTooltip: TooltipView {
|
||||
@objc
|
||||
public class func present(fromView: UIView,
|
||||
widthReferenceView: UIView,
|
||||
tailReferenceView: UIView,
|
||||
wasTappedBlock: (() -> Void)?) -> VoiceMessageTooltip {
|
||||
return VoiceMessageTooltip(fromView: fromView, widthReferenceView: widthReferenceView, tailReferenceView: tailReferenceView, wasTappedBlock: wasTappedBlock)
|
||||
}
|
||||
|
||||
public override func bubbleContentView() -> UIView {
|
||||
let label = UILabel()
|
||||
label.text = NSLocalizedString(
|
||||
"VOICE_MESSAGE_TOO_SHORT_TOOLTIP",
|
||||
comment: "Message for the tooltip indicating the 'voice message' needs to be held to be held down to record."
|
||||
)
|
||||
label.font = UIFont.ows_dynamicTypeBodyClamped
|
||||
label.textColor = Theme.primaryTextColor
|
||||
|
||||
return horizontalStack(forSubviews: [label])
|
||||
}
|
||||
|
||||
public override var bubbleColor: UIColor { Theme.backgroundColor }
|
||||
public override var bubbleHSpacing: CGFloat { 8 }
|
||||
|
||||
public override var tailDirection: TooltipView.TailDirection { .down }
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@ -9,8 +9,8 @@ public class VoiceMemoLockView: UIView {
|
||||
|
||||
private var offsetConstraint: NSLayoutConstraint!
|
||||
|
||||
private let offsetFromToolbar: CGFloat = 40
|
||||
private let backgroundViewInitialHeight: CGFloat = 80
|
||||
private let offsetFromToolbar: CGFloat = 42
|
||||
private let backgroundViewInitialHeight: CGFloat = 72
|
||||
private var chevronTravel: CGFloat {
|
||||
return -1 * (backgroundViewInitialHeight - 50)
|
||||
}
|
||||
@ -29,7 +29,7 @@ public class VoiceMemoLockView: UIView {
|
||||
// we anchor the top so that the bottom "slides up" to meet it as the user slides the lock
|
||||
backgroundView.autoPinEdge(.top, to: .bottom, of: self, withOffset: -offsetFromToolbar - backgroundViewInitialHeight)
|
||||
|
||||
backgroundView.layoutMargins = UIEdgeInsets(top: 6, leading: 6, bottom: 6, trailing: 6)
|
||||
backgroundView.layoutMargins = UIEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8)
|
||||
|
||||
lockIconView.autoPinEdges(toSuperviewMarginsExcludingEdge: .bottom)
|
||||
chevronView.autoPinEdges(toSuperviewMarginsExcludingEdge: .top)
|
||||
@ -49,9 +49,9 @@ public class VoiceMemoLockView: UIView {
|
||||
// MARK: - Subviews
|
||||
|
||||
private lazy var lockIconView: UIImageView = {
|
||||
let imageTemplate = #imageLiteral(resourceName: "ic_lock_outline").withRenderingMode(.alwaysTemplate)
|
||||
let imageTemplate = #imageLiteral(resourceName: "lock-solid-24").withRenderingMode(.alwaysTemplate)
|
||||
let imageView = UIImageView(image: imageTemplate)
|
||||
imageView.tintColor = .ows_accentRed
|
||||
imageView.tintColor = Theme.isDarkThemeEnabled ? .ows_gray15 : .ows_gray75
|
||||
imageView.autoSetDimensions(to: CGSize(square: 24))
|
||||
return imageView
|
||||
}()
|
||||
@ -59,7 +59,7 @@ public class VoiceMemoLockView: UIView {
|
||||
private lazy var chevronView: UIView = {
|
||||
let label = UILabel()
|
||||
label.text = "\u{2303}"
|
||||
label.textColor = .ows_accentRed
|
||||
label.textColor = Theme.ternaryTextColor
|
||||
label.textAlignment = .center
|
||||
return label
|
||||
}()
|
||||
@ -67,13 +67,18 @@ public class VoiceMemoLockView: UIView {
|
||||
private lazy var backgroundView: UIView = {
|
||||
let view = UIView()
|
||||
|
||||
let width: CGFloat = 36
|
||||
let width: CGFloat = 40
|
||||
view.autoSetDimension(.width, toSize: width)
|
||||
view.backgroundColor = Theme.scrollButtonBackgroundColor
|
||||
view.backgroundColor = Theme.isDarkThemeEnabled ? .ows_gray65 : .ows_gray02
|
||||
view.layer.cornerRadius = width / 2
|
||||
view.layer.borderColor = Theme.washColor.cgColor
|
||||
view.layer.borderWidth = CGHairlineWidth()
|
||||
|
||||
view.layer.shadowOffset = CGSize(width: 0, height: 4)
|
||||
view.layer.shadowRadius = 12
|
||||
view.layer.shadowOpacity = 0.3
|
||||
view.layer.shadowColor = UIColor.black.cgColor
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
|
||||
@ -2590,6 +2590,9 @@
|
||||
/* accessibility label for the button which shows the sticker picker */
|
||||
"INPUT_TOOLBAR_STICKER_BUTTON_ACCESSIBILITY_LABEL" = "Stickers";
|
||||
|
||||
/* accessibility hint for the button which records voice memos */
|
||||
"INPUT_TOOLBAR_VOICE_MEMO_BUTTON_ACCESSIBILITY_HINT" = "Double tap and hold to record. Release to send or slide left to cancel. Double tap and slide up to lock recording.";
|
||||
|
||||
/* accessibility label for the button which records voice memos */
|
||||
"INPUT_TOOLBAR_VOICE_MEMO_BUTTON_ACCESSIBILITY_LABEL" = "Voice Message";
|
||||
|
||||
@ -5858,16 +5861,13 @@
|
||||
"VIEW_ONCE_OUTGOING_TOAST" = "Outgoing view-once media files are automatically removed after they are sent.";
|
||||
|
||||
/* Indicates how to cancel a voice message. */
|
||||
"VOICE_MESSAGE_CANCEL_INSTRUCTIONS" = "Slide to Cancel";
|
||||
"VOICE_MESSAGE_CANCEL_INSTRUCTIONS" = "Slide to cancel";
|
||||
|
||||
/* Filename for voice messages. */
|
||||
"VOICE_MESSAGE_FILE_NAME" = "Voice Message";
|
||||
|
||||
/* Message for the alert indicating the 'voice message' needs to be held to be held down to record. */
|
||||
"VOICE_MESSAGE_TOO_SHORT_ALERT_MESSAGE" = "Tap and hold to record a voice message.";
|
||||
|
||||
/* Title for the alert indicating the 'voice message' needs to be held to be held down to record. */
|
||||
"VOICE_MESSAGE_TOO_SHORT_ALERT_TITLE" = "Voice Message";
|
||||
/* Message for the tooltip indicating the 'voice message' needs to be held to be held down to record. */
|
||||
"VOICE_MESSAGE_TOO_SHORT_TOOLTIP" = "Press and hold to record.";
|
||||
|
||||
/* Activity indicator title, shown upon returning to the device manager, until you complete the provisioning process on desktop */
|
||||
"WAITING_TO_COMPLETE_DEVICE_LINK_TEXT" = "Complete setup on Signal Desktop.";
|
||||
|
||||
@ -144,6 +144,9 @@ public class OWSAudioSession: NSObject {
|
||||
} else if aggregateBehaviors.contains(.playAndRecord) {
|
||||
assert(avAudioSession.recordPermission == .granted)
|
||||
try avAudioSession.setCategory(.record)
|
||||
if #available(iOS 13, *) {
|
||||
try avAudioSession.setAllowHapticsAndSystemSoundsDuringRecording(true)
|
||||
}
|
||||
} else if aggregateBehaviors.contains(.audioMessagePlayback) {
|
||||
if self.device.proximityState {
|
||||
Logger.debug("proximityState: true")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user