diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 0d2797499..5e37de0b7 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -292,6 +292,7 @@ 45BB93381E688E14001E3939 /* UIDevice+featureSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45BB93371E688E14001E3939 /* UIDevice+featureSupport.swift */; }; 45BC829D1FD9C4B400011CF3 /* ShareViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45BC829C1FD9C4B400011CF3 /* ShareViewDelegate.swift */; }; 45BD60821DE9547E00A8F436 /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45BD60811DE9547E00A8F436 /* Contacts.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 45BE4EA22012AD2000935E59 /* DisappearingTimerConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45BE4EA12012AD2000935E59 /* DisappearingTimerConfigurationView.swift */; }; 45C0DC1B1E68FE9000E04C47 /* UIApplication+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45C0DC1A1E68FE9000E04C47 /* UIApplication+OWS.swift */; }; 45C0DC1E1E69011F00E04C47 /* UIStoryboard+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45C0DC1D1E69011F00E04C47 /* UIStoryboard+OWS.swift */; }; 45C9DEB81DF4E35A0065CA84 /* WebRTCCallMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45C9DEB71DF4E35A0065CA84 /* WebRTCCallMessageHandler.swift */; }; @@ -831,6 +832,7 @@ 45BB93371E688E14001E3939 /* UIDevice+featureSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIDevice+featureSupport.swift"; sourceTree = ""; }; 45BC829C1FD9C4B400011CF3 /* ShareViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewDelegate.swift; sourceTree = ""; }; 45BD60811DE9547E00A8F436 /* Contacts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Contacts.framework; path = System/Library/Frameworks/Contacts.framework; sourceTree = SDKROOT; }; + 45BE4EA12012AD2000935E59 /* DisappearingTimerConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisappearingTimerConfigurationView.swift; sourceTree = ""; }; 45C0DC1A1E68FE9000E04C47 /* UIApplication+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIApplication+OWS.swift"; sourceTree = ""; }; 45C0DC1D1E69011F00E04C47 /* UIStoryboard+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard+OWS.swift"; sourceTree = ""; }; 45C9DEB71DF4E35A0065CA84 /* WebRTCCallMessageHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebRTCCallMessageHandler.swift; sourceTree = ""; }; @@ -1255,6 +1257,7 @@ 340CB2221EAC155C0001CAA1 /* ContactsViewHelper.h */, 340CB2231EAC155C0001CAA1 /* ContactsViewHelper.m */, 346129D11FD2085A00532771 /* CommonStrings.swift */, + 45BE4EA12012AD2000935E59 /* DisappearingTimerConfigurationView.swift */, 346129CF1FD207F200532771 /* OWSAlerts.swift */, 454A965E1FD60EA2008D2A0E /* OWSFlatButton.swift */, 3400C7971EAFB772008A8584 /* ThreadViewHelper.h */, @@ -2752,6 +2755,7 @@ buildActionMask = 2147483647; files = ( 45194F951FD7216600333B2C /* TSUnreadIndicatorInteraction.m in Sources */, + 45BE4EA22012AD2000935E59 /* DisappearingTimerConfigurationView.swift in Sources */, 346129F71FD5F31400532771 /* OWS105AttachmentFilePaths.m in Sources */, 45194F931FD7215C00333B2C /* OWSContactOffersInteraction.m in Sources */, 450998681FD8C0FF00D89EB3 /* AttachmentSharing.m in Sources */, diff --git a/Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme b/Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme index fdcb74c83..526b04e7a 100644 --- a/Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme +++ b/Signal.xcodeproj/xcshareddata/xcschemes/Signal.xcscheme @@ -28,7 +28,7 @@ buildForAnalyzing = "YES"> diff --git a/Signal/Images.xcassets/button_timer_white.imageset/Contents.json b/Signal/Images.xcassets/button_timer_white.imageset/Contents.json deleted file mode 100644 index 72e4dc33d..000000000 --- a/Signal/Images.xcassets/button_timer_white.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "ic_timer_white.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ic_timer_white@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ic_timer_white@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Signal/Images.xcassets/button_timer_white.imageset/ic_timer_white.png b/Signal/Images.xcassets/button_timer_white.imageset/ic_timer_white.png deleted file mode 100644 index dccf011ee..000000000 Binary files a/Signal/Images.xcassets/button_timer_white.imageset/ic_timer_white.png and /dev/null differ diff --git a/Signal/Images.xcassets/button_timer_white.imageset/ic_timer_white@2x.png b/Signal/Images.xcassets/button_timer_white.imageset/ic_timer_white@2x.png deleted file mode 100644 index 0b0e2134a..000000000 Binary files a/Signal/Images.xcassets/button_timer_white.imageset/ic_timer_white@2x.png and /dev/null differ diff --git a/Signal/Images.xcassets/button_timer_white.imageset/ic_timer_white@3x.png b/Signal/Images.xcassets/button_timer_white.imageset/ic_timer_white@3x.png deleted file mode 100644 index 77a03d877..000000000 Binary files a/Signal/Images.xcassets/button_timer_white.imageset/ic_timer_white@3x.png and /dev/null differ diff --git a/Signal/Images.xcassets/table_ic_hourglass.imageset/Contents.json b/Signal/Images.xcassets/ic_timer.imageset/Contents.json similarity index 72% rename from Signal/Images.xcassets/table_ic_hourglass.imageset/Contents.json rename to Signal/Images.xcassets/ic_timer.imageset/Contents.json index a62333543..b78318659 100644 --- a/Signal/Images.xcassets/table_ic_hourglass.imageset/Contents.json +++ b/Signal/Images.xcassets/ic_timer.imageset/Contents.json @@ -2,17 +2,17 @@ "images" : [ { "idiom" : "universal", - "filename" : "table_ic_timer.png", + "filename" : "ic_timer.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "table_ic_timer@2x.png", + "filename" : "ic_timer@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "table_ic_timer@3x.png", + "filename" : "ic_timer@3x.png", "scale" : "3x" } ], diff --git a/Signal/Images.xcassets/ic_timer.imageset/ic_timer.png b/Signal/Images.xcassets/ic_timer.imageset/ic_timer.png new file mode 100644 index 000000000..8b93f6873 Binary files /dev/null and b/Signal/Images.xcassets/ic_timer.imageset/ic_timer.png differ diff --git a/Signal/Images.xcassets/ic_timer.imageset/ic_timer@2x.png b/Signal/Images.xcassets/ic_timer.imageset/ic_timer@2x.png new file mode 100644 index 000000000..ba56ce864 Binary files /dev/null and b/Signal/Images.xcassets/ic_timer.imageset/ic_timer@2x.png differ diff --git a/Signal/Images.xcassets/ic_timer.imageset/ic_timer@3x.png b/Signal/Images.xcassets/ic_timer.imageset/ic_timer@3x.png new file mode 100644 index 000000000..0075da23e Binary files /dev/null and b/Signal/Images.xcassets/ic_timer.imageset/ic_timer@3x.png differ diff --git a/Signal/Images.xcassets/system_message_timer.imageset/Contents.json b/Signal/Images.xcassets/system_message_timer.imageset/Contents.json deleted file mode 100644 index 72e4dc33d..000000000 --- a/Signal/Images.xcassets/system_message_timer.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "ic_timer_white.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ic_timer_white@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ic_timer_white@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Signal/Images.xcassets/system_message_timer.imageset/ic_timer_white.png b/Signal/Images.xcassets/system_message_timer.imageset/ic_timer_white.png deleted file mode 100644 index dccf011ee..000000000 Binary files a/Signal/Images.xcassets/system_message_timer.imageset/ic_timer_white.png and /dev/null differ diff --git a/Signal/Images.xcassets/system_message_timer.imageset/ic_timer_white@2x.png b/Signal/Images.xcassets/system_message_timer.imageset/ic_timer_white@2x.png deleted file mode 100644 index 0b0e2134a..000000000 Binary files a/Signal/Images.xcassets/system_message_timer.imageset/ic_timer_white@2x.png and /dev/null differ diff --git a/Signal/Images.xcassets/system_message_timer.imageset/ic_timer_white@3x.png b/Signal/Images.xcassets/system_message_timer.imageset/ic_timer_white@3x.png deleted file mode 100644 index 77a03d877..000000000 Binary files a/Signal/Images.xcassets/system_message_timer.imageset/ic_timer_white@3x.png and /dev/null differ diff --git a/Signal/Images.xcassets/table_ic_hourglass.imageset/table_ic_timer.png b/Signal/Images.xcassets/table_ic_hourglass.imageset/table_ic_timer.png deleted file mode 100644 index 8c66731fe..000000000 Binary files a/Signal/Images.xcassets/table_ic_hourglass.imageset/table_ic_timer.png and /dev/null differ diff --git a/Signal/Images.xcassets/table_ic_hourglass.imageset/table_ic_timer@2x.png b/Signal/Images.xcassets/table_ic_hourglass.imageset/table_ic_timer@2x.png deleted file mode 100644 index 0a0f15709..000000000 Binary files a/Signal/Images.xcassets/table_ic_hourglass.imageset/table_ic_timer@2x.png and /dev/null differ diff --git a/Signal/Images.xcassets/table_ic_hourglass.imageset/table_ic_timer@3x.png b/Signal/Images.xcassets/table_ic_hourglass.imageset/table_ic_timer@3x.png deleted file mode 100644 index a10068531..000000000 Binary files a/Signal/Images.xcassets/table_ic_hourglass.imageset/table_ic_timer@3x.png and /dev/null differ diff --git a/Signal/Images.xcassets/table_ic_timer.imageset/Contents.json b/Signal/Images.xcassets/table_ic_timer.imageset/Contents.json deleted file mode 100644 index a62333543..000000000 --- a/Signal/Images.xcassets/table_ic_timer.imageset/Contents.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "table_ic_timer.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "table_ic_timer@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "table_ic_timer@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template" - } -} \ No newline at end of file diff --git a/Signal/Images.xcassets/table_ic_timer.imageset/table_ic_timer.png b/Signal/Images.xcassets/table_ic_timer.imageset/table_ic_timer.png deleted file mode 100644 index 8c66731fe..000000000 Binary files a/Signal/Images.xcassets/table_ic_timer.imageset/table_ic_timer.png and /dev/null differ diff --git a/Signal/Images.xcassets/table_ic_timer.imageset/table_ic_timer@2x.png b/Signal/Images.xcassets/table_ic_timer.imageset/table_ic_timer@2x.png deleted file mode 100644 index 0a0f15709..000000000 Binary files a/Signal/Images.xcassets/table_ic_timer.imageset/table_ic_timer@2x.png and /dev/null differ diff --git a/Signal/Images.xcassets/table_ic_timer.imageset/table_ic_timer@3x.png b/Signal/Images.xcassets/table_ic_timer.imageset/table_ic_timer@3x.png deleted file mode 100644 index a10068531..000000000 Binary files a/Signal/Images.xcassets/table_ic_timer.imageset/table_ic_timer@3x.png and /dev/null differ diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSSystemMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSSystemMessageCell.m index cdcf5c455..d84d2f460 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSSystemMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSSystemMessageCell.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSSystemMessageCell.h" @@ -154,7 +154,7 @@ NS_ASSUME_NONNULL_BEGIN result = [UIImage imageNamed:@"system_message_group"]; break; case TSInfoMessageTypeDisappearingMessagesUpdate: - result = [UIImage imageNamed:@"system_message_timer"]; + result = [UIImage imageNamed:@"ic_timer"]; break; case TSInfoMessageVerificationStateChange: result = [UIImage imageNamed:@"system_message_verified"]; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 0db85d2aa..4846dc0d2 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -128,6 +128,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { ContactsViewHelperDelegate, ContactEditingDelegate, CNContactViewControllerDelegate, + DisappearingTimerConfigurationViewDelegate, OWSConversationSettingsViewDelegate, ConversationViewLayoutDelegate, ConversationViewCellDelegate, @@ -1240,34 +1241,22 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { } if (self.disappearingMessagesConfiguration.isEnabled) { - UIButton *timerButton = [UIButton buttonWithType:UIButtonTypeCustom]; - UIImage *image = [UIImage imageNamed:@"button_timer_white"]; - [timerButton setImage:image forState:UIControlStateNormal]; - UIEdgeInsets imageEdgeInsets = UIEdgeInsetsZero; - // We normally would want to use left and right insets that ensure the button - // is square and the icon is centered. However UINavigationBar doesn't offer us - // control over the margins and spacing of its content, and the buttons end up - // too far apart and too far from the edge of the screen. So we use a smaller - // right inset tighten up the layout. - imageEdgeInsets.left = round((kBarButtonSize - image.size.width) * 0.5f); - imageEdgeInsets.right = round((kBarButtonSize - (image.size.width + imageEdgeInsets.left)) * 0.5f); - imageEdgeInsets.top = round((kBarButtonSize - image.size.height) * 0.5f); - imageEdgeInsets.bottom = round(kBarButtonSize - (image.size.height + imageEdgeInsets.top)); - timerButton.imageEdgeInsets = imageEdgeInsets; - timerButton.accessibilityLabel - = NSLocalizedString(@"DISAPPEARING_MESSAGES_LABEL", @"Accessibility label for disappearing messages"); - NSString *formatString = NSLocalizedString( - @"DISAPPEARING_MESSAGES_HINT", @"Accessibility hint that contains current timeout information"); - timerButton.accessibilityHint = - [NSString stringWithFormat:formatString, self.disappearingMessagesConfiguration.durationString]; - [timerButton addTarget:self - action:@selector(didTapTimerInNavbar:) - forControlEvents:UIControlEventTouchUpInside]; - timerButton.frame = CGRectMake(0, - 0, - round(image.size.width + imageEdgeInsets.left + imageEdgeInsets.right), - round(image.size.height + imageEdgeInsets.top + imageEdgeInsets.bottom)); - [barButtons addObject:[[UIBarButtonItem alloc] initWithCustomView:timerButton]]; + DisappearingTimerConfigurationView *timerView = [[DisappearingTimerConfigurationView alloc] + initWithDurationSeconds:self.disappearingMessagesConfiguration.durationSeconds]; + timerView.delegate = self; + timerView.tintColor = UIColor.whiteColor; + + // As of iOS11, we can size barButton item custom views with autoLayout. + // Before that, though we can still use autoLayout *within* the customView, + // setting the view's size with constraints causes the customView to be temporarily + // laid out with a misplaced origin. + if (@available(iOS 11.0, *)) { + [timerView autoSetDimensionsToSize:CGSizeMake(36, 44)]; + } else { + timerView.frame = CGRectMake(0, 0, 36, 44); + } + + [barButtons addObject:[[UIBarButtonItem alloc] initWithCustomView:timerView]]; } self.navigationItem.rightBarButtonItems = [barButtons copy]; @@ -1453,7 +1442,9 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { [self.navigationController pushViewController:settingsVC animated:YES]; } -- (void)didTapTimerInNavbar:(id)sender +#pragma mark - DisappearingTimerConfigurationViewDelegate + +- (void)disappearingTimerConfigurationViewWasTapped:(DisappearingTimerConfigurationView *)disappearingTimerView { DDLogDebug(@"%@ Tapped timer in navbar", self.logTag); [self showConversationSettings]; diff --git a/Signal/src/ViewControllers/OWSConversationSettingsViewController.m b/Signal/src/ViewControllers/OWSConversationSettingsViewController.m index 52340d4da..8e33c08df 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsViewController.m +++ b/Signal/src/ViewControllers/OWSConversationSettingsViewController.m @@ -343,7 +343,7 @@ NS_ASSUME_NONNULL_BEGIN [topView autoPinEdgeToSuperviewEdge:ALEdgeTop]; [topView autoSetDimension:ALDimensionHeight toSize:kOWSTable_DefaultCellHeight]; - UIImageView *iconView = [strongSelf viewForIconWithName:@"table_ic_timer"]; + UIImageView *iconView = [strongSelf viewForIconWithName:@"ic_timer"]; [topView addSubview:iconView]; [iconView autoVCenterInSuperview]; [iconView autoPinLeadingToSuperview]; @@ -402,7 +402,7 @@ NS_ASSUME_NONNULL_BEGIN [topView autoPinEdgeToSuperviewEdge:ALEdgeTop]; [topView autoSetDimension:ALDimensionHeight toSize:kOWSTable_DefaultCellHeight]; - UIImageView *iconView = [strongSelf viewForIconWithName:@"table_ic_timer"]; + UIImageView *iconView = [strongSelf viewForIconWithName:@"ic_timer"]; [topView addSubview:iconView]; [iconView autoVCenterInSuperview]; [iconView autoPinLeadingToSuperview]; @@ -731,11 +731,15 @@ NS_ASSUME_NONNULL_BEGIN - (UIImageView *)viewForIconWithName:(NSString *)iconName { UIImage *icon = [UIImage imageNamed:iconName]; + OWSAssert(icon); UIImageView *iconView = [UIImageView new]; iconView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - iconView.tintColor = [UIColor colorWithRGBHex:0x505050]; - iconView.contentMode = UIViewContentModeScaleToFill; + iconView.tintColor = [UIColor ows_blackIconColor]; + iconView.contentMode = UIViewContentModeScaleAspectFit; + iconView.layer.minificationFilter = kCAFilterTrilinear; + iconView.layer.magnificationFilter = kCAFilterTrilinear; + [iconView autoSetDimension:ALDimensionWidth toSize:24.f]; [iconView autoSetDimension:ALDimensionHeight toSize:24.f]; return iconView; diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 3e693ec26..66896619e 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1647,15 +1647,27 @@ /* {{number of days}} embedded in strings, e.g. 'Alice updated disappearing messages expiration to {{5 days}}'. See other *_TIME_AMOUNT strings */ "TIME_AMOUNT_DAYS" = "%@ days"; +/* Label text below navbar button, embeds {{number of days}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5d' not '5 d'. See other *_TIME_AMOUNT strings */ +"TIME_AMOUNT_DAYS_SHORT_FORMAT" = "%@d"; + /* {{number of hours}} embedded in strings, e.g. 'Alice updated disappearing messages expiration to {{5 hours}}'. See other *_TIME_AMOUNT strings */ "TIME_AMOUNT_HOURS" = "%@ hours"; +/* Label text below navbar button, embeds {{number of hours}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5h' not '5 h'. See other *_TIME_AMOUNT strings */ +"TIME_AMOUNT_HOURS_SHORT_FORMAT" = "%@h"; + /* {{number of minutes}} embedded in strings, e.g. 'Alice updated disappearing messages expiration to {{5 minutes}}'. See other *_TIME_AMOUNT strings */ "TIME_AMOUNT_MINUTES" = "%@ minutes"; +/* Label text below navbar button, embeds {{number of minutes}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5m' not '5 m'. See other *_TIME_AMOUNT strings */ +"TIME_AMOUNT_MINUTES_SHORT_FORMAT" = "%@m"; + /* {{number of seconds}} embedded in strings, e.g. 'Alice updated disappearing messages expiration to {{5 seconds}}'. See other *_TIME_AMOUNT strings */ "TIME_AMOUNT_SECONDS" = "%@ seconds"; +/* Label text below navbar button, embeds {{number of seconds}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5s' not '5 s'. See other *_TIME_AMOUNT strings */ +"TIME_AMOUNT_SECONDS_SHORT_FORMAT" = "%@s"; + /* {{1 day}} embedded in strings, e.g. 'Alice updated disappearing messages expiration to {{1 day}}'. See other *_TIME_AMOUNT strings */ "TIME_AMOUNT_SINGLE_DAY" = "%@ day"; @@ -1671,6 +1683,9 @@ /* {{number of weeks}}, embedded in strings, e.g. 'Alice updated disappearing messages expiration to {{5 weeks}}'. See other *_TIME_AMOUNT strings */ "TIME_AMOUNT_WEEKS" = "%@ weeks"; +/* Label text below navbar button, embeds {{number of weeks}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5w' not '5 w'. See other *_TIME_AMOUNT strings */ +"TIME_AMOUNT_WEEKS_SHORT_FORMAT" = "%@w"; + /* Label for the cancel button in an alert or action sheet. */ "TXT_CANCEL_TITLE" = "Cancel"; diff --git a/SignalMessaging/Views/DisappearingTimerConfigurationView.swift b/SignalMessaging/Views/DisappearingTimerConfigurationView.swift new file mode 100644 index 000000000..f5cc7128d --- /dev/null +++ b/SignalMessaging/Views/DisappearingTimerConfigurationView.swift @@ -0,0 +1,134 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +@objc +public protocol DisappearingTimerConfigurationViewDelegate: class { + func disappearingTimerConfigurationViewWasTapped(_ disappearingTimerView: DisappearingTimerConfigurationView) +} + +// DisappearingTimerConfigurationView shows a timer icon and a short label showing the duration +// of disappearing messages for a thread. +// +// If you assign a delegate, it behaves like a button. +@objc +public class DisappearingTimerConfigurationView: UIView { + + public weak var delegate: DisappearingTimerConfigurationViewDelegate? { + didSet { + // gesture recognizer is only enabled when a delegate is assigned. + // This lets us use this view as either an interactive button + // or as a non-interactive status indicator + pressGesture.isEnabled = delegate != nil + } + } + + override public var frame: CGRect { + didSet { + Logger.verbose("\(logTag) in \(#function): \(oldValue) -> \(frame)") + } + } + + override public var bounds: CGRect { + didSet { + Logger.verbose("\(logTag) in \(#function): \(oldValue) -> \(bounds)") + } + } + + override public func layoutSubviews() { + let oldFrame = self.frame + super.layoutSubviews() + Logger.verbose("\(logTag) in \(#function). Frame: \(oldFrame) -> \(self.frame)") + } + + private let imageView: UIImageView + private let label: UILabel + private var pressGesture: UILongPressGestureRecognizer! + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc + public init(durationSeconds: UInt32) { + self.imageView = UIImageView(image: #imageLiteral(resourceName: "ic_timer")) + imageView.contentMode = .scaleAspectFit + + self.label = UILabel() + label.text = OWSDisappearingMessagesConfiguration.string(forDurationSeconds: durationSeconds, useShortFormat: true) + label.font = UIFont.systemFont(ofSize: 10) + label.textColor = UIColor.white + label.textAlignment = .center + label.minimumScaleFactor = 0.5 + + super.init(frame: CGRect.zero) + + applyTintColor(self.tintColor) + + // Gesture, simulating button touch up inside + let gesture = UILongPressGestureRecognizer(target: self, action: #selector(pressHandler)) + gesture.minimumPressDuration = 0 + self.pressGesture = gesture + self.addGestureRecognizer(pressGesture) + + // disable gesture recognizer until a delegate is assigned + // this lets us use the UI as either an interactive button + // or as a non-interactive status indicator + pressGesture.isEnabled = false + + // Accessability + self.accessibilityLabel = NSLocalizedString("DISAPPEARING_MESSAGES_LABEL", comment: "Accessibility label for disappearing messages") + let hintFormatString = NSLocalizedString("DISAPPEARING_MESSAGES_HINT", comment: "Accessibility hint that contains current timeout information") + let durationString = OWSDisappearingMessagesConfiguration.string(forDurationSeconds: durationSeconds, useShortFormat: false) + self.accessibilityHint = String(format: hintFormatString, durationString) + + // Layout + self.addSubview(imageView) + self.addSubview(label) + + let kHorizontalPadding: CGFloat = 4 + let kVerticalPadding: CGFloat = 6 + imageView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: kVerticalPadding, left: kHorizontalPadding, bottom: 0, right: kHorizontalPadding), excludingEdge: .bottom) + label.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: 0, left: kHorizontalPadding, bottom: kVerticalPadding, right: kHorizontalPadding), excludingEdge: .top) + label.autoPinEdge(.top, to: .bottom, of: imageView) + } + + @objc + func pressHandler(_ gestureRecognizer: UILongPressGestureRecognizer) { + Logger.verbose("\(self.logTag) in \(#function)") + + // handle touch down and touch up events separately + if gestureRecognizer.state == .began { + applyTintColor(UIColor.gray) + } else if gestureRecognizer.state == .ended { + applyTintColor(self.tintColor) + + let location = gestureRecognizer.location(in: self) + let isTouchUpInside = self.bounds.contains(location) + + if (isTouchUpInside) { + // Similar to a UIButton's touch-up-inside + self.delegate?.disappearingTimerConfigurationViewWasTapped(self) + } else { + // Similar to a UIButton's touch-up-outside + + // cancel gesture + gestureRecognizer.isEnabled = false + gestureRecognizer.isEnabled = true + } + } + } + + override public var tintColor: UIColor! { + didSet { + applyTintColor(tintColor) + } + } + + private func applyTintColor(_ color: UIColor) { + imageView.tintColor = color + label.textColor = color + } +} diff --git a/SignalMessaging/categories/UIColor+OWS.h b/SignalMessaging/categories/UIColor+OWS.h index ac5c5263a..de19f3b5d 100644 --- a/SignalMessaging/categories/UIColor+OWS.h +++ b/SignalMessaging/categories/UIColor+OWS.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import @@ -21,6 +21,7 @@ NS_ASSUME_NONNULL_BEGIN @property (class, readonly, nonatomic) UIColor *ows_greenColor; @property (class, readonly, nonatomic) UIColor *ows_redColor; @property (class, readonly, nonatomic) UIColor *ows_blackColor; +@property (class, readonly, nonatomic) UIColor *ows_blackIconColor; @property (class, readonly, nonatomic) UIColor *ows_errorMessageBorderColor; @property (class, readonly, nonatomic) UIColor *ows_infoMessageBorderColor; @property (class, readonly, nonatomic) UIColor *ows_inputToolbarBackgroundColor; diff --git a/SignalMessaging/categories/UIColor+OWS.m b/SignalMessaging/categories/UIColor+OWS.m index a6736c3e9..d48b14435 100644 --- a/SignalMessaging/categories/UIColor+OWS.m +++ b/SignalMessaging/categories/UIColor+OWS.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSMath.h" @@ -27,6 +27,11 @@ NS_ASSUME_NONNULL_BEGIN return [UIColor colorWithRed:8.f / 255.f green:10.f / 255.f blue:0. / 255.f alpha:1.f]; } ++ (UIColor *)ows_blackIconColor +{ + return [UIColor colorWithRGBHex:0x505050]; +} + + (UIColor *)ows_darkGrayColor { return [UIColor colorWithRed:81.f / 255.f green:81.f / 255.f blue:81.f / 255.f alpha:1.f]; diff --git a/SignalMessaging/contacts/SelectThreadViewController.m b/SignalMessaging/contacts/SelectThreadViewController.m index 22790a509..62d44b97f 100644 --- a/SignalMessaging/contacts/SelectThreadViewController.m +++ b/SignalMessaging/contacts/SelectThreadViewController.m @@ -217,17 +217,14 @@ NS_ASSUME_NONNULL_BEGIN }]; if (disappearingMessagesConfiguration && disappearingMessagesConfiguration.isEnabled) { - UIImage *icon = [UIImage imageNamed:@"table_ic_hourglass"]; - OWSAssert(icon); - UIImageView *iconView = [UIImageView new]; - iconView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - iconView.tintColor = [UIColor colorWithWhite:0.5f alpha:1.f]; - iconView.contentMode = UIViewContentModeScaleAspectFit; - // Default size of this icon is a too large for the thread picker context - // so we specify a bit smaller. - iconView.frame = CGRectMake(0, 0, 20, 20); - - cell.accessoryView = iconView; + DisappearingTimerConfigurationView *disappearingTimerConfigurationView = + [[DisappearingTimerConfigurationView alloc] + initWithDurationSeconds:disappearingMessagesConfiguration.durationSeconds]; + + disappearingTimerConfigurationView.frame = CGRectMake(0, 0, 44, 44); + disappearingTimerConfigurationView.tintColor = [UIColor colorWithWhite:0.5f alpha:1.f]; + + cell.accessoryView = disappearingTimerConfigurationView; } } diff --git a/SignalServiceKit/src/Contacts/OWSDisappearingMessagesConfiguration.h b/SignalServiceKit/src/Contacts/OWSDisappearingMessagesConfiguration.h index 03ee715f0..1aa20daab 100644 --- a/SignalServiceKit/src/Contacts/OWSDisappearingMessagesConfiguration.h +++ b/SignalServiceKit/src/Contacts/OWSDisappearingMessagesConfiguration.h @@ -1,5 +1,6 @@ -// Created by Michael Kirk on 9/23/16. -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// #import "TSYapDatabaseObject.h" @@ -24,7 +25,7 @@ extern const uint32_t OWSDisappearingMessagesConfigurationDefaultExpirationDurat + (NSArray *)validDurationsSeconds; -+ (NSString *)stringForDurationSeconds:(uint32_t)durationSeconds; ++ (NSString *)stringForDurationSeconds:(uint32_t)durationSeconds useShortFormat:(BOOL)useShortFormat; @end diff --git a/SignalServiceKit/src/Contacts/OWSDisappearingMessagesConfiguration.m b/SignalServiceKit/src/Contacts/OWSDisappearingMessagesConfiguration.m index f3242de2f..6ea20514b 100644 --- a/SignalServiceKit/src/Contacts/OWSDisappearingMessagesConfiguration.m +++ b/SignalServiceKit/src/Contacts/OWSDisappearingMessagesConfiguration.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSDisappearingMessagesConfiguration.h" @@ -62,7 +62,7 @@ const uint32_t OWSDisappearingMessagesConfigurationDefaultExpirationDuration = k } } -+ (NSString *)stringForDurationSeconds:(uint32_t)durationSeconds ++ (NSString *)stringForDurationSeconds:(uint32_t)durationSeconds useShortFormat:(BOOL)useShortFormat { NSString *amountFormat; uint32_t duration; @@ -73,55 +73,101 @@ const uint32_t OWSDisappearingMessagesConfigurationDefaultExpirationDuration = k uint32_t secondsPerWeek = secondsPerDay * 7; if (durationSeconds < secondsPerMinute) { // XX Seconds - amountFormat = NSLocalizedString(@"TIME_AMOUNT_SECONDS", - @"{{number of seconds}} embedded in strings, e.g. 'Alice updated disappearing messages " - @"expiration to {{5 seconds}}'. See other *_TIME_AMOUNT strings"); + if (useShortFormat) { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_SECONDS_SHORT_FORMAT", + @"Label text below navbar button, embeds {{number of seconds}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5s' not '5 s'. See other *_TIME_AMOUNT strings"); + } else { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_SECONDS", + @"{{number of seconds}} embedded in strings, e.g. 'Alice updated disappearing messages " + @"expiration to {{5 seconds}}'. See other *_TIME_AMOUNT strings"); + } + duration = durationSeconds; } else if (durationSeconds < secondsPerMinute * 1.5) { // 1 Minute - amountFormat = NSLocalizedString(@"TIME_AMOUNT_SINGLE_MINUTE", - @"{{1 minute}} embedded in strings, e.g. 'Alice updated disappearing messages " - @"expiration to {{1 minute}}'. See other *_TIME_AMOUNT strings"); + if (useShortFormat) { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_MINUTES_SHORT_FORMAT", + @"Label text below navbar button, embeds {{number of minutes}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5m' not '5 m'. See other *_TIME_AMOUNT strings"); + } else { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_SINGLE_MINUTE", + @"{{1 minute}} embedded in strings, e.g. 'Alice updated disappearing messages " + @"expiration to {{1 minute}}'. See other *_TIME_AMOUNT strings"); + } duration = durationSeconds / secondsPerMinute; } else if (durationSeconds < secondsPerHour) { // Multiple Minutes - amountFormat = NSLocalizedString(@"TIME_AMOUNT_MINUTES", - @"{{number of minutes}} embedded in strings, e.g. 'Alice updated disappearing messages " - @"expiration to {{5 minutes}}'. See other *_TIME_AMOUNT strings"); + if (useShortFormat) { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_MINUTES_SHORT_FORMAT", + @"Label text below navbar button, embeds {{number of minutes}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5m' not '5 m'. See other *_TIME_AMOUNT strings"); + } else { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_MINUTES", + @"{{number of minutes}} embedded in strings, e.g. 'Alice updated disappearing messages " + @"expiration to {{5 minutes}}'. See other *_TIME_AMOUNT strings"); + } duration = durationSeconds / secondsPerMinute; } else if (durationSeconds < secondsPerHour * 1.5) { // 1 Hour - amountFormat = NSLocalizedString(@"TIME_AMOUNT_SINGLE_HOUR", - @"{{1 hour}} embedded in strings, e.g. 'Alice updated disappearing messages " - @"expiration to {{1 hour}}'. See other *_TIME_AMOUNT strings"); + if (useShortFormat) { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_HOURS_SHORT_FORMAT", + @"Label text below navbar button, embeds {{number of hours}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5h' not '5 h'. See other *_TIME_AMOUNT strings"); + } else { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_SINGLE_HOUR", + @"{{1 hour}} embedded in strings, e.g. 'Alice updated disappearing messages " + @"expiration to {{1 hour}}'. See other *_TIME_AMOUNT strings"); + } duration = durationSeconds / secondsPerHour; } else if (durationSeconds < secondsPerDay) { // Multiple Hours - amountFormat = NSLocalizedString(@"TIME_AMOUNT_HOURS", - @"{{number of hours}} embedded in strings, e.g. 'Alice updated disappearing messages " - @"expiration to {{5 hours}}'. See other *_TIME_AMOUNT strings"); + if (useShortFormat) { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_HOURS_SHORT_FORMAT", + @"Label text below navbar button, embeds {{number of hours}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5h' not '5 h'. See other *_TIME_AMOUNT strings"); + } else { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_HOURS", + @"{{number of hours}} embedded in strings, e.g. 'Alice updated disappearing messages " + @"expiration to {{5 hours}}'. See other *_TIME_AMOUNT strings"); + } duration = durationSeconds / secondsPerHour; } else if (durationSeconds < secondsPerDay * 1.5) { // 1 Day - amountFormat = NSLocalizedString(@"TIME_AMOUNT_SINGLE_DAY", - @"{{1 day}} embedded in strings, e.g. 'Alice updated disappearing messages " - @"expiration to {{1 day}}'. See other *_TIME_AMOUNT strings"); + if (useShortFormat) { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_DAYS_SHORT_FORMAT", + @"Label text below navbar button, embeds {{number of days}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5d' not '5 d'. See other *_TIME_AMOUNT strings"); + } else { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_SINGLE_DAY", + @"{{1 day}} embedded in strings, e.g. 'Alice updated disappearing messages " + @"expiration to {{1 day}}'. See other *_TIME_AMOUNT strings"); + } duration = durationSeconds / secondsPerDay; } else if (durationSeconds < secondsPerWeek) { // Multiple Days - amountFormat = NSLocalizedString(@"TIME_AMOUNT_DAYS", - @"{{number of days}} embedded in strings, e.g. 'Alice updated disappearing messages " - @"expiration to {{5 days}}'. See other *_TIME_AMOUNT strings"); + if (useShortFormat) { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_DAYS_SHORT_FORMAT", + @"Label text below navbar button, embeds {{number of days}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5d' not '5 d'. See other *_TIME_AMOUNT strings"); + } else { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_DAYS", + @"{{number of days}} embedded in strings, e.g. 'Alice updated disappearing messages " + @"expiration to {{5 days}}'. See other *_TIME_AMOUNT strings"); + } duration = durationSeconds / secondsPerDay; } else if (durationSeconds < secondsPerWeek * 1.5) { // 1 Week - amountFormat = NSLocalizedString(@"TIME_AMOUNT_SINGLE_WEEK", - @"{{1 week}} embedded in strings, e.g. 'Alice updated disappearing messages " - @"expiration to {{1 week}}'. See other *_TIME_AMOUNT strings"); + if (useShortFormat) { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_WEEKS_SHORT_FORMAT", + @"Label text below navbar button, embeds {{number of weeks}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5w' not '5 w'. See other *_TIME_AMOUNT strings"); + } else { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_SINGLE_WEEK", + @"{{1 week}} embedded in strings, e.g. 'Alice updated disappearing messages " + @"expiration to {{1 week}}'. See other *_TIME_AMOUNT strings"); + } duration = durationSeconds / secondsPerWeek; } else { // Multiple weeks - amountFormat = NSLocalizedString(@"TIME_AMOUNT_WEEKS", - @"{{number of weeks}}, embedded in strings, e.g. 'Alice updated disappearing messages " - @"expiration to {{5 weeks}}'. See other *_TIME_AMOUNT strings"); + if (useShortFormat) { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_WEEKS_SHORT_FORMAT", + @"Label text below navbar button, embeds {{number of weeks}}. Must be very short, like 1 or 2 characters, The space is intentionally ommitted between the text and the embedded duration so that we get, e.g. '5w' not '5 w'. See other *_TIME_AMOUNT strings"); + } else { + amountFormat = NSLocalizedString(@"TIME_AMOUNT_WEEKS", + @"{{number of weeks}}, embedded in strings, e.g. 'Alice updated disappearing messages " + @"expiration to {{5 weeks}}'. See other *_TIME_AMOUNT strings"); + } duration = durationSeconds / secondsPerWeek; } @@ -152,7 +198,7 @@ const uint32_t OWSDisappearingMessagesConfigurationDefaultExpirationDuration = k - (NSString *)durationString { - return [self.class stringForDurationSeconds:self.durationSeconds]; + return [self.class stringForDurationSeconds:self.durationSeconds useShortFormat:NO]; } #pragma mark - Dirty Tracking diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m index 96f03bb7e..eec9f356b 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m @@ -1,5 +1,6 @@ -// Created by Michael Kirk on 9/25/16. -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// #import "OWSDisappearingConfigurationUpdateInfoMessage.h" #import "OWSDisappearingMessagesConfiguration.h" @@ -57,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN @"strings for context."); NSString *durationString = - [OWSDisappearingMessagesConfiguration stringForDurationSeconds:self.configurationDurationSeconds]; + [OWSDisappearingMessagesConfiguration stringForDurationSeconds:self.configurationDurationSeconds useShortFormat:NO]; return [NSString stringWithFormat:infoFormat, self.createdByRemoteName, durationString]; } else { NSString *infoFormat = NSLocalizedString(@"OTHER_DISABLED_DISAPPEARING_MESSAGES_CONFIGURATION", @@ -70,7 +71,7 @@ NS_ASSUME_NONNULL_BEGIN @"Info message embedding a {{time amount}}, see the *_TIME_AMOUNT strings for context."); NSString *durationString = - [OWSDisappearingMessagesConfiguration stringForDurationSeconds:self.configurationDurationSeconds]; + [OWSDisappearingMessagesConfiguration stringForDurationSeconds:self.configurationDurationSeconds useShortFormat:NO]; return [NSString stringWithFormat:infoFormat, durationString]; } else { return NSLocalizedString(@"YOU_DISABLED_DISAPPEARING_MESSAGES_CONFIGURATION",