From d7fcac8a5a96645b7aaf3712100ff17e51f96aca Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 5 Mar 2018 17:59:09 -0500 Subject: [PATCH 1/6] In-App notifications don't pause background audio // FREEBIE --- .../ConversationViewController.m | 2 +- .../attachments/OWSVideoPlayer.swift | 2 +- .../environment/OWSAudioSession.swift | 28 +++++++++++-------- SignalMessaging/environment/OWSSounds.m | 2 +- SignalMessaging/utils/OWSAudioPlayer.h | 3 ++ SignalMessaging/utils/OWSAudioPlayer.m | 10 ++++++- 6 files changed, 31 insertions(+), 16 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 95361da33..3206a1854 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -3203,7 +3203,7 @@ typedef enum : NSUInteger { NSURL *fileURL = [NSURL fileURLWithPath:filepath]; // Setup audio session - BOOL configuredAudio = [OWSAudioSession.shared setRecordCategoryWithAudioActivity:self.voiceNoteAudioActivity]; + BOOL configuredAudio = [OWSAudioSession.shared startRecordingAudioActivity:self.voiceNoteAudioActivity]; if (!configuredAudio) { OWSFail(@"%@ Couldn't configure audio session", self.logTag); [self cancelVoiceMemo]; diff --git a/SignalMessaging/attachments/OWSVideoPlayer.swift b/SignalMessaging/attachments/OWSVideoPlayer.swift index f5405e64b..c5b86db31 100644 --- a/SignalMessaging/attachments/OWSVideoPlayer.swift +++ b/SignalMessaging/attachments/OWSVideoPlayer.swift @@ -38,7 +38,7 @@ public class OWSVideoPlayer: NSObject { } public func play() { - OWSAudioSession.shared.setPlaybackCategory(audioActivity: self.audioActivity) + OWSAudioSession.shared.startPlaybackAudioActivity(self.audioActivity) guard let item = avPlayer.currentItem else { owsFail("\(logTag) video player item was unexpectedly nil") diff --git a/SignalMessaging/environment/OWSAudioSession.swift b/SignalMessaging/environment/OWSAudioSession.swift index e892975ff..62a5baa62 100644 --- a/SignalMessaging/environment/OWSAudioSession.swift +++ b/SignalMessaging/environment/OWSAudioSession.swift @@ -33,14 +33,24 @@ public class OWSAudioSession: NSObject { private var currentActivities: [Weak] = [] + // Respects hardware mute switch, plays through external speaker, mixes with backround audio + // appropriate for foreground sound effects. + public func startAmbientAudioActivity(_ audioActivity: AudioActivity) { + Logger.debug("\(logTag) in \(#function)") + + startAudioActivity(audioActivity) + + do { + try avAudioSession.setCategory(AVAudioSessionCategoryAmbient) + } catch { + owsFail("\(logTag) in \(#function) failed with error: \(error)") + } + } + // Ignores hardware mute switch, plays through external speaker - public func setPlaybackCategory(audioActivity: AudioActivity) { + public func startPlaybackAudioActivity(_ audioActivity: AudioActivity) { Logger.debug("\(logTag) in \(#function)") - // In general, we should have put the audio session back to it's default - // category when we were done with whatever activity required it to be modified - assert(avAudioSession.category == AVAudioSessionCategorySoloAmbient) - startAudioActivity(audioActivity) do { @@ -50,13 +60,9 @@ public class OWSAudioSession: NSObject { } } - public func setRecordCategory(audioActivity: AudioActivity) -> Bool { + public func startRecordingAudioActivity(_ audioActivity: AudioActivity) -> Bool { Logger.debug("\(logTag) in \(#function)") - // In general, we should have put the audio session back to it's default - // category when we were done with whatever activity required it to be modified - assert(avAudioSession.category == AVAudioSessionCategorySoloAmbient) - assert(avAudioSession.recordPermission() == .granted) startAudioActivity(audioActivity) @@ -104,8 +110,6 @@ public class OWSAudioSession: NSObject { } do { - try avAudioSession.setCategory(AVAudioSessionCategorySoloAmbient) - // When playing audio in Signal, other apps audio (e.g. Music) is paused. // By notifying when we deactivate, the other app can resume playback. try avAudioSession.setActive(false, with: [.notifyOthersOnDeactivation]) diff --git a/SignalMessaging/environment/OWSSounds.m b/SignalMessaging/environment/OWSSounds.m index 86f4afa44..a9247f583 100644 --- a/SignalMessaging/environment/OWSSounds.m +++ b/SignalMessaging/environment/OWSSounds.m @@ -224,7 +224,7 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob [self.audioPlayer stop]; self.audioPlayer = [OWSSounds audioPlayerForSound:sound quiet:quiet]; if (shouldRespectSilentSwitch) { - [self.audioPlayer playWithCurrentAudioCategory]; + [self.audioPlayer playWithAmbientAudioCategory]; } else { [self.audioPlayer playWithPlaybackAudioCategory]; } diff --git a/SignalMessaging/utils/OWSAudioPlayer.h b/SignalMessaging/utils/OWSAudioPlayer.h index 98a9a2f13..f8aee4045 100644 --- a/SignalMessaging/utils/OWSAudioPlayer.h +++ b/SignalMessaging/utils/OWSAudioPlayer.h @@ -38,6 +38,9 @@ typedef NS_ENUM(NSInteger, AudioPlaybackState) { // respects silent switch - (void)playWithCurrentAudioCategory; +// respects silent switch, mixes with others +- (void)playWithAmbientAudioCategory; + // will ensure sound is audible, even if silent switch is enabled - (void)playWithPlaybackAudioCategory; diff --git a/SignalMessaging/utils/OWSAudioPlayer.m b/SignalMessaging/utils/OWSAudioPlayer.m index e01efcaab..5f935d507 100644 --- a/SignalMessaging/utils/OWSAudioPlayer.m +++ b/SignalMessaging/utils/OWSAudioPlayer.m @@ -99,7 +99,15 @@ NS_ASSUME_NONNULL_BEGIN - (void)playWithPlaybackAudioCategory { OWSAssertIsOnMainThread(); - [OWSAudioSession.shared setPlaybackCategoryWithAudioActivity:self.audioActivity]; + [OWSAudioSession.shared startPlaybackAudioActivity:self.audioActivity]; + + [self play]; +} + +- (void)playWithAmbientAudioCategory +{ + OWSAssertIsOnMainThread(); + [OWSAudioSession.shared startAmbientAudioActivity:self.audioActivity]; [self play]; } From d3be2b4a3b2d661a3908c374273af9de25e4c082 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 5 Mar 2018 18:47:35 -0500 Subject: [PATCH 2/6] Vibrate when playing sound as alert // FREEBIE --- Signal/src/environment/NotificationsManager.m | 8 ++++--- SignalMessaging/environment/OWSSounds.h | 3 --- SignalMessaging/environment/OWSSounds.m | 23 ------------------- SignalMessaging/utils/OWSAudioPlayer.h | 2 +- SignalMessaging/utils/OWSAudioPlayer.m | 3 ++- 5 files changed, 8 insertions(+), 31 deletions(-) diff --git a/Signal/src/environment/NotificationsManager.m b/Signal/src/environment/NotificationsManager.m index 3b65fac5b..7c2503dcf 100644 --- a/Signal/src/environment/NotificationsManager.m +++ b/Signal/src/environment/NotificationsManager.m @@ -25,6 +25,7 @@ @property (nonatomic, readonly) NotificationType notificationPreviewType; @property (nonatomic, readonly) NSMutableArray *notificationHistory; +@property (nonatomic, nullable) OWSAudioPlayer *audioPlayer; @end @@ -238,7 +239,8 @@ } else { if (shouldPlaySound && [Environment.preferences soundInForeground]) { OWSSound sound = [OWSSounds notificationSoundForThread:thread]; - [OWSSounds playSound:sound quiet:YES shouldRespectSilentSwitch:YES]; + self.audioPlayer = [OWSSounds audioPlayerForSound:sound]; + [self.audioPlayer playAsForegroundAlert]; } } }); @@ -343,8 +345,8 @@ } else { if (shouldPlaySound && [Environment.preferences soundInForeground]) { OWSSound sound = [OWSSounds notificationSoundForThread:thread]; - // We play the "quiet" variation of sounds if possible for notifications in the foreground. - [OWSSounds playSound:sound quiet:YES shouldRespectSilentSwitch:YES]; + self.audioPlayer = [OWSSounds audioPlayerForSound:sound]; + [self.audioPlayer playAsForegroundAlert]; } } }); diff --git a/SignalMessaging/environment/OWSSounds.h b/SignalMessaging/environment/OWSSounds.h index 223ef11b1..99d40ccfc 100644 --- a/SignalMessaging/environment/OWSSounds.h +++ b/SignalMessaging/environment/OWSSounds.h @@ -48,9 +48,6 @@ typedef NS_ENUM(NSUInteger, OWSSound) { + (nullable NSString *)filenameForSound:(OWSSound)sound; -+ (void)playSound:(OWSSound)sound shouldRespectSilentSwitch:(BOOL)shouldRespectSilentSwitch; -+ (void)playSound:(OWSSound)sound quiet:(BOOL)quiet shouldRespectSilentSwitch:(BOOL)shouldRespectSilentSwitch; - #pragma mark - Notifications + (NSArray *)allNotificationSounds; diff --git a/SignalMessaging/environment/OWSSounds.m b/SignalMessaging/environment/OWSSounds.m index a9247f583..6d9d89f1d 100644 --- a/SignalMessaging/environment/OWSSounds.m +++ b/SignalMessaging/environment/OWSSounds.m @@ -17,8 +17,6 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; -@property (nonatomic, nullable) OWSAudioPlayer *audioPlayer; - @end #pragma mark - @@ -209,27 +207,6 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob return url; } -+ (void)playSound:(OWSSound)sound shouldRespectSilentSwitch:(BOOL)shouldRespectSilentSwitch -{ - [self.sharedManager playSound:sound quiet:NO shouldRespectSilentSwitch:shouldRespectSilentSwitch]; -} - -+ (void)playSound:(OWSSound)sound quiet:(BOOL)quiet shouldRespectSilentSwitch:(BOOL)shouldRespectSilentSwitch -{ - [self.sharedManager playSound:sound quiet:quiet shouldRespectSilentSwitch:shouldRespectSilentSwitch]; -} - -- (void)playSound:(OWSSound)sound quiet:(BOOL)quiet shouldRespectSilentSwitch:(BOOL)shouldRespectSilentSwitch -{ - [self.audioPlayer stop]; - self.audioPlayer = [OWSSounds audioPlayerForSound:sound quiet:quiet]; - if (shouldRespectSilentSwitch) { - [self.audioPlayer playWithAmbientAudioCategory]; - } else { - [self.audioPlayer playWithPlaybackAudioCategory]; - } -} - #pragma mark - Notifications + (OWSSound)defaultNotificationSound diff --git a/SignalMessaging/utils/OWSAudioPlayer.h b/SignalMessaging/utils/OWSAudioPlayer.h index f8aee4045..10a5acfaa 100644 --- a/SignalMessaging/utils/OWSAudioPlayer.h +++ b/SignalMessaging/utils/OWSAudioPlayer.h @@ -39,7 +39,7 @@ typedef NS_ENUM(NSInteger, AudioPlaybackState) { - (void)playWithCurrentAudioCategory; // respects silent switch, mixes with others -- (void)playWithAmbientAudioCategory; +- (void)playAsForegroundAlert; // will ensure sound is audible, even if silent switch is enabled - (void)playWithPlaybackAudioCategory; diff --git a/SignalMessaging/utils/OWSAudioPlayer.m b/SignalMessaging/utils/OWSAudioPlayer.m index 5f935d507..4586aea1f 100644 --- a/SignalMessaging/utils/OWSAudioPlayer.m +++ b/SignalMessaging/utils/OWSAudioPlayer.m @@ -104,11 +104,12 @@ NS_ASSUME_NONNULL_BEGIN [self play]; } -- (void)playWithAmbientAudioCategory +- (void)playAsForegroundAlert { OWSAssertIsOnMainThread(); [OWSAudioSession.shared startAmbientAudioActivity:self.audioActivity]; + AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); [self play]; } From 788316726553c4b1d6b2236c21896e5a2a2e2e43 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 5 Mar 2018 18:48:37 -0500 Subject: [PATCH 3/6] Fix "None" audio for fallback notifications. // FREEBIE --- SignalMessaging/environment/OWSSounds.m | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/SignalMessaging/environment/OWSSounds.m b/SignalMessaging/environment/OWSSounds.m index 6d9d89f1d..0634304cd 100644 --- a/SignalMessaging/environment/OWSSounds.m +++ b/SignalMessaging/environment/OWSSounds.m @@ -259,11 +259,18 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob DDLogDebug(@"%@ writing new default sound to %@", self.logTag, defaultSoundPath); NSURL *_Nullable soundURL = [OWSSounds soundURLForSound:sound quiet:NO]; - OWSAssert(soundURL); + + NSData *soundData = ^{ + if (soundURL) { + return [NSData dataWithContentsOfURL:soundURL]; + } else { + OWSAssert(sound == OWSSound_None); + return [NSData new]; + } + }(); // Quick way to achieve an atomic "copy" operation that allows overwriting if the user has previously specified // a default notification sound. - NSData *soundData = [NSData dataWithContentsOfURL:soundURL]; BOOL success = [soundData writeToFile:defaultSoundPath atomically:YES]; // The globally configured sound the user has configured is unprotected, so that we can still play the sound if the From 830e9f1bfd4b75c7a685b9932b4ece69829800b4 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 5 Mar 2018 19:00:18 -0500 Subject: [PATCH 4/6] Make "Signal Classic" audio stand out more // FREEBIE --- SignalMessaging/environment/OWSSounds.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SignalMessaging/environment/OWSSounds.m b/SignalMessaging/environment/OWSSounds.m index 0634304cd..60acb3871 100644 --- a/SignalMessaging/environment/OWSSounds.m +++ b/SignalMessaging/environment/OWSSounds.m @@ -68,7 +68,6 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob @(OWSSound_Bamboo), @(OWSSound_Chord), @(OWSSound_Circles), - @(OWSSound_ClassicNotification), @(OWSSound_Complete), @(OWSSound_Hello), @(OWSSound_Input), @@ -76,6 +75,7 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob @(OWSSound_Popcorn), @(OWSSound_Pulse), @(OWSSound_Synth), + @(OWSSound_ClassicNotification), ]; } @@ -113,7 +113,7 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob case OWSSound_Synth: return @"Synth"; case OWSSound_ClassicNotification: - return @"Classic"; + return @"Signal Classic"; // Call Audio case OWSSound_Opening: From c2501d8d1ee09f27b0252eec5c03643a9ac6268a Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 5 Mar 2018 19:00:48 -0500 Subject: [PATCH 5/6] Don't migrate legacy users to use new audio tones // FREEBIE --- .../ExperienceUpgradesPageViewController.swift | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Signal/src/ViewControllers/ExperienceUpgradesPageViewController.swift b/Signal/src/ViewControllers/ExperienceUpgradesPageViewController.swift index b5dc87592..8d8b2bc63 100644 --- a/Signal/src/ViewControllers/ExperienceUpgradesPageViewController.swift +++ b/Signal/src/ViewControllers/ExperienceUpgradesPageViewController.swift @@ -9,13 +9,6 @@ private class IntroducingCustomNotificationAudioExperienceUpgradeViewController: var buttonAction: ((UIButton) -> Void)? - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - // Opt users in to the new default sound ("note") after they've seen the splash screen. - OWSSounds.setGlobalNotificationSound(.note) - } - override func loadView() { self.view = UIView.container() From 4e64b09ad6fd0c2d1a908a638d81552ed0c3bbc5 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 5 Mar 2018 20:45:23 -0500 Subject: [PATCH 6/6] Don't set audio to ambient while other audioActivity exists // FREEBIE --- SignalMessaging/environment/OWSAudioSession.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SignalMessaging/environment/OWSAudioSession.swift b/SignalMessaging/environment/OWSAudioSession.swift index 62a5baa62..d6d60d2d6 100644 --- a/SignalMessaging/environment/OWSAudioSession.swift +++ b/SignalMessaging/environment/OWSAudioSession.swift @@ -40,6 +40,12 @@ public class OWSAudioSession: NSObject { startAudioActivity(audioActivity) + guard currentActivities.count == 0 else { + // We don't want to clobber the audio capabilities configured by (e.g.) media playback or an in-progress call + Logger.info("\(logTag) in \(#function) not touching audio session since another currentActivity exists.") + return + } + do { try avAudioSession.setCategory(AVAudioSessionCategoryAmbient) } catch {