Commit a04f522e authored by Peter E Conn's avatar Peter E Conn Committed by Commit Bot

💸 Implement Digital Goods API ListPurchases on Android.

This CL takes the Mojo changes from:
  https://chromium-review.googlesource.com/c/chromium/src/+/2484038
and adds an Android implementation for them.

Once these mojo changes are wired up on the Blink side, I'll add some
tests to DigitalGoodsTests.

Bug: 1139795
Change-Id: Ib8c92cbc619b8977f37d3a9b59a3045d5c1bbb7f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2510271
Commit-Queue: Peter Conn <peconn@chromium.org>
Reviewed-by: default avatarMichael van Ouwerkerk <mvanouwerkerk@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarGlen Robertson <glenrob@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/master@{#824001}
parent 9ba7081c
...@@ -900,6 +900,7 @@ junit_binary("chrome_junit_tests") { ...@@ -900,6 +900,7 @@ junit_binary("chrome_junit_tests") {
"//content/public/test/android:content_java_test_support", "//content/public/test/android:content_java_test_support",
"//mojo/public/java:bindings_java", "//mojo/public/java:bindings_java",
"//mojo/public/java:system_java", "//mojo/public/java:system_java",
"//mojo/public/mojom/base:base_java",
"//net/android:net_java", "//net/android:net_java",
"//services/device/public/mojom:mojom_java", "//services/device/public/mojom:mojom_java",
"//services/media_session/public/cpp/android:media_session_java", "//services/media_session/public/cpp/android:media_session_java",
......
...@@ -191,6 +191,7 @@ chrome_java_sources = [ ...@@ -191,6 +191,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryImpl.java", "java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryImpl.java",
"java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsImpl.java", "java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsImpl.java",
"java/src/org/chromium/chrome/browser/browserservices/digitalgoods/GetDetailsConverter.java", "java/src/org/chromium/chrome/browser/browserservices/digitalgoods/GetDetailsConverter.java",
"java/src/org/chromium/chrome/browser/browserservices/digitalgoods/ListPurchasesConverter.java",
"java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappBridge.java", "java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappBridge.java",
"java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappGeolocationBridge.java", "java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappGeolocationBridge.java",
"java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/LocationPermissionUpdater.java", "java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/LocationPermissionUpdater.java",
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
package org.chromium.chrome.browser.browserservices.digitalgoods; package org.chromium.chrome.browser.browserservices.digitalgoods;
import static org.chromium.chrome.browser.browserservices.digitalgoods.DigitalGoodsConverter.convertResponseCodes; import static org.chromium.chrome.browser.browserservices.digitalgoods.DigitalGoodsConverter.convertResponseCode;
import android.os.Bundle; import android.os.Bundle;
...@@ -60,7 +60,7 @@ class AcknowledgeConverter { ...@@ -60,7 +60,7 @@ class AcknowledgeConverter {
} }
int code = args.getInt(RESPONSE_ACKNOWLEDGE_RESPONSE_CODE); int code = args.getInt(RESPONSE_ACKNOWLEDGE_RESPONSE_CODE);
callback.call(convertResponseCodes(code)); callback.call(convertResponseCode(code));
} }
}; };
} }
......
...@@ -15,6 +15,7 @@ import org.chromium.chrome.browser.browserservices.TrustedWebActivityClient; ...@@ -15,6 +15,7 @@ import org.chromium.chrome.browser.browserservices.TrustedWebActivityClient;
import org.chromium.components.embedder_support.util.Origin; import org.chromium.components.embedder_support.util.Origin;
import org.chromium.payments.mojom.DigitalGoods.AcknowledgeResponse; import org.chromium.payments.mojom.DigitalGoods.AcknowledgeResponse;
import org.chromium.payments.mojom.DigitalGoods.GetDetailsResponse; import org.chromium.payments.mojom.DigitalGoods.GetDetailsResponse;
import org.chromium.payments.mojom.DigitalGoods.ListPurchasesResponse;
/** /**
* This class uses the {@link DigitalGoodsConverter} to convert data types between mojo types and * This class uses the {@link DigitalGoodsConverter} to convert data types between mojo types and
...@@ -26,6 +27,7 @@ public class DigitalGoodsAdapter { ...@@ -26,6 +27,7 @@ public class DigitalGoodsAdapter {
public static final String COMMAND_ACKNOWLEDGE = "acknowledge"; public static final String COMMAND_ACKNOWLEDGE = "acknowledge";
public static final String COMMAND_GET_DETAILS = "getDetails"; public static final String COMMAND_GET_DETAILS = "getDetails";
public static final String COMMAND_LIST_PURCHASES = "listPurchases";
public static final String KEY_SUCCESS = "success"; public static final String KEY_SUCCESS = "success";
private final TrustedWebActivityClient mClient; private final TrustedWebActivityClient mClient;
...@@ -53,6 +55,15 @@ public class DigitalGoodsAdapter { ...@@ -53,6 +55,15 @@ public class DigitalGoodsAdapter {
execute(scope, COMMAND_ACKNOWLEDGE, args, callback, onError, onUnavailable); execute(scope, COMMAND_ACKNOWLEDGE, args, callback, onError, onUnavailable);
} }
public void listPurchases(Uri scope, ListPurchasesResponse response) {
Bundle args = new Bundle();
TrustedWebActivityCallback callback = ListPurchasesConverter.convertCallback(response);
Runnable onError = () -> ListPurchasesConverter.returnClientAppError(response);
Runnable onUnavailable = () -> ListPurchasesConverter.returnClientAppUnavailable(response);
execute(scope, COMMAND_LIST_PURCHASES, args, callback, onError, onUnavailable);
}
private void execute(Uri scope, String command, Bundle args, private void execute(Uri scope, String command, Bundle args,
TrustedWebActivityCallback callback, Runnable onClientAppError, TrustedWebActivityCallback callback, Runnable onClientAppError,
Runnable onClientAppUnavailable) { Runnable onClientAppUnavailable) {
......
...@@ -4,9 +4,17 @@ ...@@ -4,9 +4,17 @@
package org.chromium.chrome.browser.browserservices.digitalgoods; package org.chromium.chrome.browser.browserservices.digitalgoods;
import android.os.Bundle;
import android.os.Parcelable;
import androidx.annotation.Nullable;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.payments.mojom.BillingResponseCode; import org.chromium.payments.mojom.BillingResponseCode;
import java.util.ArrayList;
import java.util.List;
/** /**
* The *Converter classes take care of converting between the mojo types that * The *Converter classes take care of converting between the mojo types that
* {@link DigitalGoodsImpl} deals with and the Android types that {@link TrustedWebActivityClient} * {@link DigitalGoodsImpl} deals with and the Android types that {@link TrustedWebActivityClient}
...@@ -27,7 +35,7 @@ public class DigitalGoodsConverter { ...@@ -27,7 +35,7 @@ public class DigitalGoodsConverter {
private DigitalGoodsConverter() {} private DigitalGoodsConverter() {}
static int convertResponseCodes(int responseCode) { static int convertResponseCode(int responseCode) {
switch (responseCode) { switch (responseCode) {
case PLAY_BILLING_OK: case PLAY_BILLING_OK:
return BillingResponseCode.OK; return BillingResponseCode.OK;
...@@ -42,4 +50,38 @@ public class DigitalGoodsConverter { ...@@ -42,4 +50,38 @@ public class DigitalGoodsConverter {
return BillingResponseCode.ERROR; return BillingResponseCode.ERROR;
} }
} }
/** Checks that the given field exists and is of the required type in a Bundle. */
static <T> boolean checkField(Bundle bundle, String key, Class<T> clazz) {
if (bundle.containsKey(key) && clazz.isAssignableFrom(bundle.get(key).getClass())) {
return true;
}
Log.w(TAG, "Missing field " + key + " of type " + clazz.getName() + ".");
return false;
}
/** An interface for use with {@link #convertParcelableArray}. */
interface Converter<T> {
@Nullable
T convert(Bundle bundle);
}
/**
* Runs through the given {@link Parcelable[]} and for each item that is a {@link Bundle}, calls
* {@link Converter#convert}, returning an array of the non-null results.
*/
static <T> List<T> convertParcelableArray(Parcelable[] array, Converter<T> converter) {
List<T> list = new ArrayList<>();
for (Parcelable item : array) {
if (!(item instanceof Bundle)) {
Log.w(TAG, "Passed a Parcelable that was not a Bundle.");
continue;
}
T converted = converter.convert((Bundle) item);
if (converted != null) list.add(converted);
}
return list;
}
} }
...@@ -12,6 +12,7 @@ import org.chromium.mojo.system.MojoException; ...@@ -12,6 +12,7 @@ import org.chromium.mojo.system.MojoException;
import org.chromium.payments.mojom.DigitalGoods; import org.chromium.payments.mojom.DigitalGoods;
import org.chromium.payments.mojom.DigitalGoods.AcknowledgeResponse; import org.chromium.payments.mojom.DigitalGoods.AcknowledgeResponse;
import org.chromium.payments.mojom.DigitalGoods.GetDetailsResponse; import org.chromium.payments.mojom.DigitalGoods.GetDetailsResponse;
import org.chromium.payments.mojom.DigitalGoods.ListPurchasesResponse;
/** /**
* An implementation of the {@link DigitalGoods} mojo interface that communicates with Trusted Web * An implementation of the {@link DigitalGoods} mojo interface that communicates with Trusted Web
...@@ -49,6 +50,12 @@ public class DigitalGoodsImpl implements DigitalGoods { ...@@ -49,6 +50,12 @@ public class DigitalGoodsImpl implements DigitalGoods {
} }
} }
@Override
public void listPurchases(ListPurchasesResponse callback) {
String url = mDelegate.getUrl();
if (url != null) mAdapter.listPurchases(Uri.parse(mDelegate.getUrl()), callback);
}
@Override @Override
public void close() {} public void close() {}
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
package org.chromium.chrome.browser.browserservices.digitalgoods; package org.chromium.chrome.browser.browserservices.digitalgoods;
import static org.chromium.chrome.browser.browserservices.digitalgoods.DigitalGoodsConverter.convertResponseCodes; import static org.chromium.chrome.browser.browserservices.digitalgoods.DigitalGoodsConverter.checkField;
import static org.chromium.chrome.browser.browserservices.digitalgoods.DigitalGoodsConverter.convertParcelableArray;
import static org.chromium.chrome.browser.browserservices.digitalgoods.DigitalGoodsConverter.convertResponseCode;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
...@@ -20,9 +22,6 @@ import org.chromium.payments.mojom.DigitalGoods.GetDetailsResponse; ...@@ -20,9 +22,6 @@ import org.chromium.payments.mojom.DigitalGoods.GetDetailsResponse;
import org.chromium.payments.mojom.ItemDetails; import org.chromium.payments.mojom.ItemDetails;
import org.chromium.payments.mojom.PaymentCurrencyAmount; import org.chromium.payments.mojom.PaymentCurrencyAmount;
import java.util.ArrayList;
import java.util.List;
/** /**
* A converter that deals with the parameters and result for GetDetails calls. * A converter that deals with the parameters and result for GetDetails calls.
*/ */
...@@ -30,9 +29,9 @@ public class GetDetailsConverter { ...@@ -30,9 +29,9 @@ public class GetDetailsConverter {
private static final String TAG = "DigitalGoods"; private static final String TAG = "DigitalGoods";
static final String PARAM_GET_DETAILS_ITEM_IDS = "getDetails.itemIds"; static final String PARAM_GET_DETAILS_ITEM_IDS = "getDetails.itemIds";
public static final String RESPONSE_GET_DETAILS = "getDetails.response"; public static final String RESPONSE_COMMAND = "getDetails.response";
static final String RESPONSE_GET_DETAILS_RESPONSE_CODE = "getDetails.responseCode"; static final String KEY_RESPONSE_CODE = "getDetails.responseCode";
static final String RESPONSE_GET_DETAILS_DETAILS_LIST = "getDetails.detailsList"; static final String KEY_DETAILS_LIST = "getDetails.detailsList";
static final String KEY_ID = "itemDetails.id"; static final String KEY_ID = "itemDetails.id";
static final String KEY_TITLE = "itemDetails.title"; static final String KEY_TITLE = "itemDetails.title";
...@@ -66,7 +65,7 @@ public class GetDetailsConverter { ...@@ -66,7 +65,7 @@ public class GetDetailsConverter {
return new TrustedWebActivityCallback() { return new TrustedWebActivityCallback() {
@Override @Override
public void onExtraCallback(@NonNull String callbackName, @Nullable Bundle args) { public void onExtraCallback(@NonNull String callbackName, @Nullable Bundle args) {
if (!RESPONSE_GET_DETAILS.equals(callbackName)) { if (!RESPONSE_COMMAND.equals(callbackName)) {
Log.w(TAG, "Wrong callback name given: " + callbackName + "."); Log.w(TAG, "Wrong callback name given: " + callbackName + ".");
returnClientAppError(callback); returnClientAppError(callback);
return; return;
...@@ -78,39 +77,26 @@ public class GetDetailsConverter { ...@@ -78,39 +77,26 @@ public class GetDetailsConverter {
return; return;
} }
if (!(args.get(RESPONSE_GET_DETAILS_RESPONSE_CODE) instanceof Integer) if (!checkField(args, KEY_RESPONSE_CODE, Integer.class)
|| !(args.get(RESPONSE_GET_DETAILS_DETAILS_LIST) instanceof Parcelable[])) { || !checkField(args, KEY_DETAILS_LIST, Parcelable[].class)) {
Log.w(TAG, "Poorly formed args provided.");
returnClientAppError(callback); returnClientAppError(callback);
return; return;
} }
int code = args.getInt(RESPONSE_GET_DETAILS_RESPONSE_CODE); int code = args.getInt(KEY_RESPONSE_CODE);
ItemDetails[] details = convertItemDetailsList( Parcelable[] array = args.getParcelableArray(KEY_DETAILS_LIST);
args.getParcelableArray(RESPONSE_GET_DETAILS_DETAILS_LIST));
callback.call(convertResponseCodes(code), details); ItemDetails[] details =
convertParcelableArray(array, GetDetailsConverter::convertItemDetails)
.toArray(new ItemDetails[0]);
callback.call(convertResponseCode(code), details);
} }
}; };
} }
private static ItemDetails[] convertItemDetailsList(Parcelable[] list) {
List<ItemDetails> details = new ArrayList<>();
for (Parcelable item : list) {
details.add(convertItemDetails(item));
}
return details.toArray(new ItemDetails[0]);
}
/** Extracts an {@link ItemDetails} from a {@link Parcelable}. */ /** Extracts an {@link ItemDetails} from a {@link Parcelable}. */
@Nullable @Nullable
static ItemDetails convertItemDetails(Parcelable itemAsParcelable) { static ItemDetails convertItemDetails(Bundle item) {
if (!(itemAsParcelable instanceof Bundle)) {
Log.w(TAG, "Item is not a Bundle.");
return null;
}
Bundle item = (Bundle) itemAsParcelable;
for (String field : REQUIRED_FIELDS) { for (String field : REQUIRED_FIELDS) {
if (item.containsKey(field) && (item.get(field) instanceof String)) continue; if (item.containsKey(field) && (item.get(field) instanceof String)) continue;
Log.w(TAG, "Item does not contain field String " + field + "."); Log.w(TAG, "Item does not contain field String " + field + ".");
...@@ -146,11 +132,11 @@ public class GetDetailsConverter { ...@@ -146,11 +132,11 @@ public class GetDetailsConverter {
return result; return result;
} }
public static void returnClientAppUnavailable(GetDetailsResponse callback) { static void returnClientAppUnavailable(GetDetailsResponse callback) {
callback.call(BillingResponseCode.CLIENT_APP_UNAVAILABLE, new ItemDetails[0]); callback.call(BillingResponseCode.CLIENT_APP_UNAVAILABLE, new ItemDetails[0]);
} }
public static void returnClientAppError(GetDetailsResponse callback) { static void returnClientAppError(GetDetailsResponse callback) {
callback.call(BillingResponseCode.CLIENT_APP_ERROR, new ItemDetails[0]); callback.call(BillingResponseCode.CLIENT_APP_ERROR, new ItemDetails[0]);
} }
...@@ -159,10 +145,10 @@ public class GetDetailsConverter { ...@@ -159,10 +145,10 @@ public class GetDetailsConverter {
* This would be used by the client app and is here only to help testing. * This would be used by the client app and is here only to help testing.
*/ */
@VisibleForTesting @VisibleForTesting
public static Bundle createItemDetailsBundle(String id, String title, String desc, static Bundle createItemDetailsBundle(String id, String title, String desc, String currency,
String currency, String value, @Nullable String subsPeriod, String value, @Nullable String subsPeriod, @Nullable String freeTrialPeriod,
@Nullable String freeTrialPeriod, @Nullable String introPriceCurrency, @Nullable String introPriceCurrency, @Nullable String introPriceValue,
@Nullable String introPriceValue, @Nullable String intoPricePeriod) { @Nullable String intoPricePeriod) {
Bundle bundle = createItemDetailsBundle(id, title, desc, currency, value); Bundle bundle = createItemDetailsBundle(id, title, desc, currency, value);
bundle.putString(KEY_SUBS_PERIOD, subsPeriod); bundle.putString(KEY_SUBS_PERIOD, subsPeriod);
...@@ -178,7 +164,7 @@ public class GetDetailsConverter { ...@@ -178,7 +164,7 @@ public class GetDetailsConverter {
* Like the above method, but provides {@code null} for all optional parameters. * Like the above method, but provides {@code null} for all optional parameters.
*/ */
@VisibleForTesting @VisibleForTesting
public static Bundle createItemDetailsBundle( static Bundle createItemDetailsBundle(
String id, String title, String desc, String currency, String value) { String id, String title, String desc, String currency, String value) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
...@@ -196,11 +182,11 @@ public class GetDetailsConverter { ...@@ -196,11 +182,11 @@ public class GetDetailsConverter {
* carried out by the client app and is only here to help testing. * carried out by the client app and is only here to help testing.
*/ */
@VisibleForTesting @VisibleForTesting
public static Bundle createResponseBundle(int responseCode, Bundle... itemDetails) { static Bundle createResponseBundle(int responseCode, Bundle... itemDetails) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putInt(RESPONSE_GET_DETAILS_RESPONSE_CODE, responseCode); bundle.putInt(KEY_RESPONSE_CODE, responseCode);
bundle.putParcelableArray(RESPONSE_GET_DETAILS_DETAILS_LIST, itemDetails); bundle.putParcelableArray(KEY_DETAILS_LIST, itemDetails);
return bundle; return bundle;
} }
......
// Copyright 2020 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 org.chromium.chrome.browser.browserservices.digitalgoods;
import static org.chromium.chrome.browser.browserservices.digitalgoods.DigitalGoodsConverter.checkField;
import static org.chromium.chrome.browser.browserservices.digitalgoods.DigitalGoodsConverter.convertParcelableArray;
import static org.chromium.chrome.browser.browserservices.digitalgoods.DigitalGoodsConverter.convertResponseCode;
import android.os.Bundle;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.browser.trusted.TrustedWebActivityCallback;
import org.chromium.base.Log;
import org.chromium.mojo_base.mojom.TimeDelta;
import org.chromium.payments.mojom.BillingResponseCode;
import org.chromium.payments.mojom.DigitalGoods.ListPurchasesResponse;
import org.chromium.payments.mojom.PurchaseDetails;
import org.chromium.payments.mojom.PurchaseState;
/**
* A converter that deals with the results of ListPurchases calls.
*/
class ListPurchasesConverter {
private static final String TAG = "DigitalGoods";
static final String RESPONSE_COMMAND = "listPurchases.response";
static final String KEY_PURCHASES_LIST = "listPurchases.purchasesList";
static final String KEY_RESPONSE_CODE = "listPurchases.responseCode";
static final String KEY_ITEM_ID = "listPurchases.itemId";
static final String KEY_PURCHASE_TOKEN = "listPurchases.purchaseToken";
static final String KEY_ACKNOWLEDGED = "listPurchases.acknowledged";
static final String KEY_PURCHASE_STATE = "listPurchases.purchaseState";
static final String KEY_PURCHASE_TIME_MS_PAST_UNIX_EPOCH = "listPurchases.purchaseTime";
static final String KEY_WILL_AUTO_RENEW = "listPurchases.willAutoRenew";
// These values are copied from the Play Billing library since Chrome cannot depend on it.
// https://developer.android.com/reference/com/android/billingclient/api/Purchase.PurchaseState
static final int PLAY_BILLING_PURCHASE_STATE_PENDING = 2;
static final int PLAY_BILLING_PURCHASE_STATE_PURCHASED = 1;
static final int PLAY_BILLING_PURCHASE_STATE_UNSPECIFIED = 0;
/**
* Produces a {@link TrustedWebActivityCallback} that calls the given
* {@link ListPurchasesResponse}.
*/
static TrustedWebActivityCallback convertCallback(ListPurchasesResponse callback) {
return new TrustedWebActivityCallback() {
@Override
public void onExtraCallback(@NonNull String callbackName, @Nullable Bundle args) {
if (!RESPONSE_COMMAND.equals(callbackName)) {
Log.w(TAG, "Wrong callback name given: " + callbackName + ".");
returnClientAppError(callback);
return;
}
if (args == null) {
Log.w(TAG, "No args provided.");
returnClientAppError(callback);
return;
}
if (!checkField(args, KEY_RESPONSE_CODE, Integer.class)
|| !checkField(args, KEY_PURCHASES_LIST, Parcelable[].class)) {
returnClientAppError(callback);
return;
}
int code = args.getInt(KEY_RESPONSE_CODE);
Parcelable[] array = args.getParcelableArray(KEY_PURCHASES_LIST);
PurchaseDetails[] details = convertParcelableArray(
array, ListPurchasesConverter::convertPurchaseDetails)
.toArray(new PurchaseDetails[0]);
callback.call(convertResponseCode(code), details);
}
};
}
static PurchaseDetails convertPurchaseDetails(Bundle purchase) {
if (!checkField(purchase, KEY_ITEM_ID, String.class)) return null;
if (!checkField(purchase, KEY_PURCHASE_TOKEN, String.class)) return null;
if (!checkField(purchase, KEY_ACKNOWLEDGED, Boolean.class)) return null;
if (!checkField(purchase, KEY_PURCHASE_STATE, Integer.class)) return null;
if (!checkField(purchase, KEY_PURCHASE_TIME_MS_PAST_UNIX_EPOCH, Long.class)) return null;
if (!checkField(purchase, KEY_WILL_AUTO_RENEW, Boolean.class)) return null;
TimeDelta purchaseTime = new TimeDelta();
purchaseTime.microseconds = purchase.getLong(KEY_PURCHASE_TIME_MS_PAST_UNIX_EPOCH);
PurchaseDetails result = new PurchaseDetails();
result.itemId = purchase.getString(KEY_ITEM_ID);
result.purchaseToken = purchase.getString(KEY_PURCHASE_TOKEN);
result.acknowledged = purchase.getBoolean(KEY_ACKNOWLEDGED);
result.purchaseState = convertPurchaseState(purchase.getInt(KEY_PURCHASE_STATE));
result.purchaseTime = purchaseTime;
result.willAutoRenew = purchase.getBoolean(KEY_WILL_AUTO_RENEW);
return result;
}
static int convertPurchaseState(int purchaseState) {
switch (purchaseState) {
case PLAY_BILLING_PURCHASE_STATE_PENDING:
return PurchaseState.PENDING;
case PLAY_BILLING_PURCHASE_STATE_PURCHASED:
return PurchaseState.PURCHASED;
default:
return PurchaseState.UNKNOWN;
}
}
static void returnClientAppUnavailable(ListPurchasesResponse callback) {
callback.call(BillingResponseCode.CLIENT_APP_UNAVAILABLE, new PurchaseDetails[0]);
}
static void returnClientAppError(ListPurchasesResponse callback) {
callback.call(BillingResponseCode.CLIENT_APP_ERROR, new PurchaseDetails[0]);
}
@VisibleForTesting
static Bundle createPurchaseDetailsBundle(String itemId, String purchaseToken,
boolean acknowledged, int purchaseState, long purchaseTimeMsPastUnixEpoch,
boolean willAutoRenew) {
Bundle bundle = new Bundle();
bundle.putString(KEY_ITEM_ID, itemId);
bundle.putString(KEY_PURCHASE_TOKEN, purchaseToken);
bundle.putBoolean(KEY_ACKNOWLEDGED, acknowledged);
bundle.putInt(KEY_PURCHASE_STATE, purchaseState);
bundle.putLong(KEY_PURCHASE_TIME_MS_PAST_UNIX_EPOCH, purchaseTimeMsPastUnixEpoch);
bundle.putBoolean(KEY_WILL_AUTO_RENEW, willAutoRenew);
return bundle;
}
@VisibleForTesting
static Bundle createResponseBundle(int responseCode, Bundle... purchaseDetails) {
Bundle bundle = new Bundle();
bundle.putInt(KEY_RESPONSE_CODE, responseCode);
bundle.putParcelableArray(KEY_PURCHASES_LIST, purchaseDetails);
return bundle;
}
}
...@@ -10,7 +10,6 @@ import static org.chromium.chrome.browser.browserservices.TestTrustedWebActivity ...@@ -10,7 +10,6 @@ import static org.chromium.chrome.browser.browserservices.TestTrustedWebActivity
import static org.chromium.chrome.browser.browserservices.TestTrustedWebActivityService.SET_RESPONSE_BUNDLE; import static org.chromium.chrome.browser.browserservices.TestTrustedWebActivityService.SET_RESPONSE_BUNDLE;
import static org.chromium.chrome.browser.browserservices.TestTrustedWebActivityService.SET_RESPONSE_NAME; import static org.chromium.chrome.browser.browserservices.TestTrustedWebActivityService.SET_RESPONSE_NAME;
import static org.chromium.chrome.browser.browserservices.digitalgoods.AcknowledgeConverter.RESPONSE_ACKNOWLEDGE; import static org.chromium.chrome.browser.browserservices.digitalgoods.AcknowledgeConverter.RESPONSE_ACKNOWLEDGE;
import static org.chromium.chrome.browser.browserservices.digitalgoods.GetDetailsConverter.RESPONSE_GET_DETAILS;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
...@@ -115,7 +114,7 @@ public class DigitalGoodsTest { ...@@ -115,7 +114,7 @@ public class DigitalGoodsTest {
public void twaServiceConnected() throws TimeoutException { public void twaServiceConnected() throws TimeoutException {
DigitalGoodsImpl impl = createFixedDigitalGoods(); DigitalGoodsImpl impl = createFixedDigitalGoods();
setTwaServiceResponse(RESPONSE_GET_DETAILS, setTwaServiceResponse(GetDetailsConverter.RESPONSE_COMMAND,
GetDetailsConverter.createResponseBundle(0, GetDetailsConverter.createResponseBundle(0,
GetDetailsConverter.createItemDetailsBundle( GetDetailsConverter.createItemDetailsBundle(
"id1", "Item 1", "Desc 1", "GBP", "10"))); "id1", "Item 1", "Desc 1", "GBP", "10")));
...@@ -146,7 +145,7 @@ public class DigitalGoodsTest { ...@@ -146,7 +145,7 @@ public class DigitalGoodsTest {
// Note: The response code much be 0 for success otherwise it doesn't propagate through to // Note: The response code much be 0 for success otherwise it doesn't propagate through to
// JS. // JS.
setTwaServiceResponse(RESPONSE_GET_DETAILS, setTwaServiceResponse(GetDetailsConverter.RESPONSE_COMMAND,
GetDetailsConverter.createResponseBundle(0, GetDetailsConverter.createResponseBundle(0,
GetDetailsConverter.createItemDetailsBundle( GetDetailsConverter.createItemDetailsBundle(
"id1", "Item 1", "Desc 1", "GBP", "10"))); "id1", "Item 1", "Desc 1", "GBP", "10")));
......
...@@ -8,6 +8,7 @@ import org.chromium.mojo.system.MojoException; ...@@ -8,6 +8,7 @@ import org.chromium.mojo.system.MojoException;
import org.chromium.payments.mojom.DigitalGoods; import org.chromium.payments.mojom.DigitalGoods;
import org.chromium.payments.mojom.DigitalGoods.AcknowledgeResponse; import org.chromium.payments.mojom.DigitalGoods.AcknowledgeResponse;
import org.chromium.payments.mojom.DigitalGoods.GetDetailsResponse; import org.chromium.payments.mojom.DigitalGoods.GetDetailsResponse;
import org.chromium.payments.mojom.DigitalGoods.ListPurchasesResponse;
import org.chromium.payments.mojom.ItemDetails; import org.chromium.payments.mojom.ItemDetails;
import org.chromium.payments.mojom.PaymentCurrencyAmount; import org.chromium.payments.mojom.PaymentCurrencyAmount;
...@@ -53,8 +54,11 @@ class FakeDigitalGoods implements DigitalGoods { ...@@ -53,8 +54,11 @@ class FakeDigitalGoods implements DigitalGoods {
} }
@Override @Override
public void acknowledge(String purchaseToken, boolean makeAvailableAgain, public void acknowledge(
AcknowledgeResponse callback) { } String purchaseToken, boolean makeAvailableAgain, AcknowledgeResponse callback) {}
@Override
public void listPurchases(ListPurchasesResponse callback) {}
@Override @Override
public void close() {} public void close() {}
......
...@@ -22,6 +22,12 @@ interface DigitalGoods { ...@@ -22,6 +22,12 @@ interface DigitalGoods {
// permanent upgrade). // permanent upgrade).
Acknowledge(string purchase_token, bool make_available_again) Acknowledge(string purchase_token, bool make_available_again)
=> (BillingResponseCode code); => (BillingResponseCode code);
// Queries the associated backend for information on all items that are
// currently owned by the user.
ListPurchases()
=> (BillingResponseCode code,
array<PurchaseDetails> purchase_details_list);
}; };
// Allow the renderer to request a |DigitalGoods| instance. DigitalGoods // Allow the renderer to request a |DigitalGoods| instance. DigitalGoods
...@@ -64,3 +70,19 @@ enum CreateDigitalGoodsResponseCode { ...@@ -64,3 +70,19 @@ enum CreateDigitalGoodsResponseCode {
kUnsupportedPaymentMethod, kUnsupportedPaymentMethod,
kUnsupportedContext, kUnsupportedContext,
}; };
struct PurchaseDetails {
string item_id;
string purchase_token;
bool acknowledged;
PurchaseState purchase_state;
// Microseconds since the Unix epoch.
mojo_base.mojom.TimeDelta purchase_time;
bool will_auto_renew;
};
enum PurchaseState {
kUnknown,
kPurchased,
kPending,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment