diff options
Diffstat (limited to 'libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/JsCallbackReceiver.java')
-rwxr-xr-x | libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/JsCallbackReceiver.java | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/JsCallbackReceiver.java b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/JsCallbackReceiver.java new file mode 100755 index 000000000..b8212fcef --- /dev/null +++ b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/JsCallbackReceiver.java @@ -0,0 +1,236 @@ +package org.wordpress.android.editor; + +import android.webkit.JavascriptInterface; + +import org.json.JSONException; +import org.json.JSONObject; +import org.wordpress.android.util.AppLog; +import org.wordpress.android.util.JSONUtils; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.wordpress.android.editor.EditorFragmentAbstract.MediaType; + +public class JsCallbackReceiver { + private static final String JS_CALLBACK_DELIMITER = "~"; + + private static final String CALLBACK_DOM_LOADED = "callback-dom-loaded"; + private static final String CALLBACK_NEW_FIELD = "callback-new-field"; + + private static final String CALLBACK_INPUT = "callback-input"; + private static final String CALLBACK_SELECTION_CHANGED = "callback-selection-changed"; + private static final String CALLBACK_SELECTION_STYLE = "callback-selection-style"; + + private static final String CALLBACK_FOCUS_IN = "callback-focus-in"; + private static final String CALLBACK_FOCUS_OUT = "callback-focus-out"; + + private static final String CALLBACK_IMAGE_REPLACED = "callback-image-replaced"; + private static final String CALLBACK_VIDEO_REPLACED = "callback-video-replaced"; + private static final String CALLBACK_IMAGE_TAP = "callback-image-tap"; + private static final String CALLBACK_LINK_TAP = "callback-link-tap"; + private static final String CALLBACK_MEDIA_REMOVED = "callback-media-removed"; + + private static final String CALLBACK_VIDEOPRESS_INFO_REQUEST = "callback-videopress-info-request"; + + private static final String CALLBACK_LOG = "callback-log"; + + private static final String CALLBACK_RESPONSE_STRING = "callback-response-string"; + + private static final String CALLBACK_ACTION_FINISHED = "callback-action-finished"; + + private final OnJsEditorStateChangedListener mListener; + + private Set<String> mPreviousStyleSet = new HashSet<>(); + + public JsCallbackReceiver(EditorFragmentAbstract editorFragmentAbstract) { + mListener = (OnJsEditorStateChangedListener) editorFragmentAbstract; + } + + @JavascriptInterface + public void executeCallback(String callbackId, String params) { + switch (callbackId) { + case CALLBACK_DOM_LOADED: + mListener.onDomLoaded(); + break; + case CALLBACK_SELECTION_STYLE: + // Compare the new styles to the previous ones, and notify the JsCallbackListener of the changeset + Set<String> rawStyleSet = Utils.splitDelimitedString(params, JS_CALLBACK_DELIMITER); + + // Strip link details from active style set + Set<String> newStyleSet = new HashSet<>(); + for (String element : rawStyleSet) { + if (element.matches("link:(.*)")) { + newStyleSet.add("link"); + } else if (!element.matches("link-title:(.*)")) { + newStyleSet.add(element); + } + } + + mListener.onSelectionStyleChanged(Utils.getChangeMapFromSets(mPreviousStyleSet, newStyleSet)); + mPreviousStyleSet = newStyleSet; + break; + case CALLBACK_SELECTION_CHANGED: + // Called for changes to the field in current focus and for changes made to selection + // (includes moving the caret without selecting text) + // TODO: Possibly needed for handling WebView scrolling when caret moves (from iOS) + Set<String> selectionKeyValueSet = Utils.splitDelimitedString(params, JS_CALLBACK_DELIMITER); + mListener.onSelectionChanged(Utils.buildMapFromKeyValuePairs(selectionKeyValueSet)); + break; + case CALLBACK_INPUT: + // Called on key press + // TODO: Possibly needed for handling WebView scrolling when caret moves (from iOS) + break; + case CALLBACK_FOCUS_IN: + // TODO: Needed to handle displaying/graying the format bar when focus changes between the title and content + AppLog.d(AppLog.T.EDITOR, "Focus in callback received"); + break; + case CALLBACK_FOCUS_OUT: + // TODO: Needed to handle displaying/graying the format bar when focus changes between the title and content + AppLog.d(AppLog.T.EDITOR, "Focus out callback received"); + break; + case CALLBACK_NEW_FIELD: + // TODO: Used for logging/testing purposes on iOS + AppLog.d(AppLog.T.EDITOR, "New field created, " + params); + break; + case CALLBACK_IMAGE_REPLACED: + AppLog.d(AppLog.T.EDITOR, "Image replaced, " + params); + + // Extract the local media id from the callback string (stripping the 'id=' part) + if (params.length() > 3) { + mListener.onMediaReplaced(params.substring(3)); + } + break; + case CALLBACK_VIDEO_REPLACED: + AppLog.d(AppLog.T.EDITOR, "Video replaced, " + params); + + // Extract the local media id from the callback string (stripping the 'id=' part) + if (params.length() > 3) { + mListener.onMediaReplaced(params.substring(3)); + } + break; + case CALLBACK_IMAGE_TAP: + AppLog.d(AppLog.T.EDITOR, "Image tapped, " + params); + + String uploadStatus = ""; + + List<String> mediaIds = new ArrayList<>(); + mediaIds.add("id"); + mediaIds.add("url"); + mediaIds.add("meta"); + mediaIds.add("type"); + + Set<String> mediaDataSet = Utils.splitValuePairDelimitedString(params, JS_CALLBACK_DELIMITER, mediaIds); + Map<String, String> mediaDataMap = Utils.buildMapFromKeyValuePairs(mediaDataSet); + + String mediaId = mediaDataMap.get("id"); + + String mediaUrl = mediaDataMap.get("url"); + if (mediaUrl != null) { + mediaUrl = Utils.decodeHtml(mediaUrl); + } + + MediaType mediaType = MediaType.fromString(mediaDataMap.get("type")); + + String mediaMeta = mediaDataMap.get("meta"); + JSONObject mediaMetaJson = new JSONObject(); + + if (mediaMeta != null) { + mediaMeta = Utils.decodeHtml(mediaMeta); + + try { + mediaMetaJson = new JSONObject(mediaMeta); + String classes = JSONUtils.getString(mediaMetaJson, "classes"); + Set<String> classesSet = Utils.splitDelimitedString(classes, ", "); + + if (classesSet.contains("uploading")) { + uploadStatus = "uploading"; + } else if (classesSet.contains("failed")) { + uploadStatus = "failed"; + } + } catch (JSONException e) { + e.printStackTrace(); + AppLog.d(AppLog.T.EDITOR, "Media meta data from callback-image-tap was not JSON-formatted"); + } + } + + mListener.onMediaTapped(mediaId, mediaType, mediaMetaJson, uploadStatus); + break; + case CALLBACK_LINK_TAP: + // Extract and HTML-decode the link data from the callback params + AppLog.d(AppLog.T.EDITOR, "Link tapped, " + params); + + List<String> linkIds = new ArrayList<>(); + linkIds.add("url"); + linkIds.add("title"); + + Set<String> linkDataSet = Utils.splitValuePairDelimitedString(params, JS_CALLBACK_DELIMITER, linkIds); + Map<String, String> linkDataMap = Utils.buildMapFromKeyValuePairs(linkDataSet); + + String url = linkDataMap.get("url"); + if (url != null) { + url = Utils.decodeHtml(url); + } + + String title = linkDataMap.get("title"); + if (title != null) { + title = Utils.decodeHtml(title); + } + + mListener.onLinkTapped(url, title); + break; + case CALLBACK_MEDIA_REMOVED: + AppLog.d(AppLog.T.EDITOR, "Media removed, " + params); + // Extract the media id from the callback string (stripping the 'id=' part of the callback string) + if (params.length() > 3) { + mListener.onMediaRemoved(params.substring(3)); + } + break; + case CALLBACK_VIDEOPRESS_INFO_REQUEST: + // Extract the VideoPress id from the callback string (stripping the 'id=' part of the callback string) + if (params.length() > 3) { + mListener.onVideoPressInfoRequested(params.substring(3)); + } + break; + case CALLBACK_LOG: + // Strip 'msg=' from beginning of string + if (params.length() > 4) { + AppLog.d(AppLog.T.EDITOR, callbackId + ": " + params.substring(4)); + } + break; + case CALLBACK_RESPONSE_STRING: + AppLog.d(AppLog.T.EDITOR, callbackId + ": " + params); + Set<String> responseDataSet; + if (params.startsWith("function=") && params.contains(JS_CALLBACK_DELIMITER)) { + String functionName = params.substring("function=".length(), params.indexOf(JS_CALLBACK_DELIMITER)); + + List<String> responseIds = new ArrayList<>(); + switch (functionName) { + case "getHTMLForCallback": + responseIds.add("id"); + responseIds.add("contents"); + break; + case "getSelectedTextToLinkify": + responseIds.add("result"); + break; + case "getFailedMedia": + responseIds.add("ids"); + } + + responseDataSet = Utils.splitValuePairDelimitedString(params, JS_CALLBACK_DELIMITER, responseIds); + } else { + responseDataSet = Utils.splitDelimitedString(params, JS_CALLBACK_DELIMITER); + } + mListener.onGetHtmlResponse(Utils.buildMapFromKeyValuePairs(responseDataSet)); + break; + case CALLBACK_ACTION_FINISHED: + mListener.onActionFinished(); + break; + default: + AppLog.d(AppLog.T.EDITOR, "Unhandled callback: " + callbackId + ":" + params); + } + } +} |