Custom Notification Tones (#5527)

* added foundation for custom notifications

* feat: add localizations for tone names

also, if available, include the pro sound in the element x sound list

* use a static file location to store the notification tone

and update the file at that location when selected

* if there's a problem previewing or setting the alert tone, toast the user with the error

* rename tone directory

* create NotificationToneManager to handle tone business logic

* conform to project formatting

* assign custom sound to actual notifications

* dry up notification generation in the nse

* add compilation condition for including notification alert tone and remove from share extension

* swift format had some opinions

* flatten the notification tone ui, removing system/elementx designations

* fix stored file url breaking due to sandbox path changes

* tone manager can import custom tones and provide a list of imported tones

* ui support for custom tones

* can delete custom imported tones

* sort the custom tones

* add additional preset sounds

* can choose caf files for custom sounds and fix location persistence issues

* store sound locations with relative links

this is a better solution than storing bookmarks since bookmark resolution can be variable and differ from freshly derived urls

* add docs for new notification features

* run tone conversion in background

* resolved several ai found issues

* added protocol and mocks for notification tone manager

* added tests for notification settings screen vm interactions with the tone manager

* add tests for notification tone manager

* rename element tone names per feedback

* move custom tone option to the top of the notification tone list

* rename Tone to Sound for consistency with Android

* remove additional bundled tones

* restore accidentally deleted Element Default sound file

* system alert tones are filtered by what's available on the system prior to finalizing the collection

* Add a couple more caf file conversion log statements

* Simplify the notification sound conversion logic

* Update notification sound strings in sync with Android

* update xcodeproj after rebase

* remove conditional compilation and include NotificationAlertSound in share extension

* Use AudioPlayer instead of custom tone previewer and remove the previewer

* rename NotificationTone

* Consolidate and move errors to top of NotificationToneManager

* tone manager no longer depends on userinterfacecontroller

* Move static Tone properties to NotificationToneManager

* resolve view model initializer feedback

* update xcodeproj

* fatalerror when tone manager cannot init

* Remove banner icon on alert tone error

* fixup review feedback

* Moved URL handling to NotificationToneManager
This commit is contained in:
Michael
2026-05-20 07:40:34 -05:00
committed by GitHub
parent cb1c89be9d
commit dd0de4d7b0
22 changed files with 1213 additions and 17 deletions
@@ -18,6 +18,74 @@ internal enum UntranslatedL10n {
internal static var cryptoHistorySharingConfirmStartChatDialogContent: String { return UntranslatedL10n.tr("Untranslated", "crypto_history_sharing_confirm_start_chat_dialog_content") }
/// Start a chat with this new contact?
internal static var cryptoHistorySharingConfirmStartChatDialogTitle: String { return UntranslatedL10n.tr("Untranslated", "crypto_history_sharing_confirm_start_chat_dialog_title") }
/// Custom sound...
internal static var screenNotificationSettingsSoundCustomSoundButtonTitle: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_custom_sound_button_title") }
/// Error deleting file
internal static var screenNotificationSettingsSoundDeleteSoundErrorTitle: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_delete_sound_error_title") }
/// Element Default
internal static var screenNotificationSettingsSoundElementDefault: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_element_default") }
/// Element Fade
internal static var screenNotificationSettingsSoundElementFade: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_element_fade") }
/// Error importing file
internal static var screenNotificationSettingsSoundImportSoundErrorTitle: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_import_sound_error_title") }
/// Problem previewing alert sound
internal static var screenNotificationSettingsSoundPreviewSoundErrorTitle: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_preview_sound_error_title") }
/// Sound
internal static var screenNotificationSettingsSoundSectionTitle: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_section_title") }
/// Problem setting alert sound
internal static var screenNotificationSettingsSoundSetSoundErrorTitle: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_set_sound_error_title") }
/// Alert
internal static var screenNotificationSettingsSoundSystemAlert: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_alert") }
/// Anticipate
internal static var screenNotificationSettingsSoundSystemAnticipate: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_anticipate") }
/// Bell
internal static var screenNotificationSettingsSoundSystemBell: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_bell") }
/// Bloom
internal static var screenNotificationSettingsSoundSystemBloom: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_bloom") }
/// Calypso
internal static var screenNotificationSettingsSoundSystemCalypso: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_calypso") }
/// Chime
internal static var screenNotificationSettingsSoundSystemChime: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_chime") }
/// Choo Choo
internal static var screenNotificationSettingsSoundSystemChooChoo: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_choo_choo") }
/// Descent
internal static var screenNotificationSettingsSoundSystemDescent: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_descent") }
/// Electronic
internal static var screenNotificationSettingsSoundSystemElectronic: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_electronic") }
/// Fanfare
internal static var screenNotificationSettingsSoundSystemFanfare: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_fanfare") }
/// Glass
internal static var screenNotificationSettingsSoundSystemGlass: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_glass") }
/// Horn
internal static var screenNotificationSettingsSoundSystemHorn: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_horn") }
/// Ladder
internal static var screenNotificationSettingsSoundSystemLadder: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_ladder") }
/// Minuet
internal static var screenNotificationSettingsSoundSystemMinuet: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_minuet") }
/// News Flash
internal static var screenNotificationSettingsSoundSystemNewsFlash: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_news_flash") }
/// Noir
internal static var screenNotificationSettingsSoundSystemNoir: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_noir") }
/// Sherwood Forest
internal static var screenNotificationSettingsSoundSystemSherwoodForest: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_sherwood_forest") }
/// Spell
internal static var screenNotificationSettingsSoundSystemSpell: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_spell") }
/// Suspense
internal static var screenNotificationSettingsSoundSystemSuspense: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_suspense") }
/// Swish
internal static var screenNotificationSettingsSoundSystemSwish: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_swish") }
/// Telegraph
internal static var screenNotificationSettingsSoundSystemTelegraph: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_telegraph") }
/// Tiptoes
internal static var screenNotificationSettingsSoundSystemTiptoes: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_tiptoes") }
/// Tri-tone
internal static var screenNotificationSettingsSoundSystemTriTone: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_tri_tone") }
/// Tweet
internal static var screenNotificationSettingsSoundSystemTweet: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_tweet") }
/// Typewriters
internal static var screenNotificationSettingsSoundSystemTypewriters: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_typewriters") }
/// Update
internal static var screenNotificationSettingsSoundSystemUpdate: String { return UntranslatedL10n.tr("Untranslated", "screen_notification_settings_sound_system_update") }
/// Clear all data currently stored on this device?
/// Sign in again to access your account data and messages.
internal static var softLogoutClearDataDialogContent: String { return UntranslatedL10n.tr("Untranslated", "soft_logout_clear_data_dialog_content") }