Tweak calls.
This commit is contained in:
parent
74ce3012cf
commit
1a9a5016fe
|
@ -43,6 +43,7 @@
|
|||
340FC8D0205BF2FA007AEB0F /* OWSBackupIO.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC8CE205BF2FA007AEB0F /* OWSBackupIO.m */; };
|
||||
341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */ = {isa = PBXBuildFile; fileRef = 341F2C0E1F2B8AE700D07D6B /* DebugUIMisc.m */; };
|
||||
34277A5E20751BDC006049F2 /* OWSQuotedMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34277A5C20751BDC006049F2 /* OWSQuotedMessageView.m */; };
|
||||
3427C64020EFD43E00EEC730 /* OWSCallMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3427C63E20EFD43D00EEC730 /* OWSCallMessageCell.m */; };
|
||||
3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3430FE171F7751D4000EC51B /* GiphyAPI.swift */; };
|
||||
34330A5A1E7875FB00DF2FB9 /* fontawesome-webfont.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A591E7875FB00DF2FB9 /* fontawesome-webfont.ttf */; };
|
||||
34330A5C1E787A9800DF2FB9 /* dripicons-v2.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A5B1E787A9800DF2FB9 /* dripicons-v2.ttf */; };
|
||||
|
@ -640,6 +641,8 @@
|
|||
341F2C0E1F2B8AE700D07D6B /* DebugUIMisc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIMisc.m; sourceTree = "<group>"; };
|
||||
34277A5C20751BDC006049F2 /* OWSQuotedMessageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSQuotedMessageView.m; sourceTree = "<group>"; };
|
||||
34277A5D20751BDC006049F2 /* OWSQuotedMessageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSQuotedMessageView.h; sourceTree = "<group>"; };
|
||||
3427C63E20EFD43D00EEC730 /* OWSCallMessageCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSCallMessageCell.m; sourceTree = "<group>"; };
|
||||
3427C63F20EFD43E00EEC730 /* OWSCallMessageCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSCallMessageCell.h; sourceTree = "<group>"; };
|
||||
3430FE171F7751D4000EC51B /* GiphyAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GiphyAPI.swift; sourceTree = "<group>"; };
|
||||
34330A591E7875FB00DF2FB9 /* fontawesome-webfont.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "fontawesome-webfont.ttf"; sourceTree = "<group>"; };
|
||||
34330A5B1E787A9800DF2FB9 /* dripicons-v2.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "dripicons-v2.ttf"; sourceTree = "<group>"; };
|
||||
|
@ -1753,6 +1756,8 @@
|
|||
34DBF006206C3CB200025978 /* OWSBubbleShapeView.m */,
|
||||
34DBF002206BD5A500025978 /* OWSBubbleView.h */,
|
||||
34DBF001206BD5A500025978 /* OWSBubbleView.m */,
|
||||
3427C63F20EFD43E00EEC730 /* OWSCallMessageCell.h */,
|
||||
3427C63E20EFD43D00EEC730 /* OWSCallMessageCell.m */,
|
||||
34D1F09A1F867BFC0066283D /* OWSContactOffersCell.h */,
|
||||
34D1F09B1F867BFC0066283D /* OWSContactOffersCell.m */,
|
||||
3403B95C20EA9527001A1F44 /* OWSContactShareButtonsView.h */,
|
||||
|
@ -3200,6 +3205,7 @@
|
|||
34DBF007206C3CB200025978 /* OWSBubbleShapeView.m in Sources */,
|
||||
34D1F0BA1F8800D90066283D /* OWSAudioMessageView.m in Sources */,
|
||||
34D8C02B1ED3685800188D7C /* DebugUIContacts.m in Sources */,
|
||||
3427C64020EFD43E00EEC730 /* OWSCallMessageCell.m in Sources */,
|
||||
45C9DEB81DF4E35A0065CA84 /* WebRTCCallMessageHandler.swift in Sources */,
|
||||
34D1F0501F7D45A60066283D /* GifPickerCell.swift in Sources */,
|
||||
34D99C931F2937CC00D284D6 /* OWSAnalytics.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "phonedown-20@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "phonedown-20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "phonedown-20@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 381 B |
Binary file not shown.
After Width: | Height: | Size: 719 B |
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "phoneup-20@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "phoneup-20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "phoneup-20@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 402 B |
Binary file not shown.
After Width: | Height: | Size: 748 B |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -11,6 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@class OWSContactsManager;
|
||||
@class TSAttachmentPointer;
|
||||
@class TSAttachmentStream;
|
||||
@class TSCall;
|
||||
@class TSInteraction;
|
||||
@class TSMessage;
|
||||
@class TSOutgoingMessage;
|
||||
|
@ -25,6 +26,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
- (void)showMetadataViewForViewItem:(ConversationViewItem *)conversationItem;
|
||||
- (void)conversationCell:(ConversationViewCell *)cell didTapReplyForViewItem:(ConversationViewItem *)conversationItem;
|
||||
|
||||
#pragma mark - Calls
|
||||
|
||||
- (void)didTapCall:(TSCall *)call;
|
||||
|
||||
#pragma mark - System Cell
|
||||
|
||||
// TODO: We might want to decompose this method.
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSBubbleView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern const CGFloat kOWSMessageCellCornerRadius_Large;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ConversationViewCell.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSInteraction;
|
||||
|
||||
@interface OWSCallMessageCell : ConversationViewCell
|
||||
|
||||
+ (NSString *)cellReuseIdentifier;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,386 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSCallMessageCell.h"
|
||||
#import "ConversationViewItem.h"
|
||||
#import "OWSBubbleView.h"
|
||||
#import "OWSMessageFooterView.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import "UIColor+OWS.h"
|
||||
#import "UIFont+OWS.h"
|
||||
#import "UIView+OWS.h"
|
||||
#import <SignalMessaging/Environment.h>
|
||||
#import <SignalMessaging/OWSContactsManager.h>
|
||||
#import <SignalServiceKit/OWSVerificationStateChangeMessage.h>
|
||||
#import <SignalServiceKit/TSCall.h>
|
||||
#import <SignalServiceKit/TSErrorMessage.h>
|
||||
#import <SignalServiceKit/TSInfoMessage.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSCallMessageCell ()
|
||||
|
||||
@property (nonatomic, nullable) TSInteraction *interaction;
|
||||
|
||||
@property (nonatomic) OWSBubbleView *bubbleView;
|
||||
@property (nonatomic) UIImageView *imageView;
|
||||
@property (nonatomic) UIView *circleView;
|
||||
@property (nonatomic) UILabel *titleLabel;
|
||||
@property (nonatomic) OWSMessageFooterView *footerView;
|
||||
@property (nonatomic) UIStackView *hStackView;
|
||||
@property (nonatomic) UIStackView *vStackView;
|
||||
@property (nonatomic) NSMutableArray<NSLayoutConstraint *> *layoutConstraints;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation OWSCallMessageCell
|
||||
|
||||
// `[UIView init]` invokes `[self initWithFrame:...]`.
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self commontInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)commontInit
|
||||
{
|
||||
OWSAssert(!self.imageView);
|
||||
|
||||
self.layoutMargins = UIEdgeInsetsZero;
|
||||
self.contentView.layoutMargins = UIEdgeInsetsZero;
|
||||
|
||||
self.layoutConstraints = [NSMutableArray new];
|
||||
|
||||
self.bubbleView = [OWSBubbleView new];
|
||||
self.bubbleView.userInteractionEnabled = NO;
|
||||
[self.contentView addSubview:self.bubbleView];
|
||||
[self.bubbleView autoPinEdgeToSuperviewEdge:ALEdgeTop];
|
||||
[self.bubbleView autoPinEdgeToSuperviewEdge:ALEdgeBottom];
|
||||
|
||||
self.imageView = [UIImageView new];
|
||||
[self.imageView setContentHuggingHigh];
|
||||
|
||||
self.circleView = [UIView new];
|
||||
self.circleView.backgroundColor = [UIColor whiteColor];
|
||||
self.circleView.layer.cornerRadius = self.circleSize * 0.5f;
|
||||
[self.circleView addSubview:self.imageView];
|
||||
[self.imageView autoCenterInSuperview];
|
||||
[self.circleView autoSetDimension:ALDimensionWidth toSize:self.circleSize];
|
||||
[self.circleView autoSetDimension:ALDimensionHeight toSize:self.circleSize];
|
||||
[self.circleView setContentHuggingHigh];
|
||||
|
||||
self.titleLabel = [UILabel new];
|
||||
self.titleLabel.numberOfLines = 0;
|
||||
self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
[self.titleLabel setContentHuggingLow];
|
||||
|
||||
self.hStackView = [[UIStackView alloc] initWithArrangedSubviews:@[
|
||||
self.circleView,
|
||||
self.titleLabel,
|
||||
]];
|
||||
self.hStackView.axis = UILayoutConstraintAxisHorizontal;
|
||||
self.hStackView.spacing = self.hSpacing;
|
||||
self.hStackView.alignment = UIStackViewAlignmentCenter;
|
||||
|
||||
self.footerView = [OWSMessageFooterView new];
|
||||
|
||||
self.vStackView = [[UIStackView alloc] initWithArrangedSubviews:@[
|
||||
self.hStackView,
|
||||
self.footerView,
|
||||
]];
|
||||
self.vStackView.axis = UILayoutConstraintAxisVertical;
|
||||
self.vStackView.spacing = self.vSpacing;
|
||||
self.vStackView.userInteractionEnabled = NO;
|
||||
[self.bubbleView addSubview:self.vStackView];
|
||||
[self.vStackView autoPinToSuperviewEdges];
|
||||
|
||||
UITapGestureRecognizer *tap =
|
||||
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
|
||||
[self addGestureRecognizer:tap];
|
||||
|
||||
UILongPressGestureRecognizer *longPress =
|
||||
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)];
|
||||
[self addGestureRecognizer:longPress];
|
||||
}
|
||||
|
||||
- (void)configureFonts
|
||||
{
|
||||
// Update cell to reflect changes in dynamic text.
|
||||
self.titleLabel.font = UIFont.ows_dynamicTypeSubheadlineFont;
|
||||
}
|
||||
|
||||
+ (NSString *)cellReuseIdentifier
|
||||
{
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(self.conversationStyle);
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSCall class]]);
|
||||
|
||||
TSCall *call = (TSCall *)self.viewItem.interaction;
|
||||
|
||||
self.bubbleView.bubbleColor = [self bubbleColorForCall:call];
|
||||
|
||||
UIImage *icon = [self iconForCall:call];
|
||||
self.imageView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
self.imageView.tintColor = [self iconColorForCall:call];
|
||||
self.titleLabel.textColor = [self textColorForCall:call];
|
||||
[self applyTitleForCall:call label:self.titleLabel];
|
||||
|
||||
if (self.hasFooter) {
|
||||
[self.footerView configureWithConversationViewItem:self.viewItem isOverlayingMedia:NO];
|
||||
self.footerView.hidden = NO;
|
||||
} else {
|
||||
self.footerView.hidden = YES;
|
||||
}
|
||||
|
||||
if (call.isIncoming) {
|
||||
[self.layoutConstraints addObjectsFromArray:@[
|
||||
[self.bubbleView autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:self.conversationStyle.gutterLeading],
|
||||
[self.bubbleView autoPinEdgeToSuperviewEdge:ALEdgeTrailing
|
||||
withInset:self.conversationStyle.gutterTrailing
|
||||
relation:NSLayoutRelationGreaterThanOrEqual],
|
||||
]];
|
||||
} else {
|
||||
[self.layoutConstraints addObjectsFromArray:@[
|
||||
[self.bubbleView autoPinEdgeToSuperviewEdge:ALEdgeLeading
|
||||
withInset:self.conversationStyle.gutterLeading
|
||||
relation:NSLayoutRelationGreaterThanOrEqual],
|
||||
[self.bubbleView autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:self.conversationStyle.gutterTrailing],
|
||||
]];
|
||||
}
|
||||
|
||||
CGSize cellSize = [self cellSizeWithTransaction:transaction];
|
||||
[self.layoutConstraints addObjectsFromArray:@[
|
||||
[self.bubbleView autoSetDimension:ALDimensionWidth toSize:cellSize.width],
|
||||
[self.bubbleView autoSetDimension:ALDimensionHeight toSize:cellSize.height],
|
||||
]];
|
||||
|
||||
self.vStackView.layoutMarginsRelativeArrangement = YES;
|
||||
self.vStackView.layoutMargins = UIEdgeInsetsMake(self.conversationStyle.textInsetTop,
|
||||
self.conversationStyle.textInsetHorizontal,
|
||||
self.conversationStyle.textInsetBottom,
|
||||
self.conversationStyle.textInsetHorizontal);
|
||||
}
|
||||
|
||||
- (BOOL)hasFooter
|
||||
{
|
||||
return !self.viewItem.shouldHideFooter;
|
||||
}
|
||||
|
||||
- (CGFloat)circleSize
|
||||
{
|
||||
return 48.f;
|
||||
}
|
||||
|
||||
- (UIColor *)textColorForCall:(TSCall *)call
|
||||
{
|
||||
return [self.conversationStyle bubbleTextColorWithCall:call];
|
||||
}
|
||||
|
||||
- (UIColor *)bubbleColorForCall:(TSCall *)call
|
||||
{
|
||||
return [self.conversationStyle bubbleColorWithCall:call];
|
||||
}
|
||||
|
||||
- (UIColor *)iconColorForCall:(TSCall *)call
|
||||
{
|
||||
switch (call.callType) {
|
||||
case RPRecentCallTypeIncoming:
|
||||
case RPRecentCallTypeOutgoing:
|
||||
case RPRecentCallTypeIncomingIncomplete:
|
||||
case RPRecentCallTypeOutgoingIncomplete:
|
||||
return [UIColor ows_greenColor];
|
||||
case RPRecentCallTypeIncomingMissed:
|
||||
case RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity:
|
||||
case RPRecentCallTypeIncomingDeclined:
|
||||
return [UIColor ows_redColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage *)iconForCall:(TSCall *)call
|
||||
{
|
||||
UIImage *result = nil;
|
||||
switch (call.callType) {
|
||||
case RPRecentCallTypeIncoming:
|
||||
case RPRecentCallTypeOutgoing:
|
||||
case RPRecentCallTypeIncomingIncomplete:
|
||||
case RPRecentCallTypeOutgoingIncomplete:
|
||||
result = [UIImage imageNamed:@"phone-up"];
|
||||
break;
|
||||
case RPRecentCallTypeIncomingMissed:
|
||||
case RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity:
|
||||
case RPRecentCallTypeIncomingDeclined:
|
||||
result = [UIImage imageNamed:@"phone-down"];
|
||||
break;
|
||||
}
|
||||
OWSAssert(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)applyTitleForCall:(TSCall *)call label:(UILabel *)label
|
||||
{
|
||||
OWSAssert(call);
|
||||
OWSAssert(label);
|
||||
|
||||
[self configureFonts];
|
||||
|
||||
label.text = [self titleForCall:call];
|
||||
}
|
||||
|
||||
- (NSString *)titleForCall:(TSCall *)call
|
||||
{
|
||||
// We don't actually use the `transaction` but other sibling classes do.
|
||||
switch (call.callType) {
|
||||
case RPRecentCallTypeIncoming:
|
||||
case RPRecentCallTypeOutgoing:
|
||||
case RPRecentCallTypeOutgoingIncomplete:
|
||||
case RPRecentCallTypeIncomingIncomplete:
|
||||
return NSLocalizedString(@"CALL_DEFAULT_STATUS",
|
||||
@"Message recorded in conversation history when local user is making or has completed a call.");
|
||||
case RPRecentCallTypeIncomingMissed:
|
||||
case RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity:
|
||||
return NSLocalizedString(
|
||||
@"CALL_MISSED", @"Message recorded in conversation history when local user missed a call.");
|
||||
case RPRecentCallTypeIncomingDeclined:
|
||||
return NSLocalizedString(
|
||||
@"CALL_DECLINED", @"Message recorded in conversation history when local user declined a call.");
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)hSpacing
|
||||
{
|
||||
return 8.f;
|
||||
}
|
||||
|
||||
- (CGFloat)vSpacing
|
||||
{
|
||||
return 6.f;
|
||||
}
|
||||
|
||||
- (CGSize)titleSize
|
||||
{
|
||||
OWSAssert(self.conversationStyle);
|
||||
OWSAssert(self.viewItem);
|
||||
|
||||
CGFloat maxTitleWidth = (CGFloat)ceil(self.conversationStyle.maxMessageWidth
|
||||
- (self.circleSize + self.hSpacing + self.conversationStyle.textInsetHorizontal * 2));
|
||||
DDLogVerbose(@"%@ maxTitleWidth %f", self.logTag, maxTitleWidth);
|
||||
return [self.titleLabel sizeThatFits:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)];
|
||||
}
|
||||
|
||||
- (CGSize)cellSizeWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(self.conversationStyle);
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSCall class]]);
|
||||
|
||||
TSCall *call = (TSCall *)self.viewItem.interaction;
|
||||
|
||||
[self applyTitleForCall:call label:self.titleLabel];
|
||||
CGSize titleSize = [self titleSize];
|
||||
|
||||
CGSize hStackSize = titleSize;
|
||||
hStackSize.width += (self.hSpacing + self.circleSize);
|
||||
hStackSize.height = MAX(hStackSize.height, self.circleSize);
|
||||
|
||||
CGSize vStackSize = hStackSize;
|
||||
if (self.hasFooter) {
|
||||
CGSize footerSize = [self.footerView measureWithConversationViewItem:self.viewItem];
|
||||
vStackSize.height += (self.vSpacing + footerSize.height);
|
||||
vStackSize.width = MAX(vStackSize.width, footerSize.width);
|
||||
}
|
||||
|
||||
CGSize result = CGSizeCeil(CGSizeMake(
|
||||
MIN(self.conversationStyle.viewWidth, vStackSize.width + self.conversationStyle.textInsetHorizontal * 2),
|
||||
vStackSize.height + self.conversationStyle.textInsetTop + self.conversationStyle.textInsetBottom));
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma mark - UIMenuController
|
||||
|
||||
- (void)showMenuController
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
DDLogDebug(@"%@ long pressed call cell: %@", self.logTag, self.viewItem.interaction.debugDescription);
|
||||
|
||||
[self becomeFirstResponder];
|
||||
|
||||
if ([UIMenuController sharedMenuController].isMenuVisible) {
|
||||
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
|
||||
}
|
||||
|
||||
UIMenuController *menuController = [UIMenuController sharedMenuController];
|
||||
menuController.menuItems = @[];
|
||||
UIView *fromView = self.titleLabel;
|
||||
CGRect targetRect = [fromView.superview convertRect:fromView.frame toView:self];
|
||||
[menuController setTargetRect:targetRect inView:self];
|
||||
[menuController setMenuVisible:YES animated:YES];
|
||||
}
|
||||
|
||||
- (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender
|
||||
{
|
||||
return action == @selector(delete:);
|
||||
}
|
||||
|
||||
- (void) delete:(nullable id)sender
|
||||
{
|
||||
DDLogInfo(@"%@ chose delete", self.logTag);
|
||||
|
||||
TSInteraction *interaction = self.viewItem.interaction;
|
||||
OWSAssert(interaction);
|
||||
|
||||
[interaction remove];
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)prepareForReuse
|
||||
{
|
||||
[NSLayoutConstraint deactivateConstraints:self.layoutConstraints];
|
||||
[self.layoutConstraints removeAllObjects];
|
||||
|
||||
[self.footerView prepareForReuse];
|
||||
}
|
||||
|
||||
#pragma mark - Gesture recognizers
|
||||
|
||||
- (void)handleTapGesture:(UITapGestureRecognizer *)sender
|
||||
{
|
||||
OWSAssert(self.delegate);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSCall class]]);
|
||||
|
||||
if (sender.state == UIGestureRecognizerStateRecognized) {
|
||||
TSCall *call = (TSCall *)self.viewItem.interaction;
|
||||
[self.delegate didTapCall:call];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleLongPressGesture:(UILongPressGestureRecognizer *)longPress
|
||||
{
|
||||
OWSAssert(self.delegate);
|
||||
|
||||
TSInteraction *interaction = self.viewItem.interaction;
|
||||
OWSAssert(interaction);
|
||||
|
||||
if (longPress.state == UIGestureRecognizerStateBegan) {
|
||||
[self showMenuController];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -20,6 +20,7 @@
|
|||
#import "NSAttributedString+OWS.h"
|
||||
#import "NewGroupViewController.h"
|
||||
#import "OWSAudioPlayer.h"
|
||||
#import "OWSCallMessageCell.h"
|
||||
#import "OWSContactOffersCell.h"
|
||||
#import "OWSConversationSettingsViewController.h"
|
||||
#import "OWSConversationSettingsViewDelegate.h"
|
||||
|
@ -606,6 +607,8 @@ typedef enum : NSUInteger {
|
|||
{
|
||||
[self.collectionView registerClass:[OWSSystemMessageCell class]
|
||||
forCellWithReuseIdentifier:[OWSSystemMessageCell cellReuseIdentifier]];
|
||||
[self.collectionView registerClass:[OWSCallMessageCell class]
|
||||
forCellWithReuseIdentifier:[OWSCallMessageCell cellReuseIdentifier]];
|
||||
[self.collectionView registerClass:[OWSUnreadIndicatorCell class]
|
||||
forCellWithReuseIdentifier:[OWSUnreadIndicatorCell cellReuseIdentifier]];
|
||||
[self.collectionView registerClass:[OWSContactOffersCell class]
|
||||
|
@ -2483,6 +2486,16 @@ typedef enum : NSUInteger {
|
|||
[self.inputToolbar beginEditingTextMessage];
|
||||
}
|
||||
|
||||
#pragma mark - Calls
|
||||
|
||||
- (void)didTapCall:(TSCall *)call
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
OWSAssert([call isKindOfClass:[TSCall class]]);
|
||||
|
||||
[self handleCallTap:call];
|
||||
}
|
||||
|
||||
#pragma mark - System Messages
|
||||
|
||||
- (void)didTapSystemMessageWithInteraction:(TSInteraction *)interaction
|
||||
|
@ -2494,8 +2507,6 @@ typedef enum : NSUInteger {
|
|||
[self handleErrorMessageTap:(TSErrorMessage *)interaction];
|
||||
} else if ([interaction isKindOfClass:[TSInfoMessage class]]) {
|
||||
[self handleInfoMessageTap:(TSInfoMessage *)interaction];
|
||||
} else if ([interaction isKindOfClass:[TSCall class]]) {
|
||||
[self handleCallTap:(TSCall *)interaction];
|
||||
} else {
|
||||
OWSFail(@"Tap for system messages of unknown type: %@", [interaction class]);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#import "ConversationViewItem.h"
|
||||
#import "OWSAudioMessageView.h"
|
||||
#import "OWSCallMessageCell.h"
|
||||
#import "OWSContactOffersCell.h"
|
||||
#import "OWSMessageCell.h"
|
||||
#import "OWSSystemMessageCell.h"
|
||||
|
@ -230,9 +231,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
break;
|
||||
case OWSInteractionType_Error:
|
||||
case OWSInteractionType_Info:
|
||||
case OWSInteractionType_Call:
|
||||
measurementCell = [OWSSystemMessageCell new];
|
||||
break;
|
||||
case OWSInteractionType_Call:
|
||||
measurementCell = [OWSCallMessageCell new];
|
||||
break;
|
||||
case OWSInteractionType_UnreadIndicator:
|
||||
measurementCell = [OWSUnreadIndicatorCell new];
|
||||
break;
|
||||
|
@ -292,9 +295,11 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
forIndexPath:indexPath];
|
||||
case OWSInteractionType_Error:
|
||||
case OWSInteractionType_Info:
|
||||
case OWSInteractionType_Call:
|
||||
return [collectionView dequeueReusableCellWithReuseIdentifier:[OWSSystemMessageCell cellReuseIdentifier]
|
||||
forIndexPath:indexPath];
|
||||
case OWSInteractionType_Call:
|
||||
return [collectionView dequeueReusableCellWithReuseIdentifier:[OWSCallMessageCell cellReuseIdentifier]
|
||||
forIndexPath:indexPath];
|
||||
case OWSInteractionType_UnreadIndicator:
|
||||
return [collectionView dequeueReusableCellWithReuseIdentifier:[OWSUnreadIndicatorCell cellReuseIdentifier]
|
||||
forIndexPath:indexPath];
|
||||
|
|
|
@ -3411,11 +3411,11 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac
|
|||
inThread:contactThread]];
|
||||
[result addObject:[[TSCall alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
|
||||
withCallNumber:@"+19174054215"
|
||||
callType:RPRecentCallTypeMissed
|
||||
callType:RPRecentCallTypeIncomingMissed
|
||||
inThread:contactThread]];
|
||||
[result addObject:[[TSCall alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
|
||||
withCallNumber:@"+19174054215"
|
||||
callType:RPRecentCallTypeMissedBecauseOfChangedIdentity
|
||||
callType:RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity
|
||||
inThread:contactThread]];
|
||||
[result addObject:[[TSCall alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
|
||||
withCallNumber:@"+19174054215"
|
||||
|
@ -3425,6 +3425,10 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac
|
|||
withCallNumber:@"+19174054215"
|
||||
callType:RPRecentCallTypeIncomingIncomplete
|
||||
inThread:contactThread]];
|
||||
[result addObject:[[TSCall alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
|
||||
withCallNumber:@"+19174054215"
|
||||
callType:RPRecentCallTypeIncomingDeclined
|
||||
inThread:contactThread]];
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -515,12 +515,12 @@ private class SignalCallData: NSObject {
|
|||
// Insert missed call record
|
||||
if let callRecord = call.callRecord {
|
||||
if callRecord.callType == RPRecentCallTypeIncoming {
|
||||
callRecord.updateCallType(RPRecentCallTypeMissed)
|
||||
callRecord.updateCallType(RPRecentCallTypeIncomingMissed)
|
||||
}
|
||||
} else {
|
||||
call.callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(),
|
||||
withCallNumber: call.thread.contactIdentifier(),
|
||||
callType: RPRecentCallTypeMissed,
|
||||
callType: RPRecentCallTypeIncomingMissed,
|
||||
in: call.thread)
|
||||
}
|
||||
|
||||
|
@ -602,7 +602,7 @@ private class SignalCallData: NSObject {
|
|||
|
||||
let callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(),
|
||||
withCallNumber: thread.contactIdentifier(),
|
||||
callType: RPRecentCallTypeMissedBecauseOfChangedIdentity,
|
||||
callType: RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity,
|
||||
in: thread)
|
||||
assert(newCall.callRecord == nil)
|
||||
newCall.callRecord = callRecord
|
||||
|
|
|
@ -290,9 +290,18 @@
|
|||
/* Alert title when calling and permissions for microphone are missing */
|
||||
"CALL_AUDIO_PERMISSION_TITLE" = "Microphone Access Required";
|
||||
|
||||
/* Message recorded in conversation history when local user declined a call. */
|
||||
"CALL_DECLINED" = "Call Declined";
|
||||
|
||||
/* Message recorded in conversation history when local user is making or has completed a call. */
|
||||
"CALL_DEFAULT_STATUS" = "Contact Called";
|
||||
|
||||
/* Accessibility label for placing call button */
|
||||
"CALL_LABEL" = "Call";
|
||||
|
||||
/* Message recorded in conversation history when local user missed a call. */
|
||||
"CALL_MISSED" = "Missed Call";
|
||||
|
||||
/* Call setup status label after outgoing call times out */
|
||||
"CALL_SCREEN_STATUS_NO_ANSWER" = "No Answer.";
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value);
|
|||
- (NSLayoutConstraint *)autoHCenterInSuperview;
|
||||
- (NSLayoutConstraint *)autoVCenterInSuperview;
|
||||
|
||||
- (void)autoPinEdgesToEdgesOfView:(UIView *)view;
|
||||
- (void)autoPinWidthToWidthOfView:(UIView *)view;
|
||||
- (void)autoPinHeightToHeightOfView:(UIView *)view;
|
||||
|
||||
|
|
|
@ -111,6 +111,14 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
|||
return [self autoAlignAxis:ALAxisHorizontal toSameAxisOfView:self.superview];
|
||||
}
|
||||
|
||||
- (void)autoPinEdgesToEdgesOfView:(UIView *)view
|
||||
{
|
||||
OWSAssert(view);
|
||||
|
||||
[self autoPinWidthToWidthOfView:view];
|
||||
[self autoPinHeightToHeightOfView:view];
|
||||
}
|
||||
|
||||
- (void)autoPinWidthToWidthOfView:(UIView *)view
|
||||
{
|
||||
OWSAssert(view);
|
||||
|
|
|
@ -169,6 +169,15 @@ public class ConversationStyle: NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public func bubbleColor(call: TSCall) -> UIColor {
|
||||
if call.isIncoming {
|
||||
return primaryColor
|
||||
} else {
|
||||
return self.bubbleColorOutgoingSent
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public static var bubbleTextColorIncoming = UIColor.ows_white
|
||||
|
||||
|
@ -190,4 +199,13 @@ public class ConversationStyle: NSObject {
|
|||
return UIColor.ows_materialBlue
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public func bubbleTextColor(call: TSCall) -> UIColor {
|
||||
if call.isIncoming {
|
||||
return ConversationStyle.bubbleTextColorIncoming
|
||||
} else {
|
||||
return UIColor.ows_black
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,11 +12,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
typedef enum {
|
||||
RPRecentCallTypeIncoming = 1,
|
||||
RPRecentCallTypeOutgoing,
|
||||
RPRecentCallTypeMissed,
|
||||
RPRecentCallTypeIncomingMissed,
|
||||
// These call types are used until the call connects.
|
||||
RPRecentCallTypeOutgoingIncomplete,
|
||||
RPRecentCallTypeIncomingIncomplete,
|
||||
RPRecentCallTypeMissedBecauseOfChangedIdentity,
|
||||
RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity,
|
||||
RPRecentCallTypeIncomingDeclined
|
||||
} RPRecentCallType;
|
||||
|
||||
|
@ -24,6 +24,8 @@ typedef enum {
|
|||
|
||||
@property (nonatomic, readonly) RPRecentCallType callType;
|
||||
|
||||
@property (nonatomic, readonly) BOOL isIncoming;
|
||||
|
||||
- (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
|
|
|
@ -36,7 +36,8 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
|
|||
|
||||
_callSchemaVersion = TSCallCurrentSchemaVersion;
|
||||
_callType = callType;
|
||||
if (_callType == RPRecentCallTypeMissed || _callType == RPRecentCallTypeMissedBecauseOfChangedIdentity) {
|
||||
if (_callType == RPRecentCallTypeIncomingMissed
|
||||
|| _callType == RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity) {
|
||||
_read = NO;
|
||||
} else {
|
||||
_read = YES;
|
||||
|
@ -75,13 +76,13 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
|
|||
return NSLocalizedString(@"INCOMING_CALL", @"");
|
||||
case RPRecentCallTypeOutgoing:
|
||||
return NSLocalizedString(@"OUTGOING_CALL", @"");
|
||||
case RPRecentCallTypeMissed:
|
||||
case RPRecentCallTypeIncomingMissed:
|
||||
return NSLocalizedString(@"MISSED_CALL", @"");
|
||||
case RPRecentCallTypeOutgoingIncomplete:
|
||||
return NSLocalizedString(@"OUTGOING_INCOMPLETE_CALL", @"");
|
||||
case RPRecentCallTypeIncomingIncomplete:
|
||||
return NSLocalizedString(@"INCOMING_INCOMPLETE_CALL", @"");
|
||||
case RPRecentCallTypeMissedBecauseOfChangedIdentity:
|
||||
case RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity:
|
||||
return NSLocalizedString(@"INFO_MESSAGE_MISSED_CALL_DUE_TO_CHANGED_IDENITY", @"info message text shown in conversation view");
|
||||
case RPRecentCallTypeIncomingDeclined:
|
||||
return NSLocalizedString(@"INCOMING_DECLINED_CALL",
|
||||
|
@ -141,6 +142,21 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
|
|||
}];
|
||||
}
|
||||
|
||||
- (BOOL)isIncoming
|
||||
{
|
||||
switch (self.callType) {
|
||||
case RPRecentCallTypeIncoming:
|
||||
case RPRecentCallTypeIncomingMissed:
|
||||
case RPRecentCallTypeIncomingIncomplete:
|
||||
case RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity:
|
||||
case RPRecentCallTypeIncomingDeclined:
|
||||
return YES;
|
||||
case RPRecentCallTypeOutgoing:
|
||||
case RPRecentCallTypeOutgoingIncomplete:
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
Loading…
Reference in New Issue