External Storage Permission Issue with MediaProvider / Ring Tones

|▌冷眼眸甩不掉的悲伤 提交于 2019-11-30 22:09:35

Just had the same problem and came up with the following solution:

private Cursor createCursor()
{
    Uri uri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;

    String[] columns = new String[]
    {
        MediaStore.Audio.Media._ID,
        MediaStore.Audio.Media.TITLE,
        MediaStore.Audio.Media.TITLE_KEY
    };

    String filter = createBooleanFilter(MediaStore.Audio.AudioColumns.IS_ALARM);
    String order = MediaStore.Audio.Media.DEFAULT_SORT_ORDER;

    return getContext().getContentResolver().query(uri, columns, filter, null, order);
}

private String createBooleanFilter(String... columns)
{
    if(columns.length > 0)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("(");
        for(int i = columns.length - 1; i > 0; i--)
        {
            sb.append(columns[i]).append("=1 or ");
        }
        sb.append(columns[0]);
        sb.append(")");
        return sb.toString();
    }
    return null;
}

To get the Uri of a ringtone you need to combine the INTERNAL_CONTENT_URI with the _ID column value, you can do this by using ContentUris class:

Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, cursor.getLong(0));

You can find the external storage directory without needing WRITE_EXTERNAL_STORAGE or READ_EXTERNAL_STORAGE by using Environment.getExternalStorageDirectory().

You can then compare the paths for the URIs provided by RingtoneManager to this path to see if they are on the external storage or not and if so add those items to a List.

Then, rather than passing the raw Cursor to the UI you can use that List with a ListAdapter instead.

For example (untested, you may need to change the method of comparing paths):

class RingtoneDetails
{
    public String ID;
    public String Title;
    public Uri Uri;

    public RingtoneDetails(String id, String title, Uri uri)
    {
        ID = id;
        Title = title;
        Uri = uri;
    }
}

private List<RingtoneDetails> getNonExternalRingtones(RingtoneManager manager)
{
    List<RingtoneDetails> ringtones = new List<RingtoneDetails>();
    Cursor cursor = manager.getCursor();
    String extDir = Environment.getExternalStorageDirectory().getAbsolutePath();

    while (cursor.moveToNext()) 
    {
        String id = cursor.getString(cursor.getColumnIndex(RingtoneManager.ID_COLUMN_INDEX));
        String title = cursor.getString(cursor.getColumnIndex(RingtoneManager.TITLE_COLUMN_INDEX));
        Uri uri= cursor.getString(cursor.getColumnIndex(RingtoneManager.URI_COLUMN_INDEX));

        if(!uri.getPath().contains(extDir))
        {
            ringtones.add(new Ringtone(id, title, uri));
        }
    }

    return ringtones;
}

Previously, I was using RingtoneManager to get a list and display that in a dialog for a user to select. It was throwing the SecurityException on ringtoneManager.getCursor();

I did not want to add the external storage permission, so I switched to doing:

final Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, "Select Ringtone");
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,RingtoneManager.TYPE_ALL);
startActivityForResult( intent, RINGTONE_RESULT);

And then in onActivityResult

if (requestCode == RINGTONE_RESULT&&resultCode == RESULT_OK&&data!=null) {                                                                             
    try {                                                                                                              
        Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);                                  
        if (uri==null){                                                                                                
            setSilent(); //UI stuff in this method                                                                                              
        } else {                                                                            
            Ringtone ringtone = RingtoneManager.getRingtone(context, uri);                                             
            String name = ringtone.getTitle(context);                                                                  
            changeTone.setText(name); //changeTone is a button                                                                  
        }                                                                                                              
    } catch (SecurityException e){                                                                                     
        setSilent();                                                                                                   
        Toast.makeText(context, "Error. Tone on user storage. Select a different ringtone.", Toast.LENGTH_LONG).show();
    } catch (Exception e){                                                                                             
        setSilent();                                                                                                   
        Toast.makeText(context, "Unknown error. Select a different ringtone.", Toast.LENGTH_SHORT).show();             
    }                                                                                                                  
} else {                                                                                                               
    Toast.makeText(context, "Ringtone not selected. Tone set to silent.", Toast.LENGTH_SHORT).show();                  
        setSilent();                                                                                                    
}  
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!