0

I'm trying to find a way to set a new default ringtone by code from my Android activity.

It works well on Android 13 but on Android 14+ on Android Studio Emulator (for example Nexus 6P API 34) the app crashes. My code is below:

 interface Tone{
        String PHONE_RINGTONE = "ringtone";
        String NOTIFICATION_TONE = "notification";
        String ALARM = "alarm";
    }


 public void setRingtone(String type) {
        File exportedDir = new File(getExternalFilesDir(Environment.DIRECTORY_MUSIC)
                .toString() + "/" + getPackageName()+ "/" + type + "/");
        if (!exportedDir.exists()) {
            exportedDir.mkdirs();
        }

        //delete previous
        File[] files = exportedDir.listFiles();
        if (files != null) {
            for (File file : files)
                if (!file.isDirectory())
                    file.delete();
        }

        File f = new File(exportedDir, System.currentTimeMillis() + ".mp3");

        Uri mUri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.horse);
        ContentResolver mCr = getContentResolver();
        AssetFileDescriptor soundFile;
        try {
            soundFile = mCr.openAssetFileDescriptor(mUri, "r");
        } catch (FileNotFoundException e) {
            soundFile = null;
        }

        try {
            byte[] readData = new byte[1024];
            FileInputStream fis = soundFile.createInputStream();
            FileOutputStream fos = new FileOutputStream(f);
            int i = fis.read(readData);
            while (i != -1) {
                fos.write(readData, 0, i);
                i = fis.read(readData);
            }
            fos.close();
            fos.flush();
        } catch (IOException io) {
            Toast.makeText(this, io.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
        }

        ContentValues values = new ContentValues();
        values.put(MediaStore.MediaColumns.DATA, f.getAbsolutePath());
        values.put(MediaStore.MediaColumns.TITLE, getString(R.string.app_name));
        values.put(MediaStore.MediaColumns.MIME_TYPE, getMIMEType(f.getAbsolutePath()));
        values.put(MediaStore.MediaColumns.SIZE, f.length());
        values.put(MediaStore.Audio.Media.ARTIST, R.string.app_name);
        if (Objects.equals(type, NOTIFICATION_TONE)) {
            values.put(MediaStore.Audio.Media.IS_NOTIFICATION, true);
        } else if (Objects.equals(type, ALARM)) {
            values.put(MediaStore.Audio.Media.IS_ALARM, true);
        } else {
            values.put(MediaStore.Audio.Media.IS_RINGTONE, true);
        }

        Uri newUri;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            newUri = getContentResolver()
                    .insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
            try (OutputStream os = getContentResolver().openOutputStream(newUri)) {
                int size = (int) f.length();
                byte[] bytes = new byte[size];
                try {
                    BufferedInputStream buf = new BufferedInputStream(Files.newInputStream(f.toPath()));
                    buf.read(bytes, 0, bytes.length);
                    buf.close();
                    os.write(bytes);
                    os.close();
                    os.flush();
                } catch (IOException e) {
                }
            } catch (Exception ignored) {
            }
        }


        try {
            if (Objects.equals(type, NOTIFICATION_TONE)) {
                RingtoneManager.setActualDefaultRingtoneUri(this,
                        RingtoneManager.TYPE_NOTIFICATION, newUri);
                Settings.System.putString(mCr, Settings.System.NOTIFICATION_SOUND, newUri.toString());
                showMessage("Done", "The notification tone changed successfully.");
            }
            else if (Objects.equals(type, ALARM)) {
                RingtoneManager.setActualDefaultRingtoneUri(this,
                        RingtoneManager.TYPE_ALARM, newUri);
                Settings.System.putString(mCr, Settings.System.ALARM_ALERT, newUri.toString());
                showMessage("Done", "Alarm sound changed successfully.");
            } else {
                RingtoneManager.setActualDefaultRingtoneUri(this,
                        RingtoneManager.TYPE_RINGTONE, newUri);
                Settings.System.putString(mCr, Settings.System.RINGTONE, newUri.toString());
                showMessage("Done", "The ringtone changed successfully.");
            }
        } catch (Throwable t) {
            Toast.makeText(this, t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
        }
    }

This code doesn't work on Android 14.

My logcat:

2023-08-02 17:06:41.065 6899-6899/myapp.adfg.com E/AndroidRuntime: FATAL EXCEPTION: main
   Process: myapp.adfg.com, PID: 6899
   java.lang.IllegalArgumentException: Mutation of _data is not allowed.
       at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
       at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
       at android.content.ContentProviderProxy.insert(ContentProviderNative.java:589)
       at android.content.ContentResolver.insert(ContentResolver.java:2209)
       at android.content.ContentResolver.insert(ContentResolver.java:2171)
       at myapp.adfg.com.MainActivity.setRingtone(MainActivity.java:224)
       at myapp.adfg.com.MainActivity.lambda$onCreate$3(MainActivity.java:69)
       at myapp.adfg.com.MainActivity.$r8$lambda$w64HvE4bNy_ZGRaJSSBx6mEZm-E(Unknown Source:0)
       at myapp.adfg.com.MainActivity$$ExternalSyntheticLambda5.onClick(Unknown Source:2)
       at android.view.View.performClick(View.java:7659)
       at android.view.View.performClickInternal(View.java:7636)
       at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
       at android.view.View$PerformClick.run(View.java:30156)
       at android.os.Handler.handleCallback(Handler.java:958)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:205)
       at android.os.Looper.loop(Looper.java:294)
       at android.app.ActivityThread.main(ActivityThread.java:8176)
       at java.lang.reflect.Method.invoke(Native Method)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)

The problem is on this line here: newUri = getContentResolver() .insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);

Can someone help me to fix it, please?

I tried to set the sound from the raw folder as a phone ringtone programmatically for the new Android 14+ OS which is incoming this month. I have tested on Android Studio Emulator (for example Nexus 6P API 34). The result is a crash. See the logcat.

0 Answers0