Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android API 29 uses msf: prefix for downloaded files uri id? #2

Closed
tamo opened this issue Sep 14, 2019 · 8 comments
Closed

Android API 29 uses msf: prefix for downloaded files uri id? #2

tamo opened this issue Sep 14, 2019 · 8 comments

Comments

@tamo
Copy link

tamo commented Sep 14, 2019

On an emulated Pixel2 API 29, WhatTheCodec fails to open mp4s in Downloads.
Seems like the uri looks like "content://com.android.providers.downloads.documents/document/msf:24"
and the "msf:" is not needed.
So PathUtil.java needs something like:

38                } else if (id.startsWith("msf:")) {
39                    final String[] split = id.split(":");
40                    uri = ContentUris.withAppendedId(
41                            Uri.parse("content://downloads/public_downloads"), Long.valueOf(split[1]));

The logcat follows:

com.javernaut.whatthecodec E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.javernaut.whatthecodec, PID: 13131
    java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=42, result=-1, data=Intent { dat=content://com.android.providers.downloads.documents/document/msf:24 flg=0x1 }} to activity {com.javernaut.whatthecodec/com.javernaut.whatthecodec.MainActivity}: java.lang.NumberFormatException: For input string: "msf:24"
        at android.app.ActivityThread.deliverResults(ActivityThread.java:4845)
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:4886)
        at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
     Caused by: java.lang.NumberFormatException: For input string: "msf:24"
        at java.lang.Long.parseLong(Long.java:594)
        at java.lang.Long.valueOf(Long.java:808)
        at com.javernaut.whatthecodec.PathUtil.getPath(PathUtil.java:40)
        at com.javernaut.whatthecodec.MainActivity.tryGetVideoConfig(MainActivity.kt:108)
        at com.javernaut.whatthecodec.MainActivity.onActivityResult(MainActivity.kt:39)
        at android.app.Activity.dispatchActivityResult(Activity.java:8110)
        at android.app.ActivityThread.deliverResults(ActivityThread.java:4838)
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:4886) 
        at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016) 
        at android.os.Handler.dispatchMessage(Handler.java:107) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7356) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 
@tamo
Copy link
Author

tamo commented Sep 14, 2019

And I forgot to say a big thanks to your excellent article.

@Javernaut
Copy link
Owner

Hello @tamo ,
Thanks for kind words :)

As for the issue, I managed to reproduce it by downloading a video file via the Chrome app on Android 10 emulator. Unfortunately, just skipping the 'msf:' part doesn't help to make a proper Uri that could lead us to a file path. That is why I decided to just fallback to the File Descriptor in this case. The code change is already merged.

Thanks for pointing this thing out.
Soon I'll make a new release with this change.

@tamo
Copy link
Author

tamo commented Sep 15, 2019

Thanks for the fix.
Yes, I downloaded movie files via Chrome. I should have mentioned it. Sorry.

For now your fix seems the only way to go. Googling MSF tells me only about medical information.

And I'm looking forward to see the new release.

@tamo tamo closed this as completed Sep 15, 2019
@AliElDerawi
Copy link

AliElDerawi commented Feb 16, 2021

I tried this solution and its worked.
It simply copies the file for the application cache directory , then gets the path of the generated file.

 if (id != null && id.startsWith("msf:")) {
                    final File file = new File(context.getCacheDir(), uri.getLastPathSegment());
                    try (final InputStream inputStream = context.getContentResolver().openInputStream(uri);
                         OutputStream output = new FileOutputStream(file)) {
                         // You may need to change buffer size. I use large buffer size to help loading large file , but be ware of 
                        //  OutOfMemory Exception
                        final byte[] buffer = new byte[8 * 1024]; 
                        int read;

                        while ((read = inputStream.read(buffer)) != -1) {
                            output.write(buffer, 0, read);
                        }

                        output.flush();
                        return file.getPath();
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                    return null;
                }

                

P.S. You may need to clear the cache directory after finished

@Javernaut
Copy link
Owner

@AliElDerawi , you are trying to solve just a slightly different problem here. The original issue was about having the prefix in the URI.

After that I acutually found a way of accessing files by a FileDescriptor (even if it isn't perfect).

Regarding copying the file to the cache, I wouldn't do such a thing. Original files can be very big (like movies) and actually I don't want to copy user's data, because it would lead to my Privacy Policy changing.

@SanskarDahiya
Copy link

Hello guys,
I'm getting issue for same in Android 10,
as my file URI consist msf: format.
Most solutions said to copy files into cache path & then use that path.

Thus, here is the solution to get a path, without copying it.

 if (isAndroid10 && id.startsWith("msf:")) {
     final String[] split = id.split(":");
     final String selection = "_id=?";
     final String[] selectionArgs = new String[] { split[1] };
     return getDataColumn(context, MediaStore.Downloads.EXTERNAL_CONTENT_URI, selection, selectionArgs);
}

Try to implement this.

MediaStore.Downloads.EXTERNAL_CONTENT_URI -- ONLY PRESENT FROM ANDROID 10

@HBiSoft
Copy link

HBiSoft commented Nov 3, 2021

@SanskarDahiya
I tried what you said:

String id = DocumentsContract.getDocumentId(uri);
if (id.startsWith("msf")){
    final String[] split = id.split(":");
    final String selection = "_id=?";
    final String[] selectionArgs = new String[] { split[1] };
    Log.e("Returned ", getDataColumn(context, MediaStore.Downloads.EXTERNAL_CONTENT_URI, selection, selectionArgs));
}

The Uri above is content://com.android.providers.downloads.documents/document/msf%3A16803.

Here is my 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 index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        }
    }catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}

The cursor is null so getDataColumn returns null.

Is there something I'm doing wrong?

@SanskarDahiya
Copy link

@SanskarDahiya I tried what you said:

String id = DocumentsContract.getDocumentId(uri);
if (id.startsWith("msf")){
    final String[] split = id.split(":");
    final String selection = "_id=?";
    final String[] selectionArgs = new String[] { split[1] };
    Log.e("Returned ", getDataColumn(context, MediaStore.Downloads.EXTERNAL_CONTENT_URI, selection, selectionArgs));
}

The Uri above is content://com.android.providers.downloads.documents/document/msf%3A16803.

Here is my 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 index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        }
    }catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}

The cursor is null so getDataColumn returns null.

Is there something I'm doing wrong?

Hi @HBiSoft
You are doing everything fine. I'm also implementing the same code as mentioned by you.

Sill I'm facing an issue to get paths from some URI's in specific devices. I don't know what to do now.
Since I'm not an Android developer, So I kinda drop this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants