[in_app_purchases] Android: make sure properly disconnect the billing client object. (#2509)

diff --git a/packages/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/CHANGELOG.md
index d85a879..02eda42 100644
--- a/packages/in_app_purchase/CHANGELOG.md
+++ b/packages/in_app_purchase/CHANGELOG.md
@@ -1,6 +1,13 @@
+## 0.3.1
+
+* Android: Fix a bug where the `BillingClient` is disconnected when app goes to the background.
+* Android: Make sure the `BillingClient` object is disconnected before the activity is destroyed.
+* Android: Fix minor compiler warning.
+* Fix typo in CHANGELOG.
+
 ## 0.3.0+3
 
-* Fix pendingCompletePurchase flag status to allow to complete the pruchsase. 
+* Fix pendingCompletePurchase flag status to allow to complete purchases.
 
 ## 0.3.0+2
 
diff --git a/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/InAppPurchasePlugin.java b/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/InAppPurchasePlugin.java
index a9302d1..b7edcfb 100644
--- a/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/InAppPurchasePlugin.java
+++ b/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/InAppPurchasePlugin.java
@@ -5,6 +5,7 @@
 package io.flutter.plugins.inapppurchase;
 
 import android.app.Activity;
+import android.app.Application;
 import android.content.Context;
 import androidx.annotation.VisibleForTesting;
 import com.android.billingclient.api.BillingClient;
@@ -49,14 +50,14 @@
   public static void registerWith(Registrar registrar) {
     InAppPurchasePlugin plugin = new InAppPurchasePlugin();
     plugin.setupMethodChannel(registrar.activity(), registrar.messenger(), registrar.context());
+    ((Application) registrar.context())
+        .registerActivityLifecycleCallbacks(plugin.methodCallHandler);
   }
 
   @Override
   public void onAttachedToEngine(FlutterPlugin.FlutterPluginBinding binding) {
     setupMethodChannel(
-        /*activity=*/ null,
-        binding.getFlutterEngine().getDartExecutor(),
-        binding.getApplicationContext());
+        /*activity=*/ null, binding.getBinaryMessenger(), binding.getApplicationContext());
   }
 
   @Override
@@ -72,6 +73,7 @@
   @Override
   public void onDetachedFromActivity() {
     methodCallHandler.setActivity(null);
+    methodCallHandler.onDetachedFromActivity();
   }
 
   @Override
@@ -81,7 +83,7 @@
 
   @Override
   public void onDetachedFromActivityForConfigChanges() {
-    onDetachedFromActivity();
+    methodCallHandler.setActivity(null);
   }
 
   private void setupMethodChannel(Activity activity, BinaryMessenger messenger, Context context) {
@@ -96,4 +98,9 @@
     methodChannel = null;
     methodCallHandler = null;
   }
+
+  @VisibleForTesting
+  void setMethodCallHandler(MethodCallHandlerImpl methodCallHandler) {
+    this.methodCallHandler = methodCallHandler;
+  }
 }
diff --git a/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java b/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java
index 9108ab3..335d4b8 100644
--- a/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java
+++ b/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java
@@ -9,7 +9,9 @@
 import static io.flutter.plugins.inapppurchase.Translator.fromSkuDetailsList;
 
 import android.app.Activity;
+import android.app.Application;
 import android.content.Context;
+import android.os.Bundle;
 import android.util.Log;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -33,7 +35,8 @@
 import java.util.Map;
 
 /** Handles method channel for the plugin. */
-class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
+class MethodCallHandlerImpl
+    implements MethodChannel.MethodCallHandler, Application.ActivityLifecycleCallbacks {
 
   private static final String TAG = "InAppPurchasePlugin";
 
@@ -67,6 +70,36 @@
   }
 
   @Override
+  public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
+
+  @Override
+  public void onActivityStarted(Activity activity) {}
+
+  @Override
+  public void onActivityResumed(Activity activity) {}
+
+  @Override
+  public void onActivityPaused(Activity activity) {}
+
+  @Override
+  public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
+
+  @Override
+  public void onActivityDestroyed(Activity activity) {
+    if (this.activity == activity && this.applicationContext != null) {
+      ((Application) this.applicationContext).unregisterActivityLifecycleCallbacks(this);
+      endBillingClientConnection();
+    }
+  }
+
+  @Override
+  public void onActivityStopped(Activity activity) {}
+
+  void onDetachedFromActivity() {
+    endBillingClientConnection();
+  }
+
+  @Override
   public void onMethodCall(MethodCall call, MethodChannel.Result result) {
     switch (call.method) {
       case InAppPurchasePlugin.MethodNames.IS_READY:
@@ -113,11 +146,15 @@
   }
 
   private void endConnection(final MethodChannel.Result result) {
+    endBillingClientConnection();
+    result.success(null);
+  }
+
+  private void endBillingClientConnection() {
     if (billingClient != null) {
       billingClient.endConnection();
       billingClient = null;
     }
-    result.success(null);
   }
 
   private void isReady(MethodChannel.Result result) {
diff --git a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java
index be00ac4..c6a9b41 100644
--- a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java
+++ b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java
@@ -52,6 +52,7 @@
 import com.android.billingclient.api.SkuDetails;
 import com.android.billingclient.api.SkuDetailsParams;
 import com.android.billingclient.api.SkuDetailsResponseListener;
+import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
 import io.flutter.plugin.common.MethodCall;
 import io.flutter.plugin.common.MethodChannel;
 import io.flutter.plugin.common.MethodChannel.Result;
@@ -73,6 +74,7 @@
   @Spy Result result;
   @Mock Activity activity;
   @Mock Context context;
+  @Mock ActivityPluginBinding mockActivityPluginBinding;
 
   @Before
   public void setUp() {
@@ -82,6 +84,7 @@
             @NonNull MethodChannel channel,
             boolean enablePendingPurchases) -> mockBillingClient;
     methodChannelHandler = new MethodCallHandlerImpl(activity, context, mockMethodChannel, factory);
+    when(mockActivityPluginBinding.getActivity()).thenReturn(activity);
   }
 
   @Test
@@ -551,6 +554,15 @@
     verify(result, times(1)).success(fromBillingResult(billingResult));
   }
 
+  @Test
+  public void endConnection_if_activity_dettached() {
+    InAppPurchasePlugin plugin = new InAppPurchasePlugin();
+    plugin.setMethodCallHandler(methodChannelHandler);
+    mockStartConnection();
+    plugin.onDetachedFromActivity();
+    verify(mockBillingClient).endConnection();
+  }
+
   private ArgumentCaptor<BillingClientStateListener> mockStartConnection() {
     Map<String, Object> arguments = new HashMap<>();
     arguments.put("handle", 1);
diff --git a/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart b/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart
index f2cd87b..581a7bd 100644
--- a/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart
+++ b/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart
@@ -161,19 +161,6 @@
         'The method <refreshPurchaseVerificationData> only works on iOS.');
   }
 
-  @override
-  void didChangeAppLifecycleState(AppLifecycleState state) {
-    switch (state) {
-      case AppLifecycleState.paused:
-        _disconnect();
-        break;
-      case AppLifecycleState.resumed:
-        _readyFuture = _connect();
-        break;
-      default:
-    }
-  }
-
   @visibleForTesting
   static void reset() => _instance = null;
 
@@ -189,8 +176,6 @@
   Future<void> _connect() =>
       billingClient.startConnection(onBillingServiceDisconnected: () {});
 
-  Future<void> _disconnect() => billingClient.endConnection();
-
   /// Query the product detail list.
   ///
   /// This method only returns [ProductDetailsResponse].
diff --git a/packages/in_app_purchase/test/in_app_purchase_connection/google_play_connection_test.dart b/packages/in_app_purchase/test/in_app_purchase_connection/google_play_connection_test.dart
index dfad32d..f06c4ff 100644
--- a/packages/in_app_purchase/test/in_app_purchase_connection/google_play_connection_test.dart
+++ b/packages/in_app_purchase/test/in_app_purchase_connection/google_play_connection_test.dart
@@ -56,18 +56,6 @@
     test('connects on initialization', () {
       expect(stubPlatform.countPreviousCalls(startConnectionCall), equals(1));
     });
-
-    test('disconnects on app pause', () {
-      expect(stubPlatform.countPreviousCalls(endConnectionCall), equals(0));
-      connection.didChangeAppLifecycleState(AppLifecycleState.paused);
-      expect(stubPlatform.countPreviousCalls(endConnectionCall), equals(1));
-    });
-
-    test('reconnects on app resume', () {
-      expect(stubPlatform.countPreviousCalls(startConnectionCall), equals(1));
-      connection.didChangeAppLifecycleState(AppLifecycleState.resumed);
-      expect(stubPlatform.countPreviousCalls(startConnectionCall), equals(2));
-    });
   });
 
   group('isAvailable', () {