Loading core/java/android/webkit/WebView.java +153 −26 Original line number Diff line number Diff line Loading @@ -60,6 +60,10 @@ import android.os.StrictMode; import android.os.SystemClock; import android.provider.Settings; import android.speech.tts.TextToSpeech; import android.text.Editable; import android.text.InputType; import android.text.Selection; import android.text.TextUtils; import android.util.AttributeSet; import android.util.EventLog; import android.util.Log; Loading Loading @@ -363,39 +367,126 @@ public class WebView extends AbsoluteLayout } /** * InputConnection used for ContentEditable. This captures the 'delete' * commands and sends delete key presses. * InputConnection used for ContentEditable. This captures changes * to the text and sends them either as key strokes or text changes. */ private class WebViewInputConnection extends BaseInputConnection { // Used for mapping characters to keys typed. private KeyCharacterMap mKeyCharacterMap; public WebViewInputConnection() { super(WebView.this, false); super(WebView.this, true); } @Override public boolean setComposingText(CharSequence text, int newCursorPosition) { Editable editable = getEditable(); int start = getComposingSpanStart(editable); int end = getComposingSpanEnd(editable); if (start < 0 || end < 0) { start = Selection.getSelectionStart(editable); end = Selection.getSelectionEnd(editable); } if (end < start) { int temp = end; end = start; start = temp; } setNewText(start, end, text); return super.setComposingText(text, newCursorPosition); } @Override public boolean commitText(CharSequence text, int newCursorPosition) { setComposingText(text, newCursorPosition); finishComposingText(); return true; } @Override public boolean deleteSurroundingText(int leftLength, int rightLength) { Editable editable = getEditable(); int cursorPosition = Selection.getSelectionEnd(editable); int startDelete = Math.max(0, cursorPosition - leftLength); int endDelete = Math.min(editable.length(), cursorPosition + rightLength); setNewText(startDelete, endDelete, ""); return super.deleteSurroundingText(leftLength, rightLength); } /** * Sends a text change to webkit indirectly. If it is a single- * character add or delete, it sends it as a key stroke. If it cannot * be represented as a key stroke, it sends it as a field change. * @param start The start offset (inclusive) of the text being changed. * @param end The end offset (exclusive) of the text being changed. * @param text The new text to replace the changed text. */ private void setNewText(int start, int end, CharSequence text) { Editable editable = getEditable(); CharSequence original = editable.subSequence(start, end); boolean isCharacterAdd = false; boolean isCharacterDelete = false; int textLength = text.length(); int originalLength = original.length(); if (textLength > originalLength) { isCharacterAdd = (textLength == originalLength + 1) && TextUtils.regionMatches(text, 0, original, 0, originalLength); } else if (originalLength > textLength) { isCharacterDelete = (textLength == originalLength - 1) && TextUtils.regionMatches(text, 0, original, 0, textLength); } if (isCharacterAdd) { sendCharacter(text.charAt(textLength - 1)); mTextGeneration++; } else if (isCharacterDelete) { sendDeleteKey(); mTextGeneration++; } else if (textLength != originalLength || !TextUtils.regionMatches(text, 0, original, 0, textLength)) { // Send a message so that key strokes and text replacement // do not come out of order. Message replaceMessage = mPrivateHandler.obtainMessage( REPLACE_TEXT, start, end, text.toString()); mPrivateHandler.sendMessage(replaceMessage); } } /** * Send a single character to the WebView as a key down and up event. * @param c The character to be sent. */ private void sendCharacter(char c) { if (mKeyCharacterMap == null) { mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); } char[] chars = new char[1]; chars[0] = c; KeyEvent[] events = mKeyCharacterMap.getEvents(chars); if (events != null) { for (KeyEvent event : events) { sendKeyEvent(event); } } } private void sendKeyPress(int keyCode) { /** * Send the delete character as a key down and up event. */ private void sendDeleteKey() { long eventTime = SystemClock.uptimeMillis(); sendKeyEvent(new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyCode, 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD)); sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, KeyEvent.ACTION_UP, keyCode, 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD)); } @Override public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Look for one-character delete and send it as a key press. if (beforeLength == 1 && afterLength == 0) { sendKeyPress(KeyEvent.KEYCODE_DEL); } else if (beforeLength == 0 && afterLength == 1){ sendKeyPress(KeyEvent.KEYCODE_FORWARD_DEL); } else if (mWebViewCore != null) { mWebViewCore.sendMessage(EventHub.DELETE_SURROUNDING_TEXT, beforeLength, afterLength); } return super.deleteSurroundingText(beforeLength, afterLength); } } Loading Loading @@ -799,6 +890,8 @@ public class WebView extends AbsoluteLayout static final int EXIT_FULLSCREEN_VIDEO = 140; static final int COPY_TO_CLIPBOARD = 141; static final int INIT_EDIT_FIELD = 142; static final int REPLACE_TEXT = 143; private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID; private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT; Loading Loading @@ -4951,12 +5044,18 @@ public class WebView extends AbsoluteLayout @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN outAttrs.inputType = EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_NORMAL; | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES; outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE; if (mInputConnection == null) { mInputConnection = new WebViewInputConnection(); } outAttrs.initialCapsMode = mInputConnection.getCursorCapsMode(InputType.TYPE_CLASS_TEXT); return mInputConnection; } Loading Loading @@ -8963,6 +9062,31 @@ public class WebView extends AbsoluteLayout copyToClipboard((String) msg.obj); break; case INIT_EDIT_FIELD: if (mInputConnection != null) { mTextGeneration = 0; String text = (String)msg.obj; mInputConnection.beginBatchEdit(); Editable editable = mInputConnection.getEditable(); editable.replace(0, editable.length(), text); int start = msg.arg1; int end = msg.arg2; mInputConnection.setComposingRegion(end, end); mInputConnection.setSelection(start, end); mInputConnection.endBatchEdit(); } break; case REPLACE_TEXT:{ String text = (String)msg.obj; int start = msg.arg1; int end = msg.arg2; int cursorPosition = start + text.length(); replaceTextfieldText(start, end, text, cursorPosition, cursorPosition); break; } default: super.handleMessage(msg); break; Loading Loading @@ -9088,10 +9212,13 @@ public class WebView extends AbsoluteLayout */ private void updateTextSelectionFromMessage(int nodePointer, int textGeneration, WebViewCore.TextSelectionData data) { if (textGeneration == mTextGeneration) { if (inEditingMode() && mWebTextView.isSameTextField(nodePointer) && textGeneration == mTextGeneration) { && mWebTextView.isSameTextField(nodePointer)) { mWebTextView.setSelectionFromWebKit(data.mStart, data.mEnd); } else if (mInputConnection != null){ mInputConnection.setSelection(data.mStart, data.mEnd); } } } Loading core/java/android/webkit/WebViewCore.java +9 −17 Original line number Diff line number Diff line Loading @@ -646,18 +646,6 @@ public final class WebViewCore { private native void nativeDeleteSelection(int nativeClass, int start, int end, int textGeneration); /** * Delete text near the cursor. * @param nativeClass The pointer to the native class (mNativeClass) * @param leftLength The number of characters to the left of the cursor to * delete * @param rightLength The number of characters to the right of the cursor * to delete. */ private native void nativeDeleteSurroundingText(int nativeClass, int leftLength, int rightLength); /** * Set the selection to (start, end) in the focused textfield. If start and * end are out of order, swap them. Loading Loading @@ -1576,11 +1564,6 @@ public final class WebViewCore { deleteSelectionData.mStart, deleteSelectionData.mEnd, msg.arg1); break; case DELETE_SURROUNDING_TEXT: nativeDeleteSurroundingText(mNativeClass, msg.arg1, msg.arg2); break; case SET_SELECTION: nativeSetSelection(mNativeClass, msg.arg1, msg.arg2); break; Loading Loading @@ -2739,6 +2722,15 @@ public final class WebViewCore { WebView.FIND_AGAIN).sendToTarget(); } // called by JNI private void initEditField(String text, int start, int end) { if (mWebView == null) { return; } Message.obtain(mWebView.mPrivateHandler, WebView.INIT_EDIT_FIELD, start, end, text).sendToTarget(); } private native void nativeUpdateFrameCacheIfLoading(int nativeClass); private native void nativeRevealSelection(int nativeClass); private native String nativeRequestLabel(int nativeClass, int framePtr, Loading Loading
core/java/android/webkit/WebView.java +153 −26 Original line number Diff line number Diff line Loading @@ -60,6 +60,10 @@ import android.os.StrictMode; import android.os.SystemClock; import android.provider.Settings; import android.speech.tts.TextToSpeech; import android.text.Editable; import android.text.InputType; import android.text.Selection; import android.text.TextUtils; import android.util.AttributeSet; import android.util.EventLog; import android.util.Log; Loading Loading @@ -363,39 +367,126 @@ public class WebView extends AbsoluteLayout } /** * InputConnection used for ContentEditable. This captures the 'delete' * commands and sends delete key presses. * InputConnection used for ContentEditable. This captures changes * to the text and sends them either as key strokes or text changes. */ private class WebViewInputConnection extends BaseInputConnection { // Used for mapping characters to keys typed. private KeyCharacterMap mKeyCharacterMap; public WebViewInputConnection() { super(WebView.this, false); super(WebView.this, true); } @Override public boolean setComposingText(CharSequence text, int newCursorPosition) { Editable editable = getEditable(); int start = getComposingSpanStart(editable); int end = getComposingSpanEnd(editable); if (start < 0 || end < 0) { start = Selection.getSelectionStart(editable); end = Selection.getSelectionEnd(editable); } if (end < start) { int temp = end; end = start; start = temp; } setNewText(start, end, text); return super.setComposingText(text, newCursorPosition); } @Override public boolean commitText(CharSequence text, int newCursorPosition) { setComposingText(text, newCursorPosition); finishComposingText(); return true; } @Override public boolean deleteSurroundingText(int leftLength, int rightLength) { Editable editable = getEditable(); int cursorPosition = Selection.getSelectionEnd(editable); int startDelete = Math.max(0, cursorPosition - leftLength); int endDelete = Math.min(editable.length(), cursorPosition + rightLength); setNewText(startDelete, endDelete, ""); return super.deleteSurroundingText(leftLength, rightLength); } /** * Sends a text change to webkit indirectly. If it is a single- * character add or delete, it sends it as a key stroke. If it cannot * be represented as a key stroke, it sends it as a field change. * @param start The start offset (inclusive) of the text being changed. * @param end The end offset (exclusive) of the text being changed. * @param text The new text to replace the changed text. */ private void setNewText(int start, int end, CharSequence text) { Editable editable = getEditable(); CharSequence original = editable.subSequence(start, end); boolean isCharacterAdd = false; boolean isCharacterDelete = false; int textLength = text.length(); int originalLength = original.length(); if (textLength > originalLength) { isCharacterAdd = (textLength == originalLength + 1) && TextUtils.regionMatches(text, 0, original, 0, originalLength); } else if (originalLength > textLength) { isCharacterDelete = (textLength == originalLength - 1) && TextUtils.regionMatches(text, 0, original, 0, textLength); } if (isCharacterAdd) { sendCharacter(text.charAt(textLength - 1)); mTextGeneration++; } else if (isCharacterDelete) { sendDeleteKey(); mTextGeneration++; } else if (textLength != originalLength || !TextUtils.regionMatches(text, 0, original, 0, textLength)) { // Send a message so that key strokes and text replacement // do not come out of order. Message replaceMessage = mPrivateHandler.obtainMessage( REPLACE_TEXT, start, end, text.toString()); mPrivateHandler.sendMessage(replaceMessage); } } /** * Send a single character to the WebView as a key down and up event. * @param c The character to be sent. */ private void sendCharacter(char c) { if (mKeyCharacterMap == null) { mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); } char[] chars = new char[1]; chars[0] = c; KeyEvent[] events = mKeyCharacterMap.getEvents(chars); if (events != null) { for (KeyEvent event : events) { sendKeyEvent(event); } } } private void sendKeyPress(int keyCode) { /** * Send the delete character as a key down and up event. */ private void sendDeleteKey() { long eventTime = SystemClock.uptimeMillis(); sendKeyEvent(new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyCode, 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD)); sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, KeyEvent.ACTION_UP, keyCode, 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD)); } @Override public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Look for one-character delete and send it as a key press. if (beforeLength == 1 && afterLength == 0) { sendKeyPress(KeyEvent.KEYCODE_DEL); } else if (beforeLength == 0 && afterLength == 1){ sendKeyPress(KeyEvent.KEYCODE_FORWARD_DEL); } else if (mWebViewCore != null) { mWebViewCore.sendMessage(EventHub.DELETE_SURROUNDING_TEXT, beforeLength, afterLength); } return super.deleteSurroundingText(beforeLength, afterLength); } } Loading Loading @@ -799,6 +890,8 @@ public class WebView extends AbsoluteLayout static final int EXIT_FULLSCREEN_VIDEO = 140; static final int COPY_TO_CLIPBOARD = 141; static final int INIT_EDIT_FIELD = 142; static final int REPLACE_TEXT = 143; private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID; private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT; Loading Loading @@ -4951,12 +5044,18 @@ public class WebView extends AbsoluteLayout @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN outAttrs.inputType = EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_NORMAL; | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES; outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE; if (mInputConnection == null) { mInputConnection = new WebViewInputConnection(); } outAttrs.initialCapsMode = mInputConnection.getCursorCapsMode(InputType.TYPE_CLASS_TEXT); return mInputConnection; } Loading Loading @@ -8963,6 +9062,31 @@ public class WebView extends AbsoluteLayout copyToClipboard((String) msg.obj); break; case INIT_EDIT_FIELD: if (mInputConnection != null) { mTextGeneration = 0; String text = (String)msg.obj; mInputConnection.beginBatchEdit(); Editable editable = mInputConnection.getEditable(); editable.replace(0, editable.length(), text); int start = msg.arg1; int end = msg.arg2; mInputConnection.setComposingRegion(end, end); mInputConnection.setSelection(start, end); mInputConnection.endBatchEdit(); } break; case REPLACE_TEXT:{ String text = (String)msg.obj; int start = msg.arg1; int end = msg.arg2; int cursorPosition = start + text.length(); replaceTextfieldText(start, end, text, cursorPosition, cursorPosition); break; } default: super.handleMessage(msg); break; Loading Loading @@ -9088,10 +9212,13 @@ public class WebView extends AbsoluteLayout */ private void updateTextSelectionFromMessage(int nodePointer, int textGeneration, WebViewCore.TextSelectionData data) { if (textGeneration == mTextGeneration) { if (inEditingMode() && mWebTextView.isSameTextField(nodePointer) && textGeneration == mTextGeneration) { && mWebTextView.isSameTextField(nodePointer)) { mWebTextView.setSelectionFromWebKit(data.mStart, data.mEnd); } else if (mInputConnection != null){ mInputConnection.setSelection(data.mStart, data.mEnd); } } } Loading
core/java/android/webkit/WebViewCore.java +9 −17 Original line number Diff line number Diff line Loading @@ -646,18 +646,6 @@ public final class WebViewCore { private native void nativeDeleteSelection(int nativeClass, int start, int end, int textGeneration); /** * Delete text near the cursor. * @param nativeClass The pointer to the native class (mNativeClass) * @param leftLength The number of characters to the left of the cursor to * delete * @param rightLength The number of characters to the right of the cursor * to delete. */ private native void nativeDeleteSurroundingText(int nativeClass, int leftLength, int rightLength); /** * Set the selection to (start, end) in the focused textfield. If start and * end are out of order, swap them. Loading Loading @@ -1576,11 +1564,6 @@ public final class WebViewCore { deleteSelectionData.mStart, deleteSelectionData.mEnd, msg.arg1); break; case DELETE_SURROUNDING_TEXT: nativeDeleteSurroundingText(mNativeClass, msg.arg1, msg.arg2); break; case SET_SELECTION: nativeSetSelection(mNativeClass, msg.arg1, msg.arg2); break; Loading Loading @@ -2739,6 +2722,15 @@ public final class WebViewCore { WebView.FIND_AGAIN).sendToTarget(); } // called by JNI private void initEditField(String text, int start, int end) { if (mWebView == null) { return; } Message.obtain(mWebView.mPrivateHandler, WebView.INIT_EDIT_FIELD, start, end, text).sendToTarget(); } private native void nativeUpdateFrameCacheIfLoading(int nativeClass); private native void nativeRevealSelection(int nativeClass); private native String nativeRequestLabel(int nativeClass, int framePtr, Loading