Skip to content

Commit f9855eb

Browse files
committed
Merge pull request afollestad#861 from jmartingit/master
new File Chooser Dialog
2 parents 36ee9b8 + 0da98d0 commit f9855eb

File tree

1 file changed

+254
-0
lines changed

1 file changed

+254
-0
lines changed
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
package com.afollestad.materialdialogs.folderselector;
2+
3+
import android.Manifest;
4+
import android.app.Activity;
5+
import android.app.Dialog;
6+
import android.content.pm.PackageManager;
7+
import android.os.Build;
8+
import android.os.Bundle;
9+
import android.os.Environment;
10+
import android.support.annotation.NonNull;
11+
import android.support.annotation.Nullable;
12+
import android.support.annotation.StringRes;
13+
import android.support.v4.app.ActivityCompat;
14+
import android.support.v4.app.DialogFragment;
15+
import android.support.v4.app.Fragment;
16+
import android.support.v7.app.AppCompatActivity;
17+
import android.view.View;
18+
import android.webkit.MimeTypeMap;
19+
20+
import com.afollestad.materialdialogs.DialogAction;
21+
import com.afollestad.materialdialogs.MaterialDialog;
22+
import com.afollestad.materialdialogs.commons.R;
23+
24+
import java.io.File;
25+
import java.io.Serializable;
26+
import java.util.ArrayList;
27+
import java.util.Collections;
28+
import java.util.Comparator;
29+
import java.util.List;
30+
31+
public class FileChooserDialog extends DialogFragment implements MaterialDialog.ListCallback {
32+
33+
private final static String TAG = "[MD_FILE_SELECTOR]";
34+
35+
private File parentFolder;
36+
private File[] parentContents;
37+
private boolean canGoUp = true;
38+
private FileCallback mCallback;
39+
40+
public interface FileCallback {
41+
void onFileSelection(File file);
42+
}
43+
44+
public FileChooserDialog() {
45+
}
46+
47+
String[] getContentsArray() {
48+
if (parentContents == null) return new String[]{};
49+
String[] results = new String[parentContents.length + (canGoUp ? 1 : 0)];
50+
if (canGoUp) results[0] = "...";
51+
for (int i = 0; i < parentContents.length; i++)
52+
results[canGoUp ? i + 1 : i] = parentContents[i].getName();
53+
return results;
54+
}
55+
56+
File[] listFiles(String mimeType) {
57+
File[] contents = parentFolder.listFiles();
58+
List<File> results = new ArrayList<>();
59+
if (contents != null) {
60+
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
61+
for (File fi : contents) {
62+
if (fi.isDirectory()) {
63+
results.add(fi);
64+
} else {
65+
if (fileIsMimeType(fi, mimeType, mimeTypeMap)) {
66+
results.add(fi);
67+
}
68+
}
69+
}
70+
Collections.sort(results, new FileSorter());
71+
return results.toArray(new File[results.size()]);
72+
}
73+
return null;
74+
}
75+
76+
boolean fileIsMimeType(File file, String mimeType, MimeTypeMap mimeTypeMap) {
77+
if (mimeType == null || mimeType.equals("*/*")) {
78+
return true;
79+
} else {
80+
// get the file mime type
81+
String filename = file.toURI().toString();
82+
int dotPos = filename.lastIndexOf('.');
83+
if (dotPos == -1) {
84+
return false;
85+
}
86+
String fileExtension = filename.substring(dotPos + 1);
87+
String fileType = mimeTypeMap.getMimeTypeFromExtension(fileExtension);
88+
if (fileType == null) {
89+
return false;
90+
}
91+
// check the 'type/subtype' pattern
92+
if (fileType.equals(mimeType)) {
93+
return true;
94+
}
95+
// check the 'type/*' pattern
96+
int mimeTypeDelimiter = mimeType.lastIndexOf('/');
97+
if (mimeTypeDelimiter == -1) {
98+
return false;
99+
}
100+
String mimeTypeMainType = mimeType.substring(0, mimeTypeDelimiter);
101+
String mimeTypeSubtype = mimeType.substring(mimeTypeDelimiter + 1);
102+
if (!mimeTypeSubtype.equals("*")) {
103+
return false;
104+
}
105+
int fileTypeDelimiter = fileType.lastIndexOf('/');
106+
if (fileTypeDelimiter == -1 ) {
107+
return false;
108+
}
109+
String fileTypeMainType = fileType.substring(0, fileTypeDelimiter);
110+
if (fileTypeMainType.equals(mimeTypeMainType)) {
111+
return true;
112+
}
113+
}
114+
return false;
115+
}
116+
117+
@SuppressWarnings("ConstantConditions")
118+
@NonNull
119+
@Override
120+
public Dialog onCreateDialog(Bundle savedInstanceState) {
121+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
122+
ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) !=
123+
PackageManager.PERMISSION_GRANTED) {
124+
return new MaterialDialog.Builder(getActivity())
125+
.title(R.string.md_error_label)
126+
.content(R.string.md_storage_perm_error)
127+
.positiveText(android.R.string.ok)
128+
.build();
129+
}
130+
131+
if (getArguments() == null || !getArguments().containsKey("builder"))
132+
throw new IllegalStateException("You must create a FileChooserDialog using the Builder.");
133+
if (!getArguments().containsKey("current_path"))
134+
getArguments().putString("current_path", getBuilder().mInitialPath);
135+
parentFolder = new File(getArguments().getString("current_path"));
136+
parentContents = listFiles(getBuilder().mMimeType);
137+
return new MaterialDialog.Builder(getActivity())
138+
.title(parentFolder.getAbsolutePath())
139+
.items(getContentsArray())
140+
.itemsCallback(this)
141+
.onNegative(new MaterialDialog.SingleButtonCallback() {
142+
@Override
143+
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
144+
dialog.dismiss();
145+
}
146+
})
147+
.autoDismiss(false)
148+
.negativeText(getBuilder().mCancelButton)
149+
.build();
150+
}
151+
152+
@Override
153+
public void onSelection(MaterialDialog materialDialog, View view, int i, CharSequence s) {
154+
if (canGoUp && i == 0) {
155+
parentFolder = parentFolder.getParentFile();
156+
canGoUp = parentFolder.getParent() != null;
157+
} else {
158+
parentFolder = parentContents[canGoUp ? i - 1 : i];
159+
canGoUp = true;
160+
}
161+
if (parentFolder.isFile()) {
162+
mCallback.onFileSelection(parentFolder);
163+
dismiss();
164+
} else {
165+
parentContents = listFiles(getBuilder().mMimeType);
166+
MaterialDialog dialog = (MaterialDialog) getDialog();
167+
dialog.setTitle(parentFolder.getAbsolutePath());
168+
getArguments().putString("current_path", parentFolder.getAbsolutePath());
169+
dialog.setItems(getContentsArray());
170+
}
171+
}
172+
173+
@Override
174+
public void onAttach(Activity activity) {
175+
super.onAttach(activity);
176+
mCallback = (FileCallback) activity;
177+
}
178+
179+
public void show(AppCompatActivity context) {
180+
Fragment frag = context.getSupportFragmentManager().findFragmentByTag(TAG);
181+
if (frag != null) {
182+
((DialogFragment) frag).dismiss();
183+
context.getSupportFragmentManager().beginTransaction()
184+
.remove(frag).commit();
185+
}
186+
show(context.getSupportFragmentManager(), TAG);
187+
}
188+
189+
public static class Builder implements Serializable {
190+
191+
@NonNull
192+
protected final transient AppCompatActivity mContext;
193+
@StringRes
194+
protected int mCancelButton;
195+
protected String mInitialPath;
196+
protected String mMimeType;
197+
198+
public <ActivityType extends AppCompatActivity & FileCallback> Builder(@NonNull ActivityType context) {
199+
mContext = context;
200+
mCancelButton = android.R.string.cancel;
201+
mInitialPath = Environment.getExternalStorageDirectory().getAbsolutePath();
202+
mMimeType = null;
203+
}
204+
205+
@NonNull
206+
public Builder cancelButton(@StringRes int text) {
207+
mCancelButton = text;
208+
return this;
209+
}
210+
211+
@NonNull
212+
public Builder initialPath(@Nullable String initialPath) {
213+
if (initialPath == null)
214+
initialPath = File.separator;
215+
mInitialPath = initialPath;
216+
return this;
217+
}
218+
219+
@NonNull
220+
public Builder mimeType(@Nullable String type) {
221+
mMimeType = type;
222+
return this;
223+
}
224+
225+
@NonNull
226+
public FileChooserDialog build() {
227+
FileChooserDialog dialog = new FileChooserDialog();
228+
Bundle args = new Bundle();
229+
args.putSerializable("builder", this);
230+
dialog.setArguments(args);
231+
return dialog;
232+
}
233+
234+
@NonNull
235+
public FileChooserDialog show() {
236+
FileChooserDialog dialog = build();
237+
dialog.show(mContext);
238+
return dialog;
239+
}
240+
}
241+
242+
@SuppressWarnings("ConstantConditions")
243+
@NonNull
244+
private Builder getBuilder() {
245+
return (Builder) getArguments().getSerializable("builder");
246+
}
247+
248+
private static class FileSorter implements Comparator<File> {
249+
@Override
250+
public int compare(File lhs, File rhs) {
251+
return lhs.getName().compareTo(rhs.getName());
252+
}
253+
}
254+
}

0 commit comments

Comments
 (0)