Use customer-facing platform

United States
Canada
Europe
Latin America

Prerequisite

This tutorial assumes you're using clover-cfp-sdk version 4.1.0 or higher.

Customer Facing Platform (CFP)

The Customer Facing Platform (CFP) provides the capability for custom activities to be launched and controlled on the customer facing device in a tethered configuration, including Clover Station Pro or a Clover Station tethered to a Clover Mini. The clover-cfp-sdk provides support for custom activities, as well as launching and communicating with custom activities.

Clover CFP SDK

The clover-cfp-sdk library contains classes that support building a custom activity and controlling a custom activity remotely. For custom activities, clover-cfp-sdk provides a base implementation of an activity to simplify custom activity development. You can extend it to facilitate setting result values, as well as sending and receiving string messages between the POS and Custom Activity. For controlling the custom activity, clover-cfp-sdk provides the RemoteDeviceConnector class.

The SDK is divided into two packages for handling separate responsibilities:

  • com.clover.cfp.activity - Customer-facing device classes
  • com.clover.cfp.connector - Merchant-facing device classes

The SDK is available from MVN Repository or The Central Repository.

Custom activities (customer-facing device) classes

The com.clover.cfp.activity.helper - CloverCFPActivityHelper, as well as CloverCFPCommsHelper provide utilities for customer-facing activities. They provide methods for handling messages sent from the POS, as well as some convenience behavior like setting the result and finishing and standard 4-finger exit behavior. The deprecated class of com.clover.cfp.activity CloverCFPActivity is a convenience base class leveraging the helper classes, but requires extending it so is no longer the preferred option.

public class CloverCFPActivityHelper {
  public String getInitialPayload() // payload used to start custom activity
  public void setResultAndFinish(int result, String data); // used to set the result code and payload returned to the RemoteDeviceConnector
}
public class CloverCFPCommsHelper {
  public void sendMessage(String payload) throws Exception; // used to send a payload back to the RemoteDeviceConnector
  public void onMessage(String payload); // override to receive payload messages from the RemoteDeviceConnector

}

Example Activity using CloverCFPActivityHelper

public class CustomerActivity extends Activity {
  private CloverCFPActivityHelper activityHelper;
  private CloverCFPCommsHelper commsHelper;

  @Override protected void onCreate(Bundle savedInstance) {
    super.onCreate(savedInstance);
    setContentView(R.layout.activity_customer);

    activityHelper = new CloverCFPActivityHelper(this);
    commsHelper = new CloverCFPCommsHelper(this, getIntent(), new MessageListener(){
        public void onMessage(String payload) {
            // do something with a message from the RemoteDeviceConnector
    
            // RemoteDeviceConnector is requesting the 
            // CustomActivity finishes itself
            if ("FINISH".equals(message)) {
                setResultAndFinish(RESULT_OK, "{\"action\":\"FINISHING\"}");
            }
        }
    });

    String initialPayload = activityHelper.getInitialPayload();
    // do something with the payload
  }
  
  public void clickYes(View view) {
    try {
      sendMessage("{\"action\":\"YES\"}");
    } catch(Exception e) {
      Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT);
    }
  }
  public void clickNo(View view) {
    try {
      sendMessage("{\"action\":\"NO\"}");
    } catch(Exception e) {
      Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT);
    }
  }
  public void clickDone(View view) {
    activityHelper.setResultAndFinish(RESULT_OK, "{\"action\":\"DONE\"}");
  }
}

AndroidManifest.xml definition for a custom activity

<activity android:name=".CustomerActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
    <intent-filter>
        <action android:name="com.yourdomain.ACTIVITY_NAME"/>
        <category android:name="com.clover.cfp.ACTIVITY"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

Remote Device Connector (merchant-facing device) classes - com.clover.cfp.connector

RemoteDeviceConnector is the main class used to interact with the customer-facing device, including starting and communicating with a custom activity. In addition to starting a custom activity and sending a message to the activity, the connector also provides the ability to query the status of the device and reset the state of the device if needed.

📘

NOTES

  1. The Remote Device Connector (merchant-facing device) classes - com.clover.cfp.connector is not applicable to REST Pay Display integrations.
  2. Applications can only start custom activities when the device is in an idle state (for example, at the Welcome, Display Order, or Thank You screen).

Starting custom activities and getting activity results

An initial string payload can be passed in during construction or returned through an activity result. The initial payload can be passed in through the CustomActivityRequest and retrieved in the custom activity using the SDK's CFPConstants.EXTRA_PAYLOAD extra. The same key can be used to return a value in the result, or use the CloverCFPActivity.setResultAndFinish(int result, string payload) call to finish the CustomActivity.

📘

NOTE

All methods except disconnect() must be called off the main/UI thread.

Simple POS activity starting a custom activity on the customer-facing device

public class POSActivity extends Activity {
  private static final String CUSTOM_ACTIVITY_NAME = "com.yourdomain.ACTIVITY_NAME";
  private Executor executor = Executors.newFixedThreadPool(3);
  private RemoteDeviceConnector remoteDeviceConnector;
  
  @Override protected void onCreate(Bundle savedInstance) {
    remoteDeviceConnector = new RemoteDeviceConnector(this, CloverAccount.getAccount(this));
  }
  
  @Override protected void onResume() {
    super.onResume();
    final String payload = "{\"msg\"=\"Initial...\"}"; // if you need an inital payload
    final CustomActivityRequest car = new CustomActivityRequest(CUSTOM_ACTIVITY_NAME, payload);
    executor.execute(()->{
      remoteDeviceConnector.resetDevice(new ResetDeviceRequest());
      remoteDeviceConnector.startCustomActivity(car, new CustomActivityListener(){
        @Override public void onMessageFromActivity(MessageFromActivity message) {
          // handle message from the CustomActivity
        }
        
        @Override public void onCustomActivityResult(CustomActivityResponse response) {
          // handle the result of the CustomActivity
        }
      });
    });
  }
  
  @Override protected void onPause() {
    super.onPause();
    executor.execute(()->{
      remoteDeviceConnector.sendMessageToActivity(new MessageToActivity(CUSTOM_ACTIVITY_NAME, "FINISH");
      remoteDeviceConnector.disconnect();        
    });
  }
}

Helper classes

Starting with version 4.1, helper classes were added to the sdk to remove the need to subclass CloverCFPActivity. There are 2 helper classes:
CloverCFPActivityHelper—Provides 4-finger exit support, simplifies getting the initial payload, and simplifies setting the result in the proper format and finishing the activity.
CloverCFPCommsHelper—Provides methods to enable communicating between the point of sale (POS) and Custom Activity

Note: Use of helper classes is not required to start a custom activity.

public class MyCFPActivity extends Activity implements CloverCFPCommsHelper.MessageListener {
   CloverCFPActivityHelper activityHelper;
   CloverCFPCommsHelper commsHelper;
   
   public void onCreate(Bundle savedInstance) {
     super.onCreate(savedInstance);
     setContentView(R.layout.activity_mycfp);
    
     activityHelper = new CloverCFPActivityHelper(this);
     commsHelper = new CloverCFPCommsHelper(this, getIntent(), this);
   }
   
   public void onDestroy() {
     activityHelper.dispose();
     commsHelper.dispose();
     super.onDestroy();
   }
  
  public void onMessage(String payload) {
    // called when remoteDeviceConnector.sendMessageToActivity(...)
    // is invoked from POS
    if("FINISH".equals(payload)) {
      finishWithPayloadToPOS("");
    }
  }
  
  public void doSendMessageToPOS() {
    try {
      String payload = "some message";
      commsHelper.sendMessage(payload); // send a message to the RemoteDeviceConnector instance
    } catch(Exception e) {
      // log the excption, update ui, etc.
    }
  }
  
  public void finishWithPayloadToPOS(String resultPayload) {
    activityHelper.setResultAndFinish(RESULT_OK, resultPayload); 
  }
}