From e8acd2c4713c4b872345938eb54bc067440c8a83 Mon Sep 17 00:00:00 2001
From: Maxime Biais
Date: Thu, 14 Apr 2016 09:20:38 +0200
Subject: [PATCH 1/2] Use a static listener to handle reflection failures
---
.../editor/EditorWebViewCompatibility.java | 50 ++++++++++++++-----
1 file changed, 37 insertions(+), 13 deletions(-)
diff --git a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorWebViewCompatibility.java b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorWebViewCompatibility.java
index 714f87ac8763..4f72868b5937 100644
--- a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorWebViewCompatibility.java
+++ b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorWebViewCompatibility.java
@@ -18,30 +18,52 @@
* JavaScript, {@link #loadJavaScript(String)}, instead of {@link WebView#loadUrl(String)}. This is needed because
* WebView#loadUrl(String) on API<19 eventually calls WebViewClassic#hideSoftKeyboard(),
* hiding the keyboard whenever JavaScript is executed.
- *
+ *
* This class uses reflection to access the normally inaccessible WebViewCore#sendMessage(Message)
* and use it to execute JavaScript, sidestepping WebView#loadUrl(String) and the keyboard issue.
*/
@SuppressWarnings("TryWithIdenticalCatches")
public class EditorWebViewCompatibility extends EditorWebViewAbstract {
+ public interface ReflectionFailureListener {
+ void onReflectionFailure(ReflectionException e);
+ }
+
+ public class ReflectionException extends Exception {
+ public ReflectionException(Throwable cause) {
+ super(cause);
+ }
+ }
+
private static final int EXECUTE_JS = 194; // WebViewCore internal JS message code
private Object mWebViewCore;
private Method mSendMessageMethod;
+ // Dirty static listener, but it's impossible to set the listener during the construction if we want to keep
+ // the xml layout
+ private static ReflectionFailureListener mReflectionFailureListener;
+ private boolean mReflectionSucceed = true;
+
+ public static void setReflectionFailureListener(ReflectionFailureListener reflectionFailureListener) {
+ mReflectionFailureListener = reflectionFailureListener;
+ }
+
public EditorWebViewCompatibility(Context context, AttributeSet attrs) {
super(context, attrs);
try {
this.initReflection();
} catch (ReflectionException e) {
AppLog.e(T.EDITOR, e);
- handleReflectionFailure();
+ handleReflectionFailure(e);
}
}
private void initReflection() throws ReflectionException {
+ if (!mReflectionSucceed) {
+ // Reflection failed once already, it won't succeed on a second try
+ return;
+ }
Object webViewProvider;
-
try {
if (Build.VERSION.SDK_INT >= 16) {
// On API >= 16, the WebViewCore instance is not defined inside WebView itself but inside a
@@ -58,7 +80,6 @@ private void initReflection() throws ReflectionException {
mWebViewCore = webViewCoreField.get(webViewProvider);
} else {
// On API < 16, the WebViewCore is directly accessible from the WebView
-
// Access WebViewCore object
Field webViewCoreField = WebView.class.getDeclaredField("mWebViewCore");
webViewCoreField.setAccessible(true);
@@ -97,19 +118,22 @@ private void loadJavaScript(final String javaScript) throws ReflectionException
public void execJavaScriptFromString(String javaScript) {
try {
loadJavaScript(javaScript);
- } catch(ReflectionException e) {
+ } catch (ReflectionException e) {
AppLog.e(T.EDITOR, e);
- handleReflectionFailure();
+ handleReflectionFailure(e);
}
}
- private void handleReflectionFailure() {
- // TODO: Fallback to legacy editor and pass the error to analytics
+ private void handleReflectionFailure(ReflectionException e) {
+ if (mReflectionFailureListener != null) {
+ mReflectionFailureListener.onReflectionFailure(e);
+ }
+ mReflectionSucceed = false;
}
- public class ReflectionException extends Exception {
- public ReflectionException(Throwable cause) {
- super(cause);
- }
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mReflectionFailureListener = null;
}
-}
\ No newline at end of file
+}
From a5b1e206bbd67d5fe4a834b2c8483792b9ec0035 Mon Sep 17 00:00:00 2001
From: Maxime Biais
Date: Thu, 14 Apr 2016 09:35:57 +0200
Subject: [PATCH 2/2] Log exception, show a toast and restart editor activity
onReflectionFailure
---
.../android/ui/posts/EditPostActivity.java | 20 ++++++++++++++++++-
WordPress/src/main/res/values/strings.xml | 2 ++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java
index e197d5c24b29..56fdd677a06d 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java
@@ -57,6 +57,8 @@
import org.wordpress.android.editor.EditorFragmentAbstract.TrackableEvent;
import org.wordpress.android.editor.EditorMediaUploadListener;
import org.wordpress.android.editor.EditorWebViewAbstract.ErrorListener;
+import org.wordpress.android.editor.EditorWebViewCompatibility;
+import org.wordpress.android.editor.EditorWebViewCompatibility.ReflectionException;
import org.wordpress.android.editor.ImageSettingsDialogFragment;
import org.wordpress.android.editor.LegacyEditorFragment;
import org.wordpress.android.models.AccountHelper;
@@ -122,7 +124,7 @@
import de.greenrobot.event.EventBus;
public class EditPostActivity extends AppCompatActivity implements EditorFragmentListener,
- ActivityCompat.OnRequestPermissionsResultCallback {
+ ActivityCompat.OnRequestPermissionsResultCallback, EditorWebViewCompatibility.ReflectionFailureListener {
public static final String EXTRA_POSTID = "postId";
public static final String EXTRA_IS_PAGE = "isPage";
public static final String EXTRA_IS_NEW_POST = "isNewPost";
@@ -866,6 +868,21 @@ private void saveAndFinish() {
finish();
}
+ /**
+ * Disable visual editor mode and log the exception if we get a Reflection failure when the webview is being
+ * initialized.
+ */
+ @Override
+ public void onReflectionFailure(ReflectionException e) {
+ CrashlyticsUtils.logException(e, ExceptionType.SPECIFIC, T.EDITOR, "Reflection Failure on Visual Editor init");
+ // Disable visual editor and show an error message
+ AppPrefs.setVisualEditorEnabled(false);
+ ToastUtils.showToast(this, R.string.new_editor_reflection_error, Duration.LONG);
+ // Restart the activity (will start the legacy editor)
+ finish();
+ startActivity(getIntent());
+ }
+
/**
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
@@ -886,6 +903,7 @@ public Fragment getItem(int position) {
case 0:
// TODO: switch between legacy and new editor here (AB test?)
if (mShowNewEditor) {
+ EditorWebViewCompatibility.setReflectionFailureListener(EditPostActivity.this);
return new EditorFragment();
} else {
return new LegacyEditorFragment();
diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml
index 14b79f08d4c2..9156b75a4d01 100644
--- a/WordPress/src/main/res/values/strings.xml
+++ b/WordPress/src/main/res/values/strings.xml
@@ -914,6 +914,8 @@
Brand new editor
The WordPress app for Android now includes a beautiful new visual
editor. Try it out by creating a new post.
+ Visual editor is not compatible with your device. It was
+ automatically disabled.
Featured Image