diff --git a/android/src/main/java/io/invertase/firebase/RNFirebaseModule.java b/android/src/main/java/io/invertase/firebase/RNFirebaseModule.java index 9a778357..9a38c745 100644 --- a/android/src/main/java/io/invertase/firebase/RNFirebaseModule.java +++ b/android/src/main/java/io/invertase/firebase/RNFirebaseModule.java @@ -1,22 +1,23 @@ package io.invertase.firebase; +import android.util.Log; import android.app.Activity; +import android.content.IntentSender; -import java.util.ArrayList; -import java.util.List; import java.util.Map; +import java.util.List; import java.util.HashMap; +import java.util.ArrayList; // react -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.Promise; +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.LifecycleEventListener; +import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactMethod; // play services import com.google.android.gms.common.ConnectionResult; @@ -25,7 +26,7 @@ import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; @SuppressWarnings("WeakerAccess") -public class RNFirebaseModule extends ReactContextBaseJavaModule implements LifecycleEventListener { +public class RNFirebaseModule extends ReactContextBaseJavaModule { private static final String TAG = "RNFirebase"; public RNFirebaseModule(ReactApplicationContext reactContext) { @@ -42,12 +43,12 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule implements Life public void initializeApp(String appName, ReadableMap options, Callback callback) { FirebaseOptions.Builder builder = new FirebaseOptions.Builder(); - builder.setApplicationId(options.getString("appId")); - builder.setGcmSenderId(options.getString("messagingSenderId")); builder.setApiKey(options.getString("apiKey")); + builder.setApplicationId(options.getString("appId")); builder.setProjectId(options.getString("projectId")); builder.setDatabaseUrl(options.getString("databaseURL")); builder.setStorageBucket(options.getString("storageBucket")); + builder.setGcmSenderId(options.getString("messagingSenderId")); // todo firebase sdk has no client id setter FirebaseApp.initializeApp(getReactApplicationContext(), builder.build(), appName); @@ -84,8 +85,9 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule implements Life result.putBoolean("isAvailable", true); } else { result.putBoolean("isAvailable", false); - result.putBoolean("isUserResolvableError", gapi.isUserResolvableError(status)); result.putString("error", gapi.getErrorString(status)); + result.putBoolean("isUserResolvableError", gapi.isUserResolvableError(status)); + result.putBoolean("hasResolution", new ConnectionResult(status).hasResolution()); } return result; } @@ -94,7 +96,7 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule implements Life * Prompt the device user to update play services */ @ReactMethod - public void promptPlayServices() { + public void promptForPlayServices() { GoogleApiAvailability gapi = GoogleApiAvailability.getInstance(); int status = gapi.isGooglePlayServicesAvailable(getReactApplicationContext()); @@ -106,6 +108,27 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule implements Life } } + /** + * Prompt the device user to update play services + */ + @ReactMethod + public void resolutionForPlayServices() { + int status = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(getReactApplicationContext()); + ConnectionResult connectionResult = new ConnectionResult(status); + + if (!connectionResult.isSuccess() && connectionResult.hasResolution()) { + Activity activity = getCurrentActivity(); + if (activity != null) { + try { + connectionResult.startResolutionForResult(activity, status); + } catch (IntentSender.SendIntentException error) { + Log.d(TAG, "resolutionForPlayServices", error); + } + } + } + } + + /** * Prompt the device user to update play services */ @@ -122,32 +145,16 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule implements Life } } - @Override - public void onHostResume() { -// WritableMap params = Arguments.createMap(); -// params.putBoolean("isForeground", true); -// Utils.sendEvent(getReactApplicationContext(), "RNFirebaseAppState", params); - } - - @Override - public void onHostPause() { -// WritableMap params = Arguments.createMap(); -// params.putBoolean("isForeground", false); -// Utils.sendEvent(getReactApplicationContext(), "RNFirebaseAppState", params); - } - - @Override - public void onHostDestroy() { - - } @Override public Map getConstants() { FirebaseApp firebaseApp; - Map constants = new HashMap<>(); - List firebaseAppList = FirebaseApp.getApps(getReactApplicationContext()); - List> appMapsList = new ArrayList>(); + Map constants = new HashMap<>(); + List> appMapsList = new ArrayList<>(); + List firebaseAppList = FirebaseApp.getApps(getReactApplicationContext()); + + // TODO no way to get client id currently from app options - firebase sdk issue for (FirebaseApp app : firebaseAppList) { String appName = app.getName(); FirebaseOptions appOptions = app.getOptions(); @@ -156,16 +163,16 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule implements Life appProps.put("name", appName); appProps.put("apiKey", appOptions.getApiKey()); appProps.put("appId", appOptions.getApplicationId()); + appProps.put("projectId", appOptions.getProjectId()); appProps.put("databaseURL", appOptions.getDatabaseUrl()); appProps.put("messagingSenderId", appOptions.getGcmSenderId()); - appProps.put("projectId", appOptions.getProjectId()); appProps.put("storageBucket", appOptions.getStorageBucket()); - // TODO no way to get client id currently from app options - firebase sdk issue + appMapsList.add(appProps); } constants.put("apps", appMapsList); - constants.put("googleApiAvailability", getPlayServicesStatus()); + constants.put("playServicesAvailability", getPlayServicesStatus()); return constants; } } diff --git a/lib/firebase-app.js b/lib/firebase-app.js index 4f451203..1a4742e9 100644 --- a/lib/firebase-app.js +++ b/lib/firebase-app.js @@ -13,6 +13,7 @@ import Storage, { statics as StorageStatics } from './modules/storage'; import Database, { statics as DatabaseStatics } from './modules/database'; import Messaging, { statics as MessagingStatics } from './modules/messaging'; import Firestore, { statics as FirestoreStatics } from './modules/firestore'; +import Utils, { statics as UtilsStatics } from './modules/utils'; const FirebaseCoreModule = NativeModules.RNFirebase; @@ -37,6 +38,7 @@ export default class FirebaseApp { this.messaging = this._staticsOrModuleInstance(MessagingStatics, Messaging); this.perf = this._staticsOrModuleInstance({}, Performance); this.storage = this._staticsOrModuleInstance(StorageStatics, Storage); + this.utils = this._staticsOrModuleInstance(UtilsStatics, Utils); this._extendedProps = {}; } diff --git a/lib/firebase.js b/lib/firebase.js index c67b4f8f..ee8d3ddb 100644 --- a/lib/firebase.js +++ b/lib/firebase.js @@ -4,11 +4,9 @@ */ import { NativeModules, NativeEventEmitter } from 'react-native'; -import { isObject, isString } from './utils'; - import INTERNALS from './internals'; -import PACKAGE from './../package.json'; import FirebaseApp from './firebase-app'; +import { isObject, isString } from './utils'; // module imports import AdMob, { statics as AdMobStatics } from './modules/admob'; @@ -21,6 +19,7 @@ import Storage, { statics as StorageStatics } from './modules/storage'; import Database, { statics as DatabaseStatics } from './modules/database'; import Messaging, { statics as MessagingStatics } from './modules/messaging'; import Firestore, { statics as FirestoreStatics } from './modules/firestore'; +import Utils, { statics as UtilsStatics } from './modules/utils'; const FirebaseCoreModule = NativeModules.RNFirebase; @@ -52,6 +51,7 @@ class FirebaseCore { this.messaging = this._appNamespaceOrStatics(MessagingStatics, Messaging); this.perf = this._appNamespaceOrStatics(DatabaseStatics, Performance); this.storage = this._appNamespaceOrStatics(StorageStatics, Storage); + this.utils = this._appNamespaceOrStatics(UtilsStatics, Utils); } /** @@ -139,42 +139,6 @@ class FirebaseCore { return Object.values(INTERNALS.APPS); } - /** - * The current RNFirebase SDK version. - */ - get SDK_VERSION() { - return PACKAGE.version; - } - - /** - * The platform specific default app name - */ - get DEFAULT_APP_NAME() { - return INTERNALS.STRINGS.DEFAULT_APP_NAME; - } - - /** - * Returns props from the android GoogleApiAvailability sdk - * @android - * @return {RNFirebase.GoogleApiAvailabilityType|{isAvailable: boolean, status: number}} - */ - get googleApiAvailability(): GoogleApiAvailabilityType { - return FirebaseCoreModule.googleApiAvailability || { isAvailable: true, status: 0 }; - } - - /* - * CONFIG METHODS - */ - /** - * Set the global logging level for all logs. - * - * @param booleanOrDebugString - */ - setLogLevel(booleanOrDebugString) { - INTERNALS.OPTIONS.logLevel = booleanOrDebugString; - Log.setLevel(booleanOrDebugString); - } - /* * INTERNALS */ diff --git a/lib/modules/utils/index.js b/lib/modules/utils/index.js new file mode 100644 index 00000000..333cabd1 --- /dev/null +++ b/lib/modules/utils/index.js @@ -0,0 +1,135 @@ +// @flow +import { NativeModules } from 'react-native'; +import { version as ReactVersion } from 'react'; +import ReactNativeVersion from 'react-native/Libraries/Core/ReactNativeVersion'; + +import INTERNALS from './../../internals'; +import { isIOS } from './../../utils'; +import PACKAGE from './../../../package.json'; + +const FirebaseCoreModule = NativeModules.RNFirebase; + +export default class RNFirebaseUtils { + static _NAMESPACE = 'utils'; + static _NATIVE_DISABLED = true; + static _NATIVE_MODULE = 'RNFirebaseUtils'; + + /** + * + */ + checkPlayServicesAvailability() { + if (isIOS) return null; + if (!this.playServicesAvailability.isAvailable) { + if (INTERNALS.OPTIONS.promptOnMissingPlayServices && this.playServicesAvailability.isUserResolvableError) { + this.promptForPlayServices(); + } else { + const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(this.playServicesAvailability.code); + if (INTERNALS.OPTIONS.errorOnMissingPlayServices) { + throw new Error(error); + } else { + console.warn(error); + } + } + } + + return null; + } + + promptForPlayServices() { + if (isIOS) return null; + return FirebaseCoreModule.promptForPlayServices(); + } + + resolutionForPlayServices() { + if (isIOS) return null; + return FirebaseCoreModule.resolutionForPlayServices(); + } + + makePlayServicesAvailable() { + if (isIOS) return null; + return FirebaseCoreModule.makePlayServicesAvailable(); + } + + + get sharedEventEmitter(): Object { + return INTERNALS.SharedEventEmitter; + } + + /** + * Set the global logging level for all logs. + * + * @param booleanOrDebugString + */ + set logLevel(booleanOrDebugString) { + INTERNALS.OPTIONS.logLevel = booleanOrDebugString; + } + + + /** + * Returns an array of all current database registrations id strings + */ + get databaseRegistrations(): Array { + return Object.keys(INTERNALS.SyncTree._reverseLookup); + } + + /** + * Call with a registration id string to get the details off this reg + */ + get getDatabaseRegistrationDetails(): Function { + return INTERNALS.SyncTree.getRegistration.bind(INTERNALS.SyncTree); + } + + /** + * Accepts an array or a single string of registration ids. + * This will remove the refs on both the js and native sides and their listeners. + * @return {function(this:T)} + */ + get removeDatabaseRegistration(): Function { + return INTERNALS.SyncTree.removeListenersForRegistrations.bind(INTERNALS.SyncTree); + } + + /** + * The platform specific default app name + */ + get DEFAULT_APP_NAME() { + return INTERNALS.STRINGS.DEFAULT_APP_NAME; + } + + /** + * Returns props from the android GoogleApiAvailability sdk + * @android + * @return {RNFirebase.GoogleApiAvailabilityType|{isAvailable: boolean, status: number}} + */ + get playServicesAvailability(): GoogleApiAvailabilityType { + return FirebaseCoreModule.playServicesAvailability || { isAvailable: true, status: 0 }; + } + + /** + * Enable/Disable automatic prompting of the play services update dialog + * @android + * @param bool + */ + set errorOnMissingPlayServices(bool: Boolean) { + INTERNALS.OPTIONS.errorOnMissingPlayServices = bool; + } + + /** + * Enable/Disable automatic prompting of the play services update dialog + * @android + * @param bool + */ + set promptOnMissingPlayServices(bool: Boolean) { + INTERNALS.OPTIONS.promptOnMissingPlayServices = bool; + } +} + + +export const statics = { + DEFAULT_APP_NAME: INTERNALS.STRINGS.DEFAULT_APP_NAME, + VERSIONS: { + react: ReactVersion, + 'react-native': Object.values(ReactNativeVersion.version).slice(0, 3).join('.'), + 'react-native-firebase': PACKAGE.version, + }, +}; +