[android][storage] storage implementation using react native promises

This commit is contained in:
Salakar 2017-03-29 17:50:25 +01:00
parent b78ebc7de2
commit e16b788ad7
1 changed files with 331 additions and 164 deletions

View File

@ -7,6 +7,7 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
@ -15,13 +16,12 @@ import android.database.Cursor;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactContextBaseJavaModule;
@ -29,8 +29,6 @@ import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener; import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.storage.StorageException; import com.google.firebase.storage.StorageException;
import com.google.firebase.storage.StorageTask; import com.google.firebase.storage.StorageTask;
import com.google.firebase.storage.StreamDownloadTask; import com.google.firebase.storage.StreamDownloadTask;
@ -54,21 +52,17 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
private static final String PicturesDirectoryPath = "PICTURES_DIRECTORY_PATH"; private static final String PicturesDirectoryPath = "PICTURES_DIRECTORY_PATH";
private static final String TemporaryDirectoryPath = "TEMPORARY_DIRECTORY_PATH"; private static final String TemporaryDirectoryPath = "TEMPORARY_DIRECTORY_PATH";
private static final String CachesDirectoryPath = "CACHES_DIRECTORY_PATH"; private static final String CachesDirectoryPath = "CACHES_DIRECTORY_PATH";
private static final String DocumentDirectory = "DOCUMENT_DIRECTORY_PATH";
private static final String FileTypeRegular = "FILETYPE_REGULAR"; private static final String FileTypeRegular = "FILETYPE_REGULAR";
private static final String FileTypeDirectory = "FILETYPE_DIRECTORY"; private static final String FileTypeDirectory = "FILETYPE_DIRECTORY";
private static final String STORAGE_EVENT = "storage_event"; private static final String STORAGE_EVENT = "storage_event";
private static final String STORAGE_ERROR = "storage_error";
private static final String STORAGE_STATE_CHANGED = "state_changed"; private static final String STORAGE_STATE_CHANGED = "state_changed";
private static final String STORAGE_UPLOAD_SUCCESS = "upload_success"; private static final String STORAGE_UPLOAD_SUCCESS = "upload_success";
private static final String STORAGE_UPLOAD_FAILURE = "upload_failure"; private static final String STORAGE_UPLOAD_FAILURE = "upload_failure";
private static final String STORAGE_DOWNLOAD_SUCCESS = "download_success"; private static final String STORAGE_DOWNLOAD_SUCCESS = "download_success";
private static final String STORAGE_DOWNLOAD_FAILURE = "download_failure"; private static final String STORAGE_DOWNLOAD_FAILURE = "download_failure";
private ReactContext mReactContext;
public RNFirebaseStorage(ReactApplicationContext reactContext) { public RNFirebaseStorage(ReactApplicationContext reactContext) {
super(reactContext); super(reactContext);
@ -81,35 +75,65 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
} }
/**
* Check if we can write to storage, usually false if no permission set on manifest
*
* @return
*/
public boolean isExternalStorageWritable() { public boolean isExternalStorageWritable() {
boolean mExternalStorageAvailable;
boolean mExternalStorageWritable;
String state = Environment.getExternalStorageState(); String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state);
if (Environment.MEDIA_MOUNTED.equals(state)) {
// we can read and write the media
mExternalStorageAvailable = mExternalStorageWritable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// we can only read the media
mExternalStorageAvailable = true;
mExternalStorageWritable = false;
} else {
// something else is wrong. It may be one of many other states, but all we need
// to know is we can neither read nor write
mExternalStorageAvailable = mExternalStorageWritable = false;
}
return mExternalStorageAvailable && mExternalStorageWritable;
} }
/**
* delete
*
* @param path
* @param promise
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete
*/
@ReactMethod @ReactMethod
public void delete(final String path, public void delete(final String path, final Promise promise) {
final Callback callback) {
StorageReference reference = this.getReference(path); StorageReference reference = this.getReference(path);
reference.delete().addOnSuccessListener(new OnSuccessListener<Void>() { reference.delete().addOnSuccessListener(new OnSuccessListener<Void>() {
@Override @Override
public void onSuccess(Void aVoid) { public void onSuccess(Void aVoid) {
WritableMap data = Arguments.createMap(); promise.resolve(null);
data.putString("success", "success");
data.putString("path", path);
callback.invoke(null, data);
} }
}).addOnFailureListener(new OnFailureListener() { }).addOnFailureListener(new OnFailureListener() {
@Override @Override
public void onFailure(Exception exception) { public void onFailure(@NonNull Exception exception) {
callback.invoke(makeErrorPayload(1, exception)); promiseRejectStorageException(promise, exception);
} }
}); });
} }
/**
* getDownloadURL
*
* @param path
* @param promise
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getDownloadURL
*/
@ReactMethod @ReactMethod
public void getDownloadURL(final String path, public void getDownloadURL(final String path, final Promise promise) {
final Callback callback) { Log.d(TAG, "getDownloadURL path " + path);
Log.d(TAG, "Download url for remote path: " + path);
final StorageReference reference = this.getReference(path); final StorageReference reference = this.getReference(path);
Task<Uri> downloadTask = reference.getDownloadUrl(); Task<Uri> downloadTask = reference.getDownloadUrl();
@ -117,70 +141,88 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
.addOnSuccessListener(new OnSuccessListener<Uri>() { .addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override @Override
public void onSuccess(Uri uri) { public void onSuccess(Uri uri) {
callback.invoke(null, uri.toString()); promise.resolve(uri.toString());
} }
}) })
.addOnFailureListener(new OnFailureListener() { .addOnFailureListener(new OnFailureListener() {
@Override @Override
public void onFailure(@NonNull Exception exception) { public void onFailure(@NonNull Exception exception) {
callback.invoke(makeErrorPayload(1, exception)); promiseRejectStorageException(promise, exception);
} }
}); });
} }
/**
* getMetadata
*
* @param path
* @param promise
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getMetadata
*/
@ReactMethod @ReactMethod
public void getMetadata(final String path, public void getMetadata(final String path, final Promise promise) {
final Callback callback) {
StorageReference reference = this.getReference(path); StorageReference reference = this.getReference(path);
reference.getMetadata().addOnSuccessListener(new OnSuccessListener<StorageMetadata>() { reference.getMetadata().addOnSuccessListener(new OnSuccessListener<StorageMetadata>() {
@Override @Override
public void onSuccess(StorageMetadata storageMetadata) { public void onSuccess(StorageMetadata storageMetadata) {
WritableMap data = getMetadataAsMap(storageMetadata); promise.resolve(getMetadataAsMap(storageMetadata));
callback.invoke(null, data);
} }
}).addOnFailureListener(new OnFailureListener() { }).addOnFailureListener(new OnFailureListener() {
@Override @Override
public void onFailure(Exception exception) { public void onFailure(@NonNull Exception exception) {
callback.invoke(makeErrorPayload(1, exception)); promiseRejectStorageException(promise, exception);
} }
}); });
} }
/**
* updateMetadata
*
* @param path
* @param metadata
* @param promise
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#updateMetadata
*/
@ReactMethod @ReactMethod
public void updateMetadata(final String path, public void updateMetadata(final String path, final ReadableMap metadata, final Promise promise) {
final ReadableMap metadata,
final Callback callback) {
StorageReference reference = this.getReference(path); StorageReference reference = this.getReference(path);
StorageMetadata md = buildMetadataFromMap(metadata); StorageMetadata md = buildMetadataFromMap(metadata);
reference.updateMetadata(md).addOnSuccessListener(new OnSuccessListener<StorageMetadata>() { reference.updateMetadata(md).addOnSuccessListener(new OnSuccessListener<StorageMetadata>() {
@Override @Override
public void onSuccess(StorageMetadata storageMetadata) { public void onSuccess(StorageMetadata storageMetadata) {
WritableMap data = getMetadataAsMap(storageMetadata); WritableMap data = getMetadataAsMap(storageMetadata);
callback.invoke(null, data); promise.resolve(data);
} }
}).addOnFailureListener(new OnFailureListener() { }).addOnFailureListener(new OnFailureListener() {
@Override @Override
public void onFailure(Exception exception) { public void onFailure(@NonNull Exception exception) {
callback.invoke(makeErrorPayload(1, exception)); promiseRejectStorageException(promise, exception);
} }
}); });
} }
/**
* downloadFile
*
* @param path
* @param localPath
* @param promise
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#downloadFile
*/
@ReactMethod @ReactMethod
public void downloadFile(final String path, public void downloadFile(final String path, final String localPath, final Promise promise) {
final String localPath,
final Callback callback) {
if (!isExternalStorageWritable()) { if (!isExternalStorageWritable()) {
Log.w(TAG, "downloadFile failed: external storage not writable"); promise.reject(
WritableMap error = Arguments.createMap(); "storage/invalid-device-file-path",
final int errorCode = 1; "The specified device file path is invalid or is restricted."
error.putDouble("code", errorCode); );
error.putString("description", "downloadFile failed: external storage not writable");
callback.invoke(error);
return; return;
} }
Log.d(TAG, "downloadFile from remote path: " + path);
Log.d(TAG, "downloadFile path: " + path);
StorageReference reference = this.getReference(path); StorageReference reference = this.getReference(path);
reference.getStream(new StreamDownloadTask.StreamProcessor() { reference.getStream(new StreamDownloadTask.StreamProcessor() {
@ -190,56 +232,101 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
String pathMinusFileName = indexOfLastSlash > 0 ? localPath.substring(0, indexOfLastSlash) + "/" : "/"; String pathMinusFileName = indexOfLastSlash > 0 ? localPath.substring(0, indexOfLastSlash) + "/" : "/";
String filename = indexOfLastSlash > 0 ? localPath.substring(indexOfLastSlash + 1) : localPath; String filename = indexOfLastSlash > 0 ? localPath.substring(indexOfLastSlash + 1) : localPath;
File fileWithJustPath = new File(pathMinusFileName); File fileWithJustPath = new File(pathMinusFileName);
fileWithJustPath.mkdirs();
// directoriesCreated assignment for not consumed warning
Boolean directoriesCreated = fileWithJustPath.mkdirs();
File fileWithFullPath = new File(pathMinusFileName, filename); File fileWithFullPath = new File(pathMinusFileName, filename);
FileOutputStream output = new FileOutputStream(fileWithFullPath); FileOutputStream output = new FileOutputStream(fileWithFullPath);
int bufferSize = 1024; int bufferSize = 1024;
byte[] buffer = new byte[bufferSize]; byte[] buffer = new byte[bufferSize];
int len = 0;
int len;
while ((len = inputStream.read(buffer)) != -1) { while ((len = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, len); output.write(buffer, 0, len);
} }
output.close(); output.close();
} }
}).addOnProgressListener(new OnProgressListener<StreamDownloadTask.TaskSnapshot>() { }).addOnProgressListener(new OnProgressListener<StreamDownloadTask.TaskSnapshot>() {
@Override @Override
public void onProgress(StreamDownloadTask.TaskSnapshot taskSnapshot) { public void onProgress(StreamDownloadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "Got download progress " + taskSnapshot); Log.d(TAG, "downloadFile progress " + taskSnapshot);
WritableMap event = getDownloadTaskAsMap(taskSnapshot); WritableMap event = getDownloadTaskAsMap(taskSnapshot);
handleStorageEvent(STORAGE_STATE_CHANGED, path, event); sendJSEvent(STORAGE_STATE_CHANGED, path, event);
} }
}).addOnPausedListener(new OnPausedListener<StreamDownloadTask.TaskSnapshot>() { }).addOnPausedListener(new OnPausedListener<StreamDownloadTask.TaskSnapshot>() {
@Override @Override
public void onPaused(StreamDownloadTask.TaskSnapshot taskSnapshot) { public void onPaused(StreamDownloadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "Download is paused " + taskSnapshot); Log.d(TAG, "downloadFile paused " + taskSnapshot);
WritableMap event = getDownloadTaskAsMap(taskSnapshot); WritableMap event = getDownloadTaskAsMap(taskSnapshot);
handleStorageEvent(STORAGE_STATE_CHANGED, path, event); sendJSEvent(STORAGE_STATE_CHANGED, path, event);
} }
}).addOnSuccessListener(new OnSuccessListener<StreamDownloadTask.TaskSnapshot>() { }).addOnSuccessListener(new OnSuccessListener<StreamDownloadTask.TaskSnapshot>() {
@Override @Override
public void onSuccess(StreamDownloadTask.TaskSnapshot taskSnapshot) { public void onSuccess(StreamDownloadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "Successfully downloaded file " + taskSnapshot); Log.d(TAG, "downloadFile success" + taskSnapshot);
WritableMap resp = getDownloadTaskAsMap(taskSnapshot); WritableMap resp = getDownloadTaskAsMap(taskSnapshot);
handleStorageEvent(STORAGE_DOWNLOAD_SUCCESS, path, resp); sendJSEvent(STORAGE_DOWNLOAD_SUCCESS, path, resp);
//TODO: A little hacky, but otherwise throws a not consumed exception
resp = getDownloadTaskAsMap(taskSnapshot); resp = getDownloadTaskAsMap(taskSnapshot);
callback.invoke(null, resp); promise.resolve(resp);
} }
}).addOnFailureListener(new OnFailureListener() { }).addOnFailureListener(new OnFailureListener() {
@Override @Override
public void onFailure(@NonNull Exception exception) { public void onFailure(@NonNull Exception exception) {
Log.e(TAG, "Failed to download file " + exception.getMessage()); Log.e(TAG, "downloadFile failure " + exception.getMessage());
//TODO: JS Error event // TODO sendJS error event
callback.invoke(makeErrorPayload(1, exception)); promiseRejectStorageException(promise, exception);
} }
}); });
} }
/**
* setMaxDownloadRetryTime
*
* @param milliseconds
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxDownloadRetryTime
*/
@ReactMethod @ReactMethod
public void putFile(final String path, final String localPath, final ReadableMap metadata, final Callback callback) { public void setMaxDownloadRetryTime(final double milliseconds) {
FirebaseStorage.getInstance().setMaxDownloadRetryTimeMillis((long) milliseconds);
}
/**
* setMaxOperationRetryTime
*
* @param milliseconds
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxOperationRetryTime
*/
@ReactMethod
public void setMaxOperationRetryTime(final double milliseconds) {
FirebaseStorage.getInstance().setMaxOperationRetryTimeMillis((long) milliseconds);
}
/**
* setMaxUploadRetryTime
*
* @param milliseconds
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxUploadRetryTime
*/
@ReactMethod
public void setMaxUploadRetryTime(final double milliseconds) {
FirebaseStorage.getInstance().setMaxUploadRetryTimeMillis((long) milliseconds);
}
/**
* putFile
*
* @param path
* @param localPath
* @param metadata
* @param promise
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#putFile
*/
@ReactMethod
public void putFile(final String path, final String localPath, final ReadableMap metadata, final Promise promise) {
StorageReference reference = this.getReference(path); StorageReference reference = this.getReference(path);
Log.i(TAG, "Upload file: " + localPath + " to " + path); Log.i(TAG, "putFile: " + localPath + " to " + path);
try { try {
Uri file; Uri file;
@ -259,60 +346,46 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
@Override @Override
public void onFailure(@NonNull Exception exception) { public void onFailure(@NonNull Exception exception) {
// handle unsuccessful uploads // handle unsuccessful uploads
Log.e(TAG, "Failed to upload file " + exception.getMessage()); Log.e(TAG, "putFile failure " + exception.getMessage());
//TODO: JS Error event // TODO sendJS error event
callback.invoke(makeErrorPayload(1, exception)); promiseRejectStorageException(promise, exception);
} }
}) })
.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { .addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override @Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "Successfully uploaded file " + taskSnapshot); Log.d(TAG, "putFile success " + taskSnapshot);
WritableMap resp = getUploadTaskAsMap(taskSnapshot); WritableMap resp = getUploadTaskAsMap(taskSnapshot);
handleStorageEvent(STORAGE_UPLOAD_SUCCESS, path, resp); sendJSEvent(STORAGE_UPLOAD_SUCCESS, path, resp);
//TODO: A little hacky, but otherwise throws a not consumed exception
resp = getUploadTaskAsMap(taskSnapshot); resp = getUploadTaskAsMap(taskSnapshot);
callback.invoke(null, resp); promise.resolve(resp);
} }
}) })
.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() { .addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
@Override @Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot) { public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "Got upload progress " + taskSnapshot); Log.d(TAG, "putFile progress " + taskSnapshot);
WritableMap event = getUploadTaskAsMap(taskSnapshot); sendJSEvent(STORAGE_STATE_CHANGED, path, getUploadTaskAsMap(taskSnapshot));
handleStorageEvent(STORAGE_STATE_CHANGED, path, event);
} }
}) })
.addOnPausedListener(new OnPausedListener<UploadTask.TaskSnapshot>() { .addOnPausedListener(new OnPausedListener<UploadTask.TaskSnapshot>() {
@Override @Override
public void onPaused(UploadTask.TaskSnapshot taskSnapshot) { public void onPaused(UploadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "Upload is paused " + taskSnapshot); Log.d(TAG, "putFile paused " + taskSnapshot);
WritableMap event = getUploadTaskAsMap(taskSnapshot); WritableMap event = getUploadTaskAsMap(taskSnapshot);
handleStorageEvent(STORAGE_STATE_CHANGED, path, event); sendJSEvent(STORAGE_STATE_CHANGED, path, event);
} }
}); });
} catch (Exception ex) { } catch (Exception exception) {
final int errorCode = 2; promiseRejectStorageException(promise, exception);
callback.invoke(makeErrorPayload(errorCode, ex));
} }
} }
//Firebase.Storage methods /**
@ReactMethod * Internal helper to detect if ref is from url or a path.
public void setMaxDownloadRetryTime(final double milliseconds) { * @param path
FirebaseStorage.getInstance().setMaxDownloadRetryTimeMillis((long) milliseconds); * @return
} */
@ReactMethod
public void setMaxOperationRetryTime(final double milliseconds) {
FirebaseStorage.getInstance().setMaxOperationRetryTimeMillis((long) milliseconds);
}
@ReactMethod
public void setMaxUploadRetryTime(final double milliseconds) {
FirebaseStorage.getInstance().setMaxUploadRetryTimeMillis((long) milliseconds);
}
private StorageReference getReference(String path) { private StorageReference getReference(String path) {
if (path.startsWith("url::")) { if (path.startsWith("url::")) {
String url = path.substring(5); String url = path.substring(5);
@ -322,6 +395,32 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
} }
} }
/**
* Internal helper to convert content:// uri's to a real path
* @param uri
* @return
*/
private String getRealPathFromURI(final String uri) {
Cursor cursor = null;
try {
String[] proj = {MediaStore.Images.Media.DATA};
cursor = getReactApplicationContext().getContentResolver().query(Uri.parse(uri), proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Converts a RN ReadableMap into a StorageMetadata instance
*
* @param metadata
* @return
*/
private StorageMetadata buildMetadataFromMap(ReadableMap metadata) { private StorageMetadata buildMetadataFromMap(ReadableMap metadata) {
StorageMetadata.Builder metadataBuilder = new StorageMetadata.Builder(); StorageMetadata.Builder metadataBuilder = new StorageMetadata.Builder();
Map<String, Object> m = Utils.recursivelyDeconstructReadableMap(metadata); Map<String, Object> m = Utils.recursivelyDeconstructReadableMap(metadata);
@ -333,6 +432,55 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
return metadataBuilder.build(); return metadataBuilder.build();
} }
/**
* Convert an download task snapshot to a RN WritableMAP
*
* @param taskSnapshot
* @return
*/
private WritableMap getDownloadTaskAsMap(final StreamDownloadTask.TaskSnapshot taskSnapshot) {
WritableMap resp = Arguments.createMap();
resp.putDouble("bytesTransferred", taskSnapshot.getBytesTransferred());
resp.putString("ref", taskSnapshot.getStorage().getPath());
resp.putString("state", this.getTaskStatus(taskSnapshot.getTask()));
resp.putDouble("totalBytes", taskSnapshot.getTotalByteCount());
return resp;
}
/**
* Convert an upload task snapshot to a RN WritableMAP
*
* @param taskSnapshot
* @return
*/
private WritableMap getUploadTaskAsMap(UploadTask.TaskSnapshot taskSnapshot) {
WritableMap resp = Arguments.createMap();
if (taskSnapshot != null) {
resp.putDouble("bytesTransferred", taskSnapshot.getBytesTransferred());
resp.putString("downloadUrl", taskSnapshot.getDownloadUrl() != null ? taskSnapshot.getDownloadUrl().toString() : null);
StorageMetadata d = taskSnapshot.getMetadata();
if (d != null) {
WritableMap metadata = getMetadataAsMap(d);
resp.putMap("metadata", metadata);
}
resp.putString("ref", taskSnapshot.getStorage().getPath());
resp.putString("state", this.getTaskStatus(taskSnapshot.getTask()));
resp.putDouble("totalBytes", taskSnapshot.getTotalByteCount());
}
return resp;
}
/**
* Converts storageMetadata into a map
*
* @param storageMetadata
* @return
*/
private WritableMap getMetadataAsMap(StorageMetadata storageMetadata) { private WritableMap getMetadataAsMap(StorageMetadata storageMetadata) {
WritableMap metadata = Arguments.createMap(); WritableMap metadata = Arguments.createMap();
metadata.putString("bucket", storageMetadata.getBucket()); metadata.putString("bucket", storageMetadata.getBucket());
@ -351,9 +499,14 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
metadata.putString("contentType", storageMetadata.getContentType()); metadata.putString("contentType", storageMetadata.getContentType());
WritableArray downloadURLs = Arguments.createArray(); WritableArray downloadURLs = Arguments.createArray();
for (Uri uri : storageMetadata.getDownloadUrls()) { List<Uri> _downloadURLS = storageMetadata.getDownloadUrls();
downloadURLs.pushString(uri.getPath());
if (_downloadURLS != null) {
for (Uri uri : _downloadURLS) {
downloadURLs.pushString(uri.getPath());
}
} }
metadata.putArray("downloadURLs", downloadURLs); metadata.putArray("downloadURLs", downloadURLs);
WritableMap customMetadata = Arguments.createMap(); WritableMap customMetadata = Arguments.createMap();
@ -365,99 +518,113 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule {
return metadata; return metadata;
} }
private String getRealPathFromURI(final String uri) { /**
Cursor cursor = null; * Returns the task status as string
try { *
String[] proj = {MediaStore.Images.Media.DATA}; * @param task
cursor = getReactApplicationContext().getContentResolver().query(Uri.parse(uri), proj, null, null, null); * @return
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); */
cursor.moveToFirst();
return cursor.getString(column_index);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
private WritableMap getDownloadTaskAsMap(final StreamDownloadTask.TaskSnapshot taskSnapshot) {
WritableMap resp = Arguments.createMap();
resp.putDouble("bytesTransferred", taskSnapshot.getBytesTransferred());
resp.putString("ref", taskSnapshot.getStorage().getPath());
resp.putString("state", this.getTaskStatus(taskSnapshot.getTask()));
resp.putDouble("totalBytes", taskSnapshot.getTotalByteCount());
return resp;
}
private WritableMap getUploadTaskAsMap(final UploadTask.TaskSnapshot taskSnapshot) {
StorageMetadata d = taskSnapshot.getMetadata();
WritableMap resp = Arguments.createMap();
resp.putDouble("bytesTransferred", taskSnapshot.getBytesTransferred());
resp.putString("downloadUrl", taskSnapshot.getDownloadUrl() != null ? taskSnapshot.getDownloadUrl().toString() : null);
resp.putString("ref", taskSnapshot.getStorage().getPath());
resp.putString("state", this.getTaskStatus(taskSnapshot.getTask()));
resp.putDouble("totalBytes", taskSnapshot.getTotalByteCount());
if (taskSnapshot.getMetadata() != null) {
WritableMap metadata = getMetadataAsMap(taskSnapshot.getMetadata());
resp.putMap("metadata", metadata);
}
return resp;
}
private String getTaskStatus(StorageTask<?> task) { private String getTaskStatus(StorageTask<?> task) {
if (task.isInProgress()) { if (task.isInProgress()) {
return "RUNNING"; return "running";
} else if (task.isPaused()) { } else if (task.isPaused()) {
return "PAUSED"; return "paused";
} else if (task.isSuccessful() || task.isComplete()) { } else if (task.isSuccessful() || task.isComplete()) {
return "SUCCESS"; return "success";
} else if (task.isCanceled()) { } else if (task.isCanceled()) {
return "CANCELLED"; return "cancelled";
} else if (task.getException() != null) { } else if (task.getException() != null) {
return "ERROR"; return "error";
} else { } else {
return "UNKNOWN"; return "unknown";
} }
} }
private void handleStorageEvent(final String name, final String path, WritableMap body) { /**
WritableMap evt = Arguments.createMap(); * @param name
evt.putString("eventName", name); * @param path
evt.putString("path", path); * @param body
evt.putMap("body", body); */
private void sendJSEvent(final String name, final String path, WritableMap body) {
WritableMap event = Arguments.createMap();
Utils.sendEvent(this.getReactApplicationContext(), STORAGE_EVENT, evt); event.putString("eventName", name);
event.putString("path", path);
event.putMap("body", body);
Utils.sendEvent(this.getReactApplicationContext(), STORAGE_EVENT, event);
} }
private void handleStorageError(final String path, final StorageException error) { /**
WritableMap body = Arguments.createMap(); * Reject a promise with a web sdk error code
body.putString("path", path); *
body.putString("message", error.getMessage()); * @param promise
* @param exception
*/
private void promiseRejectStorageException(Promise promise, Exception exception) {
String code = "storage/unknown";
String message = exception.getMessage();
WritableMap evt = Arguments.createMap(); try {
evt.putString("eventName", STORAGE_ERROR); StorageException storageException = (StorageException) exception;
evt.putMap("body", body);
Utils.sendEvent(this.getReactApplicationContext(), STORAGE_ERROR, evt); switch (storageException.getErrorCode()) {
case StorageException.ERROR_UNKNOWN:
code = "storage/unknown";
message = "An unknown error has occurred.";
break;
case StorageException.ERROR_OBJECT_NOT_FOUND:
code = "storage/object-not-found";
message = "No object exists at the desired reference.";
break;
case StorageException.ERROR_BUCKET_NOT_FOUND:
code = "storage/bucket-not-found";
message = "No bucket is configured for Firebase Storage.";
break;
case StorageException.ERROR_PROJECT_NOT_FOUND:
code = "storage/project-not-found";
message = "No project is configured for Firebase Storage.";
break;
case StorageException.ERROR_QUOTA_EXCEEDED:
code = "storage/quota-exceeded";
message = "Quota on your Firebase Storage bucket has been exceeded.";
break;
case StorageException.ERROR_NOT_AUTHENTICATED:
code = "storage/unauthenticated";
message = "User is unauthenticated. Authenticate and try again.";
break;
case StorageException.ERROR_NOT_AUTHORIZED:
code = "storage/unauthorized";
message = "User is not authorized to perform the desired action.";
break;
case StorageException.ERROR_RETRY_LIMIT_EXCEEDED:
code = "storage/retry-limit-exceeded";
message = "The maximum time limit on an operation (upload, download, delete, etc.) has been exceeded.";
break;
case StorageException.ERROR_INVALID_CHECKSUM:
code = "storage/non-matching-checksum";
message = "File on the client does not match the checksum of the file received by the server.";
break;
case StorageException.ERROR_CANCELED:
code = "storage/cancelled";
message = "User cancelled the operation.";
break;
}
} finally {
promise.reject(code, message, exception);
}
} }
private WritableMap makeErrorPayload(double code, Exception ex) { /**
WritableMap error = Arguments.createMap(); * Constants bootstrapped on react native app boot
error.putDouble("code", code); * e.g. firebase.storage.Native.DOCUMENT_DIRECTORY_PATH
error.putString("message", ex.getMessage()); *
return error; * @return
} */
@Override @Override
public Map<String, Object> getConstants() { public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>(); final Map<String, Object> constants = new HashMap<>();
constants.put(DocumentDirectory, 0);
constants.put(DocumentDirectoryPath, this.getReactApplicationContext().getFilesDir().getAbsolutePath()); constants.put(DocumentDirectoryPath, this.getReactApplicationContext().getFilesDir().getAbsolutePath());
constants.put(TemporaryDirectoryPath, null); constants.put(TemporaryDirectoryPath, null);
constants.put(PicturesDirectoryPath, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath()); constants.put(PicturesDirectoryPath, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath());