From 68eb9fa4364ec533e2c2853effea2eb4a55480aa Mon Sep 17 00:00:00 2001 From: Maurice Parrish Date: Wed, 11 Dec 2019 15:27:42 -0800 Subject: [PATCH] [firebase_admob] Support v2 embedding (#1365) --- packages/firebase_admob/CHANGELOG.md | 5 + packages/firebase_admob/android/build.gradle | 26 +++++ .../firebase_admob/android/gradle.properties | 1 - .../android/src/main/AndroidManifest.xml | 5 - .../firebaseadmob/FirebaseAdMobPlugin.java | 98 +++++++++++++++---- .../plugins/firebaseadmob/MobileAd.java | 9 +- .../example/android/app/build.gradle | 1 + .../EmbeddingV1ActivityTest.java | 14 +++ .../firebaseadmob/MainActivityTest.java | 12 +++ .../android/app/src/main/AndroidManifest.xml | 8 +- .../EmbeddingV1Activity.java | 17 ++++ .../firebaseadmobexample/MainActivity.java | 14 +-- .../example/android/gradle.properties | 1 + packages/firebase_admob/example/pubspec.yaml | 10 +- packages/firebase_admob/pubspec.yaml | 7 +- .../test/firebase_admob_e2e.dart | 15 +++ 16 files changed, 208 insertions(+), 35 deletions(-) delete mode 100644 packages/firebase_admob/android/gradle.properties create mode 100644 packages/firebase_admob/example/android/app/src/androidTest/java/io/flutter/plugins/firebaseadmob/EmbeddingV1ActivityTest.java create mode 100644 packages/firebase_admob/example/android/app/src/androidTest/java/io/flutter/plugins/firebaseadmob/MainActivityTest.java create mode 100644 packages/firebase_admob/example/android/app/src/main/java/io/flutter/plugins/firebaseadmobexample/EmbeddingV1Activity.java create mode 100644 packages/firebase_admob/test/firebase_admob_e2e.dart diff --git a/packages/firebase_admob/CHANGELOG.md b/packages/firebase_admob/CHANGELOG.md index 20e8c9fe091c..ccd0aefc6f76 100644 --- a/packages/firebase_admob/CHANGELOG.md +++ b/packages/firebase_admob/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.9.1 + +* Support v2 embedding. This will remain compatible with the original embedding and won't require + app migration. + ## 0.9.0+10 * Remove the deprecated `author:` field from pubspec.yaml diff --git a/packages/firebase_admob/android/build.gradle b/packages/firebase_admob/android/build.gradle index 0d987e7cfeb2..610d3dca8398 100644 --- a/packages/firebase_admob/android/build.gradle +++ b/packages/firebase_admob/android/build.gradle @@ -35,3 +35,29 @@ android { api 'com.google.firebase:firebase-ads:18.1.1' } } + +// 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_admob/android/gradle.properties b/packages/firebase_admob/android/gradle.properties deleted file mode 100644 index 8bd86f680510..000000000000 --- a/packages/firebase_admob/android/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M diff --git a/packages/firebase_admob/android/src/main/AndroidManifest.xml b/packages/firebase_admob/android/src/main/AndroidManifest.xml index 42a48978de0d..8bc550135c83 100644 --- a/packages/firebase_admob/android/src/main/AndroidManifest.xml +++ b/packages/firebase_admob/android/src/main/AndroidManifest.xml @@ -1,9 +1,4 @@ - - - diff --git a/packages/firebase_admob/android/src/main/java/io/flutter/plugins/firebaseadmob/FirebaseAdMobPlugin.java b/packages/firebase_admob/android/src/main/java/io/flutter/plugins/firebaseadmob/FirebaseAdMobPlugin.java index dd058a3e3bc0..62840cff420b 100644 --- a/packages/firebase_admob/android/src/main/java/io/flutter/plugins/firebaseadmob/FirebaseAdMobPlugin.java +++ b/packages/firebase_admob/android/src/main/java/io/flutter/plugins/firebaseadmob/FirebaseAdMobPlugin.java @@ -5,10 +5,15 @@ package io.flutter.plugins.firebaseadmob; import android.app.Activity; +import android.content.Context; import android.view.Gravity; import com.google.android.gms.ads.AdSize; import com.google.android.gms.ads.MobileAds; import com.google.firebase.FirebaseApp; +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; @@ -17,29 +22,50 @@ import java.util.Locale; import java.util.Map; -public class FirebaseAdMobPlugin implements MethodCallHandler { - - private final Registrar registrar; - private final MethodChannel channel; - - RewardedVideoAdWrapper rewardedWrapper; - +/** + * Flutter plugin accessing Firebase Admob API. + * + *

Instantiate this in an add to app scenario to gracefully handle activity and context changes. + */ +public class FirebaseAdMobPlugin implements FlutterPlugin, ActivityAware, MethodCallHandler { + private Context applicationContext; + private MethodChannel channel; + private Activity activity; + // This is always null when not using v2 embedding. + private FlutterPluginBinding pluginBinding; + private RewardedVideoAdWrapper rewardedWrapper; + + /** + * 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 connects this plugin's {@link + * io.flutter.plugin.common.MethodChannel.MethodCallHandler} to its {@link + * io.flutter.plugin.common.BinaryMessenger}. + */ public static void registerWith(Registrar registrar) { if (registrar.activity() == null) { // If a background Flutter view tries to register the plugin, there will be no activity from the registrar. // We stop the registering process immediately because the firebase_admob requires an activity. return; } - final MethodChannel channel = - new MethodChannel(registrar.messenger(), "plugins.flutter.io/firebase_admob"); - channel.setMethodCallHandler(new FirebaseAdMobPlugin(registrar, channel)); + + final FirebaseAdMobPlugin plugin = new FirebaseAdMobPlugin(); + plugin.initializePlugin(registrar.context(), registrar.activity(), registrar.messenger()); } - private FirebaseAdMobPlugin(Registrar registrar, MethodChannel channel) { - this.registrar = registrar; - this.channel = channel; - FirebaseApp.initializeApp(registrar.context()); - rewardedWrapper = new RewardedVideoAdWrapper(registrar.activity(), channel); + private void initializePlugin( + Context applicationContext, Activity activity, BinaryMessenger messenger) { + this.activity = activity; + this.applicationContext = applicationContext; + FirebaseApp.initializeApp(applicationContext); + + this.channel = new MethodChannel(messenger, "plugins.flutter.io/firebase_admob"); + channel.setMethodCallHandler(this); + + rewardedWrapper = new RewardedVideoAdWrapper(activity, channel); } private void callInitialize(MethodCall call, Result result) { @@ -48,7 +74,7 @@ private void callInitialize(MethodCall call, Result result) { result.error("no_app_id", "a null or empty AdMob appId was provided", null); return; } - MobileAds.initialize(registrar.context(), appId); + MobileAds.initialize(applicationContext, appId); result.success(Boolean.TRUE); } @@ -203,9 +229,45 @@ private void callDisposeAd(Integer id, Result result) { } @Override - public void onMethodCall(MethodCall call, Result result) { + public void onAttachedToEngine(FlutterPluginBinding binding) { + pluginBinding = binding; + } + + @Override + public void onDetachedFromEngine(FlutterPluginBinding binding) { + pluginBinding = null; + } - Activity activity = registrar.activity(); + @Override + public void onAttachedToActivity(ActivityPluginBinding binding) { + initializePlugin( + pluginBinding.getApplicationContext(), + binding.getActivity(), + pluginBinding.getFlutterEngine().getDartExecutor()); + } + + @Override + public void onDetachedFromActivityForConfigChanges() { + MobileAd.disposeAll(); + activity = null; + } + + @Override + public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) { + initializePlugin( + pluginBinding.getApplicationContext(), + binding.getActivity(), + pluginBinding.getFlutterEngine().getDartExecutor()); + } + + @Override + public void onDetachedFromActivity() { + MobileAd.disposeAll(); + activity = null; + } + + @Override + public void onMethodCall(MethodCall call, Result result) { if (activity == null) { result.error("no_activity", "firebase_admob plugin requires a foreground activity", null); return; diff --git a/packages/firebase_admob/android/src/main/java/io/flutter/plugins/firebaseadmob/MobileAd.java b/packages/firebase_admob/android/src/main/java/io/flutter/plugins/firebaseadmob/MobileAd.java index 95976faa3f27..237a0db092a6 100644 --- a/packages/firebase_admob/android/src/main/java/io/flutter/plugins/firebaseadmob/MobileAd.java +++ b/packages/firebase_admob/android/src/main/java/io/flutter/plugins/firebaseadmob/MobileAd.java @@ -28,7 +28,7 @@ abstract class MobileAd extends AdListener { double horizontalCenterOffset; int anchorType; - enum Status { + public enum Status { CREATED, LOADING, FAILED, @@ -73,6 +73,13 @@ void dispose() { allAds.remove(id); } + static void disposeAll() { + for (int i = 0; i < allAds.size(); i++) { + allAds.valueAt(i).dispose(); + } + allAds.clear(); + } + private Map argumentsMap(Object... args) { Map arguments = new HashMap(); arguments.put("id", id); diff --git a/packages/firebase_admob/example/android/app/build.gradle b/packages/firebase_admob/example/android/app/build.gradle index b3b7fbca28b4..f3a34c0b5cdd 100644 --- a/packages/firebase_admob/example/android/app/build.gradle +++ b/packages/firebase_admob/example/android/app/build.gradle @@ -56,6 +56,7 @@ flutter { dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/packages/firebase_admob/example/android/app/src/androidTest/java/io/flutter/plugins/firebaseadmob/EmbeddingV1ActivityTest.java b/packages/firebase_admob/example/android/app/src/androidTest/java/io/flutter/plugins/firebaseadmob/EmbeddingV1ActivityTest.java new file mode 100644 index 000000000000..cb8698270a8e --- /dev/null +++ b/packages/firebase_admob/example/android/app/src/androidTest/java/io/flutter/plugins/firebaseadmob/EmbeddingV1ActivityTest.java @@ -0,0 +1,14 @@ +package io.flutter.plugins.firebaseadmob; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.e2e.FlutterRunner; +import io.flutter.plugins.firebaseadmobexample.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_admob/example/android/app/src/androidTest/java/io/flutter/plugins/firebaseadmob/MainActivityTest.java b/packages/firebase_admob/example/android/app/src/androidTest/java/io/flutter/plugins/firebaseadmob/MainActivityTest.java new file mode 100644 index 000000000000..5ccad27cbd3a --- /dev/null +++ b/packages/firebase_admob/example/android/app/src/androidTest/java/io/flutter/plugins/firebaseadmob/MainActivityTest.java @@ -0,0 +1,12 @@ +package io.flutter.plugins.firebaseadmob; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.e2e.FlutterRunner; +import io.flutter.plugins.firebaseadmobexample.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_admob/example/android/app/src/main/AndroidManifest.xml b/packages/firebase_admob/example/android/app/src/main/AndroidManifest.xml index 2e3a97d95f94..ac5c3f67a279 100644 --- a/packages/firebase_admob/example/android/app/src/main/AndroidManifest.xml +++ b/packages/firebase_admob/example/android/app/src/main/AndroidManifest.xml @@ -8,7 +8,6 @@ android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3940256099942544~3347511713"/> + + diff --git a/packages/firebase_admob/example/android/app/src/main/java/io/flutter/plugins/firebaseadmobexample/EmbeddingV1Activity.java b/packages/firebase_admob/example/android/app/src/main/java/io/flutter/plugins/firebaseadmobexample/EmbeddingV1Activity.java new file mode 100644 index 000000000000..85ffa53f8b62 --- /dev/null +++ b/packages/firebase_admob/example/android/app/src/main/java/io/flutter/plugins/firebaseadmobexample/EmbeddingV1Activity.java @@ -0,0 +1,17 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.firebaseadmobexample; + +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_admob/example/android/app/src/main/java/io/flutter/plugins/firebaseadmobexample/MainActivity.java b/packages/firebase_admob/example/android/app/src/main/java/io/flutter/plugins/firebaseadmobexample/MainActivity.java index 14775bbf2fb7..b3b3d1cc9009 100644 --- a/packages/firebase_admob/example/android/app/src/main/java/io/flutter/plugins/firebaseadmobexample/MainActivity.java +++ b/packages/firebase_admob/example/android/app/src/main/java/io/flutter/plugins/firebaseadmobexample/MainActivity.java @@ -4,14 +4,16 @@ package io.flutter.plugins.firebaseadmobexample; -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.firebaseadmob.FirebaseAdMobPlugin; 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 E2EPlugin()); + flutterEngine.getPlugins().add(new FirebaseAdMobPlugin()); } } diff --git a/packages/firebase_admob/example/android/gradle.properties b/packages/firebase_admob/example/android/gradle.properties index 08f2b5f91bff..5c8fc8999131 100644 --- a/packages/firebase_admob/example/android/gradle.properties +++ b/packages/firebase_admob/example/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true android.enableJetifier=true android.useAndroidX=true diff --git a/packages/firebase_admob/example/pubspec.yaml b/packages/firebase_admob/example/pubspec.yaml index 775e4852b10a..4c3fe3746cd1 100644 --- a/packages/firebase_admob/example/pubspec.yaml +++ b/packages/firebase_admob/example/pubspec.yaml @@ -6,7 +6,15 @@ dependencies: sdk: flutter firebase_admob: path: ../ - firebase_core: ^0.4.0 + firebase_core: ^0.4.2+1 + +dev_dependencies: + e2e: ^0.2.1 + flutter_driver: + sdk: flutter flutter: uses-material-design: true + +environment: + sdk: ">=2.0.0-dev.28.0 <3.0.0" diff --git a/packages/firebase_admob/pubspec.yaml b/packages/firebase_admob/pubspec.yaml index 355d52cf1bb2..03e3a1325843 100644 --- a/packages/firebase_admob/pubspec.yaml +++ b/packages/firebase_admob/pubspec.yaml @@ -2,7 +2,7 @@ name: firebase_admob description: Flutter plugin for Firebase AdMob, supporting banner, interstitial (full-screen), and rewarded video ads homepage: https://github.com/FirebaseExtended/flutterfire/tree/master/packages/firebase_admob -version: 0.9.0+10 +version: 0.9.1 flutter: plugin: @@ -18,12 +18,15 @@ dependencies: platform: ^2.0.0 flutter: sdk: flutter + firebase_core: ^0.4.2+1 dev_dependencies: + e2e: ^0.2.1 + flutter_driver: + sdk: flutter mockito: ^3.0.0 flutter_test: sdk: flutter - firebase_core: ^0.4.0 environment: sdk: ">=2.0.0-dev.28.0 <3.0.0" diff --git a/packages/firebase_admob/test/firebase_admob_e2e.dart b/packages/firebase_admob/test/firebase_admob_e2e.dart new file mode 100644 index 000000000000..494363a20640 --- /dev/null +++ b/packages/firebase_admob/test/firebase_admob_e2e.dart @@ -0,0 +1,15 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:e2e/e2e.dart'; + +import 'package:firebase_admob/firebase_admob.dart'; + +void main() { + E2EWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Initialize Firebase Admob', (WidgetTester tester) async { + expect( + FirebaseAdMob.instance.initialize(appId: FirebaseAdMob.testAppId), + completion(isTrue), + ); + }); +}