diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/Messages.java b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/Messages.java
index fa1135fed036..aba22ed2b3bc 100644
--- a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/Messages.java
+++ b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/Messages.java
@@ -95,6 +95,31 @@ public enum PlatformBillingResponse {
}
}
+ /** Response code for the in-app messaging API call. */
+ public enum PlatformInAppMessageResponse {
+ /**
+ * The flow has finished and there is no action needed from developers.
+ *
+ *
Note: The API callback won't indicate whether message is dismissed by the user or there is
+ * no message available to the user.
+ */
+ NO_ACTION_NEEDED(0),
+ /**
+ * The subscription status changed.
+ *
+ *
For example, a subscription has been rec- overed from a suspended state. Developers should
+ * expect the purchase token to be returned with this response code and use the purchase token
+ * with the Google Play Developer API.
+ */
+ SUBSCRIPTION_STATUS_UPDATED(1);
+
+ final int index;
+
+ PlatformInAppMessageResponse(final int index) {
+ this.index = index;
+ }
+ }
+
public enum PlatformReplacementMode {
UNKNOWN_REPLACEMENT_MODE(0),
WITH_TIME_PRORATION(1),
@@ -1025,6 +1050,102 @@ ArrayList toList() {
}
}
+ /**
+ * Results related to in-app messaging.
+ *
+ * Generated class from Pigeon that represents data sent in messages.
+ */
+ public static final class PlatformInAppMessageResult {
+ /** Returns response code for the in-app messaging API call. */
+ private @NonNull PlatformInAppMessageResponse responseCode;
+
+ public @NonNull PlatformInAppMessageResponse getResponseCode() {
+ return responseCode;
+ }
+
+ public void setResponseCode(@NonNull PlatformInAppMessageResponse setterArg) {
+ if (setterArg == null) {
+ throw new IllegalStateException("Nonnull field \"responseCode\" is null.");
+ }
+ this.responseCode = setterArg;
+ }
+
+ /** Returns token that identifies the purchase to be acknowledged, if any. */
+ private @Nullable String purchaseToken;
+
+ public @Nullable String getPurchaseToken() {
+ return purchaseToken;
+ }
+
+ public void setPurchaseToken(@Nullable String setterArg) {
+ this.purchaseToken = setterArg;
+ }
+
+ /** Constructor is non-public to enforce null safety; use Builder. */
+ PlatformInAppMessageResult() {}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PlatformInAppMessageResult that = (PlatformInAppMessageResult) o;
+ return responseCode.equals(that.responseCode)
+ && Objects.equals(purchaseToken, that.purchaseToken);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(responseCode, purchaseToken);
+ }
+
+ public static final class Builder {
+
+ private @Nullable PlatformInAppMessageResponse responseCode;
+
+ @CanIgnoreReturnValue
+ public @NonNull Builder setResponseCode(@NonNull PlatformInAppMessageResponse setterArg) {
+ this.responseCode = setterArg;
+ return this;
+ }
+
+ private @Nullable String purchaseToken;
+
+ @CanIgnoreReturnValue
+ public @NonNull Builder setPurchaseToken(@Nullable String setterArg) {
+ this.purchaseToken = setterArg;
+ return this;
+ }
+
+ public @NonNull PlatformInAppMessageResult build() {
+ PlatformInAppMessageResult pigeonReturn = new PlatformInAppMessageResult();
+ pigeonReturn.setResponseCode(responseCode);
+ pigeonReturn.setPurchaseToken(purchaseToken);
+ return pigeonReturn;
+ }
+ }
+
+ @NonNull
+ ArrayList toList() {
+ ArrayList toListResult = new ArrayList<>(2);
+ toListResult.add(responseCode);
+ toListResult.add(purchaseToken);
+ return toListResult;
+ }
+
+ static @NonNull PlatformInAppMessageResult fromList(@NonNull ArrayList pigeonVar_list) {
+ PlatformInAppMessageResult pigeonResult = new PlatformInAppMessageResult();
+ Object responseCode = pigeonVar_list.get(0);
+ pigeonResult.setResponseCode((PlatformInAppMessageResponse) responseCode);
+ Object purchaseToken = pigeonVar_list.get(1);
+ pigeonResult.setPurchaseToken((String) purchaseToken);
+ return pigeonResult;
+ }
+ }
+
/**
* Pigeon version of BillingConfigWrapper, which contains the components of the Java
* BillingConfigResponseListener callback.
@@ -3126,80 +3247,89 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
Object value = readValue(buffer);
return value == null
? null
- : PlatformReplacementMode.values()[((Long) value).intValue()];
+ : PlatformInAppMessageResponse.values()[((Long) value).intValue()];
}
case (byte) 131:
{
Object value = readValue(buffer);
- return value == null ? null : PlatformProductType.values()[((Long) value).intValue()];
+ return value == null
+ ? null
+ : PlatformReplacementMode.values()[((Long) value).intValue()];
}
case (byte) 132:
+ {
+ Object value = readValue(buffer);
+ return value == null ? null : PlatformProductType.values()[((Long) value).intValue()];
+ }
+ case (byte) 133:
{
Object value = readValue(buffer);
return value == null
? null
: PlatformBillingChoiceMode.values()[((Long) value).intValue()];
}
- case (byte) 133:
+ case (byte) 134:
{
Object value = readValue(buffer);
return value == null
? null
: PlatformBillingClientFeature.values()[((Long) value).intValue()];
}
- case (byte) 134:
+ case (byte) 135:
{
Object value = readValue(buffer);
return value == null ? null : PlatformPurchaseState.values()[((Long) value).intValue()];
}
- case (byte) 135:
+ case (byte) 136:
{
Object value = readValue(buffer);
return value == null
? null
: PlatformRecurrenceMode.values()[((Long) value).intValue()];
}
- case (byte) 136:
- return PlatformQueryProduct.fromList((ArrayList) readValue(buffer));
case (byte) 137:
- return PlatformAccountIdentifiers.fromList((ArrayList) readValue(buffer));
+ return PlatformQueryProduct.fromList((ArrayList) readValue(buffer));
case (byte) 138:
- return PlatformBillingResult.fromList((ArrayList) readValue(buffer));
+ return PlatformAccountIdentifiers.fromList((ArrayList) readValue(buffer));
case (byte) 139:
+ return PlatformBillingResult.fromList((ArrayList) readValue(buffer));
+ case (byte) 140:
return PlatformOneTimePurchaseOfferDetails.fromList(
(ArrayList) readValue(buffer));
- case (byte) 140:
- return PlatformProductDetails.fromList((ArrayList) readValue(buffer));
case (byte) 141:
- return PlatformProductDetailsResponse.fromList((ArrayList) readValue(buffer));
+ return PlatformProductDetails.fromList((ArrayList) readValue(buffer));
case (byte) 142:
+ return PlatformProductDetailsResponse.fromList((ArrayList) readValue(buffer));
+ case (byte) 143:
return PlatformAlternativeBillingOnlyReportingDetailsResponse.fromList(
(ArrayList) readValue(buffer));
- case (byte) 143:
- return PlatformBillingConfigResponse.fromList((ArrayList) readValue(buffer));
case (byte) 144:
- return PlatformBillingFlowParams.fromList((ArrayList) readValue(buffer));
+ return PlatformInAppMessageResult.fromList((ArrayList) readValue(buffer));
case (byte) 145:
- return PlatformPricingPhase.fromList((ArrayList) readValue(buffer));
+ return PlatformBillingConfigResponse.fromList((ArrayList) readValue(buffer));
case (byte) 146:
- return PlatformPurchase.fromList((ArrayList) readValue(buffer));
+ return PlatformBillingFlowParams.fromList((ArrayList) readValue(buffer));
case (byte) 147:
- return PlatformPendingPurchaseUpdate.fromList((ArrayList) readValue(buffer));
+ return PlatformPricingPhase.fromList((ArrayList) readValue(buffer));
case (byte) 148:
- return PlatformPurchaseHistoryRecord.fromList((ArrayList) readValue(buffer));
+ return PlatformPurchase.fromList((ArrayList) readValue(buffer));
case (byte) 149:
- return PlatformPurchaseHistoryResponse.fromList((ArrayList) readValue(buffer));
+ return PlatformPendingPurchaseUpdate.fromList((ArrayList) readValue(buffer));
case (byte) 150:
- return PlatformPurchasesResponse.fromList((ArrayList) readValue(buffer));
+ return PlatformPurchaseHistoryRecord.fromList((ArrayList) readValue(buffer));
case (byte) 151:
- return PlatformSubscriptionOfferDetails.fromList((ArrayList) readValue(buffer));
+ return PlatformPurchaseHistoryResponse.fromList((ArrayList) readValue(buffer));
case (byte) 152:
- return PlatformUserChoiceDetails.fromList((ArrayList) readValue(buffer));
+ return PlatformPurchasesResponse.fromList((ArrayList) readValue(buffer));
case (byte) 153:
- return PlatformUserChoiceProduct.fromList((ArrayList) readValue(buffer));
+ return PlatformSubscriptionOfferDetails.fromList((ArrayList) readValue(buffer));
case (byte) 154:
- return PlatformInstallmentPlanDetails.fromList((ArrayList) readValue(buffer));
+ return PlatformUserChoiceDetails.fromList((ArrayList) readValue(buffer));
case (byte) 155:
+ return PlatformUserChoiceProduct.fromList((ArrayList) readValue(buffer));
+ case (byte) 156:
+ return PlatformInstallmentPlanDetails.fromList((ArrayList) readValue(buffer));
+ case (byte) 157:
return PlatformPendingPurchasesParams.fromList((ArrayList) readValue(buffer));
default:
return super.readValueOfType(type, buffer);
@@ -3211,84 +3341,90 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
if (value instanceof PlatformBillingResponse) {
stream.write(129);
writeValue(stream, value == null ? null : ((PlatformBillingResponse) value).index);
- } else if (value instanceof PlatformReplacementMode) {
+ } else if (value instanceof PlatformInAppMessageResponse) {
stream.write(130);
+ writeValue(stream, value == null ? null : ((PlatformInAppMessageResponse) value).index);
+ } else if (value instanceof PlatformReplacementMode) {
+ stream.write(131);
writeValue(stream, value == null ? null : ((PlatformReplacementMode) value).index);
} else if (value instanceof PlatformProductType) {
- stream.write(131);
+ stream.write(132);
writeValue(stream, value == null ? null : ((PlatformProductType) value).index);
} else if (value instanceof PlatformBillingChoiceMode) {
- stream.write(132);
+ stream.write(133);
writeValue(stream, value == null ? null : ((PlatformBillingChoiceMode) value).index);
} else if (value instanceof PlatformBillingClientFeature) {
- stream.write(133);
+ stream.write(134);
writeValue(stream, value == null ? null : ((PlatformBillingClientFeature) value).index);
} else if (value instanceof PlatformPurchaseState) {
- stream.write(134);
+ stream.write(135);
writeValue(stream, value == null ? null : ((PlatformPurchaseState) value).index);
} else if (value instanceof PlatformRecurrenceMode) {
- stream.write(135);
+ stream.write(136);
writeValue(stream, value == null ? null : ((PlatformRecurrenceMode) value).index);
} else if (value instanceof PlatformQueryProduct) {
- stream.write(136);
+ stream.write(137);
writeValue(stream, ((PlatformQueryProduct) value).toList());
} else if (value instanceof PlatformAccountIdentifiers) {
- stream.write(137);
+ stream.write(138);
writeValue(stream, ((PlatformAccountIdentifiers) value).toList());
} else if (value instanceof PlatformBillingResult) {
- stream.write(138);
+ stream.write(139);
writeValue(stream, ((PlatformBillingResult) value).toList());
} else if (value instanceof PlatformOneTimePurchaseOfferDetails) {
- stream.write(139);
+ stream.write(140);
writeValue(stream, ((PlatformOneTimePurchaseOfferDetails) value).toList());
} else if (value instanceof PlatformProductDetails) {
- stream.write(140);
+ stream.write(141);
writeValue(stream, ((PlatformProductDetails) value).toList());
} else if (value instanceof PlatformProductDetailsResponse) {
- stream.write(141);
+ stream.write(142);
writeValue(stream, ((PlatformProductDetailsResponse) value).toList());
} else if (value instanceof PlatformAlternativeBillingOnlyReportingDetailsResponse) {
- stream.write(142);
+ stream.write(143);
writeValue(
stream, ((PlatformAlternativeBillingOnlyReportingDetailsResponse) value).toList());
+ } else if (value instanceof PlatformInAppMessageResult) {
+ stream.write(144);
+ writeValue(stream, ((PlatformInAppMessageResult) value).toList());
} else if (value instanceof PlatformBillingConfigResponse) {
- stream.write(143);
+ stream.write(145);
writeValue(stream, ((PlatformBillingConfigResponse) value).toList());
} else if (value instanceof PlatformBillingFlowParams) {
- stream.write(144);
+ stream.write(146);
writeValue(stream, ((PlatformBillingFlowParams) value).toList());
} else if (value instanceof PlatformPricingPhase) {
- stream.write(145);
+ stream.write(147);
writeValue(stream, ((PlatformPricingPhase) value).toList());
} else if (value instanceof PlatformPurchase) {
- stream.write(146);
+ stream.write(148);
writeValue(stream, ((PlatformPurchase) value).toList());
} else if (value instanceof PlatformPendingPurchaseUpdate) {
- stream.write(147);
+ stream.write(149);
writeValue(stream, ((PlatformPendingPurchaseUpdate) value).toList());
} else if (value instanceof PlatformPurchaseHistoryRecord) {
- stream.write(148);
+ stream.write(150);
writeValue(stream, ((PlatformPurchaseHistoryRecord) value).toList());
} else if (value instanceof PlatformPurchaseHistoryResponse) {
- stream.write(149);
+ stream.write(151);
writeValue(stream, ((PlatformPurchaseHistoryResponse) value).toList());
} else if (value instanceof PlatformPurchasesResponse) {
- stream.write(150);
+ stream.write(152);
writeValue(stream, ((PlatformPurchasesResponse) value).toList());
} else if (value instanceof PlatformSubscriptionOfferDetails) {
- stream.write(151);
+ stream.write(153);
writeValue(stream, ((PlatformSubscriptionOfferDetails) value).toList());
} else if (value instanceof PlatformUserChoiceDetails) {
- stream.write(152);
+ stream.write(154);
writeValue(stream, ((PlatformUserChoiceDetails) value).toList());
} else if (value instanceof PlatformUserChoiceProduct) {
- stream.write(153);
+ stream.write(155);
writeValue(stream, ((PlatformUserChoiceProduct) value).toList());
} else if (value instanceof PlatformInstallmentPlanDetails) {
- stream.write(154);
+ stream.write(156);
writeValue(stream, ((PlatformInstallmentPlanDetails) value).toList());
} else if (value instanceof PlatformPendingPurchasesParams) {
- stream.write(155);
+ stream.write(157);
writeValue(stream, ((PlatformPendingPurchasesParams) value).toList());
} else {
super.writeValue(stream, value);
@@ -3380,6 +3516,8 @@ void queryProductDetailsAsync(
*/
void createAlternativeBillingOnlyReportingDetailsAsync(
@NonNull Result result);
+ /** Wraps BillingClient#showInAppMessages(). */
+ void showInAppMessages(@NonNull Result result);
/** The codec used by InAppPurchaseApi. */
static @NonNull MessageCodec getCodec() {
@@ -3811,6 +3949,36 @@ public void error(Throwable error) {
channel.setMessageHandler(null);
}
}
+ {
+ BasicMessageChannel channel =
+ new BasicMessageChannel<>(
+ binaryMessenger,
+ "dev.flutter.pigeon.in_app_purchase_android.InAppPurchaseApi.showInAppMessages"
+ + messageChannelSuffix,
+ getCodec());
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ ArrayList wrapped = new ArrayList<>();
+ Result resultCallback =
+ new Result() {
+ public void success(PlatformInAppMessageResult result) {
+ wrapped.add(0, result);
+ reply.reply(wrapped);
+ }
+
+ public void error(Throwable error) {
+ ArrayList wrappedError = wrapError(error);
+ reply.reply(wrappedError);
+ }
+ };
+
+ api.showInAppMessages(resultCallback);
+ });
+ } else {
+ channel.setMessageHandler(null);
+ }
+ }
}
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java
index d496fb57b323..262b9a85259f 100644
--- a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java
+++ b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java
@@ -7,6 +7,7 @@
import static io.flutter.plugins.inapppurchase.Translator.fromAlternativeBillingOnlyReportingDetails;
import static io.flutter.plugins.inapppurchase.Translator.fromBillingConfig;
import static io.flutter.plugins.inapppurchase.Translator.fromBillingResult;
+import static io.flutter.plugins.inapppurchase.Translator.fromInAppMessageResult;
import static io.flutter.plugins.inapppurchase.Translator.fromProductDetailsList;
import static io.flutter.plugins.inapppurchase.Translator.fromPurchaseHistoryRecordList;
import static io.flutter.plugins.inapppurchase.Translator.fromPurchasesList;
@@ -31,6 +32,7 @@
import com.android.billingclient.api.ConsumeParams;
import com.android.billingclient.api.ConsumeResponseListener;
import com.android.billingclient.api.GetBillingConfigParams;
+import com.android.billingclient.api.InAppMessageParams;
import com.android.billingclient.api.ProductDetails;
import com.android.billingclient.api.QueryProductDetailsParams;
import com.android.billingclient.api.QueryPurchaseHistoryParams;
@@ -176,6 +178,29 @@ public void isAlternativeBillingOnlyAvailableAsync(
}
}
+ @Override
+ public void showInAppMessages(@NonNull Result result) {
+ if (billingClient == null) {
+ result.error(getNullBillingClientError());
+ return;
+ }
+ if (activity == null) {
+ result.error(new FlutterError(ACTIVITY_UNAVAILABLE, "Not attempting to show dialog", null));
+ return;
+ }
+ try {
+ InAppMessageParams params =
+ InAppMessageParams.newBuilder()
+ .addInAppMessageCategoryToShow(
+ InAppMessageParams.InAppMessageCategoryId.TRANSACTIONAL)
+ .build();
+ billingClient.showInAppMessages(
+ activity, params, billingResult -> result.success(fromInAppMessageResult(billingResult)));
+ } catch (RuntimeException e) {
+ result.error(new FlutterError("error", e.getMessage(), Log.getStackTraceString(e)));
+ }
+ }
+
@Override
public void getBillingConfigAsync(
@NonNull Result result) {
diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/Translator.java b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/Translator.java
index a982964764dc..1bfb9c64996d 100644
--- a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/Translator.java
+++ b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/Translator.java
@@ -12,6 +12,7 @@
import com.android.billingclient.api.BillingConfig;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
+import com.android.billingclient.api.InAppMessageResult;
import com.android.billingclient.api.PendingPurchasesParams;
import com.android.billingclient.api.ProductDetails;
import com.android.billingclient.api.Purchase;
@@ -25,6 +26,8 @@
import io.flutter.plugins.inapppurchase.Messages.PlatformBillingConfigResponse;
import io.flutter.plugins.inapppurchase.Messages.PlatformBillingResponse;
import io.flutter.plugins.inapppurchase.Messages.PlatformBillingResult;
+import io.flutter.plugins.inapppurchase.Messages.PlatformInAppMessageResponse;
+import io.flutter.plugins.inapppurchase.Messages.PlatformInAppMessageResult;
import io.flutter.plugins.inapppurchase.Messages.PlatformOneTimePurchaseOfferDetails;
import io.flutter.plugins.inapppurchase.Messages.PlatformPendingPurchaseUpdate;
import io.flutter.plugins.inapppurchase.Messages.PlatformPricingPhase;
@@ -298,6 +301,25 @@ static PlatformPurchaseState toPlatformPurchaseState(int state) {
return serialized;
}
+ static @NonNull PlatformInAppMessageResult fromInAppMessageResult(
+ @NonNull InAppMessageResult inAppMessageResult) {
+ return new PlatformInAppMessageResult.Builder()
+ .setResponseCode(fromInAppMessageResponseCode(inAppMessageResult.getResponseCode()))
+ .setPurchaseToken(inAppMessageResult.getPurchaseToken())
+ .build();
+ }
+
+ static @NonNull PlatformInAppMessageResponse fromInAppMessageResponseCode(
+ @InAppMessageResult.InAppMessageResponseCode int inAppMessageResponseCode) {
+ switch (inAppMessageResponseCode) {
+ case InAppMessageResult.InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED:
+ return PlatformInAppMessageResponse.SUBSCRIPTION_STATUS_UPDATED;
+ case InAppMessageResult.InAppMessageResponseCode.NO_ACTION_NEEDED:
+ return PlatformInAppMessageResponse.NO_ACTION_NEEDED;
+ }
+ return PlatformInAppMessageResponse.NO_ACTION_NEEDED;
+ }
+
static @NonNull PlatformBillingResult fromBillingResult(@NonNull BillingResult billingResult) {
return new PlatformBillingResult.Builder()
.setResponseCode(fromBillingResponseCode(billingResult.getResponseCode()))
diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java b/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java
index d4cfeda77d0d..723c655d85aa 100644
--- a/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java
+++ b/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java
@@ -7,6 +7,7 @@
import static io.flutter.plugins.inapppurchase.MethodCallHandlerImpl.ACTIVITY_UNAVAILABLE;
import static io.flutter.plugins.inapppurchase.MethodCallHandlerImpl.REPLACEMENT_MODE_UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY;
import static io.flutter.plugins.inapppurchase.Translator.fromBillingResponseCode;
+import static io.flutter.plugins.inapppurchase.Translator.fromInAppMessageResponseCode;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableList;
@@ -45,6 +46,8 @@
import com.android.billingclient.api.ConsumeParams;
import com.android.billingclient.api.ConsumeResponseListener;
import com.android.billingclient.api.GetBillingConfigParams;
+import com.android.billingclient.api.InAppMessageResponseListener;
+import com.android.billingclient.api.InAppMessageResult;
import com.android.billingclient.api.ProductDetails;
import com.android.billingclient.api.ProductDetailsResponseListener;
import com.android.billingclient.api.Purchase;
@@ -63,6 +66,7 @@
import io.flutter.plugins.inapppurchase.Messages.PlatformBillingConfigResponse;
import io.flutter.plugins.inapppurchase.Messages.PlatformBillingFlowParams;
import io.flutter.plugins.inapppurchase.Messages.PlatformBillingResult;
+import io.flutter.plugins.inapppurchase.Messages.PlatformInAppMessageResult;
import io.flutter.plugins.inapppurchase.Messages.PlatformProductDetailsResponse;
import io.flutter.plugins.inapppurchase.Messages.PlatformProductType;
import io.flutter.plugins.inapppurchase.Messages.PlatformPurchaseHistoryResponse;
@@ -96,6 +100,7 @@ public class MethodCallHandlerTest {
Messages.Result
platformAlternativeBillingOnlyReportingDetailsResult;
+ @Spy Messages.Result platformInAppMessageResult;
@Spy Messages.Result platformBillingConfigResult;
@Spy Messages.Result platformBillingResult;
@Spy Messages.Result platformProductDetailsResult;
@@ -527,6 +532,62 @@ public void showAlternativeBillingOnlyInformationDialog_NullActivity() {
.contains("Not attempting to show dialog"));
}
+ @Test
+ public void showInAppMessagesSuccess() {
+ mockStartConnection();
+ ArgumentCaptor listenerCaptor =
+ ArgumentCaptor.forClass(InAppMessageResponseListener.class);
+ BillingResult billingResult = buildBillingResult(BillingClient.BillingResponseCode.OK);
+ InAppMessageResult inAppMessageResult =
+ buildInAppMessageResult(
+ InAppMessageResult.InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED);
+
+ when(mockBillingClient.showInAppMessages(eq(activity), any(), listenerCaptor.capture()))
+ .thenReturn(billingResult);
+
+ methodChannelHandler.showInAppMessages(platformInAppMessageResult);
+ listenerCaptor.getValue().onInAppMessageResponse(inAppMessageResult);
+
+ ArgumentCaptor resultCaptor =
+ ArgumentCaptor.forClass(PlatformInAppMessageResult.class);
+ verify(platformInAppMessageResult, times(1)).success(resultCaptor.capture());
+ assertEquals(
+ resultCaptor.getValue().getResponseCode(),
+ fromInAppMessageResponseCode(inAppMessageResult.getResponseCode()));
+ assertEquals(resultCaptor.getValue().getPurchaseToken(), inAppMessageResult.getPurchaseToken());
+ verify(platformInAppMessageResult, never()).error(any());
+ }
+
+ @Test
+ public void showInAppMessages_serviceDisconnected() {
+ methodChannelHandler.showInAppMessages(platformInAppMessageResult);
+
+ // Assert that the async call returns an error result.
+ verify(platformInAppMessageResult, never()).success(any());
+ ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(FlutterError.class);
+ verify(platformInAppMessageResult, times(1)).error(errorCaptor.capture());
+ assertEquals("UNAVAILABLE", errorCaptor.getValue().code);
+ assertTrue(
+ Objects.requireNonNull(errorCaptor.getValue().getMessage()).contains("BillingClient"));
+ }
+
+ @Test
+ public void showInAppMessages_NullActivity() {
+ mockStartConnection();
+ methodChannelHandler.setActivity(null);
+
+ methodChannelHandler.showInAppMessages(platformInAppMessageResult);
+
+ // Assert that the async call returns an error result.
+ verify(platformInAppMessageResult, never()).success(any());
+ ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(FlutterError.class);
+ verify(platformInAppMessageResult, times(1)).error(errorCaptor.capture());
+ assertEquals(ACTIVITY_UNAVAILABLE, errorCaptor.getValue().code);
+ assertTrue(
+ Objects.requireNonNull(errorCaptor.getValue().getMessage())
+ .contains("Not attempting to show dialog"));
+ }
+
@Test
public void endConnection() {
// Set up a connected BillingClient instance
@@ -1252,6 +1313,10 @@ private BillingResult buildBillingResult(int responseCode) {
.build();
}
+ private InAppMessageResult buildInAppMessageResult(int responseCode) {
+ return new InAppMessageResult(responseCode, "dummy purchase token");
+ }
+
private void assertResultsMatch(PlatformBillingResult pigeonResult, BillingResult nativeResult) {
assertEquals(
pigeonResult.getResponseCode(), fromBillingResponseCode(nativeResult.getResponseCode()));
diff --git a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart
index 6bb520121d19..bbc860589ce6 100644
--- a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart
+++ b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart
@@ -53,6 +53,7 @@ class _MyAppState extends State<_MyApp> {
String _isAlternativeBillingOnlyAvailableResponseCode = '';
String _showAlternativeBillingOnlyDialogResponseCode = '';
String _alternativeBillingOnlyReportingDetailsToken = '';
+ String _showInAppMessagesResponseCode = '';
bool _isAvailable = false;
bool _purchasePending = false;
bool _loading = true;
@@ -274,6 +275,15 @@ class _MyAppState extends State<_MyApp> {
subtitle: Text(_alternativeBillingOnlyReportingDetailsToken),
),
);
+ entries.add(
+ ListTile(
+ title: Text(
+ 'showInAppMessages response code',
+ style: TextStyle(color: ThemeData.light().colorScheme.primary),
+ ),
+ subtitle: Text(_showInAppMessagesResponseCode),
+ ),
+ );
final List buttons = [];
buttons.add(
@@ -374,6 +384,25 @@ class _MyAppState extends State<_MyApp> {
),
),
);
+ buttons.add(
+ ListTile(
+ title: TextButton(
+ style: TextButton.styleFrom(
+ backgroundColor: Colors.green[800],
+ foregroundColor: Colors.white,
+ ),
+ onPressed: () {
+ final addition =
+ InAppPurchasePlatformAddition.instance!
+ as InAppPurchaseAndroidPlatformAddition;
+ unawaited(
+ deliverShowInAppMessagesResult(addition.showInAppMessages()),
+ );
+ },
+ child: const Text('showInAppMessages'),
+ ),
+ ),
+ );
return Card(
child: Column(
children: [
@@ -597,6 +626,15 @@ class _MyAppState extends State<_MyApp> {
});
}
+ Future deliverShowInAppMessagesResult(
+ Future inAppMessageResult,
+ ) async {
+ final InAppMessageResultWrapper wrapper = await inAppMessageResult;
+ setState(() {
+ _showInAppMessagesResponseCode = wrapper.responseCode.name;
+ });
+ }
+
Future deliverProduct(PurchaseDetails purchaseDetails) async {
// IMPORTANT!! Always verify purchase details before delivering the product.
if (purchaseDetails.productID == _kConsumableId) {
diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/billing_client_wrappers.dart b/packages/in_app_purchase/in_app_purchase_android/lib/billing_client_wrappers.dart
index 2224a4e8a543..8d5fda1661b5 100644
--- a/packages/in_app_purchase/in_app_purchase_android/lib/billing_client_wrappers.dart
+++ b/packages/in_app_purchase/in_app_purchase_android/lib/billing_client_wrappers.dart
@@ -6,6 +6,7 @@ export 'src/billing_client_wrappers/alternative_billing_only_reporting_details_w
export 'src/billing_client_wrappers/billing_client_manager.dart';
export 'src/billing_client_wrappers/billing_client_wrapper.dart';
export 'src/billing_client_wrappers/billing_response_wrapper.dart';
+export 'src/billing_client_wrappers/in_app_message_wrapper.dart';
export 'src/billing_client_wrappers/one_time_purchase_offer_details_wrapper.dart';
export 'src/billing_client_wrappers/product_details_wrapper.dart';
export 'src/billing_client_wrappers/product_wrapper.dart';
diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart
index b3df0cf619b9..0924054c0c32 100644
--- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart
+++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart
@@ -10,6 +10,7 @@ import '../../billing_client_wrappers.dart';
import '../messages.g.dart';
import '../pigeon_converters.dart';
import 'billing_config_wrapper.dart';
+import 'in_app_message_wrapper.dart';
import 'pending_purchases_params_wrapper.dart';
/// Callback triggered by Play in response to purchase activity.
@@ -347,6 +348,16 @@ class BillingClient {
await _hostApi.createAlternativeBillingOnlyReportingDetailsAsync(),
);
}
+
+ /// Overlays billing related messages on top of the calling app.
+ //
+ // For example, show a message to inform users that their subscription payment
+ // has been declined and provide options to take them to fix their payment method.
+ Future showInAppMessages() async {
+ return inAppMessageResultWrapperFromPlatform(
+ await _hostApi.showInAppMessages(),
+ );
+ }
}
/// Implementation of InAppPurchaseCallbackApi, for use by [BillingClient].
diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/in_app_message_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/in_app_message_wrapper.dart
new file mode 100644
index 000000000000..c694a322fc42
--- /dev/null
+++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/in_app_message_wrapper.dart
@@ -0,0 +1,47 @@
+import 'package:flutter/foundation.dart';
+
+/// Response code for the in-app messaging API call.
+enum InAppMessageResponse {
+ /// The flow has finished and there is no action needed from developers.
+ ///
+ /// Note: The API callback won't indicate whether message is dismissed by the
+ /// user or there is no message available to the user.
+ noActionNeeded,
+
+ /// The subscription status changed.
+ ///
+ /// For example, a subscription has been rec-
+ /// overed from a suspended state. Developers should expect the purchase token
+ /// to be returned with this response code and use the purchase token with the
+ /// Google Play Developer API.
+ subscriptionStatusUpdated,
+}
+
+/// Results related to in-app messaging.
+///
+/// Wraps [`com.android.billingclient.api.InAppMessageResult`](https://developer.android.com/reference/com/android/billingclient/api/InAppMessageResult).
+@immutable
+class InAppMessageResultWrapper {
+ /// Creates a [InAppMessageResultWrapper]
+ const InAppMessageResultWrapper({
+ required this.responseCode,
+ this.purchaseToken,
+ });
+
+ /// Returns response code for the in-app messaging API call.
+ final InAppMessageResponse responseCode;
+
+ /// Returns token that identifies the purchase to be acknowledged, if any.
+ final String? purchaseToken;
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is InAppMessageResultWrapper &&
+ runtimeType == other.runtimeType &&
+ responseCode == other.responseCode &&
+ purchaseToken == other.purchaseToken;
+
+ @override
+ int get hashCode => Object.hash(responseCode, purchaseToken);
+}
diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart
index e4d27cdb99bc..5dd2e3f9aa35 100644
--- a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart
+++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart
@@ -198,6 +198,19 @@ class InAppPurchaseAndroidPlatformAddition
return wrapper;
}
+ /// Overlays billing related messages on top of the calling app.
+ ///
+ /// For example, show a message to inform users that their subscription payment
+ /// has been declined and provide options to take them to fix their payment method.
+ /// See: https://developer.android.com/reference/com/android/billingclient/api/BillingClient#showInAppMessages(android.app.Activity,com.android.billingclient.api.InAppMessageParams,com.android.billingclient.api.InAppMessageResponseListener)
+ Future showInAppMessages() async {
+ final InAppMessageResultWrapper wrapper = await _billingClientManager
+ .runWithClientNonRetryable(
+ (BillingClient client) => client.showInAppMessages(),
+ );
+ return wrapper;
+ }
+
/// Disconnects, sets AlternativeBillingOnly to true, and reconnects to
/// the [BillingClient].
///
diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/messages.g.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/messages.g.dart
index cbcdb28231df..dba8c866b9dc 100644
--- a/packages/in_app_purchase/in_app_purchase_android/lib/src/messages.g.dart
+++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/messages.g.dart
@@ -67,6 +67,23 @@ enum PlatformBillingResponse {
networkError,
}
+/// Response code for the in-app messaging API call.
+enum PlatformInAppMessageResponse {
+ /// The flow has finished and there is no action needed from developers.
+ ///
+ /// Note: The API callback won't indicate whether message is dismissed by the
+ /// user or there is no message available to the user.
+ noActionNeeded,
+
+ /// The subscription status changed.
+ ///
+ /// For example, a subscription has been rec-
+ /// overed from a suspended state. Developers should expect the purchase token
+ /// to be returned with this response code and use the purchase token with the
+ /// Google Play Developer API.
+ subscriptionStatusUpdated,
+}
+
enum PlatformReplacementMode {
unknownReplacementMode,
withTimeProration,
@@ -460,6 +477,50 @@ class PlatformAlternativeBillingOnlyReportingDetailsResponse {
int get hashCode => Object.hashAll(_toList());
}
+/// Results related to in-app messaging.
+class PlatformInAppMessageResult {
+ PlatformInAppMessageResult({required this.responseCode, this.purchaseToken});
+
+ /// Returns response code for the in-app messaging API call.
+ PlatformInAppMessageResponse responseCode;
+
+ /// Returns token that identifies the purchase to be acknowledged, if any.
+ String? purchaseToken;
+
+ List _toList() {
+ return [responseCode, purchaseToken];
+ }
+
+ Object encode() {
+ return _toList();
+ }
+
+ static PlatformInAppMessageResult decode(Object result) {
+ result as List;
+ return PlatformInAppMessageResult(
+ responseCode: result[0]! as PlatformInAppMessageResponse,
+ purchaseToken: result[1] as String?,
+ );
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ bool operator ==(Object other) {
+ if (other is! PlatformInAppMessageResult ||
+ other.runtimeType != runtimeType) {
+ return false;
+ }
+ if (identical(this, other)) {
+ return true;
+ }
+ return _deepEquals(encode(), other.encode());
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ int get hashCode => Object.hashAll(_toList());
+}
+
/// Pigeon version of BillingConfigWrapper, which contains the components of the
/// Java BillingConfigResponseListener callback.
class PlatformBillingConfigResponse {
@@ -1240,84 +1301,90 @@ class _PigeonCodec extends StandardMessageCodec {
} else if (value is PlatformBillingResponse) {
buffer.putUint8(129);
writeValue(buffer, value.index);
- } else if (value is PlatformReplacementMode) {
+ } else if (value is PlatformInAppMessageResponse) {
buffer.putUint8(130);
writeValue(buffer, value.index);
- } else if (value is PlatformProductType) {
+ } else if (value is PlatformReplacementMode) {
buffer.putUint8(131);
writeValue(buffer, value.index);
- } else if (value is PlatformBillingChoiceMode) {
+ } else if (value is PlatformProductType) {
buffer.putUint8(132);
writeValue(buffer, value.index);
- } else if (value is PlatformBillingClientFeature) {
+ } else if (value is PlatformBillingChoiceMode) {
buffer.putUint8(133);
writeValue(buffer, value.index);
- } else if (value is PlatformPurchaseState) {
+ } else if (value is PlatformBillingClientFeature) {
buffer.putUint8(134);
writeValue(buffer, value.index);
- } else if (value is PlatformRecurrenceMode) {
+ } else if (value is PlatformPurchaseState) {
buffer.putUint8(135);
writeValue(buffer, value.index);
- } else if (value is PlatformQueryProduct) {
+ } else if (value is PlatformRecurrenceMode) {
buffer.putUint8(136);
+ writeValue(buffer, value.index);
+ } else if (value is PlatformQueryProduct) {
+ buffer.putUint8(137);
writeValue(buffer, value.encode());
} else if (value is PlatformAccountIdentifiers) {
- buffer.putUint8(137);
+ buffer.putUint8(138);
writeValue(buffer, value.encode());
} else if (value is PlatformBillingResult) {
- buffer.putUint8(138);
+ buffer.putUint8(139);
writeValue(buffer, value.encode());
} else if (value is PlatformOneTimePurchaseOfferDetails) {
- buffer.putUint8(139);
+ buffer.putUint8(140);
writeValue(buffer, value.encode());
} else if (value is PlatformProductDetails) {
- buffer.putUint8(140);
+ buffer.putUint8(141);
writeValue(buffer, value.encode());
} else if (value is PlatformProductDetailsResponse) {
- buffer.putUint8(141);
+ buffer.putUint8(142);
writeValue(buffer, value.encode());
} else if (value
is PlatformAlternativeBillingOnlyReportingDetailsResponse) {
- buffer.putUint8(142);
+ buffer.putUint8(143);
+ writeValue(buffer, value.encode());
+ } else if (value is PlatformInAppMessageResult) {
+ buffer.putUint8(144);
writeValue(buffer, value.encode());
} else if (value is PlatformBillingConfigResponse) {
- buffer.putUint8(143);
+ buffer.putUint8(145);
writeValue(buffer, value.encode());
} else if (value is PlatformBillingFlowParams) {
- buffer.putUint8(144);
+ buffer.putUint8(146);
writeValue(buffer, value.encode());
} else if (value is PlatformPricingPhase) {
- buffer.putUint8(145);
+ buffer.putUint8(147);
writeValue(buffer, value.encode());
} else if (value is PlatformPurchase) {
- buffer.putUint8(146);
+ buffer.putUint8(148);
writeValue(buffer, value.encode());
} else if (value is PlatformPendingPurchaseUpdate) {
- buffer.putUint8(147);
+ buffer.putUint8(149);
writeValue(buffer, value.encode());
} else if (value is PlatformPurchaseHistoryRecord) {
- buffer.putUint8(148);
+ buffer.putUint8(150);
writeValue(buffer, value.encode());
} else if (value is PlatformPurchaseHistoryResponse) {
- buffer.putUint8(149);
+ buffer.putUint8(151);
writeValue(buffer, value.encode());
} else if (value is PlatformPurchasesResponse) {
- buffer.putUint8(150);
+ buffer.putUint8(152);
writeValue(buffer, value.encode());
} else if (value is PlatformSubscriptionOfferDetails) {
- buffer.putUint8(151);
+ buffer.putUint8(153);
writeValue(buffer, value.encode());
} else if (value is PlatformUserChoiceDetails) {
- buffer.putUint8(152);
+ buffer.putUint8(154);
writeValue(buffer, value.encode());
} else if (value is PlatformUserChoiceProduct) {
- buffer.putUint8(153);
+ buffer.putUint8(155);
writeValue(buffer, value.encode());
} else if (value is PlatformInstallmentPlanDetails) {
- buffer.putUint8(154);
+ buffer.putUint8(156);
writeValue(buffer, value.encode());
} else if (value is PlatformPendingPurchasesParams) {
- buffer.putUint8(155);
+ buffer.putUint8(157);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
@@ -1332,65 +1399,72 @@ class _PigeonCodec extends StandardMessageCodec {
return value == null ? null : PlatformBillingResponse.values[value];
case 130:
final int? value = readValue(buffer) as int?;
- return value == null ? null : PlatformReplacementMode.values[value];
+ return value == null
+ ? null
+ : PlatformInAppMessageResponse.values[value];
case 131:
final int? value = readValue(buffer) as int?;
- return value == null ? null : PlatformProductType.values[value];
+ return value == null ? null : PlatformReplacementMode.values[value];
case 132:
final int? value = readValue(buffer) as int?;
- return value == null ? null : PlatformBillingChoiceMode.values[value];
+ return value == null ? null : PlatformProductType.values[value];
case 133:
+ final int? value = readValue(buffer) as int?;
+ return value == null ? null : PlatformBillingChoiceMode.values[value];
+ case 134:
final int? value = readValue(buffer) as int?;
return value == null
? null
: PlatformBillingClientFeature.values[value];
- case 134:
+ case 135:
final int? value = readValue(buffer) as int?;
return value == null ? null : PlatformPurchaseState.values[value];
- case 135:
+ case 136:
final int? value = readValue(buffer) as int?;
return value == null ? null : PlatformRecurrenceMode.values[value];
- case 136:
- return PlatformQueryProduct.decode(readValue(buffer)!);
case 137:
- return PlatformAccountIdentifiers.decode(readValue(buffer)!);
+ return PlatformQueryProduct.decode(readValue(buffer)!);
case 138:
- return PlatformBillingResult.decode(readValue(buffer)!);
+ return PlatformAccountIdentifiers.decode(readValue(buffer)!);
case 139:
- return PlatformOneTimePurchaseOfferDetails.decode(readValue(buffer)!);
+ return PlatformBillingResult.decode(readValue(buffer)!);
case 140:
- return PlatformProductDetails.decode(readValue(buffer)!);
+ return PlatformOneTimePurchaseOfferDetails.decode(readValue(buffer)!);
case 141:
- return PlatformProductDetailsResponse.decode(readValue(buffer)!);
+ return PlatformProductDetails.decode(readValue(buffer)!);
case 142:
+ return PlatformProductDetailsResponse.decode(readValue(buffer)!);
+ case 143:
return PlatformAlternativeBillingOnlyReportingDetailsResponse.decode(
readValue(buffer)!,
);
- case 143:
- return PlatformBillingConfigResponse.decode(readValue(buffer)!);
case 144:
- return PlatformBillingFlowParams.decode(readValue(buffer)!);
+ return PlatformInAppMessageResult.decode(readValue(buffer)!);
case 145:
- return PlatformPricingPhase.decode(readValue(buffer)!);
+ return PlatformBillingConfigResponse.decode(readValue(buffer)!);
case 146:
- return PlatformPurchase.decode(readValue(buffer)!);
+ return PlatformBillingFlowParams.decode(readValue(buffer)!);
case 147:
- return PlatformPendingPurchaseUpdate.decode(readValue(buffer)!);
+ return PlatformPricingPhase.decode(readValue(buffer)!);
case 148:
- return PlatformPurchaseHistoryRecord.decode(readValue(buffer)!);
+ return PlatformPurchase.decode(readValue(buffer)!);
case 149:
- return PlatformPurchaseHistoryResponse.decode(readValue(buffer)!);
+ return PlatformPendingPurchaseUpdate.decode(readValue(buffer)!);
case 150:
- return PlatformPurchasesResponse.decode(readValue(buffer)!);
+ return PlatformPurchaseHistoryRecord.decode(readValue(buffer)!);
case 151:
- return PlatformSubscriptionOfferDetails.decode(readValue(buffer)!);
+ return PlatformPurchaseHistoryResponse.decode(readValue(buffer)!);
case 152:
- return PlatformUserChoiceDetails.decode(readValue(buffer)!);
+ return PlatformPurchasesResponse.decode(readValue(buffer)!);
case 153:
- return PlatformUserChoiceProduct.decode(readValue(buffer)!);
+ return PlatformSubscriptionOfferDetails.decode(readValue(buffer)!);
case 154:
- return PlatformInstallmentPlanDetails.decode(readValue(buffer)!);
+ return PlatformUserChoiceDetails.decode(readValue(buffer)!);
case 155:
+ return PlatformUserChoiceProduct.decode(readValue(buffer)!);
+ case 156:
+ return PlatformInstallmentPlanDetails.decode(readValue(buffer)!);
+ case 157:
return PlatformPendingPurchasesParams.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
@@ -1876,6 +1950,37 @@ class InAppPurchaseApi {
as PlatformAlternativeBillingOnlyReportingDetailsResponse?)!;
}
}
+
+ /// Wraps BillingClient#showInAppMessages().
+ Future showInAppMessages() async {
+ final String pigeonVar_channelName =
+ 'dev.flutter.pigeon.in_app_purchase_android.InAppPurchaseApi.showInAppMessages$pigeonVar_messageChannelSuffix';
+ final BasicMessageChannel pigeonVar_channel =
+ BasicMessageChannel(
+ pigeonVar_channelName,
+ pigeonChannelCodec,
+ binaryMessenger: pigeonVar_binaryMessenger,
+ );
+ final Future pigeonVar_sendFuture = pigeonVar_channel.send(null);
+ final List? pigeonVar_replyList =
+ await pigeonVar_sendFuture as List?;
+ if (pigeonVar_replyList == null) {
+ throw _createConnectionError(pigeonVar_channelName);
+ } else if (pigeonVar_replyList.length > 1) {
+ throw PlatformException(
+ code: pigeonVar_replyList[0]! as String,
+ message: pigeonVar_replyList[1] as String?,
+ details: pigeonVar_replyList[2],
+ );
+ } else if (pigeonVar_replyList[0] == null) {
+ throw PlatformException(
+ code: 'null-error',
+ message: 'Host platform returned null value for non-null return value.',
+ );
+ } else {
+ return (pigeonVar_replyList[0] as PlatformInAppMessageResult?)!;
+ }
+ }
}
abstract class InAppPurchaseCallbackApi {
diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/pigeon_converters.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/pigeon_converters.dart
index c3586df9c6a7..dcf22a3acab7 100644
--- a/packages/in_app_purchase/in_app_purchase_android/lib/src/pigeon_converters.dart
+++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/pigeon_converters.dart
@@ -6,6 +6,7 @@ import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_inte
import '../billing_client_wrappers.dart';
import 'billing_client_wrappers/billing_config_wrapper.dart';
+import 'billing_client_wrappers/in_app_message_wrapper.dart';
import 'billing_client_wrappers/pending_purchases_params_wrapper.dart';
import 'messages.g.dart';
@@ -130,6 +131,28 @@ alternativeBillingOnlyReportingDetailsWrapperFromPlatform(
);
}
+/// Converts [PlatformInAppMessageResponse] to its public API enum equivalent.
+InAppMessageResponse inAppMessageResponseFromPlatform(
+ PlatformInAppMessageResponse responseCode,
+) {
+ return switch (responseCode) {
+ PlatformInAppMessageResponse.noActionNeeded =>
+ InAppMessageResponse.noActionNeeded,
+ PlatformInAppMessageResponse.subscriptionStatusUpdated =>
+ InAppMessageResponse.subscriptionStatusUpdated,
+ };
+}
+
+/// Creates a [InAppMessageResultWrapper] from the Pigeon equivalent.
+InAppMessageResultWrapper inAppMessageResultWrapperFromPlatform(
+ PlatformInAppMessageResult result,
+) {
+ return InAppMessageResultWrapper(
+ responseCode: inAppMessageResponseFromPlatform(result.responseCode),
+ purchaseToken: result.purchaseToken,
+ );
+}
+
/// Creates a [BillingConfigWrapper] from the Pigeon equivalent.
BillingConfigWrapper billingConfigWrapperFromPlatform(
PlatformBillingConfigResponse response,
diff --git a/packages/in_app_purchase/in_app_purchase_android/pigeons/messages.dart b/packages/in_app_purchase/in_app_purchase_android/pigeons/messages.dart
index 90f987cc8dfc..8047f94b581c 100644
--- a/packages/in_app_purchase/in_app_purchase_android/pigeons/messages.dart
+++ b/packages/in_app_purchase/in_app_purchase_android/pigeons/messages.dart
@@ -118,6 +118,37 @@ class PlatformAlternativeBillingOnlyReportingDetailsResponse {
final String externalTransactionToken;
}
+/// Response code for the in-app messaging API call.
+enum PlatformInAppMessageResponse {
+ /// The flow has finished and there is no action needed from developers.
+ ///
+ /// Note: The API callback won't indicate whether message is dismissed by the
+ /// user or there is no message available to the user.
+ noActionNeeded,
+
+ /// The subscription status changed.
+ ///
+ /// For example, a subscription has been rec-
+ /// overed from a suspended state. Developers should expect the purchase token
+ /// to be returned with this response code and use the purchase token with the
+ /// Google Play Developer API.
+ subscriptionStatusUpdated,
+}
+
+/// Results related to in-app messaging.
+class PlatformInAppMessageResult {
+ PlatformInAppMessageResult({
+ required this.responseCode,
+ required this.purchaseToken,
+ });
+
+ /// Returns response code for the in-app messaging API call.
+ final PlatformInAppMessageResponse responseCode;
+
+ /// Returns token that identifies the purchase to be acknowledged, if any.
+ final String? purchaseToken;
+}
+
/// Pigeon version of BillingConfigWrapper, which contains the components of the
/// Java BillingConfigResponseListener callback.
class PlatformBillingConfigResponse {
@@ -443,6 +474,10 @@ abstract class InAppPurchaseApi {
@async
PlatformAlternativeBillingOnlyReportingDetailsResponse
createAlternativeBillingOnlyReportingDetailsAsync();
+
+ /// Wraps BillingClient#showInAppMessages().
+ @async
+ PlatformInAppMessageResult showInAppMessages();
}
@FlutterApi()
diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart
index 2959ea704712..bf2ed65830c7 100644
--- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart
+++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart
@@ -684,6 +684,24 @@ void main() {
expect(result, expected);
});
});
+
+ group('showInAppMessages', () {
+ test('returns object', () async {
+ const expected = InAppMessageResultWrapper(
+ responseCode: InAppMessageResponse.subscriptionStatusUpdated,
+ purchaseToken: 'dummy purchase token',
+ );
+ when(mockApi.showInAppMessages()).thenAnswer(
+ (_) async => PlatformInAppMessageResult(
+ responseCode: PlatformInAppMessageResponse.subscriptionStatusUpdated,
+ purchaseToken: expected.purchaseToken,
+ ),
+ );
+ final InAppMessageResultWrapper result = await billingClient
+ .showInAppMessages();
+ expect(result, expected);
+ });
+ });
}
PlatformBillingConfigResponse platformBillingConfigFromWrapper(
diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.mocks.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.mocks.dart
index e8d4c2c38e67..30c4526d7742 100644
--- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.mocks.dart
+++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.mocks.dart
@@ -1,4 +1,4 @@
-// Mocks generated by Mockito 5.4.4 from annotations
+// Mocks generated by Mockito 5.4.6 from annotations
// in in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart.
// Do not manually edit this file.
@@ -67,6 +67,12 @@ class _FakePlatformAlternativeBillingOnlyReportingDetailsResponse_5
) : super(parent, parentInvocation);
}
+class _FakePlatformInAppMessageResult_6 extends _i1.SmartFake
+ implements _i2.PlatformInAppMessageResult {
+ _FakePlatformInAppMessageResult_6(Object parent, Invocation parentInvocation)
+ : super(parent, parentInvocation);
+}
+
/// A class which mocks [InAppPurchaseApi].
///
/// See the documentation for Mockito's code generation for more information.
@@ -390,4 +396,24 @@ class MockInAppPurchaseApi extends _i1.Mock implements _i2.InAppPurchaseApi {
as _i4.Future<
_i2.PlatformAlternativeBillingOnlyReportingDetailsResponse
>);
+
+ @override
+ _i4.Future<_i2.PlatformInAppMessageResult> showInAppMessages() =>
+ (super.noSuchMethod(
+ Invocation.method(#showInAppMessages, []),
+ returnValue: _i4.Future<_i2.PlatformInAppMessageResult>.value(
+ _FakePlatformInAppMessageResult_6(
+ this,
+ Invocation.method(#showInAppMessages, []),
+ ),
+ ),
+ returnValueForMissingStub:
+ _i4.Future<_i2.PlatformInAppMessageResult>.value(
+ _FakePlatformInAppMessageResult_6(
+ this,
+ Invocation.method(#showInAppMessages, []),
+ ),
+ ),
+ )
+ as _i4.Future<_i2.PlatformInAppMessageResult>);
}