Skip to content
This repository has been archived by the owner on Jul 23, 2022. It is now read-only.
/ android-file-chooser Public archive

a lightweight file/folder chooser or picker

License

Notifications You must be signed in to change notification settings

hedzr/android-file-chooser

Repository files navigation

android-file-chooser

Financial Contributors on Open Collective Android Arsenal Download Release Build Status

IMPORTANT:

THIS REPO HAS BEEN STOPPED TO UPDATE TO COMPLY WITH THE NEWER ANDROID LIKE 'Q' AND ABOVE.


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

banner

Demo Application

A demo-app can be installed from Play Store.

Get it on Google Play

Xamarin Binding

A Xamarin nuget package by @Guiorgy can be found at NuGet

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: spring-gradle-plugins/dependency-management-plugin#74 (comment)

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()


withOnBackPressedListener

BackPressedListener will be called every time back key is pressed, and current directory is not the root of Primary/SdCard storage. LastBackPressedListener will be called if back key is pressed, and current directory is the root of Primary/SdCard storage.

.withOnBackPressedListener(dialog -> chooserDialog.goBack())
.withOnLastBackPressedListener(dialog -> dialog.cancel())

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")

Under Kotlin

class MyFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val root = inflater.inflate(R.layout.fragment_book, container, false)
        root.upload_button.setOnClickListener { _: View ->
            ChooserDialog().with(activity)
                    .withStartFile(Environment.getExternalStorageDirectory().absolutePath)
                    // .withStartFile(Environment.getExternalStorageState()+"/")
                    .withFilterRegex(false, false, ".*\\.(jpe?g|png)")
                    .titleFollowsDir(true)
                    .displayPath(true)
                    .customizePathView{ pathView -> pathView.setGravity(Gravity.RIGHT) }
                    .withChosenListener { path, pathFile -> activity!!.toast("FILE: $path / $pathFile") }
                    .build()
                    .show()
        }

        return root
    }
}

And:

        ChooserDialog(context)
                .withFilterRegex(false, true, ".*\\.(jpe?g|png)")
                .withStartFile(startPath)
                .withResources(R.string.title_choose_file, R.string.title_choose, R.string.dialog_cancel)
                .withChosenListener { path, pathFile ->
                    Toast.makeText(context, "FILE: $path; PATHFILE: $pathFile", Toast.LENGTH_SHORT).show()

                    //_path = path
                    //_tv.setText(_path)
                    ////_iv.setImageURI(Uri.fromFile(pathFile));
                    //_iv.setImageBitmap(ImageUtil.decodeFile(pathFile))
                }
                .withNavigateUpTo { true }
                .withNavigateTo { true }
                .build()
                .show()

For Library Developers

Just fork and build me currently.

Contrib

Contributions and translations are welcome.

Feedback

feel free to make a new issue.

Acknowledges

many peoples report or contribute to improve me, but only a few of them be put here — it's hard to list all.

Contributors

Code Contributors

This project exists thanks to all the people who contribute. [Contribute].

Financial Contributors

Become a financial contributor and help us sustain our community. [Contribute]

Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]

License

Standard Apache 2.0

Copyright 2015-2019 Hedzr Yeh.