3

I'm currently attempting to make a media player for audio. I'm currently running lollipop. I ran into a problem setting the dataSource for the media player. First, here's how I set the dataSource:

public void playSong() {
    player.reset();
    Song selSong = songs.get(songPos);
    long currSong = selSong.getId();
    //Get Uri of song
    Uri trackUri = ContentUris.withAppendedId(
            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, currSong);
    try {
        //Tell the player the song to play
        player.setDataSource(getApplicationContext(), trackUri);
    } catch (Exception e) {
        Log.e("MUSIC SERVICE", "Error setting data source", e);
        Log.d("URI Path", trackUri.toString());
    }

    //Will prepare the song and call onPrepare of player
    player.prepareAsync();
}

And the Uri comes out to this:

content://media/external/audio/media/22

I did some research, and from my understanding, after Android 4.1, you can no longer use a URI for the dataSource. When I run this app with the code above, I'll get this error:

E/MediaPlayer﹕ Unable to create media player
E/MUSIC SERVICE﹕ Error setting data source
    java.io.IOException: setDataSource failed.: status=0x80000000
            at android.media.MediaPlayer.nativeSetDataSource(Native Method)

So now I need to convert the URI to a file path and provide that as the dataSource. And, after more research, it appears kitkat changed the way URI's are provided so its difficult to get the file path from the URI. However, I'm not sure if this change persisted into Android Lollipop 5.0.2.

Essentially, I have the URI of a song, but I need to provide something other than a URI to a dataSource. Is there any way that I can convert the URI on Lollipop, and, if not, how can I provide the dataSource only knowing the song's id? Thanks.

josephoneill
  • 853
  • 2
  • 10
  • 33
  • 1
    "I did some research, and from my understanding, after Android 4.1, you can no longer use a URI for the dataSource" -- citation, please. If anything, the reverse is true -- you cannot assume that a file path will work. "When I run this app with the code above, I'll get this error" -- if you are testing on Android 4.4+, add the `READ_EXTERNAL_STORAGE` permission. "it appears kitkat changed the way URI's are provided so its difficult to get the file path from the URI" -- there is no requirement that a `Uri` be a file, let alone one that you can access. – CommonsWare Jan 10 '15 at 17:54
  • Sure, I'll see if I can find where I found that statement. I just assumed it to be correct since my URI wasn't working. I do have the READ_EXTERNAL_STORAGE permission, as well as INTERNET. – josephoneill Jan 10 '15 at 17:58
  • Here's where I read that URI's won't work. I may have misunderstood. He says that on Android 4.1.1, you need to use a local path if you use the constructor that passes in a String. http://stackoverflow.com/questions/16395559/mediaplayer-setdatasource-failed-with-status-0x80000000-for-ringtone-set-by-file/22152628#22152628 – josephoneill Jan 10 '15 at 18:02
  • That is only relevant for the `setDataSource()` that takes a `String`. That, as documented, takes "the path of the file, or the http/rtsp URL of the stream you want to play" as a parameter. Versions of `setDataSource()` that take a `Uri` as a value should work fine with `content://` `Uri` values. Whether or not your particular `Uri` is valid, given that you are assembling it yourself from pieces, is another matter. – CommonsWare Jan 10 '15 at 18:07
  • Sorry, I didn't notice the constructor only took a String. Then I suppose that my uri is incorrect, and therefore the dataSource cannot be set. Thanks you. – josephoneill Jan 10 '15 at 18:10
  • Yeah, I'm an idiot. I was putting the album id in for the the song id. That fixes it. Again, thanks. – josephoneill Jan 10 '15 at 18:16

1 Answers1

0

Lollipop decided to take another course with getting files from the system. (Some say it is from KitKat, but I haven't encountered it yet on KitKat). The code below is to get the filepath on lollipop

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && isMediaDocument(uri))
    {
        final String docId = DocumentsContract.getDocumentId(uri);
        final String[] split = docId.split(":");
        final String type = split[0];

        Uri contentUri = null;
        if ("audio".equals(type)) 
        {
            contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        }

        final String selection = "_id=?";
        final String[] selectionArgs = new String[] {
                split[1]
        };

        String filePath = getDataColumn(context, contentUri, selection, selectionArgs);
    }

isMediaDocument:

public static boolean isMediaDocument(Uri uri)
{
    return "com.android.providers.media.documents".equals(uri.getAuthority());
}

getDataColumn:

private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs)
{
    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = {
            column
    };

    try {
        cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
        if (cursor != null && cursor.moveToFirst())
        {
            final int column_index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(column_index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}

If you still have problems, this is the full answer that checks for images, audio, video, files, etc.

Community
  • 1
  • 1
Kevin van Mierlo
  • 9,554
  • 5
  • 44
  • 76
  • This solution doesn't seem to be working for me. Drive files seem to have a null _data column. https://code.google.com/p/android/issues/detail?id=63651 – android_student Mar 31 '15 at 21:30
  • @hhoang Hmm that's strange, if I have any time this week I'll look for an answer – Kevin van Mierlo Apr 01 '15 at 07:50
  • Just to clarify, it seems like _data will be null when the content uri being sent isn't actually stored on the device. For instance, if I attempt to share a photo from the google photos app which is stored on the cloud. The _data column will be null. The only work around i've found is to copy the file and then pass the new file's absolute path back to my activity. However, I'm not sure if that's the best method available. I wrote a SO question here, if you could take a look. http://stackoverflow.com/questions/29378651/get-filepath-from-google-drive-in-lollipop-mediastore-mediacolumns-data-null – android_student Apr 02 '15 at 02:49
  • @hhoang I'll look into it when I have the time, I mostly post answers during work because I just needed it. I'm very busy right now. But one tip: perhaps you can check if the uri you're getting back starts with http or something and then make a download request to download it. – Kevin van Mierlo Apr 03 '15 at 07:12