Android Code Snippet Allow user to select custom notification sound and have complete control over playing it

JohnC

Well-Known Member
Licensed User
You can use the Ringtone Manager to allow the user to select a notification sound in your app. But the typical way of using the RTM to play it has many restrictions...

1) After the user selects the sound, you can only "Play" and "Stop" using RTM.
2) You can not easily control the volume without temporarily messing with the devices global volume setting.
3) You can not easily play the file in a loop because you can not determine how long the file is or when it is done playing to know when to start a new play action.

The below method allows you to use the MediaPlayer, so you can do all of the above things plus other methods like pause, etc that the mediaplayer provides.

The URI that is typically returned by the Ringtonemanager is:

content://media/external/audio/media/32599

Which doesn't provide the "Sound Name" that the user chose or an filename/extension to know the audio format of the file.

UPDATE: Luckily another member (Wes58) posted some cool Inline Java to get the sound name from the URI, which I updated the below code to use. Which will come in handy to display it's name in the settings of your app to the user.


Even without a full filename, it appears the MediaPlayer will figure out how to play an audio file - probably by reading the file's header.

Hope this codes helps you :)

B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.

    Dim AlarmURI As String

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.

    Dim Rm As RingtoneManager
    Dim MP As MediaPlayer

End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    'Activity.LoadLayout("Layout1")

    MP.Initialize2("mp")

End Sub

Sub cmdReminderPick_Click
    'allow user to select from among alarm and nototification type sounds
    '(and if AlarmURI has a value, preselect the same sound in the list)
    Rm.ShowRingtonePicker("rm", Bit.Or(Rm.TYPE_ALARM,Rm.TYPE_NOTIFICATION), True, AlarmURI)
End Sub

Sub rm_PickerResult (Success As Boolean, Uri As String)

    If Success Then
        Log(Uri)
        AlarmURI = Uri    'save for when calling showringtonepicker again so will pre-select same sound in the list

        If Uri = "" Then
            ToastMessageShow("Silent was chosen", True)

        Else

            'File.Copy("ContentDir",Uri,File.DirInternal,"sound.m4a")   'not needed
            'MP.Load(File.DirInternal,"sound.m4a")    'not needed

             Log("SoundName = " & GetURISoundName(URI))    'allows you to display/store name of sound to user (thanks to Wes58)

             MP.Load("ContentDir", URI)        'load it into media player

             Log("Length(msec):" & MP.Duration)

             MP.Play                    'play it
             Sleep(3000)                'but only for a preview amount of time
             MP.Stop
        End If

    Else
        'this branch will also happen if the user selects the same alarmuri as the previous
        'nothing needs to be done because AlarmURI already contains the alarm the user wants
        ToastMessageShow("Error loading ringtone.", True)
    End If

End Sub

Sub mp_Complete
    Log("done playing sound")
End Sub

Sub GetURISoundName (SoundURI As String) As String
    'remember to include the below JAVA code from post #2
    Private Jo As JavaObject
    Jo.InitializeContext
    Return Jo.RunMethod("getRingtoneTitle", Array As String(SoundURI))
End Sub
 
Last edited:

wes58

Active Member
Licensed User
The below code will prepare the selected sound so it can be used with the MediaPlayer, so you can do all of the above things plus other methods like pause, etc that the mediaplayer provides.

You will notice that I am hardcoding the sound filename as "sound.m4a". I do this because the URI that is typically returned by the Ringtonemanager is:

content://media/external/audio/media/32599

Which doesn't provide the "Sound Name" that the user chose or an extension to know the audio format of the file.

But luckily, it appears the MediaPlayer will figure out how to play any file even if the extension is wrong - probably by reading the file's header. So even if I name the file with a M4A file extension but the file is actually an OGG format, media player will still play it without a problem.

Hope this codes helps you :)
Here are two function that you can call using Java Object which allow you to:
1. Get the Sound Title if you know sound URI
2. Get the Sound URI if you know the Sound Title

B4X:
#if JAVA
import android.content.Context;
import android.database.Cursor;
import android.media.RingtoneManager;
import android.net.Uri;
import android.provider.MediaStore;
import android.media.Ringtone;

    public static String getRingtoneTitle(Object sound){
        Context context = BA.applicationContext;
        RingtoneManager rm = new RingtoneManager(context);
        Uri ringtoneUri = null;
        Ringtone ringtone;
        if(sound instanceof String){
            ringtone = rm.getRingtone(context, ringtoneUri.parse((String) sound));    //Returns a Ringtone For a given sound URI.
        }
        else if(sound instanceof Uri){
            ringtoneUri = (Uri) sound;
             ringtone = rm.getRingtone(context, ringtoneUri);    //Returns a Ringtone For a given sound URI.
       }
        else{
            return "";
        }
        String     title = ringtone.getTitle(context);
//     BA.Log("title " + title);
       return title;
    } 
    
    public static String getUriString(Uri ringtoneUri){
        return ringtoneUri.toString();
    }

    public static String getSoundUriStr(String ringtoneTitle) {
        Uri foundUri;
        Context context = BA.applicationContext;
        RingtoneManager rm = new RingtoneManager(context); 
        rm.setType(RingtoneManager.TYPE_NOTIFICATION);        //notification sounds
    //    rm.setType(RingtoneManager.TYPE_RINGTONE);            //ringtone sounds
        Cursor cursor = rm.getCursor();
        cursor.moveToFirst();

        while (cursor.moveToNext()) {
              if(ringtoneTitle.compareToIgnoreCase(cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX)) == 0) {
                int ringtoneID = cursor.getInt(RingtoneManager.ID_COLUMN_INDEX);
                foundUri = rm.getRingtoneUri(cursor.getPosition());    
    //            String name = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX);
    //            BA.Log(name);
    //            BA.Log(foundUri.toString());
                return foundUri.toString();
            }    
        }
        return "";
    }
#End If
 

JohnC

Well-Known Member
Licensed User
I just updated the OP:

1) Allow the MediaPlayer to use the URI returned by the RTM directly (no file copy required)
2) Included the use of the JAVA code posted by Wes58 you can obtain the "Name" of the sound selected by the user
 
Last edited:
Top