Android
This document contains the Programmable Wallets SDK reference for Android development.
The User Wallet provides APP SDK in iOS and Android for the customer to integrate. The APP SDK secures the process when users input their secret data, whether as PIN or security answer, and the SDK app encrypts the request body by the encryption key.
Due to the security requirement for Circle to control the end-to-end process, the SDK also exposes functionality for the customer to customize the description and layout:
- Customize predefined error messages.
- Customize the title and subtitle of the UI.
- Customize the PIN code input layout.
- Set the question list in the UI.
- Initialize the SDK to set the endpoint server.
- Accept the
challengeId
and retrieve any operations.
Tip:
To use the SDK in the most flexible way, combine this SDK reference with the Android SDK UI Customization API article.
SDK Download
Download the Android SDK here.
Android SDK
Requirements
Our Android SDK supports Android API level 21+. Earlier versions are not supported.
Recommend using the latest version of Android Studio.
WalletSDK
public class WalletSdk
extends Object
Public Methods | |
---|---|
void | init(Context context, WalletSdk.Configuration configuration) throws Throwable Configure the Circle endpoint for SDK to connect, throw Throwable if the endpoint and appId’s format are invalid. |
void | setLayoutProvider(LayoutProvider provider) Set extended LayoutProvider for customizing layouts, e.g. error code message, font and color. |
void | setViewSetterProvider(ViewSetterProvider provider) Set extended ViewSetterProvider for customized images. |
void | setSecurityQuestions(SecurityQuestion\[] questions) throws Throwable Set the security question list, throw Throwable when the SecurityQuestion array is empty or contains any question whose title length is not 2~512. |
void | execute(FragmentActivity activity, String userToken, String encryptionKey, String\[] challengeIds, Callback callback) Execute the operations by challengeId and call the callback after sending the request to Circle endpoint. |
void | addEventListener(EventListener listener) Register an EventListener for an app to handle events, e.g. forgot PIN |
void | removeEventListener(EventListener listener) Unregister an EventListener |
void | moveTaskToFront() Bring the SDK UI to the foreground. If the app launches another Activity in onEven() and onError() and makes the SDK UI in the background, use this API to go back to the SDK UI. |
Resource.DateFormat
public enum DateFormat
Enum Constants |
---|
yyyymmddHyphen("YYYY-MM-DD") ddmmyyyySlash("DD/MM/YYYY") mmddyyyySlash("MM/DD/YYYY") |
EventListener
EventListener interface that receives events when an event is triggered.
public interface EventListener
extends Object
Public Methods | |
---|---|
void | onEvent(ExecuteEvent event) Called when the event triggered. |
ExecuteEvent
ExecuteEvent, see WalletSdk.addEventListener(EventListener listener).
public enum ExecuteEvent
Enum Constants |
---|
forgotPin(0) |
WalletSdk.Configuration
SDK Configuration for WalletSdk init.
public class Configuration
extends Object
Constructors | |
---|---|
void | Configuration(String endpoint, String appId) Init with Circle endpoint. SDK will connect to this endpoint. |
LayoutProvider
LayoutProvider helps perform customization during runtime.
public class LayoutProvider
extends Object
Public Methods | |
---|---|
TextConfig | getTextConfig(String key) Define strings with specific configurations for general string customization. Returned TextConfig will replace strings.xml, colors.xml, and styles.xml values. All keys are listed in C Index Table. |
IconTextConfig[] | getIconTextConfigs(Resource.IconTextsKey key) Define icon and string sets with specific configurations for icon text list item customization. All keys are listed in B Index Table. |
TextConfig[] | getTextConfigs(Resource.TextsKey key) Define strings with specific configurations for special text customization. All keys are listed in A Index Table. |
String | getErrorString(ApiError.ErrorCode code) Define the error description. All error codes are listed in ApiError.ErrorCode. |
String | getDateFormat() Get display date format, e.g. the answer of a security question in which inputType is datePicker. Only those 3 strings are valid values: 1. “YYYY-MM-DD”, 2. “DD/MM/YYYY” 3. “MM/DD/YYYY” If it returns an invalid value, the default value would be “YYYY-MM-DD”. All supported formats are listed Resource.DateFormat. |
Boolean | isDebugging() true : default value, check returned value, and print warn level logfalse : skip checking and turn off the log. |
Resource.DateFormat
public interface DateFormat
Static Fields | |
---|---|
String | YYYYMMDD_HYPHEN = “YYYY-MM-DD” DDMMYYYY_SLASH = “DD/MM/YYYY” MMDDYYYY_SLASH = “MM/DD/YYYY” |
Resource.Key
public interface Key
Static Fields | |
---|---|
String | Circlepw_show_pin = “circlepw_show_pin” circlepw_hide_pin = “circlepw_hide_pin” See C Index Table. |
TextConfig
Data class for text customization.
public class TextConfig
extends Object
Fields | |
---|---|
String | text Text to display. |
@CorlorInt int[] | gradientColors Array of Gradient text color. Only used by TextsKey.enterPinCodeHeadline, TextsKey.securityIntroHeadline, TextsKey.newPinCodeHeadline |
@CorlorInt int[] | textColor Text color. |
Typeface | font Font. |
IconTextConfig
Data class for icon text list item customization.
public class IconTextConfig
extends Object
Fields | |
---|---|
IImageViewSetter | setter The ImageView setter for image customization. |
TextConfig | textConfig Text config for text customization. |
Resource.TextsKey
public enum TextsKey
Enum Constants |
---|
securityQuestionHeaders(0) securitySummaryQuestionHeaders(1) enterPinCodeHeadline(2) securityIntroHeadline(3) newPinCodeHeadline(4) securityIntroLink(5) confirmationAgreement(6) See A Index Table. |
Resource.IconTextsKey
public enum IconTextsKey
Enum Constants |
---|
securityConfirmationItems(0) See B Index Table. |
IImageViewSetter
The ImageView setter interface for image customization.
public interface IImageViewSetter
Public Methods | |
---|---|
void | apply(ImageView iv) Called when the ImageView needs to be set. |
LocalImageSetter
The implemented ImageView setter for image customization with local image.
public class LocalImageSetter
implements IImageViewSetter
Fields | |
---|---|
@DrawableRes int | drawableId The resource ID of drawable. |
Public Methods | |
---|---|
void | apply(ImageView iv) Set drawable to the imageView with drawableId. |
RemoteImageSetter
The implemented ImageView setter for image customization with a remote image.
public class RemoteImageSetter
implements IImageViewSetter
Fields | |
---|---|
@DrawableRes int | drawableId The resource ID of drawable. |
String | url Image URL |
Public Methods | |
---|---|
void | apply([ImageView](https://developer.android.com/reference/android/widget/ImageView) iv) Set a remote image from the URL to the ImageView. Use the drawable as the placeholder. |
IToolbarSetter
The Toolbar setter interface for image customization.
public interface IToolbarSetter
Public Methods | |
---|---|
void | apply(Toolbar toolbar) Called when the Toolbar needs to be set. |
RemoteToolbarImageSetter
The implemented Toolbar setter for image customization with a remote image.
public class RemoteToolbarImageSetter
implements IToolbarSetter
Fields | |
---|---|
@DrawableRes int | drawableId The resource ID of drawable. |
String | url Image URL |
Public Methods | |
---|---|
void | apply(Toolbar toolbar) Set a remote image from the URL to the toolbar. Use drawable as the placeholder. |
ViewSetterProvider
ViewSetterProvider supports performing image customization during runtime.
public class ViewSetterProvider
extends Object
Public Methods | |
---|---|
IImageViewSetter | getImageSetter(Resource.Icon icon) Return implemented IImageViewSetter for performing general image customization. e.g. LocalImageSetter, RemoteImageSetter All keys are listed in D Index Table. |
IToolbarSetter | getToolbarImageSetter(Resource.ToolbarIcon type) Return implemented IToolbarSetter for performing Toolbar image customization. e.g. LocalToolbarImageSetter, RemoteToolbarImageSetter All keys are listed in E Index Table. |
Resource.Icon
public enum Icon
Enum Constants |
---|
close(0) back(1) E Index Table. |
SecurityQuestion
Data class for security questions customization.
See WalletSdk.setSecurityQuestions(SecurityQuestion[] questions).
public class SecurityQuestion
extends Object
Fields | |
---|---|
String | title The question string. |
SecurityQuestion.InputType | inputType The input type of the question. Support text input and timePicker. |
SecurityQuestion.InputType
public enum InputType
Enum Constants |
---|
text(0) datePicker(1) |
Callback
A generic callback interface for SDK API calls
public interface Callback<R>
extends Object
boolean | onError(java.lang.Throwable error) The callback is triggered when a failure occurs in operation or is canceled by the user. Return true - The app will handle the following step, SDK will keep the Activity. Return false - The app won’t handle the following step, SDK will finish the Activity. |
void | onResult(R result) R - Type of result such as ExecuteResult Callback when the operation is executed successfully. Finish the Activity after this callback is triggered. |
ExecuteResult
public class ExecuteResult
extends Object
ExecuteResultType | resultType The type of operation that the challenge represents. The possible values are : UNKNOWN(-1) SET_PIN(0) RESTORE_PIN(1) CHANGE_PIN(2) CREATE_WALLET(3) CREATE_TRANSACTION(4) ACCELERATE_TRANSACTION(5) CANCEL_TRANSACTION(6) |
ExecuteResultStatus | status The status of the execution. The possible values are : UNKNOWN(-1) PENDING(0) IN_PROGRESS(1) COMPLETE(2) FAILED(3) EXPIRED(4) |
ExecuteResultType
public enum ExecuteResultType
Enum Constants |
---|
UNKNOWN (-1) SET_PIN(0) RESTORE_PIN(1) CHANGE_PIN(2) CREATE_WALLET(3) CREATE_TRANSACTION(4) ACCELERATE_TRANSACTION(5) CANCEL_TRANSACTION(6) |
ExecuteResultStatus
public enum ExecuteResultStatus
Enum Constants |
---|
UNKNOWN (-1) PENDING(0) IN_PROGRESS(1) COMPLETED(2) FAILED(3) EXPIRED(4) |
APIError
Error class for PW SDK
public abstract class ApiError
extends Throwable
Public Methods | |
---|---|
ApiError.ErrorCode | getCode() Get ErrorCode of the error |
String | getMessage() Get human-readable error message |
ApiError.ErrorCode
public enum ErrorCode
Enum Constants |
---|
unknown(-1), success(0), apiParameterMissing(1), apiParameterInvalid(2), forbidden(3), unauthorized(4), retry(9), customerSuspended(10), pending(11), invalidSession(12), invalidPartnerId(13), invalidMessage(14), invalidPhone(15), walletIdNotFound(156001), tokenIdNotFound(156002), transactionIdNotFound(156003), entityCredentialNotFound(156004), walletSetIdNotFound(156005), userAlreadyExisted(155101), userNotFound(155102), userTokenNotFound(155103), userTokenExpired(155104), invalidUserToken(155105), userWasInitialized(155106), userHasSetPin(155107), userHasSetSecurityQuestion(155108), userWasDisabled(155109), userDoesNotSetPinYet(155110), userDoesNotSetSecurityQuestionYet(155111), incorrectUserPin(155112), incorrectDeviceId(155113), incorrectAppId(155114), incorrectSecurityAnswers(155115), invalidChallengeId(155116), invalidApproveContent(155117), invalidEncryptionKey(155118), userPinLocked(155119), securityAnswersLocked(155120), walletIsFrozen(155501), maxWalletLimitReached(155502), walletSetIdMutuallyExclusive(155503), metadataUnmatched(155504), userCanceled(155701), launchUiFailed(155702), pinCodeNotMatched(155703), insecurePinCode(155704), hintsMatchAnswers(155705); |
Static Customized String
res/values/strings.xml
<resources>
<string name="circlepw_show_pin">Show PIN</string>
<string name="circlepw_hide_pin">Hide PIN</string>
<string name="circlepw_continue">Continue</string>
<string name="circlepw_next">Next</string>
<string name="circlepw_question_label">Question:</string>
</resources>
Static Customized UI Layout
res/values/color.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="circlepw_text_main">#1A1A1A</color>
<color name="circlepw_text_auxiliary">#3D3D3D</color>
<color name="circlepw_text_action">#136FD8</color>
<color name="circlepw_text_action_pressed">#B3136FD8</color>
</resources>
res/values/styles.xml
<resources>
<style name="circlepw_heading3">
<item name="android:fontFamily">@font/avenir_heavy</item>
<item name="android:fontWeight" tools:targetApi="o">600</item>
<item name="android:textSize">24sp</item>
</style>
<style name="circlepw_heading4">
<item name="android:fontFamily">@font/avenir_heavy</item>
<item name="android:fontWeight" tools:targetApi="o">600</item>
<item name="android:textSize">20sp</item>
</style>
</resources>
res/values/dimens.xml
<resources>
<dimen name="circlepw_bottom_bt_margin_top">10dp</dimen>
<dimen name="circlepw_intro_title_margin_bottom">24dp</dimen>
<dimen name="circlepw_intro_custom_grab_icon_size">48dp</dimen>
</resources>
Sample Code
// Set endpoint and app ID
try {
WalletSdk.init(getApplicationContext(), new WalletSdk.Configuration(endpoint, appId));
} catch (Throwable e){
e.printStackTrace();
return;
}
// setViewSetterProvider
WalletSdk.setViewSetterProvider(new MyViewSetterProvider());
// setLayoutProvider
WalletSdk.setLayoutProvider(new MyLayoutProvider(getApplicationContext()));
// Regester EventListener
WalletSdk.addEventListener(new EventListener() {
@Override
public void onEvent(ExecuteEvent event) {
switch (event){
case forgotPin:
startActivity(forgotPinIntent);
break;
}
}
});
// setSecurityQuestion
SecurityQuestion[] questions = new SecurityQuestion[]{
new SecurityQuestion("What is your father’s middle name?"),
new SecurityQuestion("What is your favorite sports team?"),
new SecurityQuestion("What is your mother’s maiden name?"),
new SecurityQuestion("What is the name of your first pet?"),
new SecurityQuestion("What is the name of the city you were born in?"),
new SecurityQuestion("What is the name of the first street you lived on?"),
new SecurityQuestion("When is your father’s birthday?", SecurityQuestion.InputType.datePicker)};
WalletSdk.setSecurityQuestions(questions);
// Execute
WalletSdk.execute(getActivity(), userToken, encryptionKey,
new String[]{challengeId},
new Callback<ExecuteResult>() {
@Override
public boolean onError(Throwable error) {
if(error instanceof ApiError){
ApiError apiError = (ApiError) error;
if(apiError.getCode() == ApiError.ErrorCode.userCanceled){
// App won't handle next step, SDK finishes the Activity.
return false;
}
startActivity(errorUiIntent);
// App will handle next step, SDK keeps the Activity.
return true;
}
}
@Override
public void onResult(ExecuteResult result) {
// success
}
});
// Extend LayoutProvider and override
class MyLayoutProvider extends LayoutProvider {
Context context;
Typeface typeface;
int color;
public MyLayoutProvider(Context context) {
this.context = context;
}
@Override
public TextConfig getTextConfig(String key) {
switch (key){
case Resource.Key.circlepw_security_intros_description:
typeface = ResourcesCompat.getFont(context, R.font.en);
color = Color.parseColor("#105AAB");
return new TextConfig("customized description", color, typeface);
}
return super.getTextConfig(key);
}
@Override
public IconTextConfig[] getIconTextConfigs(Resource.IconTextsKey key) {
switch (key){
case securityConfirmationItems: // SQ confirmation introduction items
return new IconTextConfig[]{
new IconTextConfig(
new LocalImageSetter(R.drawable.intro_item0_icon),
new TextConfig("This is the only way to recover my account access. ")),
new IconTextConfig(
new RemoteImageSetter(R.drawable.error, "https://path/icon2.svg"),
new TextConfig("Circle won’t store my answers so it’s my responsibility to remember them.")),
new IconTextConfig(
new RemoteImageSetter(R.drawable.error, "https://path/icon3.svg"),
new TextConfig("I will lose access to my wallet and my digital assets if I forget my answers. "))
};
}
return super.getIconTextConfigs(key);
}
@Override
public TextConfig[] getTextConfigs(Resource.TextsKey key) {
switch (key){
case securityQuestionHeaders:
return new TextConfig[]{
new TextConfig("Choose your 1st question"),
new TextConfig("Choose your 2nd question")
};
case securitySummaryQuestionHeaders:
return new TextConfig[]{
new TextConfig("1st Question"),
new TextConfig("2nd Question")
};
case newPinCodeHeadline:
case enterPinCodeHeadline:
return new TextConfig[]{
new TextConfig("Enter your "),
new TextConfig("PIN", getHeadingColor(), null),
};
case securityIntroHeadline:
return new TextConfig[]{
new TextConfig("Set up your "),
new TextConfig("Recovery Method", getHeadingColor(), null),
};
}
return super.getTextConfigs(key);
}
private int getHeadingColor(){
return Color.parseColor("#0073C3");
}
@Override
public String getErrorString(ApiError.ErrorCode code) {
switch (code){
case userCanceled:
return context.getString(R.string.user_canceled);
}
return super.getErrorString(code);
}
@Override
public String getDateFormat(){
return Resource.DateFormat.MMDDYYYY_SLASH; // "MM/DD/YYYY"
}
@Override
public boolean isDebugging(){
return BuildConfig.DEBUG;
}
}
// Extend ResourceProvider and override
class MyViewSetterProvider extends ViewSetterProvider{
@Override
public IToolbarSetter getToolbarImageSetter(Resource.ToolbarIcon type) {
switch (type){
case back:
return new LocalToolbarImageSetter(R.drawable.ic_back);
case close:
return new RemoteToolbarImageSetter(R.drawable.error, "https://path/close.svg");
}
return super.getToolbarImageSetter(type);
}
@Override
public IImageViewSetter getImageSetter(Resource.Icon type) {
switch (type){
case securityIntroMain:
return new LocalImageSetter(R.drawable.main);
case selectCheckMark:
return new RemoteImageSetter(R.drawable.error, "https://path/check.svg");
case dropdownArrow:
return new RemoteImageSetter(R.drawable.error, "https://path/arrow.svg");
case errorInfo:
return new RemoteImageSetter(R.drawable.error, "https://path/errorinfo.svg");
case securityConfirmMain:
return new RemoteImageSetter(R.drawable.error, "https://path/main2.svg");
}
return super.getImageSetter(type);
}
}
Sample Code
// Set endpoint and app ID
try {
WalletSdk.init(getApplicationContext(), new WalletSdk.Configuration(endpoint, appId));
} catch (Throwable e){
e.printStackTrace();
return;
}
WalletSdk.init(getApplicationContext(), new WalletSdk.Configuration(endpoint, appId));
// setViewSetterProvider
WalletSdk.setViewSetterProvider(new MyViewSetterProvider());
// setLayoutProvider
WalletSdk.setResourceProvider(new MyLayoutProvider(getApplicationContext()));
// Regester EventListener
WalletSdk.addEventListener(new EventListener() {
@Override
public void onEvent(ExecuteEvent event) {
switch (event){
case forgotPin:
startActivity(forgotPinIntent);
break;
}
}
});
// setSecurityQuestion
SecurityQuestion[] questions = new SecurityQuestion[]{
new SecurityQuestion("What was your childhood nickname?"),
new SecurityQuestion("What is the name of your favorite childhood friend?"),
new SecurityQuestion("In what city or town did your mother and father meet?"),
new SecurityQuestion("What is the middle name of your oldest child?"),
new SecurityQuestion("When is your birthday?", SecurityQuestion.InputType.datePicker)};
WalletSdk.setSecurityQuestions(questions);
// Execute
WalletSdk.execute(getActivity(), userToken, encryptionKey,
new String[]{challengeId},
new Callback<ExecuteResult>() {
@Override
public boolean onError(Throwable error) {
if(error instanceof ApiError){
ApiError apiError = (ApiError) error;
if(apiError.getCode() == ApiError.ErrorCode.userCanceled){
// App won't handle next step, SDK finishes the Activity.
return false;
}
startActivity(errorUiIntent);
// App will handle next step, SDK keeps the Activity.
return true;
}
}
@Override
public void onResult(ExecuteResult result) {
// success
}
});
// Extend LayoutProvider and override
class MyLayoutProvider extends LayoutProvider {
Context context;
Typeface typeface;
int color;
public MyLayoutProvider(Context context) {
this.context = context;
}
@Override
public TextConfig getTextConfig(String key) {
switch (key){
case Resource.Key.circlepw_security_intros_description:
typeface = ResourcesCompat.getFont(context, R.font.en);
color = Color.parseColor("#105AAB");
return new TextConfig("customized description", color, typeface);
}
return super.getTextConfig(key);
}
@Override
public IconTextConfig[] getIconTextConfigs(Resource.IconTextsKey key) {
switch (key){
case securityConfirmationItems: // SQ confirmation introduction items
return new IconTextConfig[]{
new IconTextConfig(
new LocalImageSetter(R.drawable.intro_item0_icon),
new TextConfig("This is the only way to recover my account access. ")),
new IconTextConfig(
new RemoteImageSetter(R.drawable.error, "https://path/icon2.svg"),
new TextConfig("Circle won’t store my answers so it’s my responsibility to remember them.")),
new IconTextConfig(
new RemoteImageSetter(R.drawable.error, "https://path/icon3.svg"),
new TextConfig("I will lose access to my wallet and my digital assets if I forget my answers. "))
};
}
return super.getIconTextConfigs(key);
}
@Override
public TextConfig[] getTextConfigs(Resource.TextsKey key) {
switch (key){
case securityQuestionHeaders:
return new TextConfig[]{
new TextConfig("Choose your 1st question"),
new TextConfig("Choose your 2nd question"),
new TextConfig("Choose your 3rd question")
};
case securitySummaryQuestionHeaders:
return new TextConfig[]{
new TextConfig("1st Question"),
new TextConfig("2nd Question"),
new TextConfig("3rd Question")
};
case enterPinCodeHeadline:
return new TextConfig[]{
new TextConfig("Enter your "),
new TextConfig("Web3 PIN", getGradientColors(), getGradientTypeface()),
};
case securityIntroHeadline:
return new TextConfig[]{
new TextConfig("Set up your "),
new TextConfig("Recovery Method", getGradientColors(), getGradientTypeface()),
};
case newPinCodeHeadline:
return new TextConfig[]{
new TextConfig("Enter your new "),
new TextConfig("Web3 PIN", getGradientColors(), getGradientTypeface()),
};
}
return super.getTextConfigs(key);
}
private int[] getGradientColors(){
return new int[]{
Color.parseColor("#00B14F"),
Color.parseColor("#23B64B"),
Color.parseColor("#35BA47"),
Color.parseColor("#43BE43"),
Color.parseColor("#4FC23F"),
Color.parseColor("#59C53C"),
Color.parseColor("#62C838"),
Color.parseColor("#6ACA34"),
Color.parseColor("#71CD31"),
Color.parseColor("#77CF2D"),
Color.parseColor("#7DD02A"),
Color.parseColor("#82D228"),
Color.parseColor("#85D325"),
Color.parseColor("#88D424"),
Color.parseColor("#8AD522"),
Color.parseColor("#8CD521"),
Color.parseColor("#8CD521"),
};
}
private Typeface getGradientTypeface(){
return ResourcesCompat.getFont(context, R.font.en);
}
@Override
public String getErrorString(ApiError.ErrorCode code) {
switch (code){
case userCanceled:
return context.getString(R.string.user_canceled);
}
return super.getErrorString(code);
}
@Override
public String getDateFormat(){
return Resource.DateFormat.MMDDYYYY_SLASH; // "MM/DD/YYYY"
}
@Override
public boolean isDebugging(){
return BuildConfig.DEBUG;
}
}
// Extend ResourceProvider and override
class MyViewSetterProvider extends ViewSetterProvider{
@Override
public IToolbarSetter getToolbarImageSetter(Resource.ToolbarIcon type) {
switch (type){
case back:
return new LocalToolbarImageSetter(R.drawable.ic_back);
case close:
return new RemoteToolbarImageSetter(R.drawable.error, "https://path/close.svg");
}
return super.getToolbarImageSetter(type);
}
@Override
public IImageViewSetter getImageSetter(Resource.Icon type) {
switch (type){
case securityIntroMain:
return new LocalImageSetter(R.drawable.main);
case selectCheckMark:
return new RemoteImageSetter(R.drawable.error, "https://path/check.svg");
case dropdownArrow:
return new RemoteImageSetter(R.drawable.error, "https://path/arrow.svg");
case errorInfo:
return new RemoteImageSetter(R.drawable.error, "https://path/errorinfo.svg");
case securityConfirmMain:
return new RemoteImageSetter(R.drawable.error, "https://path/main2.svg");
}
return super.getImageSetter(type);
}
}
Updated 3 days ago