Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(file-picker): implements pickDirectory #326

Merged
merged 4 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/popular-games-prove.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@capawesome/capacitor-file-picker': minor
---

Implments pickDirectory
ebarooni marked this conversation as resolved.
Show resolved Hide resolved
23 changes: 23 additions & 0 deletions packages/file-picker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const requestPermissions = async () => {
* [`checkPermissions()`](#checkpermissions)
* [`convertHeicToJpeg(...)`](#convertheictojpeg)
* [`pickFiles(...)`](#pickfiles)
* [`pickDirectory()`](#pickdirectory)
* [`pickImages(...)`](#pickimages)
* [`pickMedia(...)`](#pickmedia)
* [`pickVideos(...)`](#pickvideos)
Expand Down Expand Up @@ -151,6 +152,21 @@ Open the file picker that allows the user to select one or more files.
--------------------


### pickDirectory()

```typescript
pickDirectory() => Promise<PickDirectoryResult>
```

Open a picker dialog that allows the user to select a directory.

**Returns:** <code>Promise&lt;<a href="#pickdirectoryresult">PickDirectoryResult</a>&gt;</code>

**Since:** 6.2.0

--------------------


### pickImages(...)

```typescript
Expand Down Expand Up @@ -333,6 +349,13 @@ Remove all listeners for this plugin.
| **`readData`** | <code>boolean</code> | Whether to read the file data. | <code>false</code> | |


#### PickDirectoryResult

| Prop | Type | Description | Since |
| ---------- | ------------------- | ----------------------------------- | ----- |
| **`path`** | <code>string</code> | The path to the selected directory. | 6.2.0 |


#### PickMediaOptions

| Prop | Type | Description | Default | Since |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public class FilePickerPlugin extends Plugin {

public static final String ERROR_PICK_FILE_FAILED = "pickFiles failed.";
public static final String ERROR_PICK_FILE_CANCELED = "pickFiles canceled.";
public static final String ERROR_PICK_DIRECTORY_FAILED = "pickDirectory failed.";
public static final String ERROR_PICK_DIRECTORY_CANCELED = "pickDirectory canceled.";
public static final String PERMISSION_ACCESS_MEDIA_LOCATION = "accessMediaLocation";
public static final String PERMISSION_READ_EXTERNAL_STORAGE = "readExternalStorage";
public static final String TAG = "FilePickerPlugin";
Expand Down Expand Up @@ -78,6 +80,18 @@ public void pickFiles(PluginCall call) {
}
}

@PluginMethod
public void pickDirectory(PluginCall call) {
try {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(call, intent, "pickDirectoryResult");
} catch (Exception ex) {
String message = ex.getMessage();
Log.e(TAG, message);
call.reject(message);
}
}

@PluginMethod
public void pickImages(PluginCall call) {
try {
Expand Down Expand Up @@ -200,6 +214,28 @@ private void pickFilesResult(PluginCall call, ActivityResult result) {
}
}

@ActivityCallback
private void pickDirectoryResult(PluginCall call, ActivityResult result) {
try {
int resultCode = result.getResultCode();
switch (resultCode) {
case Activity.RESULT_OK:
JSObject callResult = createPickDirectoryResult(result.getData());
call.resolve(callResult);
break;
case Activity.RESULT_CANCELED:
call.reject(ERROR_PICK_DIRECTORY_CANCELED);
break;
default:
call.reject(ERROR_PICK_DIRECTORY_FAILED);
}
} catch (Exception ex) {
String message = ex.getMessage();
Log.e(TAG, message);
call.reject(message);
}
}

private JSObject createPickFilesResult(@Nullable Intent data, boolean readData) {
JSObject callResult = new JSObject();
List<JSObject> filesResultList = new ArrayList<>();
Expand Down Expand Up @@ -260,4 +296,15 @@ private JSObject createPickFilesResult(@Nullable Intent data, boolean readData)
callResult.put("files", JSArray.from(filesResultList.toArray()));
return callResult;
}

private JSObject createPickDirectoryResult(@Nullable Intent data) {
JSObject callResult = new JSObject();
if (data != null) {
Uri uri = data.getData();
if (uri != null) {
callResult.put("path", implementation.getPathFromUri(uri));
}
}
return callResult;
}
}
41 changes: 35 additions & 6 deletions packages/file-picker/ios/Plugin/FilePicker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ import MobileCoreServices
}
}

public func openDirectoryPicker() {
DispatchQueue.main.async {
if #available(iOS 14.0, *) {
let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.folder])
picker.delegate = self
picker.modalPresentationStyle = .fullScreen
self.presentViewController(picker)
} else {
let documentTypes = [kUTTypeFolder as String]
let picker = UIDocumentPickerViewController(documentTypes: documentTypes, in: .open)
picker.delegate = self
picker.modalPresentationStyle = .fullScreen
self.presentViewController(picker)
}
}
}

public func openImagePicker(limit: Int, skipTranscoding: Bool, ordered: Bool) {
DispatchQueue.main.async {
if #available(iOS 14, *) {
Expand Down Expand Up @@ -257,17 +274,29 @@ import MobileCoreServices

extension FilePicker: UIDocumentPickerDelegate {
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
do {
let temporaryUrls = try urls.map { try saveTemporaryFile($0) }
plugin?.handleDocumentPickerResult(urls: temporaryUrls, error: nil)
} catch {
plugin?.handleDocumentPickerResult(urls: nil, error: self.plugin?.errorTemporaryCopyFailed)
if plugin?.invokedMethod == "pickFiles" {
do {
let temporaryUrls = try urls.map { try saveTemporaryFile($0) }
plugin?.handleDocumentPickerResult(urls: temporaryUrls, error: nil)
} catch {
plugin?.handleDocumentPickerResult(urls: nil, error: self.plugin?.errorTemporaryCopyFailed)
}
} else if plugin?.invokedMethod == "pickDirectory" {
plugin?.handleDirectoryPickerResult(path: urls.first?.absoluteString, error: nil)
} else {
return
}
plugin?.notifyPickerDismissedListener()
}

public func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
plugin?.handleDocumentPickerResult(urls: nil, error: nil)
if plugin?.invokedMethod == "pickFiles" {
plugin?.handleDocumentPickerResult(urls: nil, error: nil)
} else if plugin?.invokedMethod == "pickDirectory" {
plugin?.handleDirectoryPickerResult(path: nil, error: nil)
} else {
return
}
plugin?.notifyPickerDismissedListener()
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/file-picker/ios/Plugin/FilePickerPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
CAP_PLUGIN_METHOD(pickImages, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(pickMedia, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(pickVideos, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(pickDirectory, CAPPluginReturnPromise);
)
26 changes: 26 additions & 0 deletions packages/file-picker/ios/Plugin/FilePickerPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ public class FilePickerPlugin: CAPPlugin {
public let errorFileNotExist = "File does not exist."
public let errorConvertFailed = "File could not be converted."
public let errorPickFileCanceled = "pickFiles canceled."
public let errorPickDirectoryCanceled = "pickDirectory canceled."
public let errorUnknown = "Unknown error occurred."
public let errorTemporaryCopyFailed = "An unknown error occurred while creating a temporary copy of the file."
public let errorUnsupportedFileTypeIdentifier = "Unsupported file type identifier."
public let pickerDismissedEvent = "pickerDismissed"
public var invokedMethod: String?
ebarooni marked this conversation as resolved.
Show resolved Hide resolved
private var implementation: FilePicker?
private var savedCall: CAPPluginCall?

Expand Down Expand Up @@ -51,6 +53,7 @@ public class FilePickerPlugin: CAPPlugin {

@objc func pickFiles(_ call: CAPPluginCall) {
savedCall = call
invokedMethod = "pickFiles"

let limit = call.getInt("limit", 0)
let types = call.getArray("types", String.self) ?? []
Expand All @@ -60,6 +63,12 @@ public class FilePickerPlugin: CAPPlugin {
implementation?.openDocumentPicker(limit: limit, documentTypes: documentTypes)
}

@objc func pickDirectory(_ call: CAPPluginCall) {
savedCall = call
invokedMethod = "pickDirectory"
implementation?.openDirectoryPicker()
}

@objc func pickImages(_ call: CAPPluginCall) {
savedCall = call

Expand Down Expand Up @@ -140,6 +149,23 @@ public class FilePickerPlugin: CAPPlugin {
}
}

@objc func handleDirectoryPickerResult(path: String?, error: String?) {
guard let savedCall = savedCall else {
return
}
if let error = error {
savedCall.reject(error)
return
}
guard let path = path else {
savedCall.reject(errorPickDirectoryCanceled)
return
}
var result = JSObject()
result["path"] = path
savedCall.resolve(result)
}

private func parseTypesOption(_ types: [String]) -> [String] {
var parsedTypes: [String] = []
for (_, type) in types.enumerated() {
Expand Down
15 changes: 15 additions & 0 deletions packages/file-picker/src/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export interface FilePickerPlugin {
* Open the file picker that allows the user to select one or more files.
*/
pickFiles(options?: PickFilesOptions): Promise<PickFilesResult>;
/**
* Open a picker dialog that allows the user to select a directory.
*
ebarooni marked this conversation as resolved.
Show resolved Hide resolved
* @since 6.2.0
*/
pickDirectory(): Promise<PickDirectoryResult>;
/**
* Pick one or more images from the gallery.
*
Expand Down Expand Up @@ -269,6 +275,15 @@ export interface PickMediaOptions {
ordered?: boolean;
}

export interface PickDirectoryResult {
/**
* The path to the selected directory.
*
* @since 6.2.0
*/
path: string;
}

/**
* @since 0.5.3
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/file-picker/src/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
PickVideosResult,
PickedFile,
RequestPermissionsOptions,
PickDirectoryResult,
} from './definitions';

export class FilePickerWeb extends WebPlugin implements FilePickerPlugin {
Expand Down Expand Up @@ -55,6 +56,10 @@ export class FilePickerWeb extends WebPlugin implements FilePickerPlugin {
return result;
}

public async pickDirectory(): Promise<PickDirectoryResult> {
throw this.unimplemented('Not implemented on web.');
}

public async pickImages(
options?: PickImagesOptions,
): Promise<PickImagesResult> {
Expand Down