Skip to content

Commit

Permalink
Merge pull request #136 from wordpress-mobile/issue/132-formatbar-key…
Browse files Browse the repository at this point in the history
…board-old-api

Issue #132: format bar buttons hide keyboard
  • Loading branch information
bummytime committed Apr 28, 2015
2 parents 4478cf3 + 2d7c9c4 commit 64b83d3
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,11 @@
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.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 Down Expand Up @@ -49,7 +43,7 @@ public class EditorFragment extends EditorFragmentAbstract implements View.OnCli
private String mParamContent;

private Activity mActivity;
private EditorWebView mWebView;
private EditorWebViewAbstract mWebView;

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

Expand Down Expand Up @@ -78,8 +72,8 @@ public void onCreate(Bundle savedInstanceState) {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_editor, container, false);
mWebView = (EditorWebView) view.findViewById(R.id.webview);
initWebView();
mWebView = (EditorWebViewAbstract) view.findViewById(R.id.webview);
initJsEditor();

ToggleButton mediaButton = (ToggleButton) view.findViewById(R.id.format_bar_button_media);
mTagToggleButtonMap.put(TAG_FORMAT_BAR_BUTTON_MEDIA, mediaButton);
Expand Down Expand Up @@ -117,30 +111,7 @@ public void onDetach() {
super.onDetach();
}

@SuppressLint("SetJavaScriptEnabled")
private void initWebView() {
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDefaultTextEncodingName("utf-8");
mWebView.setWebViewClient(new WebViewClient() {
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
AppLog.e(T.EDITOR, description);
}
});
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onConsoleMessage(@NonNull ConsoleMessage cm) {
AppLog.d(T.EDITOR, cm.message() + " -- From line " + cm.lineNumber() + " of " + cm.sourceId());
return true;
}

@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
AppLog.d(T.EDITOR, message);
return true;
}
});

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

mWebView.addJavascriptInterface(new JsCallbackReceiver(this), JS_CALLBACK_HANDLER);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,23 @@
package org.wordpress.android.editor;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.webkit.WebView;

public class EditorWebView extends WebView {

public EditorWebView(Context context) {
super(context);
}
public class EditorWebView extends EditorWebViewAbstract {

public EditorWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public EditorWebView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

@Override
public boolean onCheckIsTextEditor() {
return true;
}

@SuppressLint("NewApi")
public void execJavaScriptFromString(String javaScript) {
this.loadUrl("javascript:" + javaScript);
if (Build.VERSION.SDK_INT >= 19) {
this.evaluateJavascript(javaScript, null);
} else {
this.loadUrl("javascript:" + javaScript);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.wordpress.android.editor;

import android.annotation.SuppressLint;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.webkit.ConsoleMessage;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import org.wordpress.android.util.AppLog;

/**
* A text editor WebView with support for JavaScript execution.
*/
public abstract class EditorWebViewAbstract extends WebView {
public abstract void execJavaScriptFromString(String javaScript);

public EditorWebViewAbstract(Context context, AttributeSet attrs) {
super(context, attrs);
configureWebView();
}

@SuppressLint("SetJavaScriptEnabled")
private void configureWebView() {
WebSettings webSettings = this.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDefaultTextEncodingName("utf-8");

this.setWebViewClient(new WebViewClient() {
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
AppLog.e(AppLog.T.EDITOR, description);
}
});

this.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onConsoleMessage(@NonNull ConsoleMessage cm) {
AppLog.d(AppLog.T.EDITOR, cm.message() + " -- From line " + cm.lineNumber() + " of " + cm.sourceId());
return true;
}

@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
AppLog.d(AppLog.T.EDITOR, message);
return true;
}
});
}

@Override
public boolean onCheckIsTextEditor() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package org.wordpress.android.editor;

import android.content.Context;
import android.os.Build;
import android.os.Message;
import android.util.AttributeSet;
import android.webkit.WebView;

import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppLog.T;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* <p>Compatibility <code>EditorWebView</code> for pre-Chromium WebView (API<19). Provides a custom method for executing
* JavaScript, {@link #loadJavaScript(String)}, instead of {@link WebView#loadUrl(String)}. This is needed because
* <code>WebView#loadUrl(String)</code> on API<19 eventually calls <code>WebViewClassic#hideSoftKeyboard()</code>,
* hiding the keyboard whenever JavaScript is executed.</p>
*
* <p>This class uses reflection to access the normally inaccessible <code>WebViewCore#sendMessage(Message)</code>
* and use it to execute JavaScript, sidestepping <code>WebView#loadUrl(String)</code> and the keyboard issue.</p>
*/
@SuppressWarnings("TryWithIdenticalCatches")
public class EditorWebViewCompatibility extends EditorWebViewAbstract {
private static final int EXECUTE_JS = 194; // WebViewCore internal JS message code

private Object mWebViewCore;
private Method mSendMessageMethod;

public EditorWebViewCompatibility(Context context, AttributeSet attrs) {
super(context, attrs);
try {
this.initReflection();
} catch (ReflectionException e) {
AppLog.e(T.EDITOR, e);
handleReflectionFailure();
}
}

private void initReflection() throws ReflectionException {
Object webViewProvider;

try {
if (Build.VERSION.SDK_INT >= 16) {
// On API >= 16, the WebViewCore instance is not defined inside WebView itself but inside a
// WebViewClassic (implementation of WebViewProvider), referenced from the WebView as mProvider

// Access WebViewClassic object
Field webViewProviderField = WebView.class.getDeclaredField("mProvider");
webViewProviderField.setAccessible(true);
webViewProvider = webViewProviderField.get(this);

// Access WebViewCore object
Field webViewCoreField = webViewProvider.getClass().getDeclaredField("mWebViewCore");
webViewCoreField.setAccessible(true);
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);
mWebViewCore = webViewCoreField.get(this);
}

// Access WebViewCore#sendMessage(Message) method
if (mWebViewCore != null) {
mSendMessageMethod = mWebViewCore.getClass().getDeclaredMethod("sendMessage", Message.class);
mSendMessageMethod.setAccessible(true);
}
} catch (NoSuchFieldException e) {
throw new ReflectionException(e);
} catch (NoSuchMethodException e) {
throw new ReflectionException(e);
} catch (IllegalAccessException e) {
throw new ReflectionException(e);
}
}

private void loadJavaScript(final String javaScript) throws ReflectionException {
if (mSendMessageMethod == null) {
initReflection();
} else {
Message jsMessage = Message.obtain(null, EXECUTE_JS, javaScript);
try {
mSendMessageMethod.invoke(mWebViewCore, jsMessage);
} catch (InvocationTargetException e) {
throw new ReflectionException(e);
} catch (IllegalAccessException e) {
throw new ReflectionException(e);
}
}
}

public void execJavaScriptFromString(String javaScript) {
try {
loadJavaScript(javaScript);
} catch(ReflectionException e) {
AppLog.e(T.EDITOR, e);
handleReflectionFailure();
}
}

private void handleReflectionFailure() {
// TODO: Fallback to legacy editor and pass the error to analytics
}

public class ReflectionException extends Exception {
public ReflectionException(Throwable cause) {
super(cause);
}
}
}
6 changes: 6 additions & 0 deletions WordPressEditor/src/main/res/layout-v19/editor_webview.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<org.wordpress.android.editor.EditorWebView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
6 changes: 6 additions & 0 deletions WordPressEditor/src/main/res/layout/editor_webview.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<org.wordpress.android.editor.EditorWebViewCompatibility
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
7 changes: 3 additions & 4 deletions WordPressEditor/src/main/res/layout/fragment_editor.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
android:layout_height="match_parent"
tools:context="org.wordpress.android.editor.EditorFragment">

<org.wordpress.android.editor.EditorWebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/webview"/>
<include
layout="@layout/editor_webview"
android:id="@+id/webview" />

<LinearLayout
android:id="@+id/format_bar"
Expand Down

0 comments on commit 64b83d3

Please sign in to comment.