I used to use following code to save a raw sound file to external storage and set it as ringtone. But things seems to change in android 10. Can you help me or show me some guideline how to update my code to work with android 10?
Here is the code to save file to external storage:
Saving file:
String path = Environment.getExternalStorageDirectory() + "/customsounds";
public File getFile() {
boolean exists = (new File(path).exists());
if (!exists) {
new File(path).mkdirs();
}
File newSoundFile = new File(path, sound.getFileName() + ".mp3");
Uri mUri = Uri.parse("android.resource://com.example.customsounds/" + sound.getId());
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(newSoundFile);
int i = fis.read(readData);
while (i != -1) {
fos.write(readData, 0, i);
i = fis.read(readData);
}
fos.close();
} catch (IOException io) {
Log.e(TAG, "io exception");
return null;
}
return newSoundFile;
}
Setting as ringtone:
public void setAsRingtone() {
File newSoundFile = getFile();
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DATA, newSoundFile.getAbsolutePath());
values.put(MediaStore.MediaColumns.TITLE, sound.getTitle());
values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mp3");
values.put(MediaStore.MediaColumns.SIZE, newSoundFile.length());
values.put(MediaStore.Audio.Media.ARTIST, R.string.app_name);
values.put(MediaStore.Audio.Media.IS_RINGTONE, true);
values.put(MediaStore.Audio.Media.IS_NOTIFICATION, true);
values.put(MediaStore.Audio.Media.IS_ALARM, true);
values.put(MediaStore.Audio.Media.IS_MUSIC, false);
Uri uri = MediaStore.Audio.Media.getContentUriForPath(newSoundFile.getAbsolutePath());
getContentResolver().delete(uri,
MediaStore.MediaColumns.DATA + "=?", new String[]{newSoundFile.getAbsolutePath()});
Uri newUri = getContentResolver().insert(uri, values);
RingtoneUtils.setRingtone(this, newUri, type);
}
Here is the RingtoneUtils:
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
public class RingtoneUtils {
private static final String LOG_TAG = "RingtoneUtils";
public static boolean setRingtone(@NonNull Context context, @NonNull Uri ringtoneUri, int type) {
Log.v(LOG_TAG, "Setting Ringtone to: " + ringtoneUri);
if (!hasMarshmallow()) {
Log.v(LOG_TAG, "On a Lollipop or below device, so go ahead and change device ringtone");
setActualRingtone(context, ringtoneUri, type);
return true;
} else if (hasMarshmallow() && canEditSystemSettings(context)) {
Log.v(LOG_TAG, "On a marshmallow or above device but app has the permission to edit system settings");
setActualRingtone(context, ringtoneUri, type);
return true;
} else if (hasMarshmallow() && !canEditSystemSettings(context)) {
Log.d(LOG_TAG, "On android Marshmallow and above but app does not have permission to" +
" edit system settings. Opening the manage write settings activity...");
startManageWriteSettingsActivity(context);
Toast.makeText(context, "Please allow app to edit settings so your ringtone/notification can be updated", Toast.LENGTH_LONG).show();
return false;
}
return false;
}
private static void setActualRingtone(@NonNull Context context, @NonNull Uri ringtoneUri, int type) {
RingtoneManager.setActualDefaultRingtoneUri(context, type, ringtoneUri);
String message="";
if(type == RingtoneManager.TYPE_RINGTONE) {
message = context.getString(R.string.ringtone_set_success);
} else if(type == RingtoneManager.TYPE_NOTIFICATION) {
message = context.getString(R.string.notification_set_success);
}
if ((RingtoneManager.getActualDefaultRingtoneUri(context, type)).equals(ringtoneUri)) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, context.getString(R.string.operation_failed), Toast.LENGTH_SHORT).show();
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
private static void startManageWriteSettingsActivity(@NonNull Context context) {
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
// Passing in the app package here allows the settings app to open the exact app
intent.setData(Uri.parse("package:" + context.getApplicationContext().getPackageName()));
// Optional. If you pass in a service context without setting this flag, you will get an exception
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
private static boolean hasMarshmallow() {
// returns true if the device is Android Marshmallow or above, false otherwise
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
@RequiresApi(api = Build.VERSION_CODES.M)
private static boolean canEditSystemSettings(@NonNull Context context) {
// returns true if the app can edit system settings, false otherwise
return Settings.System.canWrite(context.getApplicationContext());
}
}
Pre-Android 10 exception:(@greywolf82)
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.lastIndexOf(int)' on a null object reference
at android.os.Parcel.readException(Parcel.java:1957)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
at android.content.ContentProviderProxy.insert(ContentProviderNative.java:476)
at android.content.ContentResolver.insert(ContentResolver.java:154
This method works for almost all api's.
private boolean SetAsRingtoneOrNotification(File k, int type) {
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.TITLE, k.getName());
values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mp3");
if (RingtoneManager.TYPE_RINGTONE == type) {
values.put(MediaStore.Audio.Media.IS_RINGTONE, true);
} else if (RingtoneManager.TYPE_NOTIFICATION == type) {
values.put(MediaStore.Audio.Media.IS_NOTIFICATION, true);
}
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Uri newUri = this.getContentResolver()
.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
try (OutputStream os = getContentResolver().openOutputStream(newUri)) {
int size = (int) k.length();
byte[] bytes = new byte[size];
try {
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(k));
buf.read(bytes, 0, bytes.length);
buf.close();
os.write(bytes);
os.close();
os.flush();
} catch (IOException e) {
return false;
}
} catch (Exception ignored) {
return false;
}
RingtoneManager.setActualDefaultRingtoneUri(Emotes.this, type,
newUri);
return true;
} else {
values.put(MediaStore.MediaColumns.DATA, k.getAbsolutePath());
Uri uri = MediaStore.Audio.Media.getContentUriForPath(k
.getAbsolutePath());
getContentResolver().delete(uri, MediaStore.MediaColumns.DATA + "=\"" + k.getAbsolutePath() + "\"", null);
Uri newUri = Emotes.this.getContentResolver().insert(uri, values);
RingtoneManager.setActualDefaultRingtoneUri(Emotes.this, type,
newUri);
this.getContentResolver()
.insert(MediaStore.Audio.Media.getContentUriForPath(k
.getAbsolutePath()), values);
return true;
}
}
/working in android 10 and 11/
private void SetAsRingtoneAndroid(File k) {
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.TITLE, k.getName());
values.put(MediaStore.MediaColumns.MIME_TYPE, getMIMEType(k.getAbsolutePath()));//// getMIMEType(k.getAbsolutePath())
values.put(MediaStore.MediaColumns.SIZE, k.length());
values.put(MediaStore.Audio.Media.ARTIST, R.string.app_name);
values.put(MediaStore.Audio.Media.IS_RINGTONE, true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Uri newUri = getContentResolver()
.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
try (OutputStream os = getContentResolver().openOutputStream(newUri)) {
int size = (int) k.length();
byte[] bytes = new byte[size];
try {
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(k));
buf.read(bytes, 0, bytes.length);
buf.close();
os.write(bytes);
os.close();
os.flush();
} catch (IOException e) {
}
} catch (Exception ignored) {
}
RingtoneManager.setActualDefaultRingtoneUri(getApplicationContext(), RingtoneManager.TYPE_RINGTONE, newUri);
Toast.makeText(this, "Ringtone set", Toast.LENGTH_SHORT).show();
}
}
public static String getMIMEType(String url) {
String mType = null;
String mExtension = MimeTypeMap.getFileExtensionFromUrl(url);
if (mExtension != null) {
mType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(mExtension);
}
return mType;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With