The document discusses best practices for implementing native payment functionality in Codename One applications. It covers:
1) Using the native IDE to develop code and copy it back to the native directory to streamline the process.
2) Running UI code on the main thread to avoid errors, as Android and iOS are sensitive to violations of the event dispatch thread.
3) Retrieving activity instances and using callbacks appropriately to integrate with the Codename One API from native code.
4) Being aware of threads and returning to the Codename One EDT from callbacks to avoid issues.
3. final Activity act = AndroidNativeUtil.getActivity();
act.runOnUiThread(new Runnable() {
public void run() {
DropInRequest dropInRequest = new DropInRequest()
.clientToken(param);
AndroidNativeUtil.startActivityForResult(dropInRequest.getIntent(act),
new IntentResultListener() {
public void onActivityResult (int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
DropInResult result = data.getParcelableExtra(DropInResult.EXTRA_DROP_IN_RESULT);
BraintreePaymentCallback.onPurchaseSuccess(
result.getPaymentMethodNonce().getNonce());
} else if (resultCode == Activity.RESULT_CANCELED) {
BraintreePaymentCallback.onPurchaseCancel();
} else {
Exception error = (Exception)data.getSerializableExtra(DropInActivity.EXTRA_ERROR);
BraintreePaymentCallback.onPurchaseFail(error.toString());
Log.e(error);
Log.sendLog();
}
}
});
}
});
Android
4. final Activity act = AndroidNativeUtil.getActivity();
act.runOnUiThread(new Runnable() {
public void run() {
DropInRequest dropInRequest = new DropInRequest()
.clientToken(param);
AndroidNativeUtil.startActivityForResult(dropInRequest.getIntent(act),
new IntentResultListener() {
public void onActivityResult (int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
DropInResult result = data.getParcelableExtra(DropInResult.EXTRA_DROP_IN_RESULT);
BraintreePaymentCallback.onPurchaseSuccess(
result.getPaymentMethodNonce().getNonce());
} else if (resultCode == Activity.RESULT_CANCELED) {
BraintreePaymentCallback.onPurchaseCancel();
} else {
Exception error = (Exception)data.getSerializableExtra(DropInActivity.EXTRA_ERROR);
BraintreePaymentCallback.onPurchaseFail(error.toString());
Log.e(error);
Log.sendLog();
}
}
});
}
});
Android
Activity
Almost all Android Sample code
assumes this==activity. In a native
interface that just isn’t true which is
why in most cases where this is
referenced you can just do getActivity
using AndroidNative Util
5. final Activity act = AndroidNativeUtil.getActivity();
act.runOnUiThread(new Runnable() {
public void run() {
DropInRequest dropInRequest = new DropInRequest()
.clientToken(param);
AndroidNativeUtil.startActivityForResult(dropInRequest.getIntent(act),
new IntentResultListener() {
public void onActivityResult (int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
DropInResult result = data.getParcelableExtra(DropInResult.EXTRA_DROP_IN_RESULT);
BraintreePaymentCallback.onPurchaseSuccess(
result.getPaymentMethodNonce().getNonce());
} else if (resultCode == Activity.RESULT_CANCELED) {
BraintreePaymentCallback.onPurchaseCancel();
} else {
Exception error = (Exception)data.getSerializableExtra(DropInActivity.EXTRA_ERROR);
BraintreePaymentCallback.onPurchaseFail(error.toString());
Log.e(error);
Log.sendLog();
}
}
});
}
});
Android
UI Thread
Most Android sample code assumes
you are on the Android equivalent of the
EDT (UI thread). We are on the
Codename One EDT or a Codename
One thread in a native interface so it’s
best to do something like this unless
you need a synchronous call
6. final Activity act = AndroidNativeUtil.getActivity();
act.runOnUiThread(new Runnable() {
public void run() {
DropInRequest dropInRequest = new DropInRequest()
.clientToken(param);
AndroidNativeUtil.startActivityForResult(dropInRequest.getIntent(act),
new IntentResultListener() {
public void onActivityResult (int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
DropInResult result = data.getParcelableExtra(DropInResult.EXTRA_DROP_IN_RESULT);
BraintreePaymentCallback.onPurchaseSuccess(
result.getPaymentMethodNonce().getNonce());
} else if (resultCode == Activity.RESULT_CANCELED) {
BraintreePaymentCallback.onPurchaseCancel();
} else {
Exception error = (Exception)data.getSerializableExtra(DropInActivity.EXTRA_ERROR);
BraintreePaymentCallback.onPurchaseFail(error.toString());
Log.e(error);
Log.sendLog();
}
}
});
}
});
Android
startActivityForResult
This is a very common method of
activity but grabbing the result isn’t
intuitive which is why we have this
method
7. final Activity act = AndroidNativeUtil.getActivity();
act.runOnUiThread(new Runnable() {
public void run() {
DropInRequest dropInRequest = new DropInRequest()
.clientToken(param);
AndroidNativeUtil.startActivityForResult(dropInRequest.getIntent(act),
new IntentResultListener() {
public void onActivityResult (int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
DropInResult result = data.getParcelableExtra(DropInResult.EXTRA_DROP_IN_RESULT);
BraintreePaymentCallback.onPurchaseSuccess(
result.getPaymentMethodNonce().getNonce());
} else if (resultCode == Activity.RESULT_CANCELED) {
BraintreePaymentCallback.onPurchaseCancel();
} else {
Exception error = (Exception)data.getSerializableExtra(DropInActivity.EXTRA_ERROR);
BraintreePaymentCallback.onPurchaseFail(error.toString());
Log.e(error);
Log.sendLog();
}
}
});
}
});
Android
Callbacks & API
On Android we can access the
Codename One API directly since it’s all
just Java…
9. dispatch_async(dispatch_get_main_queue(), ^{
BTDropInRequest *request = [[BTDropInRequest alloc] init];
BTDropInController *dropIn = [[BTDropInController alloc] initWithAuthorization:param request:request
handler:^(BTDropInController * _Nonnull controller, BTDropInResult * _Nullable result, NSError *
_Nullable error) {
if (error != nil) {
com_myrestaurant_app_payment_BraintreePaymentCallback_onPurchaseFail___java_lang_String(CN1_THREAD_GET_ST
ATE_PASS_ARG fromNSString(CN1_THREAD_GET_STATE_PASS_ARG [error localizedDescription]));
} else if (result.cancelled) {
com_myrestaurant_app_payment_BraintreePaymentCallback_onPurchaseCancel__(CN1_THREAD_GET_STATE_PASS_SINGLE
_ARG);
} else {
com_myrestaurant_app_payment_BraintreePaymentCallback_onPurchaseSuccess___java_lang_String(CN1_THREAD_GET
_STATE_PASS_ARG fromNSString(CN1_THREAD_GET_STATE_PASS_ARG result.paymentMethod.nonce));
}
[controller dismissViewControllerAnimated:YES completion:nil];
}];
[[CodenameOne_GLViewController instance] presentViewController:dropIn animated:YES
completion:nil];
});
iOS
iOS EDT
iOS is very sensitive to EDT violations.
It’s really important to use it’s main
queue (native EDT) for everything
related to UI
10. dispatch_async(dispatch_get_main_queue(), ^{
BTDropInRequest *request = [[BTDropInRequest alloc] init];
BTDropInController *dropIn = [[BTDropInController alloc] initWithAuthorization:param request:request
handler:^(BTDropInController * _Nonnull controller, BTDropInResult * _Nullable result, NSError *
_Nullable error) {
if (error != nil) {
com_myrestaurant_app_payment_BraintreePaymentCallback_onPurchaseFail___java_lang_String(CN1_THREAD_GET_ST
ATE_PASS_ARG fromNSString(CN1_THREAD_GET_STATE_PASS_ARG [error localizedDescription]));
} else if (result.cancelled) {
com_myrestaurant_app_payment_BraintreePaymentCallback_onPurchaseCancel__(CN1_THREAD_GET_STATE_PASS_SINGLE
_ARG);
} else {
com_myrestaurant_app_payment_BraintreePaymentCallback_onPurchaseSuccess___java_lang_String(CN1_THREAD_GET
_STATE_PASS_ARG fromNSString(CN1_THREAD_GET_STATE_PASS_ARG result.paymentMethod.nonce));
}
[controller dismissViewControllerAnimated:YES completion:nil];
}];
[[CodenameOne_GLViewController instance] presentViewController:dropIn animated:YES
completion:nil];
});
iOS
Self
Self is the Objective-C equivalent of
this. In the original code self was used
as it was assumed to be a Controller
(commonly used iOS abstraction) when
you need a controller you can use this
syntax
11. dispatch_async(dispatch_get_main_queue(), ^{
BTDropInRequest *request = [[BTDropInRequest alloc] init];
BTDropInController *dropIn = [[BTDropInController alloc] initWithAuthorization:param request:request
handler:^(BTDropInController * _Nonnull controller, BTDropInResult * _Nullable result, NSError *
_Nullable error) {
if (error != nil) {
com_myrestaurant_app_payment_BraintreePaymentCallback_onPurchaseFail___java_lang_String(CN1_THREAD_GET_ST
ATE_PASS_ARG fromNSString(CN1_THREAD_GET_STATE_PASS_ARG [error localizedDescription]));
} else if (result.cancelled) {
com_myrestaurant_app_payment_BraintreePaymentCallback_onPurchaseCancel__(CN1_THREAD_GET_STATE_PASS_SINGLE
_ARG);
} else {
com_myrestaurant_app_payment_BraintreePaymentCallback_onPurchaseSuccess___java_lang_String(CN1_THREAD_GET
_STATE_PASS_ARG fromNSString(CN1_THREAD_GET_STATE_PASS_ARG result.paymentMethod.nonce));
}
[controller dismissViewControllerAnimated:YES completion:nil];
}];
[[CodenameOne_GLViewController instance] presentViewController:dropIn animated:YES
completion:nil];
});
iOS
Callbacks
Callbacks to Java code are complex
and have a slightly different syntax
when calling to a no-args method but
IDE completion should help…
12. What did we learn?
✦Native interfaces aren’t trivial, but are doable when
we overcome the pitfalls and use Google
aggressively
✦Error logs on Android are painful and we need to
develop reading skill to go thru them
✦Use native IDE’s to develop the native code and
shorten the back and forth cycles