简体   繁体   中英

Supply default path for ACTION_OPEN_DOCUMENT

In my app I want to give users a way to pick a file from the app's data directory. This is my code:

// use ACTION_OPEN_DOCUMENT because ACTION_GET_CONTENT will give us
// gallery and other stuff we don’t need
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
Uri uri = Uri.parse(getExternalFilesDir(null).getAbsolutePath());
Log.d(TAG, "Browsing " + uri.toString());
intent.setDataAndType(uri, "*/*");
// show the entire internal storage tree
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
startActivityForResult(intent, 42);

The logcat shows me that the URI that I am setting is file:///sdcard/Android/data/my.app/files , but the file picker UI defaults to the shared storage root ( /sdcard ).

The following code works (requires API 26+ as per the documentation, the intent is available from the API as DocumentsContract.EXTRA_INITIAL_URI ):

// works only with this intent, at the expense of gallery etc. appearing
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);

// apparently we need a valid content URI
Uri uri = Uri.parse("content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fdata%2Fmy.app%2Ffiles");

intent.putExtra("android.provider.extra.INITIAL_URI", uri);

Log.d(TAG, "Browsing " + uri.toString());
intent.setType("*/*");
// show the entire internal storage tree
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
startActivityForResult(intent, 42);
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri);

However, ACTION_GET_CONTENT causes all kinds of providers to appear, such as Gallery and Music, when all I need is the local file system (and in fact just the app's private subtree). If I change the intent to ACTION_OPEN_DOCUMENT , the URI I supply is ignored.

How can I get the file picker UI to start in a directory of my choice, with only a minimal choice of content providers?

Edit: Testing this on Anbox, which I just realize is only at API 25—in fact, I need a way that works on APIs as low as 24.

There may not be a universally viable solution, but the following has worked on some builds (though not on others):

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);

Uri uri = Uri.parse("content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fdata%2Fmy.app%2Ffiles");

intent.setData(uri);
intent.setType("*/*");
intent.putExtra("android.provider.extra.INITIAL_URI", uri);
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);

startActivityForResult(intent, 42);

uri must be a content URI for the com.android.externalstorage.documents provider. The URI path is /document/primary%3A , followed by the filesystem path to the folder to start in. The path must be relative to the shared storage root (ie drop the leading /sdcard/ or equivalent on the device and make sure the result does not start with a slash) and escaped.

The call to Intent#setData() does not help in setting the default location (unlike with some third-party file managers) but prevents unwanted storage providers (such as Gallery and Music) from being displayed.

The android.provider.extra.INITIAL_URI extra sets the initial URI, but this may not work prior to API 26 (although it does work on some flavors of Android).

The android.content.extra.SHOW_ADVANCED extra causes device storage to be available as a provider (otherwise, depending on the flavor of Android, it may require the user to select it or not be available at all).

Again, still not a perfect solution but the closest I managed to get.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM