diff --git a/Signal/src/ViewControllers/OWSSoundSettingsViewController.m b/Signal/src/ViewControllers/OWSSoundSettingsViewController.m index ec5dfe09c..f526da1cd 100644 --- a/Signal/src/ViewControllers/OWSSoundSettingsViewController.m +++ b/Signal/src/ViewControllers/OWSSoundSettingsViewController.m @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) OWSSound currentSound; -@property (nonatomic) AVAudioPlayer *audioPlayer; +@property (nonatomic, nullable) AVAudioPlayer *audioPlayer; @end diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 558b61d39..855492af1 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1668,6 +1668,9 @@ /* body sent to contacts when inviting to Install Signal */ "SMS_INVITE_BODY" = "I'm inviting you to install Signal! Here is the link:"; +/* Label for the 'no sound' option that allows users to disable sounds for notifications, ringtones, etc. */ +"SOUNDS_NONE" = "None"; + /* Alert body after verifying privacy with {{other user's name}} */ "SUCCESSFUL_VERIFICATION_DESCRIPTION" = "Your safety number with %@ matches. You can mark this contact as verified."; diff --git a/SignalMessaging/environment/OWSSounds.h b/SignalMessaging/environment/OWSSounds.h index 69f68bef1..000bf6747 100644 --- a/SignalMessaging/environment/OWSSounds.h +++ b/SignalMessaging/environment/OWSSounds.h @@ -47,12 +47,15 @@ typedef NS_ENUM(NSUInteger, OWSSound) { OWSSound_Twinkle, OWSSound_Uplift, OWSSound_Waves, - + // Calls OWSSound_CallConnecting, OWSSound_CallOutboundRinging, OWSSound_CallBusy, OWSSound_CallFailure, + + // Other + OWSSound_None, }; @class AVAudioPlayer; @@ -64,7 +67,7 @@ typedef NS_ENUM(NSUInteger, OWSSound) { + (NSString *)displayNameForSound:(OWSSound)sound; -+ (NSString *)filenameForSound:(OWSSound)sound; ++ (nullable NSString *)filenameForSound:(OWSSound)sound; + (void)playSound:(OWSSound)sound; diff --git a/SignalMessaging/environment/OWSSounds.m b/SignalMessaging/environment/OWSSounds.m index 3367db382..447b6f0e6 100644 --- a/SignalMessaging/environment/OWSSounds.m +++ b/SignalMessaging/environment/OWSSounds.m @@ -4,7 +4,6 @@ #import "OWSSounds.h" #import -#import #import #import #import @@ -19,7 +18,7 @@ NSString *const kOWSSoundsStorageGlobalRingtoneKey = @"kOWSSoundsStorageGlobalRi @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; -@property (nonatomic) NSMutableDictionary *systemSoundIDMap; +@property (nonatomic, nullable) AVAudioPlayer *audioPlayer; @end @@ -55,9 +54,6 @@ NSString *const kOWSSoundsStorageGlobalRingtoneKey = @"kOWSSoundsStorageGlobalRi OWSAssert(storageManager); _dbConnection = storageManager.newDatabaseConnection; - // TODO: Is it safe to load all of these sounds into memory? - // Probably better to do LRU cache. - self.systemSoundIDMap = [NSMutableDictionary new]; OWSSingletonAssert(); @@ -67,6 +63,9 @@ NSString *const kOWSSoundsStorageGlobalRingtoneKey = @"kOWSSoundsStorageGlobalRi + (NSArray *)allNotificationSounds { return @[ + // None should be first. + @(OWSSound_None), + @(OWSSound_Aurora), @(OWSSound_Bamboo), @(OWSSound_Chord), @@ -85,6 +84,9 @@ NSString *const kOWSSoundsStorageGlobalRingtoneKey = @"kOWSSoundsStorageGlobalRi + (NSArray *)allRingtoneSounds { return @[ + // None should be first. + @(OWSSound_None), + @(OWSSound_Apex), @(OWSSound_Beacon), @(OWSSound_Bulletin), @@ -214,10 +216,16 @@ NSString *const kOWSSoundsStorageGlobalRingtoneKey = @"kOWSSoundsStorageGlobalRi return @"Call Busy"; case OWSSound_CallFailure: return @"Call Failure"; + + // Other + case OWSSound_None: + return NSLocalizedString(@"SOUNDS_NONE", + @"Label for the 'no sound' option that allows users to disable sounds for notifications, ringtones, " + @"etc."); } } -+ (NSString *)filenameForSound:(OWSSound)sound ++ (nullable NSString *)filenameForSound:(OWSSound)sound { switch (sound) { case OWSSound_Default: @@ -315,12 +323,19 @@ NSString *const kOWSSoundsStorageGlobalRingtoneKey = @"kOWSSoundsStorageGlobalRi return @"busy_tone_ansi.caf"; case OWSSound_CallFailure: return @"end_call_tone_cept.caf"; + + // Other + case OWSSound_None: + return nil; } } -+ (NSURL *)soundURLForSound:(OWSSound)sound ++ (nullable NSURL *)soundURLForSound:(OWSSound)sound { - NSString *filename = [self filenameForSound:sound]; + NSString *_Nullable filename = [self filenameForSound:sound]; + if (!filename) { + return nil; + } NSURL *_Nullable url = [[NSBundle mainBundle] URLForResource:filename.stringByDeletingPathExtension withExtension:filename.pathExtension]; OWSAssert(url); @@ -332,29 +347,11 @@ NSString *const kOWSSoundsStorageGlobalRingtoneKey = @"kOWSSoundsStorageGlobalRi [self.sharedManager playSound:sound]; } -- (SystemSoundID)systemSoundIDForSound:(OWSSound)sound -{ - @synchronized(self) - { - NSNumber *_Nullable systemSoundID = self.systemSoundIDMap[@(sound)]; - if (!systemSoundID) { - NSURL *soundURL = [OWSSounds soundURLForSound:sound]; - SystemSoundID newSystemSoundID; - OSStatus error = AudioServicesCreateSystemSoundID((__bridge CFURLRef)soundURL, &newSystemSoundID); - if (error) { - OWSFail(@"%@ could not load sound.", self.logTag); - } - systemSoundID = @(newSystemSoundID); - self.systemSoundIDMap[@(sound)] = systemSoundID; - } - return (SystemSoundID)systemSoundID.unsignedIntegerValue; - } -} - - (void)playSound:(OWSSound)sound { - SystemSoundID systemSoundID = [self systemSoundIDForSound:sound]; - AudioServicesPlayAlertSound(systemSoundID); + [self.audioPlayer stop]; + self.audioPlayer = [OWSSounds audioPlayerForSound:sound]; + [self.audioPlayer play]; } #pragma mark - Notifications @@ -447,7 +444,10 @@ NSString *const kOWSSoundsStorageGlobalRingtoneKey = @"kOWSSoundsStorageGlobalRi + (nullable AVAudioPlayer *)audioPlayerForSound:(OWSSound)sound { - NSURL *soundURL = [OWSSounds soundURLForSound:sound]; + NSURL *_Nullable soundURL = [OWSSounds soundURLForSound:sound]; + if (!soundURL) { + return nil; + } NSError *error; AVAudioPlayer *_Nullable player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:&error]; if (error || !player) {