From fee133cc78cb79e5d2e055e40fd4d184535ad969 Mon Sep 17 00:00:00 2001 From: pragunvohra Date: Thu, 17 Mar 2016 18:52:54 -0400 Subject: [PATCH] Fix callback concurrency issues; ensure more methods will resolve callbacks; do a best-case handling for userCompletedAction; update readme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. No more class-level callbackContext; individual callbackContexts are passed between methods to ensure we don’t clobber the callbackContext with two concurrent calls to execute() 2. logout, registerView, and userCompletedAction were missing callback resolutions, so I added those. userCompletedAction’s logic is not completely correct as it will always (early) resolve to “success” but Branch.java in the Branch SDK does not expose a method that takes in a listener for userCompletedAction that I saw. 3. Fixed CreditHistoryListener to reject a promise if there was a JSON exception 4. Added properly evaluated callback to setIdentity 5. Updated Readme.md to match code for installation --- README.md | 14 +- src/android/io/branch/BranchSDK.java | 270 ++++++++++++++++++++------- 2 files changed, 209 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 05e884f2..9f4cf1ac 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ There's a full demo app embedded in this repository. It should serve as an examp ### Command link install **Install parameters:** -* `BRANCH_LIVE_KEY` - Your Branch live API key. You can sign up for your own Branch key at [https://dashboard.branch.io](https://dashboard.branch.io). +* `BRANCH_KEY` - Your Branch live API key. You can sign up for your own Branch key at [https://dashboard.branch.io](https://dashboard.branch.io). * `BRANCH_TEST_KEY` - Your Branch test API key. You can sign up for your own Branch key at [https://dashboard.branch.io](https://dashboard.branch.io). * `URI_SCHEME` - It could be your app name or the URI set in your Branch dashboard. As a reminder, the URI scheme is what you use to open your app from a browser, i.e. `yourapp://`. * [optional] `ENCODED_ID` - This is for supporting App Links (6.0+) on Android. You can obtain the encodied id from the Branch dashboard. For more info about App Links, please see [this](https://github.com/BranchMetrics/Android-Deferred-Deep-Linking-SDK/blob/master/README.md#leverage-android-app-links-for-deep-linking) section of the Android readme. @@ -32,33 +32,33 @@ There's a full demo app embedded in this repository. It should serve as an examp #### Cordova ```sh -cordova plugin add https://github.com/BranchMetrics/Cordova-Ionic-PhoneGap-Deferred-Deep-Linking-SDK.git --variable BRANCH_LIVE_KEY= --variable BRANCH_TEST_KEY= --variable URI_SCHEME= +cordova plugin add https://github.com/BranchMetrics/Cordova-Ionic-PhoneGap-Deferred-Deep-Linking-SDK.git --variable BRANCH_KEY= --variable BRANCH_TEST_KEY= --variable URI_SCHEME= ``` example: ```sh -cordova plugin add https://github.com/BranchMetrics/Cordova-Ionic-PhoneGap-Deferred-Deep-Linking-SDK.git --variable BRANCH_LIVE_KEY=key_live_fnmRM1FXtu11t6e4LU8WsldpvDcA0bzv --variable BRANCH_TEST_KEY=key_test_oicIH5u2yxW9w3i6UZdfCiokyrixWpBJ --variable URI_SCHEME=branchsters +cordova plugin add https://github.com/BranchMetrics/Cordova-Ionic-PhoneGap-Deferred-Deep-Linking-SDK.git --variable BRANCH_KEY=key_live_fnmRM1FXtu11t6e4LU8WsldpvDcA0bzv --variable BRANCH_TEST_KEY=key_test_oicIH5u2yxW9w3i6UZdfCiokyrixWpBJ --variable URI_SCHEME=branchsters ``` #### Phonegap ```sh -phonegap plugin add https://github.com/BranchMetrics/Cordova-Ionic-PhoneGap-Deferred-Deep-Linking-SDK.git --variable BRANCH_LIVE_KEY=your-branch-key --variable BRANCH_TEST_KEY=your-branch-key --variable URI_SCHEME=your-app-uri-scheme --variable ENCODED_ID=your-encoded-id +phonegap plugin add https://github.com/BranchMetrics/Cordova-Ionic-PhoneGap-Deferred-Deep-Linking-SDK.git --variable BRANCH_KEY=your-branch-key --variable BRANCH_TEST_KEY=your-branch-key --variable URI_SCHEME=your-app-uri-scheme --variable ENCODED_ID=your-encoded-id ``` example: ```sh -phonegap plugin add https://github.com/BranchMetrics/Cordova-Ionic-PhoneGap-Deferred-Deep-Linking-SDK.git --variable BRANCH_LIVE_KEY=key_live_fnmRM1FXtu11t6e4LU8WsldpvDcA0bzv --variable BRANCH_TEST_KEY=key_test_oicIH5u2yxW9w3i6UZdfCiokyrixWpBJ --variable URI_SCHEME=branchsters +phonegap plugin add https://github.com/BranchMetrics/Cordova-Ionic-PhoneGap-Deferred-Deep-Linking-SDK.git --variable BRANCH_KEY=key_live_fnmRM1FXtu11t6e4LU8WsldpvDcA0bzv --variable BRANCH_TEST_KEY=key_test_oicIH5u2yxW9w3i6UZdfCiokyrixWpBJ --variable URI_SCHEME=branchsters ``` #### NPM ```sh -npm install branch-cordova-sdk --variable BRANCH_LIVE_KEY=your-branch-key --variable BRANCH_TEST_KEY=your-branch-key --variable URI_SCHEME=your-app-uri-scheme +npm install branch-cordova-sdk --variable BRANCH_KEY=your-branch-key --variable BRANCH_TEST_KEY=your-branch-key --variable URI_SCHEME=your-app-uri-scheme ``` example: ```sh -npm install branch-cordova-sdk --variable BRANCH_LIVE_KEY=key_live_fnmRM1FXtu11t6e4LU8WsldpvDcA0bzv --variable BRANCH_TEST_KEY=key_test_oicIH5u2yxW9w3i6UZdfCiokyrixWpBJ --variable URI_SCHEME=branchsters +npm install branch-cordova-sdk --variable BRANCH_KEY=key_live_fnmRM1FXtu11t6e4LU8WsldpvDcA0bzv --variable BRANCH_TEST_KEY=key_test_oicIH5u2yxW9w3i6UZdfCiokyrixWpBJ --variable URI_SCHEME=branchsters ``` ### Additional App Permissions diff --git a/src/android/io/branch/BranchSDK.java b/src/android/io/branch/BranchSDK.java index 66c37237..11457f6f 100644 --- a/src/android/io/branch/BranchSDK.java +++ b/src/android/io/branch/BranchSDK.java @@ -31,7 +31,6 @@ static class BranchLinkProperties extends io.branch.referral.util.LinkProperties // Private Method Properties private BranchUniversalObject branchObj; - private CallbackContext callbackContext; private CallbackContext onShareLinkDialogLaunched; private CallbackContext onShareLinkDialogDismissed; private CallbackContext onLinkShareResponse; @@ -47,7 +46,6 @@ public BranchSDK() this.activity = null; this.instance = null; this.branchObj = null; - this.callbackContext = null; this.onShareLinkDialogLaunched = null; this.onShareLinkDialogDismissed = null; this.onLinkShareResponse = null; @@ -63,7 +61,7 @@ public void onNewIntent(Intent intent) if (this.activity != null) { this.setDebug(true); - this.initSession(); + this.initSession(null); } } @@ -108,7 +106,6 @@ public void onStop() @Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { - this.callbackContext = callbackContext; if (action.equals("setDebug")) { if (args.length() == 1) { @@ -116,50 +113,50 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo } return true; } else if (action.equals("initSession")) { - this.initSession(); + this.initSession(callbackContext); return true; } else { if (this.instance != null) { if (action.equals("setIdentity")) { - this.setIdentity(args.getString(0)); + this.setIdentity(args.getString(0), callbackContext); return true; } else if (action.equals("userCompletedAction")) { if (args.length() == 2) { - this.userCompletedAction (args.getString(0), args.getJSONObject(1)); + this.userCompletedAction(args.getString(0), args.getJSONObject(1), callbackContext); } else { - this.userCompletedAction(args.getString(0)); + this.userCompletedAction(args.getString(0), callbackContext); } return true; } else if (action.equals("getFirstReferringParams")) { - this.getFirstReferringParams(); + this.getFirstReferringParams(callbackContext); return true; } else if (action.equals("getLatestReferringParams")) { - this.getLatestReferringParams(); + this.getLatestReferringParams(callbackContext); return true; } else if (action.equals("logout")) { - this.logout(); + this.logout(callbackContext); return true; } else if (action.equals("loadRewards")) { - this.loadRewards(); + this.loadRewards(callbackContext); return true; } else if (action.equals("redeemRewards")) { if (args.length() == 1) { - this.redeemRewards(args.getInt(0)); + this.redeemRewards(args.getInt(0), callbackContext); } else if (args.length() == 2) { - this.redeemRewards(args.getInt(0), args.getString(1)); + this.redeemRewards(args.getInt(0), args.getString(1), callbackContext); } return true; } else if (action.equals("getCreditHistory")) { - this.getCreditHistory(); + this.getCreditHistory(callbackContext); return true; } else if (action.equals("createBranchUniversalObject")) { - this.createBranchUniversalObject(args.getJSONObject(0)); + this.createBranchUniversalObject(args.getJSONObject(0), callbackContext); return true; } else if (action.equals(("generateShortUrl"))) { - this.generateShortUrl(args.getJSONObject(0), args.getJSONObject(1)); + this.generateShortUrl(args.getJSONObject(0), args.getJSONObject(1), callbackContext); return true; } else if (action.equals("registerView")) { - this.registerView(); + this.registerView(callbackContext); return true; } else if (action.equals("showShareSheet")) { this.showShareSheet(args.getJSONObject(0), args.getJSONObject(1)); @@ -189,17 +186,19 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo ////////////////////////////////////////////////// /** - *

Initialises a session with the Branch API, assigning a {@link BranchUniversalReferralInitListener} + *

Initialises a session with the Branch API, assigning a {@link Branch.BranchUniversalReferralInitListener} * to perform an action upon successful initialisation.

+ * + * @param callbackContext A callback to execute at the end of this method */ - private void initSession() + private void initSession(CallbackContext callbackContext) { - Log.d(LCAT, "start initSession()"); - this.instance = Branch.getAutoInstance(this.activity.getApplicationContext()); - this.instance.initSession(new SessionListener(), activity.getIntent().getData(), activity); + this.activity = this.cordova.getActivity(); + this.instance = Branch.getAutoInstance(this.activity.getApplicationContext()); + this.instance.initSession(new SessionListener(callbackContext), activity.getIntent().getData(), activity); } @@ -207,13 +206,15 @@ private void initSession() *

This method should be called if you know that a different person is about to use the app. For example, * if you allow users to log out and let their friend use the app, you should call this to notify Branch * to create a new user for this device. This will clear the first and latest params, as a new session is created.

+ * + * @param callbackContext A callback to execute at the end of this method */ - private void logout() + private void logout(CallbackContext callbackContext) { Log.d(LCAT, "start logout()"); - this.instance.logout(); + this.instance.logout(new LogoutStatusListener(callbackContext)); } @@ -222,15 +223,16 @@ private void logout() * credits within it. If the number to redeem exceeds the number available in the bucket, all of * the available credits will be redeemed instead.

* - * @param count A {@link Integer} specifying the number of credits to attempt to redeem from + * @param value An {@link Integer} specifying the number of credits to attempt to redeem from * the bucket. + * @param callbackContext A callback to execute at the end of this method */ - private void redeemRewards(final int value) + private void redeemRewards(final int value, CallbackContext callbackContext) { Log.d(LCAT, "start redeemRewards()"); - this.instance.redeemRewards(value, new LoadRewardsListener()); + this.instance.redeemRewards(value, new LoadRewardsListener(callbackContext)); } @@ -239,15 +241,17 @@ private void redeemRewards(final int value) * credits within it. If the number to redeem exceeds the number available in the bucket, all of * the available credits will be redeemed instead.

* - * @param count A {@link Integer} specifying the number of credits to attempt to redeem from + * @param value An {@link Integer} specifying the number of credits to attempt to redeem from * the bucket. + * @param bucket The name of the bucket to remove the credits from. + * @param callbackContext A callback to execute at the end of this method */ - private void redeemRewards(int value, String bucket) + private void redeemRewards(int value, String bucket, CallbackContext callbackContext) { Log.d(LCAT, "start redeemRewards()"); - this.instance.redeemRewards(bucket, value, new LoadRewardsListener()); + this.instance.redeemRewards(bucket, value, new LoadRewardsListener(callbackContext)); } @@ -255,13 +259,14 @@ private void redeemRewards(int value, String bucket) *

Retrieves rewards for the current session, with a callback to perform a predefined * action following successful report of state change. You'll then need to call getCredits * in the callback to update the credit totals in your UX.

+ * + * @param callbackContext A callback to execute at the end of this method */ - private void loadRewards() + private void loadRewards(CallbackContext callbackContext) { Log.d(LCAT, "start loadRewards()"); - - this.instance.loadRewards(new LoadRewardsListener()); + this.instance.loadRewards(new LoadRewardsListener(callbackContext)); } @@ -272,10 +277,12 @@ private void loadRewards() * sessions persist for the duration of time that the app is in focus. For example, if you * minimize the app, these parameters will be cleared when closeSession is called.

* + * @param callbackContext A callback to execute at the end of this method + * * @return A {@link JSONObject} containing the latest referring parameters as * configured locally. */ - private void getLatestReferringParams() + private void getLatestReferringParams(CallbackContext callbackContext) { Log.d(LCAT, "start getLatestReferringParams()"); @@ -289,7 +296,7 @@ private void getLatestReferringParams() Log.d(LCAT, sessionParams.toString()); } - this.callbackContext.success(sessionParams); + callbackContext.success(sessionParams); } @@ -299,8 +306,10 @@ private void getLatestReferringParams() * It is also only set if isReferrable is equal to true, which by default is only true * on a fresh install (not upgrade or reinstall). This will change on setIdentity (if the * user already exists from a previous device) and logout.

+ * + * @param callbackContext A callback to execute at the end of this method */ - private void getFirstReferringParams() + private void getFirstReferringParams(CallbackContext callbackContext) { Log.d(LCAT, "start getFirstReferringParams()"); @@ -314,7 +323,7 @@ private void getFirstReferringParams() Log.d(LCAT, installParams.toString()); } - this.callbackContext.success(installParams); + callbackContext.success(installParams); } @@ -327,7 +336,7 @@ private void getFirstReferringParams() * * @return A {@link JSONObject} value of BranchUniversalObject instance. */ - private void createBranchUniversalObject(JSONObject options) throws JSONException + private void createBranchUniversalObject(JSONObject options, CallbackContext callbackContext) throws JSONException { Log.d(LCAT, "start createBranchUniversalObject()"); @@ -381,8 +390,7 @@ private void createBranchUniversalObject(JSONObject options) throws JSONExceptio } } - this.callbackContext.success("Success"); - + callbackContext.success("Success"); } /** @@ -483,12 +491,12 @@ private BranchLinkProperties createLinkProperties(JSONObject options, JSONObject /** * Mark the content referred by this object as viewed. This increment the view count of the contents referred by this object. */ - private void registerView() + private void registerView(CallbackContext callbackContext) { Log.d(LCAT, "start registerView()"); - this.branchObj.registerView(); + this.branchObj.registerView(new RegisterViewStatusListener(callbackContext)); } @@ -498,7 +506,7 @@ private void registerView() * @param options A {@link JSONObject} value to set URL options. * @param controlParams A {@link JSONObject} value to set the URL control parameters. */ - private void generateShortUrl(JSONObject options, JSONObject controlParams) throws JSONException + private void generateShortUrl(JSONObject options, JSONObject controlParams, CallbackContext callbackContext) throws JSONException { Log.d(LCAT, "start generateShortUrl()"); @@ -558,7 +566,7 @@ private void generateShortUrl(JSONObject options, JSONObject controlParams) thro linkProperties.addControlParameter("$windows_phone_url", controlParams.getString("$windows_phone_url")); } - this.branchObj.generateShortUrl(this.activity, linkProperties, new GenerateShortUrlListener()); + this.branchObj.generateShortUrl(this.activity, linkProperties, new GenerateShortUrlListener(callbackContext)); } @@ -575,7 +583,6 @@ private void setDebug(boolean isEnable) this.activity = this.cordova.getActivity(); - Branch debugInstance = Branch.getAutoInstance(this.activity.getApplicationContext()); if (isEnable) { @@ -589,14 +596,14 @@ private void setDebug(boolean isEnable) * identifier as a {@link String} value.

* * @param newIdentity A {@link String} value containing the unique identifier of the user. + * @param callbackContext A callback to execute at the end of this method */ - private void setIdentity(String newIdentity) + private void setIdentity(String newIdentity, CallbackContext callbackContext) { Log.d(LCAT, "start setIdentity()"); - this.instance.setIdentity(newIdentity); - this.callbackContext.success("Success"); + this.instance.setIdentity(newIdentity, new SetIdentityListener(callbackContext)); } @@ -606,13 +613,15 @@ private void setIdentity(String newIdentity) * * @param action A {@link String} value to be passed as an action that the user has carried out. * For example "logged in" or "registered". + * @param callbackContext A callback to execute at the end of this method */ - private void userCompletedAction(String action) + private void userCompletedAction(String action, CallbackContext callbackContext) { Log.d(LCAT, "start userCompletedAction()"); this.instance.userCompletedAction(action); + callbackContext.success("Success"); } @@ -624,27 +633,30 @@ private void userCompletedAction(String action) * out. For example "logged in" or "registered". * @param metaData A {@link JSONObject} containing app-defined meta-data to be attached to a * user action that has just been completed. + * @param callbackContext A callback to execute at the end of this method */ - private void userCompletedAction(String action, JSONObject metaData) + private void userCompletedAction(String action, JSONObject metaData, CallbackContext callbackContext) { Log.d(LCAT, "start userCompletedAction()"); this.instance.userCompletedAction(action, metaData); - this.callbackContext.success("Success"); + callbackContext.success("Success"); } /** *

Gets the credit history of the specified bucket and triggers a callback to handle the * response.

+ * + * @param callbackContext A callback to execute at the end of this method */ - private void getCreditHistory() + private void getCreditHistory(CallbackContext callbackContext) { Log.d(LCAT, "start creditHistory()"); - this.instance.getCreditHistory(new CreditHistoryListener()); + this.instance.getCreditHistory(new CreditHistoryListener(callbackContext)); } @@ -654,6 +666,12 @@ private void getCreditHistory() protected class SessionListener implements Branch.BranchReferralInitListener { + private CallbackContext _callbackContext; + + // Constructor that takes in a required callbackContext object + public SessionListener(CallbackContext callbackContext) { + this._callbackContext = callbackContext; + } //Listener that implements BranchReferralInitListener for initSession @Override @@ -676,7 +694,9 @@ public void onInitFinished(JSONObject referringParams, BranchError error) { String out = String.format("DeepLinkHandler(%s)", referringParams.toString()); webView.sendJavascript(out); - callbackContext.success(referringParams); + if (this._callbackContext != null) { + this._callbackContext.success(referringParams); + } } else { String errorMessage = error.getMessage(); @@ -686,16 +706,111 @@ public void onInitFinished(JSONObject referringParams, BranchError error) { String out = String.format("DeepLinkHandler(%s)", referringParams.toString()); webView.sendJavascript(out); - callbackContext.error(errorMessage); + if (this._callbackContext != null) { + this._callbackContext.error(errorMessage); + } + } + + } + + } + + protected class LogoutStatusListener implements Branch.LogoutStatusListener + { + private CallbackContext _callbackContext; + + // Constructor that takes in a required callbackContext object + public LogoutStatusListener (CallbackContext callbackContext) { + this._callbackContext = callbackContext; + } + + /** + * Called on finishing the the logout process + * + * @param loggedOut A {@link Boolean} which is set to true if logout succeeded + * @param error An instance of {@link BranchError} to notify any error occurred during logout. + * A null value is set if logout succeeded. + */ + @Override + public void onLogoutFinished(boolean loggedOut, BranchError error) { + if (error == null) { + Log.d(LCAT, "no error on logout"); + this._callbackContext.success(Boolean.toString(loggedOut)); + } else { + Log.d(LCAT, "error on logout"); + this._callbackContext.error(error.getMessage()); + } + } + } + + protected class SetIdentityListener implements Branch.BranchReferralInitListener + { + private CallbackContext _callbackContext; + + public SetIdentityListener (CallbackContext callbackContext) { + this._callbackContext = callbackContext; + } + + //Listener that implements BranchReferralInitListener for setIdentity + @Override + public void onInitFinished(JSONObject referringParams, BranchError error) { + + Log.d(LCAT, "SessionListener onSetIdentityFinished()"); + + if (error == null) { + + this._callbackContext.success("Success"); + + } else { + + String errorMessage = error.getMessage(); + + Log.d(LCAT, errorMessage); + + this._callbackContext.error(errorMessage); } } + + } + + protected class RegisterViewStatusListener implements BranchUniversalObject.RegisterViewStatusListener + { + + private CallbackContext _callbackContext; + + public RegisterViewStatusListener(CallbackContext callbackContext) { + this._callbackContext = callbackContext; + } + + @Override + public void onRegisterViewFinished(boolean registered, BranchError error) { + + Log.d(LCAT, "RegisterViewStatusListener registerViewFinished()"); + + if (error == null) { + this._callbackContext.success(Boolean.toString(registered)); + } else { + + String errorMessage = error.getMessage(); + + Log.d(LCAT, errorMessage); + + this._callbackContext.error(errorMessage); + } + } } protected class LoadRewardsListener implements Branch.BranchReferralStateChangedListener { + private CallbackContext _callbackContext; + + // Constructor that takes in a required callbackContext object + public LoadRewardsListener(CallbackContext callbackContext) { + this._callbackContext = callbackContext; + } // Listener that implements BranchReferralStateChangedListener for loadRewards @Override @@ -709,7 +824,7 @@ public void onStateChanged(boolean changed, BranchError error) { Log.d(LCAT, "LoadRewards success"); - callbackContext.success(credits); + this._callbackContext.success(credits); } else { @@ -717,7 +832,7 @@ public void onStateChanged(boolean changed, BranchError error) { Log.d(LCAT, errorMessage); - callbackContext.error(errorMessage); + this._callbackContext.error(errorMessage); } @@ -726,6 +841,13 @@ public void onStateChanged(boolean changed, BranchError error) { protected class GenerateShortUrlListener implements Branch.BranchLinkCreateListener { + private CallbackContext _callbackContext; + + // Constructor that takes in a required callbackContext object + public GenerateShortUrlListener(CallbackContext callbackContext) { + this._callbackContext = callbackContext; + } + @Override public void onLinkCreate(String url, BranchError error) { @@ -745,7 +867,7 @@ public void onLinkCreate(String url, BranchError error) { } Log.d(LCAT, response.toString()); - callbackContext.success(response); + this._callbackContext.success(response); } else { @@ -761,7 +883,7 @@ public void onLinkCreate(String url, BranchError error) { } Log.d(LCAT, response.toString()); - callbackContext.error(response); + this._callbackContext.error(response); } @@ -873,13 +995,19 @@ public void onChannelSelected(String channelName) { protected class CreditHistoryListener implements Branch.BranchListResponseListener { + private CallbackContext _callbackContext; + + // Constructor that takes in a required callbackContext object + public CreditHistoryListener(CallbackContext callbackContext) { + this._callbackContext = callbackContext; + } + // Listener that implements BranchListResponseListener for getCreditHistory() @Override public void onReceivingResponse(JSONArray list, BranchError error) { Log.d(LCAT, "inside onReceivingResponse"); - - JSONObject response = new JSONObject(); + ArrayList errors = new ArrayList(); if (error == null) { @@ -896,24 +1024,30 @@ public void onReceivingResponse(JSONArray list, BranchError error) { data.put(entry); } catch (JSONException e) { e.printStackTrace(); - return; + errors.add(e.getMessage()); } } } - callbackContext.success(data); - + if (errors.size() > 0) { + StringBuilder sb = new StringBuilder(); + for (String s : errors) { + sb.append(s); + sb.append("\n"); + } + this._callbackContext.error(sb.toString()); + } else { + this._callbackContext.success(data); + } } else { - response = new JSONObject(); - String errorMessage = error.getMessage(); Log.d(LCAT, errorMessage); - callbackContext.error(errorMessage); + this._callbackContext.error(errorMessage); } }