How to create file/folder picker with a storage chooser in Android +4.4 using the Storage Chooser library

How to create file/folder picker with a storage chooser in Android +4.4 using the Storage Chooser library

Willing to implement a file/folder picker for your Android application? Surely you don't want to create one by yourself, instead you would like to rely on a third party library for this. Although there are a lot of libraries that allow you to implement such feature, Storage Chooser is without a doubt a pretty nice option if you want to give your users the option to pick, from which storage of your device he would like to pick a file/directory. Storage Chooser is a pretty and simple directory chooser and file picker library for 4.4+ (Kitkat or newer) devices. There are too many storage chooser out there but this one is too materially. Easy to implement and does not take a lot of your valueable time in setting-up all the other necessary things that every developer seeks, like:

  • saving path to sharedPreference
  • event when path is selected and act upon that path
  • and much more.

There are also some really nice features that I thought would come in handy:

  • You show a quick overview of the storages present and their memory available before choosing so that users know which storage to choose.
  • you can choose between sheet and sleek layouts.
  • Inline create folder view (not another dialog to handle)
  • Full localization. I mean literally every strings can be localized to your desired language.
  • Memory thresholding - a restriction or a toast that it shows when user's memory is less than your defined memory of operations.
  • and more will be added soon.

In this article, we will show you how to implement this Storage Chooser/File chooser/ Folder chooser easily in your Android application.

1. Add library as dependency

To install this library in your project, add the JitPack repository to your project and add the dependency in your build.gradle file like this and synchronize your project:

android {
    repositories {
        maven { url "https://jitpack.io" }
    }
}

dependencies {
    implementation 'com.github.codekidX:storage-chooser:2.0.4.4'
}

For more information about this library, please visit the official repository at Github here.

2. Migrate to AndroidX

If you have any Maven dependencies that have not been migrated to the AndroidX namespace, the Android Studio build system also migrates those dependencies for you when you set the following two flags to true in your gradle.properties fileYou will need to add the following properties to the gradle.properties file and synchronize your project:

android.useAndroidX=true
android.enableJetifier=true

3. Using the Storage Chooser

After installing the library, you will only need create an instance of the StorageChooser.Builder class and customize it according to your needs:

File picker

The following code initializes a Storage Chooser that allows you to pick from where you want to select a file in your device:

// 1. Initialize dialog
final StorageChooser chooser = new StorageChooser.Builder()
    // Specify context of the dialog
    .withActivity(MainActivity.this)
    .withFragmentManager(getFragmentManager())
    .withMemoryBar(true)
    .allowCustomPath(true)
    // Define the mode as the FILE CHOOSER
    .setType(StorageChooser.FILE_PICKER)
    .build();

// 2. Handle what should happend when the user selects the directory !
chooser.setOnSelectListener(new StorageChooser.OnSelectListener() {
    @Override
    public void onSelect(String path) {
        // e.g /storage/emulated/0/Documents/file.txt
        Log.i(path);
    }
});

// 3. Display File Picker whenever you need to !
chooser.show();

The filepicker supports multipick without any extra code, the user just will need to select longer the first item and that's it.

Folder/Directory picker

The following code initializes a Storage Chooser that allows you to pick from where you want to select a directory/folder in your device:

// 1. Initialize dialog
final StorageChooser chooser = new StorageChooser.Builder()
    // Specify context of the dialog
    .withActivity(MainActivity.this)
    .withFragmentManager(getFragmentManager())
    .withMemoryBar(true)
    .allowCustomPath(true)
    // Define the mode as the FOLDER/DIRECTORY CHOOSER
    .setType(StorageChooser.DIRECTORY_CHOOSER)
    .build();

// 2. Handle what should happend when the user selects the directory !
chooser.setOnSelectListener(new StorageChooser.OnSelectListener() {
    @Override
    public void onSelect(String path) {
        // e.g /storage/emulated/0/Documents
        Log.i(path);
    }
});

// 3. Display File Picker whenever you need to !
chooser.show();

For detailed information about more options of the library, don't forget to check the documentation in the official repository at Github. Don't forget that you will need the READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions granted in your application.

4. Full examples

Our example will include the management of the permissions, specifically the READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions of Android. If you don't request the permissions, you will see the following exception through a Toast in your application: The Storage you have chosen is not accessible. Basically caused by the lack of permissions in your app, so that's why we'll include an example of how to request the permissions in Android 6+. You will need as well to add the following attribute:

android:configChanges="orientation|screenSize"

To the activity where you want to implement the Storage Chooser, for example in the AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ourcodeworld.sandboxkitkat44">

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|screenSize"
        >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Having said that, let's show the full examples for every case:

A. Filepicker example

In this example we will have a main activity, whose layout in the activity_main.xml file will have the following content:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button_filepicker"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Pick a file" />
</LinearLayout>

And the Main Activity code will the following:

package com.yourcompany.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.codekidlabs.storagechooser.StorageChooser;

public class MainActivity extends AppCompatActivity {

    public static final int FILEPICKER_PERMISSIONS = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button filepickerBtn = findViewById(R.id.button_filepicker);
        filepickerBtn.setOnClickListener(new View.OnClickListener(){
            @Override
            //On click function
            public void onClick(View view) {
                String[] PERMISSIONS = {
                    android.Manifest.permission.READ_EXTERNAL_STORAGE,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE
                };

                if(hasPermissions(MainActivity.this, PERMISSIONS)){
                    ShowFilepicker();
                }else{
                    ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS, FILEPICKER_PERMISSIONS);
                }
            }
        });
    }

    /**
     * Method that displays the filepicker of the StorageChooser.
     */
    public void ShowFilepicker(){
        // 1. Initialize dialog
        final StorageChooser chooser = new StorageChooser.Builder()
            .withActivity(MainActivity.this)
            .withFragmentManager(getFragmentManager())
            .withMemoryBar(true)
            .allowCustomPath(true)
            .setType(StorageChooser.FILE_PICKER)
            .build();

        // 2. Retrieve the selected path by the user and show in a toast !
        chooser.setOnSelectListener(new StorageChooser.OnSelectListener() {
            @Override
            public void onSelect(String path) {
                Toast.makeText(MainActivity.this, "The selected path is : " + path, Toast.LENGTH_SHORT).show();
            }
        });

        // 3. Display File Picker !
        chooser.show();
    }

    /**
     * Helper method that verifies whether the permissions of a given array are granted or not.
     *
     * @param context
     * @param permissions
     * @return {Boolean}
     */
    public static boolean hasPermissions(Context context, String... permissions) {
        if (context != null && permissions != null) {
            for (String permission : permissions) {
                if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Callback that handles the status of the permissions request.
     * 
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case FILEPICKER_PERMISSIONS: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(
                        MainActivity.this,
                        "Permission granted! Please click on pick a file once again.",
                        Toast.LENGTH_SHORT
                    ).show();
                } else {
                    Toast.makeText(
                        MainActivity.this,
                        "Permission denied to read your External storage :(",
                        Toast.LENGTH_SHORT
                    ).show();
                }

                return;
            }
        }
    }
}

B. Folder picker example

In this example we will have a main activity, whose layout in the activity_main.xml file will have the following content:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button_folderpicker"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Pick a directory" />
</LinearLayout>

And the Main Activity code will the following:

package com.yourcompany.yourapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.codekidlabs.storagechooser.StorageChooser;

public class MainActivity extends AppCompatActivity {

    public static final int FOLDERPICKER_PERMISSIONS = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button folderpickerBtn = findViewById(R.id.button_folderpicker);
        folderpickerBtn.setOnClickListener(new View.OnClickListener(){
            @Override
            //On click function
            public void onClick(View view) {
                String[] PERMISSIONS = {
                    android.Manifest.permission.READ_EXTERNAL_STORAGE,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE
                };

                if(hasPermissions(MainActivity.this, PERMISSIONS)){
                    ShowDirectoryPicker();
                }else{
                    ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS, FOLDERPICKER_PERMISSIONS);
                }
            }
        });
    }

    /**
     * Method that displays the directory chooser of the StorageChooser.
     */
    public void ShowDirectoryPicker(){
        // 1. Initialize dialog
        final StorageChooser chooser = new StorageChooser.Builder()
            .withActivity(MainActivity.this)
            .withFragmentManager(getFragmentManager())
            .withMemoryBar(true)
            .allowCustomPath(true)
            .setType(StorageChooser.DIRECTORY_CHOOSER)
            .build();

        // 2. Retrieve the selected path by the user and show in a toast !
        chooser.setOnSelectListener(new StorageChooser.OnSelectListener() {
            @Override
            public void onSelect(String path) {
                Toast.makeText(MainActivity.this, "The selected path is : " + path, Toast.LENGTH_SHORT).show();
            }
        });

        // 3. Display File Picker !
        chooser.show();
    }

    /**
     * Helper method that verifies whether the permissions of a given array are granted or not.
     *
     * @param context
     * @param permissions
     * @return {Boolean}
     */
    public static boolean hasPermissions(Context context, String... permissions) {
        if (context != null && permissions != null) {
            for (String permission : permissions) {
                if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Callback that handles the status of the permissions request.
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case FOLDERPICKER_PERMISSIONS: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(
                        MainActivity.this,
                        "Permission granted! Please click on pick a file once again.",
                        Toast.LENGTH_SHORT
                    ).show();
                } else {
                    Toast.makeText(
                        MainActivity.this,
                        "Permission denied to read your External storage :(",
                        Toast.LENGTH_SHORT
                    ).show();
                }

                return;
            }
        }
    }
}

Happy coding !

This could interest you

Become a more social person