Notification settings: pick custom sounds for messages and call ringtones#6702
Notification settings: pick custom sounds for messages and call ringtones#6702jennaharris7 wants to merge 12 commits intoelement-hq:developfrom
Conversation
|
Thank you for your contribution! Here are a few things to check in the PR to ensure it's reviewed as quickly as possible:
|
Resolves merge conflict in NotificationSettingsViewTest.kt by porting the two new sound-picker tests to the v2 testing API (runAndroidComposeUiTest) introduced upstream in commit 11b9efa.
- libraries:preferences:api now uses io.element.android-compose-library so NotificationSound's @immutable annotation can resolve, matching the pattern in libraries:matrix:api. - Rename SoundUnavailableBannerStateProvider to NotificationSoundUnavailableStateProvider so the class name contains the parameter type as required by KonsistClassNameTest.
The three new sound-unavailable tests pushed the class past Detekt's default LargeClass threshold. Matches the suppression pattern already used on five presenter test classes under features/messages/impl/test.
…on mode rememberLauncherForActivityResult requires a LocalActivityResultRegistryOwner, which Paparazzi-driven preview snapshot tests don't provide, causing NotificationSettingsViewPreview to throw IllegalStateException for every preview parameter. Extract the click handlers into a helper that returns a no-op when LocalInspectionMode.current is true; the runtime path is unchanged.
|
Thanks! A question before I review the PR: would it be possible, when the user selects a sound, to copy the selected file in the private application storage, so that the sound file can never be deleted / lost / updated? All the resilience part of this PR could then be removed, that would simplify the PR a lot. Also it will prevent the file to be updated (by a third party app), and so the sound to change without Element app to be aware of, even with the resilience system. |
|
We can absolutely do that - how do we want to handle the current message.mp3 in the app? This will be the starting point for users, do we want to show the label for this to be "Default", or "Custom" (and leave the default label for the true system default?) I've code the code changes in place (barring a change to the message.mp3 label), I just want to run it through some testing before I push |
Maybe something like:
@mxandreas any thoughts? |
|
I have taken a quick look at it, and please see also my comment on the iOS PR for more context: element-hq/element-x-ios#5527 (comment)
|
I've gone ahead and added the Element fade tone in the selection picker - subject to name or ui changes (Android does use the term "Sound", which differs from iOS) |

Content
Notification sound picking moves in-app for both messages and incoming calls.
For message notifications, tapping the row opens a dialog with four options:
The current bundled "Element default" is always reachable through the dialog, so users can return to it after experimenting with custom tones. Silent is reachable inside the system picker.
Open to feedback on the dialog's option set, labels, and presentation — happy to iterate on the UX based on review.
For incoming call ringtones, tapping the row opens the system picker directly — there's no bundled call ringtone to surface as a preset.
Custom picks are copied into app-private storage and re-exposed through the app's FileProvider (
DefaultNotificationSoundCopier), so a chosen sound can't disappear when the source app is uninstalled or the URI grant lapses.Users on prior builds who never picked a sound keep hearing the bundled tone — a one-shot migration on first launch flips their stored state to Element default and bumps the channel version. Gated on
version == 0, so it runs at most once.Channel recreation
Android forbids mutating an existing channel's sound, so each pick increments a per-slot version counter and creates a new channel id (
{base}_v{n}); the previous versioned channel is deleted. Persistence and the version bump happen in a single transaction.Display name
The row shows the friendly tone name, falling back through
Ringtone.getTitle()→OpenableColumns.DISPLAY_NAMEof the source URI → localised "Custom". The live resolver skips our own FileProvider URIs, wheregetTitle()would otherwise surface our internal filename (call_sound.ogg).Failure path
If the copy fails, an inline dismissible error appears below the row and the persisted state is left untouched.
Motivation and context
Closes #6672.
Today, changing the notification sound requires leaving Element X to dig through Android's per-channel system settings, which most users never discover. Bringing the picker in-app makes the setting discoverable, gives messages and calls independent controls, and lets users return to the bundled Element default sound at any time.
Screenshots / GIFs
**Subject to design review






Tests
message.mp3.element_fade.mp3.message.mp3.Tested devices
Checklist