Skip to content

Commit 9907504

Browse files
committed
Squashed 'libs/editor/' changes from 17887d3..8818dd1
8818dd1 Merge pull request #360 from wordpress-mobile/issue/344-remove-blockquotes a5b021d Check that parent blockquote is the first thing inside the post before applying afterKeyDownEvent fix ed096e2 Allow individual checks in afterKeyDownEvent to determine if they should go through if the HTML hasn't changed 2206af5 In afterKeyDownEvent, compare before and after HTML and quit if they're the same 23ec38e Refactored afterKeyDownEvent for readability ae65514 Added generic giveFocusToElement method cb8cccd Fixed an issue where backspacing doesn't remove a blockquote when it's at the start of a post with more content below it 8f20ff5 Added ZSSField.afterKeyDownEvent, which is called after the results of a keydown event have taken effect c949332 Merge branch 'develop' of https://github.com/wordpress-mobile/WordPress-Editor-Android into develop 7ce2aa7 Merge branch 'issue/315-paste-fixes' into develop a00e762 Merge pull request #358 from wordpress-mobile/issue/315-paste-fixes 4a6b534 Merge pull request #357 from wordpress-mobile/issue/350-fix-newlines-api16 edf8b4f Fixed an issue with incorrect paragraph wrapping when starting a post with a blockquote 822ce8f update comment b8a90b2 Fix newlines when switching to HTML mode on API <= 16 26b7be5 Merge commit 'ac1a4af3be8b6e95d1799ce2f82fca06a73577c0' into develop 1215bdd Make sure text is wrapped in paragraph tags when adding blockquotes f16e222 Wrap hardware pastes in paragraph tags in empty posts 5e9559f Fix software paste on API<19 by wrapping the paste in paragraph tags after the fact, when a new line is entered 61e5cf3 Wrap software pastes in paragraph tags for API19+ for empty posts 458b6cf Merge pull request #3967 from wordpress-mobile/issue/328editor-track-reflection-failure 9e112be Merge pull request #3961 from wordpress-mobile/issue/3930-crash-when-adding-invalid-image cf7b400 Merge commit 'be4aac09995c4a35b2354854ef0f3ff5b1ae07e7' into develop 7885bca Use a static listener to handle reflection failures 182e5ef Updates the p/div conversion regex to more strictly match tags eba46dd Move the addMediaFile logic in an AsyncTask, only WPEditImageSpan construction now lives in a background thread git-subtree-dir: libs/editor git-subtree-split: 8818dd1d120140283da46b40b7ad7fc59d772def
1 parent ac1a4af commit 9907504

File tree

4 files changed

+213
-80
lines changed

4 files changed

+213
-80
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import java.io.IOException;
3030
import java.net.HttpURLConnection;
31+
import java.net.URLDecoder;
3132
import java.util.HashMap;
3233
import java.util.Map;
3334

@@ -60,7 +61,8 @@ private void configureWebView() {
6061
@Override
6162
public boolean shouldOverrideUrlLoading(WebView view, String url) {
6263
if (url != null && url.startsWith("callback") && mJsCallbackReceiver != null) {
63-
String[] split = url.split(":", 2);
64+
String data = URLDecoder.decode(url);
65+
String[] split = data.split(":", 2);
6466
String callbackId = split[0];
6567
String params = (split.length > 1 ? split[1] : "");
6668
mJsCallbackReceiver.executeCallback(callbackId, params);

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

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,52 @@
1818
* JavaScript, {@link #loadJavaScript(String)}, instead of {@link WebView#loadUrl(String)}. This is needed because
1919
* <code>WebView#loadUrl(String)</code> on API<19 eventually calls <code>WebViewClassic#hideSoftKeyboard()</code>,
2020
* hiding the keyboard whenever JavaScript is executed.</p>
21-
*
21+
* <p/>
2222
* <p>This class uses reflection to access the normally inaccessible <code>WebViewCore#sendMessage(Message)</code>
2323
* and use it to execute JavaScript, sidestepping <code>WebView#loadUrl(String)</code> and the keyboard issue.</p>
2424
*/
2525
@SuppressWarnings("TryWithIdenticalCatches")
2626
public class EditorWebViewCompatibility extends EditorWebViewAbstract {
27+
public interface ReflectionFailureListener {
28+
void onReflectionFailure(ReflectionException e);
29+
}
30+
31+
public class ReflectionException extends Exception {
32+
public ReflectionException(Throwable cause) {
33+
super(cause);
34+
}
35+
}
36+
2737
private static final int EXECUTE_JS = 194; // WebViewCore internal JS message code
2838

2939
private Object mWebViewCore;
3040
private Method mSendMessageMethod;
3141

42+
// Dirty static listener, but it's impossible to set the listener during the construction if we want to keep
43+
// the xml layout
44+
private static ReflectionFailureListener mReflectionFailureListener;
45+
private boolean mReflectionSucceed = true;
46+
47+
public static void setReflectionFailureListener(ReflectionFailureListener reflectionFailureListener) {
48+
mReflectionFailureListener = reflectionFailureListener;
49+
}
50+
3251
public EditorWebViewCompatibility(Context context, AttributeSet attrs) {
3352
super(context, attrs);
3453
try {
3554
this.initReflection();
3655
} catch (ReflectionException e) {
3756
AppLog.e(T.EDITOR, e);
38-
handleReflectionFailure();
57+
handleReflectionFailure(e);
3958
}
4059
}
4160

4261
private void initReflection() throws ReflectionException {
62+
if (!mReflectionSucceed) {
63+
// Reflection failed once already, it won't succeed on a second try
64+
return;
65+
}
4366
Object webViewProvider;
44-
4567
try {
4668
if (Build.VERSION.SDK_INT >= 16) {
4769
// On API >= 16, the WebViewCore instance is not defined inside WebView itself but inside a
@@ -58,7 +80,6 @@ private void initReflection() throws ReflectionException {
5880
mWebViewCore = webViewCoreField.get(webViewProvider);
5981
} else {
6082
// On API < 16, the WebViewCore is directly accessible from the WebView
61-
6283
// Access WebViewCore object
6384
Field webViewCoreField = WebView.class.getDeclaredField("mWebViewCore");
6485
webViewCoreField.setAccessible(true);
@@ -97,19 +118,22 @@ private void loadJavaScript(final String javaScript) throws ReflectionException
97118
public void execJavaScriptFromString(String javaScript) {
98119
try {
99120
loadJavaScript(javaScript);
100-
} catch(ReflectionException e) {
121+
} catch (ReflectionException e) {
101122
AppLog.e(T.EDITOR, e);
102-
handleReflectionFailure();
123+
handleReflectionFailure(e);
103124
}
104125
}
105126

106-
private void handleReflectionFailure() {
107-
// TODO: Fallback to legacy editor and pass the error to analytics
127+
private void handleReflectionFailure(ReflectionException e) {
128+
if (mReflectionFailureListener != null) {
129+
mReflectionFailureListener.onReflectionFailure(e);
130+
}
131+
mReflectionSucceed = false;
108132
}
109133

110-
public class ReflectionException extends Exception {
111-
public ReflectionException(Throwable cause) {
112-
super(cause);
113-
}
134+
@Override
135+
protected void onDetachedFromWindow() {
136+
super.onDetachedFromWindow();
137+
mReflectionFailureListener = null;
114138
}
115-
}
139+
}

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

Lines changed: 70 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99
import android.graphics.BitmapFactory;
1010
import android.graphics.Typeface;
1111
import android.net.Uri;
12+
import android.os.AsyncTask;
1213
import android.os.Bundle;
13-
import android.os.Handler;
14-
import android.os.Looper;
1514
import android.os.Parcelable;
1615
import android.support.v7.app.ActionBar;
1716
import android.support.v7.app.AppCompatActivity;
@@ -1023,66 +1022,83 @@ public void onSaveInstanceState(Bundle outState) {
10231022
outState.putString(KEY_CONTENT, mContentEditText.getText().toString());
10241023
}
10251024

1026-
public void addMediaFile(final MediaFile mediaFile, final String imageUrl, final ImageLoader imageLoader, final int start, final int end) {
1027-
mediaFile.setFileURL(imageUrl);
1028-
mediaFile.setFilePath(imageUrl);
1029-
final WPEditImageSpan imageSpan = createWPEditImageSpan(getActivity(), mediaFile);
1030-
mEditorFragmentListener.saveMediaFile(mediaFile);
1031-
imageSpan.setMediaFile(mediaFile);
1025+
private class AddMediaFileTask extends AsyncTask<Void, Void, WPEditImageSpan> {
1026+
private MediaFile mMediaFile;
1027+
private String mImageUrl;
1028+
private ImageLoader mImageLoader;
1029+
private int mStart;
1030+
private int mEnd;
1031+
1032+
public AddMediaFileTask(MediaFile mediaFile, String imageUrl, ImageLoader imageLoader, int start, int end) {
1033+
mMediaFile = mediaFile;
1034+
mImageUrl = imageUrl;
1035+
mImageLoader = imageLoader;
1036+
mStart = start;
1037+
mEnd = end;
1038+
}
10321039

1033-
Handler handler = new Handler(Looper.getMainLooper());
1034-
final Runnable r = new Runnable() {
1035-
@Override
1036-
public void run() {
1037-
// Insert the WPImageSpan in the content field
1038-
int selectionStart = start;
1039-
int selectionEnd = end;
1040-
1041-
if (selectionStart > selectionEnd) {
1042-
int temp = selectionEnd;
1043-
selectionEnd = selectionStart;
1044-
selectionStart = temp;
1045-
}
1040+
protected WPEditImageSpan doInBackground(Void... voids) {
1041+
mMediaFile.setFileURL(mImageUrl);
1042+
mMediaFile.setFilePath(mImageUrl);
1043+
WPEditImageSpan imageSpan = createWPEditImageSpan(getActivity(), mMediaFile);
1044+
mEditorFragmentListener.saveMediaFile(mMediaFile);
1045+
return imageSpan;
1046+
}
10461047

1047-
imageSpan.setPosition(selectionStart, selectionEnd);
1048+
protected void onPostExecute(WPEditImageSpan imageSpan) {
1049+
// Insert the WPImageSpan in the content field
1050+
int selectionStart = mStart;
1051+
int selectionEnd = mEnd;
10481052

1049-
int line, column = 0;
1050-
if (mContentEditText.getLayout() != null) {
1051-
line = mContentEditText.getLayout().getLineForOffset(selectionStart);
1052-
column = selectionStart - mContentEditText.getLayout().getLineStart(line);
1053-
}
1053+
if (selectionStart > selectionEnd) {
1054+
int temp = selectionEnd;
1055+
selectionEnd = selectionStart;
1056+
selectionStart = temp;
1057+
}
10541058

1055-
Editable s = mContentEditText.getText();
1056-
if (s == null) {
1057-
return;
1058-
}
1059+
imageSpan.setPosition(selectionStart, selectionEnd);
10591060

1060-
WPImageSpan[] imageSpans = s.getSpans(selectionStart, selectionEnd, WPImageSpan.class);
1061-
if (imageSpans.length != 0) {
1062-
// insert a few line breaks if the cursor is already on an image
1063-
s.insert(selectionEnd, "\n\n");
1064-
selectionStart = selectionStart + 2;
1065-
selectionEnd = selectionEnd + 2;
1066-
} else if (column != 0) {
1067-
// insert one line break if the cursor is not at the first column
1068-
s.insert(selectionEnd, "\n");
1069-
selectionStart = selectionStart + 1;
1070-
selectionEnd = selectionEnd + 1;
1071-
}
1061+
int line, column = 0;
1062+
if (mContentEditText.getLayout() != null) {
1063+
line = mContentEditText.getLayout().getLineForOffset(selectionStart);
1064+
column = selectionStart - mContentEditText.getLayout().getLineStart(line);
1065+
}
10721066

1073-
s.insert(selectionStart, " ");
1074-
s.setSpan(imageSpan, selectionStart, selectionEnd + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
1075-
AlignmentSpan.Standard as = new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER);
1076-
s.setSpan(as, selectionStart, selectionEnd + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
1077-
s.insert(selectionEnd + 1, "\n\n");
1067+
Editable s = mContentEditText.getText();
1068+
if (s == null) {
1069+
return;
1070+
}
10781071

1079-
// Fetch and replace the WPImageSpan if it's a remote media
1080-
if (imageLoader != null && URLUtil.isNetworkUrl(imageUrl)) {
1081-
loadWPImageSpanThumbnail(mediaFile, imageUrl, imageLoader);
1082-
}
1072+
WPImageSpan[] imageSpans = s.getSpans(selectionStart, selectionEnd, WPImageSpan.class);
1073+
if (imageSpans.length != 0) {
1074+
// insert a few line breaks if the cursor is already on an image
1075+
s.insert(selectionEnd, "\n\n");
1076+
selectionStart = selectionStart + 2;
1077+
selectionEnd = selectionEnd + 2;
1078+
} else if (column != 0) {
1079+
// insert one line break if the cursor is not at the first column
1080+
s.insert(selectionEnd, "\n");
1081+
selectionStart = selectionStart + 1;
1082+
selectionEnd = selectionEnd + 1;
10831083
}
1084-
};
1085-
handler.postDelayed(r, 1);
1084+
1085+
s.insert(selectionStart, " ");
1086+
s.setSpan(imageSpan, selectionStart, selectionEnd + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
1087+
AlignmentSpan.Standard as = new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER);
1088+
s.setSpan(as, selectionStart, selectionEnd + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
1089+
s.insert(selectionEnd + 1, "\n\n");
1090+
1091+
// Fetch and replace the WPImageSpan if it's a remote media
1092+
if (mImageLoader != null && URLUtil.isNetworkUrl(mImageUrl)) {
1093+
loadWPImageSpanThumbnail(mMediaFile, mImageUrl, mImageLoader);
1094+
}
1095+
}
1096+
}
1097+
1098+
public void addMediaFile(final MediaFile mediaFile, final String imageUrl, final ImageLoader imageLoader,
1099+
final int start, final int end) {
1100+
AddMediaFileTask addMediaFileTask = new AddMediaFileTask(mediaFile, imageUrl, imageLoader, start, end);
1101+
addMediaFileTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
10861102
}
10871103

10881104
@Override

0 commit comments

Comments
 (0)