2020import static io .flutter .plugins .inapppurchase .MethodCallHandlerImpl .MethodNames .QUERY_PURCHASE_HISTORY_ASYNC ;
2121import static io .flutter .plugins .inapppurchase .MethodCallHandlerImpl .MethodNames .SHOW_ALTERNATIVE_BILLING_ONLY_INFORMATION_DIALOG ;
2222import static io .flutter .plugins .inapppurchase .MethodCallHandlerImpl .MethodNames .START_CONNECTION ;
23+ import static io .flutter .plugins .inapppurchase .MethodCallHandlerImpl .MethodNames .USER_SELECTED_ALTERNATIVE_BILLING ;
2324import static io .flutter .plugins .inapppurchase .PluginPurchaseListener .ON_PURCHASES_UPDATED ;
2425import static io .flutter .plugins .inapppurchase .Translator .fromAlternativeBillingOnlyReportingDetails ;
2526import static io .flutter .plugins .inapppurchase .Translator .fromBillingConfig ;
2627import static io .flutter .plugins .inapppurchase .Translator .fromBillingResult ;
2728import static io .flutter .plugins .inapppurchase .Translator .fromProductDetailsList ;
2829import static io .flutter .plugins .inapppurchase .Translator .fromPurchaseHistoryRecordList ;
2930import static io .flutter .plugins .inapppurchase .Translator .fromPurchasesList ;
31+ import static io .flutter .plugins .inapppurchase .Translator .fromUserChoiceDetails ;
3032import static java .util .Arrays .asList ;
3133import static java .util .Collections .singletonList ;
3234import static java .util .Collections .unmodifiableList ;
7375import com .android .billingclient .api .QueryProductDetailsParams ;
7476import com .android .billingclient .api .QueryPurchaseHistoryParams ;
7577import com .android .billingclient .api .QueryPurchasesParams ;
78+ import com .android .billingclient .api .UserChoiceBillingListener ;
79+ import com .android .billingclient .api .UserChoiceDetails ;
7680import io .flutter .embedding .engine .plugins .activity .ActivityPluginBinding ;
7781import io .flutter .plugin .common .MethodCall ;
7882import io .flutter .plugin .common .MethodChannel ;
8286import java .lang .reflect .Constructor ;
8387import java .lang .reflect .InvocationTargetException ;
8488import java .util .ArrayList ;
89+ import java .util .Collections ;
8590import java .util .HashMap ;
8691import java .util .List ;
8792import java .util .Map ;
9297import org .mockito .ArgumentCaptor ;
9398import org .mockito .Captor ;
9499import org .mockito .Mock ;
100+ import org .mockito .Mockito ;
95101import org .mockito .MockitoAnnotations ;
96102import org .mockito .Spy ;
97103import org .mockito .stubbing .Answer ;
@@ -107,15 +113,23 @@ public class MethodCallHandlerTest {
107113 @ Mock ActivityPluginBinding mockActivityPluginBinding ;
108114 @ Captor ArgumentCaptor <HashMap <String , Object >> resultCaptor ;
109115
116+ private final int DEFAULT_HANDLE = 1 ;
117+
110118 @ Before
111119 public void setUp () {
112120 MockitoAnnotations .openMocks (this );
113121 // Use the same client no matter if alternative billing is enabled or not.
114122 when (factory .createBillingClient (
115- context , mockMethodChannel , BillingChoiceMode .PLAY_BILLING_ONLY ))
123+ context , mockMethodChannel , BillingChoiceMode .PLAY_BILLING_ONLY , null ))
124+ .thenReturn (mockBillingClient );
125+ when (factory .createBillingClient (
126+ context , mockMethodChannel , BillingChoiceMode .ALTERNATIVE_BILLING_ONLY , null ))
116127 .thenReturn (mockBillingClient );
117128 when (factory .createBillingClient (
118- context , mockMethodChannel , BillingChoiceMode .ALTERNATIVE_BILLING_ONLY ))
129+ any (Context .class ),
130+ any (MethodChannel .class ),
131+ eq (BillingChoiceMode .USER_CHOICE_BILLING ),
132+ any (UserChoiceBillingListener .class )))
119133 .thenReturn (mockBillingClient );
120134 methodChannelHandler = new MethodCallHandlerImpl (activity , context , mockMethodChannel , factory );
121135 when (mockActivityPluginBinding .getActivity ()).thenReturn (activity );
@@ -164,7 +178,7 @@ public void startConnection() {
164178 mockStartConnection (BillingChoiceMode .PLAY_BILLING_ONLY );
165179 verify (result , never ()).success (any ());
166180 verify (factory , times (1 ))
167- .createBillingClient (context , mockMethodChannel , BillingChoiceMode .PLAY_BILLING_ONLY );
181+ .createBillingClient (context , mockMethodChannel , BillingChoiceMode .PLAY_BILLING_ONLY , null );
168182
169183 BillingResult billingResult =
170184 BillingResult .newBuilder ()
@@ -183,7 +197,7 @@ public void startConnectionAlternativeBillingOnly() {
183197 verify (result , never ()).success (any ());
184198 verify (factory , times (1 ))
185199 .createBillingClient (
186- context , mockMethodChannel , BillingChoiceMode .ALTERNATIVE_BILLING_ONLY );
200+ context , mockMethodChannel , BillingChoiceMode .ALTERNATIVE_BILLING_ONLY , null );
187201
188202 BillingResult billingResult =
189203 BillingResult .newBuilder ()
@@ -209,7 +223,7 @@ public void startConnectionAlternativeBillingUnset() {
209223 methodChannelHandler .onMethodCall (call , result );
210224 verify (result , never ()).success (any ());
211225 verify (factory , times (1 ))
212- .createBillingClient (context , mockMethodChannel , BillingChoiceMode .PLAY_BILLING_ONLY );
226+ .createBillingClient (context , mockMethodChannel , BillingChoiceMode .PLAY_BILLING_ONLY , null );
213227
214228 BillingResult billingResult =
215229 BillingResult .newBuilder ()
@@ -221,6 +235,106 @@ public void startConnectionAlternativeBillingUnset() {
221235 verify (result , times (1 )).success (fromBillingResult (billingResult ));
222236 }
223237
238+ @ Test
239+ public void startConnectionUserChoiceBilling () {
240+ ArgumentCaptor <BillingClientStateListener > captor =
241+ mockStartConnection (BillingChoiceMode .USER_CHOICE_BILLING );
242+ ArgumentCaptor <UserChoiceBillingListener > billingCaptor =
243+ ArgumentCaptor .forClass (UserChoiceBillingListener .class );
244+ verify (result , never ()).success (any ());
245+ verify (factory , times (1 ))
246+ .createBillingClient (
247+ any (Context .class ),
248+ any (MethodChannel .class ),
249+ eq (BillingChoiceMode .USER_CHOICE_BILLING ),
250+ billingCaptor .capture ());
251+
252+ BillingResult billingResult =
253+ BillingResult .newBuilder ()
254+ .setResponseCode (100 )
255+ .setDebugMessage ("dummy debug message" )
256+ .build ();
257+ captor .getValue ().onBillingSetupFinished (billingResult );
258+
259+ verify (result , times (1 )).success (fromBillingResult (billingResult ));
260+ UserChoiceDetails details = mock (UserChoiceDetails .class );
261+ final String externalTransactionToken = "someLongTokenId1234" ;
262+ final String originalTransactionId = "originalTransactionId123456" ;
263+ when (details .getExternalTransactionToken ()).thenReturn (externalTransactionToken );
264+ when (details .getOriginalExternalTransactionId ()).thenReturn (originalTransactionId );
265+ when (details .getProducts ()).thenReturn (Collections .emptyList ());
266+ billingCaptor .getValue ().userSelectedAlternativeBilling (details );
267+
268+ verify (mockMethodChannel , times (1 ))
269+ .invokeMethod (USER_SELECTED_ALTERNATIVE_BILLING , fromUserChoiceDetails (details ));
270+ }
271+
272+ @ Test
273+ public void userChoiceBillingOnSecondConnection () {
274+ // First connection.
275+ ArgumentCaptor <BillingClientStateListener > captor1 =
276+ mockStartConnection (BillingChoiceMode .PLAY_BILLING_ONLY );
277+ verify (result , never ()).success (any ());
278+ verify (factory , times (1 ))
279+ .createBillingClient (context , mockMethodChannel , BillingChoiceMode .PLAY_BILLING_ONLY , null );
280+
281+ BillingResult billingResult1 =
282+ BillingResult .newBuilder ()
283+ .setResponseCode (100 )
284+ .setDebugMessage ("dummy debug message" )
285+ .build ();
286+ final BillingClientStateListener stateListener = captor1 .getValue ();
287+ stateListener .onBillingSetupFinished (billingResult1 );
288+ verify (result , times (1 )).success (fromBillingResult (billingResult1 ));
289+ Mockito .reset (result , mockMethodChannel , mockBillingClient );
290+
291+ // Disconnect
292+ MethodCall disconnectCall = new MethodCall (END_CONNECTION , null );
293+ methodChannelHandler .onMethodCall (disconnectCall , result );
294+
295+ // Verify that the client is disconnected and that the OnDisconnect callback has
296+ // been triggered
297+ verify (result , times (1 )).success (any ());
298+ verify (mockBillingClient , times (1 )).endConnection ();
299+ stateListener .onBillingServiceDisconnected ();
300+ Map <String , Integer > expectedInvocation = new HashMap <>();
301+ expectedInvocation .put ("handle" , DEFAULT_HANDLE );
302+ verify (mockMethodChannel , times (1 )).invokeMethod (ON_DISCONNECT , expectedInvocation );
303+ Mockito .reset (result , mockMethodChannel , mockBillingClient );
304+
305+ // Second connection.
306+ ArgumentCaptor <BillingClientStateListener > captor2 =
307+ mockStartConnection (BillingChoiceMode .USER_CHOICE_BILLING );
308+ ArgumentCaptor <UserChoiceBillingListener > billingCaptor =
309+ ArgumentCaptor .forClass (UserChoiceBillingListener .class );
310+ verify (result , never ()).success (any ());
311+ verify (factory , times (1 ))
312+ .createBillingClient (
313+ any (Context .class ),
314+ any (MethodChannel .class ),
315+ eq (BillingChoiceMode .USER_CHOICE_BILLING ),
316+ billingCaptor .capture ());
317+
318+ BillingResult billingResult2 =
319+ BillingResult .newBuilder ()
320+ .setResponseCode (100 )
321+ .setDebugMessage ("dummy debug message" )
322+ .build ();
323+ captor2 .getValue ().onBillingSetupFinished (billingResult2 );
324+
325+ verify (result , times (1 )).success (fromBillingResult (billingResult2 ));
326+ UserChoiceDetails details = mock (UserChoiceDetails .class );
327+ final String externalTransactionToken = "someLongTokenId1234" ;
328+ final String originalTransactionId = "originalTransactionId123456" ;
329+ when (details .getExternalTransactionToken ()).thenReturn (externalTransactionToken );
330+ when (details .getOriginalExternalTransactionId ()).thenReturn (originalTransactionId );
331+ when (details .getProducts ()).thenReturn (Collections .emptyList ());
332+ billingCaptor .getValue ().userSelectedAlternativeBilling (details );
333+
334+ verify (mockMethodChannel , times (1 ))
335+ .invokeMethod (USER_SELECTED_ALTERNATIVE_BILLING , fromUserChoiceDetails (details ));
336+ }
337+
224338 @ Test
225339 public void startConnection_multipleCalls () {
226340 Map <String , Object > arguments = new HashMap <>();
@@ -1071,7 +1185,7 @@ private ArgumentCaptor<BillingClientStateListener> mockStartConnection() {
10711185 */
10721186 private ArgumentCaptor <BillingClientStateListener > mockStartConnection (int billingChoiceMode ) {
10731187 Map <String , Object > arguments = new HashMap <>();
1074- arguments .put (MethodArgs .HANDLE , 1 );
1188+ arguments .put (MethodArgs .HANDLE , DEFAULT_HANDLE );
10751189 arguments .put (MethodArgs .BILLING_CHOICE_MODE , billingChoiceMode );
10761190 MethodCall call = new MethodCall (START_CONNECTION , arguments );
10771191 ArgumentCaptor <BillingClientStateListener > captor =
0 commit comments