Skip to content

Commit 4173790

Browse files
roman-r-mvonovak
andauthored
feat: add pickDirectory on Android (#414)
* Fix typo * Update Android module * Update js side * Fix linter errors * Update README * Always set the path field, even if null * Fix DirectoryPickerResponse.path * Address code review comments * Restore accidentally removed methods * Fix formatting * Indicate that pickDirectory can return null * Make linter happy * Only return the URI * Remove path from the docs * Delete trailing spaces * Update index.d.ts * Update index.js Co-authored-by: Vojtech Novak <[email protected]>
1 parent ccc194f commit 4173790

File tree

4 files changed

+72
-5
lines changed

4 files changed

+72
-5
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,18 @@ The file size of the document. _On Android some DocumentProviders may not provid
107107

108108
The base64 encoded content of the picked file if the option `readContent` was set to `true`.
109109

110+
111+
#### [Android only] `DocumentPicker.pickDirectory(options)`
112+
113+
Open a system directory picker. Returns a promise that resolves to the directory selected by user.
114+
115+
### Result
116+
117+
##### `uri`:
118+
119+
The URI of the selected directory. Usually it will be a content URI.
120+
121+
110122
### `DocumentPicker.types.*`
111123

112124
`DocumentPicker.types.*` provides a few common types for use as `type` values, these types will use the correct format for each platform (MIME types on Android, UTIs on iOS).

android/src/main/java/io/github/elyx0/reactnativedocumentpicker/DocumentPickerModule.java

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
public class DocumentPickerModule extends ReactContextBaseJavaModule {
4242
private static final String NAME = "RNDocumentPicker";
4343
private static final int READ_REQUEST_CODE = 41;
44+
private static final int PICK_DIR_REQUEST_CODE = 42;
4445

4546
private static final String E_ACTIVITY_DOES_NOT_EXIST = "ACTIVITY_DOES_NOT_EXIST";
4647
private static final String E_FAILED_TO_SHOW_PICKER = "FAILED_TO_SHOW_PICKER";
@@ -51,7 +52,7 @@ public class DocumentPickerModule extends ReactContextBaseJavaModule {
5152
private static final String E_UNEXPECTED_EXCEPTION = "UNEXPECTED_EXCEPTION";
5253

5354
private static final String OPTION_TYPE = "type";
54-
private static final String OPTION_MULIPLE = "multiple";
55+
private static final String OPTION_MULTIPLE = "multiple";
5556
private static final String OPTION_COPYTO = "copyTo";
5657

5758
private static final String FIELD_URI = "uri";
@@ -61,13 +62,17 @@ public class DocumentPickerModule extends ReactContextBaseJavaModule {
6162
private static final String FIELD_TYPE = "type";
6263
private static final String FIELD_SIZE = "size";
6364

65+
6466
private final ActivityEventListener activityEventListener = new BaseActivityEventListener() {
6567
@Override
6668
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
69+
if (promise == null) {
70+
return;
71+
}
6772
if (requestCode == READ_REQUEST_CODE) {
68-
if (promise != null) {
69-
onShowActivityResult(resultCode, data, promise);
70-
}
73+
onShowActivityResult(resultCode, data, promise);
74+
} else if (requestCode == PICK_DIR_REQUEST_CODE) {
75+
onPickDirectoryResult(resultCode, data, promise);
7176
}
7277
}
7378
};
@@ -126,7 +131,7 @@ public void pick(ReadableMap args, Promise promise) {
126131
}
127132
}
128133

129-
boolean multiple = !args.isNull(OPTION_MULIPLE) && args.getBoolean(OPTION_MULIPLE);
134+
boolean multiple = !args.isNull(OPTION_MULTIPLE) && args.getBoolean(OPTION_MULTIPLE);
130135
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple);
131136

132137
currentActivity.startActivityForResult(Intent.createChooser(intent, null), READ_REQUEST_CODE, Bundle.EMPTY);
@@ -175,6 +180,43 @@ public void onShowActivityResult(int resultCode, Intent data, Promise promise) {
175180
}
176181
}
177182

183+
@ReactMethod
184+
public void pickDirectory(Promise promise) {
185+
Activity currentActivity = getCurrentActivity();
186+
187+
if (currentActivity == null) {
188+
promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Current activity does not exist");
189+
return;
190+
}
191+
this.promise = promise;
192+
try {
193+
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
194+
currentActivity.startActivityForResult(intent, PICK_DIR_REQUEST_CODE, null);
195+
} catch (Exception e) {
196+
sendError(E_FAILED_TO_SHOW_PICKER, "Failed to create directory picker", e);
197+
}
198+
}
199+
200+
private void onPickDirectoryResult(int resultCode, Intent data, Promise promise) {
201+
if (resultCode == Activity.RESULT_CANCELED) {
202+
sendError(E_DOCUMENT_PICKER_CANCELED, "User canceled directory picker");
203+
return;
204+
} else if (resultCode != Activity.RESULT_OK) {
205+
sendError(E_UNKNOWN_ACTIVITY_RESULT, "Unknown activity result: " + resultCode);
206+
return;
207+
}
208+
209+
if (data == null || data.getData() == null) {
210+
sendError(E_INVALID_DATA_RETURNED, "Invalid data returned by intent");
211+
return;
212+
}
213+
Uri uri = data.getData();
214+
215+
WritableMap map = Arguments.createMap();
216+
map.putString(FIELD_URI, uri.toString());
217+
promise.resolve(map);
218+
}
219+
178220
private static class ProcessDataTask extends GuardedResultAsyncTask<ReadableArray> {
179221
private final WeakReference<Context> weakContext;
180222
private final List<Uri> uris;

index.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ declare module 'react-native-document-picker' {
7878
name: string;
7979
size: number;
8080
}
81+
interface DirectoryPickerResponse {
82+
uri: string;
83+
}
84+
8185
type Platform = 'ios' | 'android' | 'windows';
8286
export default class DocumentPicker<OS extends keyof PlatformTypes = Platform> {
8387
static types: PlatformTypes['ios'] | PlatformTypes['android'] | PlatformTypes['windows'];
@@ -89,5 +93,6 @@ declare module 'react-native-document-picker' {
8993
): Promise<DocumentPickerResponse[]>;
9094
static isCancel<IError extends { code?: string }>(err?: IError): boolean;
9195
static releaseSecureAccess(uris: Array<string>): void;
96+
static pickDirectory(): Promise<DirectoryPickerResponse | null>;
9297
}
9398
}

index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,12 @@ export default class DocumentPicker {
189189
static releaseSecureAccess(uris) {
190190
releaseSecureAccess(uris);
191191
}
192+
193+
static pickDirectory() {
194+
if (Platform.OS === 'android') {
195+
return RNDocumentPicker.pickDirectory();
196+
} else {
197+
return Promise.resolve(null);
198+
}
199+
}
192200
}

0 commit comments

Comments
 (0)