Notification titles for iOS10+

This commit is contained in:
Michael Kirk 2019-01-30 19:11:56 -07:00
parent 1dbb9849c5
commit d88ffc4775
7 changed files with 144 additions and 122 deletions

View File

@ -115,8 +115,8 @@ protocol NotificationPresenterAdaptee: class {
func registerNotificationSettings() -> Promise<Void> func registerNotificationSettings() -> Promise<Void>
func notify(category: AppNotificationCategory, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?) func notify(category: AppNotificationCategory, title: String?, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?)
func notify(category: AppNotificationCategory, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?, replacingIdentifier: String?) func notify(category: AppNotificationCategory, title: String?, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?, replacingIdentifier: String?)
func cancelNotifications(threadId: String) func cancelNotifications(threadId: String)
func clearAllNotifications() func clearAllNotifications()
@ -210,14 +210,15 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
} }
func presentIncomingCall(_ call: SignalCall, callerName: String) { func presentIncomingCall(_ call: SignalCall, callerName: String) {
let alertMessage: String
let notificationTitle: String?
switch previewType { switch previewType {
case .noNameNoPreview: case .noNameNoPreview:
alertMessage = CallStrings.incomingCallWithoutCallerNameNotification notificationTitle = nil
case .nameNoPreview, .namePreview: case .nameNoPreview, .namePreview:
alertMessage = String(format: CallStrings.incomingCallNotificationFormat, callerName) notificationTitle = callerName
} }
let notificationBody = "☎️".rtlSafeAppend(" ").rtlSafeAppend(alertMessage) let notificationBody = NotificationStrings.incomingCallBody
let remotePhoneNumber = call.remotePhoneNumber let remotePhoneNumber = call.remotePhoneNumber
let thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber) let thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber)
@ -234,6 +235,7 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
DispatchQueue.main.async { DispatchQueue.main.async {
self.adaptee.notify(category: .incomingCall, self.adaptee.notify(category: .incomingCall,
title: notificationTitle,
body: notificationBody, body: notificationBody,
userInfo: userInfo, userInfo: userInfo,
sound: .defaultiOSIncomingRingtone, sound: .defaultiOSIncomingRingtone,
@ -242,13 +244,14 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
} }
func presentMissedCall(_ call: SignalCall, callerName: String) { func presentMissedCall(_ call: SignalCall, callerName: String) {
let notificationBody: String let notificationTitle: String?
switch previewType { switch previewType {
case .noNameNoPreview: case .noNameNoPreview:
notificationBody = CallStrings.missedCallNotificationBodyWithoutCallerName notificationTitle = nil
case .nameNoPreview, .namePreview: case .nameNoPreview, .namePreview:
notificationBody = String(format: CallStrings.missedCallNotificationBodyWithCallerName, callerName) notificationTitle = callerName
} }
let notificationBody = NotificationStrings.missedCallBody
let remotePhoneNumber = call.remotePhoneNumber let remotePhoneNumber = call.remotePhoneNumber
let thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber) let thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber)
@ -266,6 +269,7 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
DispatchQueue.main.async { DispatchQueue.main.async {
let sound = self.requestSound(thread: thread) let sound = self.requestSound(thread: thread)
self.adaptee.notify(category: .missedCall, self.adaptee.notify(category: .missedCall,
title: notificationTitle,
body: notificationBody, body: notificationBody,
userInfo: userInfo, userInfo: userInfo,
sound: sound, sound: sound,
@ -274,13 +278,14 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
} }
public func presentMissedCallBecauseOfNoLongerVerifiedIdentity(call: SignalCall, callerName: String) { public func presentMissedCallBecauseOfNoLongerVerifiedIdentity(call: SignalCall, callerName: String) {
let notificationBody: String let notificationTitle: String?
switch previewType { switch previewType {
case .noNameNoPreview: case .noNameNoPreview:
notificationBody = CallStrings.missedCallWithIdentityChangeNotificationBodyWithoutCallerName notificationTitle = nil
case .nameNoPreview, .namePreview: case .nameNoPreview, .namePreview:
notificationBody = String(format: CallStrings.missedCallWithIdentityChangeNotificationBodyWithCallerName, callerName) notificationTitle = callerName
} }
let notificationBody = NotificationStrings.missedCallBecauseOfIdentityChangeBody
let remotePhoneNumber = call.remotePhoneNumber let remotePhoneNumber = call.remotePhoneNumber
let thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber) let thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber)
@ -296,6 +301,7 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
DispatchQueue.main.async { DispatchQueue.main.async {
let sound = self.requestSound(thread: thread) let sound = self.requestSound(thread: thread)
self.adaptee.notify(category: .missedCallFromNoLongerVerifiedIdentity, self.adaptee.notify(category: .missedCallFromNoLongerVerifiedIdentity,
title: notificationTitle,
body: notificationBody, body: notificationBody,
userInfo: userInfo, userInfo: userInfo,
sound: sound, sound: sound,
@ -304,14 +310,14 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
} }
public func presentMissedCallBecauseOfNewIdentity(call: SignalCall, callerName: String) { public func presentMissedCallBecauseOfNewIdentity(call: SignalCall, callerName: String) {
let notificationTitle: String?
let notificationBody: String
switch previewType { switch previewType {
case .noNameNoPreview: case .noNameNoPreview:
notificationBody = CallStrings.missedCallWithIdentityChangeNotificationBodyWithoutCallerName notificationTitle = nil
case .nameNoPreview, .namePreview: case .nameNoPreview, .namePreview:
notificationBody = String(format: CallStrings.missedCallWithIdentityChangeNotificationBodyWithCallerName, callerName) notificationTitle = callerName
} }
let notificationBody = NotificationStrings.missedCallBecauseOfIdentityChangeBody
let remotePhoneNumber = call.remotePhoneNumber let remotePhoneNumber = call.remotePhoneNumber
let thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber) let thread = TSContactThread.getOrCreateThread(contactId: remotePhoneNumber)
@ -329,6 +335,7 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
DispatchQueue.main.async { DispatchQueue.main.async {
let sound = self.requestSound(thread: thread) let sound = self.requestSound(thread: thread)
self.adaptee.notify(category: .missedCall, self.adaptee.notify(category: .missedCall,
title: notificationTitle,
body: notificationBody, body: notificationBody,
userInfo: userInfo, userInfo: userInfo,
sound: sound, sound: sound,
@ -354,46 +361,34 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
let senderName = contactsManager.displayName(forPhoneIdentifier: incomingMessage.authorId) let senderName = contactsManager.displayName(forPhoneIdentifier: incomingMessage.authorId)
let notificationBody: String let notificationTitle: String?
switch previewType { switch previewType {
case .noNameNoPreview: case .noNameNoPreview:
notificationBody = NSLocalizedString("APN_Message", comment: "") notificationTitle = nil
case .nameNoPreview: case .nameNoPreview, .namePreview:
switch thread { switch thread {
case is TSContactThread: case is TSContactThread:
// TODO - should this be a format string? seems weird we're hardcoding in a ":" notificationTitle = senderName
let fromText = NSLocalizedString("APN_MESSAGE_FROM", comment: "")
notificationBody = String(format: "%@: %@", fromText, senderName)
case is TSGroupThread: case is TSGroupThread:
var groupName = thread.name() var groupName = thread.name()
if groupName.count < 1 { if groupName.count < 1 {
groupName = MessageStrings.newGroupDefaultTitle groupName = MessageStrings.newGroupDefaultTitle
} }
// TODO - should this be a format string? seems weird we're hardcoding in the quotes notificationTitle = String(format: NotificationStrings.incomingGroupMessageTitleFormat,
let fromText = NSLocalizedString("APN_MESSAGE_IN_GROUP", comment: "") senderName,
notificationBody = String(format: "%@ \"%@\"", fromText, groupName) groupName)
default: default:
owsFailDebug("unexpected thread: \(thread)") owsFailDebug("unexpected thread: \(thread)")
return return
} }
case .namePreview: }
switch thread {
case is TSContactThread:
notificationBody = String(format: "%@: %@", senderName, messageText ?? "")
case is TSGroupThread:
var groupName = thread.name()
if groupName.count < 1 {
groupName = MessageStrings.newGroupDefaultTitle
}
let threadName = String(format: "\"%@\"", groupName)
let bodyFormat = NSLocalizedString("APN_MESSAGE_IN_GROUP_DETAILED", comment: "") let notificationBody: String?
notificationBody = String(format: bodyFormat, senderName, threadName, messageText ?? "") switch previewType {
default: case .noNameNoPreview, .nameNoPreview:
owsFailDebug("unexpected thread: \(thread)") notificationBody = NotificationStrings.incomingMessageBody
return case .namePreview:
} notificationBody = messageText
} }
guard let threadId = thread.uniqueId else { guard let threadId = thread.uniqueId else {
@ -401,6 +396,8 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
return return
} }
assert((notificationBody ?? notificationTitle) != nil)
// Don't reply from lockscreen if anyone in this conversation is // Don't reply from lockscreen if anyone in this conversation is
// "no longer verified". // "no longer verified".
var category = AppNotificationCategory.incomingMessage var category = AppNotificationCategory.incomingMessage
@ -417,13 +414,24 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
DispatchQueue.main.async { DispatchQueue.main.async {
let sound = self.requestSound(thread: thread) let sound = self.requestSound(thread: thread)
self.adaptee.notify(category: category, body: notificationBody, userInfo: userInfo, sound: sound) self.adaptee.notify(category: category,
title: notificationTitle,
body: notificationBody ?? "",
userInfo: userInfo,
sound: sound)
} }
} }
public func notifyForFailedSend(inThread thread: TSThread) { public func notifyForFailedSend(inThread thread: TSThread) {
let notificationFormat = NSLocalizedString("NOTIFICATION_SEND_FAILED", comment: "subsequent notification body when replying from notification fails") let notificationTitle: String?
let notificationBody = String(format: notificationFormat, thread.name()) switch previewType {
case .noNameNoPreview:
notificationTitle = nil
case .nameNoPreview, .namePreview:
notificationTitle = thread.name()
}
let notificationBody = NotificationStrings.failedToSendBody
guard let threadId = thread.uniqueId else { guard let threadId = thread.uniqueId else {
owsFailDebug("threadId was unexpectedly nil") owsFailDebug("threadId was unexpectedly nil")
@ -436,23 +444,26 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
DispatchQueue.main.async { DispatchQueue.main.async {
let sound = self.requestSound(thread: thread) let sound = self.requestSound(thread: thread)
self.adaptee.notify(category: .errorMessage, body: notificationBody, userInfo: userInfo, sound: sound) self.adaptee.notify(category: .errorMessage,
title: notificationTitle,
body: notificationBody,
userInfo: userInfo,
sound: sound)
} }
} }
public func notifyUser(for errorMessage: TSErrorMessage, thread: TSThread, transaction: YapDatabaseReadWriteTransaction) { public func notifyUser(for errorMessage: TSErrorMessage, thread: TSThread, transaction: YapDatabaseReadWriteTransaction) {
let messageText = errorMessage.previewText(with: transaction)
let authorName = thread.name()
let notificationBody: String let notificationTitle: String?
switch self.previewType { switch self.previewType {
case .namePreview, .nameNoPreview: case .namePreview, .nameNoPreview:
// TODO better format string, seems weird to hardcode ":" notificationTitle = thread.name()
notificationBody = authorName.rtlSafeAppend(":").rtlSafeAppend(" ").rtlSafeAppend(messageText)
case .noNameNoPreview: case .noNameNoPreview:
notificationBody = messageText notificationTitle = nil
} }
let notificationBody = errorMessage.previewText(with: transaction)
guard let threadId = thread.uniqueId else { guard let threadId = thread.uniqueId else {
owsFailDebug("threadId was unexpectedly nil") owsFailDebug("threadId was unexpectedly nil")
return return
@ -464,7 +475,11 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
transaction.addCompletionQueue(DispatchQueue.main) { transaction.addCompletionQueue(DispatchQueue.main) {
let sound = self.requestSound(thread: thread) let sound = self.requestSound(thread: thread)
self.adaptee.notify(category: .errorMessage, body: notificationBody, userInfo: userInfo, sound: sound) self.adaptee.notify(category: .errorMessage,
title: notificationTitle,
body: notificationBody,
userInfo: userInfo,
sound: sound)
} }
} }
@ -473,7 +488,11 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
transaction.addCompletionQueue(DispatchQueue.main) { transaction.addCompletionQueue(DispatchQueue.main) {
let sound = self.checkIfShouldPlaySound() ? OWSSounds.globalNotificationSound() : nil let sound = self.checkIfShouldPlaySound() ? OWSSounds.globalNotificationSound() : nil
self.adaptee.notify(category: .threadlessErrorMessage, body: notificationBody, userInfo: [:], sound: sound) self.adaptee.notify(category: .threadlessErrorMessage,
title: nil,
body: notificationBody,
userInfo: [:],
sound: sound)
} }
} }

View File

@ -137,12 +137,12 @@ extension LegacyNotificationPresenterAdaptee: NotificationPresenterAdaptee {
return promise return promise
} }
func notify(category: AppNotificationCategory, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?) { func notify(category: AppNotificationCategory, title: String?, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?) {
AssertIsOnMainThread() AssertIsOnMainThread()
notify(category: category, body: body, userInfo: userInfo, sound: sound, replacingIdentifier: nil) notify(category: category, title: title, body: body, userInfo: userInfo, sound: sound, replacingIdentifier: nil)
} }
func notify(category: AppNotificationCategory, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?, replacingIdentifier: String?) { func notify(category: AppNotificationCategory, title: String?, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?, replacingIdentifier: String?) {
AssertIsOnMainThread() AssertIsOnMainThread()
guard UIApplication.shared.applicationState != .active else { guard UIApplication.shared.applicationState != .active else {
if let sound = sound { if let sound = sound {
@ -154,9 +154,16 @@ extension LegacyNotificationPresenterAdaptee: NotificationPresenterAdaptee {
return return
} }
let alertBody: String
if let title = title {
alertBody = title.rtlSafeAppend(":").rtlSafeAppend(" ").rtlSafeAppend(body)
} else {
alertBody = body
}
let notification = UILocalNotification() let notification = UILocalNotification()
notification.category = category.identifier notification.category = category.identifier
notification.alertBody = body notification.alertBody = alertBody
notification.userInfo = userInfo notification.userInfo = userInfo
notification.soundName = sound?.filename notification.soundName = sound?.filename

View File

@ -101,12 +101,12 @@ extension UserNotificationPresenterAdaptee: NotificationPresenterAdaptee {
} }
} }
func notify(category: AppNotificationCategory, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?) { func notify(category: AppNotificationCategory, title: String?, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?) {
AssertIsOnMainThread() AssertIsOnMainThread()
notify(category: category, body: body, userInfo: userInfo, sound: sound, replacingIdentifier: nil) notify(category: category, title: title, body: body, userInfo: userInfo, sound: sound, replacingIdentifier: nil)
} }
func notify(category: AppNotificationCategory, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?, replacingIdentifier: String?) { func notify(category: AppNotificationCategory, title: String?, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?, replacingIdentifier: String?) {
AssertIsOnMainThread() AssertIsOnMainThread()
let content = UNMutableNotificationContent() let content = UNMutableNotificationContent()
@ -132,6 +132,9 @@ extension UserNotificationPresenterAdaptee: NotificationPresenterAdaptee {
} }
if shouldPresentNotification(category: category, userInfo: userInfo) { if shouldPresentNotification(category: category, userInfo: userInfo) {
if let title = title {
content.title = title
}
content.body = body content.body = body
} else { } else {
// Play sound and vibrate, but without a `body` no banner will show. // Play sound and vibrate, but without a `body` no banner will show.

View File

@ -53,21 +53,12 @@
/* The label for the 'save' button in action sheets. */ /* The label for the 'save' button in action sheets. */
"ALERT_SAVE" = "Save"; "ALERT_SAVE" = "Save";
/* No comment provided by engineer. */ /* notification action */
"ANSWER_CALL_BUTTON_TITLE" = "Answer"; "ANSWER_CALL_BUTTON_TITLE" = "Answer";
/* No comment provided by engineer. */ /* notification body */
"APN_Message" = "New Message!"; "APN_Message" = "New Message!";
/* No comment provided by engineer. */
"APN_MESSAGE_FROM" = "Message from";
/* No comment provided by engineer. */
"APN_MESSAGE_IN_GROUP" = "Message in group";
/* No comment provided by engineer. */
"APN_MESSAGE_IN_GROUP_DETAILED" = "%@ in group %@: %@";
/* Message for the 'app launch failed' alert. */ /* Message for the 'app launch failed' alert. */
"APP_LAUNCH_FAILURE_ALERT_MESSAGE" = "Signal can't launch. Please send a debug log to support@signal.org so that we can troubleshoot this issue."; "APP_LAUNCH_FAILURE_ALERT_MESSAGE" = "Signal can't launch. Please send a debug log to support@signal.org so that we can troubleshoot this issue.";
@ -329,9 +320,18 @@
/* Alert title when calling and permissions for microphone are missing */ /* Alert title when calling and permissions for microphone are missing */
"CALL_AUDIO_PERMISSION_TITLE" = "Microphone Access Required"; "CALL_AUDIO_PERMISSION_TITLE" = "Microphone Access Required";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
/* Accessibility label for placing call button */ /* Accessibility label for placing call button */
"CALL_LABEL" = "Call"; "CALL_LABEL" = "Call";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
/* Call setup status label after outgoing call times out */ /* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "No Answer"; "CALL_SCREEN_STATUS_NO_ANSWER" = "No Answer";
@ -1113,16 +1113,13 @@
/* Multi-line label explaining how to show names instead of phone numbers in your inbox */ /* Multi-line label explaining how to show names instead of phone numbers in your inbox */
"INBOX_VIEW_MISSING_CONTACTS_PERMISSION" = "You can enable contacts access in the iOS Settings app to see contact names in your Signal conversation list."; "INBOX_VIEW_MISSING_CONTACTS_PERMISSION" = "You can enable contacts access in the iOS Settings app to see contact names in your Signal conversation list.";
/* notification body */ /* info message text in conversation view */
"INCOMING_CALL" = "Incoming call"; "INCOMING_CALL" = "Incoming call";
/* notification body */
"INCOMING_CALL_FROM" = "Incoming call from %@";
/* info message recorded in conversation history when local user declined a call */ /* info message recorded in conversation history when local user declined a call */
"INCOMING_DECLINED_CALL" = "You declined a call"; "INCOMING_DECLINED_CALL" = "You declined a call";
/* No comment provided by engineer. */ /* info message text in conversation view */
"INCOMING_INCOMPLETE_CALL" = "Incoming call"; "INCOMING_INCOMPLETE_CALL" = "Incoming call";
/* info message text shown in conversation view */ /* info message text shown in conversation view */
@ -1374,15 +1371,9 @@
/* Messages that indicates that there are more unseen messages including safety number changes. */ /* Messages that indicates that there are more unseen messages including safety number changes. */
"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_AND_SAFETY_NUMBER_CHANGES" = "There are more unread messages (including safety number changes)."; "MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_AND_SAFETY_NUMBER_CHANGES" = "There are more unread messages (including safety number changes).";
/* notification title */ /* info message text in conversation view */
"MISSED_CALL" = "Missed call"; "MISSED_CALL" = "Missed call";
/* notification title. Embeds {{caller's name or phone number}} */
"MISSED_CALL_WITH_CHANGED_IDENTITY_BODY_WITH_CALLER_NAME" = "Missed call from %@ because their safety number changed.";
/* notification title */
"MISSED_CALL_WITH_CHANGED_IDENTITY_BODY_WITHOUT_CALLER_NAME" = "Missed call because the caller's safety number changed.";
/* Alert body /* Alert body
Alert body when camera is not authorized */ Alert body when camera is not authorized */
"MISSING_CAMERA_PERMISSION_MESSAGE" = "You can enable camera access in the iOS Settings app to make video calls in Signal."; "MISSING_CAMERA_PERMISSION_MESSAGE" = "You can enable camera access in the iOS Settings app to make video calls in Signal.";
@ -1397,9 +1388,6 @@
/* Alert title when user has previously denied media library access */ /* Alert title when user has previously denied media library access */
"MISSING_MEDIA_LIBRARY_PERMISSION_TITLE" = "Signal requires access to your photos for this feature."; "MISSING_MEDIA_LIBRARY_PERMISSION_TITLE" = "Signal requires access to your photos for this feature.";
/* notification title. Embeds {{caller's name or phone number}} */
"MSGVIEW_MISSED_CALL_WITH_NAME" = "Missed call from %@.";
/* alert title: cannot link - reached max linked devices */ /* alert title: cannot link - reached max linked devices */
"MULTIDEVICE_PAIRING_MAX_DESC" = "You cannot link any more devices."; "MULTIDEVICE_PAIRING_MAX_DESC" = "You cannot link any more devices.";
@ -1451,6 +1439,9 @@
/* An indicator that a user is a member of the new group. */ /* An indicator that a user is a member of the new group. */
"NEW_GROUP_MEMBER_LABEL" = "Member"; "NEW_GROUP_MEMBER_LABEL" = "Member";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
/* Placeholder text for group name field */ /* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Name this group chat"; "NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Name this group chat";
@ -1481,9 +1472,6 @@
/* Lock screen notification text presented after user powers on their device without unlocking. Embeds {{device model}} (either 'iPad' or 'iPhone') */ /* Lock screen notification text presented after user powers on their device without unlocking. Embeds {{device model}} (either 'iPad' or 'iPhone') */
"NOTIFICATION_BODY_PHONE_LOCKED_FORMAT" = "You may have received messages while your %@ was restarting."; "NOTIFICATION_BODY_PHONE_LOCKED_FORMAT" = "You may have received messages while your %@ was restarting.";
/* No comment provided by engineer. */
"NOTIFICATION_SEND_FAILED" = "Your message failed to send to %@.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"NOTIFICATIONS_FOOTER_WARNING" = "Due to known bugs in Apple's push framework, message previews will only be shown if the message is retrieved within 30 seconds after being sent. The application badge might be inaccurate as a result."; "NOTIFICATIONS_FOOTER_WARNING" = "Due to known bugs in Apple's push framework, message previews will only be shown if the message is retrieved within 30 seconds after being sent. The application badge might be inaccurate as a result.";
@ -1520,10 +1508,10 @@
/* Label warning the user that the Signal service may be down. */ /* Label warning the user that the Signal service may be down. */
"OUTAGE_WARNING" = "Signal is experiencing technical difficulties. We are working hard to restore service as quickly as possible."; "OUTAGE_WARNING" = "Signal is experiencing technical difficulties. We are working hard to restore service as quickly as possible.";
/* No comment provided by engineer. */ /* info message text in conversation view */
"OUTGOING_CALL" = "Outgoing call"; "OUTGOING_CALL" = "Outgoing call";
/* No comment provided by engineer. */ /* info message text in conversation view */
"OUTGOING_INCOMPLETE_CALL" = "Outgoing call"; "OUTGOING_INCOMPLETE_CALL" = "Outgoing call";
/* info message recorded in conversation history when local user tries and fails to call another user. */ /* info message recorded in conversation history when local user tries and fails to call another user. */
@ -1667,10 +1655,10 @@
/* Title for the profile view. */ /* Title for the profile view. */
"PROFILE_VIEW_TITLE" = "Profile"; "PROFILE_VIEW_TITLE" = "Profile";
/* No comment provided by engineer. */ /* Notification action button title */
"PUSH_MANAGER_MARKREAD" = "Mark as Read"; "PUSH_MANAGER_MARKREAD" = "Mark as Read";
/* No comment provided by engineer. */ /* Notification action button title */
"PUSH_MANAGER_REPLY" = "Reply"; "PUSH_MANAGER_REPLY" = "Reply";
/* Title of alert shown when push tokens sync job succeeds. */ /* Title of alert shown when push tokens sync job succeeds. */
@ -1814,7 +1802,7 @@
/* Title of alert indicating that users needs to enter a phone number to register. */ /* Title of alert indicating that users needs to enter a phone number to register. */
"REGISTRATION_VIEW_NO_PHONE_NUMBER_ALERT_TITLE" = "No Phone Number"; "REGISTRATION_VIEW_NO_PHONE_NUMBER_ALERT_TITLE" = "No Phone Number";
/* No comment provided by engineer. */ /* notification action */
"REJECT_CALL_BUTTON_TITLE" = "Reject"; "REJECT_CALL_BUTTON_TITLE" = "Reject";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
@ -1922,9 +1910,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"SEND_AGAIN_BUTTON" = "Send Again"; "SEND_AGAIN_BUTTON" = "Send Again";
/* Label for the send button in the conversation view. */ /* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send"; "SEND_BUTTON_TITLE" = "Send";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
/* Alert body after invite failed */ /* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "Sending invite failed, please try again later."; "SEND_INVITE_FAILURE" = "Sending invite failed, please try again later.";

View File

@ -50,8 +50,7 @@ public class MessageApprovalViewController: OWSViewController, UITextViewDelegat
comment: "Title for the 'message approval' dialog.") comment: "Title for the 'message approval' dialog.")
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(cancelPressed)) self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(cancelPressed))
sendButton = UIBarButtonItem(title: NSLocalizedString("SEND_BUTTON_TITLE", sendButton = UIBarButtonItem(title: MessageStrings.sendButton,
comment: "Label for the send button in the conversation view."),
style: .plain, style: .plain,
target: self, target: self,
action: #selector(sendPressed)) action: #selector(sendPressed))

View File

@ -37,6 +37,27 @@ import Foundation
static public let sendButton = NSLocalizedString("SEND_BUTTON_TITLE", comment: "Label for the button to send a message") static public let sendButton = NSLocalizedString("SEND_BUTTON_TITLE", comment: "Label for the button to send a message")
} }
@objc
public class NotificationStrings: NSObject {
@objc
static public let incomingCallBody = NSLocalizedString("CALL_INCOMING_NOTIFICATION_BODY", comment: "notification body")
@objc
static public let missedCallBody = NSLocalizedString("CALL_MISSED_NOTIFICATION_BODY", comment: "notification body")
@objc
static public let missedCallBecauseOfIdentityChangeBody = NSLocalizedString("CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY", comment: "notification body")
@objc
static public let incomingMessageBody = NSLocalizedString("APN_Message", comment: "notification body")
@objc
static public let incomingGroupMessageTitleFormat = NSLocalizedString("NEW_GROUP_MESSAGE_NOTIFICATION_TITLE", comment: "notification title. Embeds {{author name}} and {{group name}}")
@objc
static public let failedToSendBody = NSLocalizedString("SEND_FAILED_NOTIFICATION_BODY", comment: "notification body")
}
@objc public class CallStrings: NSObject { @objc public class CallStrings: NSObject {
@objc @objc
static public let callStatusFormat = NSLocalizedString("CALL_STATUS_FORMAT", comment: "embeds {{Call Status}} in call screen label. For ongoing calls, {{Call Status}} is a seconds timer like 01:23, otherwise {{Call Status}} is a short text like 'Ringing', 'Busy', or 'Failed Call'") static public let callStatusFormat = NSLocalizedString("CALL_STATUS_FORMAT", comment: "embeds {{Call Status}} in call screen label. For ongoing calls, {{Call Status}} is a seconds timer like 01:23, otherwise {{Call Status}} is a short text like 'Ringing', 'Busy', or 'Failed Call'")
@ -59,25 +80,7 @@ import Foundation
@objc @objc
static public let answerCallButtonTitle = NSLocalizedString("ANSWER_CALL_BUTTON_TITLE", comment: "notification action") static public let answerCallButtonTitle = NSLocalizedString("ANSWER_CALL_BUTTON_TITLE", comment: "notification action")
@objc @objc
static public let declineCallButtonTitle = NSLocalizedString("REJECT_CALL_BUTTON_TITLE", comment: "") static public let declineCallButtonTitle = NSLocalizedString("REJECT_CALL_BUTTON_TITLE", comment: "notification action")
// MARK: Missed Call Notification
@objc
static public let missedCallNotificationBodyWithoutCallerName = NSLocalizedString("MISSED_CALL", comment: "notification title")
@objc
static public let missedCallNotificationBodyWithCallerName = NSLocalizedString("MSGVIEW_MISSED_CALL_WITH_NAME", comment: "notification title. Embeds {{caller's name or phone number}}")
// MARK: Missed with changed identity notification (for not previously verified identity)
@objc
static public let missedCallWithIdentityChangeNotificationBodyWithoutCallerName = NSLocalizedString("MISSED_CALL_WITH_CHANGED_IDENTITY_BODY_WITHOUT_CALLER_NAME", comment: "notification title")
@objc
static public let missedCallWithIdentityChangeNotificationBodyWithCallerName = NSLocalizedString("MISSED_CALL_WITH_CHANGED_IDENTITY_BODY_WITH_CALLER_NAME", comment: "notification title. Embeds {{caller's name or phone number}}")
@objc
static public let incomingCallWithoutCallerNameNotification = NSLocalizedString("INCOMING_CALL", comment: "notification body, does not include the callers name")
@objc
static public let incomingCallNotificationFormat = NSLocalizedString("INCOMING_CALL_FROM", comment: "notification body, embeds {{caller name or number}}")
} }
@objc public class MediaStrings: NSObject { @objc public class MediaStrings: NSObject {

View File

@ -98,15 +98,15 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
// We don't actually use the `transaction` but other sibling classes do. // We don't actually use the `transaction` but other sibling classes do.
switch (_callType) { switch (_callType) {
case RPRecentCallTypeIncoming: case RPRecentCallTypeIncoming:
return NSLocalizedString(@"INCOMING_CALL", @""); return NSLocalizedString(@"INCOMING_CALL", @"info message text in conversation view");
case RPRecentCallTypeOutgoing: case RPRecentCallTypeOutgoing:
return NSLocalizedString(@"OUTGOING_CALL", @""); return NSLocalizedString(@"OUTGOING_CALL", @"info message text in conversation view");
case RPRecentCallTypeIncomingMissed: case RPRecentCallTypeIncomingMissed:
return NSLocalizedString(@"MISSED_CALL", @""); return NSLocalizedString(@"MISSED_CALL", @"info message text in conversation view");
case RPRecentCallTypeOutgoingIncomplete: case RPRecentCallTypeOutgoingIncomplete:
return NSLocalizedString(@"OUTGOING_INCOMPLETE_CALL", @""); return NSLocalizedString(@"OUTGOING_INCOMPLETE_CALL", @"info message text in conversation view");
case RPRecentCallTypeIncomingIncomplete: case RPRecentCallTypeIncomingIncomplete:
return NSLocalizedString(@"INCOMING_INCOMPLETE_CALL", @""); return NSLocalizedString(@"INCOMING_INCOMPLETE_CALL", @"info message text in conversation view");
case RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity: case RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity:
return NSLocalizedString(@"INFO_MESSAGE_MISSED_CALL_DUE_TO_CHANGED_IDENITY", @"info message text shown in conversation view"); return NSLocalizedString(@"INFO_MESSAGE_MISSED_CALL_DUE_TO_CHANGED_IDENITY", @"info message text shown in conversation view");
case RPRecentCallTypeIncomingDeclined: case RPRecentCallTypeIncomingDeclined: