package org.apache.cordova.sharingreceptor; import java.lang.RuntimeException; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.HashSet; import java.util.Arrays; import org.apache.cordova.CordovaActivity; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.Intent; import android.os.Bundle; import android.net.Uri; import android.text.Html; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CordovaResourceApi; import org.apache.cordova.PluginResult; import android.util.Log; /** * * @author [email protected] */ public class SharingReceptor extends CordovaPlugin { private static final String TAG = SharingReceptor.class.getSimpleName(); // Constant that holds all the intent actions that we will handle. private static final Set<String> SEND_INTENTS = new HashSet<String>(Arrays.asList( Intent.ACTION_SEND, Intent.ACTION_SEND_MULTIPLE )); private static boolean isSendIntent(Intent intent) { return SEND_INTENTS.contains(intent.getAction()); } private static JSONObject serializeIntent(Intent intent) throws JSONException { JSONObject extras = SharingReceptor.serializeBundle(intent.getExtras()); JSONObject intentJson = new JSONObject(); intentJson.put("action", intent.getAction()); intentJson.put("data", SharingReceptor.safeJSONWrap(intent.getData()) ); intentJson.put("dataString", intent.getDataString()); intentJson.put("type", intent.getType()); intentJson.put("extras", extras); JSONObject result = new JSONObject(); result.put("platform", "android"); result.put("intent", intentJson); return result; } // See http://stackoverflow.com/a/21859000/82609 private static JSONObject serializeBundle(Bundle bundle) throws JSONException { JSONObject json = new JSONObject(); if ( bundle != null ) { Set<String> keys = bundle.keySet(); for (String key : keys) { Object value = bundle.get(key); try { json.put(key, SharingReceptor.safeJSONWrap(value)); } catch(Exception e) { Log.e(TAG,"Can't serialize key " + key,e); throw new RuntimeException("Can't serialize bundle for key = " + key,e); } } } Log.i(TAG, "json -> " + json.toString()); return json; } // Because JSONObject.wrap(value) returns null on failure (this is the case for HierarchicalUri instances for example) private static Object safeJSONWrap(Object value) throws JSONException { Object wrappedValue = JSONObject.wrap(value); boolean wrappingHasFailed = (value != null && wrappedValue == null); if ( wrappingHasFailed ) { wrappedValue = value.toString(); // Fallback method } return wrappedValue; } /////////////////////////////////////////////////////////////////////////////////////////: /////////////////////////////////////////////////////////////////////////////////////////: /////////////////////////////////////////////////////////////////////////////////////////: private CallbackContext listenerCallback = null; @Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) { try { if ( action.equals("listen") ) { installListener(callbackContext); return true; } else { callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.INVALID_ACTION,"action ["+action+"] does not exist")); return false; } } catch (Exception e) { Log.e(TAG,"Error while executing action ["+action+"]",e); callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR,e.getMessage())); return false; } } private void installListener(CallbackContext callbackContext) { if ( this.listenerCallback != null ) { throw new RuntimeException("You already set a listener: it does not make sense to reset it."); } this.listenerCallback = callbackContext; // handle the case where the app is started by a new intent: // we publish the current intent (if needed) directly after the callback registration this.maybePublishIntent(this.cordova.getActivity().getIntent()); } // handle the case when the app is already in background, and is "awakened" by a new intent sent from another app @Override public void onNewIntent(Intent intent) { Log.i(TAG, "onNewIntent -> " + intent); this.maybePublishIntent(intent); // TODO would it be useful to replace current activity intent by new intent? Can it messes things up? } // We try to publish in the JS callback the intent data, if the intent is a send intent, and if the callback was correctly setup private void maybePublishIntent(Intent intent) { if ( !SharingReceptor.isSendIntent(intent) ) { Log.i(TAG, "maybePublishIntent -> not publishing intent because the action name is not part of SEND_INTENTS=" + SEND_INTENTS); } else if ( this.listenerCallback == null ) { Log.w(TAG, "maybePublishIntent -> not publishing intent because listener callback not set"); } else { JSONObject intentJson; try { intentJson = SharingReceptor.serializeIntent(intent); } catch (Exception e) { throw new RuntimeException("Can't serialize intent " + intent,e); } Log.i(TAG, "maybePublishIntent -> will publish intent -> " + intentJson.toString()); PluginResult result = new PluginResult(PluginResult.Status.OK, intentJson); result.setKeepCallback(true); this.listenerCallback.sendPluginResult(result); } } }