Most of the code below relates to GUI messaging, but this Android app is full HCE for a Cloud Based Payments card. It is a total of 201 lines of code including comments, and line spaces. It is a single class application for simplicity.
The goal is to show that the libraries for managing CBP on the mobile app are lightweight for developers to use.
The libs are designed to be placed into ANY application that may want to enable CBP. The one below is about the simplest integration.
The full application is called SimplestTapp and can be downloaded and tested here:
http://wiki.simplytapp.com/home/self-driven-hce-pilot/make-a-payment-card/simplytapp-s-vcbp-changelog/software-dev-kits/mobile-sdk
it also may be good to view this help resource for creating and managing the card perso and lifecycle in the cloud:
http://wiki.simplytapp.com/home/using-admin-api-console
The core of the card download and enablement is really contained in this section. This section instantiates a VirtualCard that will be loaded from the remote server. In order to instantiate the CBP card, you will see that there are some required credential information because a request will be made to the CBP server to retrieve the card that will be used.
The data in the snippet is sample data only.
The AsyncTask is used because the load() method makes network calls and can't be run on the UI thread.
// Create a card with data from AdminApi console // See wiki for help: // http://wiki.simplytapp.com/home/using-admin-api-console // // Values are found within your AdminApi Console. Click Get APP // Credentials. // // card agent code hash is found inside the Issuer Entity portion of the // web portal // final VirtualCard card = VirtualCardBuilder .cardId("5901") .mobileAppConsumerKey( "oJmqoey7I97FBOZQDXoH0A9RbM7BciXbD8CHZDMU") .mobileAppConsumerSecret( "ugTD5dGjBUEC1pyzxjMNtw6k9TUocGtY4plta9he") .walletAccessToken("O15F4jG3PcwC44NpkfEqv1ZD7tjNo5NhGpHHFEax") .walletTokenSecret("Ur504iPQn2lxgsS5fVBycf2zK9uuSglppvn2mBa3") .cardAgentCodeHash("aa4185c2364048447a38aa02499a9897") .context(this).build(); // now load the card... // this requires network connectivity and is blocking // so we must use AsyncTask to avoid running this in UI thread // new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { myActivity.postUi("==================================" + "\n" + "=====Loading Card From Server=====" + "\n" + "==================================" + "\n"); card.load(); } catch (IOException ex) { Log.e(TAG, "Failed to load", ex); } return (Void) null; } }.execute();
// define the message default card message handler // and set it in the lib static { VirtualCardMessaging virtualCardMessenger = new VirtualCardMessaging() { @Override public void virtualCardMessage(VirtualCardMessaging.Message message) { switch (message.getCode()) { case VirtualCardMessaging.CARD_CREATION_COMPLETED: try { // when card is created, then activate the card agent, // then connect it to the ApduService, // then ask the user if they want this app the default // app // for servicing the reader message.getVirtualCard().activate(); ApduService.setVirtualCard(message.getVirtualCard()); myActivity.postUi("========================" + "\n" + "=====Card Created=======" + "\n" + "========================" + "\n" + "==Card Id: " + message.getVirtualCard().getVirtualCardId() + "\n" + "==Num: " + message.getVirtualCard() .getVirtualCardNumber() + "\n" + "==Card Exp: " + message.getVirtualCard() .getVirtualCardExpDate() + "\n" + "==Card Type: " + message.getVirtualCard().getVirtualCardType() + "\n" + "==Card Logo: " + message.getVirtualCard().getVirtualCardLogo() + "\n"); myActivity.postUi("========================" + "\n" + "=====Card Activating====" + "\n" + "========================" + "\n"); } catch (IOException e) { e.printStackTrace(); } break; case VirtualCardMessaging.CARD_ACTIVATE_COMPLETED: myActivity.postUi("========================" + "\n" + "========================" + "\n" + "========================" + "\n" + "=====Card Activated=====" + "\n" + "=====Ready To Tap=======" + "\n" + "========================" + "\n" + "========================" + "\n" + "========================" + "\n"); break; case VirtualCardMessaging.TRANSACTION_ENDED: myActivity.postUi("========================" + "\n" + "========================" + "\n" + "========================" + "\n" + "===Ready To Tap Again====" + "\n" + "========================" + "\n" + "========================" + "\n" + "========================" + "\n"); break; default: break; } // log the message Log.d(TAG, message.getMessage() + " cardId=" + message.getVirtualCardId() + " code=" + message.getCode()); myActivity.postUi(message.getMessage() + " cardId=" + message.getVirtualCardId() + " code=" + message.getCode() + "\n"); } }; // set the default messenger VirtualCard.setDefaultVirtualCardMessaging(virtualCardMessenger); }
You will notice above that the messaging loop is logging all messages from the card and printing them to the screen of the app and the debugger. This can give you an idea of the events the card notifies the app. As far as API requests to the card from the app, the VirtualCard library contains many methods. Here is the JavaDoc:
http://simplytapp.github.io/android/virtualcard/doc/
Full activity class code for this example:
package com.simplytapp.example.simplesttapp; import java.io.IOException; import com.simplytapp.virtualcard.ApduService; import com.simplytapp.virtualcard.VirtualCard; import com.simplytapp.virtualcard.VirtualCardBuilder; import com.simplytapp.virtualcard.VirtualCardMessaging; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.nfc.NfcAdapter; import android.nfc.cardemulation.CardEmulation; import android.os.AsyncTask; import android.os.Bundle; import android.text.method.ScrollingMovementMethod; import android.util.Log; import android.widget.TextView; public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleName(); private static MainActivity myActivity = null; private TextView tv = null; // this method posts strings to the textview box in the UI thread private void postUi(final String t) { new Runnable() { public void run() { runOnUiThread(new Runnable() { public void run() { tv.append(t); if (tv.getLayout() != null) { // auto-scroll vertically final int scrollAmount = tv.getLayout().getLineTop( tv.getLineCount()) - tv.getHeight(); // if there is no need to scroll, scrollAmount will // be <=0 if (scrollAmount > 0) tv.scrollTo(0, scrollAmount); else tv.scrollTo(0, 0); } } }); } }.run(); } // define the message default card message handler // and set it in the lib static { VirtualCardMessaging virtualCardMessenger = new VirtualCardMessaging() { @Override public void virtualCardMessage(VirtualCardMessaging.Message message) { switch (message.getCode()) { case VirtualCardMessaging.CARD_CREATION_COMPLETED: try { // when card is created, then activate the card agent, // then connect it to the ApduService, // then ask the user if they want this app the default // app // for servicing the reader message.getVirtualCard().activate(); ApduService.setVirtualCard(message.getVirtualCard()); myActivity.postUi("========================" + "\n" + "=====Card Created=======" + "\n" + "========================" + "\n" + "==Card Id: " + message.getVirtualCard().getVirtualCardId() + "\n" + "==Num: " + message.getVirtualCard() .getVirtualCardNumber() + "\n" + "==Card Exp: " + message.getVirtualCard() .getVirtualCardExpDate() + "\n" + "==Card Type: " + message.getVirtualCard().getVirtualCardType() + "\n" + "==Card Logo: " + message.getVirtualCard().getVirtualCardLogo() + "\n"); myActivity.postUi("========================" + "\n" + "=====Card Activating====" + "\n" + "========================" + "\n"); } catch (IOException e) { e.printStackTrace(); } break; case VirtualCardMessaging.CARD_ACTIVATE_COMPLETED: myActivity.postUi("========================" + "\n" + "========================" + "\n" + "========================" + "\n" + "=====Card Activated=====" + "\n" + "=====Ready To Tap=======" + "\n" + "========================" + "\n" + "========================" + "\n" + "========================" + "\n"); break; case VirtualCardMessaging.TRANSACTION_ENDED: myActivity.postUi("========================" + "\n" + "========================" + "\n" + "========================" + "\n" + "===Ready To Tap Again====" + "\n" + "========================" + "\n" + "========================" + "\n" + "========================" + "\n"); break; default: break; } // log the message Log.d(TAG, message.getMessage() + " cardId=" + message.getVirtualCardId() + " code=" + message.getCode()); myActivity.postUi(message.getMessage() + " cardId=" + message.getVirtualCardId() + " code=" + message.getCode() + "\n"); } }; // set the default messenger VirtualCard.setDefaultVirtualCardMessaging(virtualCardMessenger); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myActivity = this; tv = (TextView) findViewById(R.id.textView1); tv.setMovementMethod(new ScrollingMovementMethod()); // try to set this app as the default HCE application app CardEmulation cardEmulationManager = CardEmulation .getInstance(NfcAdapter.getDefaultAdapter(this)); ComponentName paymentServiceComponent = new ComponentName( getApplicationContext(), ApduService.class.getCanonicalName()); if (!cardEmulationManager.isDefaultServiceForCategory( paymentServiceComponent, CardEmulation.CATEGORY_PAYMENT)) { Intent intent = new Intent(CardEmulation.ACTION_CHANGE_DEFAULT); intent.putExtra(CardEmulation.EXTRA_CATEGORY, CardEmulation.CATEGORY_PAYMENT); intent.putExtra(CardEmulation.EXTRA_SERVICE_COMPONENT, paymentServiceComponent); startActivityForResult(intent, 0); } // Create a card with data from AdminApi console // See wiki for help: // http://wiki.simplytapp.com/home/using-admin-api-console // // Values are found within your AdminApi Console. Click Get APP // Credentials. // // card agent code hash is found inside the Issuer Entity portion of the // web portal // final VirtualCard card = VirtualCardBuilder .cardId("5901") .mobileAppConsumerKey( "oJmqoey7I97FBOZQDXoH0A9RbM7BciXbD8CHZDMU") .mobileAppConsumerSecret( "ugTD5dGjBUEC1pyzxjMNtw6k9TUocGtY4plta9he") .walletAccessToken("O15F4jG3PcwC44NpkfEqv1ZD7tjNo5NhGpHHFEax") .walletTokenSecret("Ur504iPQn2lxgsS5fVBycf2zK9uuSglppvn2mBa3") .cardAgentCodeHash("aa4185c2364048447a38aa02499a9897") .context(this).build(); // now load the card... // this requires network connectivity and is blocking // so we must use AsyncTask to avoid running this in UI thread // new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { myActivity.postUi("==================================" + "\n" + "=====Loading Card From Server=====" + "\n" + "==================================" + "\n"); card.load(); } catch (IOException ex) { Log.e(TAG, "Failed to load", ex); } return (Void) null; } }.execute(); } }