13

My app allows you to specify a different ringtone for different incoming events. EG: incoming call, incoming SMS, etc.

What I am trying to accomplish is when I receive for example an incoming call, I check my apps database if a specific option is selected and if there is a ringtone option set play that ringtone.

However the problem I am having is I am unable to override / stop the default phone ringtone from playing.

I have tried several different ways, but from the docs most of those methods only stop the current instance and are not global methods.

I can't set the ringtone in the default phones ringtone settings as it needs to be dynamic based on the incoming call.

If anyone knows of a trick or a way to accomplish this that would be great. I hope that makes sense.

David Mulder
  • 26,123
  • 9
  • 51
  • 114
userdelroot
  • 197
  • 1
  • 5

2 Answers2

12

There are a several steps to dynamically change the ringtone.

1. Prepare ringtone

    File k = new File("/sdcard/ringtone", "kolyan_.mp3");
    ContentValues values = new ContentValues();
    values.put(MediaStore.MediaColumns.DATA, k.getAbsolutePath());
    values.put(MediaStore.MediaColumns.TITLE, "My Song title");
    values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mpeg");
    values.put(MediaStore.Audio.Media.ARTIST, "Some Artist");
    values.put(MediaStore.Audio.Media.IS_RINGTONE, true);
    values.put(MediaStore.Audio.Media.IS_NOTIFICATION, false);
    values.put(MediaStore.Audio.Media.IS_ALARM, false);
    values.put(MediaStore.Audio.Media.IS_MUSIC, false);

2. Insert it into the database

    Uri uri = MediaStore.Audio.Media.getContentUriForPath(k.getAbsolutePath());
    // Line below is major because we need to delete old entry
    getContentResolver().delete(uri, MediaStore.MediaColumns.DATA + "=\"" + k.getAbsolutePath() + "\"", null);
    mUri = getContentResolver().insert(uri, values);

3. Save the current default ringtone and subscribe to CallListener

    // Be careful by calling getActualDefaultRingtoneUri in CallListener, it could return null, better way to save it in OnCreate
    mOldUri = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_RINGTONE);

    TelephonyManager mTelephonyMgr = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
    mTelephonyMgr.listen(new MyCallListener(), PhoneStateListener.LISTEN_CALL_STATE);

4. Create MyCallListener class

class MyCallListener extends PhoneStateListener {
    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        switch (state) {
            case TelephonyManager.CALL_STATE_RINGING:
                // On call you replace the ringtone with your own mUri
                RingtoneManager.setActualDefaultRingtoneUri(
                        MainActivity.this,
                        RingtoneManager.TYPE_RINGTONE,
                        mUri
                );
                break;
            case TelephonyManager.CALL_STATE_IDLE:
                // Restore the default ringtone
                RingtoneManager.setActualDefaultRingtoneUri(
                        MainActivity.this,
                        RingtoneManager.TYPE_RINGTONE,
                        mOldUri
                );
                break;
            default:
                break;
        }

        super.onCallStateChanged(state, incomingNumber);
    }
}

5. Add permissions to you AndroidManifest.xml

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>

6. Done

The main idea is hooking for incoming call and replace the ringtone by your own. And of course you should restore default ringtone to saved value after call.

asktomsk
  • 2,289
  • 16
  • 23
  • Somehow for me, even tough the ringtone is effectively replaced it still play the old one... any clue? – BrainCrash Oct 08 '13 at 15:54
  • Not enough information to figure out. Try look at logs. Try to check return value of getActualDefaultRingtoneUri after you set it. – asktomsk Oct 08 '13 at 19:42
  • 1
    I did (getActualDefaultRingtoneUri) and the value is correct and it will be the default if I don't revert it back at CALL_STATE_IDLE. It seems as if the system acquire the Ringtone Uri to play before I change it. – BrainCrash Oct 09 '13 at 11:39
  • I'm still asked to include `WRITE_EXTERNAL_STORAGE` permission – Olayinka May 18 '14 at 02:05
4

Starting with API 5 (Android 2.x), the Contacts database has a CUSTOM_RINGTONE field, see this page:

http://developer.android.com/reference/android/provider/ContactsContract.ContactOptionsColumns.html#CUSTOM_RINGTONE

The value for this field must be a content:// URI to a media file. You can obtain one from the MedciaStore content provider:

http://developer.android.com/reference/android/provider/MediaStore.MediaColumns.html

That may be enough to get you started. This is all standard content provider stuff; lots of existing material for both tasks.

escape-llc
  • 1,295
  • 1
  • 12
  • 25