2

I'm using this FileUtils class to handle the Uri:

public class FileUtils {
private FileUtils() {
}

private static final String TAG = "FileUtils";
private static final boolean DEBUG = false;

private static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

private static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

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


private static boolean isGooglePhotosUri(Uri uri) {
    return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}

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()) {
            if (DEBUG)
                DatabaseUtils.dumpCursor(cursor);

            final int column_index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(column_index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static String getPath(final Context context, final Uri uri) {

    if (DEBUG)
        Log.d(TAG + " File -",
                "Authority: " + uri.getAuthority() +
                        ", Fragment: " + uri.getFragment() +
                        ", Port: " + uri.getPort() +
                        ", Query: " + uri.getQuery() +
                        ", Scheme: " + uri.getScheme() +
                        ", Host: " + uri.getHost() +
                        ", Segments: " + uri.getPathSegments().toString()
        );

    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/" + split[1];
            }

        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

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

            return getDataColumn(context, contentUri, selection, selectionArgs);
        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

I call it in my MainActivity, inside onActivityResult, after selecting a video from the device:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == SELECT_VIDEO_REQUEST && resultCode == RESULT_OK) {

        if (Build.VERSION.SDK_INT >= 19) {
            
            //Calling FileUtils class
            String sourcePath = FileUtils.getPath(getApplicationContext(), data.getData());
          
            Intent intent = new Intent();
            intent.setClass(MainActivity.this, PlayerActivity.class);
            intent.putExtra("videoUri", sourcePath);
            startActivity(intent);

        } else {
            //Not relevant to the question
            .......

        }
    }
    if (requestCode == SELECT_VIDEO_REQUEST && resultCode != RESULT_OK) {
        Toast.makeText(getApplicationContext(), "Failed to select video", Toast.LENGTH_LONG).show();
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }


}

In my PlayerActivity I get the String and try to parse the Uri, like this:

//Getting String from Intent
mStringFilePath = getIntent().getStringExtra("videoUri");
//Parse Uri from String
mVideoUri = Uri.parse(mStringFilePath);

When testing on my device, I have no issues. I get the correct path and the Uri.parse works fine. But I see a lot of crashes on my Crashlytics saying - nullpointerexception at line 59, referring to mVideoUri = Uri.parse(mStringFilePath);

What am I doing wrong? Why is it working on my device and returning null on some other devices?

Edit 1

I forgot to add <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> in my Manifest. I'm at a point where I atleast don't get a nullpointerexception, but I get the following -

java.io.FileNotFoundException: Not found (No such file or directory)

Somehow the Uri is incorrect when I select a file from my SD Card, any reason why?

halfer
  • 19,824
  • 17
  • 99
  • 186
ClassA
  • 2,480
  • 1
  • 26
  • 57

1 Answers1

0

The Crash is probably occurred due to null String on mStringFilePath = getIntent().getStringExtra("videoUri"); since getIntent().getStringExtras() won't throw nullpointerexception if there isn't a value for the requested Extras but also won't give a data to the assigned variable the final result of the mStringFilePath will be null to fix this issue make sure that the given String for the uri parse is wrapped in:

if(mStringFilePath != null){ mVideoUri = Uri.parse(mStringFilePath); }

  • Yes I know, but I would like to know why it is null. On my device I get the path and the uri as expected. Checking if it is null will not resolve the issue. – ClassA Oct 16 '18 at 09:55
  • Oh the Only Current Problem I can see in this code is that there isn't handling for a non-primary volume on the device, if u have access to SD Card please try sending a file that is attached to the SD Card and see the result – David Marinov Oct 16 '18 at 10:04
  • if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; // Here is what i mean by handling only primary volumes if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } } – David Marinov Oct 16 '18 at 10:06
  • Ok I will try it and let you know. I think you are right, I don't have a SD Card in my device, that is why I don't experience the issue. – ClassA Oct 16 '18 at 10:08
  • You can check this https://stackoverflow.com/questions/42110882/get-real-path-from-uri-of-file-in-sdcard-marshmallow for Information of Implementing SD Card Storage for getting path from uri – David Marinov Oct 16 '18 at 10:20