feat(lib): Moved deletePhotos to use new PHAsset API and added an implementation for Android (#69)
* deletePhotos works in iOS * Deletion works on Android. * Removing unnecessary commented out code. * Updated typescript typings. * Made readme more accurate based on being able to retrieve failure from the iOS API. * Let formatter run, also now rejecting the promise when there's any error on deletion on Android.
This commit is contained in:
parent
384103860f
commit
7850dd538f
21
README.md
21
README.md
|
@ -73,6 +73,7 @@ On Android permission is required to read the external storage. Add below line t
|
|||
* [`saveToCameraRoll`](#savetocameraroll)
|
||||
* [`save`](#save)
|
||||
* [`getPhotos`](#getphotos)
|
||||
* [`deletePhotos`](#deletephotos)
|
||||
|
||||
---
|
||||
|
||||
|
@ -208,3 +209,23 @@ render() {
|
|||
);
|
||||
}
|
||||
```
|
||||
---
|
||||
### `deletePhotos()`
|
||||
|
||||
```javascript
|
||||
CameraRoll.deletePhotos([uri]);
|
||||
```
|
||||
|
||||
Requests deletion of photos in the camera roll.
|
||||
|
||||
On Android, the uri must be a local image or video URI, such as `"file:///sdcard/img.png"`.
|
||||
|
||||
On iOS, the uri can be any image URI (including local, remote asset-library and base64 data URIs) or a local video file URI. The user is presented with a dialog box that shows them the asset(s) and asks them to confirm deletion. This is not able to be bypassed as per Apple Developer guidelines.
|
||||
|
||||
Returns a Promise which will resolve when the deletion request is completed, or reject if there is a problem during the deletion. On iOS the user is able to cancel the deletion request, which causes a rejection, while on Android the rejection will be due to a system error.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| ---- | ---------------------- | -------- | ---------------------------------------------------------- |
|
||||
| uri | string | Yes | See above. |
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
package com.reactnativecommunity.cameraroll;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.database.Cursor;
|
||||
|
@ -61,6 +62,7 @@ public class CameraRollModule extends ReactContextBaseJavaModule {
|
|||
private static final String ERROR_UNABLE_TO_LOAD = "E_UNABLE_TO_LOAD";
|
||||
private static final String ERROR_UNABLE_TO_LOAD_PERMISSION = "E_UNABLE_TO_LOAD_PERMISSION";
|
||||
private static final String ERROR_UNABLE_TO_SAVE = "E_UNABLE_TO_SAVE";
|
||||
private static final String ERROR_UNABLE_TO_DELETE = "E_UNABLE_TO_DELETE";
|
||||
private static final String ERROR_UNABLE_TO_FILTER = "E_UNABLE_TO_FILTER";
|
||||
|
||||
private static final String ASSET_TYPE_PHOTOS = "Photos";
|
||||
|
@ -504,4 +506,79 @@ public class CameraRollModule extends ReactContextBaseJavaModule {
|
|||
node.putMap("location", location);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a set of images.
|
||||
*
|
||||
* @param uris array of file:// URIs of the images to delete
|
||||
* @param promise to be resolved
|
||||
*/
|
||||
@ReactMethod
|
||||
public void deletePhotos(ReadableArray uris, Promise promise) {
|
||||
if (uris.size() == 0) {
|
||||
promise.reject(ERROR_UNABLE_TO_DELETE, "Need at least one URI to delete");
|
||||
} else {
|
||||
new DeletePhotos(getReactApplicationContext(), uris, promise)
|
||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DeletePhotos extends GuardedAsyncTask<Void, Void> {
|
||||
|
||||
private final Context mContext;
|
||||
private final ReadableArray mUris;
|
||||
private final Promise mPromise;
|
||||
|
||||
public DeletePhotos(ReactContext context, ReadableArray uris, Promise promise) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
mUris = uris;
|
||||
mPromise = promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInBackgroundGuarded(Void... params) {
|
||||
ContentResolver resolver = mContext.getContentResolver();
|
||||
|
||||
// Set up the projection (we only need the ID)
|
||||
String[] projection = { MediaStore.Images.Media._ID };
|
||||
|
||||
// Match on the file path
|
||||
String innerWhere = "?";
|
||||
for (int i = 1; i < mUris.size(); i++) {
|
||||
innerWhere += ", ?";
|
||||
}
|
||||
|
||||
String selection = MediaStore.Images.Media.DATA + " IN (" + innerWhere + ")";
|
||||
// Query for the ID of the media matching the file path
|
||||
Uri queryUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
|
||||
String[] selectionArgs = new String[mUris.size()];
|
||||
for (int i = 0; i < mUris.size(); i++) {
|
||||
Uri uri = Uri.parse(mUris.getString(i));
|
||||
selectionArgs[i] = uri.getPath();
|
||||
}
|
||||
|
||||
Cursor cursor = resolver.query(queryUri, projection, selection, selectionArgs, null);
|
||||
int deletedCount = 0;
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
|
||||
Uri deleteUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
|
||||
|
||||
if (resolver.delete(deleteUri, null, null) == 1) {
|
||||
deletedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
|
||||
if (deletedCount == mUris.size()) {
|
||||
mPromise.resolve(null);
|
||||
} else {
|
||||
mPromise.reject(ERROR_UNABLE_TO_DELETE,
|
||||
"Could not delete all media, only deleted " + deletedCount + " photos.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -367,12 +367,17 @@ RCT_EXPORT_METHOD(deletePhotos:(NSArray<NSString *>*)assets
|
|||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
NSArray<NSURL *> *assets_ = [RCTConvert NSURLArray:assets];
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
||||
PHFetchResult<PHAsset *> *fetched =
|
||||
[PHAsset fetchAssetsWithALAssetURLs:assets_ options:nil];
|
||||
[PHAssetChangeRequest deleteAssets:fetched];
|
||||
NSMutableArray *convertedAssets = [NSMutableArray array];
|
||||
|
||||
for (NSString *asset in assets) {
|
||||
[convertedAssets addObject: [asset stringByReplacingOccurrencesOfString:@"ph://" withString:@""]];
|
||||
}
|
||||
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
||||
PHFetchResult<PHAsset *> *fetched =
|
||||
[PHAsset fetchAssetsWithLocalIdentifiers:convertedAssets options:nil];
|
||||
[PHAssetChangeRequest deleteAssets:fetched];
|
||||
}
|
||||
completionHandler:^(BOOL success, NSError *error) {
|
||||
if (success == YES) {
|
||||
resolve(@(success));
|
||||
|
|
|
@ -123,8 +123,13 @@ class CameraRoll {
|
|||
return this.saveToCameraRoll(tag, 'photo');
|
||||
}
|
||||
|
||||
static deletePhotos(photos: Array<string>) {
|
||||
return RNCCameraRoll.deletePhotos(photos);
|
||||
/**
|
||||
* On iOS: requests deletion of a set of photos from the camera roll.
|
||||
* On Android: Deletes a set of photos from the camera roll.
|
||||
*
|
||||
*/
|
||||
static deletePhotos(photoUris: Array<string>) {
|
||||
return RNCCameraRoll.deletePhotos(photoUris);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -71,11 +71,10 @@ declare namespace CameraRoll {
|
|||
function saveImageWithTag(tag: string): Promise<string>;
|
||||
|
||||
/**
|
||||
* Delete a photo from the camera roll or media library. photos is an array of photo uri's.
|
||||
* Delete a photo from the camera roll or media library. photoUris is an array of photo uri's.
|
||||
*/
|
||||
function deletePhotos(photos: Array<string>): void;
|
||||
// deletePhotos: (photos: Array<string>) => void;
|
||||
|
||||
function deletePhotos(photoUris: Array<string>): void;
|
||||
|
||||
/**
|
||||
* Saves the photo or video to the camera roll or photo library.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue