Skip to content

Commit

Permalink
Merge pull request #129 from wordpress-mobile/issue/23-extract-callba…
Browse files Browse the repository at this point in the history
…ck-handler

Issue #23: Extract callback handler
  • Loading branch information
aforcier committed Apr 15, 2015
2 parents eaa2f17 + 228a563 commit 6195fe0
Show file tree
Hide file tree
Showing 9 changed files with 471 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package org.wordpress.android.editor;

import android.util.Log;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.wordpress.android.util.AppLog;

import java.util.List;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static org.mockito.Mockito.mock;
import static org.robolectric.shadows.ShadowLog.LogItem;

@Config(emulateSdk = 18)
@RunWith(RobolectricTestRunner.class)
public class JsCallbackHandlerTest {
private final static String EDITOR_LOG_TAG = "WordPress-" + AppLog.T.EDITOR.toString();

private JsCallbackReceiver mJsCallbackReceiver;

@Before
public void setUp() {
EditorFragment editorFragment = mock(EditorFragment.class);
mJsCallbackReceiver = new JsCallbackReceiver(editorFragment);
}

@Test
public void testCallbacksRecognized() {
mJsCallbackReceiver.executeCallback("callback-dom-loaded", "");
assertNotLogged("Unhandled callback");

mJsCallbackReceiver.executeCallback("callback-new-field", "field-name");
assertNotLogged("Unhandled callback");

mJsCallbackReceiver.executeCallback("callback-input", "arguments");
assertNotLogged("Unhandled callback");

mJsCallbackReceiver.executeCallback("callback-selection-changed", "arguments");
assertNotLogged("Unhandled callback");

mJsCallbackReceiver.executeCallback("callback-selection-style", "arguments");
assertNotLogged("Unhandled callback");

mJsCallbackReceiver.executeCallback("callback-focus-in", "");
assertNotLogged("Unhandled callback");

mJsCallbackReceiver.executeCallback("callback-focus-out", "");
assertNotLogged("Unhandled callback");

mJsCallbackReceiver.executeCallback("callback-image-replaced", "arguments");
assertNotLogged("Unhandled callback");

mJsCallbackReceiver.executeCallback("callback-image-tap", "arguments");
assertNotLogged("Unhandled callback");

mJsCallbackReceiver.executeCallback("callback-link-tap", "arguments");
assertNotLogged("Unhandled callback");

mJsCallbackReceiver.executeCallback("callback-log", "arguments");
assertNotLogged("Unhandled callback");
}

@Test
public void testUnknownCallbackShouldBeLogged() {
mJsCallbackReceiver.executeCallback("callback-does-not-exist", "content");
assertLogged(Log.DEBUG, EDITOR_LOG_TAG, "Unhandled callback: callback-does-not-exist:content", null);
}

@Test
public void testCallbackLog() {
mJsCallbackReceiver.executeCallback("callback-log", "msg=test-message");
assertLogged(Log.DEBUG, EDITOR_LOG_TAG, "callback-log: test-message", null);
}

private void assertLogged(int type, String tag, String msg, Throwable throwable) {
LogItem lastLog = ShadowLog.getLogs().get(0);
assertEquals(type, lastLog.type);
assertEquals(msg, lastLog.msg);
assertEquals(tag, lastLog.tag);
assertEquals(throwable, lastLog.throwable);
}

private void assertNotLogged(String msg) {
List<LogItem> logList = ShadowLog.getLogs();
if (!logList.isEmpty()) {
assertFalse(logList.get(0).msg.contains(msg));
ShadowLog.reset();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.wordpress.android.editor;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static org.junit.Assert.assertEquals;
import static org.wordpress.android.editor.Utils.getChangeMapFromSets;
import static org.wordpress.android.editor.Utils.splitDelimitedString;

@Config(emulateSdk = 18)
@RunWith(RobolectricTestRunner.class)
public class UtilsTest {

@Test
public void testSplitDelimitedString() {
Set<String> splitString = new HashSet<>();

// Test normal usage
splitString.add("p");
splitString.add("bold");
splitString.add("justifyLeft");

assertEquals(splitString, splitDelimitedString("p~bold~justifyLeft", "~"));

// Test empty string
assertEquals(Collections.emptySet(), splitDelimitedString("", "~"));
}

@Test
public void testGetChangeMapFromSets() {
Set<String> oldSet = new HashSet<>();
Set<String> newSet = new HashSet<>();
Map<String, Boolean> expectedMap = new HashMap<>();

// Test normal usage
oldSet.add("p");
oldSet.add("bold");
oldSet.add("justifyLeft");

newSet.add("p");
newSet.add("justifyRight");

expectedMap.put("bold", false);
expectedMap.put("justifyLeft", false);
expectedMap.put("justifyRight", true);

assertEquals(expectedMap, getChangeMapFromSets(oldSet, newSet));

// Test no changes
oldSet.clear();
oldSet.add("p");
oldSet.add("bold");

newSet.clear();
newSet.add("p");
newSet.add("bold");

assertEquals(Collections.emptyMap(), getChangeMapFromSets(oldSet, newSet));

// Test empty sets
assertEquals(Collections.emptyMap(), getChangeMapFromSets(Collections.emptySet(), Collections.emptySet()));
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package org.wordpress.android.editor;

import android.annotation.SuppressLint;
import android.content.res.AssetManager;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.text.Spanned;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.ConsoleMessage;
import android.webkit.JavascriptInterface;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ToggleButton;

import com.android.volley.toolbox.ImageLoader;

Expand All @@ -23,20 +24,25 @@
import org.wordpress.android.util.helpers.MediaFile;
import org.wordpress.android.util.helpers.MediaGallery;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

public class EditorFragment extends EditorFragmentAbstract {
public class EditorFragment extends EditorFragmentAbstract implements View.OnClickListener,
OnJsEditorStateChangedListener {
private static final String ARG_PARAM_TITLE = "param_title";
private static final String ARG_PARAM_CONTENT = "param_content";

private static final String JS_CALLBACK_HANDLER = "nativeCallbackHandler";

private static final String TAG_FORMAT_BAR_BUTTON_BOLD = "bold";

private String mParamTitle;
private String mParamContent;
private WebView mWebView;

private Activity mActivity;
private EditorWebView mWebView;

private final Map<String, ToggleButton> mTagToggleButtonMap = new HashMap<>();

public static EditorFragment newInstance(String title, String content) {
EditorFragment fragment = new EditorFragment();
Expand All @@ -53,18 +59,23 @@ public EditorFragment() {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivity = getActivity();
if (getArguments() != null) {
mParamTitle = getArguments().getString(ARG_PARAM_TITLE);
mParamContent = getArguments().getString(ARG_PARAM_CONTENT);
}
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_editor, container, false);
mWebView = (WebView) view.findViewById(R.id.webview);
mWebView = (EditorWebView) view.findViewById(R.id.webview);
initWebView();

ToggleButton boldButton = (ToggleButton) view.findViewById(R.id.bold);
boldButton.setOnClickListener(this);
mTagToggleButtonMap.put(TAG_FORMAT_BAR_BUTTON_BOLD, boldButton);

return view;
}

Expand All @@ -84,7 +95,8 @@ public void onReceivedError(WebView view, int errorCode, String description, Str
}
});
mWebView.setWebChromeClient(new WebChromeClient() {
public boolean onConsoleMessage(ConsoleMessage cm) {
@Override
public boolean onConsoleMessage(@NonNull ConsoleMessage cm) {
AppLog.d(T.EDITOR, cm.message() + " -- From line " + cm.lineNumber() + " of " + cm.sourceId());
return true;
}
Expand All @@ -94,46 +106,22 @@ public boolean onJsAlert(WebView view, String url, String message, JsResult resu
AppLog.d(T.EDITOR, message);
return true;
}

@Override
public void onConsoleMessage(String message, int lineNumber, String sourceId) {
AppLog.d(T.EDITOR, message + " -- from line " + lineNumber + " of " + sourceId);
}
});

String htmlEditor = getHtmlFromFile("android-editor.html");
String htmlEditor = Utils.getHtmlFromFile(mActivity, "android-editor.html");

mWebView.addJavascriptInterface(new JsCallbackHandler(), JS_CALLBACK_HANDLER);
mWebView.addJavascriptInterface(new JsCallbackReceiver(this), JS_CALLBACK_HANDLER);

mWebView.loadDataWithBaseURL("file:///android_asset/", htmlEditor, "text/html", "utf-8", "");

enableWebDebugging(true);
}

private String getStringFromAsset(String filename) throws IOException {
if (!isAdded()) {
return null;
}
AssetManager assetManager = getActivity().getAssets();
InputStream in = assetManager.open(filename);
InputStreamReader is = new InputStreamReader(in);
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(is);
String read = br.readLine();
while (read != null) {
sb.append(read);
sb.append('\n');
read = br.readLine();
}
return sb.toString();
}

private String getHtmlFromFile(String filename) {
try {
return getStringFromAsset(filename);
} catch (IOException e) {
AppLog.e(T.EDITOR, e.getMessage());
return null;
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.bold) {
mWebView.execJavaScriptFromString("ZSSEditor.setBold();");
}
}

Expand Down Expand Up @@ -182,24 +170,34 @@ public Spanned getSpannedContent() {
return null;
}

class JsCallbackHandler {
@JavascriptInterface
public void executeCallback(final String callbackId) {
if (callbackId.equals("callback-dom-loaded")) {
// Run on UI thread
mWebView.post(new Runnable() {
public void run() {
String title = "I'm editing a post!";
String contentHtml = getHtmlFromFile("example-content.html");

// Load example content into editor
mWebView.loadUrl("javascript:ZSSEditor.getField('zss_field_title').setHTML('" +
Utils.escapeHtml(title) + "');");
mWebView.loadUrl("javascript:ZSSEditor.getField('zss_field_content').setHTML('" +
Utils.escapeHtml(contentHtml) + "');");
public void onDomLoaded() {
mWebView.post(new Runnable() {
public void run() {
String title = "I'm editing a post!";
String contentHtml = Utils.getHtmlFromFile(mActivity, "example-content.html");

mWebView.execJavaScriptFromString("ZSSEditor.getField('zss_field_content').setMultiline('true');");

// Load example content into editor
mWebView.execJavaScriptFromString("ZSSEditor.getField('zss_field_title').setHTML('" +
Utils.escapeHtml(title) + "');");
mWebView.execJavaScriptFromString("ZSSEditor.getField('zss_field_content').setHTML('" +
Utils.escapeHtml(contentHtml) + "');");
}
});
}

public void onSelectionStyleChanged(final Map<String, Boolean> changeMap) {
mWebView.post(new Runnable() {
public void run() {
for (Map.Entry<String, Boolean> entry : changeMap.entrySet()) {
// Handle toggling format bar style buttons
ToggleButton button = mTagToggleButtonMap.get(entry.getKey());
if (button != null) {
button.setChecked(entry.getValue());
}
});
}
}
}
});
}
}
Loading

0 comments on commit 6195fe0

Please sign in to comment.