diff --git a/packages/firebase_dynamic_links/CHANGELOG.md b/packages/firebase_dynamic_links/CHANGELOG.md index d70d789ffdbc..fb72d7fcc133 100644 --- a/packages/firebase_dynamic_links/CHANGELOG.md +++ b/packages/firebase_dynamic_links/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.0+8 + +* Support v2 embedding. This will remain compatible with the original embedding and won't require app migration. + ## 0.5.0+7 * Add `getDynamicLink` to support expanding from short links. diff --git a/packages/firebase_dynamic_links/android/build.gradle b/packages/firebase_dynamic_links/android/build.gradle index 8e5b53c6f334..8a84add378d6 100644 --- a/packages/firebase_dynamic_links/android/build.gradle +++ b/packages/firebase_dynamic_links/android/build.gradle @@ -34,8 +34,33 @@ android { dependencies { api 'com.google.firebase:firebase-dynamic-links:16.1.8' implementation 'com.google.firebase:firebase-common:16.1.0' - implementation 'androidx.annotation:annotation:1.0.0' } } apply from: file("./user-agent.gradle") + +// TODO(bparrishMines): Remove this hack once androidx.lifecycle is included on stable. https://github.com/flutter/flutter/issues/42348 +afterEvaluate { + def containsEmbeddingDependencies = false + for (def configuration : configurations.all) { + for (def dependency : configuration.dependencies) { + if (dependency.group == 'io.flutter' && + dependency.name.startsWith('flutter_embedding') && + dependency.isTransitive()) + { + containsEmbeddingDependencies = true + break + } + } + } + if (!containsEmbeddingDependencies) { + android { + dependencies { + def lifecycle_version = "1.1.1" + compileOnly "android.arch.lifecycle:runtime:$lifecycle_version" + compileOnly "android.arch.lifecycle:common:$lifecycle_version" + compileOnly "android.arch.lifecycle:common-java8:$lifecycle_version" + } + } + } +} diff --git a/packages/firebase_dynamic_links/android/gradle.properties b/packages/firebase_dynamic_links/android/gradle.properties deleted file mode 100644 index 8bd86f680510..000000000000 --- a/packages/firebase_dynamic_links/android/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M diff --git a/packages/firebase_dynamic_links/android/src/main/java/io/flutter/plugins/firebasedynamiclinks/FirebaseDynamicLinksPlugin.java b/packages/firebase_dynamic_links/android/src/main/java/io/flutter/plugins/firebasedynamiclinks/FirebaseDynamicLinksPlugin.java index ef7a0263b39e..31e2567349fe 100644 --- a/packages/firebase_dynamic_links/android/src/main/java/io/flutter/plugins/firebasedynamiclinks/FirebaseDynamicLinksPlugin.java +++ b/packages/firebase_dynamic_links/android/src/main/java/io/flutter/plugins/firebasedynamiclinks/FirebaseDynamicLinksPlugin.java @@ -4,9 +4,9 @@ package io.flutter.plugins.firebasedynamiclinks; +import android.app.Activity; import android.content.Intent; import android.net.Uri; -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; @@ -15,6 +15,10 @@ import com.google.firebase.dynamiclinks.FirebaseDynamicLinks; import com.google.firebase.dynamiclinks.PendingDynamicLinkData; import com.google.firebase.dynamiclinks.ShortDynamicLink; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; @@ -26,16 +30,50 @@ import java.util.List; import java.util.Map; -/** FirebaseDynamicLinksPlugin */ -public class FirebaseDynamicLinksPlugin implements MethodCallHandler, NewIntentListener { - private final Registrar registrar; - private final MethodChannel channel; - - private FirebaseDynamicLinksPlugin(Registrar registrar, MethodChannel channel) { - this.registrar = registrar; +/** + * Flutter plugin accessing for Firebase Dynamic Links API. + * + *

Instantiate this in an add to app scenario to gracefully handle activity and context changes. + */ +public class FirebaseDynamicLinksPlugin + implements FlutterPlugin, ActivityAware, MethodCallHandler, NewIntentListener { + private Activity activity; + private MethodChannel channel; + + private FirebaseDynamicLinksPlugin(Activity activity, MethodChannel channel) { + this.activity = activity; this.channel = channel; } + /** + * Default Constructor. + * + *

Use this when adding the plugin to your FlutterEngine + */ + public FirebaseDynamicLinksPlugin() {} + + /** + * Registers a plugin with the v1 embedding api {@code io.flutter.plugin.common}. + * + *

Calling this will register the plugin with the passed registrar. However, plugins + * initialized this way won't react to changes in activity or context. + * + * @param registrar attaches this plugin's {@link + * io.flutter.plugin.common.MethodChannel.MethodCallHandler} to the registrar's {@link + * io.flutter.plugin.common.BinaryMessenger}. + */ + public static void registerWith(Registrar registrar) { + final MethodChannel channel = createChannel(registrar.messenger()); + final FirebaseDynamicLinksPlugin plugin = + new FirebaseDynamicLinksPlugin(registrar.activity(), channel); + registrar.addNewIntentListener(plugin); + channel.setMethodCallHandler(plugin); + } + + private static MethodChannel createChannel(final BinaryMessenger messenger) { + return new MethodChannel(messenger, "plugins.flutter.io/firebase_dynamic_links"); + } + @Override public boolean onNewIntent(Intent intent) { FirebaseDynamicLinks.getInstance() @@ -54,7 +92,7 @@ public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) { .addOnFailureListener( new OnFailureListener() { @Override - public void onFailure(@NonNull Exception e) { + public void onFailure(Exception e) { Map exception = new HashMap<>(); exception.put("code", e.getClass().getSimpleName()); exception.put("message", e.getMessage()); @@ -66,12 +104,37 @@ public void onFailure(@NonNull Exception e) { return false; } - public static void registerWith(Registrar registrar) { - final MethodChannel channel = - new MethodChannel(registrar.messenger(), "plugins.flutter.io/firebase_dynamic_links"); - final FirebaseDynamicLinksPlugin plugin = new FirebaseDynamicLinksPlugin(registrar, channel); - registrar.addNewIntentListener(plugin); - channel.setMethodCallHandler(plugin); + @Override + public void onAttachedToEngine(FlutterPluginBinding binding) { + channel = createChannel(binding.getFlutterEngine().getDartExecutor()); + channel.setMethodCallHandler(this); + } + + @Override + public void onDetachedFromEngine(FlutterPluginBinding binding) { + channel.setMethodCallHandler(null); + } + + @Override + public void onAttachedToActivity(ActivityPluginBinding binding) { + activity = binding.getActivity(); + binding.addOnNewIntentListener(this); + } + + @Override + public void onDetachedFromActivityForConfigChanges() { + activity = null; + } + + @Override + public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) { + activity = binding.getActivity(); + binding.addOnNewIntentListener(this); + } + + @Override + public void onDetachedFromActivity() { + activity = null; } @Override @@ -134,7 +197,7 @@ public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) { .addOnFailureListener( new OnFailureListener() { @Override - public void onFailure(@NonNull Exception e) { + public void onFailure(Exception e) { result.error(e.getClass().getSimpleName(), e.getMessage(), null); } }); @@ -146,20 +209,19 @@ private void handleGetDynamicLink(final Result result, Uri uri) { private void handleGetInitialDynamicLink(final Result result) { // If there's no activity, then there's no initial dynamic link. - if (registrar.activity() == null) { + if (activity == null) { result.success(null); return; } addDynamicLinkListener( - FirebaseDynamicLinks.getInstance().getDynamicLink(registrar.activity().getIntent()), - result); + FirebaseDynamicLinks.getInstance().getDynamicLink(activity.getIntent()), result); } private OnCompleteListener createShortLinkListener(final Result result) { return new OnCompleteListener() { @Override - public void onComplete(@NonNull Task task) { + public void onComplete(Task task) { if (task.isSuccessful()) { Map url = new HashMap<>(); url.put("url", task.getResult().getShortLink().toString()); diff --git a/packages/firebase_dynamic_links/example/android/app/build.gradle b/packages/firebase_dynamic_links/example/android/app/build.gradle index 75ab78059c1c..f620169bc756 100644 --- a/packages/firebase_dynamic_links/example/android/app/build.gradle +++ b/packages/firebase_dynamic_links/example/android/app/build.gradle @@ -52,6 +52,9 @@ flutter { } dependencies { + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test:rules:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } apply plugin: 'com.google.gms.google-services' diff --git a/packages/firebase_dynamic_links/example/android/app/src/androidTest/java/io/flutter/plugins/firebasedynamiclinks/EmbeddingV1ActivityTest.java b/packages/firebase_dynamic_links/example/android/app/src/androidTest/java/io/flutter/plugins/firebasedynamiclinks/EmbeddingV1ActivityTest.java new file mode 100644 index 000000000000..e46c9dea4e3c --- /dev/null +++ b/packages/firebase_dynamic_links/example/android/app/src/androidTest/java/io/flutter/plugins/firebasedynamiclinks/EmbeddingV1ActivityTest.java @@ -0,0 +1,14 @@ +package io.flutter.plugins.firebasedynamiclinks; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.e2e.FlutterRunner; +import io.flutter.plugins.firebasedynamiclinksexample.EmbeddingV1Activity; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@RunWith(FlutterRunner.class) +public class EmbeddingV1ActivityTest { + @Rule + public ActivityTestRule rule = + new ActivityTestRule<>(EmbeddingV1Activity.class); +} diff --git a/packages/firebase_dynamic_links/example/android/app/src/androidTest/java/io/flutter/plugins/firebasedynamiclinks/MainActivityTest.java b/packages/firebase_dynamic_links/example/android/app/src/androidTest/java/io/flutter/plugins/firebasedynamiclinks/MainActivityTest.java new file mode 100644 index 000000000000..da558649436f --- /dev/null +++ b/packages/firebase_dynamic_links/example/android/app/src/androidTest/java/io/flutter/plugins/firebasedynamiclinks/MainActivityTest.java @@ -0,0 +1,12 @@ +package io.flutter.plugins.firebasedynamiclinks; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.e2e.FlutterRunner; +import io.flutter.plugins.firebasedynamiclinksexample.MainActivity; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@RunWith(FlutterRunner.class) +public class MainActivityTest { + @Rule public ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class); +} diff --git a/packages/firebase_dynamic_links/example/android/app/src/main/AndroidManifest.xml b/packages/firebase_dynamic_links/example/android/app/src/main/AndroidManifest.xml index ed3cca73d5d3..9f2179a23235 100644 --- a/packages/firebase_dynamic_links/example/android/app/src/main/AndroidManifest.xml +++ b/packages/firebase_dynamic_links/example/android/app/src/main/AndroidManifest.xml @@ -14,13 +14,17 @@ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> - + + diff --git a/packages/firebase_dynamic_links/example/android/app/src/main/java/io/flutter/plugins/firebasedynamiclinksexample/EmbeddingV1Activity.java b/packages/firebase_dynamic_links/example/android/app/src/main/java/io/flutter/plugins/firebasedynamiclinksexample/EmbeddingV1Activity.java new file mode 100644 index 000000000000..1d8a55dae484 --- /dev/null +++ b/packages/firebase_dynamic_links/example/android/app/src/main/java/io/flutter/plugins/firebasedynamiclinksexample/EmbeddingV1Activity.java @@ -0,0 +1,13 @@ +package io.flutter.plugins.firebasedynamiclinksexample; + +import android.os.Bundle; +import io.flutter.app.FlutterActivity; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class EmbeddingV1Activity extends FlutterActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + GeneratedPluginRegistrant.registerWith(this); + } +} diff --git a/packages/firebase_dynamic_links/example/android/app/src/main/java/io/flutter/plugins/firebasedynamiclinksexample/MainActivity.java b/packages/firebase_dynamic_links/example/android/app/src/main/java/io/flutter/plugins/firebasedynamiclinksexample/MainActivity.java index 885f28ec4efc..4b1e59618568 100644 --- a/packages/firebase_dynamic_links/example/android/app/src/main/java/io/flutter/plugins/firebasedynamiclinksexample/MainActivity.java +++ b/packages/firebase_dynamic_links/example/android/app/src/main/java/io/flutter/plugins/firebasedynamiclinksexample/MainActivity.java @@ -1,13 +1,17 @@ package io.flutter.plugins.firebasedynamiclinksexample; -import android.os.Bundle; -import io.flutter.app.FlutterActivity; -import io.flutter.plugins.GeneratedPluginRegistrant; +import dev.flutter.plugins.e2e.E2EPlugin; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.plugins.firebasedynamiclinks.FirebaseDynamicLinksPlugin; +import io.flutter.plugins.urllauncher.UrlLauncherPlugin; public class MainActivity extends FlutterActivity { + // TODO(bparrishMines): Remove this once v2 of GeneratedPluginRegistrant rolls to stable. https://github.com/flutter/flutter/issues/42694 @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); + public void configureFlutterEngine(FlutterEngine flutterEngine) { + flutterEngine.getPlugins().add(new FirebaseDynamicLinksPlugin()); + flutterEngine.getPlugins().add(new E2EPlugin()); + flutterEngine.getPlugins().add(new UrlLauncherPlugin()); } } diff --git a/packages/firebase_dynamic_links/example/android/gradle.properties b/packages/firebase_dynamic_links/example/android/gradle.properties index 7be3d8b46841..38c8d4544ff1 100644 --- a/packages/firebase_dynamic_links/example/android/gradle.properties +++ b/packages/firebase_dynamic_links/example/android/gradle.properties @@ -1,2 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/firebase_dynamic_links/example/pubspec.yaml b/packages/firebase_dynamic_links/example/pubspec.yaml index a2b3297b1452..bb7c5ce9b2ea 100644 --- a/packages/firebase_dynamic_links/example/pubspec.yaml +++ b/packages/firebase_dynamic_links/example/pubspec.yaml @@ -11,5 +11,10 @@ dependencies: url_launcher: ^5.1.6 +dev_dependencies: + e2e: ^0.2.1 + flutter_driver: + sdk: flutter + flutter: uses-material-design: true diff --git a/packages/firebase_dynamic_links/pubspec.yaml b/packages/firebase_dynamic_links/pubspec.yaml index 032659e93a4d..417256abfe97 100644 --- a/packages/firebase_dynamic_links/pubspec.yaml +++ b/packages/firebase_dynamic_links/pubspec.yaml @@ -1,7 +1,7 @@ name: firebase_dynamic_links description: Flutter plugin for Google Dynamic Links for Firebase, an app solution for creating and handling links across multiple platforms. -version: 0.5.0+7 +version: 0.5.0+8 author: Flutter Team homepage: https://github.com/FirebaseExtended/flutterfire/tree/master/packages/firebase_dynamic_links @@ -11,6 +11,9 @@ dependencies: sdk: flutter dev_dependencies: + e2e: ^0.2.1 + flutter_driver: + sdk: flutter flutter_test: sdk: flutter url_launcher: ^4.2.0 @@ -24,4 +27,4 @@ flutter: environment: sdk: ">=2.0.0-dev.28.0 <3.0.0" - flutter: ">=1.5.0 <2.0.0" + flutter: ">=1.9.1+hotfix.4 <2.0.0" diff --git a/packages/firebase_dynamic_links/test/firebase_dynamic_links_e2e.dart b/packages/firebase_dynamic_links/test/firebase_dynamic_links_e2e.dart new file mode 100644 index 000000000000..e2927d8e500c --- /dev/null +++ b/packages/firebase_dynamic_links/test/firebase_dynamic_links_e2e.dart @@ -0,0 +1,32 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:e2e/e2e.dart'; + +import '../lib/firebase_dynamic_links.dart'; + +void main() { + E2EWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('buildUrl', (WidgetTester tester) async { + final DynamicLinkParameters parameters = DynamicLinkParameters( + uriPrefix: 'https://cx4k7.app.goo.gl', + link: Uri.parse('https://dynamic.link.example/helloworld'), + androidParameters: AndroidParameters( + packageName: 'io.flutter.plugins.firebasedynamiclinksexample', + minimumVersion: 0, + ), + dynamicLinkParametersOptions: DynamicLinkParametersOptions( + shortDynamicLinkPathLength: ShortDynamicLinkPathLength.short, + ), + iosParameters: IosParameters( + bundleId: 'com.google.FirebaseCppDynamicLinksTestApp.dev', + minimumVersion: '0', + ), + ); + + final Uri uri = await parameters.buildUrl(); + expect( + uri.toString(), + 'https://cx4k7.app.goo.gl?amv=0&apn=io.flutter.plugins.firebasedynamiclinksexample&ibi=com.google.FirebaseCppDynamicLinksTestApp.dev&imv=0&link=https%3A%2F%2Fdynamic.link.example%2Fhelloworld', + ); + }); +}