Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8dee086
[functions][js] start of functions implementationn
Salakar May 4, 2018
368b3ad
[functions][android][js] implementation - wip
Salakar May 4, 2018
a07e859
[tests][android] add functions sdk to app + switch out remaining grad…
Salakar May 4, 2018
300c31f
[tests][functions] create testing cloud function project
Salakar May 5, 2018
9fa059b
[tests][helpers][functions] expose TestHelpers.functions.data
Salakar May 5, 2018
ca068db
[tests][functions] update cloud function to support primitive types o…
Salakar May 5, 2018
30f2331
[core] add functions module to core + types
Salakar May 5, 2018
1526d57
[tests] add logging to cloud function + use deepEqual
Salakar May 5, 2018
d06fbec
[js][functions] finalise + cleanup
Salakar May 5, 2018
d5abd6a
[android][functions] finalise + cleanup
Salakar May 5, 2018
3306d92
[android][utils] refactor legacy utils to use react natives own conve…
Salakar May 5, 2018
d5b55ab
[tests][android] add functions module package to app
Salakar May 5, 2018
1741609
[tests][functions] start of functions tests - todo
Salakar May 5, 2018
e5233c9
Merge branch 'master' of https://github.com/invertase/react-native-fi…
Salakar May 5, 2018
a767558
[functions][android] add null check to logging
Salakar May 6, 2018
6e3c4f0
[tests][functions] httpsCallable tests
Salakar May 6, 2018
73d915f
[android][utils] add java.lang.Integer support for mapPutValue
Salakar May 6, 2018
d26f7a1
[js][functions] misc HttpsError
Salakar May 6, 2018
263df53
[tests][functions] added HttpsError tests
Salakar May 6, 2018
7d5fa69
[ios][functions] start of implementation
Salakar May 6, 2018
d0198d9
[tests][ios] add Firebase/Functions to pods
Salakar May 6, 2018
c5df065
[ios][functions] finalise implementation
Salakar May 6, 2018
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,4 @@ tests/ios/Fabric.framework/Fabric
bridge/android/app/.classpath
bridge/android/app/.project
**/.vscode
bridge/functions/firebase-debug.log
1 change: 1 addition & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ dependencies {
compileOnly "com.google.firebase:firebase-ads:$firebaseVersion"
compileOnly "com.google.firebase:firebase-firestore:$firebaseVersion"
compileOnly "com.google.firebase:firebase-invites:$firebaseVersion"
compileOnly "com.google.firebase:firebase-functions:$firebaseVersion"
compileOnly('com.crashlytics.sdk.android:crashlytics:2.9.1@aar') {
transitive = true
}
Expand Down
220 changes: 80 additions & 140 deletions android/src/main/java/io/invertase/firebase/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,24 @@
import android.content.Context;
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.modules.core.DeviceEventManagerModule;

import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMapKeySetIterator;

import javax.annotation.Nullable;


@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
**/
Expand All @@ -50,160 +36,114 @@ public static void sendEvent(final ReactContext context, final String eventName,
}

/**
* @param key map key
* @param value map value
* @param map map
*/
public static void mapPutValue(String key, Object value, WritableMap map) {
String type = value != null ? value.getClass().getName() : "";
switch (type) {
case "java.lang.Boolean":
map.putBoolean(key, (Boolean) value);
break;
case "java.lang.Long":
Long longVal = (Long) value;
map.putDouble(key, (double) longVal);
break;
case "java.lang.Double":
map.putDouble(key, (Double) value);
break;
case "java.lang.String":
map.putString(key, (String) value);
break;
default:
map.putString(key, null);
}
}



/**
* Takes a value and calls the appropriate setter for its type on the target map + key
*
* @param map
* @return
* @param key String key to set on target map
* @param value Object value to set on target map
* @param map WritableMap target map to write the value to
*/
public static WritableMap readableMapToWritableMap(ReadableMap map) {
WritableMap writableMap = Arguments.createMap();

ReadableMapKeySetIterator iterator = map.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
ReadableType type = map.getType(key);
@SuppressWarnings("unchecked")
public static void mapPutValue(String key, @Nullable Object value, WritableMap map) {
if (value == null) {
map.putNull(key);
} else {
String type = value.getClass().getName();
switch (type) {
case Null:
writableMap.putNull(key);
case "java.lang.Boolean":
map.putBoolean(key, (Boolean) value);
break;
case Boolean:
writableMap.putBoolean(key, map.getBoolean(key));
case "java.lang.Long":
Long longVal = (Long) value;
map.putDouble(key, (double) longVal);
break;
case Number:
writableMap.putDouble(key, map.getDouble(key));
case "java.lang.Float":
float floatVal = (float) value;
map.putDouble(key, (double) floatVal);
break;
case String:
writableMap.putString(key, map.getString(key));
case "java.lang.Double":
map.putDouble(key, (Double) value);
break;
case Map:
writableMap.putMap(key, readableMapToWritableMap(map.getMap(key)));
case "java.lang.Integer":
map.putInt(key, (int) value);
break;
case Array:
// TODO writableMap.putArray(key, readableArrayToWritableArray(map.getArray(key)));
case "java.lang.String":
map.putString(key, (String) value);
break;
default:
throw new IllegalArgumentException("Could not convert object with key: " + key + ".");
if (List.class.isAssignableFrom(value.getClass())) {
map.putArray(key, Arguments.makeNativeArray((List<Object>) value));
} else if (Map.class.isAssignableFrom(value.getClass())) {
map.putMap(key, Arguments.makeNativeMap((Map<String, Object>) value));
} else {
Log.d(TAG, "utils:mapPutValue:unknownType:" + type);
map.putNull(key);
}
}

}
}

/**
* Convert a ReadableMap to a WritableMap for the purposes of re-sending back to JS
* TODO This is now a legacy util - internally uses RN functionality
*
* @param map ReadableMap
* @return WritableMap
*/
public static WritableMap readableMapToWritableMap(ReadableMap map) {
WritableMap writableMap = Arguments.createMap();
// https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeMap.java#L54
writableMap.merge(map);
return writableMap;
}


/**
* Convert a ReadableMap into a native Java Map
* TODO This is now a legacy util - internally uses RN functionality
*
* @param readableMap ReadableMap
* @return Map
*/
public static Map<String, Object> recursivelyDeconstructReadableMap(ReadableMap readableMap) {
Map<String, Object> 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;
// https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.java#L216
return readableMap.toHashMap();
}

/**
* Convert a ReadableArray into a native Java Map
* TODO This is now a legacy util - internally uses RN functionality
*
* @param readableArray ReadableArray
* @return List<Object>
*/
public static List<Object> recursivelyDeconstructReadableArray(ReadableArray readableArray) {
List<Object> deconstructedList = new ArrayList<>(readableArray.size());
for (int i = 0; i < readableArray.size(); i++) {
ReadableType indexType = readableArray.getType(i);
switch (indexType) {
case Null:
deconstructedList.add(i, null);
break;
case Boolean:
deconstructedList.add(i, readableArray.getBoolean(i));
break;
case Number:
deconstructedList.add(i, readableArray.getDouble(i));
break;
case String:
deconstructedList.add(i, readableArray.getString(i));
break;
case Map:
deconstructedList.add(i, Utils.recursivelyDeconstructReadableMap(readableArray.getMap(i)));
break;
case Array:
deconstructedList.add(i, Utils.recursivelyDeconstructReadableArray(readableArray.getArray(i)));
break;
default:
throw new IllegalArgumentException("Could not convert object at index " + i + ".");
}
}
return deconstructedList;
// https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.java#L175
return readableArray.toArrayList();
}

/**
* We need to check if app is in foreground otherwise the app will crash.
* http://stackoverflow.com/questions/8489993/check-android-application-is-in-foreground-or-not
*
* @param context Context
* @return boolean
*/
public static boolean isAppInForeground(Context context) {
/**
We need to check if app is in foreground otherwise the app will crash.
http://stackoverflow.com/questions/8489993/check-android-application-is-in-foreground-or-not
**/
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses =
activityManager.getRunningAppProcesses();
if (appProcesses == null) {
return false;
}
if (activityManager == null) return false;

List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if (appProcesses == null) return false;

final String packageName = context.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance ==
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
appProcess.processName.equals(packageName)) {
if (
appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
&& appProcess.processName.equals(packageName)
) {
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package io.invertase.firebase.functions;

import android.support.annotation.NonNull;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;

import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.functions.FirebaseFunctions;
import com.google.firebase.functions.FirebaseFunctionsException;
import com.google.firebase.functions.HttpsCallableReference;
import com.google.firebase.functions.HttpsCallableResult;

import io.invertase.firebase.Utils;

public class RNFirebaseFunctions extends ReactContextBaseJavaModule {

private static final String DATA_KEY = "data";
private static final String CODE_KEY = "code";
private static final String MSG_KEY = "message";
private static final String ERROR_KEY = "__error";
private static final String DETAILS_KEY = "details";
private static final String TAG = "RNFirebaseFunctions";

RNFirebaseFunctions(ReactApplicationContext reactContext) {
super(reactContext);
Log.d(TAG, "New instance");
}

@Override
public String getName() {
return TAG;
}

@ReactMethod
public void httpsCallable(final String name, ReadableMap wrapper, final Promise promise) {
Object input = wrapper.toHashMap().get(DATA_KEY);
Log.d(TAG, "function:call:input:" + name + ":" + (input != null ? input.toString() : "null"));

HttpsCallableReference httpsCallableReference = FirebaseFunctions
.getInstance()
.getHttpsCallable(name);

httpsCallableReference
.call(input)
.addOnSuccessListener(new OnSuccessListener<HttpsCallableResult>() {
@Override
public void onSuccess(HttpsCallableResult httpsCallableResult) {
WritableMap map = Arguments.createMap();
Object result = httpsCallableResult.getData();

Log.d(
TAG,
"function:call:onSuccess:" + name
);
Log.d(
TAG,
"function:call:onSuccess:result:type:" + name + ":" + (result != null ? result.getClass().getName() : "null")
);
Log.d(
TAG,
"function:call:onSuccess:result:data:" + name + ":" + (result != null ? result.toString() : "null")
);

Utils.mapPutValue(DATA_KEY, result, map);
promise.resolve(map);

}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
Log.d(TAG, "function:call:onFailure:" + name, exception);

String message;
Object details = null;
String code = "UNKNOWN";
WritableMap map = Arguments.createMap();

if (exception instanceof FirebaseFunctionsException) {
FirebaseFunctionsException ffe = (FirebaseFunctionsException) exception;
details = ffe.getDetails();
code = ffe.getCode().name();
message = ffe.getLocalizedMessage();
} else {
message = exception.getLocalizedMessage();
}

Utils.mapPutValue(CODE_KEY, code, map);
Utils.mapPutValue(MSG_KEY, message, map);
Utils.mapPutValue(ERROR_KEY, true, map);
Utils.mapPutValue(DETAILS_KEY, details, map);
promise.resolve(map);
}
});
}
}
Loading