Android API 29 uses msf: prefix for downloaded files uri id?
tamo opened this issue · 8 comments
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)
And I forgot to say a big thanks to your excellent article.
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.
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.
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
@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.
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
@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 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 iscontent://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
isnull
sogetDataColumn
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.