diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index e56467b94..d4f0f90ee 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -327,6 +327,7 @@ 458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */; }; 458E383A1D6699FA0094BD24 /* OWSDeviceProvisioningURLParserTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 458E38391D6699FA0094BD24 /* OWSDeviceProvisioningURLParserTest.m */; }; 459311FC1D75C948008DD4F0 /* OWSDeviceTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 459311FB1D75C948008DD4F0 /* OWSDeviceTableViewCell.m */; }; + 45A2F005204473A3002E978A /* NewMessage.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 45A2F004204473A3002E978A /* NewMessage.aifc */; }; 45A663C51F92EC760027B59E /* GroupTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45A663C41F92EC760027B59E /* GroupTableViewCell.swift */; }; 45A6DAD61EBBF85500893231 /* ReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45A6DAD51EBBF85500893231 /* ReminderView.swift */; }; 45AE48511E0732D6004D96C2 /* TurnServerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45AE48501E0732D6004D96C2 /* TurnServerInfo.swift */; }; @@ -901,6 +902,7 @@ 459311FB1D75C948008DD4F0 /* OWSDeviceTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDeviceTableViewCell.m; sourceTree = ""; }; 4597E94E1D8313C100040CDE /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = translations/sq.lproj/Localizable.strings; sourceTree = ""; }; 4597E94F1D8313CB00040CDE /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = translations/bg.lproj/Localizable.strings; sourceTree = ""; }; + 45A2F004204473A3002E978A /* NewMessage.aifc */ = {isa = PBXFileReference; lastKnownFileType = file; name = NewMessage.aifc; path = Signal/AudioFiles/NewMessage.aifc; sourceTree = SOURCE_ROOT; }; 45A663C41F92EC760027B59E /* GroupTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupTableViewCell.swift; sourceTree = ""; }; 45A6DAD51EBBF85500893231 /* ReminderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReminderView.swift; sourceTree = ""; }; 45AE48501E0732D6004D96C2 /* TurnServerInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TurnServerInfo.swift; sourceTree = ""; }; @@ -1150,6 +1152,7 @@ 34074F54203D0722004596AE /* Sounds */ = { isa = PBXGroup; children = ( + 45A2F004204473A3002E978A /* NewMessage.aifc */, 34CF0783203E6B77005C4D61 /* busy_tone_ansi.caf */, 34CF0786203E6B78005C4D61 /* end_call_tone_cept.caf */, 34F77044203F5E4B00C7CBB7 /* messageReceivedClassic-quiet.caf */, @@ -2543,6 +2546,7 @@ 34074FEE203E5436004596AE /* aurora.m4r in Resources */, 34330A5E1E787BD800DF2FB9 /* ElegantIcons.ttf in Resources */, AD83FF451A73426500B5C81A /* audio_pause_button@2x.png in Resources */, + 45A2F005204473A3002E978A /* NewMessage.aifc in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Signal/AudioFiles/NewMessage.aifc b/Signal/AudioFiles/NewMessage.aifc index 0f8777861..694ff1c23 100644 Binary files a/Signal/AudioFiles/NewMessage.aifc and b/Signal/AudioFiles/NewMessage.aifc differ diff --git a/Signal/src/ViewControllers/DebugUI/DebugUITableViewController.m b/Signal/src/ViewControllers/DebugUI/DebugUITableViewController.m index 2502f455b..620ecb481 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUITableViewController.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUITableViewController.m @@ -102,9 +102,9 @@ NS_ASSUME_NONNULL_BEGIN }]; [subsectionItems addObject:sharedDataFileBrowserItem]; OWSTableItem *documentsFileBrowserItem = [OWSTableItem - disclosureItemWithText:@"📁 Document Dir" + disclosureItemWithText:@"📁 App Container" actionBlock:^{ - NSURL *baseURL = [NSURL URLWithString:[OWSFileSystem appDocumentDirectoryPath]]; + NSURL *baseURL = [NSURL URLWithString:[OWSFileSystem appLibraryDirectoryPath]]; DebugUIFileBrowser *fileBrowser = [[DebugUIFileBrowser alloc] initWithFileURL:baseURL]; [viewController.navigationController pushViewController:fileBrowser animated:YES]; }]; diff --git a/SignalMessaging/environment/OWSSounds.m b/SignalMessaging/environment/OWSSounds.m index d6c95cda2..5f33841e6 100644 --- a/SignalMessaging/environment/OWSSounds.m +++ b/SignalMessaging/environment/OWSSounds.m @@ -4,6 +4,7 @@ #import "OWSSounds.h" #import "OWSAudioPlayer.h" +#import #import #import #import @@ -245,6 +246,36 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob + (void)setGlobalNotificationSound:(OWSSound)sound { + // Fallback push notifications play a sound specified by the server, but we don't want to store this configuration + // on the server. Instead, we create a file with the same name as the default to be played when receiving + // a fallback notification. + NSString *dirPath = [[OWSFileSystem appLibraryDirectoryPath] stringByAppendingPathComponent:@"Sounds"]; + [OWSFileSystem ensureDirectoryExists:dirPath]; + + // This name is specified in the payload by the Signal Service when requesting fallback push notifications. + NSString *kDefaultNotificationSoundFilename = @"NewMessage.aifc"; + NSString *defaultSoundPath = [dirPath stringByAppendingPathComponent:kDefaultNotificationSoundFilename]; + + DDLogDebug(@"%@ writing new default sound to %@", self.logTag, defaultSoundPath); + + NSURL *_Nullable soundURL = [OWSSounds soundURLForSound:sound quiet:NO]; + OWSAssert(soundURL); + + // 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 + // user hasn't authenticated after power-cycling their device. + [OWSFileSystem protectFileOrFolderAtPath:defaultSoundPath fileProtectionType:NSFileProtectionNone]; + + if (!success) { + OWSProdLogAndFail( + @"%@ Unable to write new default sound data from: %@ to :%@", self.logTag, soundURL, defaultSoundPath); + return; + } + OWSSounds *instance = OWSSounds.sharedManager; [instance.dbConnection setObject:@(sound) forKey:kOWSSoundsStorageGlobalNotificationKey diff --git a/SignalServiceKit/src/Util/OWSFileSystem.h b/SignalServiceKit/src/Util/OWSFileSystem.h index c5c7da271..70ec5492b 100644 --- a/SignalServiceKit/src/Util/OWSFileSystem.h +++ b/SignalServiceKit/src/Util/OWSFileSystem.h @@ -9,10 +9,14 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; + (BOOL)protectFileOrFolderAtPath:(NSString *)path; ++ (BOOL)protectFileOrFolderAtPath:(NSString *)path fileProtectionType:(NSFileProtectionType)fileProtectionType; + + (BOOL)protectRecursiveContentsAtPath:(NSString *)path; + (NSString *)appDocumentDirectoryPath; ++ (NSString *)appLibraryDirectoryPath; + + (NSString *)appSharedDataDirectoryPath; + (NSString *)cachesDirectoryPath; diff --git a/SignalServiceKit/src/Util/OWSFileSystem.m b/SignalServiceKit/src/Util/OWSFileSystem.m index d4fcf5d17..15d04b0e9 100644 --- a/SignalServiceKit/src/Util/OWSFileSystem.m +++ b/SignalServiceKit/src/Util/OWSFileSystem.m @@ -37,6 +37,12 @@ NS_ASSUME_NONNULL_BEGIN } + (BOOL)protectFileOrFolderAtPath:(NSString *)path +{ + return + [self protectFileOrFolderAtPath:path fileProtectionType:NSFileProtectionCompleteUntilFirstUserAuthentication]; +} + ++ (BOOL)protectFileOrFolderAtPath:(NSString *)path fileProtectionType:(NSFileProtectionType)fileProtectionType { DDLogVerbose(@"%@ protecting file at path: %@", self.logTag, path); if (![NSFileManager.defaultManager fileExistsAtPath:path]) { @@ -44,7 +50,7 @@ NS_ASSUME_NONNULL_BEGIN } NSError *error; - NSDictionary *fileProtection = @{ NSFileProtectionKey : NSFileProtectionCompleteUntilFirstUserAuthentication }; + NSDictionary *fileProtection = @{ NSFileProtectionKey : fileProtectionType }; [[NSFileManager defaultManager] setAttributes:fileProtection ofItemAtPath:path error:&error]; NSDictionary *resourcesAttrs = @{ NSURLIsExcludedFromBackupKey : @YES }; @@ -86,6 +92,14 @@ NS_ASSUME_NONNULL_BEGIN } } ++ (NSString *)appLibraryDirectoryPath +{ + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSURL *documentDirectoryURL = + [[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject]; + return [documentDirectoryURL path]; +} + + (NSString *)appDocumentDirectoryPath { NSFileManager *fileManager = [NSFileManager defaultManager];