diff --git a/android/build.gradle b/android/build.gradle index 577ab4dd..6ba9e9d7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -61,4 +61,5 @@ dependencies { compile "com.google.firebase:firebase-config:$firebaseVersion" compile "com.google.firebase:firebase-perf:$firebaseVersion" compile "com.google.firebase:firebase-ads:$firebaseVersion" + compile "com.google.firebase:firebase-invites:$firebaseVersion" } diff --git a/android/src/main/java/io/invertase/firebase/links/RNFirebaseLinks.java b/android/src/main/java/io/invertase/firebase/links/RNFirebaseLinks.java new file mode 100644 index 00000000..7124bb8f --- /dev/null +++ b/android/src/main/java/io/invertase/firebase/links/RNFirebaseLinks.java @@ -0,0 +1,81 @@ +package io.invertase.firebase.links; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.util.Log; + +import com.facebook.react.bridge.ActivityEventListener; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.firebase.dynamiclinks.FirebaseDynamicLinks; +import com.google.firebase.dynamiclinks.PendingDynamicLinkData; + +import io.invertase.firebase.Utils; + +public class RNFirebaseLinks extends ReactContextBaseJavaModule implements ActivityEventListener { + private final static String TAG = RNFirebaseLinks.class.getCanonicalName(); + private String initialLink = null; + + public RNFirebaseLinks(ReactApplicationContext reactContext) { + super(reactContext); + getReactApplicationContext().addActivityEventListener(this); + registerLinksHandler(); + } + + @Override + public String getName() { + return "RNFirebaseLinks"; + } + + @ReactMethod + public void getInitialLink(Promise promise) { + promise.resolve(initialLink); + } + + private void registerLinksHandler() { + Activity activity = getCurrentActivity(); + if (activity == null) { + return; + } + + FirebaseDynamicLinks.getInstance() + .getDynamicLink(activity.getIntent()) + .addOnSuccessListener(activity, new OnSuccessListener() { + @Override + public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) { + // Get deep link from result (may be null if no link is found) + if (pendingDynamicLinkData != null) { + Uri deepLinkUri = pendingDynamicLinkData.getLink(); + String deepLink = deepLinkUri.toString(); + // TODO: Validate that this is called when opening from a deep link + if (initialLink == null) { + initialLink = deepLink; + } + Utils.sendEvent(getReactApplicationContext(), "dynamic_link_received", deepLink); + } + } + }) + .addOnFailureListener(activity, new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + Log.w(TAG, "getDynamicLink:onFailure", e); + } + }); + } + + @Override + public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { + // Not required for this module + } + + @Override + public void onNewIntent(Intent intent) { + // TODO: Do I need to re-register the links handler for each new intent? + } +} diff --git a/android/src/main/java/io/invertase/firebase/links/RNFirebaseLinksPackage.java b/android/src/main/java/io/invertase/firebase/links/RNFirebaseLinksPackage.java new file mode 100644 index 00000000..a4780b01 --- /dev/null +++ b/android/src/main/java/io/invertase/firebase/links/RNFirebaseLinksPackage.java @@ -0,0 +1,51 @@ +package io.invertase.firebase.links; + +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.ArrayList; +import java.util.Collections; +import java.util.List; + +@SuppressWarnings("unused") +public class RNFirebaseLinksPackage implements ReactPackage { + public RNFirebaseLinksPackage() { + } + + /** + * @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 RNFirebaseLinks(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/lib/firebase.js b/lib/firebase.js index 95170e00..363fd0a1 100644 --- a/lib/firebase.js +++ b/lib/firebase.js @@ -17,6 +17,7 @@ import Crash from './modules/crash'; import RemoteConfig from './modules/config'; import Performance from './modules/perf'; import AdMob, { statics as AdMobStatics } from './modules/admob'; +import Links, { statis as LinksStatics } from './modules/links'; const instances: Object = { default: null }; const FirebaseModule = NativeModules.RNFirebase; @@ -38,6 +39,7 @@ export default class Firebase { _crash: ?Object; _perf: ?Object; _admob: ?Object; + _links: ?Object; auth: Function; crash: Function; @@ -48,6 +50,7 @@ export default class Firebase { config: Function; perf: Function; admob: Function; + links: Function; eventHandlers: Object; debug: boolean; @@ -94,6 +97,7 @@ export default class Firebase { this.config = this._staticsOrInstance('config', {}, RemoteConfig); this.perf = this._staticsOrInstance('perf', {}, Performance); this.admob = this._staticsOrInstance('admob', AdMobStatics, AdMob); + this.links = this._staticsOrInstance('links', LinksStatics, Links); // init auth to start listeners if (NativeModules.RNFirebaseAuth) { diff --git a/lib/modules/links/index.js b/lib/modules/links/index.js new file mode 100644 index 00000000..ee60bc42 --- /dev/null +++ b/lib/modules/links/index.js @@ -0,0 +1,50 @@ +import { NativeEventEmitter, NativeModules } from 'react-native'; +import { Base } from './../base'; +import { nativeSDKMissing } from './../../utils'; + +const FirebaseLinks = NativeModules.RNFirebaseLinks; +const FirebaseLinksEvt = FirebaseLinks && new NativeEventEmitter(FirebaseLinks); + +const EVENT_TYPE = { + Link: 'dynamic_link_received', +}; + +/** + * @class Links + */ +export default class Links extends Base { + constructor(firebase, options = {}) { + super(firebase, options); + if (!FirebaseLinks) { + return nativeSDKMissing('links'); + } + + this.namespace = 'firebase:links'; + } + + get EVENT_TYPE() { + return EVENT_TYPE; + } + + /** + * Returns the link that triggered application open + * @returns {*} + */ + getInitialLink() { + return FirebaseLinks.getInitialLink(); + } + + /** + * Subscribe to dynamic links + * @param listener + * @returns {*} + */ + onLink(listener: Function): () => any { + const rnListener = FirebaseLinksEvt.addListener(EVENT_TYPE.Link, listener); + return () => rnListener.remove(); + } +} + +export const statics = { + EVENT_TYPE, +};