android - Android 10 如何设置文件为铃声?

标签 android ringtone android-10.0 android-storage

我曾经使用以下代码将原始声音文件保存到外部存储并将其设置为铃声。但是在 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

最佳答案

此方法几乎适用于所有 api。

 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;
        }



    }

关于android - Android 10 如何设置文件为铃声?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58255532/

相关文章:

android - 错误 : No resource identifier found for attribute 'layout_alignStart'

Android - 在 ImageView 上创建可点击区域

android - 使用 Android Q 中引入的 Scoped Storage,我应该如何开发文件管理器应用程序?

java - 如何创建一个文件夹并将我的应用程序生成的所有文件存储在 android q 的该文件夹中

java - 播放声音作为媒体/闹钟/铃声?

android - FFmpeg:无法在 Android Q 上使用文件描述符进行搜索

android - React-Native Animated 在低端 Android 手机中落后

android - Javadoc 文本颜色含义

android - 设置联系人自定义铃声,怎么办?

ios - 访问苹果的 iOS 铃声并显示它们