[google_sign_in] Add forceCodeForRefreshToken parameter platform implementations (#6130)

diff --git a/packages/google_sign_in/google_sign_in_android/AUTHORS b/packages/google_sign_in/google_sign_in_android/AUTHORS
index 493a0b4..35d24a5 100644
--- a/packages/google_sign_in/google_sign_in_android/AUTHORS
+++ b/packages/google_sign_in/google_sign_in_android/AUTHORS
@@ -64,3 +64,4 @@
 Anton Borries <mail@antonborri.es>
 Alex Li <google@alexv525.com>
 Rahul Raj <64.rahulraj@gmail.com>
+Twin Sun, LLC <google-contrib@twinsunsolutions.com>
diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md
index 9852a1f..5125b22 100644
--- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md
+++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 6.1.0
+
+* Adds override for `GoogleSignIn.initWithParams` to handle new `forceCodeForRefreshToken` parameter.
+
 ## 6.0.1
 
 * Updates gradle version to 7.2.1 on Android.
diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java
index 2164023..e84d196 100644
--- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java
+++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java
@@ -140,8 +140,15 @@
         String hostedDomain = call.argument("hostedDomain");
         String clientId = call.argument("clientId");
         String serverClientId = call.argument("serverClientId");
+        boolean forceCodeForRefreshToken = call.argument("forceCodeForRefreshToken");
         delegate.init(
-            result, signInOption, requestedScopes, hostedDomain, clientId, serverClientId);
+            result,
+            signInOption,
+            requestedScopes,
+            hostedDomain,
+            clientId,
+            serverClientId,
+            forceCodeForRefreshToken);
         break;
 
       case METHOD_SIGN_IN_SILENTLY:
@@ -198,7 +205,8 @@
         List<String> requestedScopes,
         String hostedDomain,
         String clientId,
-        String serverClientId);
+        String serverClientId,
+        boolean forceCodeForRefreshToken);
 
     /**
      * Returns the account information for the user who is signed in to this app. If no user is
@@ -326,7 +334,8 @@
         List<String> requestedScopes,
         String hostedDomain,
         String clientId,
-        String serverClientId) {
+        String serverClientId,
+        boolean forceCodeForRefreshToken) {
       try {
         GoogleSignInOptions.Builder optionsBuilder;
 
@@ -374,7 +383,7 @@
         }
         if (!Strings.isNullOrEmpty(serverClientId)) {
           optionsBuilder.requestIdToken(serverClientId);
-          optionsBuilder.requestServerAuthCode(serverClientId);
+          optionsBuilder.requestServerAuthCode(serverClientId, forceCodeForRefreshToken);
         }
         for (String scope : requestedScopes) {
           optionsBuilder.requestScopes(new Scope(scope));
diff --git a/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java
index 11f8cda..9692417 100644
--- a/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java
+++ b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java
@@ -239,6 +239,48 @@
     initAndAssertServerClientId(methodCall, serverClientId);
   }
 
+  @Test
+  public void init_PassesForceCodeForRefreshTokenFalseWithServerClientIdParameter() {
+    MethodCall methodCall = buildInitMethodCall("fakeClientId", "fakeServerClientId", false);
+
+    initAndAssertForceCodeForRefreshToken(methodCall, false);
+  }
+
+  @Test
+  public void init_PassesForceCodeForRefreshTokenTrueWithServerClientIdParameter() {
+    MethodCall methodCall = buildInitMethodCall("fakeClientId", "fakeServerClientId", true);
+
+    initAndAssertForceCodeForRefreshToken(methodCall, true);
+  }
+
+  @Test
+  public void init_PassesForceCodeForRefreshTokenFalseWithServerClientIdFromResources() {
+    final String packageName = "fakePackageName";
+    final String serverClientId = "fakeServerClientId";
+    final int resourceId = 1;
+    MethodCall methodCall = buildInitMethodCall(null, null, false);
+    when(mockContext.getPackageName()).thenReturn(packageName);
+    when(mockResources.getIdentifier("default_web_client_id", "string", packageName))
+        .thenReturn(resourceId);
+    when(mockContext.getString(resourceId)).thenReturn(serverClientId);
+
+    initAndAssertForceCodeForRefreshToken(methodCall, false);
+  }
+
+  @Test
+  public void init_PassesForceCodeForRefreshTokenTrueWithServerClientIdFromResources() {
+    final String packageName = "fakePackageName";
+    final String serverClientId = "fakeServerClientId";
+    final int resourceId = 1;
+    MethodCall methodCall = buildInitMethodCall(null, null, true);
+    when(mockContext.getPackageName()).thenReturn(packageName);
+    when(mockResources.getIdentifier("default_web_client_id", "string", packageName))
+        .thenReturn(resourceId);
+    when(mockContext.getString(resourceId)).thenReturn(serverClientId);
+
+    initAndAssertForceCodeForRefreshToken(methodCall, true);
+  }
+
   public void initAndAssertServerClientId(MethodCall methodCall, String serverClientId) {
     ArgumentCaptor<GoogleSignInOptions> optionsCaptor =
         ArgumentCaptor.forClass(GoogleSignInOptions.class);
@@ -249,13 +291,39 @@
     Assert.assertEquals(serverClientId, optionsCaptor.getValue().getServerClientId());
   }
 
+  public void initAndAssertForceCodeForRefreshToken(
+      MethodCall methodCall, boolean forceCodeForRefreshToken) {
+    ArgumentCaptor<GoogleSignInOptions> optionsCaptor =
+        ArgumentCaptor.forClass(GoogleSignInOptions.class);
+    when(mockGoogleSignIn.getClient(any(Context.class), optionsCaptor.capture()))
+        .thenReturn(mockClient);
+    plugin.onMethodCall(methodCall, result);
+    verify(result).success(null);
+    Assert.assertEquals(
+        forceCodeForRefreshToken, optionsCaptor.getValue().isForceCodeForRefreshToken());
+  }
+
   private static MethodCall buildInitMethodCall(String clientId, String serverClientId) {
     return buildInitMethodCall(
-        "SignInOption.standard", Collections.<String>emptyList(), clientId, serverClientId);
+        "SignInOption.standard", Collections.<String>emptyList(), clientId, serverClientId, false);
   }
 
   private static MethodCall buildInitMethodCall(
-      String signInOption, List<String> scopes, String clientId, String serverClientId) {
+      String clientId, String serverClientId, boolean forceCodeForRefreshToken) {
+    return buildInitMethodCall(
+        "SignInOption.standard",
+        Collections.<String>emptyList(),
+        clientId,
+        serverClientId,
+        forceCodeForRefreshToken);
+  }
+
+  private static MethodCall buildInitMethodCall(
+      String signInOption,
+      List<String> scopes,
+      String clientId,
+      String serverClientId,
+      boolean forceCodeForRefreshToken) {
     HashMap<String, Object> arguments = new HashMap<>();
     arguments.put("signInOption", signInOption);
     arguments.put("scopes", scopes);
@@ -265,6 +333,7 @@
     if (serverClientId != null) {
       arguments.put("serverClientId", serverClientId);
     }
+    arguments.put("forceCodeForRefreshToken", forceCodeForRefreshToken);
     return new MethodCall("init", arguments);
   }
 }
diff --git a/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart b/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart
index d96328d..731da39 100644
--- a/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart
+++ b/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart
@@ -30,11 +30,22 @@
     String? hostedDomain,
     String? clientId,
   }) {
+    return initWithParams(SignInInitParameters(
+      signInOption: signInOption,
+      scopes: scopes,
+      hostedDomain: hostedDomain,
+      clientId: clientId,
+    ));
+  }
+
+  @override
+  Future<void> initWithParams(SignInInitParameters params) {
     return channel.invokeMethod<void>('init', <String, dynamic>{
-      'signInOption': signInOption.toString(),
-      'scopes': scopes,
-      'hostedDomain': hostedDomain,
-      'clientId': clientId,
+      'signInOption': params.signInOption.toString(),
+      'scopes': params.scopes,
+      'hostedDomain': params.hostedDomain,
+      'clientId': params.clientId,
+      'forceCodeForRefreshToken': params.forceCodeForRefreshToken,
     });
   }
 
diff --git a/packages/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml
index 0a863dc..de5e3f0 100644
--- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml
+++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Android implementation of the google_sign_in plugin.
 repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_android
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22
-version: 6.0.1
+version: 6.1.0
 
 environment:
   sdk: ">=2.14.0 <3.0.0"
diff --git a/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart b/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart
index 7d39ae5..948ced3 100644
--- a/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart
+++ b/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart
@@ -111,6 +111,21 @@
         'scopes': <String>['two', 'scopes'],
         'signInOption': 'SignInOption.games',
         'clientId': 'fakeClientId',
+        'forceCodeForRefreshToken': false,
+      }),
+      () {
+        googleSignIn.initWithParams(const SignInInitParameters(
+            hostedDomain: 'example.com',
+            scopes: <String>['two', 'scopes'],
+            signInOption: SignInOption.games,
+            clientId: 'fakeClientId',
+            forceCodeForRefreshToken: true));
+      }: isMethodCall('init', arguments: <String, dynamic>{
+        'hostedDomain': 'example.com',
+        'scopes': <String>['two', 'scopes'],
+        'signInOption': 'SignInOption.games',
+        'clientId': 'fakeClientId',
+        'forceCodeForRefreshToken': true,
       }),
       () {
         googleSignIn.getTokens(
diff --git a/packages/google_sign_in/google_sign_in_ios/AUTHORS b/packages/google_sign_in/google_sign_in_ios/AUTHORS
index 493a0b4..35d24a5 100644
--- a/packages/google_sign_in/google_sign_in_ios/AUTHORS
+++ b/packages/google_sign_in/google_sign_in_ios/AUTHORS
@@ -64,3 +64,4 @@
 Anton Borries <mail@antonborri.es>
 Alex Li <google@alexv525.com>
 Rahul Raj <64.rahulraj@gmail.com>
+Twin Sun, LLC <google-contrib@twinsunsolutions.com>
diff --git a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md
index 5ed38de..2b62f8a 100644
--- a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md
+++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 5.5.0
+
+* Adds override for `GoogleSignInPlatform.initWithParams`.
+
 ## 5.4.0
 
 * Adds support for `serverClientId` configuration option.
diff --git a/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart b/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart
index ce88656..07407ea 100644
--- a/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart
+++ b/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart
@@ -30,15 +30,25 @@
     String? hostedDomain,
     String? clientId,
   }) {
-    if (signInOption == SignInOption.games) {
+    return initWithParams(SignInInitParameters(
+      signInOption: signInOption,
+      scopes: scopes,
+      hostedDomain: hostedDomain,
+      clientId: clientId,
+    ));
+  }
+
+  @override
+  Future<void> initWithParams(SignInInitParameters params) {
+    if (params.signInOption == SignInOption.games) {
       throw PlatformException(
           code: 'unsupported-options',
           message: 'Games sign in is not supported on iOS');
     }
     return channel.invokeMethod<void>('init', <String, dynamic>{
-      'scopes': scopes,
-      'hostedDomain': hostedDomain,
-      'clientId': clientId,
+      'scopes': params.scopes,
+      'hostedDomain': params.hostedDomain,
+      'clientId': params.clientId,
     });
   }
 
diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml
index 65c8928..760e333 100644
--- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml
+++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml
@@ -2,7 +2,7 @@
 description: iOS implementation of the google_sign_in plugin.
 repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_ios
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22
-version: 5.4.0
+version: 5.5.0
 
 environment:
   sdk: ">=2.14.0 <3.0.0"
diff --git a/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart b/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart
index 92637e9..ace6509 100644
--- a/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart
+++ b/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart
@@ -126,6 +126,16 @@
         'clientId': 'fakeClientId',
       }),
       () {
+        googleSignIn.initWithParams(const SignInInitParameters(
+            hostedDomain: 'example.com',
+            scopes: <String>['two', 'scopes'],
+            clientId: 'fakeClientId'));
+      }: isMethodCall('init', arguments: <String, dynamic>{
+        'hostedDomain': 'example.com',
+        'scopes': <String>['two', 'scopes'],
+        'clientId': 'fakeClientId',
+      }),
+      () {
         googleSignIn.getTokens(
             email: 'example@example.com', shouldRecoverAuth: false);
       }: isMethodCall('getTokens', arguments: <String, dynamic>{