package io.fullstack.firestack;
import android.os.Environment;
import android.os.StatFs;
import android.content.Context;
import android.util.Log;
import java.util.Map;
import java.util.HashMap;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.FileNotFoundException;
import android.net.Uri;
import android.provider.MediaStore;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.ReactContext;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.storage.OnProgressListener;
import com.google.firebase.storage.OnPausedListener;
import com.google.firebase.FirebaseApp;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.UploadTask;
import com.google.firebase.storage.StorageMetadata;
import com.google.firebase.storage.StorageReference;
class FirestackStorage extends ReactContextBaseJavaModule {
private static final String TAG = "FirestackStorage";
private static final String DocumentDirectoryPath = "DOCUMENT_DIRECTORY_PATH";
private static final String ExternalDirectoryPath = "EXTERNAL_DIRECTORY_PATH";
private static final String ExternalStorageDirectoryPath = "EXTERNAL_STORAGE_DIRECTORY_PATH";
private static final String PicturesDirectoryPath = "PICTURES_DIRECTORY_PATH";
private static final String TemporaryDirectoryPath = "TEMPORARY_DIRECTORY_PATH";
private static final String CachesDirectoryPath = "CACHES_DIRECTORY_PATH";
private static final String DocumentDirectory = "DOCUMENT_DIRECTORY_PATH";
private static final String BundlePath = "MAIN_BUNDLE_PATH";
private static final String FileTypeRegular = "FILETYPE_REGULAR";
private static final String FileTypeDirectory = "FILETYPE_DIRECTORY";
private Context context;
private ReactContext mReactContext;
private FirebaseApp app;
public FirestackStorage(ReactApplicationContext reactContext) {
super(reactContext);
Log.d(TAG, "Attaching FirestackStorage");
this.context = reactContext;
mReactContext = reactContext;
Log.d(TAG, "New instance");
}
@Override
public String getName() {
return TAG;
}
@ReactMethod
public void downloadUrl(final String javascriptStorageBucket,
final String path,
final Callback callback) {
FirebaseStorage storage = FirebaseStorage.getInstance();
String storageBucket = storage.getApp().getOptions().getStorageBucket();
String storageUrl = "gs://"+storageBucket;
Log.d(TAG, "Storage url " + storageUrl + path);
final StorageReference storageRef = storage.getReferenceFromUrl(storageUrl);
final StorageReference fileRef = storageRef.child(path);
Task<Uri> downloadTask = fileRef.getDownloadUrl();
downloadTask.addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(Uri uri) {
final WritableMap res = Arguments.createMap();
res.putString("status", "success");
res.putString("bucket", storageRef.getBucket());
res.putString("fullPath", uri.toString());
res.putString("path", uri.getPath());
res.putString("url", uri.toString());
fileRef.getMetadata()
.addOnSuccessListener(new OnSuccessListener<StorageMetadata>() {
@Override
public void onSuccess(final StorageMetadata storageMetadata) {
Log.d(TAG, "getMetadata success " + storageMetadata);
res.putString("name", storageMetadata.getName());
WritableMap metadata = Arguments.createMap();
metadata.putString("getBucket", storageMetadata.getBucket());
metadata.putString("getName", storageMetadata.getName());
metadata.putDouble("sizeBytes", storageMetadata.getSizeBytes());
metadata.putDouble("created_at", storageMetadata.getCreationTimeMillis());
metadata.putDouble("updated_at", storageMetadata.getUpdatedTimeMillis());
metadata.putString("md5hash", storageMetadata.getMd5Hash());
metadata.putString("encoding", storageMetadata.getContentEncoding());
res.putString("url", storageMetadata.getDownloadUrl().toString());
res.putMap("metadata", metadata);
callback.invoke(null, res);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
Log.e(TAG, "Failure in download " + exception);
callback.invoke(makeErrorPayload(1, exception));
}
});
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
Log.e(TAG, "Failed to download file " + exception.getMessage());
WritableMap err = Arguments.createMap();
err.putString("status", "error");
err.putString("description", exception.getLocalizedMessage());
callback.invoke(err);
}
});
}
// STORAGE
@ReactMethod
public void uploadFile(final String urlStr, final String name, final String filepath, final ReadableMap metadata, final Callback callback) {
FirebaseStorage storage = FirebaseStorage.getInstance();
StorageReference storageRef = urlStr!=null ? storage.getReferenceFromUrl(urlStr) : storage.getReference();
StorageReference fileRef = storageRef.child(name);
Log.i(TAG, "From file: " + filepath + " to " + urlStr + " with name " + name);
try {
// InputStream stream = new FileInputStream(new File(filepath));
Uri file = Uri.fromFile(new File(filepath));
StorageMetadata.Builder metadataBuilder = new StorageMetadata.Builder();
Map<String, Object> m = FirestackUtils.recursivelyDeconstructReadableMap(metadata);
StorageMetadata md = metadataBuilder.build();
UploadTask uploadTask = fileRef.putFile(file, md);
// UploadTask uploadTask = fileRef.putStream(stream, md);
// Register observers to listen for when the download is done or if it fails
uploadTask.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
// Handle unsuccessful uploads
Log.e(TAG, "Failed to upload file " + exception.getMessage());
WritableMap err = Arguments.createMap();
err.putString("description", exception.getLocalizedMessage());
callback.invoke(err);
}
}).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "Successfully uploaded file " + taskSnapshot);
// taskSnapshot.getMetadata() contains file metadata such as size, content-type, and download URL.
WritableMap resp = getDownloadData(taskSnapshot);
// NSDictionary *props = @{
// @"fullPath": ref.fullPath,
// @"bucket": ref.bucket,
// @"name": ref.name,
// @"metadata": [snapshot.metadata dictionaryRepresentation]
// };
callback.invoke(null, resp);
}
})
.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
@Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
double totalBytes = taskSnapshot.getTotalByteCount();
double bytesTransferred = taskSnapshot.getBytesTransferred();
double progress = (100.0 * bytesTransferred) / totalBytes;
System.out.println("Transferred " + bytesTransferred + "/" + totalBytes + "("+progress + "% complete)");
if (progress >= 0) {
WritableMap data = Arguments.createMap();
data.putString("eventName", "upload_progress");
data.putDouble("progress", progress);
FirestackUtils.sendEvent(mReactContext, "upload_progress", data);
}
}
}).addOnPausedListener(new OnPausedListener<UploadTask.TaskSnapshot>() {
@Override
public void onPaused(UploadTask.TaskSnapshot taskSnapshot) {
System.out.println("Upload is paused");
StorageMetadata d = taskSnapshot.getMetadata();
String bucket = d.getBucket();
WritableMap data = Arguments.createMap();
data.putString("eventName", "upload_paused");
data.putString("ref", bucket);
FirestackUtils.sendEvent(mReactContext, "upload_paused", data);
}
});
}
catch (Exception ex) {
callback.invoke(makeErrorPayload(2, ex));
}
}
@ReactMethod
public void getRealPathFromURI(final String uri, final Callback callback) {
try {
Context context = getReactApplicationContext();
String [] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = context.getContentResolver().query(Uri.parse(uri), proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String path = cursor.getString(column_index);
cursor.close();
callback.invoke(null, path);
} catch (Exception ex) {
ex.printStackTrace();
callback.invoke(makeErrorPayload(1, ex));
}
}
private WritableMap getDownloadData(final UploadTask.TaskSnapshot taskSnapshot) {
Uri downloadUrl = taskSnapshot.getDownloadUrl();
StorageMetadata d = taskSnapshot.getMetadata();
WritableMap resp = Arguments.createMap();
resp.putString("downloadUrl", downloadUrl.toString());
resp.putString("fullPath", d.getPath());
resp.putString("bucket", d.getBucket());
resp.putString("name", d.getName());
WritableMap metadataObj = Arguments.createMap();
metadataObj.putString("cacheControl", d.getCacheControl());
metadataObj.putString("contentDisposition", d.getContentDisposition());
metadataObj.putString("contentType", d.getContentType());
resp.putMap("metadata", metadataObj);
return resp;
}
private WritableMap makeErrorPayload(double code, Exception ex) {
WritableMap error = Arguments.createMap();
error.putDouble("code", code);
error.putString("message", ex.getMessage());
return error;
}
// Comes almost directory from react-native-fs
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put(DocumentDirectory, 0);
constants.put(DocumentDirectoryPath, this.getReactApplicationContext().getFilesDir().getAbsolutePath());
constants.put(TemporaryDirectoryPath, null);
constants.put(PicturesDirectoryPath, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath());
constants.put(CachesDirectoryPath, this.getReactApplicationContext().getCacheDir().getAbsolutePath());
constants.put(FileTypeRegular, 0);
constants.put(FileTypeDirectory, 1);
constants.put(BundlePath, null);
File externalStorageDirectory = Environment.getExternalStorageDirectory();
if (externalStorageDirectory != null) {
constants.put(ExternalStorageDirectoryPath, externalStorageDirectory.getAbsolutePath());
} else {
constants.put(ExternalStorageDirectoryPath, null);
}
File externalDirectory = this.getReactApplicationContext().getExternalFilesDir(null);
if (externalDirectory != null) {
constants.put(ExternalDirectoryPath, externalDirectory.getAbsolutePath());
} else {
constants.put(ExternalDirectoryPath, null);
}
return constants;
}
}