- Respects Scoped Storage(MediaStore is used)
- Available as an Activity and a Dialog
- Options to pick alarm sound, notification sound, ringtone sound, and external ringtones.
- Ringtone preview
- An interface to set a default entry
- An interface to add custom ringtone entries
- Sorted external ringtones with artists, albums and folders
- Automatically remembers which external ringtones users have picked
- Multi-select
- Dark theme support out of box
- Permissions are handled internally
- Storage Access Framework support
The library is inspired by AOSP DeskClock RingtonePickerActivity.
![]() |
![]() |
![]() |
Step 1. Add the JitPack repository to your build file
Add it in your root build.gradle at the end of repositories:
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
Step 2. Add the dependency
dependencies {
implementation 'com.github.DeweyReed:UltimateRingtonePicker:3.2.0'
}
Demo APK and examples in the MainActivity.
Add <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
or <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
when targeting Android
13 to your Manifest if you are not going to use Storage Access Framework.
val settings = UltimateRingtonePicker.Settings(
systemRingtonePicker = UltimateRingtonePicker.SystemRingtonePicker(
customSection = UltimateRingtonePicker.SystemRingtonePicker.CustomSection(),
defaultSection = UltimateRingtonePicker.SystemRingtonePicker.DefaultSection(),
ringtoneTypes = listOf(
RingtoneManager.TYPE_RINGTONE,
RingtoneManager.TYPE_NOTIFICATION,
RingtoneManager.TYPE_ALARM
)
),
deviceRingtonePicker = UltimateRingtonePicker.DeviceRingtonePicker(
deviceRingtoneTypes = listOf(
UltimateRingtonePicker.RingtoneCategoryType.All,
UltimateRingtonePicker.RingtoneCategoryType.Artist,
UltimateRingtonePicker.RingtoneCategoryType.Album,
UltimateRingtonePicker.RingtoneCategoryType.Folder
)
)
)
-
Launch the Activity picker
-
Add the Activity to the manifest.
<activity android:name="xyz.aprildown.ultimateringtonepicker.RingtonePickerActivity" />
-
Register Activity Result callback
rivate val ringtoneLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == Activity.RESULT_OK && it.data != null) { val ringtones = RingtonePickerActivity.getPickerResult(data) } }
-
Start Activity
ringtoneLauncher.launch( RingtonePickerActivity.getIntent( context = this, settings = settings, windowTitle = "Activity Picker" ) )
-
-
Launch the dialog picker
-
Show the dialog
RingtonePickerDialog.createInstance( settings = settings, dialogTitle = "Dialog!" ).show(supportFragmentManager, null)
-
Get the result
Implement
UltimateRingtonePicker.RingtonePickerListener
in your activity or fragment.override fun onRingtonePicked(ringtones: List<UltimateRingtonePicker.RingtoneEntry>) { }
Alternatively, you can launch the dialog and get the result without implementing the interface, but the dialog will be dismissed in
onPause
:RingtonePickerDialog.createEphemeralInstance( settings = settings, dialogTitle = "Dialog", listener = object : UltimateRingtonePicker.RingtonePickerListener { override fun onRingtonePicked(ringtones: List<UltimateRingtonePicker.RingtoneEntry>) { } } ).show(supportFragmentManager, null)
-
UltimateRingtonePicker
supports activity pick RingtonePickerActivity
and dialog pick RingtonePickerDialog
out of the box. Both of them are just wrappers of RingtonePickerFragment
. Therefore, you can directly wrap RingtonePickerFragment
into your activity/fragment to provide more customization!