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