// test that after a page load the cached callback id and the live callback id match // this is to prevent a regression // the issue was that CordovaWebViewImpl's cached instance of CoreAndroid would become stale on // page load // this is because the cached instance was not being cleared when the pluginManager was reset on // page load // the plugin manager would get a new instance which would be updated with a new callback id // the cached instance's message channel callback id would become stale // effectively this caused message channel events to not be delivered public void testThatCachedCallbackIdIsValid() throws Throwable { Class cordovaWebViewImpl = CordovaWebViewImpl.class; // send a test event - this initializes cordovaWebViewImpl.appPlugin (the cached instance of // CoreAndroid) Method method = cordovaWebViewImpl.getDeclaredMethod("sendJavascriptEvent", String.class); method.setAccessible(true); method.invoke(cordovaWebView, "testEvent"); sleep(1000); // load a page - this resets the plugin manager and nulls cordovaWebViewImpl.appPlugin // (previously this resets plugin manager but did not null cordovaWebViewImpl.appPlugin, leading // to the issue) runTestOnUiThread( new Runnable() { public void run() { cordovaWebView.loadUrl(START_URL); } }); assertEquals(START_URL, testActivity.onPageFinishedUrl.take()); // send a test event - this initializes cordovaWebViewImpl.appPlugin (the cached instance of // CoreAndroid) method.invoke(cordovaWebView, "testEvent"); sleep(1000); // get reference to package protected class CoreAndroid Class coreAndroid = Class.forName("org.apache.cordova.CoreAndroid"); // get cached CoreAndroid Field appPluginField = cordovaWebViewImpl.getDeclaredField("appPlugin"); appPluginField.setAccessible(true); Object cachedAppPlugin = appPluginField.get(cordovaWebView); // get cached CallbackContext Field messageChannelField = coreAndroid.getDeclaredField("messageChannel"); messageChannelField.setAccessible(true); CallbackContext cachedCallbackContext = (CallbackContext) messageChannelField.get(cachedAppPlugin); // get live CoreAndroid PluginManager pluginManager = MessageChannelMultiPageTest.this.cordovaWebView.getPluginManager(); Field coreAndroidPluginNameField = coreAndroid.getField("PLUGIN_NAME"); String coreAndroidPluginName = (String) coreAndroidPluginNameField.get(null); Object liveAppPlugin = pluginManager.getPlugin(coreAndroidPluginName); // get live CallbackContext CallbackContext liveCallbackContext = (CallbackContext) messageChannelField.get(liveAppPlugin); // get callback id from live callbackcontext String liveCallbackId = (liveCallbackContext != null) ? liveCallbackContext.getCallbackId() : null; // get callback id from cached callbackcontext String cachedCallbackId = (cachedCallbackContext != null) ? cachedCallbackContext.getCallbackId() : null; // verify that the live message channel has been initialized assertNotNull(liveCallbackId); // verify that the cached message channel and the live message channel have the same id assertEquals(liveCallbackId, cachedCallbackId); }
/** * Executes the request and returns PluginResult. * * @param action The action to execute * @param args JSONArray of arguments for the plugin. * @param callbackContext The callback id used when calling back into JavaScript * @return True if the action was valid, otherwise false */ @Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { Log.d(EstimoteBeacons.class.toString(), "action -> " + action); try { if (action.equalsIgnoreCase(START_MONITORING_BEACONS_IN_REGION)) { startMonitoringBeaconsInRegion(callbackContext); return true; } if (action.equalsIgnoreCase(STOP_MONITORING_BEACONS_IN_REGION)) { stopMonitoringBeaconsInRegion(); callbackContext.success(callbackContext.getCallbackId()); return true; } if (action.equalsIgnoreCase(START_RANGING_BEACONS_IN_REGION)) { startRangingBeaconsInRegion(); callbackContext.success(callbackContext.getCallbackId()); return true; } if (action.equalsIgnoreCase(STOP_RANGING_BEACONS_IN_REGION)) { stopRangingBeaconsInRegion(); callbackContext.success(callbackContext.getCallbackId()); return true; } if (action.equalsIgnoreCase(GET_BEACONS)) { callbackContext.sendPluginResult( new PluginResult(PluginResult.Status.OK, listToJSONArray(beacons))); return true; } if (action.equalsIgnoreCase(IS_BLE_SUPPORTED)) { isBleSupported(callbackContext); return true; } if (action.equalsIgnoreCase(IS_BLUETOOTH_ENABLED)) { isBluetoothEnabled(callbackContext); return true; } } catch (Exception e) { System.out.println(e.getMessage()); callbackContext.error(e.getMessage()); return false; } return false; }
public void setPushReceiveCallbackChannel(JSONArray data, CallbackContext callbackContext) { Log.i(TAG, "setPushReceiveCallbackChannel:" + callbackContext.getCallbackId()); mJPushReceiveCallback = callbackContext; PluginResult dataResult = new PluginResult(PluginResult.Status.OK, "js call init ok"); dataResult.setKeepCallback(true); // 必要 mJPushReceiveCallback.sendPluginResult(dataResult); }
public boolean execute(String action, JSONArray data, final CallbackContext callbackContext) { Boolean result = false; if (ACTION_START.equalsIgnoreCase(action)) { if (geotrigger.isEnabled()) { callbackContext.error("Service has already been started"); } else if (!geotrigger.isConfigured()) { callbackContext.error("Call configure before calling start"); } else { this.geotrigger.start(); result = true; callbackContext.success("start succeed"); } } else if (ACTION_STOP.equalsIgnoreCase(action)) { if (!geotrigger.isEnabled()) { callbackContext.error("The service hasn't started yet"); } else { this.geotrigger.stop(); result = true; callbackContext.success("stop succeed"); } } else if (ACTION_CONFIGURE.equalsIgnoreCase(action)) { if (geotrigger.isEnabled()) { Log.v(Config.TAG, "has enabled, then disable all the previous geofences"); geotrigger.stop(); } TripPlan tripPlanToConfigure = null; try { TripPlanParser parser = new TripPlanParser(data.toString()); tripPlanToConfigure = parser.getTripplan(); } catch (Exception e) { e.printStackTrace(); callbackContext.error("Errors occur when parsing tripplan data"); } if (tripPlanToConfigure != null) { this.geotrigger.configure(tripPlanToConfigure); result = true; callbackContext.success("configure succeed"); } } else if (ACTION_RECONFIGURE.equalsIgnoreCase(action)) { TripPlan tripPlanToConfigure = null; try { TripPlanParser parser = new TripPlanParser(data.toString()); tripPlanToConfigure = parser.getTripplan(); } catch (Exception e) { e.printStackTrace(); callbackContext.error("Errors occur when parsing tripplan data"); } if (tripPlanToConfigure != null) { this.geotrigger.reconfigure(tripPlanToConfigure); result = true; callbackContext.success("configure succeed"); } } else if (ACTION_ADDPLACE.equalsIgnoreCase(action)) { if (!this.geotrigger.isConfigured()) { callbackContext.error("The service is not configured yet"); } else { Place place = null; try { PlaceParser parser = new PlaceParser(data.toString()); place = parser.getPlace(); } catch (Exception e) { e.printStackTrace(); } if (place == null) { callbackContext.error("Errors occur when parsing tripplan data"); } else { boolean isAdded = this.geotrigger.addPlace(place); if (isAdded) { result = true; callbackContext.success("adding place succeed"); } else { callbackContext.error("failed to add place"); } } } } else if (ACTION_DELETEPLACE.equalsIgnoreCase(action)) { if (!this.geotrigger.isConfigured()) { callbackContext.error("The service is not configured yet"); } else { String placeUuid = null; try { PlaceUuidParser parser = new PlaceUuidParser(data.toString()); placeUuid = parser.getPlaceUuid(); } catch (Exception e) { e.printStackTrace(); } if (placeUuid == null) { callbackContext.error("Errors occur when parsing place uuid"); } else { boolean isDeleted = this.geotrigger.deletePlace(placeUuid); if (isDeleted) { result = true; callbackContext.success("deleting place succeed"); } else { callbackContext.error("failed to delete place"); } } } } else if (ACTION_ENABLEPLACE.equalsIgnoreCase(action)) { if (!this.geotrigger.isConfigured()) { callbackContext.error("The service is not configured yet"); } else { String placeUuid = null; try { PlaceUuidParser parser = new PlaceUuidParser(data.toString()); placeUuid = parser.getPlaceUuid(); } catch (Exception e) { e.printStackTrace(); } if (placeUuid == null) { callbackContext.error("Errors occur when parsing place uuid"); } else { boolean isEnabled = this.geotrigger.enablePlace(placeUuid); if (isEnabled) { result = true; callbackContext.success("enable place succeed"); } else { callbackContext.error("failed to enable place"); } } } } else if (ACTION_DISABLEPLACE.equalsIgnoreCase(action)) { if (!this.geotrigger.isConfigured()) { callbackContext.error("The service is not configured yet"); } else { String placeUuid = null; try { PlaceUuidParser parser = new PlaceUuidParser(data.toString()); placeUuid = parser.getPlaceUuid(); } catch (Exception e) { e.printStackTrace(); } if (placeUuid == null) { callbackContext.error("Errors occur when parsing place uuid"); } else { boolean isEnabled = this.geotrigger.disablePlace(placeUuid); if (isEnabled) { result = true; callbackContext.success("disable place succeed"); } else { callbackContext.error("failed to disable place"); } } } } else if (ACTION_GETCURRENTLOCATION.equalsIgnoreCase(action)) { final LocationManager locationManager = (LocationManager) this.cordova.getActivity().getSystemService(Context.LOCATION_SERVICE); Criteria criteria = new Criteria(); criteria.setAltitudeRequired(false); criteria.setBearingRequired(false); criteria.setSpeedRequired(true); criteria.setCostAllowed(true); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH); criteria.setPowerRequirement(Criteria.POWER_HIGH); String bestProvider = locationManager.getBestProvider(criteria, true); if (bestProvider != LocationManager.PASSIVE_PROVIDER) { locationManager.requestLocationUpdates( bestProvider, 0, 0, new LocationListener() { @Override public void onLocationChanged(Location location) { JSONObject object = new JSONObject(); try { object.put("lat", location.getLatitude()); object.put("lng", location.getLongitude()); } catch (JSONException e) { e.printStackTrace(); } callbackContext.success(object); locationManager.removeUpdates(this); } @Override public void onProviderDisabled(String arg0) {} @Override public void onProviderEnabled(String arg0) {} @Override public void onStatusChanged(String arg0, int arg1, Bundle arg2) {} }); } } else if (ACTION_SETONNOTIFICATIONCLICKEDCALLBACK.equalsIgnoreCase(action)) { String callbackId = callbackContext.getCallbackId(); this.callbackIds.put(ACTION_SETONNOTIFICATIONCLICKEDCALLBACK, callbackId); Log.d(Config.TAG, "notificaton callback is set"); return true; } else if (ACTION_MOCK_START.equalsIgnoreCase(action)) { if (this.geoFaker != null && this.geoFaker.isStarted()) { callbackContext.error("geofaker has started"); } else { this.geoFaker = new Geofaker(this.cordova.getActivity()); this.geoFaker.start(); result = true; callbackContext.success("mocking started"); } } else if (ACTION_MOCK_STOP.equalsIgnoreCase(action)) { if (this.geoFaker != null && this.geoFaker.isStarted()) { this.geoFaker.stop(); result = true; callbackContext.success("mocking stopped"); } else { callbackContext.error("geofaker has already stopped"); } } else if (ACTION_MOCK.equalsIgnoreCase(action)) { Intent intent = new Intent(); intent.setAction(Geofaker.INTENT_MOCK_GPS_PROVIDER); intent.putExtra(Geofaker.MOCKED_JSON_COORDINATES, data.toString()); activity.sendBroadcast(intent); result = true; callbackContext.success("mocked position: " + data.toString()); } return result; }
/** * Executes the request and returns PluginResult. * * @param action The action to execute. * @param args JSONArry of arguments for the plugin. * @param callbackId The callback id used when calling back into JavaScript. * @return A PluginResult object with a status and message. */ public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException { if (action.equals("open")) { this.callbackContext = callbackContext; final String url = args.getString(0); String t = args.optString(1); if (t == null || t.equals("") || t.equals(NULL)) { t = SELF; } final String target = t; final HashMap<String, Boolean> features = parseFeature(args.optString(2)); Log.d(LOG_TAG, "target = " + target); this.cordova .getActivity() .runOnUiThread( new Runnable() { @Override public void run() { String result = ""; // SELF if (SELF.equals(target)) { Log.d(LOG_TAG, "in self"); // load in webview if (url.startsWith("file://") || url.startsWith("javascript:") || Config.isUrlWhiteListed(url)) { webView.loadUrl(url); } // Load the dialer else if (url.startsWith(AmazonWebView.SCHEME_TEL)) { try { Intent intent = new Intent(Intent.ACTION_DIAL); intent.setData(Uri.parse(url)); cordova.getActivity().startActivity(intent); } catch (android.content.ActivityNotFoundException e) { LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString()); } } // load in InAppBrowser else { result = showWebPage(url, features); } } // SYSTEM else if (SYSTEM.equals(target)) { Log.d(LOG_TAG, "in system"); result = openExternal(url); } // BLANK - or anything else else { Log.d(LOG_TAG, "in blank"); result = showWebPage(url, features); } PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result); pluginResult.setKeepCallback(true); callbackContext.sendPluginResult(pluginResult); } }); } else if (action.equals("close")) { closeDialog(); } else if (action.equals("injectScriptCode")) { String jsWrapper = null; if (args.getBoolean(1)) { jsWrapper = String.format( "prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId()); } injectDeferredObject(args.getString(0), jsWrapper); } else if (action.equals("injectScriptFile")) { String jsWrapper; if (args.getBoolean(1)) { jsWrapper = String.format( "(function(d) { var c = d.createElement('script'); c.src = %%s; c.onload = function() { prompt('', 'gap-iab://%s'); }; d.body.appendChild(c); })(document)", callbackContext.getCallbackId()); } else { jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)"; } injectDeferredObject(args.getString(0), jsWrapper); } else if (action.equals("injectStyleCode")) { String jsWrapper; if (args.getBoolean(1)) { jsWrapper = String.format( "(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId()); } else { jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)"; } injectDeferredObject(args.getString(0), jsWrapper); } else if (action.equals("injectStyleFile")) { String jsWrapper; if (args.getBoolean(1)) { jsWrapper = String.format( "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%s; d.head.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId()); } else { jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)"; } injectDeferredObject(args.getString(0), jsWrapper); } else if (action.equals("show")) { this.cordova .getActivity() .runOnUiThread( new Runnable() { @Override public void run() { dialog.show(); } }); PluginResult pluginResult = new PluginResult(PluginResult.Status.OK); pluginResult.setKeepCallback(true); this.callbackContext.sendPluginResult(pluginResult); } else { return false; } return true; }