diff --git a/android/.editorconfig b/android/.editorconfig
new file mode 100644
index 00000000..0f099897
--- /dev/null
+++ b/android/.editorconfig
@@ -0,0 +1,10 @@
+# editorconfig.org
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 00000000..760a0a03
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,56 @@
+// START - required to allow working on this project inside Android Studio
+// YES, jcenter is required twice - it somehow tricks studio into compiling deps below
+// doesn't break anything anywhere else and projects using this lib work as normal
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.1.3'
+ }
+}
+// END
+
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.1"
+
+ defaultConfig {
+ minSdkVersion 16
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ multiDexEnabled true
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ }
+}
+
+// START - required to allow working on this project inside Android Studio
+// YES, jcenter is required twice - it somehow tricks studio into compiling deps below
+// doesn't break anything anywhere else and projects using this lib work as normal
+// you'll now have code completion/validation and all the other AS goodies.
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+// END
+
+dependencies {
+ compile 'com.facebook.react:react-native:0.20.+'
+ compile 'com.google.android.gms:play-services-base:9.8.0'
+ compile 'com.google.firebase:firebase-core:9.8.0'
+ compile 'com.google.firebase:firebase-config:9.8.0'
+ compile 'com.google.firebase:firebase-auth:9.8.0'
+ compile 'com.google.firebase:firebase-analytics:9.8.0'
+ compile 'com.google.firebase:firebase-database:9.8.0'
+ compile 'com.google.firebase:firebase-storage:9.8.0'
+ compile 'com.google.firebase:firebase-messaging:9.8.0'
+}
+
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..45a2feb8
--- /dev/null
+++ b/android/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/android/src/main/java/io/invertase/firebase/RNFirebaseInstanceIdService.java b/android/src/main/java/io/invertase/firebase/RNFirebaseInstanceIdService.java
new file mode 100644
index 00000000..1e852635
--- /dev/null
+++ b/android/src/main/java/io/invertase/firebase/RNFirebaseInstanceIdService.java
@@ -0,0 +1,29 @@
+package io.invertase.firebase;
+
+import android.util.Log;
+import android.os.Bundle;
+import android.content.Intent;
+
+import com.google.firebase.iid.FirebaseInstanceId;
+import com.google.firebase.iid.FirebaseInstanceIdService;
+
+import io.invertase.firebase.messaging.RNFirebaseMessaging;
+
+public class RNFirebaseInstanceIdService extends FirebaseInstanceIdService {
+
+ private static final String TAG = "FSInstanceIdService";
+
+ /**
+ *
+ */
+ @Override
+ public void onTokenRefresh() {
+ String refreshedToken = FirebaseInstanceId.getInstance().getToken();
+ Log.d(TAG, "Refreshed token: " + refreshedToken);
+ Intent i = new Intent(RNFirebaseMessaging.INTENT_NAME_TOKEN);
+ Bundle bundle = new Bundle();
+ bundle.putString("token", refreshedToken);
+ i.putExtras(bundle);
+ sendBroadcast(i);
+ }
+}
diff --git a/android/src/main/java/io/invertase/firebase/RNFirebaseMessagingService.java b/android/src/main/java/io/invertase/firebase/RNFirebaseMessagingService.java
new file mode 100644
index 00000000..fedc2212
--- /dev/null
+++ b/android/src/main/java/io/invertase/firebase/RNFirebaseMessagingService.java
@@ -0,0 +1,74 @@
+package io.invertase.firebase;
+
+import android.content.Intent;
+import android.util.Log;
+
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+import com.google.firebase.messaging.SendException;
+
+import io.invertase.firebase.messaging.RNFirebaseMessaging;
+
+public class RNFirebaseMessagingService extends FirebaseMessagingService {
+
+ private static final String TAG = "FSMessagingService";
+
+ @Override
+ public void onMessageReceived(RemoteMessage remoteMessage) {
+ Log.d(TAG, "Remote message received");
+ // debug
+ Log.d(TAG, "From: " + remoteMessage.getFrom());
+
+ if (remoteMessage.getData().size() > 0) {
+ Log.d(TAG, "Message data payload: " + remoteMessage.getData());
+ }
+
+ if (remoteMessage.getNotification() != null) {
+ Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
+ }
+
+ Intent i = new Intent(RNFirebaseMessaging.INTENT_NAME_NOTIFICATION);
+ i.putExtra("data", remoteMessage);
+ sendOrderedBroadcast(i, null);
+
+ }
+
+ @Override
+ public void onMessageSent(String msgId) {
+ // Called when an upstream message has been successfully sent to the GCM connection server.
+ Log.d(TAG, "upstream message has been successfully sent");
+ Intent i = new Intent(RNFirebaseMessaging.INTENT_NAME_SEND);
+ i.putExtra("msgId", msgId);
+ sendOrderedBroadcast(i, null);
+ }
+
+ @Override
+ public void onSendError(String msgId, Exception exception) {
+ // Called when there was an error sending an upstream message.
+ Log.d(TAG, "error sending an upstream message");
+ Intent i = new Intent(RNFirebaseMessaging.INTENT_NAME_SEND);
+ i.putExtra("msgId", msgId);
+ i.putExtra("hasError", true);
+ SendException sendException = (SendException) exception;
+ i.putExtra("errorCode", sendException.getErrorCode());
+ switch(sendException.getErrorCode()){
+ case SendException.ERROR_INVALID_PARAMETERS:
+ i.putExtra("errorMessage", "Message was sent with invalid parameters.");
+ break;
+ case SendException.ERROR_SIZE:
+ i.putExtra("errorMessage", "Message exceeded the maximum payload size.");
+ break;
+ case SendException.ERROR_TOO_MANY_MESSAGES:
+ i.putExtra("errorMessage", "App has too many pending messages so this one was dropped.");
+ break;
+ case SendException.ERROR_TTL_EXCEEDED:
+ i.putExtra("errorMessage", "Message time to live (TTL) was exceeded before the message could be sent.");
+ break;
+ case SendException.ERROR_UNKNOWN:
+ default:
+ i.putExtra("errorMessage", "Unknown error.");
+ break;
+ }
+ sendOrderedBroadcast(i, null);
+ }
+}
diff --git a/android/src/main/java/io/invertase/firebase/RNFirebaseModule.java b/android/src/main/java/io/invertase/firebase/RNFirebaseModule.java
new file mode 100644
index 00000000..17c1ec39
--- /dev/null
+++ b/android/src/main/java/io/invertase/firebase/RNFirebaseModule.java
@@ -0,0 +1,208 @@
+package io.invertase.firebase;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import android.util.Log;
+import android.content.Context;
+import android.support.annotation.Nullable;
+
+import com.facebook.react.bridge.Callback;
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.bridge.ReactMethod;
+import com.facebook.react.bridge.LifecycleEventListener;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GoogleApiAvailability;
+
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.FirebaseOptions;
+import com.google.firebase.database.ServerValue;
+
+interface KeySetterFn {
+ String setKeyOrDefault(String a, String b);
+}
+
+@SuppressWarnings("WeakerAccess")
+public class RNFirebaseModule extends ReactContextBaseJavaModule implements LifecycleEventListener {
+ private static final String TAG = "RNFirebase";
+ private FirebaseApp app;
+
+ public RNFirebaseModule(ReactApplicationContext reactContext) {
+ super(reactContext);
+ }
+
+ @Override
+ public String getName() {
+ return TAG;
+ }
+
+ private WritableMap getPlayServicesStatus() {
+ GoogleApiAvailability gapi = GoogleApiAvailability.getInstance();
+ final int status = gapi.isGooglePlayServicesAvailable(getReactApplicationContext());
+ WritableMap result = Arguments.createMap();
+ result.putInt("status", status);
+ if (status == ConnectionResult.SUCCESS) {
+ result.putBoolean("isAvailable", true);
+ } else {
+ result.putBoolean("isAvailable", false);
+ result.putBoolean("isUserResolvableError", gapi.isUserResolvableError(status));
+ result.putString("error", gapi.getErrorString(status));
+ }
+ return result;
+ }
+
+ @ReactMethod
+ public void configureWithOptions(final ReadableMap params, @Nullable final Callback onComplete) {
+ Log.i(TAG, "configureWithOptions");
+
+ FirebaseOptions.Builder builder = new FirebaseOptions.Builder();
+ FirebaseOptions defaultOptions = FirebaseOptions.fromResource(getReactApplicationContext().getBaseContext());
+
+ if (defaultOptions == null) {
+ defaultOptions = new FirebaseOptions.Builder().build();
+ }
+
+ KeySetterFn fn = new KeySetterFn() {
+ public String setKeyOrDefault(
+ final String key,
+ final String defaultValue) {
+ if (params.hasKey(key)) {
+ // User-set key
+ final String val = params.getString(key);
+ Log.d(TAG, "Setting " + key + " from params to: " + val);
+ return val;
+ } else if (defaultValue != null && !defaultValue.equals("")) {
+ Log.d(TAG, "Setting " + key + " from params to: " + defaultValue);
+ return defaultValue;
+ } else {
+ return null;
+ }
+ }
+ };
+
+ String val = fn.setKeyOrDefault("applicationId", defaultOptions.getApplicationId());
+ if (val != null) builder.setApplicationId(val);
+
+ val = fn.setKeyOrDefault("apiKey", defaultOptions.getApiKey());
+ if (val != null) builder.setApiKey(val);
+
+ val = fn.setKeyOrDefault("gcmSenderID", defaultOptions.getGcmSenderId());
+ if (val != null) builder.setGcmSenderId(val);
+
+ val = fn.setKeyOrDefault("storageBucket", defaultOptions.getStorageBucket());
+ if (val != null) builder.setStorageBucket(val);
+
+ val = fn.setKeyOrDefault("databaseURL", defaultOptions.getDatabaseUrl());
+ if (val != null) builder.setDatabaseUrl(val);
+
+ val = fn.setKeyOrDefault("databaseUrl", defaultOptions.getDatabaseUrl());
+ if (val != null) builder.setDatabaseUrl(val);
+
+ val = fn.setKeyOrDefault("clientId", defaultOptions.getApplicationId());
+ if (val != null) builder.setApplicationId(val);
+
+
+ // if (params.hasKey("applicationId")) {
+ // final String applicationId = params.getString("applicationId");
+ // Log.d(TAG, "Setting applicationId from params " + applicationId);
+ // builder.setApplicationId(applicationId);
+ // }
+ // if (params.hasKey("apiKey")) {
+ // final String apiKey = params.getString("apiKey");
+ // Log.d(TAG, "Setting API key from params " + apiKey);
+ // builder.setApiKey(apiKey);
+ // }
+ // if (params.hasKey("APIKey")) {
+ // final String apiKey = params.getString("APIKey");
+ // Log.d(TAG, "Setting API key from params " + apiKey);
+ // builder.setApiKey(apiKey);
+ // }
+ // if (params.hasKey("gcmSenderID")) {
+ // final String gcmSenderID = params.getString("gcmSenderID");
+ // Log.d(TAG, "Setting gcmSenderID from params " + gcmSenderID );
+ // builder.setGcmSenderId(gcmSenderID);
+ // }
+ // if (params.hasKey("storageBucket")) {
+ // final String storageBucket = params.getString("storageBucket");
+ // Log.d(TAG, "Setting storageBucket from params " + storageBucket);
+ // builder.setStorageBucket(storageBucket);
+ // }
+ // if (params.hasKey("databaseURL")) {
+ // final String databaseURL = params.getString("databaseURL");
+ // Log.d(TAG, "Setting databaseURL from params " + databaseURL);
+ // builder.setDatabaseUrl(databaseURL);
+ // }
+ // if (params.hasKey("clientID")) {
+ // final String clientID = params.getString("clientID");
+ // Log.d(TAG, "Setting clientID from params " + clientID);
+ // builder.setApplicationId(clientID);
+ // }
+
+ try {
+ Log.i(TAG, "Configuring app");
+ if (app == null) {
+ app = FirebaseApp.initializeApp(getReactApplicationContext().getBaseContext(), builder.build());
+ }
+ Log.i(TAG, "Configured");
+
+ WritableMap resp = Arguments.createMap();
+ resp.putString("msg", "success");
+ onComplete.invoke(null, resp);
+ } catch (Exception ex) {
+ Log.e(TAG, "ERROR configureWithOptions");
+ Log.e(TAG, ex.getMessage());
+
+ WritableMap resp = Arguments.createMap();
+ resp.putString("msg", ex.getMessage());
+
+ onComplete.invoke(resp);
+ }
+ }
+
+ @ReactMethod
+ public void serverValue(@Nullable final Callback onComplete) {
+ WritableMap timestampMap = Arguments.createMap();
+ for (Map.Entry entry : ServerValue.TIMESTAMP.entrySet()) {
+ timestampMap.putString(entry.getKey(), entry.getValue());
+ }
+
+ WritableMap map = Arguments.createMap();
+ map.putMap("TIMESTAMP", timestampMap);
+ if (onComplete != null) onComplete.invoke(null, map);
+ }
+
+ // Internal helpers
+ @Override
+ public void onHostResume() {
+ WritableMap params = Arguments.createMap();
+ params.putBoolean("isForground", true);
+ Utils.sendEvent(getReactApplicationContext(), "RNFirebaseAppState", params);
+ }
+
+ @Override
+ public void onHostPause() {
+ WritableMap params = Arguments.createMap();
+ params.putBoolean("isForground", false);
+ Utils.sendEvent(getReactApplicationContext(), "RNFirebaseAppState", params);
+ }
+
+ @Override
+ public void onHostDestroy() {
+
+ }
+
+ @Override
+ public Map getConstants() {
+ final Map constants = new HashMap<>();
+ constants.put("googleApiAvailability", getPlayServicesStatus());
+
+ // TODO remove once this has been moved on ios
+ constants.put("serverValueTimestamp", ServerValue.TIMESTAMP);
+ return constants;
+ }
+}
diff --git a/android/src/main/java/io/invertase/firebase/RNFirebasePackage.java b/android/src/main/java/io/invertase/firebase/RNFirebasePackage.java
new file mode 100644
index 00000000..e8bd891e
--- /dev/null
+++ b/android/src/main/java/io/invertase/firebase/RNFirebasePackage.java
@@ -0,0 +1,64 @@
+package io.invertase.firebase;
+
+import android.content.Context;
+
+import com.facebook.react.ReactPackage;
+import com.facebook.react.bridge.JavaScriptModule;
+import com.facebook.react.bridge.NativeModule;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.uimanager.UIManagerModule;
+import com.facebook.react.uimanager.ViewManager;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import io.invertase.firebase.auth.RNFirebaseAuth;
+import io.invertase.firebase.storage.RNFirebaseStorage;
+import io.invertase.firebase.database.RNFirebaseDatabase;
+import io.invertase.firebase.analytics.RNFirebaseAnalytics;
+import io.invertase.firebase.messaging.RNFirebaseMessaging;
+
+@SuppressWarnings("unused")
+public class RNFirebasePackage implements ReactPackage {
+ private Context mContext;
+
+ public RNFirebasePackage() {
+ }
+ /**
+ * @param reactContext react application context that can be used to create modules
+ * @return list of native modules to register with the newly created catalyst instance
+ */
+ @Override
+ public List createNativeModules(ReactApplicationContext reactContext) {
+ List modules = new ArrayList<>();
+ modules.add(new RNFirebaseModule(reactContext));
+ modules.add(new RNFirebaseAuth(reactContext));
+ modules.add(new RNFirebaseDatabase(reactContext));
+ modules.add(new RNFirebaseAnalytics(reactContext));
+ modules.add(new RNFirebaseStorage(reactContext));
+ modules.add(new RNFirebaseMessaging(reactContext));
+ return modules;
+ }
+
+ /**
+ * @return list of JS modules to register with the newly created catalyst instance.
+ *
+ * IMPORTANT: Note that only modules that needs to be accessible from the native code should be
+ * listed here. Also listing a native module here doesn't imply that the JS implementation of it
+ * will be automatically included in the JS bundle.
+ */
+ @Override
+ public List> createJSModules() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * @param reactContext
+ * @return a list of view managers that should be registered with {@link UIManagerModule}
+ */
+ @Override
+ public List createViewManagers(ReactApplicationContext reactContext) {
+ return Collections.emptyList();
+ }
+}
diff --git a/android/src/main/java/io/invertase/firebase/Utils.java b/android/src/main/java/io/invertase/firebase/Utils.java
new file mode 100644
index 00000000..e836e4e7
--- /dev/null
+++ b/android/src/main/java/io/invertase/firebase/Utils.java
@@ -0,0 +1,301 @@
+package io.invertase.firebase;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.Callback;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.WritableArray;
+import com.facebook.react.bridge.WritableNativeArray;
+import com.facebook.react.modules.core.DeviceEventManagerModule;
+
+import com.facebook.react.bridge.ReadableType;
+import com.facebook.react.bridge.ReadableArray;
+import com.google.firebase.database.DataSnapshot;
+import com.facebook.react.bridge.ReadableMapKeySetIterator;
+
+@SuppressWarnings("WeakerAccess")
+public class Utils {
+ private static final String TAG = "Utils";
+
+ // TODO NOTE
+ public static void todoNote(final String tag, final String name, final Callback callback) {
+ Log.e(tag, "The method " + name + " has not yet been implemented.");
+ Log.e(tag, "Feel free to contribute to finish the method in the source.");
+
+ WritableMap errorMap = Arguments.createMap();
+ errorMap.putString("error", "unimplemented");
+ callback.invoke(null, errorMap);
+ }
+
+ /**
+ * send a JS event
+ **/
+ public static void sendEvent(final ReactContext context, final String eventName, final WritableMap params) {
+ if (context != null) {
+ context
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
+ .emit(eventName, params);
+ } else {
+ Log.d(TAG, "Missing context - cannot send event!");
+ }
+ }
+
+ // snapshot
+ public static WritableMap dataSnapshotToMap(
+ String name,
+ String path,
+ String modifiersString,
+ DataSnapshot dataSnapshot
+ ) {
+ WritableMap data = Arguments.createMap();
+
+ data.putString("key", dataSnapshot.getKey());
+ data.putBoolean("exists", dataSnapshot.exists());
+ data.putBoolean("hasChildren", dataSnapshot.hasChildren());
+
+ data.putDouble("childrenCount", dataSnapshot.getChildrenCount());
+ if (!dataSnapshot.hasChildren()) {
+ Object value = dataSnapshot.getValue();
+ String type = value != null ? value.getClass().getName() : "";
+ switch (type) {
+ case "java.lang.Boolean":
+ data.putBoolean("value", (Boolean) value);
+ break;
+ case "java.lang.Long":
+ Long longVal = (Long) value;
+ data.putDouble("value", (double) longVal);
+ break;
+ case "java.lang.Double":
+ data.putDouble("value", (Double) value);
+ break;
+ case "java.lang.String":
+ data.putString("value", (String) value);
+ break;
+ default:
+ data.putString("value", null);
+ }
+ } else {
+ Object value = Utils.castSnapshotValue(dataSnapshot);
+ if (value instanceof WritableNativeArray) {
+ data.putArray("value", (WritableArray) value);
+ } else {
+ data.putMap("value", (WritableMap) value);
+ }
+ }
+
+ // Child keys
+ WritableArray childKeys = Utils.getChildKeys(dataSnapshot);
+ data.putArray("childKeys", childKeys);
+
+ Object priority = dataSnapshot.getPriority();
+ if (priority == null) {
+ data.putString("priority", null);
+ } else {
+ data.putString("priority", priority.toString());
+ }
+
+ WritableMap eventMap = Arguments.createMap();
+ eventMap.putString("eventName", name);
+ eventMap.putMap("snapshot", data);
+ eventMap.putString("path", path);
+ eventMap.putString("modifiersString", modifiersString);
+ return eventMap;
+ }
+
+ public static Any castSnapshotValue(DataSnapshot snapshot) {
+ if (snapshot.hasChildren()) {
+ if (isArray(snapshot)) {
+ return (Any) buildArray(snapshot);
+ } else {
+ return (Any) buildMap(snapshot);
+ }
+ } else {
+ if (snapshot.getValue() != null) {
+ String type = snapshot.getValue().getClass().getName();
+ switch (type) {
+ case "java.lang.Boolean":
+ return (Any) (snapshot.getValue());
+ case "java.lang.Long":
+ return (Any) (snapshot.getValue());
+ case "java.lang.Double":
+ return (Any) (snapshot.getValue());
+ case "java.lang.String":
+ return (Any) (snapshot.getValue());
+ default:
+ Log.w(TAG, "Invalid type: " + type);
+ return (Any) null;
+ }
+ }
+ return (Any) null;
+ }
+ }
+
+ private static boolean isArray(DataSnapshot snapshot) {
+ long expectedKey = 0;
+ for (DataSnapshot child : snapshot.getChildren()) {
+ try {
+ long key = Long.parseLong(child.getKey());
+ if (key == expectedKey) {
+ expectedKey++;
+ } else {
+ return false;
+ }
+ } catch (NumberFormatException ex) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static WritableArray buildArray(DataSnapshot snapshot) {
+ WritableArray array = Arguments.createArray();
+ for (DataSnapshot child : snapshot.getChildren()) {
+ Any castedChild = castSnapshotValue(child);
+ switch (castedChild.getClass().getName()) {
+ case "java.lang.Boolean":
+ array.pushBoolean((Boolean) castedChild);
+ break;
+ case "java.lang.Long":
+ Long longVal = (Long) castedChild;
+ array.pushDouble((double) longVal);
+ break;
+ case "java.lang.Double":
+ array.pushDouble((Double) castedChild);
+ break;
+ case "java.lang.String":
+ array.pushString((String) castedChild);
+ break;
+ case "com.facebook.react.bridge.WritableNativeMap":
+ array.pushMap((WritableMap) castedChild);
+ break;
+ case "com.facebook.react.bridge.WritableNativeArray":
+ array.pushArray((WritableArray) castedChild);
+ break;
+ default:
+ Log.w(TAG, "Invalid type: " + castedChild.getClass().getName());
+ break;
+ }
+ }
+ return array;
+ }
+
+ private static WritableMap buildMap(DataSnapshot snapshot) {
+ WritableMap map = Arguments.createMap();
+ for (DataSnapshot child : snapshot.getChildren()) {
+ Any castedChild = castSnapshotValue(child);
+
+ switch (castedChild.getClass().getName()) {
+ case "java.lang.Boolean":
+ map.putBoolean(child.getKey(), (Boolean) castedChild);
+ break;
+ case "java.lang.Long":
+ Long longVal = (Long) castedChild;
+ map.putDouble(child.getKey(), (double) longVal);
+ break;
+ case "java.lang.Double":
+ map.putDouble(child.getKey(), (Double) castedChild);
+ break;
+ case "java.lang.String":
+ map.putString(child.getKey(), (String) castedChild);
+ break;
+ case "com.facebook.react.bridge.WritableNativeMap":
+ map.putMap(child.getKey(), (WritableMap) castedChild);
+ break;
+ case "com.facebook.react.bridge.WritableNativeArray":
+ map.putArray(child.getKey(), (WritableArray) castedChild);
+ break;
+ default:
+ Log.w(TAG, "Invalid type: " + castedChild.getClass().getName());
+ break;
+ }
+ }
+ return map;
+ }
+
+ public static WritableArray getChildKeys(DataSnapshot snapshot) {
+ WritableArray childKeys = Arguments.createArray();
+
+ if (snapshot.hasChildren()) {
+ for (DataSnapshot child : snapshot.getChildren()) {
+ childKeys.pushString(child.getKey());
+ }
+ }
+
+ return childKeys;
+ }
+
+ public static Map recursivelyDeconstructReadableMap(ReadableMap readableMap) {
+ Map deconstructedMap = new HashMap<>();
+ if (readableMap == null) {
+ return deconstructedMap;
+ }
+
+ ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
+ while (iterator.hasNextKey()) {
+ String key = iterator.nextKey();
+ ReadableType type = readableMap.getType(key);
+ switch (type) {
+ case Null:
+ deconstructedMap.put(key, null);
+ break;
+ case Boolean:
+ deconstructedMap.put(key, readableMap.getBoolean(key));
+ break;
+ case Number:
+ deconstructedMap.put(key, readableMap.getDouble(key));
+ break;
+ case String:
+ deconstructedMap.put(key, readableMap.getString(key));
+ break;
+ case Map:
+ deconstructedMap.put(key, Utils.recursivelyDeconstructReadableMap(readableMap.getMap(key)));
+ break;
+ case Array:
+ deconstructedMap.put(key, Utils.recursivelyDeconstructReadableArray(readableMap.getArray(key)));
+ break;
+ default:
+ throw new IllegalArgumentException("Could not convert object with key: " + key + ".");
+ }
+
+ }
+ return deconstructedMap;
+ }
+
+ public static List