Skip to content

Commit

Permalink
[firebase_dynamic_links] Change architecture to work correctly on asy…
Browse files Browse the repository at this point in the history
…nc links and launch links (firebase#1687)

* [firebase_dynamic_links] Changed architecture to be able to difference between the dynamic link which opened the app and links clicked on app active or in background.

* [firebase_dynamic_links] Improvements provided by @bparrishMines

* [firebase_dynamic_links] Renaming configure to onLink

* [firebase_dynamic_links] Renaming getLaunchLink to getInitialLink
  • Loading branch information
jherencia authored and collinjackson committed Jul 23, 2019
1 parent b34df22 commit f1c3e1c
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 103 deletions.
8 changes: 8 additions & 0 deletions packages/firebase_dynamic_links/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 0.5.0

* **Breaking change**. Changed architecture and method names to be able to differentiate between
the dynamic link which opened the app and links clicked during app execution (active and background).
`retrieveDynamicLink` has been replaced with two different functions:
- `getInitialLink` a future to retrieve the link that opened the app
- `onLink` a callback to listen to links opened while the app is active or in background

## 0.4.0+6

* Update google-services Android gradle plugin to 4.3.0 in documentation and examples.
Expand Down
27 changes: 21 additions & 6 deletions packages/firebase_dynamic_links/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ Receiving dynamic links on *iOS* requires a couple more steps than *Android*. If
applinks:YOUR_SUBDOMAIN.page.link
```

4. To receive a dynamic link, call the `retrieveDynamicLink()` method from `FirebaseDynamicLinks`:
4. To receive a dynamic link, call the `getInitialLink()` method from `FirebaseDynamicLinks` which gets the link that opened the app (or null if it was not opened via a dynamic link)
and configure listeners for link callbacks when the application is active or in background calling `onLink`.

```dart
void main() {
Expand All @@ -117,24 +118,38 @@ class MyHomeWidgetState extends State<MyHomeWidget> {
@override
void initState() {
super.initState();
_retrieveDynamicLink();
this.initDynamicLinks();
}
Future<void> _retrieveDynamicLink() async {
final PendingDynamicLinkData data = await FirebaseDynamicLinks.instance.retrieveDynamicLink();
void initDynamicLinks() async {
final PendingDynamicLinkData data = await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deepLink = data?.link;
if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path); // deeplink.path == '/helloworld'
Navigator.pushNamed(context, deepLink.path);
}
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink = dynamicLink?.link;
if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path);
}
},
onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
}
);
}
.
.
.
}
```

If your app did not open from a dynamic link, `retrieveDynamicLink()` will return `null`.
If your app did not open from a dynamic link, `getInitialLink()` will return `null`.

## Getting Started

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import androidx.annotation.NonNull;
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.dynamiclinks.DynamicLink;
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
Expand All @@ -14,38 +15,61 @@
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.NewIntentListener;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/** FirebaseDynamicLinksPlugin */
public class FirebaseDynamicLinksPlugin implements MethodCallHandler {
private Registrar registrar;
private Intent latestIntent;
public class FirebaseDynamicLinksPlugin implements MethodCallHandler, NewIntentListener {
private final Registrar registrar;
private final MethodChannel channel;

private FirebaseDynamicLinksPlugin(Registrar registrar) {
private FirebaseDynamicLinksPlugin(Registrar registrar, MethodChannel channel) {
this.registrar = registrar;
if (registrar.activity() != null) {
latestIntent = registrar.activity().getIntent();
}
this.channel = channel;
}

registrar.addNewIntentListener(
new PluginRegistry.NewIntentListener() {
@Override
public boolean onNewIntent(Intent intent) {
latestIntent = intent;
return false;
}
});
@Override
public boolean onNewIntent(Intent intent) {
FirebaseDynamicLinks.getInstance()
.getDynamicLink(intent)
.addOnSuccessListener(
registrar.activity(),
new OnSuccessListener<PendingDynamicLinkData>() {
@Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
if (pendingDynamicLinkData != null) {
Map<String, Object> dynamicLink =
getMapFromPendingDynamicLinkData(pendingDynamicLinkData);
channel.invokeMethod("onLinkSuccess", dynamicLink);
}
}
})
.addOnFailureListener(
registrar.activity(),
new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Map<String, Object> exception = new HashMap<>();
exception.put("code", e.getClass().getSimpleName());
exception.put("message", e.getMessage());
exception.put("details", null);
channel.invokeMethod("onLinkError", exception);
}
});

return false;
}

public static void registerWith(Registrar registrar) {
final MethodChannel channel =
new MethodChannel(registrar.messenger(), "plugins.flutter.io/firebase_dynamic_links");
channel.setMethodCallHandler(new FirebaseDynamicLinksPlugin(registrar));
final FirebaseDynamicLinksPlugin plugin = new FirebaseDynamicLinksPlugin(registrar, channel);
registrar.addNewIntentListener(plugin);
channel.setMethodCallHandler(plugin);
}

@Override
Expand All @@ -66,49 +90,47 @@ public void onMethodCall(MethodCall call, Result result) {
builder.setLongLink(url);
buildShortDynamicLink(builder, call, createShortLinkListener(result));
break;
case "FirebaseDynamicLinks#retrieveDynamicLink":
handleRetrieveDynamicLink(result);
case "FirebaseDynamicLinks#getInitialLink":
handleGetInitialDynamicLink(result);
break;
default:
result.notImplemented();
break;
}
}

private void handleRetrieveDynamicLink(final Result result) {
if (latestIntent == null) {
result.success(null);
return;
}
private Map<String, Object> getMapFromPendingDynamicLinkData(
PendingDynamicLinkData pendingDynamicLinkData) {
Map<String, Object> dynamicLink = new HashMap<>();
dynamicLink.put("link", pendingDynamicLinkData.getLink().toString());

Map<String, Object> androidData = new HashMap<>();
androidData.put("clickTimestamp", pendingDynamicLinkData.getClickTimestamp());
androidData.put("minimumVersion", pendingDynamicLinkData.getMinimumAppVersion());

dynamicLink.put("android", androidData);
return dynamicLink;
}

private void handleGetInitialDynamicLink(final Result result) {
FirebaseDynamicLinks.getInstance()
.getDynamicLink(latestIntent)
.addOnCompleteListener(
.getDynamicLink(registrar.activity().getIntent())
.addOnSuccessListener(
registrar.activity(),
new OnCompleteListener<PendingDynamicLinkData>() {
new OnSuccessListener<PendingDynamicLinkData>() {
@Override
public void onComplete(@NonNull Task<PendingDynamicLinkData> task) {
if (task.isSuccessful()) {
PendingDynamicLinkData data = task.getResult();
if (data != null) {
Map<String, Object> dynamicLink = new HashMap<>();
dynamicLink.put("link", data.getLink().toString());

Map<String, Object> androidData = new HashMap<>();
androidData.put("clickTimestamp", data.getClickTimestamp());
androidData.put("minimumVersion", data.getMinimumAppVersion());

dynamicLink.put("android", androidData);

latestIntent = null;
result.success(dynamicLink);
return;
}
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
if (pendingDynamicLinkData != null) {
Map<String, Object> dynamicLink =
getMapFromPendingDynamicLinkData(pendingDynamicLinkData);
result.success(dynamicLink);
return;
}
result.success(null);
}
})
.addOnFailureListener(
registrar.activity(),
new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Expand Down
34 changes: 16 additions & 18 deletions packages/firebase_dynamic_links/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class _MainScreen extends StatefulWidget {
State<StatefulWidget> createState() => _MainScreenState();
}

class _MainScreenState extends State<_MainScreen> with WidgetsBindingObserver {
class _MainScreenState extends State<_MainScreen> {
String _linkMessage;
bool _isCreatingLink = false;
String _testString =
Expand All @@ -36,31 +36,29 @@ class _MainScreenState extends State<_MainScreen> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
_retrieveDynamicLink();
WidgetsBinding.instance.addObserver(this);
initDynamicLinks();
}

@override
void dispose() {
super.dispose();
WidgetsBinding.instance.removeObserver(this);
}

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_retrieveDynamicLink();
}
}

Future<void> _retrieveDynamicLink() async {
void initDynamicLinks() async {
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.retrieveDynamicLink();
await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deepLink = data?.link;

if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path);
}

FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink = dynamicLink?.link;

if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path);
}
}, onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
});
}

Future<void> _createDynamicLink(bool short) async {
Expand Down
Loading

0 comments on commit f1c3e1c

Please sign in to comment.