blob: a2673a56b9491e0f1230181367b8571be204a398 [file] [log] [blame]
/*
* Copyright (C) 2007-2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.internal.widget;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.KeyListener;
import android.util.Log;
import android.util.LogPrinter;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.widget.TextView;
class ComposingText {
}
public class EditableInputConnection extends BaseInputConnection {
private static final boolean DEBUG = false;
private static final String TAG = "EditableInputConnection";
public static final Object COMPOSING = new ComposingText();
private final TextView mTextView;
private Object[] mDefaultComposingSpans;
public EditableInputConnection(TextView textview) {
super(textview);
mTextView = textview;
}
public static final void removeComposingSpans(Spannable text) {
text.removeSpan(COMPOSING);
Object[] sps = text.getSpans(0, text.length(), Object.class);
if (sps != null) {
for (int i=sps.length-1; i>=0; i--) {
Object o = sps[i];
if ((text.getSpanFlags(o)&Spanned.SPAN_COMPOSING) != 0) {
text.removeSpan(o);
}
}
}
}
public static void setComposingSpans(Spannable text) {
final Object[] sps = text.getSpans(0, text.length(), Object.class);
if (sps != null) {
for (int i=sps.length-1; i>=0; i--) {
final Object o = sps[i];
if (o == COMPOSING) {
text.removeSpan(o);
continue;
}
final int fl = text.getSpanFlags(o);
if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK))
!= (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) {
text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o),
(fl&Spanned.SPAN_POINT_MARK_MASK)
| Spanned.SPAN_COMPOSING
| Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
text.setSpan(COMPOSING, 0, text.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
}
public static int getComposingSpanStart(Spannable text) {
return text.getSpanStart(COMPOSING);
}
public static int getComposingSpanEnd(Spannable text) {
return text.getSpanEnd(COMPOSING);
}
public boolean setComposingText(CharSequence text, int newCursorPosition) {
if (DEBUG) Log.v(TAG, "setComposingText " + text);
replaceText(text, newCursorPosition, true);
return true;
}
public boolean finishComposingText() {
if (DEBUG) Log.v(TAG, "finishComposingText");
final Editable content = getEditable();
if (content != null) {
removeComposingSpans(content);
}
return true;
}
public boolean commitText(CharSequence text, int newCursorPosition) {
if (DEBUG) Log.v(TAG, "commitText " + text);
replaceText(text, newCursorPosition, false);
return true;
}
public boolean commitCompletion(CompletionInfo text) {
if (DEBUG) Log.v(TAG, "commitCompletion " + text);
mTextView.onCommitCompletion(text);
return true;
}
public CharSequence getTextBeforeCursor(int length) {
final Editable content = getEditable();
if (content == null) return null;
int a = Selection.getSelectionStart(content);
int b = Selection.getSelectionEnd(content);
if (a > b) {
int tmp = a;
a = b;
b = tmp;
}
if (length > a) {
length = a;
}
return content.subSequence(a - length, a);
}
public CharSequence getTextAfterCursor(int length) {
final Editable content = getEditable();
if (content == null) return null;
int a = Selection.getSelectionStart(content);
int b = Selection.getSelectionEnd(content);
if (a > b) {
int tmp = a;
a = b;
b = tmp;
}
if (b + length > content.length()) {
length = content.length() - b;
}
return content.subSequence(b, b + length);
}
public int getCursorCapsMode(int reqModes) {
final Editable content = getEditable();
if (content == null) return 0;
int a = Selection.getSelectionStart(content);
int b = Selection.getSelectionEnd(content);
if (a > b) {
int tmp = a;
a = b;
b = tmp;
}
return TextUtils.getCapsMode(content, a, reqModes);
}
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
if (mTextView != null) {
ExtractedText et = new ExtractedText();
if (mTextView.extractText(request, et)) {
if ((flags&EXTRACTED_TEXT_MONITOR) != 0) {
mTextView.setExtracting(request);
}
return et;
}
}
return null;
}
public boolean deleteSurroundingText(int leftLength, int rightLength) {
if (DEBUG) Log.v(TAG, "deleteSurroundingText " + leftLength
+ " / " + rightLength);
final Editable content = getEditable();
if (content == null) return false;
int a = Selection.getSelectionStart(content);
int b = Selection.getSelectionEnd(content);
if (a > b) {
int tmp = a;
a = b;
b = tmp;
}
// ignore the composing text.
int ca = content.getSpanStart(COMPOSING);
int cb = content.getSpanEnd(COMPOSING);
if (cb < ca) {
int tmp = ca;
ca = cb;
cb = tmp;
}
if (ca != -1 && cb != -1) {
if (ca < a) a = ca;
if (cb > b) b = cb;
}
int deleted = 0;
if (leftLength > 0) {
int start = a - leftLength;
if (start < 0) start = 0;
content.delete(start, a);
deleted = a - start;
}
if (rightLength > 0) {
b = b - deleted;
int end = b + rightLength;
if (end > content.length()) end = content.length();
content.delete(b, end);
}
return true;
}
public boolean beginBatchEdit() {
if (mTextView == null) return false;
mTextView.onBeginBatchEdit();
return true;
}
public boolean endBatchEdit() {
if (mTextView == null) return false;
mTextView.onEndBatchEdit();
return true;
}
public boolean clearMetaKeyStates(int states) {
final Editable content = getEditable();
if (content == null) return false;
KeyListener kl = mTextView.getKeyListener();
if (kl != null) kl.clearMetaKeyState(mTextView, content, states);
return true;
}
public boolean performPrivateCommand(String action, Bundle data) {
if (mTextView == null) return false;
mTextView.onPrivateIMECommand(action, data);
return true;
}
private Editable getEditable() {
TextView tv = mTextView;
if (tv != null) {
return tv.getEditableText();
}
return null;
}
private void replaceText(CharSequence text, int newCursorPosition,
boolean composing) {
final Editable content = getEditable();
// delete composing text set previously.
int a = content.getSpanStart(COMPOSING);
int b = content.getSpanEnd(COMPOSING);
if (DEBUG) Log.v(TAG, "Composing span: " + a + " to " + b);
if (b < a) {
int tmp = a;
a = b;
b = tmp;
}
if (a != -1 && b != -1) {
removeComposingSpans(content);
} else {
a = Selection.getSelectionStart(content);
b = Selection.getSelectionEnd(content);
if (a >=0 && b>= 0 && a != b) {
if (b < a) {
int tmp = a;
a = b;
b = tmp;
}
}
}
if (composing) {
Spannable sp = null;
if (!(text instanceof Spannable)) {
sp = new SpannableStringBuilder(text);
text = sp;
if (mDefaultComposingSpans == null) {
TypedArray ta = mTextView.getContext().getTheme()
.obtainStyledAttributes(new int[] {
com.android.internal.R.attr.candidatesTextStyleSpans
});
CharSequence style = ta.getText(0);
ta.recycle();
if (style != null && style instanceof Spanned) {
mDefaultComposingSpans = ((Spanned)style).getSpans(
0, style.length(), Object.class);
}
}
if (mDefaultComposingSpans != null) {
for (int i = 0; i < mDefaultComposingSpans.length; ++i) {
sp.setSpan(mDefaultComposingSpans[i], 0, sp.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
} else {
sp = (Spannable)text;
}
setComposingSpans(sp);
}
// Adjust newCursorPosition to be relative the start of the text.
newCursorPosition += a;
if (DEBUG) Log.v(TAG, "Replacing from " + a + " to " + b + " with \""
+ text + "\", composing=" + composing
+ ", type=" + text.getClass().getCanonicalName());
if (DEBUG) {
LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG);
lp.println("Current text:");
TextUtils.dumpSpans(content, lp, " ");
lp.println("Composing text:");
TextUtils.dumpSpans(text, lp, " ");
}
content.replace(a, b, text);
if (newCursorPosition < 0) newCursorPosition = 0;
if (newCursorPosition > content.length())
newCursorPosition = content.length();
Selection.setSelection(content, newCursorPosition);
if (DEBUG) {
LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG);
lp.println("Final text:");
TextUtils.dumpSpans(content, lp, " ");
}
}
}