Skip to content

Commit 6195fe0

Browse files
committed
Merge pull request #129 from wordpress-mobile/issue/23-extract-callback-handler
Issue #23: Extract callback handler
2 parents eaa2f17 + 228a563 commit 6195fe0

File tree

9 files changed

+471
-62
lines changed

9 files changed

+471
-62
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package org.wordpress.android.editor;
2+
3+
import android.util.Log;
4+
5+
import org.junit.Before;
6+
import org.junit.Test;
7+
import org.junit.runner.RunWith;
8+
import org.robolectric.RobolectricTestRunner;
9+
import org.robolectric.annotation.Config;
10+
import org.robolectric.shadows.ShadowLog;
11+
import org.wordpress.android.util.AppLog;
12+
13+
import java.util.List;
14+
15+
import static junit.framework.Assert.assertEquals;
16+
import static junit.framework.Assert.assertFalse;
17+
import static org.mockito.Mockito.mock;
18+
import static org.robolectric.shadows.ShadowLog.LogItem;
19+
20+
@Config(emulateSdk = 18)
21+
@RunWith(RobolectricTestRunner.class)
22+
public class JsCallbackHandlerTest {
23+
private final static String EDITOR_LOG_TAG = "WordPress-" + AppLog.T.EDITOR.toString();
24+
25+
private JsCallbackReceiver mJsCallbackReceiver;
26+
27+
@Before
28+
public void setUp() {
29+
EditorFragment editorFragment = mock(EditorFragment.class);
30+
mJsCallbackReceiver = new JsCallbackReceiver(editorFragment);
31+
}
32+
33+
@Test
34+
public void testCallbacksRecognized() {
35+
mJsCallbackReceiver.executeCallback("callback-dom-loaded", "");
36+
assertNotLogged("Unhandled callback");
37+
38+
mJsCallbackReceiver.executeCallback("callback-new-field", "field-name");
39+
assertNotLogged("Unhandled callback");
40+
41+
mJsCallbackReceiver.executeCallback("callback-input", "arguments");
42+
assertNotLogged("Unhandled callback");
43+
44+
mJsCallbackReceiver.executeCallback("callback-selection-changed", "arguments");
45+
assertNotLogged("Unhandled callback");
46+
47+
mJsCallbackReceiver.executeCallback("callback-selection-style", "arguments");
48+
assertNotLogged("Unhandled callback");
49+
50+
mJsCallbackReceiver.executeCallback("callback-focus-in", "");
51+
assertNotLogged("Unhandled callback");
52+
53+
mJsCallbackReceiver.executeCallback("callback-focus-out", "");
54+
assertNotLogged("Unhandled callback");
55+
56+
mJsCallbackReceiver.executeCallback("callback-image-replaced", "arguments");
57+
assertNotLogged("Unhandled callback");
58+
59+
mJsCallbackReceiver.executeCallback("callback-image-tap", "arguments");
60+
assertNotLogged("Unhandled callback");
61+
62+
mJsCallbackReceiver.executeCallback("callback-link-tap", "arguments");
63+
assertNotLogged("Unhandled callback");
64+
65+
mJsCallbackReceiver.executeCallback("callback-log", "arguments");
66+
assertNotLogged("Unhandled callback");
67+
}
68+
69+
@Test
70+
public void testUnknownCallbackShouldBeLogged() {
71+
mJsCallbackReceiver.executeCallback("callback-does-not-exist", "content");
72+
assertLogged(Log.DEBUG, EDITOR_LOG_TAG, "Unhandled callback: callback-does-not-exist:content", null);
73+
}
74+
75+
@Test
76+
public void testCallbackLog() {
77+
mJsCallbackReceiver.executeCallback("callback-log", "msg=test-message");
78+
assertLogged(Log.DEBUG, EDITOR_LOG_TAG, "callback-log: test-message", null);
79+
}
80+
81+
private void assertLogged(int type, String tag, String msg, Throwable throwable) {
82+
LogItem lastLog = ShadowLog.getLogs().get(0);
83+
assertEquals(type, lastLog.type);
84+
assertEquals(msg, lastLog.msg);
85+
assertEquals(tag, lastLog.tag);
86+
assertEquals(throwable, lastLog.throwable);
87+
}
88+
89+
private void assertNotLogged(String msg) {
90+
List<LogItem> logList = ShadowLog.getLogs();
91+
if (!logList.isEmpty()) {
92+
assertFalse(logList.get(0).msg.contains(msg));
93+
ShadowLog.reset();
94+
}
95+
}
96+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.wordpress.android.editor;
2+
3+
import org.junit.Test;
4+
import org.junit.runner.RunWith;
5+
import org.robolectric.RobolectricTestRunner;
6+
import org.robolectric.annotation.Config;
7+
8+
import java.util.Collections;
9+
import java.util.HashMap;
10+
import java.util.HashSet;
11+
import java.util.Map;
12+
import java.util.Set;
13+
14+
import static org.junit.Assert.assertEquals;
15+
import static org.wordpress.android.editor.Utils.getChangeMapFromSets;
16+
import static org.wordpress.android.editor.Utils.splitDelimitedString;
17+
18+
@Config(emulateSdk = 18)
19+
@RunWith(RobolectricTestRunner.class)
20+
public class UtilsTest {
21+
22+
@Test
23+
public void testSplitDelimitedString() {
24+
Set<String> splitString = new HashSet<>();
25+
26+
// Test normal usage
27+
splitString.add("p");
28+
splitString.add("bold");
29+
splitString.add("justifyLeft");
30+
31+
assertEquals(splitString, splitDelimitedString("p~bold~justifyLeft", "~"));
32+
33+
// Test empty string
34+
assertEquals(Collections.emptySet(), splitDelimitedString("", "~"));
35+
}
36+
37+
@Test
38+
public void testGetChangeMapFromSets() {
39+
Set<String> oldSet = new HashSet<>();
40+
Set<String> newSet = new HashSet<>();
41+
Map<String, Boolean> expectedMap = new HashMap<>();
42+
43+
// Test normal usage
44+
oldSet.add("p");
45+
oldSet.add("bold");
46+
oldSet.add("justifyLeft");
47+
48+
newSet.add("p");
49+
newSet.add("justifyRight");
50+
51+
expectedMap.put("bold", false);
52+
expectedMap.put("justifyLeft", false);
53+
expectedMap.put("justifyRight", true);
54+
55+
assertEquals(expectedMap, getChangeMapFromSets(oldSet, newSet));
56+
57+
// Test no changes
58+
oldSet.clear();
59+
oldSet.add("p");
60+
oldSet.add("bold");
61+
62+
newSet.clear();
63+
newSet.add("p");
64+
newSet.add("bold");
65+
66+
assertEquals(Collections.emptyMap(), getChangeMapFromSets(oldSet, newSet));
67+
68+
// Test empty sets
69+
assertEquals(Collections.emptyMap(), getChangeMapFromSets(Collections.emptySet(), Collections.emptySet()));
70+
}
71+
}

WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragment.java

Lines changed: 58 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
package org.wordpress.android.editor;
22

33
import android.annotation.SuppressLint;
4-
import android.content.res.AssetManager;
4+
import android.app.Activity;
55
import android.os.Build;
66
import android.os.Bundle;
7+
import android.support.annotation.NonNull;
78
import android.text.Spanned;
89
import android.view.LayoutInflater;
910
import android.view.View;
1011
import android.view.ViewGroup;
1112
import android.webkit.ConsoleMessage;
12-
import android.webkit.JavascriptInterface;
1313
import android.webkit.JsResult;
1414
import android.webkit.WebChromeClient;
1515
import android.webkit.WebSettings;
1616
import android.webkit.WebView;
1717
import android.webkit.WebViewClient;
18+
import android.widget.ToggleButton;
1819

1920
import com.android.volley.toolbox.ImageLoader;
2021

@@ -23,20 +24,25 @@
2324
import org.wordpress.android.util.helpers.MediaFile;
2425
import org.wordpress.android.util.helpers.MediaGallery;
2526

26-
import java.io.BufferedReader;
27-
import java.io.IOException;
28-
import java.io.InputStream;
29-
import java.io.InputStreamReader;
27+
import java.util.HashMap;
28+
import java.util.Map;
3029

31-
public class EditorFragment extends EditorFragmentAbstract {
30+
public class EditorFragment extends EditorFragmentAbstract implements View.OnClickListener,
31+
OnJsEditorStateChangedListener {
3232
private static final String ARG_PARAM_TITLE = "param_title";
3333
private static final String ARG_PARAM_CONTENT = "param_content";
3434

3535
private static final String JS_CALLBACK_HANDLER = "nativeCallbackHandler";
3636

37+
private static final String TAG_FORMAT_BAR_BUTTON_BOLD = "bold";
38+
3739
private String mParamTitle;
3840
private String mParamContent;
39-
private WebView mWebView;
41+
42+
private Activity mActivity;
43+
private EditorWebView mWebView;
44+
45+
private final Map<String, ToggleButton> mTagToggleButtonMap = new HashMap<>();
4046

4147
public static EditorFragment newInstance(String title, String content) {
4248
EditorFragment fragment = new EditorFragment();
@@ -53,18 +59,23 @@ public EditorFragment() {
5359
@Override
5460
public void onCreate(Bundle savedInstanceState) {
5561
super.onCreate(savedInstanceState);
62+
mActivity = getActivity();
5663
if (getArguments() != null) {
5764
mParamTitle = getArguments().getString(ARG_PARAM_TITLE);
5865
mParamContent = getArguments().getString(ARG_PARAM_CONTENT);
5966
}
6067
}
6168

6269
@Override
63-
public View onCreateView(LayoutInflater inflater, ViewGroup container,
64-
Bundle savedInstanceState) {
70+
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
6571
View view = inflater.inflate(R.layout.fragment_editor, container, false);
66-
mWebView = (WebView) view.findViewById(R.id.webview);
72+
mWebView = (EditorWebView) view.findViewById(R.id.webview);
6773
initWebView();
74+
75+
ToggleButton boldButton = (ToggleButton) view.findViewById(R.id.bold);
76+
boldButton.setOnClickListener(this);
77+
mTagToggleButtonMap.put(TAG_FORMAT_BAR_BUTTON_BOLD, boldButton);
78+
6879
return view;
6980
}
7081

@@ -84,7 +95,8 @@ public void onReceivedError(WebView view, int errorCode, String description, Str
8495
}
8596
});
8697
mWebView.setWebChromeClient(new WebChromeClient() {
87-
public boolean onConsoleMessage(ConsoleMessage cm) {
98+
@Override
99+
public boolean onConsoleMessage(@NonNull ConsoleMessage cm) {
88100
AppLog.d(T.EDITOR, cm.message() + " -- From line " + cm.lineNumber() + " of " + cm.sourceId());
89101
return true;
90102
}
@@ -94,46 +106,22 @@ public boolean onJsAlert(WebView view, String url, String message, JsResult resu
94106
AppLog.d(T.EDITOR, message);
95107
return true;
96108
}
97-
98-
@Override
99-
public void onConsoleMessage(String message, int lineNumber, String sourceId) {
100-
AppLog.d(T.EDITOR, message + " -- from line " + lineNumber + " of " + sourceId);
101-
}
102109
});
103110

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

106-
mWebView.addJavascriptInterface(new JsCallbackHandler(), JS_CALLBACK_HANDLER);
113+
mWebView.addJavascriptInterface(new JsCallbackReceiver(this), JS_CALLBACK_HANDLER);
107114

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

110117
enableWebDebugging(true);
111118
}
112119

113-
private String getStringFromAsset(String filename) throws IOException {
114-
if (!isAdded()) {
115-
return null;
116-
}
117-
AssetManager assetManager = getActivity().getAssets();
118-
InputStream in = assetManager.open(filename);
119-
InputStreamReader is = new InputStreamReader(in);
120-
StringBuilder sb = new StringBuilder();
121-
BufferedReader br = new BufferedReader(is);
122-
String read = br.readLine();
123-
while (read != null) {
124-
sb.append(read);
125-
sb.append('\n');
126-
read = br.readLine();
127-
}
128-
return sb.toString();
129-
}
130-
131-
private String getHtmlFromFile(String filename) {
132-
try {
133-
return getStringFromAsset(filename);
134-
} catch (IOException e) {
135-
AppLog.e(T.EDITOR, e.getMessage());
136-
return null;
120+
@Override
121+
public void onClick(View v) {
122+
int id = v.getId();
123+
if (id == R.id.bold) {
124+
mWebView.execJavaScriptFromString("ZSSEditor.setBold();");
137125
}
138126
}
139127

@@ -182,24 +170,34 @@ public Spanned getSpannedContent() {
182170
return null;
183171
}
184172

185-
class JsCallbackHandler {
186-
@JavascriptInterface
187-
public void executeCallback(final String callbackId) {
188-
if (callbackId.equals("callback-dom-loaded")) {
189-
// Run on UI thread
190-
mWebView.post(new Runnable() {
191-
public void run() {
192-
String title = "I'm editing a post!";
193-
String contentHtml = getHtmlFromFile("example-content.html");
194-
195-
// Load example content into editor
196-
mWebView.loadUrl("javascript:ZSSEditor.getField('zss_field_title').setHTML('" +
197-
Utils.escapeHtml(title) + "');");
198-
mWebView.loadUrl("javascript:ZSSEditor.getField('zss_field_content').setHTML('" +
199-
Utils.escapeHtml(contentHtml) + "');");
173+
public void onDomLoaded() {
174+
mWebView.post(new Runnable() {
175+
public void run() {
176+
String title = "I'm editing a post!";
177+
String contentHtml = Utils.getHtmlFromFile(mActivity, "example-content.html");
178+
179+
mWebView.execJavaScriptFromString("ZSSEditor.getField('zss_field_content').setMultiline('true');");
180+
181+
// Load example content into editor
182+
mWebView.execJavaScriptFromString("ZSSEditor.getField('zss_field_title').setHTML('" +
183+
Utils.escapeHtml(title) + "');");
184+
mWebView.execJavaScriptFromString("ZSSEditor.getField('zss_field_content').setHTML('" +
185+
Utils.escapeHtml(contentHtml) + "');");
186+
}
187+
});
188+
}
189+
190+
public void onSelectionStyleChanged(final Map<String, Boolean> changeMap) {
191+
mWebView.post(new Runnable() {
192+
public void run() {
193+
for (Map.Entry<String, Boolean> entry : changeMap.entrySet()) {
194+
// Handle toggling format bar style buttons
195+
ToggleButton button = mTagToggleButtonMap.get(entry.getKey());
196+
if (button != null) {
197+
button.setChecked(entry.getValue());
200198
}
201-
});
199+
}
202200
}
203-
}
201+
});
204202
}
205203
}

0 commit comments

Comments
 (0)