Index: chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b38562ee18c5fa57a002ceaea4541428cf694c5c |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java |
@@ -0,0 +1,250 @@ |
+// Copyright 2016 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.payments; |
+ |
+import android.app.Activity; |
+import android.content.ComponentName; |
+import android.content.ContentResolver; |
+import android.content.Context; |
+import android.content.Intent; |
+import android.content.ServiceConnection; |
+import android.graphics.drawable.Drawable; |
+import android.os.Bundle; |
+import android.os.Handler; |
+import android.os.IBinder; |
+import android.os.RemoteException; |
+import android.util.JsonWriter; |
+ |
+import org.json.JSONObject; |
+ |
+import org.chromium.IsReadyToPayService; |
+import org.chromium.chrome.R; |
+import org.chromium.payments.mojom.PaymentItem; |
+import org.chromium.ui.base.WindowAndroid; |
+ |
+import java.io.IOException; |
+import java.io.StringWriter; |
+import java.util.ArrayList; |
+import java.util.HashSet; |
+import java.util.List; |
+import java.util.Map; |
+import java.util.Set; |
+ |
+/** A locally installed payment app. */ |
+public class AndroidPaymentApp extends PaymentInstrument implements PaymentApp, |
+ WindowAndroid.IntentCallback { |
+ private static final String DATA_EXTRA = "data"; |
+ private static final String DETAILS_EXTRA = "details"; |
+ private static final String EMPTY_JSON_DATA = "{}"; |
+ private static final String METHOD_NAME_EXTRA = "methodName"; |
+ private static final String INSTRUMENT_DETAILS_EXTRA = "instrumentDetails"; |
+ private static final String ORIGIN_EXTRA = "origin"; |
+ private static final String READY_TO_PAY = "readyToPay"; |
+ |
+ private final Handler mHandler; |
+ private final WindowAndroid mWindow; |
+ private final Intent mIsReadyToPayIntent; |
+ private final Intent mPayIntent; |
+ private final Set<String> mMethodNames; |
+ private IsReadyToPayService mIsReadyToPayService; |
+ private InstrumentsCallback mInstrumentsCallback; |
+ private InstrumentDetailsCallback mInstrumentDetailsCallback; |
+ |
+ public AndroidPaymentApp(WindowAndroid window, String packageName, String activity, |
+ String label, Drawable icon) { |
+ super(packageName, label, null, icon); |
+ mHandler = new Handler(); |
+ mWindow = window; |
+ mIsReadyToPayIntent = new Intent(); |
+ mIsReadyToPayIntent.setPackage(packageName); |
+ mPayIntent = new Intent(); |
+ mPayIntent.setClassName(packageName, activity); |
+ mMethodNames = new HashSet<>(); |
+ } |
+ |
+ public void addMethodName(String methodName) { |
+ mMethodNames.add(methodName); |
+ } |
+ |
+ public void setIsReadyToPayAction(String className) { |
+ mIsReadyToPayIntent.setClassName(mIsReadyToPayIntent.getPackage(), className); |
+ } |
+ |
+ @Override |
+ public void getInstruments( |
+ final Map<String, JSONObject> methodData, final String origin, |
+ final InstrumentsCallback callback) { |
+ mInstrumentsCallback = callback; |
+ mHandler.post(new Runnable() { |
+ @Override |
+ public void run() { |
+ if (mIsReadyToPayIntent.getComponent() != null) { |
+ isReadyToPay(origin, methodData.get(getInstrumentMethodName())); |
+ } else { |
+ sendInstrumentsReady(AndroidPaymentApp.this); |
+ } |
+ } |
+ }); |
+ } |
+ |
+ private void sendInstrumentsReady(PaymentInstrument instrument) { |
+ assert mInstrumentsCallback != null; |
+ List<PaymentInstrument> instruments = new ArrayList<>(); |
+ instruments.add(instrument); |
+ mInstrumentsCallback.onInstrumentsReady(AndroidPaymentApp.this, instruments); |
+ } |
+ |
+ private void isReadyToPay(String origin, JSONObject data) { |
+ Bundle extras = new Bundle(); |
+ extras.putString(METHOD_NAME_EXTRA, getInstrumentMethodName()); |
+ extras.putString(ORIGIN_EXTRA, origin); |
+ extras.putString(DATA_EXTRA, |
+ data == null ? EMPTY_JSON_DATA : data.toString()); |
+ mIsReadyToPayIntent.putExtras(extras); |
+ |
+ if (mIsReadyToPayService != null) { |
+ sendIsReadyToPay(); |
+ } else { |
+ try { |
+ mWindow.getApplicationContext().bindService( |
+ mIsReadyToPayIntent, mServiceConnection, Context.BIND_AUTO_CREATE); |
+ } catch (SecurityException e) { |
+ sendInstrumentsReady(null); |
+ } |
+ } |
+ } |
+ |
+ private final ServiceConnection mServiceConnection = new ServiceConnection() { |
+ @Override |
+ public void onServiceConnected(ComponentName name, IBinder service) { |
+ mIsReadyToPayService = IsReadyToPayService.Stub.asInterface(service); |
+ if (mIsReadyToPayService == null) { |
+ sendInstrumentsReady(null); |
+ } else { |
+ sendIsReadyToPay(); |
+ } |
+ } |
+ |
+ @Override |
+ public void onServiceDisconnected(ComponentName name) { |
+ sendInstrumentsReady(null); |
+ } |
+ }; |
+ private void sendIsReadyToPay() { |
+ assert mIsReadyToPayService != null; |
+ try { |
+ if (mIsReadyToPayService.isReadyToPay()) { |
+ sendInstrumentsReady(AndroidPaymentApp.this); |
+ return; |
+ } |
+ } catch (RemoteException e) { |
+ } |
+ mInstrumentsCallback.onInstrumentsReady(AndroidPaymentApp.this, null); |
+ } |
+ |
+ @Override |
+ public String getAppIdentifier() { |
+ return getIdentifier(); |
+ } |
+ |
+ @Override |
+ public Set<String> getAppMethodNames() { |
+ return mMethodNames; |
+ } |
+ |
+ @Override |
+ public String getInstrumentMethodName() { |
+ assert !mMethodNames.isEmpty(); |
+ return mMethodNames.iterator().next(); |
+ } |
+ |
+ @Override |
+ public void getInstrumentDetails(String merchantName, String origin, PaymentItem total, |
+ List<PaymentItem> cart, JSONObject data, InstrumentDetailsCallback callback) { |
+ assert !mMethodNames.isEmpty(); |
+ |
+ Bundle extras = new Bundle(); |
+ extras.putString(METHOD_NAME_EXTRA, mMethodNames.iterator().next()); |
+ extras.putString(DATA_EXTRA, data == null ? EMPTY_JSON_DATA : data.toString()); |
+ extras.putString(ORIGIN_EXTRA, origin); |
+ String details = serializeDetails(total, cart); |
+ extras.putString(DETAILS_EXTRA, details == null ? EMPTY_JSON_DATA : details); |
+ mPayIntent.putExtras(extras); |
+ |
+ mInstrumentDetailsCallback = callback; |
+ if (!mWindow.showIntent(mPayIntent, this, R.string.payments_android_app_error)) { |
+ mHandler.post(new Runnable() { |
+ @Override |
+ public void run() { |
+ mInstrumentDetailsCallback.onInstrumentDetailsError(); |
+ } |
+ }); |
+ } |
+ } |
+ |
+ private static String serializeDetails(PaymentItem total, List<PaymentItem> cart) { |
+ StringWriter stringWriter = new StringWriter(); |
+ JsonWriter json = new JsonWriter(stringWriter); |
+ try { |
+ // details {{{ |
+ json.beginObject(); |
+ |
+ // total {{{ |
+ json.name("total"); |
+ serializePaymentItem(json, total); |
+ // }}} total |
+ |
+ // displayitems {{{ |
+ if (cart != null) { |
+ json.name("displayItems").beginArray(); |
+ for (int i = 0; i < cart.size(); i++) { |
+ serializePaymentItem(json, cart.get(i)); |
+ } |
+ json.endArray(); |
+ } |
+ // }}} displayItems |
+ |
+ json.endObject(); |
+ // }}} details |
+ } catch (IOException e) { |
+ return null; |
+ } |
+ |
+ return stringWriter.toString(); |
+ } |
+ |
+ private static void serializePaymentItem(JsonWriter json, PaymentItem item) throws IOException { |
+ // item {{{ |
+ json.beginObject(); |
+ json.name("label").value(item.label); |
+ |
+ // amount {{{ |
+ json.name("amount").beginObject(); |
+ json.name("currency").value(item.amount.currency); |
+ json.name("value").value(item.amount.value); |
+ json.endObject(); |
+ // }}} amount |
+ |
+ json.endObject(); |
+ // }}} item |
+ } |
+ |
+ @Override |
+ public void onIntentCompleted( |
+ WindowAndroid window, int resultCode, ContentResolver contentResolver, Intent data) { |
+ mWindow.removeIntentCallback(this); |
+ if (data == null || data.getExtras() == null || resultCode != Activity.RESULT_OK) { |
+ mInstrumentDetailsCallback.onInstrumentDetailsError(); |
+ } else { |
+ mInstrumentDetailsCallback.onInstrumentDetailsReady( |
+ data.getExtras().getString(METHOD_NAME_EXTRA), |
+ data.getExtras().getString(INSTRUMENT_DETAILS_EXTRA)); |
+ } |
+ mInstrumentDetailsCallback = null; |
+ } |
+ |
+ @Override |
+ public void dismissInstrument() {} |
+} |