Skip to content

Commit 33111c4

Browse files
committed
Squashed 'libs/editor/' changes from e0a8651..244a513
244a513 Removed unused ModalDialog style 2079b91 Removed styling of legacy.EditLinkActivity, leaving it up to WPAndroid bcfe5db 0.3 version bump 13d8db4 Merge pull request #226 from wordpress-mobile/issue/210-blockquote-list-wrap 42a4a5e Fixed confusing comment c103bdb Updates blockquoting after "double enter" to end a list; refactoed list code 331ea9b Merge branch 'develop' of github.com:wordpress-mobile/WordPress-Editor-Android into issue/210-blockquote-list-wrap 4092508 Merge pull request #225 from wordpress-mobile/feature/186-edit-link 7520bdf First crack at fixing blockquote and list bug e3b1f23 Merge branch 'develop' into feature/186-edit-link 7347f12 Disabled all links in the WebView, so tapping links on API<19 doesn't cause the WebView to navigate to them 21d7817 Changed the link button to remove the current link instead of displaying the link dialog 3ac9fab Added a delete button to LinkDialogFragment which removes the link being updated c5b64e5 Updated the editor to support modifying links when tapping them a484307 Added tests for utility methods decodeHtml and splitValuePairDelimitedString bd30fac Added handling for callback-link-tap 87e3028 Improved the process of extracting key-value pairs from callback strings 7d5f231 Merge pull request #220 from wordpress-mobile/issue/197-rotation-redraw f579c2e Escape HTML in link url and text before passing to ZSSEditor.insertLink() c306a83 Merge branch 'develop' into issue/197-rotation-redraw 845833c Merge pull request #217 from wordpress-mobile/issue/213-Merriweather-consistency 1a9beb4 Merge pull request #216 from wordpress-mobile/issue/215-legacy-toggle-button-size 5c2ee5b Merge pull request #221 from wordpress-mobile/issue/218-api16-html-crash 943542c Merge pull request #214 from wordpress-mobile/feature/185-insert-link 35f12c6 Update HtmlStyleUtils to not apply spans when running Android 4.1 and 4.1.1 74034ed Updated the editor to update HTML mode margins on rotation 25e378f Preserve the active format bar buttons on orientation change 9b97b2d Updated the editor to reload the format bar on rotation 09196d3 Changed the example activity to preserve the editor fragment on rotation, imitating the WPAndroid app c963b80 Extracted format bar layout into width-specific XML files 8ab72ac Updated Merriweather font files 17a8fce Reverted changes to legacy format bar f33c8b5 Moved EditLinkActivity back to legacy folder b07a7ba Switched to using a DialogFragment for the link dialog b863118 Improved recognition of link style 3fbccd6 Added support for inserting links in HTML mode ae5c2aa Added support for adding a link to existing, highlighted text in visual mode 56a0e82 Changed ZSSEditor.insertLink() to use execCommand instead of document.createElement 8e146f6 Added support for retrieving the selected text from the ZSSEditor 54555f7 Added support for inserting links using the format bar button (no highlight support) 1824cf6 Updated onSelectionStyleChanged to recognize links and toggle the link button highlighting accordingly 70c2ac0 Moved EditLinkActivity out of legacy folder dca254f Changed EditLinkActivity to a modal dialog (replicating its appearance within WPAndroid) 0cdabf4 Merge pull request #209 from wordpress-mobile/issue/203-fix-legacy-format-bar e8c7d2c Merge pull request #211 from wordpress-mobile/issue/208-html-mode-content-placeholder 34e32fa Set the content placeholder in HTML mode, wrapped in p tags c39031e Defined a separate dimen for the legacy format bar height and set it back to 40dp git-subtree-dir: libs/editor git-subtree-split: 244a51315e24eddc0510b422a75e6483c6c0f6f3
1 parent f9336b1 commit 33111c4

32 files changed

+1063
-751
lines changed

WordPressEditor/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ android {
2525
buildToolsVersion "22.0.1"
2626

2727
defaultConfig {
28-
versionCode 2
29-
versionName "0.2"
28+
versionCode 3
29+
versionName "0.3"
3030
minSdkVersion 14
3131
targetSdkVersion 22
3232
}

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

Lines changed: 226 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import android.annotation.SuppressLint;
44
import android.content.Context;
5+
import android.content.Intent;
56
import android.content.res.Configuration;
67
import android.os.Build;
78
import android.os.Bundle;
@@ -27,6 +28,7 @@
2728
import org.wordpress.android.util.helpers.MediaFile;
2829
import org.wordpress.android.util.helpers.MediaGallery;
2930

31+
import java.util.ArrayList;
3032
import java.util.HashMap;
3133
import java.util.Map;
3234
import java.util.concurrent.CountDownLatch;
@@ -53,13 +55,19 @@ public class EditorFragment extends EditorFragmentAbstract implements View.OnCli
5355
private SourceViewEditText mSourceViewTitle;
5456
private SourceViewEditText mSourceViewContent;
5557

58+
private int mSelectionStart;
59+
private int mSelectionEnd;
60+
5661
private String mTitlePlaceholder = "";
5762
private String mContentPlaceholder = "";
5863

5964
private boolean mHideActionBarOnSoftKeyboardUp;
6065

66+
private String mJavaScriptResult = "";
67+
6168
private CountDownLatch mGetTitleCountDownLatch;
6269
private CountDownLatch mGetContentCountDownLatch;
70+
private CountDownLatch mGetSelectedTextCountDownLatch;
6371

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

@@ -138,42 +146,11 @@ public void onFocusChange(View v, boolean hasFocus) {
138146
mSourceViewContent.addTextChangedListener(new HtmlStyleTextWatcher());
139147

140148
mSourceViewTitle.setHint(mTitlePlaceholder);
149+
mSourceViewContent.setHint("<p>" + mContentPlaceholder + "</p>");
141150

142151
// -- Format bar configuration
143152

144-
ToggleButton boldButton = (ToggleButton) view.findViewById(R.id.format_bar_button_bold);
145-
mTagToggleButtonMap.put(getString(R.string.format_bar_tag_bold), boldButton);
146-
147-
ToggleButton italicButton = (ToggleButton) view.findViewById(R.id.format_bar_button_italic);
148-
mTagToggleButtonMap.put(getString(R.string.format_bar_tag_italic), italicButton);
149-
150-
ToggleButton quoteButton = (ToggleButton) view.findViewById(R.id.format_bar_button_quote);
151-
mTagToggleButtonMap.put(getString(R.string.format_bar_tag_blockquote), quoteButton);
152-
153-
ToggleButton ulButton = (ToggleButton) view.findViewById(R.id.format_bar_button_ul);
154-
mTagToggleButtonMap.put(getString(R.string.format_bar_tag_unorderedList), ulButton);
155-
156-
ToggleButton olButton = (ToggleButton) view.findViewById(R.id.format_bar_button_ol);
157-
mTagToggleButtonMap.put(getString(R.string.format_bar_tag_orderedList), olButton);
158-
159-
// Tablet-only
160-
ToggleButton strikethroughButton = (ToggleButton) view.findViewById(R.id.format_bar_button_strikethrough);
161-
if (strikethroughButton != null) {
162-
mTagToggleButtonMap.put(getString(R.string.format_bar_tag_strikethrough), strikethroughButton);
163-
}
164-
165-
ToggleButton mediaButton = (ToggleButton) view.findViewById(R.id.format_bar_button_media);
166-
mTagToggleButtonMap.put(TAG_FORMAT_BAR_BUTTON_MEDIA, mediaButton);
167-
168-
ToggleButton linkButton = (ToggleButton) view.findViewById(R.id.format_bar_button_link);
169-
mTagToggleButtonMap.put(TAG_FORMAT_BAR_BUTTON_LINK, linkButton);
170-
171-
ToggleButton htmlButton = (ToggleButton) view.findViewById(R.id.format_bar_button_html);
172-
htmlButton.setOnClickListener(this);
173-
174-
for (ToggleButton button : mTagToggleButtonMap.values()) {
175-
button.setOnClickListener(this);
176-
}
153+
setupFormatBarButtonMap(view);
177154

178155
return view;
179156
}
@@ -199,6 +176,56 @@ private ActionBar getActionBar() {
199176
public void onConfigurationChanged(Configuration newConfig) {
200177
super.onConfigurationChanged(newConfig);
201178

179+
if (getView() != null) {
180+
// Reload the format bar to make sure the correct one for the new screen width is being used
181+
View formatBar = getView().findViewById(R.id.format_bar);
182+
183+
if (formatBar != null) {
184+
// Remember the currently active format bar buttons so they can be re-activated after the reload
185+
ArrayList<String> activeTags = new ArrayList<>();
186+
for (Map.Entry<String, ToggleButton> entry : mTagToggleButtonMap.entrySet()) {
187+
if (entry.getValue().isChecked()) {
188+
activeTags.add(entry.getKey());
189+
}
190+
}
191+
192+
ViewGroup parent = (ViewGroup) formatBar.getParent();
193+
parent.removeView(formatBar);
194+
195+
formatBar = getActivity().getLayoutInflater().inflate(R.layout.format_bar, parent, false);
196+
formatBar.setId(R.id.format_bar);
197+
parent.addView(formatBar);
198+
199+
setupFormatBarButtonMap(formatBar);
200+
201+
// Restore the active format bar buttons
202+
for (String tag : activeTags) {
203+
mTagToggleButtonMap.get(tag).setChecked(true);
204+
}
205+
206+
if (mSourceView.getVisibility() == View.VISIBLE) {
207+
ToggleButton htmlButton = (ToggleButton) formatBar.findViewById(R.id.format_bar_button_html);
208+
htmlButton.setChecked(true);
209+
}
210+
}
211+
212+
// Reload HTML mode margins
213+
View sourceViewTitle = getView().findViewById(R.id.sourceview_title);
214+
View sourceViewContent = getView().findViewById(R.id.sourceview_content);
215+
216+
if (sourceViewTitle != null && sourceViewContent != null) {
217+
int sideMargin = (int) getActivity().getResources().getDimension(R.dimen.sourceview_side_margin);
218+
219+
ViewGroup.MarginLayoutParams titleParams =
220+
(ViewGroup.MarginLayoutParams) sourceViewTitle.getLayoutParams();
221+
ViewGroup.MarginLayoutParams contentParams =
222+
(ViewGroup.MarginLayoutParams) sourceViewContent.getLayoutParams();
223+
224+
titleParams.setMargins(sideMargin, titleParams.topMargin, sideMargin, titleParams.bottomMargin);
225+
contentParams.setMargins(sideMargin, contentParams.topMargin, sideMargin, contentParams.bottomMargin);
226+
}
227+
}
228+
202229
// Toggle action bar auto-hiding for the new orientation
203230
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
204231
&& !getResources().getBoolean(R.bool.is_large_tablet_landscape)) {
@@ -210,6 +237,42 @@ public void onConfigurationChanged(Configuration newConfig) {
210237
}
211238
}
212239

240+
private void setupFormatBarButtonMap(View view) {
241+
ToggleButton boldButton = (ToggleButton) view.findViewById(R.id.format_bar_button_bold);
242+
mTagToggleButtonMap.put(getString(R.string.format_bar_tag_bold), boldButton);
243+
244+
ToggleButton italicButton = (ToggleButton) view.findViewById(R.id.format_bar_button_italic);
245+
mTagToggleButtonMap.put(getString(R.string.format_bar_tag_italic), italicButton);
246+
247+
ToggleButton quoteButton = (ToggleButton) view.findViewById(R.id.format_bar_button_quote);
248+
mTagToggleButtonMap.put(getString(R.string.format_bar_tag_blockquote), quoteButton);
249+
250+
ToggleButton ulButton = (ToggleButton) view.findViewById(R.id.format_bar_button_ul);
251+
mTagToggleButtonMap.put(getString(R.string.format_bar_tag_unorderedList), ulButton);
252+
253+
ToggleButton olButton = (ToggleButton) view.findViewById(R.id.format_bar_button_ol);
254+
mTagToggleButtonMap.put(getString(R.string.format_bar_tag_orderedList), olButton);
255+
256+
// Tablet-only
257+
ToggleButton strikethroughButton = (ToggleButton) view.findViewById(R.id.format_bar_button_strikethrough);
258+
if (strikethroughButton != null) {
259+
mTagToggleButtonMap.put(getString(R.string.format_bar_tag_strikethrough), strikethroughButton);
260+
}
261+
262+
ToggleButton mediaButton = (ToggleButton) view.findViewById(R.id.format_bar_button_media);
263+
mTagToggleButtonMap.put(TAG_FORMAT_BAR_BUTTON_MEDIA, mediaButton);
264+
265+
ToggleButton linkButton = (ToggleButton) view.findViewById(R.id.format_bar_button_link);
266+
mTagToggleButtonMap.put(TAG_FORMAT_BAR_BUTTON_LINK, linkButton);
267+
268+
ToggleButton htmlButton = (ToggleButton) view.findViewById(R.id.format_bar_button_html);
269+
htmlButton.setOnClickListener(this);
270+
271+
for (ToggleButton button : mTagToggleButtonMap.values()) {
272+
button.setOnClickListener(this);
273+
}
274+
}
275+
213276
protected void initJsEditor() {
214277
if(!isAdded()) {
215278
return;
@@ -261,8 +324,42 @@ public void onClick(View v) {
261324
// TODO: Handle inserting media
262325
((ToggleButton) v).setChecked(false);
263326
} else if (id == R.id.format_bar_button_link) {
264-
// TODO: Handle inserting a link
327+
if (!((ToggleButton) v).isChecked()) {
328+
// The link button was checked when it was pressed; remove the current link
329+
mWebView.execJavaScriptFromString("ZSSEditor.unlink();");
330+
return;
331+
}
332+
265333
((ToggleButton) v).setChecked(false);
334+
335+
LinkDialogFragment linkDialogFragment = new LinkDialogFragment();
336+
linkDialogFragment.setTargetFragment(this, LinkDialogFragment.LINK_DIALOG_REQUEST_CODE_ADD);
337+
338+
Bundle dialogBundle = new Bundle();
339+
340+
// Pass selected text to dialog
341+
if (mSourceView.getVisibility() == View.VISIBLE) {
342+
// HTML mode
343+
mSelectionStart = mSourceViewContent.getSelectionStart();
344+
mSelectionEnd = mSourceViewContent.getSelectionEnd();
345+
346+
String selectedText = mSourceViewContent.getText().toString().substring(mSelectionStart, mSelectionEnd);
347+
dialogBundle.putString("linkText", selectedText);
348+
} else {
349+
// Visual mode
350+
mGetSelectedTextCountDownLatch = new CountDownLatch(1);
351+
mWebView.execJavaScriptFromString("ZSSEditor.execFunctionForResult('getSelectedText');");
352+
try {
353+
if (mGetSelectedTextCountDownLatch.await(1, TimeUnit.SECONDS)) {
354+
dialogBundle.putString("linkText", mJavaScriptResult);
355+
}
356+
} catch (InterruptedException e) {
357+
AppLog.d(T.EDITOR, "Failed to obtain selected text from JS editor.");
358+
}
359+
}
360+
361+
linkDialogFragment.setArguments(dialogBundle);
362+
linkDialogFragment.show(getFragmentManager(), "LinkDialogFragment");
266363
} else {
267364
if (v instanceof ToggleButton) {
268365
onFormattingButtonClicked((ToggleButton) v);
@@ -288,6 +385,61 @@ public void onImeBack() {
288385
showActionBarIfNeeded();
289386
}
290387

388+
@Override
389+
public void onActivityResult(int requestCode, int resultCode, Intent data) {
390+
super.onActivityResult(requestCode, resultCode, data);
391+
392+
if ((requestCode == LinkDialogFragment.LINK_DIALOG_REQUEST_CODE_ADD ||
393+
requestCode == LinkDialogFragment.LINK_DIALOG_REQUEST_CODE_UPDATE)) {
394+
395+
if (resultCode == LinkDialogFragment.LINK_DIALOG_REQUEST_CODE_DELETE) {
396+
mWebView.execJavaScriptFromString("ZSSEditor.unlink();");
397+
return;
398+
}
399+
400+
if (data == null) {
401+
return;
402+
}
403+
404+
Bundle extras = data.getExtras();
405+
if (extras == null) {
406+
return;
407+
}
408+
409+
String linkUrl = extras.getString("linkURL");
410+
String linkText = extras.getString("linkText");
411+
412+
if (linkText == null || linkText.equals("")) {
413+
linkText = linkUrl;
414+
}
415+
416+
if (mSourceView.getVisibility() == View.VISIBLE) {
417+
Editable content = mSourceViewContent.getText();
418+
if (content == null) {
419+
return;
420+
}
421+
422+
if (mSelectionStart < mSelectionEnd) {
423+
content.delete(mSelectionStart, mSelectionEnd);
424+
}
425+
426+
String urlHtml = "<a href=\"" + linkUrl + "\">" + linkText + "</a>";
427+
428+
content.insert(mSelectionStart, urlHtml);
429+
mSourceViewContent.setSelection(mSelectionStart + urlHtml.length());
430+
} else {
431+
String jsMethod;
432+
if (requestCode == LinkDialogFragment.LINK_DIALOG_REQUEST_CODE_ADD) {
433+
jsMethod = "ZSSEditor.insertLink";
434+
} else {
435+
jsMethod = "ZSSEditor.updateLink";
436+
}
437+
mWebView.execJavaScriptFromString(jsMethod + "('" + Utils.escapeHtml(linkUrl) + "', '" +
438+
Utils.escapeHtml(linkText) + "');");
439+
}
440+
}
441+
}
442+
291443
@SuppressLint("NewApi")
292444
private void enableWebDebugging(boolean enable) {
293445
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
@@ -467,20 +619,47 @@ public void run() {
467619
});
468620
}
469621

622+
public void onLinkTapped(String url, String title) {
623+
LinkDialogFragment linkDialogFragment = new LinkDialogFragment();
624+
linkDialogFragment.setTargetFragment(this, LinkDialogFragment.LINK_DIALOG_REQUEST_CODE_UPDATE);
625+
626+
Bundle dialogBundle = new Bundle();
627+
628+
dialogBundle.putString("linkURL", url);
629+
dialogBundle.putString("linkText", title);
630+
631+
linkDialogFragment.setArguments(dialogBundle);
632+
linkDialogFragment.show(getFragmentManager(), "LinkDialogFragment");
633+
}
634+
470635
public void onGetHtmlResponse(Map<String, String> inputArgs) {
471-
String fieldId = inputArgs.get("id");
472-
String fieldContents = inputArgs.get("contents");
473-
if (!fieldId.isEmpty()) {
474-
switch (fieldId) {
475-
case "zss_field_title":
476-
mTitle = fieldContents;
477-
mGetTitleCountDownLatch.countDown();
478-
break;
479-
case "zss_field_content":
480-
mContentHtml = fieldContents;
481-
mGetContentCountDownLatch.countDown();
482-
break;
483-
}
636+
String functionId = inputArgs.get("function");
637+
638+
if (functionId.isEmpty()) {
639+
return;
640+
}
641+
642+
switch (functionId) {
643+
case "getHTMLForCallback":
644+
String fieldId = inputArgs.get("id");
645+
String fieldContents = inputArgs.get("contents");
646+
if (!fieldId.isEmpty()) {
647+
switch (fieldId) {
648+
case "zss_field_title":
649+
mTitle = fieldContents;
650+
mGetTitleCountDownLatch.countDown();
651+
break;
652+
case "zss_field_content":
653+
mContentHtml = fieldContents;
654+
mGetContentCountDownLatch.countDown();
655+
break;
656+
}
657+
}
658+
break;
659+
case "getSelectedText":
660+
mJavaScriptResult = inputArgs.get("result");
661+
mGetSelectedTextCountDownLatch.countDown();
662+
break;
484663
}
485664
}
486665

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ private void configureWebView() {
3434
webSettings.setDefaultTextEncodingName("utf-8");
3535

3636
this.setWebViewClient(new WebViewClient() {
37+
@Override
38+
public boolean shouldOverrideUrlLoading(WebView view, String url) {
39+
return true;
40+
}
41+
42+
@Override
3743
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
3844
AppLog.e(AppLog.T.EDITOR, description);
3945
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import android.graphics.Color;
44
import android.graphics.Typeface;
5+
import android.os.Build;
56
import android.support.annotation.NonNull;
67
import android.text.Spannable;
78
import android.text.style.CharacterStyle;
@@ -77,6 +78,12 @@ public static void styleHtmlForDisplay(@NonNull Spannable content) {
7778
* @param end the index in {@code content} to style until
7879
*/
7980
public static void styleHtmlForDisplay(@NonNull Spannable content, int start, int end) {
81+
if (Build.VERSION.RELEASE.equals("4.1") || Build.VERSION.RELEASE.equals("4.1.1")) {
82+
// Avoids crashing bug in Android 4.1 and 4.1.1 triggered when spanned text is line-wrapped
83+
// AOSP issue: https://code.google.com/p/android/issues/detail?id=35466
84+
return;
85+
}
86+
8087
applySpansByRegex(content, start, end, REGEX_HTML_TAGS);
8188
applySpansByRegex(content, start, end, REGEX_HTML_ATTRIBUTES);
8289
applySpansByRegex(content, start, end, REGEX_HTML_COMMENTS);

0 commit comments

Comments
 (0)