android-file-chooser

android-file-chooser

android-file-library is a lightweight file/folder chooser.

The usages at HERE, and Acknowledges.

Legacy

1. with AndroidX

dependencies {
 // implementation 'com.github.hedzr:android-file-chooser:1.2.0-SNAPSHOT'
 implementation 'com.github.hedzr:android-file-chooser:v1.2.0-final'
}

MediaStore for Android Q (still in beta)

dependencies {
 implementation 'com.github.hedzr:android-file-chooser:devel-SNAPSHOT'
}

Overview

Demo Application

A demo-app can be installed from Play Store.

Xamarin Binding

A Xamarin nuget package by @Guiorgy can be found at

Changes

v1.1.19

  • bugs fixed
  • minor fixes for themes
  • #60, #61, #62 fixed
  • revamped Dpad controls
  • added cancelOnTouchOutside and enableDpad (true by default)
  • mainly by Guiorgy.

Archived History:

  • rewrite demo app

  • #48: add displayPath(boolean), thank you @Guiorgy, and your android-smbfile-chooser.

  • new style demo app by @Guiorgy.

  • NOTE: displayPath is true by default now.

  • since v1.1.16, bumped targer sdk to 1.8 (please include the following into your build.gradle)

    android {
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    }
  • no WRITE_EXTERNAL_STORAGE requests if not enableOptions(true);

  • after requested permissions, try showing dialog again instead of return directly;

  • #42: onBackPressedListener not fired. Now, use withCancelListener to handle back key. see also below

  • #45: add titleFollowsDir(boolean) to allow title following the change of current directory.

  • create new folder on the fly, and the optional multiple select mode for developer, thx @Guiorgy.

  • Up (..) on the primary storage root will be replaced with .. SDCard, it allows to jump to external storage such as a SDCard and going back available too.

  • DPad supports, arrow keys supports (#30)

Snapshots

More images (beyond v1.1.16) have been found at Gallery

Usages

Configuration

build.gradle

android-file-chooser was released at jcenter, declare deps with:

implementation 'com.obsez.android.lib.filechooser:filechooser:$android_file_chooser_version'

for the newest version(s), looking up the badges above.

taste the fresh

there is a way to taste the master branch with jitpack.io:

  1. add the jitpack repository url to your root build.gradle:
allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }
    }
}
  1. import android-file-chooser
implementation 'com.github.hedzr:android-file-chooser:master-SNAPSHOT'
// implementation 'com.github.hedzr:android-file-chooser:v1.1.14'

Tips for using JitPack.io

To disable gradle local cache in your project, add stretegy into your top build.grable:

configurations.all {
    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
    resolutionStrategy.cacheDynamicVersionsFor 0, 'seconds'
}

ref: https://github.com/spring-gradle-plugins/dependency-management-plugin/issues/74#issuecomment-182484694

Sometimes it's right, sometimes ... no more warrants.

Codes

Tips

  1. I am hands down AlertDialog.
  2. Any codes about ChooserDialog, such as the following demo codes, should be only put into UI thread.

FileChooser android library give a simple file/folder chooser in single call (Fluent):

Choose a Folder

    new ChooserDialog(MainActivity.this)
            .withFilter(true, false)
         .withStartFile(startingDir)
         // to handle the result(s)
            .withChosenListener(new ChooserDialog.Result() {
                @Override
                public void onChoosePath(String path, File pathFile) {
                    Toast.makeText(MainActivity.this, "FOLDER: " + path, Toast.LENGTH_SHORT).show();
                }
            })
            .build()
            .show();

Choose a File

    new ChooserDialog(MainActivity.this)
            .withStartFile(path)
            .withChosenListener(new ChooserDialog.Result() {
                @Override
                public void onChoosePath(String path, File pathFile) {
                    Toast.makeText(MainActivity.this, "FILE: " + path, Toast.LENGTH_SHORT).show();
                }
            })
         // to handle the back key pressed or clicked outside the dialog:
         .withOnCancelListener(new DialogInterface.OnCancelListener() {
       public void onCancel(DialogInterface dialog) {
           Log.d("CANCEL", "CANCEL");
           dialog.cancel(); // MUST have
       }
   })
            .build()
            .show();

Wild-match

    new ChooserDialog(MainActivity.this)
            .withFilter(false, false, "jpg", "jpeg", "png")
            .withStartFile(path)
            .withResources(R.string.title_choose_file, R.string.title_choose, R.string.dialog_cancel)
            .withChosenListener(new ChooserDialog.Result() {
                @Override
                public void onChoosePath(String path, File pathFile) {
                    Toast.makeText(MainActivity.this, "FILE: " + path, Toast.LENGTH_SHORT).show();
                }
            })
            .build()
            .show();

Regex filter

    new ChooserDialog(MainActivity.this)
            .withFilterRegex(false, false, ".*\\.(jpe?g|png)")
            .withStartFile(path)
            .withResources(R.string.title_choose_file, R.string.title_choose, R.string.dialog_cancel)
            .withChosenListener(new ChooserDialog.Result() {
                @Override
                public void onChoosePath(String path, File pathFile) {
                    Toast.makeText(NewMainActivity.this, "FILE: " + path, Toast.LENGTH_SHORT).show();
                }
            })
            .build()
            .show();

Date Format String

Since 1.1.3, new builder options withDateFormat(String) added.

    new ChooserDialog(MainActivity.this)
            .withFilter(true, false)
            .withStartFile(startingDir)
            .withDateFormat("HH:mm")    // see also SimpleDateFormat format specifiers
            .withChosenListener(new ChooserDialog.Result() {
                @Override
                public void onChoosePath(String path, File pathFile) {
                    Toast.makeText(MainActivity.this, "FOLDER: " + path, Toast.LENGTH_SHORT).show();
                }
            })
            .build()
            .show();

Modify Icon or View Layout of AlertDialog:

Since 1.1.6, 2 new options are available:

    new ChooserDialog(MainActivity.this)
            .withFilter(true, false)
            .withStartFile(startingDir)
            .withIcon(R.drawable.ic_file_chooser)
            .withLayoutView(R.layout.alert_file_chooser) // (API > 20)
            .withChosenListener(new ChooserDialog.Result() {
                @Override
                public void onChoosePath(String path, File pathFile) {
                    Toast.makeText(MainActivity.this, "FOLDER: " + path, Toast.LENGTH_SHORT).show();
                }
            })
            .build()
            .show();

Customizable NegativeButton

1.1.7 or Higher, try withNegativeButton() and/or withNegativeButtonListener()


onCancelListener

OnCancelListener will be called when touching outside the dialog (cancelOnTouchOutside must be set true), and when pressing back key. If BackPressedListener is overridden, it wont be called if dialog.dismiss is used instead of dialog.cancel. OnCancelListener will NOT be called when pressing the negative button. use withNegativeButtonListener for that.

.withOnCancelListener(new DialogInterface.OnCancelListener() {
    public void onCancel(DialogInterface dialog) {
        Log.d("CANCEL", "CANCEL");
    }
})

---

#### New calling chain

1.1.7+, new constructor `ChooserDialog(context)` can simplify the chain invoking. Also `build()` is no longer obligatory to be called:

​```java
    new ChooserDialog(MainActivity.this)
            .withFilter(true, false)
            .withStartFile(startingDir)
            ...
   .show();

And, old style is still available. No need to modify your existing codes.

withRowLayoutView(resId)

1.1.8+. Now you can customize each row.

since 1.1.17, DirAdatper.GetViewListener#getView allows you do the same thing and more, and withRowLayoutView will be deprecated. See also: withAdapterSetter(setter)

withFileIcons

1.1.9+. withFileIcons(resolveMime, fileIcon, folderIcon) and withFileIconsRes(resolveMime, fileIconResId, folderIconResId) allow user-defined file/folder icon.

resolveMime: true means that DirAdapter will try get icon from the associated app with the file's mime type.

    final Context ctx = MainActivity.this;
    new ChooserDialog(ctx)
            .withStartFile(_path)
            .withResources(R.string.title_choose_any_file, R.string.title_choose, R.string.dialog_cancel)
            .withFileIconsRes(false, R.mipmap.ic_my_file, R.mipmap.ic_my_folder)
            .withChosenListener(new ChooserDialog.Result() {
                @Override
                public void onChoosePath(String path, File pathFile) {
                    Toast.makeText(ctx, "FILE: " + path, Toast.LENGTH_SHORT).show();
                }
            })
            .build()
            .show();

withAdapterSetter(setter)

1.1.9+. a AdapterSetter can be use to customize the DirAdapter.

.withAdapterSetter(new ChooserDialog.AdapterSetter() {
    @Override
    public void apply(DirAdapter adapter) {
        adapter.setDefaultFileIcon(fileIcon);
        adapter.setDefaultFolderIcon(folderIcon);
        adapter.setResolveFileType(tryResolveFileTypeAndIcon);
  // since 1.1.17
  adapter.overrideGetView((file, isSelected, isFocused, convertView, parent, inflater) -> {
   ViewGroup view = (ViewGroup) inflater.inflate(R.layout.li_row, parent, false);
   ...
   return view;
  }
    }
})

More information in source code of DirAdapter.

since 1.1.17, DirAdapter.overrideGetView() supports GetViewListener interface.

    public interface GetView {
        /**
         * @param file        file that should me displayed
         * @param isSelected  whether file is selected when _enableMultiple is set to true
         * @param isFocused   whether this file is focused when using dpad controls
          deprecated since 1.1.18! use fileListItemFocusedDrawable attribute instead
         * @param convertView see ArrayAdapter#getView(int, View, ViewGroup)
         * @param parent      see ArrayAdapter#getView(int, View, ViewGroup)
         * @param inflater    a layout inflater with the FileChooser theme wrapped context
         * @return your custom row item view
         */
        @NonNull
        View getView(@NonNull File file, boolean isSelected, boolean isFocused, View convertView,
            @NonNull ViewGroup parent, @NonNull LayoutInflater inflater);
    }

withNavigateUpTo(CanNavigateUp)

1.1.10+. withNavigateUpTo

You can disallow someone enter some special directories.

.withNavigateUpTo(new ChooserDialog.CanNavigateUp() {
    @Override
    public boolean canUpTo(File dir) {
        return true;
    }
})

withNavigateTo(CanNavigateTo)

1.1.10+. withNavigateTo

With withStartFile(), you can limit the root folder.

.withNavigateTo(new ChooserDialog.CanNavigateTo() {
    @Override
    public boolean canNavigate(File dir) {
        return true;
    }
})

enableOptions(true)

a tri-dot menu icon will be shown at bottom left corner. this icon button allows end user to create new folder on the fly or delete one.

further tunes:

  • withOptionResources(@StringRes int createDirRes, @StringRes int deleteRes, @StringRes int newFolderCancelRes, @StringRes int newFolderOkRes)

  • withOptionStringResources(@Nullable String createDir, @Nullable String delete, @Nullable String newFolderCancel, @Nullable String newFolderOk)

    since v1.1.17

  • withOptionIcons(@DrawableRes int optionsIconRes, @DrawableRes int createDirIconRes, @DrawableRes int deleteRes)

  • withNewFolderFilter(NewFolderFilter filter)

  • withOnBackPressedListener(OnBackPressedListener listener)

  • withOnLastBackPressedListener(OnBackPressedListener listener)

see the sample codes in demo app.

NOTE:

  1. extra WRITE_EXTERNAL_STORAGE permission should be declared in your AndroidManifest.xml.
  2. we'll ask the extra runtime permission to WRITE_EXTERNAL_STORAGE on Android M and higher too.

disableTitle(true)

as named as working.

psuedo .. SDCard Storage and .. Primary Storage

since v1.11, external storage will be detected automatically. That means user can switch between internal and external storage by clicking on psuedo folder names.

titleFollowsDir(true)

since the latest patch of v1.14, it allows the chooser dialog title updated by changing directory.

displayPath(true), customizePathView(callback)

since the latest patch of v1.15, it allows a path string displayed below the title area.

since v1.16, its default value is true.

Screen Snapshot

As a useful complement, customizePathView(callback) allows tuning the path TextView. For example:

.customizePathView((pathView) -> {
    pathView.setGravity(Gravity.RIGHT);
})

since 1.1.17, this can also be done through a custom theme:

<style name="FileChooserStyle">
 ...
 <item name="fileChooserPathViewStyle">@style/FileChooserPathViewStyle</item>
</style>

<style name="FileChooserPathViewStyle">
 <item name="android:background">#ffffffff</item>
 <item name="android:textColor">#40000000</item>
 <item name="android:textSize">12sp</item>
 <item name="fileChooserPathViewElevation">2</item>
 <item name="fileChooserPathViewDisplayRoot">true</item>
</style>

withResources, withStringResources

you can customize the text of buttons:

            .withResources(R.string.title_choose_any_file, R.string.title_choose, R.string.dialog_cancel)
            .withStringResources("Title", "OK", "Cancel")