Я использовал следующий код для сохранения необработанного звукового файла на внешнее хранилище и установки его в качестве мелодии звонка. Но, похоже, что в Android 10 все меняется. Можете ли вы мне помочь или показать некоторые рекомендации, как обновить мой код для работы с Android 10?
Вот код для сохранения файла на внешнем хранилище:
Сохранение файла:
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;
}
Установка в качестве мелодии звонка:
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);
}
Вот 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());
}
}
Исключение до Android 10: (@ 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