Code drop from //branches/cupcake/...@124589
This commit is contained in:
319
samples/SoftKeyboard/src/com/example/android/softkeyboard/CandidateView.java
Executable file
319
samples/SoftKeyboard/src/com/example/android/softkeyboard/CandidateView.java
Executable file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Google Inc.
|
||||
*
|
||||
* 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.example.android.softkeyboard;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CandidateView extends View {
|
||||
|
||||
private static final int OUT_OF_BOUNDS = -1;
|
||||
|
||||
private SoftKeyboard mService;
|
||||
private List<String> mSuggestions;
|
||||
private int mSelectedIndex;
|
||||
private int mTouchX = OUT_OF_BOUNDS;
|
||||
private Drawable mSelectionHighlight;
|
||||
private boolean mTypedWordValid;
|
||||
|
||||
private Rect mBgPadding;
|
||||
|
||||
private static final int MAX_SUGGESTIONS = 32;
|
||||
private static final int SCROLL_PIXELS = 20;
|
||||
|
||||
private int[] mWordWidth = new int[MAX_SUGGESTIONS];
|
||||
private int[] mWordX = new int[MAX_SUGGESTIONS];
|
||||
|
||||
private static final int X_GAP = 10;
|
||||
|
||||
private static final List<String> EMPTY_LIST = new ArrayList<String>();
|
||||
|
||||
private int mColorNormal;
|
||||
private int mColorRecommended;
|
||||
private int mColorOther;
|
||||
private Paint mPaint;
|
||||
private boolean mScrolled;
|
||||
private int mTargetScrollX;
|
||||
|
||||
private int mTotalWidth;
|
||||
|
||||
private GestureDetector mGestureDetector;
|
||||
|
||||
/**
|
||||
* Construct a CandidateView for showing suggested words for completion.
|
||||
* @param context
|
||||
* @param attrs
|
||||
*/
|
||||
public CandidateView(Context context) {
|
||||
super(context);
|
||||
mSelectionHighlight = context.getResources().getDrawable(
|
||||
android.R.drawable.list_selector_background);
|
||||
mSelectionHighlight.setState(new int[] {
|
||||
android.R.attr.state_enabled,
|
||||
android.R.attr.state_focused,
|
||||
android.R.attr.state_window_focused,
|
||||
android.R.attr.state_pressed
|
||||
});
|
||||
|
||||
Resources r = context.getResources();
|
||||
|
||||
setBackgroundColor(r.getColor(R.color.candidate_background));
|
||||
|
||||
mColorNormal = r.getColor(R.color.candidate_normal);
|
||||
mColorRecommended = r.getColor(R.color.candidate_recommended);
|
||||
mColorOther = r.getColor(R.color.candidate_other);
|
||||
|
||||
mPaint = new Paint();
|
||||
mPaint.setColor(mColorNormal);
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_font_height));
|
||||
mPaint.setStrokeWidth(0);
|
||||
|
||||
mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
|
||||
@Override
|
||||
public boolean onScroll(MotionEvent e1, MotionEvent e2,
|
||||
float distanceX, float distanceY) {
|
||||
mScrolled = true;
|
||||
int sx = getScrollX();
|
||||
sx += distanceX;
|
||||
if (sx < 0) {
|
||||
sx = 0;
|
||||
}
|
||||
if (sx + getWidth() > mTotalWidth) {
|
||||
sx -= distanceX;
|
||||
}
|
||||
mTargetScrollX = sx;
|
||||
scrollTo(sx, getScrollY());
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
setHorizontalFadingEdgeEnabled(true);
|
||||
setWillNotDraw(false);
|
||||
setHorizontalScrollBarEnabled(false);
|
||||
setVerticalScrollBarEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* A connection back to the service to communicate with the text field
|
||||
* @param listener
|
||||
*/
|
||||
public void setService(SoftKeyboard listener) {
|
||||
mService = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeHorizontalScrollRange() {
|
||||
return mTotalWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int measuredWidth = resolveSize(50, widthMeasureSpec);
|
||||
|
||||
// Get the desired height of the icon menu view (last row of items does
|
||||
// not have a divider below)
|
||||
Rect padding = new Rect();
|
||||
mSelectionHighlight.getPadding(padding);
|
||||
final int desiredHeight = ((int)mPaint.getTextSize()) + 1
|
||||
+ padding.top + padding.bottom;
|
||||
|
||||
// Maximum possible width and desired height
|
||||
setMeasuredDimension(measuredWidth,
|
||||
resolveSize(desiredHeight, heightMeasureSpec));
|
||||
}
|
||||
|
||||
/**
|
||||
* If the canvas is null, then only touch calculations are performed to pick the target
|
||||
* candidate.
|
||||
*/
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (canvas != null) {
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
mTotalWidth = 0;
|
||||
if (mSuggestions == null) return;
|
||||
|
||||
if (mBgPadding == null) {
|
||||
mBgPadding = new Rect(0, 0, 0, 0);
|
||||
if (getBackground() != null) {
|
||||
getBackground().getPadding(mBgPadding);
|
||||
}
|
||||
}
|
||||
int x = 0;
|
||||
final int count = mSuggestions.size();
|
||||
final int height = getHeight();
|
||||
final Rect bgPadding = mBgPadding;
|
||||
final Paint paint = mPaint;
|
||||
final int touchX = mTouchX;
|
||||
final int scrollX = getScrollX();
|
||||
final boolean scrolled = mScrolled;
|
||||
final boolean typedWordValid = mTypedWordValid;
|
||||
final int y = (int) (height + mPaint.getTextSize()) / 2;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
String suggestion = mSuggestions.get(i);
|
||||
float textWidth = paint.measureText(suggestion);
|
||||
final int wordWidth = (int) textWidth + X_GAP * 2;
|
||||
|
||||
mWordX[i] = x;
|
||||
mWordWidth[i] = wordWidth;
|
||||
paint.setColor(mColorNormal);
|
||||
if (touchX + scrollX >= x && touchX + scrollX < x + wordWidth && !scrolled) {
|
||||
if (canvas != null) {
|
||||
canvas.translate(x, 0);
|
||||
mSelectionHighlight.setBounds(0, bgPadding.top, wordWidth, height);
|
||||
mSelectionHighlight.draw(canvas);
|
||||
canvas.translate(-x, 0);
|
||||
}
|
||||
mSelectedIndex = i;
|
||||
}
|
||||
|
||||
if (canvas != null) {
|
||||
if ((i == 1 && !typedWordValid) || (i == 0 && typedWordValid)) {
|
||||
paint.setFakeBoldText(true);
|
||||
paint.setColor(mColorRecommended);
|
||||
} else if (i != 0) {
|
||||
paint.setColor(mColorOther);
|
||||
}
|
||||
canvas.drawText(suggestion, x + X_GAP, y, paint);
|
||||
paint.setColor(mColorOther);
|
||||
canvas.drawLine(x + wordWidth + 0.5f, bgPadding.top,
|
||||
x + wordWidth + 0.5f, height + 1, paint);
|
||||
paint.setFakeBoldText(false);
|
||||
}
|
||||
x += wordWidth;
|
||||
}
|
||||
mTotalWidth = x;
|
||||
if (mTargetScrollX != getScrollX()) {
|
||||
scrollToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void scrollToTarget() {
|
||||
int sx = getScrollX();
|
||||
if (mTargetScrollX > sx) {
|
||||
sx += SCROLL_PIXELS;
|
||||
if (sx >= mTargetScrollX) {
|
||||
sx = mTargetScrollX;
|
||||
requestLayout();
|
||||
}
|
||||
} else {
|
||||
sx -= SCROLL_PIXELS;
|
||||
if (sx <= mTargetScrollX) {
|
||||
sx = mTargetScrollX;
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
scrollTo(sx, getScrollY());
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setSuggestions(List<String> suggestions, boolean completions,
|
||||
boolean typedWordValid) {
|
||||
clear();
|
||||
if (suggestions != null) {
|
||||
mSuggestions = new ArrayList<String>(suggestions);
|
||||
}
|
||||
mTypedWordValid = typedWordValid;
|
||||
scrollTo(0, 0);
|
||||
mTargetScrollX = 0;
|
||||
// Compute the total width
|
||||
onDraw(null);
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
mSuggestions = EMPTY_LIST;
|
||||
mTouchX = OUT_OF_BOUNDS;
|
||||
mSelectedIndex = -1;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent me) {
|
||||
|
||||
if (mGestureDetector.onTouchEvent(me)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int action = me.getAction();
|
||||
int x = (int) me.getX();
|
||||
int y = (int) me.getY();
|
||||
mTouchX = x;
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mScrolled = false;
|
||||
invalidate();
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (y <= 0) {
|
||||
// Fling up!?
|
||||
if (mSelectedIndex >= 0) {
|
||||
mService.pickSuggestionManually(mSelectedIndex);
|
||||
mSelectedIndex = -1;
|
||||
}
|
||||
}
|
||||
invalidate();
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (!mScrolled) {
|
||||
if (mSelectedIndex >= 0) {
|
||||
mService.pickSuggestionManually(mSelectedIndex);
|
||||
}
|
||||
}
|
||||
mSelectedIndex = -1;
|
||||
removeHighlight();
|
||||
requestLayout();
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* For flick through from keyboard, call this method with the x coordinate of the flick
|
||||
* gesture.
|
||||
* @param x
|
||||
*/
|
||||
public void takeSuggestionAt(float x) {
|
||||
mTouchX = (int) x;
|
||||
// To detect candidate
|
||||
onDraw(null);
|
||||
if (mSelectedIndex >= 0) {
|
||||
mService.pickSuggestionManually(mSelectedIndex);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void removeHighlight() {
|
||||
mTouchX = OUT_OF_BOUNDS;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Google Inc.
|
||||
*
|
||||
* 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.example.android.softkeyboard;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.inputmethodservice.Keyboard;
|
||||
|
||||
public class LatinKeyboard extends Keyboard {
|
||||
|
||||
public LatinKeyboard(Context context, int xmlLayoutResId) {
|
||||
super(context, xmlLayoutResId);
|
||||
}
|
||||
|
||||
public LatinKeyboard(Context context, int layoutTemplateResId,
|
||||
CharSequence characters, int columns, int horizontalPadding) {
|
||||
super(context, layoutTemplateResId, characters, columns, horizontalPadding);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
|
||||
XmlResourceParser parser) {
|
||||
return new LatinKey(res, parent, x, y, parser);
|
||||
}
|
||||
|
||||
static class LatinKey extends Keyboard.Key {
|
||||
|
||||
public LatinKey(Resources res, Keyboard.Row parent, int x, int y, XmlResourceParser parser) {
|
||||
super(res, parent, x, y, parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriding this method so that we can reduce the target area for the key that
|
||||
* closes the keyboard.
|
||||
*/
|
||||
@Override
|
||||
public boolean isInside(int x, int y) {
|
||||
return super.isInside(x, codes[0] == KEYCODE_CANCEL ? y - 10 : y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Google Inc.
|
||||
*
|
||||
* 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.example.android.softkeyboard;
|
||||
|
||||
import android.content.Context;
|
||||
import android.inputmethodservice.Keyboard;
|
||||
import android.inputmethodservice.KeyboardView;
|
||||
import android.inputmethodservice.Keyboard.Key;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
public class LatinKeyboardView extends KeyboardView {
|
||||
|
||||
static final int KEYCODE_OPTIONS = -100;
|
||||
|
||||
public LatinKeyboardView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onLongPress(Key key) {
|
||||
if (key.codes[0] == Keyboard.KEYCODE_CANCEL) {
|
||||
getOnKeyboardActionListener().onKey(KEYCODE_OPTIONS, null);
|
||||
return true;
|
||||
} else {
|
||||
return super.onLongPress(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Google Inc.
|
||||
*
|
||||
* 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.example.android.softkeyboard;
|
||||
|
||||
import android.content.Context;
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
import android.inputmethodservice.Keyboard;
|
||||
import android.inputmethodservice.KeyboardView;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Example of writing an input method for a soft keyboard. This code is
|
||||
* focused on simplicity over completeness, so it should in no way be considered
|
||||
* to be a complete soft keyboard implementation. Its purpose is to provide
|
||||
* a basic example for how you would get started writing an input method, to
|
||||
* be fleshed out as appropriate.
|
||||
*/
|
||||
public class SoftKeyboard extends InputMethodService
|
||||
implements KeyboardView.OnKeyboardActionListener {
|
||||
static final boolean DEBUG = false;
|
||||
|
||||
private KeyboardView mInputView;
|
||||
private CandidateView mCandidateView;
|
||||
private CompletionInfo[] mCompletions;
|
||||
|
||||
private StringBuilder mComposing = new StringBuilder();
|
||||
private boolean mPredicting;
|
||||
private boolean mPredictionOn;
|
||||
private boolean mCompletionOn;
|
||||
private int mLastDisplayWidth;
|
||||
private boolean mCapsLock;
|
||||
private long mLastShiftTime;
|
||||
|
||||
private Keyboard mSymbolsKeyboard;
|
||||
private Keyboard mSymbolsShiftedKeyboard;
|
||||
private Keyboard mQwertyKeyboard;
|
||||
|
||||
private String mWordSeparators;
|
||||
|
||||
private InputConnection mOldConnection;
|
||||
|
||||
private void makeKeyboards() {
|
||||
// Configuration change is coming after the keyboard gets recreated. So don't rely on that.
|
||||
// If keyboards have already been made, check if we have a screen width change and
|
||||
// create the keyboard layouts again at the correct orientation
|
||||
if (mQwertyKeyboard != null) {
|
||||
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
|
||||
int displayWidth = wm.getDefaultDisplay().getWidth();
|
||||
if (displayWidth == mLastDisplayWidth) return;
|
||||
mLastDisplayWidth = displayWidth;
|
||||
}
|
||||
mQwertyKeyboard = new LatinKeyboard(this, R.xml.qwerty);
|
||||
mSymbolsKeyboard = new LatinKeyboard(this, R.xml.symbols);
|
||||
mSymbolsShiftedKeyboard = new LatinKeyboard(this, R.xml.symbols_shift);
|
||||
}
|
||||
|
||||
@Override public void onCreate() {
|
||||
super.onCreate();
|
||||
//setStatusIcon(R.drawable.ime_qwerty);
|
||||
makeKeyboards();
|
||||
mWordSeparators = getResources().getString(R.string.word_separators);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateInputView() {
|
||||
makeKeyboards();
|
||||
mInputView = (KeyboardView) getLayoutInflater().inflate(
|
||||
R.layout.input, null);
|
||||
mInputView.setOnKeyboardActionListener(this);
|
||||
mInputView.setKeyboard(mQwertyKeyboard);
|
||||
return mInputView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateCandidatesView() {
|
||||
makeKeyboards();
|
||||
mCandidateView = new CandidateView(this);
|
||||
mCandidateView.setService(this);
|
||||
return mCandidateView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartInputView(EditorInfo attribute, boolean restarting) {
|
||||
// In landscape mode, this method gets called without the input view being created.
|
||||
if (mInputView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mQwertyKeyboard == null) {
|
||||
makeKeyboards();
|
||||
}
|
||||
|
||||
if (mOldConnection != null) {
|
||||
commitTyped(mOldConnection);
|
||||
}
|
||||
|
||||
mPredictionOn = false;
|
||||
mCompletionOn = false;
|
||||
mCompletions = null;
|
||||
switch (attribute.inputType&EditorInfo.TYPE_MASK_CLASS) {
|
||||
case EditorInfo.TYPE_CLASS_NUMBER:
|
||||
case EditorInfo.TYPE_CLASS_DATETIME:
|
||||
mInputView.setKeyboard(mSymbolsKeyboard);
|
||||
break;
|
||||
case EditorInfo.TYPE_CLASS_PHONE:
|
||||
mInputView.setKeyboard(mSymbolsKeyboard);
|
||||
break;
|
||||
default:
|
||||
mInputView.setKeyboard(mQwertyKeyboard);
|
||||
//startPrediction();
|
||||
mPredictionOn = true;
|
||||
// Make sure that passwords are not displayed in candidate view
|
||||
int variation = attribute.inputType & EditorInfo.TYPE_MASK_VARIATION;
|
||||
if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD) {
|
||||
mPredictionOn = false;
|
||||
}
|
||||
if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|
||||
|| variation == EditorInfo.TYPE_TEXT_VARIATION_URI) {
|
||||
mPredictionOn = false;
|
||||
}
|
||||
if ((attribute.inputType&EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
|
||||
mPredictionOn = false;
|
||||
mCompletionOn = isFullscreenMode();
|
||||
}
|
||||
updateShiftKeyState(attribute);
|
||||
break;
|
||||
}
|
||||
mInputView.closing();
|
||||
mComposing.setLength(0);
|
||||
mCandidateView.setSuggestions(null, false, false);
|
||||
mOldConnection = getCurrentInputConnection();
|
||||
setCandidatesViewShown(mPredictionOn || mCompletionOn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishInput() {
|
||||
super.onFinishInput();
|
||||
commitTyped(mOldConnection);
|
||||
if (mInputView != null) {
|
||||
mInputView.closing();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayCompletions(CompletionInfo[] completions) {
|
||||
if (false) {
|
||||
Log.i("foo", "Received completions:");
|
||||
for (int i=0; i<(completions != null ? completions.length : 0); i++) {
|
||||
Log.i("foo", " #" + i + ": " + completions[i]);
|
||||
}
|
||||
}
|
||||
if (mCompletionOn) {
|
||||
mCompletions = completions;
|
||||
if (completions == null) {
|
||||
mCandidateView.setSuggestions(null, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> stringList = new ArrayList<String>();
|
||||
for (int i=0; i<(completions != null ? completions.length : 0); i++) {
|
||||
CompletionInfo ci = completions[i];
|
||||
if (ci != null) stringList.add(ci.getText().toString());
|
||||
}
|
||||
mCandidateView.setSuggestions(stringList, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
if (event.getRepeatCount() == 0 && mInputView != null) {
|
||||
if (mInputView.handleBack()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
abortComposition();
|
||||
break;
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
// Enable shift key and DPAD to do selections
|
||||
if (mInputView != null && mInputView.isShown() && mInputView.isShifted()) {
|
||||
event = new KeyEvent(event.getDownTime(), event.getEventTime(),
|
||||
event.getAction(), event.getKeyCode(), event.getRepeatCount(),
|
||||
KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON);
|
||||
getCurrentInputConnection().sendKeyEvent(event);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
private void commitTyped(InputConnection inputConnection) {
|
||||
if (mComposing.length() > 0) {
|
||||
inputConnection.commitText(mComposing, mComposing.length());
|
||||
mComposing.setLength(0);
|
||||
updateCandidates();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateShiftKeyState(EditorInfo attr) {
|
||||
if (attr != null
|
||||
&& mInputView != null && mQwertyKeyboard == mInputView.getKeyboard()) {
|
||||
int caps = getCurrentInputConnection().getCursorCapsMode(attr.inputType);
|
||||
mInputView.setShifted(mCapsLock || caps != 0);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAlphabet(int code) {
|
||||
if (Character.isLetter(code)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void keyDownUp(int keyEventCode) {
|
||||
getCurrentInputConnection().sendKeyEvent(
|
||||
new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));
|
||||
getCurrentInputConnection().sendKeyEvent(
|
||||
new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
|
||||
}
|
||||
|
||||
private void sendKey(int keyCode) {
|
||||
switch (keyCode) {
|
||||
case 10:
|
||||
keyDownUp(KeyEvent.KEYCODE_ENTER);
|
||||
break;
|
||||
default:
|
||||
if (keyCode >= '0' && keyCode <= '9') {
|
||||
keyDownUp(keyCode - '0' + KeyEvent.KEYCODE_0);
|
||||
} else {
|
||||
getCurrentInputConnection().commitText(String.valueOf((char) keyCode), 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of KeyboardViewListener
|
||||
|
||||
public void onKey(int primaryCode, int[] keyCodes) {
|
||||
if (isWordSeparator(primaryCode)) {
|
||||
// Handle separator
|
||||
if (mPredicting) {
|
||||
commitTyped(getCurrentInputConnection());
|
||||
}
|
||||
sendKey(primaryCode);
|
||||
updateShiftKeyState(getCurrentInputInfo());
|
||||
} else if (primaryCode == Keyboard.KEYCODE_DELETE) {
|
||||
handleBackspace();
|
||||
} else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
|
||||
handleShift();
|
||||
} else if (primaryCode == Keyboard.KEYCODE_CANCEL) {
|
||||
handleClose();
|
||||
return;
|
||||
} else if (primaryCode == LatinKeyboardView.KEYCODE_OPTIONS) {
|
||||
// Show a menu or somethin'
|
||||
} else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE) {
|
||||
Keyboard current = mInputView.getKeyboard();
|
||||
if (current == mSymbolsKeyboard || current == mSymbolsShiftedKeyboard) {
|
||||
current = mQwertyKeyboard;
|
||||
} else {
|
||||
current = mSymbolsKeyboard;
|
||||
}
|
||||
mInputView.setKeyboard(current);
|
||||
if (current == mSymbolsKeyboard) {
|
||||
current.setShifted(false);
|
||||
}
|
||||
} else {
|
||||
handleCharacter(primaryCode, keyCodes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the list of available candidates from the current composing
|
||||
* text. This will need to be filled in by however you are determining
|
||||
* candidates.
|
||||
*/
|
||||
private void updateCandidates() {
|
||||
if (!mCompletionOn) {
|
||||
if (mComposing.length() > 0) {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
list.add(mComposing.toString());
|
||||
mCandidateView.setSuggestions(list, true, true);
|
||||
} else {
|
||||
mCandidateView.setSuggestions(null, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBackspace() {
|
||||
if (mPredicting) {
|
||||
final int length = mComposing.length();
|
||||
if (length > 0) {
|
||||
mComposing.delete(length - 1, length);
|
||||
getCurrentInputConnection().setComposingText(mComposing, mComposing.length());
|
||||
updateCandidates();
|
||||
if (mComposing.length() == 0) {
|
||||
mPredicting = false;
|
||||
}
|
||||
} else {
|
||||
getCurrentInputConnection().deleteSurroundingText(1, 0);
|
||||
}
|
||||
} else {
|
||||
//getCurrentInputConnection().deleteSurroundingText(1, 0);
|
||||
keyDownUp(KeyEvent.KEYCODE_DEL);
|
||||
}
|
||||
updateShiftKeyState(getCurrentInputInfo());
|
||||
}
|
||||
|
||||
private void handleShift() {
|
||||
Keyboard currentKeyboard = mInputView.getKeyboard();
|
||||
if (mQwertyKeyboard == currentKeyboard) {
|
||||
// Alphabet keyboard
|
||||
checkToggleCapsLock();
|
||||
mInputView.setShifted(mCapsLock || !mInputView.isShifted());
|
||||
} else if (currentKeyboard == mSymbolsKeyboard) {
|
||||
mSymbolsKeyboard.setShifted(true);
|
||||
mInputView.setKeyboard(mSymbolsShiftedKeyboard);
|
||||
mSymbolsShiftedKeyboard.setShifted(true);
|
||||
} else if (currentKeyboard == mSymbolsShiftedKeyboard) {
|
||||
mSymbolsShiftedKeyboard.setShifted(false);
|
||||
mInputView.setKeyboard(mSymbolsKeyboard);
|
||||
mSymbolsKeyboard.setShifted(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCharacter(int primaryCode, int[] keyCodes) {
|
||||
if (isAlphabet(primaryCode) && mPredictionOn && !isCursorTouchingWord()) {
|
||||
if (!mPredicting) {
|
||||
mPredicting = true;
|
||||
mComposing.setLength(0);
|
||||
}
|
||||
}
|
||||
if (mInputView.isShifted()) {
|
||||
primaryCode = Character.toUpperCase(primaryCode);
|
||||
}
|
||||
mComposing.append((char) primaryCode);
|
||||
getCurrentInputConnection().setComposingText(mComposing, mComposing.length());
|
||||
updateShiftKeyState(getCurrentInputInfo());
|
||||
updateCandidates();
|
||||
}
|
||||
|
||||
private void handleClose() {
|
||||
commitTyped(getCurrentInputConnection());
|
||||
dismissSoftInput();
|
||||
mInputView.closing();
|
||||
}
|
||||
|
||||
private void checkToggleCapsLock() {
|
||||
long now = System.currentTimeMillis();
|
||||
if (mLastShiftTime + 800 > now) {
|
||||
mCapsLock = !mCapsLock;
|
||||
mLastShiftTime = 0;
|
||||
} else {
|
||||
mLastShiftTime = now;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCursorTouchingWord() {
|
||||
CharSequence toLeft = getCurrentInputConnection().getTextBeforeCursor(1);
|
||||
CharSequence toRight = getCurrentInputConnection().getTextAfterCursor(1);
|
||||
if (!TextUtils.isEmpty(toLeft)
|
||||
&& !isWordSeparator(toLeft.charAt(0))) {
|
||||
return true;
|
||||
}
|
||||
if (!TextUtils.isEmpty(toRight)
|
||||
&& !isWordSeparator(toRight.charAt(0))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected String getWordSeparators() {
|
||||
return mWordSeparators;
|
||||
}
|
||||
|
||||
public boolean isWordSeparator(int code) {
|
||||
String separators = getWordSeparators();
|
||||
return separators.contains(String.valueOf((char)code));
|
||||
}
|
||||
|
||||
public void pickDefaultCandidate() {
|
||||
pickSuggestionManually(0);
|
||||
}
|
||||
|
||||
public void pickSuggestionManually(int index) {
|
||||
if (mCompletionOn && mCompletions != null && index >= 0
|
||||
&& index < mCompletions.length) {
|
||||
CompletionInfo ci = mCompletions[index];
|
||||
getCurrentInputConnection().commitCompletion(ci);
|
||||
if (mCandidateView != null) {
|
||||
mCandidateView.clear();
|
||||
}
|
||||
updateShiftKeyState(getCurrentInputInfo());
|
||||
}
|
||||
}
|
||||
|
||||
public void swipeRight() {
|
||||
if (mCompletionOn) {
|
||||
pickDefaultCandidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void swipeLeft() {
|
||||
handleBackspace();
|
||||
}
|
||||
|
||||
public void swipeDown() {
|
||||
handleClose();
|
||||
}
|
||||
|
||||
public void swipeUp() {
|
||||
// ?
|
||||
}
|
||||
|
||||
private void abortComposition() {
|
||||
commitTyped(getCurrentInputConnection());
|
||||
mComposing.setLength(0); // Don't want to allow uncommit after this
|
||||
updateCandidates();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user