Refactor Keyboard and KeyboardView resizing and drawing code

Bug: 4311428
Change-Id: Ice4050f92c8f3cec1bec2074fe6a913d04f50524
main
Tadashi G. Takaoka 2011-07-20 23:57:00 -07:00
parent a3dd3887c9
commit 38f55b36c3
7 changed files with 129 additions and 63 deletions

View File

@ -125,10 +125,10 @@ public class KeyboardId {
mEnableShiftLock); mEnableShiftLock);
} }
public KeyboardId cloneWithNewGeometry(int width) { public KeyboardId cloneWithNewGeometry(int orientation, int width) {
if (mWidth == width) if (mWidth == width)
return this; return this;
return new KeyboardId(mXmlName, mXmlId, mLocale, mOrientation, width, mMode, mAttribute, return new KeyboardId(mXmlName, mXmlId, mLocale, orientation, width, mMode, mAttribute,
mHasSettingsKey, mF2KeyMode, mClobberSettingsKey, mVoiceKeyEnabled, mHasVoiceKey, mHasSettingsKey, mF2KeyMode, mClobberSettingsKey, mVoiceKeyEnabled, mHasVoiceKey,
mEnableShiftLock); mEnableShiftLock);
} }

View File

@ -106,7 +106,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
private int mThemeIndex = -1; private int mThemeIndex = -1;
private Context mThemeContext; private Context mThemeContext;
private int mKeyboardWidth; private int mWindowWidth;
private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
@ -187,13 +187,24 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
setKeyboard(getKeyboard(id)); setKeyboard(getKeyboard(id));
} }
public void onSizeChanged() { @SuppressWarnings("unused")
public void onSizeChanged(int w, int h, int oldw, int oldh) {
final int width = mInputMethodService.getWindow().getWindow().getDecorView().getWidth(); final int width = mInputMethodService.getWindow().getWindow().getDecorView().getWidth();
// If the window width hasn't fixed yet or keyboard doesn't exist, nothing to do with.
if (width == 0 || mCurrentId == null) if (width == 0 || mCurrentId == null)
return; return;
mKeyboardWidth = width; // The window width is fixed.
// Set keyboard with new width. mWindowWidth = width;
final KeyboardId newId = mCurrentId.cloneWithNewGeometry(width); // If this is the first time the {@link KeyboardView} has been shown, no need to reload
// keyboard.
if (oldw == 0 && oldh == 0)
return;
// Reload keyboard with new width.
final int orientation = mInputMethodService.getResources().getConfiguration().orientation;
final KeyboardId newId = mCurrentId.cloneWithNewGeometry(orientation, width);
// If the new keyboard is the same as the current one, no need to reload it.
if (newId.equals(mCurrentId))
return;
setKeyboard(getKeyboard(newId)); setKeyboard(getKeyboard(newId));
} }
@ -289,11 +300,11 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
attribute); attribute);
final Resources res = mInputMethodService.getResources(); final Resources res = mInputMethodService.getResources();
final int orientation = res.getConfiguration().orientation; final int orientation = res.getConfiguration().orientation;
if (mKeyboardWidth == 0) if (mWindowWidth == 0)
mKeyboardWidth = res.getDisplayMetrics().widthPixels; mWindowWidth = res.getDisplayMetrics().widthPixels;
final Locale locale = mSubtypeSwitcher.getInputLocale(); final Locale locale = mSubtypeSwitcher.getInputLocale();
return new KeyboardId( return new KeyboardId(
res.getResourceEntryName(xmlId), xmlId, locale, orientation, mKeyboardWidth, res.getResourceEntryName(xmlId), xmlId, locale, orientation, mWindowWidth,
mode, attribute, hasSettingsKey, f2KeyMode, clobberSettingsKey, mVoiceKeyEnabled, mode, attribute, hasSettingsKey, f2KeyMode, clobberSettingsKey, mVoiceKeyEnabled,
hasVoiceKey, enableShiftLock); hasVoiceKey, enableShiftLock);
} }

View File

@ -73,8 +73,6 @@ import java.util.HashMap;
* @attr ref R.styleable#KeyboardView_shadowRadius * @attr ref R.styleable#KeyboardView_shadowRadius
*/ */
public class KeyboardView extends View implements PointerTracker.DrawingProxy { public class KeyboardView extends View implements PointerTracker.DrawingProxy {
private static final boolean DEBUG_KEYBOARD_GRID = false;
// Miscellaneous constants // Miscellaneous constants
private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable }; private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
@ -97,17 +95,15 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
private ViewGroup mPreviewPlacer; private ViewGroup mPreviewPlacer;
// Drawing // Drawing
/** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/ /** Whether the keyboard bitmap buffer needs to be redrawn before it's blitted. **/
private boolean mDrawPending; private boolean mBufferNeedsUpdate;
/** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */
private boolean mKeyboardChanged;
/** The dirty region in the keyboard bitmap */ /** The dirty region in the keyboard bitmap */
private final Rect mDirtyRect = new Rect(); private final Rect mDirtyRect = new Rect();
/** The key to invalidate. */ /** The key to invalidate. */
private Key mInvalidatedKey; private Key mInvalidatedKey;
/** The dirty region for single key drawing */ /** The dirty region for single key drawing */
private final Rect mInvalidatedKeyRect = new Rect(); private final Rect mInvalidatedKeyRect = new Rect();
/** The keyboard bitmap for faster updates */ /** The keyboard bitmap buffer for faster updates */
private Bitmap mBuffer; private Bitmap mBuffer;
/** The canvas for the above mutable keyboard bitmap */ /** The canvas for the above mutable keyboard bitmap */
private Canvas mCanvas; private Canvas mCanvas;
@ -361,7 +357,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
mKeyboard = keyboard; mKeyboard = keyboard;
LatinImeLogger.onSetKeyboard(keyboard); LatinImeLogger.onSetKeyboard(keyboard);
requestLayout(); requestLayout();
mKeyboardChanged = true; mDirtyRect.set(0, 0, getWidth(), getHeight());
mBufferNeedsUpdate = true;
invalidateAllKeys(); invalidateAllKeys();
final int keyHeight = keyboard.getRowHeight() - keyboard.getVerticalGap(); final int keyHeight = keyboard.getRowHeight() - keyboard.getVerticalGap();
mKeyDrawParams.updateKeyHeight(keyHeight); mKeyDrawParams.updateKeyHeight(keyHeight);
@ -399,25 +396,21 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
} }
@Override @Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Round up a little if (mKeyboard != null) {
if (mKeyboard == null) { // The main keyboard expands to the display width.
setMeasuredDimension( final int height = mKeyboard.getKeyboardHeight() + getPaddingTop() + getPaddingBottom();
getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom()); setMeasuredDimension(widthMeasureSpec, height);
} else { } else {
int width = mKeyboard.getMinWidth() + getPaddingLeft() + getPaddingRight(); super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) {
width = MeasureSpec.getSize(widthMeasureSpec);
}
setMeasuredDimension(
width, mKeyboard.getHeight() + getPaddingTop() + getPaddingBottom());
} }
} }
@Override @Override
public void onDraw(Canvas canvas) { public void onDraw(Canvas canvas) {
super.onDraw(canvas); super.onDraw(canvas);
if (mDrawPending || mBuffer == null || mKeyboardChanged) { if (mBufferNeedsUpdate || mBuffer == null) {
mBufferNeedsUpdate = false;
onBufferDraw(); onBufferDraw();
} }
canvas.drawBitmap(mBuffer, 0, 0, null); canvas.drawBitmap(mBuffer, 0, 0, null);
@ -428,14 +421,11 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
final int height = getHeight(); final int height = getHeight();
if (width == 0 || height == 0) if (width == 0 || height == 0)
return; return;
if (mBuffer == null || mKeyboardChanged) {
mKeyboardChanged = false;
mDirtyRect.union(0, 0, width, height);
}
if (mBuffer == null || mBuffer.getWidth() != width || mBuffer.getHeight() != height) { if (mBuffer == null || mBuffer.getWidth() != width || mBuffer.getHeight() != height) {
if (mBuffer != null) if (mBuffer != null)
mBuffer.recycle(); mBuffer.recycle();
mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mDirtyRect.union(0, 0, width, height);
if (mCanvas != null) { if (mCanvas != null) {
mCanvas.setBitmap(mBuffer); mCanvas.setBitmap(mBuffer);
} else { } else {
@ -469,24 +459,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
} }
} }
// TODO: Move this function to ProximityInfo for getting rid of
// public declarations for
// GRID_WIDTH and GRID_HEIGHT
if (DEBUG_KEYBOARD_GRID) {
Paint p = new Paint();
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(1.0f);
p.setColor(0x800000c0);
int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1)
/ mKeyboard.GRID_WIDTH;
int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1)
/ mKeyboard.GRID_HEIGHT;
for (int i = 0; i <= mKeyboard.GRID_WIDTH; i++)
canvas.drawLine(i * cw, 0, i * cw, ch * mKeyboard.GRID_HEIGHT, p);
for (int i = 0; i <= mKeyboard.GRID_HEIGHT; i++)
canvas.drawLine(0, i * ch, cw * mKeyboard.GRID_WIDTH, i * ch, p);
}
// Overlay a dark rectangle to dim the keyboard // Overlay a dark rectangle to dim the keyboard
if (needsToDimKeyboard()) { if (needsToDimKeyboard()) {
mPaint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24); mPaint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
@ -494,7 +466,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
} }
mInvalidatedKey = null; mInvalidatedKey = null;
mDrawPending = false;
mDirtyRect.setEmpty(); mDirtyRect.setEmpty();
} }
@ -864,7 +835,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
*/ */
public void invalidateAllKeys() { public void invalidateAllKeys() {
mDirtyRect.union(0, 0, getWidth(), getHeight()); mDirtyRect.union(0, 0, getWidth(), getHeight());
mDrawPending = true; mBufferNeedsUpdate = true;
invalidate(); invalidate();
} }
@ -884,7 +855,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
final int y = key.mY + getPaddingTop(); final int y = key.mY + getPaddingTop();
mInvalidatedKeyRect.set(x, y, x + key.mWidth, y + key.mHeight); mInvalidatedKeyRect.set(x, y, x + key.mWidth, y + key.mHeight);
mDirtyRect.union(mInvalidatedKeyRect); mDirtyRect.union(mInvalidatedKeyRect);
onBufferDraw(); mBufferNeedsUpdate = true;
invalidate(mInvalidatedKeyRect); invalidate(mInvalidatedKeyRect);
} }

View File

@ -269,7 +269,7 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
@Override @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) { protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// TODO: Should notify InputMethodService instead? // TODO: Should notify InputMethodService instead?
KeyboardSwitcher.getInstance().onSizeChanged(); KeyboardSwitcher.getInstance().onSizeChanged(w, h, oldw, oldh);
} }
/** /**

View File

@ -107,6 +107,18 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
setKeyPreviewPopupEnabled(false, 0); setKeyPreviewPopupEnabled(false, 0);
} }
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final Keyboard keyboard = getKeyboard();
if (keyboard != null) {
final int width = keyboard.getMinWidth() + getPaddingLeft() + getPaddingRight();
final int height = keyboard.getKeyboardHeight() + getPaddingTop() + getPaddingBottom();
setMeasuredDimension(width, height);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@Override @Override
public void setKeyboard(Keyboard keyboard) { public void setKeyboard(Keyboard keyboard) {
super.setKeyboard(keyboard); super.setKeyboard(keyboard);

View File

@ -218,7 +218,7 @@ public class MiniKeyboardBuilder {
mParams = params; mParams = params;
keyboard.setRowHeight(params.mRowHeight); keyboard.setRowHeight(params.mRowHeight);
keyboard.setHeight(params.getKeyboardHeight()); keyboard.setKeyboardHeight(params.getKeyboardHeight());
keyboard.setMinWidth(params.getKeyboardWidth()); keyboard.setMinWidth(params.getKeyboardWidth());
keyboard.setDefaultCoordX(params.getDefaultKeyCoordX() + params.mKeyWidth / 2); keyboard.setDefaultCoordX(params.getDefaultKeyCoordX() + params.mKeyWidth / 2);
} }

View File

@ -173,7 +173,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private int mCorrectionMode; private int mCorrectionMode;
private int mCommittedLength; private int mCommittedLength;
private int mOrientation;
// Keep track of the last selection range to decide if we need to show word alternatives // Keep track of the last selection range to decide if we need to show word alternatives
private int mLastSelectionStart; private int mLastSelectionStart;
private int mLastSelectionEnd; private int mLastSelectionEnd;
@ -192,6 +191,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// TODO: Move this flag to VoiceProxy // TODO: Move this flag to VoiceProxy
private boolean mConfigurationChanging; private boolean mConfigurationChanging;
// Member variables for remembering the current device orientation.
private int mDisplayOrientation;
private int mDisplayWidth;
private int mDisplayHeight;
// Object for reacting to adding/removing a dictionary pack. // Object for reacting to adding/removing a dictionary pack.
private BroadcastReceiver mDictionaryPackInstallReceiver = private BroadcastReceiver mDictionaryPackInstallReceiver =
new DictionaryPackInstallBroadcastReceiver(this); new DictionaryPackInstallBroadcastReceiver(this);
@ -199,7 +203,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// Keeps track of most recently inserted text (multi-character key) for reverting // Keeps track of most recently inserted text (multi-character key) for reverting
private CharSequence mEnteredText; private CharSequence mEnteredText;
public final UIHandler mHandler = new UIHandler(this); public final UIHandler mHandler = new UIHandler(this);
public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> { public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
@ -211,6 +214,29 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private static final int MSG_DISMISS_LANGUAGE_ON_SPACEBAR = 5; private static final int MSG_DISMISS_LANGUAGE_ON_SPACEBAR = 5;
private static final int MSG_SPACE_TYPED = 6; private static final int MSG_SPACE_TYPED = 6;
private static final int MSG_SET_BIGRAM_PREDICTIONS = 7; private static final int MSG_SET_BIGRAM_PREDICTIONS = 7;
private static final int MSG_CONFIRM_ORIENTATION_CHANGE = 8;
private static final int MSG_START_INPUT_VIEW = 9;
private static class OrientationChangeArgs {
public final int mOldWidth;
public final int mOldHeight;
private int mRetryCount;
public OrientationChangeArgs(int oldw, int oldh) {
mOldWidth = oldw;
mOldHeight = oldh;
mRetryCount = 0;
}
public boolean hasTimedOut() {
mRetryCount++;
return mRetryCount >= 10;
}
public boolean hasOrientationChangeFinished(DisplayMetrics dm) {
return dm.widthPixels != mOldWidth && dm.heightPixels != mOldHeight;
}
}
public UIHandler(LatinIME outerInstance) { public UIHandler(LatinIME outerInstance) {
super(outerInstance); super(outerInstance);
@ -259,6 +285,21 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
(LatinKeyboard)msg.obj); (LatinKeyboard)msg.obj);
} }
break; break;
case MSG_CONFIRM_ORIENTATION_CHANGE: {
final OrientationChangeArgs args = (OrientationChangeArgs)msg.obj;
final Resources res = latinIme.mResources;
final DisplayMetrics dm = res.getDisplayMetrics();
if (args.hasTimedOut() || args.hasOrientationChangeFinished(dm)) {
latinIme.setDisplayGeometry(res.getConfiguration(), dm);
} else {
// It seems orientation changing is on going.
postConfirmOrientationChange(args);
}
break;
}
case MSG_START_INPUT_VIEW:
latinIme.onStartInputView((EditorInfo)msg.obj, false);
break;
} }
} }
@ -348,6 +389,33 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
public boolean isAcceptingDoubleSpaces() { public boolean isAcceptingDoubleSpaces() {
return hasMessages(MSG_SPACE_TYPED); return hasMessages(MSG_SPACE_TYPED);
} }
private void postConfirmOrientationChange(OrientationChangeArgs args) {
removeMessages(MSG_CONFIRM_ORIENTATION_CHANGE);
// Will confirm whether orientation change has finished or not after 2ms again.
sendMessageDelayed(obtainMessage(MSG_CONFIRM_ORIENTATION_CHANGE, args), 2);
}
public void startOrientationChanging(int oldw, int oldh) {
postConfirmOrientationChange(new OrientationChangeArgs(oldw, oldh));
}
public boolean postStartInputView(EditorInfo attribute) {
if (hasMessages(MSG_CONFIRM_ORIENTATION_CHANGE) || hasMessages(MSG_START_INPUT_VIEW)) {
removeMessages(MSG_START_INPUT_VIEW);
// Postpone onStartInputView 20ms afterward and see if orientation change has
// finished.
sendMessageDelayed(obtainMessage(MSG_START_INPUT_VIEW, attribute), 20);
return true;
}
return false;
}
}
private void setDisplayGeometry(Configuration conf, DisplayMetrics metric) {
mDisplayOrientation = conf.orientation;
mDisplayWidth = metric.widthPixels;
mDisplayHeight = metric.heightPixels;
} }
@Override @Override
@ -387,7 +455,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
} }
} }
mOrientation = res.getConfiguration().orientation; setDisplayGeometry(res.getConfiguration(), res.getDisplayMetrics());
// Register to receive ringer mode change and network state change. // Register to receive ringer mode change and network state change.
// Also receive installation and removal of a dictionary pack. // Also receive installation and removal of a dictionary pack.
@ -484,11 +552,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
public void onConfigurationChanged(Configuration conf) { public void onConfigurationChanged(Configuration conf) {
mSubtypeSwitcher.onConfigurationChanged(conf); mSubtypeSwitcher.onConfigurationChanged(conf);
// If orientation changed while predicting, commit the change // If orientation changed while predicting, commit the change
if (conf.orientation != mOrientation) { if (conf.orientation != mDisplayOrientation) {
mHandler.startOrientationChanging(mDisplayWidth, mDisplayHeight);
InputConnection ic = getCurrentInputConnection(); InputConnection ic = getCurrentInputConnection();
commitTyped(ic); commitTyped(ic);
if (ic != null) ic.finishComposingText(); // For voice input if (ic != null) ic.finishComposingText(); // For voice input
mOrientation = conf.orientation;
if (isShowingOptionDialog()) if (isShowingOptionDialog())
mOptionsDialog.dismiss(); mOptionsDialog.dismiss();
} }
@ -525,6 +593,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
@Override @Override
public void onStartInputView(EditorInfo attribute, boolean restarting) { public void onStartInputView(EditorInfo attribute, boolean restarting) {
if (mHandler.postStartInputView(attribute)) {
return;
}
final KeyboardSwitcher switcher = mKeyboardSwitcher; final KeyboardSwitcher switcher = mKeyboardSwitcher;
LatinKeyboardView inputView = switcher.getKeyboardView(); LatinKeyboardView inputView = switcher.getKeyboardView();
@ -1446,7 +1518,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
public boolean isShowingSuggestionsStrip() { public boolean isShowingSuggestionsStrip() {
return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE) return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
|| (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
&& mOrientation == Configuration.ORIENTATION_PORTRAIT); && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT);
} }
public boolean isCandidateStripVisible() { public boolean isCandidateStripVisible() {