12

I need to open folder in internal storage that contains images.

I use following code.

Java

 File folder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),  "MyPhotos");
Intent intent = new Intent(Intent.ACTION_VIEW);
String path =folder.getPath();
Uri myImagesdir = Uri.parse("file://" + path );
intent.setDataAndType(myImagesdir,"*/*");   
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);

PATHS

 <paths xmlns:android="http://schemas.android.com/apk/res/android">
     <external-path name="images" path="Pictures" />
     <external-path name="external_files" path="."/>
     <external-path name="files_root" path="Android/data/${applicationId}"/> </paths>

Manifest

  <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.android.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

xml/file_paths

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="images" path="Pictures" />
    <external-path name="external_files" path="."/>
    <external-path name="files_root" path="Android/data/${applicationId}"/>
</paths>

ERROR

FATAL EXCEPTION: main Process: android.apps.bnb.company.myphotos, PID: 22482 android.os.FileUriExposedException: file:///storage/emulated/0/Pictures/MyPhotos exposed beyond app through Intent.getData()

Is any another way to open folder in internal storage? Thanks!

UPDATE #1

Using this arcticle https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en I replaced

Uri myImagesdir = Uri.parse("file://" + path );

with

 Uri myImagesdir = Uri.parse("content://" + path );

And the error gone.

Anyway I have to choose always app to open this folder.

Is it possibility to use My Files app by default to open certain folder?

NoWar
  • 36,338
  • 80
  • 323
  • 498
  • 1
    refer to this post https://stackoverflow.com/a/38858040/6559031 – Devil10 Apr 28 '18 at 03:50
  • 1
    Uri access should be changed. Check out [this](https://stackoverflow.com/questions/38200282) answer. – vyi Apr 28 '18 at 03:51
  • @vvy Hi! Is it `intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);` line? – NoWar Apr 28 '18 at 04:26
  • @SahilKumar Hi! Exactly what i did before post my question. And I took from there this line `intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);` – NoWar Apr 28 '18 at 04:28
  • `Is it possibility to use My Files app by default to open certain folder?` What do you want to achieve? can you elaborate? – Sagar Apr 30 '18 at 14:11
  • @Sagar Sure. Each Android smartphone has kind of File Manager. So I want programmatically open certain folder by providing path to the folder. I need to do it in Android. Well... In MS Windows it takes 1 line of the code only... – NoWar May 01 '18 at 04:29
  • "Each Android smartphone has kind of File Manager" -- no, it does not. "So I want programmatically open certain folder by providing path to the folder" -- there is no standard `Intent` structure for this, sorry. – CommonsWare May 01 '18 at 10:48

3 Answers3

10

Is it possibility to use My Files app by default to open certain folder?

Yes & No. Its not 100% guaranteed that it will work on all devices.

Edit 1:

Following is one of the way with which it can be done. I have tested on few emulators (running Android N & Android O) and loads default file explorer:

MainActivity.java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri dirUri = FileProvider.getUriForFile(this,getApplicationContext().getPackageName() + ".com.example.myapplication",Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        //intent.setDataAndtType(); I will change this value in the alternatives below
    }

AndroidManifest.xml

<provider
        android:name=".GenericFileProvider"
        android:authorities="${applicationId}.com.example.myapplication"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
</provider>

GenericFileProvider.java

public class GenericFileProvider extends FileProvider {
}

file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

The above approach doesn't work on big players like samsung

Alternatives

1. Using type DocumentsContract.Document.MIME_TYPE_DIR

intent.setDataAndType(dirUri,DocumentsContract.Document.MIME_TYPE_DIR);

This approach works on several emulators and limited set of devices. It doesn't work with big players like Samsung or Huawei.

2. Using type resource/folder

intent.setDataAndType(dirUri,"resource/folder");

This approach works only if user has installed ES file explorer app.

If you choose to use , then you have to check if any intent is available to handle it by using:

PackageManager packageManager = getActivity().getPackageManager();

if (intent.resolveActivity(packageManager) != null) {
    startActivity(intent);
} else {
   // either display error or take necessary action
}

3. Using type */*

intent.setDataAndType(dirUri,"*/*");

This approach works, if user chooses File Manager app from the Intent Chooser and mark it as default app to handle */*. However it has some drawbacks (Thanks to @CommonsWare for bringing some of it out):

  • This type will load all the apps on the device and allow user to choose one of them to complete the action.
  • If there is no file explorer and user chooses other apps to load your intent, then the other app will crash or simply show black screen. E.g. You use Gallery or some other app to launch it rather than file explorer, then Gallery app will either crash or show black screen.
  • Even if there is file explorer but user decides to use other apps, the other apps could crash

4. Using type text/csv

intent.setDataAndType(uri, "text/csv")

This will limit the number of apps which will displayed to the user but same limitations when */* used is applied. Apps which can handle csv will be displayed and if user chooses it then the apps would crash.

There are some device specific implementations available here as mentioned by @Academy of Programmer which requires to identify the default file manager's intent and extra's need by it.

Conclusion:

There is no standard type available to achieve it since there is no standard followed by the File Managers to support specific type at the moment. In future may be Google will come up with some approach. Best alternative would be to implement your own file manager just like Dropbox or Google Drive does. There are several libraries available which provide this feature.

Sagar
  • 23,903
  • 4
  • 62
  • 62
  • There is no requirement for an Android device to have a "default file explorer app", let alone one that can handle a `content` `Uri` that is backed by a filesystem directory. – CommonsWare May 01 '18 at 10:49
  • @CommonsWare Well if no default file explorer is available then it will result in prompting the user to choose from existing apps which could handle it – Sagar May 01 '18 at 10:50
  • And many of those apps will crash, because they will be expecting the `content` `Uri` to point to a stream. Yours points to nothing. – CommonsWare May 01 '18 at 10:51
  • For example, I put your code into a scrap project. I added the dependency and the `` element not mentioned in your answer. I ran it on a Nexus 5X. Your `startActivity()` call offers ~15 apps, and all but one that I tried either crashed or had an internal error. That's because your `Intent` will pull in PDF viewers, image viewers, and a variety of other apps. – CommonsWare May 01 '18 at 10:59
  • @CommonsWare You are right, I tried to use other apps to open this and some of them crash – Sagar May 01 '18 at 11:12
  • @CommonsWare I have updated my answer. – Sagar May 01 '18 at 11:46
  • And that answer is not dramatically better. There is no concept of a directory with respect to a `ContentProvider`, outside of specific protocols layered on top of it (e.g., `DocumentsProvider`). `FileProvider`, in particular, is completely incapable of serving a directory. It can only serve files. Your "solution" relies on client apps deducing that the `Uri` happens to point to external storage and then ignoring the `Uri` and working with external storage directly. That's something that well-written apps will not do, yet you assume that ~2 billion devices will be set up to work this way. – CommonsWare May 01 '18 at 12:31
  • @CommonsWare The purpose for defining `Uri` was for demonstration. You can replace with any available directory `Uri`. Have you tried the code? I will try it on real devices tomorrow. – Sagar May 01 '18 at 12:37
  • "You can replace with any available directory Uri" -- there is no "directory `Uri`" that can be served by `FileProvider`. There *is* a "directory `Uri`", but it begins with a `file` scheme. That triggers the `FileUriExposedException` on Android 7.0+. `DocumentsContract.Document.MIME_TYPE_DIR` is for document trees retrieved via the Storage Access Framework (e.g., `ACTION_OPEN_DOCUMENT_TREE`), not for arbitrary `content` `Uri` values. "I will try it on real devices tomorrow" -- there are ~10,000 Android device models. How many were you planning on testing? – CommonsWare May 01 '18 at 12:40
  • @CommonsWare `that triggers the FileUriExposedException on Android 7.0+` It won't trigger if you implement `provider` as I have implemented. As I said in my answer, I cannot 100% guarantee that it will work. I will try on few popular devices with different vendors and deduce based on it. There is no standard answer to this question. So there has to be some workarounds to get it done which could work under certain conditions. – Sagar May 01 '18 at 12:45
  • "So there has to be some workarounds to get it done which could work under certain conditions" -- there doesn't "have" to be a workaround. It could simply be unsupported. – CommonsWare May 01 '18 at 12:52
  • @CommonsWare What I meant was, since there is no direct/official way to achieve it, we need to try out some work-around before declaring it completely unsupported. My proposed answer and alternatives would work under certain conditions. – Sagar May 01 '18 at 13:00
  • Hi! Sorry but your approach does not work. – NoWar May 03 '18 at 01:36
  • @AcademyofProgrammer You are right. doesn't work on samsung. So far only worked on Techno phone and some emulator – Sagar May 03 '18 at 03:17
4

The Below Code is used by me to open an image from Storage :

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

File path = new File(Environment.getExternalStorageDirectory() + "/" + "ParentDirectory" + "/" + "ChildDirectory");

File filepath = new File(path + "/" + yourImageName.png);

Uri uri = Uri.fromFile(filepath);

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "image/jpeg");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

Edited Answer :

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

File path = new File(Environment.getExternalStorageDirectory() + "/" + "ParentDirectory" + "/" + "ChildDirectory");

Uri uri = Uri.fromFile(path);

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "image/jpeg");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
ColdFire
  • 6,764
  • 6
  • 35
  • 51
Shiva Snape
  • 544
  • 5
  • 9
  • Hi! Well... It works but still to have (1) Select app to open files (2) Navigate to the internal storage Pictures folder. Any clue how to avoid it? Thanks! – NoWar Apr 28 '18 at 05:49
  • Did u Add a Provider in your manifest? – Shiva Snape Apr 28 '18 at 05:52
  • Sure. Pls recheck my updated answer. Thank you! – NoWar Apr 28 '18 at 05:55
  • pls check the edited answer, it May help you.. – Shiva Snape Apr 28 '18 at 06:08
  • I dont have any special image to select. But in your answer I see `File filepath = new File(path + "/" + yourImageName.png);` Do you think we have to use dummy image name in order to open entire folder? – NoWar Apr 28 '18 at 06:14
  • i got your point... let me try once more. pls check my edited answer – Shiva Snape Apr 28 '18 at 06:26
  • Hi! Using your approach I see the app chooser and when I select `Gallery` it displays an EMPTY screen. It seems like we have to send the path in a correct way... But how to do it? – NoWar Apr 28 '18 at 06:37
  • 1
    please check this link https://stackoverflow.com/questions/14200309/android-display-images-from-specific-folder-in-gallery-view – Shiva Snape May 03 '18 at 06:20
  • Will Strict mode will work in Production Release as well? – Pawan Soni Jan 07 '23 at 11:24
0

I found solution here can we open download folder via. intent?

This code works perfect in my Samsung J7 to open Pictures folder (and others) from internal memory using Samsung default application My files.

File path = new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_PICTURES);
Uri uri = Uri.fromFile(path);
Intent intent = getPackageManager().getLaunchIntentForPackage("com.sec.android.app.myfiles");
intent.setAction("samsung.myfiles.intent.action.LAUNCH_MY_FILES");
                    intent.putExtra("samsung.myfiles.intent.extra.START_PATH", path.getAbsolutePath());
startActivity(intent);

It seems like we have to decompile File Manager of each manufacturer to see how to call it properly. :( And it is too much work. Well... I assumed there is some generic solution to do it.

NoWar
  • 36,338
  • 80
  • 323
  • 498