From 3f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6 Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Fri, 29 Jan 2010 20:09:49 -0800 Subject: [PATCH] Language switching with slide gesture on spacebar. Bug: 2331173 Shows the language on the spacebar and in the preview bubble. Allows dragging of the spacebar from side to side to switch to previous or next languages. --- .../inputmethod/latin/KeyboardSwitcher.java | 14 +- .../inputmethod/latin/LanguageSwitcher.java | 165 +++++++++ .../android/inputmethod/latin/LatinIME.java | 80 ++--- .../inputmethod/latin/LatinKeyboard.java | 319 +++++++++++++++++- .../inputmethod/latin/LatinKeyboardView.java | 35 +- .../inputmethod/latin/TextEntryState.java | 1 + 6 files changed, 533 insertions(+), 81 deletions(-) create mode 100644 src/com/android/inputmethod/latin/LanguageSwitcher.java diff --git a/src/com/android/inputmethod/latin/KeyboardSwitcher.java b/src/com/android/inputmethod/latin/KeyboardSwitcher.java index ea473f223..0ebfe4a9a 100644 --- a/src/com/android/inputmethod/latin/KeyboardSwitcher.java +++ b/src/com/android/inputmethod/latin/KeyboardSwitcher.java @@ -77,6 +77,7 @@ public class KeyboardSwitcher { private int mSymbolsModeState = SYMBOLS_MODE_STATE_NONE; private int mLastDisplayWidth; + private LanguageSwitcher mLanguageSwitcher; private Locale mInputLocale; private boolean mEnableMultipleLanguages; @@ -94,9 +95,10 @@ public class KeyboardSwitcher { * @param locale the current input locale, or null for default locale with no locale * button. */ - void setInputLocale(Locale locale, boolean enableMultipleLanguages) { - mInputLocale = locale; - mEnableMultipleLanguages = enableMultipleLanguages; + void setLanguageSwitcher(LanguageSwitcher languageSwitcher) { + mLanguageSwitcher = languageSwitcher; + mInputLocale = mLanguageSwitcher.getInputLocale(); + mEnableMultipleLanguages = mLanguageSwitcher.getLocaleCount() > 1; } void setInputView(LatinKeyboardView inputView) { @@ -195,11 +197,6 @@ public class KeyboardSwitcher { } mCurrentId = id; - if (mEnableMultipleLanguages) { - keyboard.setLanguage(mInputLocale); - } else { - keyboard.setLanguage(null); - } mInputView.setKeyboard(keyboard); keyboard.setShifted(false); keyboard.setShiftLocked(keyboard.isShiftLocked()); @@ -215,6 +212,7 @@ public class KeyboardSwitcher { orig.updateConfiguration(conf, null); LatinKeyboard keyboard = new LatinKeyboard( mContext, id.mXml, id.mKeyboardMode, id.mHasVoice); + keyboard.setLanguageSwitcher(mLanguageSwitcher); if (id.mKeyboardMode == KEYBOARDMODE_NORMAL || id.mKeyboardMode == KEYBOARDMODE_URL || id.mKeyboardMode == KEYBOARDMODE_IM diff --git a/src/com/android/inputmethod/latin/LanguageSwitcher.java b/src/com/android/inputmethod/latin/LanguageSwitcher.java new file mode 100644 index 000000000..97173533d --- /dev/null +++ b/src/com/android/inputmethod/latin/LanguageSwitcher.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2010 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.android.inputmethod.latin; + +import java.util.Locale; + +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.preference.PreferenceManager; + +/** + * Keeps track of list of selected input languages and the current + * input language that the user has selected. + */ +public class LanguageSwitcher { + + private Locale[] mLocales; + private LatinIME mIme; + private String[] mSelectedLanguageArray; + private String mSelectedLanguages; + private int mCurrentIndex = 0; + private String mDefaultInputLanguage; + private Locale mDefaultInputLocale; + + public LanguageSwitcher(LatinIME ime) { + mIme = ime; + mLocales = new Locale[0]; + } + + public Locale[] getLocales() { + return mLocales; + } + + public int getLocaleCount() { + return mLocales.length; + } + + /** + * Loads the currently selected input languages from shared preferences. + * @param sp + * @return whether there was any change + */ + public boolean loadLocales(SharedPreferences sp) { + String selectedLanguages = sp.getString(LatinIME.PREF_SELECTED_LANGUAGES, null); + String currentLanguage = sp.getString(LatinIME.PREF_INPUT_LANGUAGE, null); + if (selectedLanguages == null || selectedLanguages.length() < 1) { + loadDefaults(); + if (mLocales.length == 0) { + return false; + } + mLocales = new Locale[0]; + return true; + } + if (selectedLanguages.equals(mSelectedLanguages)) { + return false; + } + mSelectedLanguageArray = selectedLanguages.split(","); + mSelectedLanguages = selectedLanguages; // Cache it for comparison later + constructLocales(); + mCurrentIndex = 0; + if (currentLanguage != null) { + // Find the index + mCurrentIndex = 0; + for (int i = 0; i < mLocales.length; i++) { + if (mSelectedLanguageArray[i].equals(currentLanguage)) { + mCurrentIndex = i; + break; + } + } + // If we didn't find the index, use the first one + } + return true; + } + + private void loadDefaults() { + mDefaultInputLocale = mIme.getResources().getConfiguration().locale; + mDefaultInputLanguage = mDefaultInputLocale.getLanguage() + "_" + + mDefaultInputLocale.getCountry(); + } + + private void constructLocales() { + mLocales = new Locale[mSelectedLanguageArray.length]; + for (int i = 0; i < mLocales.length; i++) { + mLocales[i] = new Locale(mSelectedLanguageArray[i]); + } + } + + /** + * Returns the currently selected input language code, or the display language code if + * no specific locale was selected for input. + */ + public String getInputLanguage() { + if (getLocaleCount() == 0) return mDefaultInputLanguage; + + return mSelectedLanguageArray[mCurrentIndex]; + } + + /** + * Returns the currently selected input locale, or the display locale if no specific + * locale was selected for input. + * @return + */ + public Locale getInputLocale() { + if (getLocaleCount() == 0) return mDefaultInputLocale; + + return mLocales[mCurrentIndex]; + } + + /** + * Returns the next input locale in the list. Wraps around to the beginning of the + * list if we're at the end of the list. + * @return + */ + public Locale getNextInputLocale() { + if (getLocaleCount() == 0) return mDefaultInputLocale; + + return mLocales[(mCurrentIndex + 1) % mLocales.length]; + } + + /** + * Returns the previous input locale in the list. Wraps around to the end of the + * list if we're at the beginning of the list. + * @return + */ + public Locale getPrevInputLocale() { + if (getLocaleCount() == 0) return mDefaultInputLocale; + + return mLocales[(mCurrentIndex - 1 + mLocales.length) % mLocales.length]; + } + + public void reset() { + mCurrentIndex = 0; + } + + public void next() { + mCurrentIndex++; + if (mCurrentIndex >= mLocales.length) mCurrentIndex = 0; // Wrap around + } + + public void prev() { + mCurrentIndex--; + if (mCurrentIndex < 0) mCurrentIndex = mLocales.length - 1; // Wrap around + } + + public void persist() { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mIme); + Editor editor = sp.edit(); + editor.putString(LatinIME.PREF_INPUT_LANGUAGE, getInputLanguage()); + editor.commit(); + } +} diff --git a/src/com/android/inputmethod/latin/LatinIME.java b/src/com/android/inputmethod/latin/LatinIME.java index 8056030f6..ee525b495 100644 --- a/src/com/android/inputmethod/latin/LatinIME.java +++ b/src/com/android/inputmethod/latin/LatinIME.java @@ -178,6 +178,7 @@ public class LatinIME extends InputMethodService Resources mResources; private String mLocale; + private LanguageSwitcher mLanguageSwitcher; private StringBuilder mComposing = new StringBuilder(); private WordComposer mWord = new WordComposer(); @@ -244,10 +245,6 @@ public class LatinIME extends InputMethodService List candidates; Map> alternatives; } - private int mCurrentInputLocale = 0; - private String mInputLanguage; - private String[] mSelectedLanguageArray; - private String mSelectedLanguagesList; private boolean mRefreshKeyboardRequired; Handler mHandler = new Handler() { @@ -285,18 +282,19 @@ public class LatinIME extends InputMethodService @Override public void onCreate() { super.onCreate(); //setStatusIcon(R.drawable.ime_qwerty); - mKeyboardSwitcher = new KeyboardSwitcher(this, this); mResources = getResources(); final Configuration conf = mResources.getConfiguration(); - mInputLanguage = getPersistedInputLanguage(); - mSelectedLanguagesList = getSelectedInputLanguages(); - boolean enableMultipleLanguages = mSelectedLanguagesList != null - && mSelectedLanguagesList.split(",").length > 1; - if (mInputLanguage == null) { - mInputLanguage = conf.locale.toString(); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + mLanguageSwitcher = new LanguageSwitcher(this); + mLanguageSwitcher.loadLocales(prefs); + mKeyboardSwitcher = new KeyboardSwitcher(this, this); + mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher); + boolean enableMultipleLanguages = mLanguageSwitcher.getLocaleCount() > 0; + String inputLanguage = mLanguageSwitcher.getInputLanguage(); + if (inputLanguage == null) { + inputLanguage = conf.locale.toString(); } - initSuggest(mInputLanguage); - mKeyboardSwitcher.setInputLocale(conf.locale, enableMultipleLanguages); + initSuggest(inputLanguage); mOrientation = conf.orientation; mVibrateDuration = mResources.getInteger(R.integer.vibrate_duration_ms); @@ -317,8 +315,7 @@ public class LatinIME extends InputMethodService } }); } - PreferenceManager.getDefaultSharedPreferences(this) - .registerOnSharedPreferenceChangeListener(this); + prefs.registerOnSharedPreferenceChangeListener(this); } private void initSuggest(String locale) { @@ -429,7 +426,7 @@ public class LatinIME extends InputMethodService if (mRefreshKeyboardRequired) { mRefreshKeyboardRequired = false; - toggleLanguage(true); + toggleLanguage(true, true); } mKeyboardSwitcher.makeKeyboards(false); @@ -791,8 +788,7 @@ public class LatinIME extends InputMethodService if (mKeyboardSwitcher == null) { mKeyboardSwitcher = new KeyboardSwitcher(this, this); } - mKeyboardSwitcher.setInputLocale(new Locale(mInputLanguage), - getSelectedInputLanguages() != null); + mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher); if (mInputView != null) { mKeyboardSwitcher.setVoiceMode(mEnableVoice, mVoiceOnPrimary); } @@ -919,7 +915,10 @@ public class LatinIME extends InputMethodService showOptionsMenu(); break; case LatinKeyboardView.KEYCODE_NEXT_LANGUAGE: - toggleLanguage(false); + toggleLanguage(false, true); + break; + case LatinKeyboardView.KEYCODE_PREV_LANGUAGE: + toggleLanguage(false, false); break; case LatinKeyboardView.KEYCODE_SHIFT_LONGPRESS: if (mCapsLock) { @@ -1514,27 +1513,30 @@ public class LatinIME extends InputMethodService } } - private void toggleLanguage(boolean reset) { - final String [] languages = mSelectedLanguageArray; - if (reset) mCurrentInputLocale = -1; - mCurrentInputLocale = (mCurrentInputLocale + 1) - % (languages != null ? languages.length : 1); - mInputLanguage = languages != null ? languages[mCurrentInputLocale] : - getResources().getConfiguration().locale.getLanguage(); + private void toggleLanguage(boolean reset, boolean next) { + if (reset) { + mLanguageSwitcher.reset(); + } else { + if (next) { + mLanguageSwitcher.next(); + } else { + mLanguageSwitcher.prev(); + } + } int currentKeyboardMode = mKeyboardSwitcher.getKeyboardMode(); reloadKeyboards(); mKeyboardSwitcher.makeKeyboards(true); mKeyboardSwitcher.setKeyboardMode(currentKeyboardMode, 0, mEnableVoiceButton && mEnableVoice); - initSuggest(mInputLanguage); - persistInputLanguage(mInputLanguage); + initSuggest(mLanguageSwitcher.getInputLanguage()); + mLanguageSwitcher.persist(); updateShiftKeyState(getCurrentInputEditorInfo()); } public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (PREF_SELECTED_LANGUAGES.equals(key)) { - updateSelectedLanguages(sharedPreferences.getString(key, null)); + mLanguageSwitcher.loadLocales(sharedPreferences); mRefreshKeyboardRequired = true; } } @@ -1556,6 +1558,8 @@ public class LatinIME extends InputMethodService } public void onRelease(int primaryCode) { + // Reset any drag flags in the keyboard + ((LatinKeyboard) mInputView.getKeyboard()).keyReleased(); //vibrate(); } @@ -1750,16 +1754,7 @@ public class LatinIME extends InputMethodService mAutoCorrectEnabled = sp.getBoolean(PREF_AUTO_COMPLETE, mResources.getBoolean(R.bool.enable_autocorrect)) & mShowSuggestions; updateCorrectionMode(); - String languageList = sp.getString(PREF_SELECTED_LANGUAGES, null); - updateSelectedLanguages(languageList); - } - - private void updateSelectedLanguages(String languageList) { - if (languageList != null && languageList.length() > 1) { - mSelectedLanguageArray = languageList.split(","); - } else { - mSelectedLanguageArray = null; - } + mLanguageSwitcher.loadLocales(sp); } private String getPersistedInputLanguage() { @@ -1767,13 +1762,6 @@ public class LatinIME extends InputMethodService return sp.getString(PREF_INPUT_LANGUAGE, null); } - private void persistInputLanguage(String inputLanguage) { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); - Editor editor = sp.edit(); - editor.putString(PREF_INPUT_LANGUAGE, inputLanguage); - editor.commit(); - } - private String getSelectedInputLanguages() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); return sp.getString(PREF_SELECTED_LANGUAGES, null); diff --git a/src/com/android/inputmethod/latin/LatinKeyboard.java b/src/com/android/inputmethod/latin/LatinKeyboard.java index f876af709..92f93b39c 100644 --- a/src/com/android/inputmethod/latin/LatinKeyboard.java +++ b/src/com/android/inputmethod/latin/LatinKeyboard.java @@ -16,19 +16,26 @@ package com.android.inputmethod.latin; +import java.util.List; import java.util.Locale; import android.content.Context; import android.content.res.Resources; +import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.ColorFilter; import android.graphics.Paint; +import android.graphics.PixelFormat; import android.graphics.PorterDuff; +import android.graphics.Rect; import android.graphics.Paint.Align; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.inputmethodservice.Keyboard; +import android.text.TextPaint; +import android.view.ViewConfiguration; import android.view.inputmethod.EditorInfo; public class LatinKeyboard extends Keyboard { @@ -38,18 +45,29 @@ public class LatinKeyboard extends Keyboard { private Drawable mOldShiftIcon; private Drawable mOldShiftPreviewIcon; private Drawable mSpaceIcon; + private Drawable mSpacePreviewIcon; private Drawable mMicIcon; private Drawable mMicPreviewIcon; private Drawable m123MicIcon; private Drawable m123MicPreviewIcon; + private Drawable mButtonArrowLeftIcon; + private Drawable mButtonArrowRightIcon; private Key mShiftKey; private Key mEnterKey; private Key mF1Key; private Key mSpaceKey; + private int mSpaceKeyIndex = -1; + private int mSpaceDragStartX; + private int mSpaceDragLastDiff; /* package */ Locale mLocale; + private LanguageSwitcher mLanguageSwitcher; private Resources mRes; + private Context mContext; private int mMode; private boolean mHasVoice; + private boolean mCurrentlyInSpace; + private SlidingLocaleDrawable mSlidingLocaleIcon; + private Rect mBounds = new Rect(); private int mExtensionResId; @@ -59,6 +77,8 @@ public class LatinKeyboard extends Keyboard { private int mShiftState = SHIFT_OFF; + private static final float SPACEBAR_DRAG_THRESHOLD = 0.8f; + static int sSpacebarVerticalCorrection; public LatinKeyboard(Context context, int xmlLayoutResId) { @@ -68,6 +88,7 @@ public class LatinKeyboard extends Keyboard { public LatinKeyboard(Context context, int xmlLayoutResId, int mode, boolean hasVoice) { super(context, xmlLayoutResId, mode); final Resources res = context.getResources(); + mContext = context; mMode = mode; mRes = res; mHasVoice = hasVoice; @@ -77,11 +98,15 @@ public class LatinKeyboard extends Keyboard { mShiftLockPreviewIcon.getIntrinsicWidth(), mShiftLockPreviewIcon.getIntrinsicHeight()); mSpaceIcon = res.getDrawable(R.drawable.sym_keyboard_space); + mSpacePreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_space); mMicIcon = res.getDrawable(R.drawable.sym_keyboard_mic); mMicPreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_mic); + mButtonArrowLeftIcon = res.getDrawable(R.drawable.sym_keyboard_language_arrows_left); + mButtonArrowRightIcon = res.getDrawable(R.drawable.sym_keyboard_language_arrows_right); sSpacebarVerticalCorrection = res.getDimensionPixelOffset( R.dimen.spacebar_vertical_correction); setF1Key(); + mSpaceKeyIndex = indexOf((int) ' '); } public LatinKeyboard(Context context, int layoutTemplateResId, @@ -237,7 +262,6 @@ public class LatinKeyboard extends Keyboard { private void setF1Key() { if (mF1Key == null) return; - System.err.println("Setting F1 key"); if (!mHasVoice) { mF1Key.label = ","; mF1Key.codes = new int[] { ',' }; @@ -260,34 +284,179 @@ public class LatinKeyboard extends Keyboard { canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); Paint paint = new Paint(); paint.setAntiAlias(true); - // TODO: Make the text size a customizable attribute - paint.setTextSize(18); + // Get the text size from the theme + paint.setTextSize(getTextSizeFromTheme(android.R.style.TextAppearance_Small, 14)); paint.setTextAlign(Align.CENTER); // Draw a drop shadow for the text - paint.setShadowLayer(1f, 0, 0, 0xFF000000); + paint.setShadowLayer(2f, 0, 0, 0xFF000000); paint.setColor(0xFF808080); - canvas.drawText(mLocale.getDisplayLanguage(mLocale), - buffer.getWidth() / 2, - paint.ascent() + 2, paint); + final String language = getInputLanguage(mSpaceKey.width, paint); + final int ascent = (int) -paint.ascent(); + canvas.drawText(language, + buffer.getWidth() / 2, ascent, paint); + // Put arrows on either side of the text + if (mLanguageSwitcher.getLocaleCount() > 1) { + Rect bounds = new Rect(); + paint.getTextBounds(language, 0, language.length(), bounds); + drawButtonArrow(mButtonArrowLeftIcon, canvas, + (mSpaceKey.width - bounds.right) / 2 + - mButtonArrowLeftIcon.getIntrinsicWidth(), + (int) paint.getTextSize()); + drawButtonArrow(mButtonArrowRightIcon, canvas, + (mSpaceKey.width + bounds.right) / 2, (int) paint.getTextSize()); + } + // Draw the spacebar icon at the bottom int x = (buffer.getWidth() - mSpaceIcon.getIntrinsicWidth()) / 2; int y = buffer.getHeight() - mSpaceIcon.getIntrinsicHeight(); mSpaceIcon.setBounds(x, y, x + mSpaceIcon.getIntrinsicWidth(), y + mSpaceIcon.getIntrinsicHeight()); mSpaceIcon.draw(canvas); mSpaceKey.icon = new BitmapDrawable(mRes, buffer); - mSpaceKey.repeatable = false; + mSpaceKey.repeatable = mLanguageSwitcher.getLocaleCount() < 2; } else { mSpaceKey.icon = mRes.getDrawable(R.drawable.sym_keyboard_space); mSpaceKey.repeatable = true; } } - public void setLanguage(Locale locale) { + private void drawButtonArrow(Drawable arrow, Canvas canvas, int x, int bottomY) { + arrow.setBounds(x, bottomY - arrow.getIntrinsicHeight(), x + arrow.getIntrinsicWidth(), + bottomY); + arrow.draw(canvas); + } + + private String getInputLanguage(int widthAvail, Paint paint) { + return chooseDisplayName(mLanguageSwitcher.getInputLocale(), widthAvail, paint); + } + + private String getNextInputLanguage(int widthAvail, Paint paint) { + return chooseDisplayName(mLanguageSwitcher.getNextInputLocale(), widthAvail, paint); + } + + private String getPrevInputLanguage(int widthAvail, Paint paint) { + return chooseDisplayName(mLanguageSwitcher.getPrevInputLocale(), widthAvail, paint); + } + + private String chooseDisplayName(Locale locale, int widthAvail, Paint paint) { + if (widthAvail < (int) (.35 * getMinWidth())) { + return locale.getLanguage().substring(0, 2).toUpperCase(locale); + } else { + return locale.getDisplayLanguage(locale); + } + } + + private void updateLocaleDrag(int diff) { + if (mSlidingLocaleIcon == null) { + mSlidingLocaleIcon = new SlidingLocaleDrawable(mSpacePreviewIcon, mSpaceKey.width, + mSpacePreviewIcon.getIntrinsicHeight()); + mSlidingLocaleIcon.setBounds(0, 0, mSpaceKey.width, + mSpacePreviewIcon.getIntrinsicHeight()); + mSpaceKey.iconPreview = mSlidingLocaleIcon; + } + mSlidingLocaleIcon.setDiff(diff); + if (Math.abs(diff) == Integer.MAX_VALUE) { + mSpaceKey.iconPreview = mSpacePreviewIcon; + } else { + mSpaceKey.iconPreview = mSlidingLocaleIcon; + } + mSpaceKey.iconPreview.invalidateSelf(); + } + + public int getLanguageChangeDirection() { + if (mSpaceKey == null || mLanguageSwitcher.getLocaleCount() < 2 + || Math.abs(mSpaceDragLastDiff) < mSpaceKey.width * SPACEBAR_DRAG_THRESHOLD ) { + return 0; // No change + } + return mSpaceDragLastDiff > 0 ? 1 : -1; + } + + public void setLanguageSwitcher(LanguageSwitcher switcher) { + mLanguageSwitcher = switcher; + Locale locale = mLanguageSwitcher.getLocaleCount() > 0 + ? mLanguageSwitcher.getInputLocale() + : null; if (mLocale != null && mLocale.equals(locale)) return; mLocale = locale; updateSpaceBarForLocale(); } - static class LatinKey extends Keyboard.Key { + boolean isCurrentlyInSpace() { + return mCurrentlyInSpace; + } + + void keyReleased() { + mCurrentlyInSpace = false; + mSpaceDragLastDiff = 0; + if (mSpaceKey != null) { + updateLocaleDrag(Integer.MAX_VALUE); + } + } + + /** + * Does the magic of locking the touch gesture into the spacebar when + * switching input languages. + */ + boolean isInside(LatinKey key, int x, int y) { + final int code = key.codes[0]; + if (code == KEYCODE_SHIFT || + code == KEYCODE_DELETE) { + y -= key.height / 10; + if (code == KEYCODE_SHIFT) x += key.width / 6; + if (code == KEYCODE_DELETE) x -= key.width / 6; + } else if (code == LatinIME.KEYCODE_SPACE) { + y += LatinKeyboard.sSpacebarVerticalCorrection; + if (mLanguageSwitcher.getLocaleCount() > 1) { + if (mCurrentlyInSpace) { + int diff = x - mSpaceDragStartX; + if (Math.abs(diff - mSpaceDragLastDiff) > 0) { + updateLocaleDrag(diff); + } + mSpaceDragLastDiff = diff; + return true; + } else { + boolean insideSpace = key.isInsideSuper(x, y); + if (insideSpace) { + mCurrentlyInSpace = true; + mSpaceDragStartX = x; + updateLocaleDrag(0); + } + return insideSpace; + } + } + } + + // Lock into the spacebar + if (mCurrentlyInSpace) return false; + + return key.isInsideSuper(x, y); + } + + @Override + public int[] getNearestKeys(int x, int y) { + if (mCurrentlyInSpace) { + return new int[] { mSpaceKeyIndex }; + } else { + return super.getNearestKeys(x, y); + } + } + + private int indexOf(int code) { + List keys = getKeys(); + int count = keys.size(); + for (int i = 0; i < count; i++) { + if (keys.get(i).codes[0] == code) return i; + } + return -1; + } + + private int getTextSizeFromTheme(int style, int defValue) { + TypedArray array = mContext.getTheme().obtainStyledAttributes( + style, new int[] { android.R.attr.textSize }); + int textSize = array.getDimensionPixelSize(array.getResourceId(0, 0), defValue); + return textSize; + } + + class LatinKey extends Keyboard.Key { private boolean mShiftLockEnabled; @@ -318,16 +487,130 @@ public class LatinKeyboard extends Keyboard { */ @Override public boolean isInside(int x, int y) { - final int code = codes[0]; - if (code == KEYCODE_SHIFT || - code == KEYCODE_DELETE) { - y -= height / 10; - if (code == KEYCODE_SHIFT) x += width / 6; - if (code == KEYCODE_DELETE) x -= width / 6; - } else if (code == LatinIME.KEYCODE_SPACE) { - y += LatinKeyboard.sSpacebarVerticalCorrection; - } + return LatinKeyboard.this.isInside(this, x, y); + } + + boolean isInsideSuper(int x, int y) { return super.isInside(x, y); } } + + /** + * Animation to be displayed on the spacebar preview popup when switching + * languages by swiping the spacebar. It draws the current, previous and + * next languages and moves them by the delta of touch movement on the spacebar. + */ + class SlidingLocaleDrawable extends Drawable { + + private int mWidth; + private int mHeight; + private Drawable mBackground; + private int mDiff; + private TextPaint mTextPaint; + private int mMiddleX; + private int mAscent; + private Drawable mLeftDrawable; + private Drawable mRightDrawable; + private boolean mHitThreshold; + private int mThreshold; + private String mCurrentLanguage; + private String mNextLanguage; + private String mPrevLanguage; + + public SlidingLocaleDrawable(Drawable background, int width, int height) { + mBackground = background; + mBackground.setBounds(0, 0, + mBackground.getIntrinsicWidth(), mBackground.getIntrinsicHeight()); + mWidth = width; + mHeight = height; + mTextPaint = new TextPaint(); + int textSize = getTextSizeFromTheme(android.R.style.TextAppearance_Medium, 18); + mTextPaint.setTextSize(textSize); + mTextPaint.setColor(0); + mTextPaint.setTextAlign(Align.CENTER); + mTextPaint.setAlpha(255); + mTextPaint.setAntiAlias(true); + mAscent = (int) mTextPaint.ascent(); + mMiddleX = (mWidth - mBackground.getIntrinsicWidth()) / 2; + mLeftDrawable = + mRes.getDrawable(R.drawable.sym_keyboard_feedback_language_arrows_left); + mRightDrawable = + mRes.getDrawable(R.drawable.sym_keyboard_feedback_language_arrows_right); + mLeftDrawable.setBounds(0, 0, + mLeftDrawable.getIntrinsicWidth(), mLeftDrawable.getIntrinsicHeight()); + mRightDrawable.setBounds(mWidth - mRightDrawable.getIntrinsicWidth(), 0, + mWidth, mRightDrawable.getIntrinsicHeight()); + mThreshold = ViewConfiguration.get(mContext).getScaledTouchSlop(); + } + + void setDiff(int diff) { + if (diff == Integer.MAX_VALUE) { + mHitThreshold = false; + mCurrentLanguage = null; + return; + } + mDiff = diff; + if (mDiff > mWidth) mDiff = mWidth; + if (mDiff < -mWidth) mDiff = -mWidth; + if (Math.abs(mDiff) > mThreshold) mHitThreshold = true; + invalidateSelf(); + } + + @Override + public void draw(Canvas canvas) { + canvas.save(); + if (mHitThreshold) { + mTextPaint.setColor(0); + canvas.clipRect(0, 0, mWidth, mHeight); + int alpha = (255 * Math.max(0, mWidth / 2 - Math.abs(mDiff))) / (mWidth / 2); + mTextPaint.setAlpha(alpha); + + if (mCurrentLanguage == null) { + mCurrentLanguage = getInputLanguage(mWidth, mTextPaint); + mNextLanguage = getNextInputLanguage(mWidth, mTextPaint); + mPrevLanguage = getPrevInputLanguage(mWidth, mTextPaint); + } + + canvas.drawText(mCurrentLanguage, + mWidth / 2 + mDiff, -mAscent + 4, mTextPaint); + mTextPaint.setAlpha(255 - alpha); + canvas.drawText(mNextLanguage, + mDiff - mWidth / 2, -mAscent + 4, mTextPaint); + canvas.drawText(mPrevLanguage, + mDiff + mWidth + mWidth / 2, -mAscent + 4, mTextPaint); + mLeftDrawable.draw(canvas); + mRightDrawable.draw(canvas); + } + if (mBackground != null) { + canvas.translate(mMiddleX, 0); + mBackground.draw(canvas); + } + canvas.restore(); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setAlpha(int alpha) { + // Ignore + } + + @Override + public void setColorFilter(ColorFilter cf) { + // Ignore + } + + @Override + public int getIntrinsicWidth() { + return mWidth; + } + + @Override + public int getIntrinsicHeight() { + return mHeight; + } + } } diff --git a/src/com/android/inputmethod/latin/LatinKeyboardView.java b/src/com/android/inputmethod/latin/LatinKeyboardView.java index a88c1818c..05f8aff36 100644 --- a/src/com/android/inputmethod/latin/LatinKeyboardView.java +++ b/src/com/android/inputmethod/latin/LatinKeyboardView.java @@ -38,9 +38,15 @@ public class LatinKeyboardView extends KeyboardView { static final int KEYCODE_VOICE = -102; static final int KEYCODE_F1 = -103; static final int KEYCODE_NEXT_LANGUAGE = -104; + static final int KEYCODE_PREV_LANGUAGE = -105; private Keyboard mPhoneKeyboard; + private boolean mExtensionVisible; + private LatinKeyboardView mExtension; + private PopupWindow mExtensionPopup; + private boolean mFirstEvent; + public LatinKeyboardView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -66,22 +72,33 @@ public class LatinKeyboardView extends KeyboardView { // Long pressing on 0 in phone number keypad gives you a '+'. getOnKeyboardActionListener().onKey('+', null); return true; - } else if (key.codes[0] == ' ' && ((LatinKeyboard)getKeyboard()).mLocale != null) { - getOnKeyboardActionListener().onKey(KEYCODE_NEXT_LANGUAGE, null); - return true; } else { return super.onLongPress(key); } } - private boolean mExtensionVisible; - private LatinKeyboardView mExtension; - private PopupWindow mExtensionPopup; - private boolean mFirstEvent; - @Override public boolean onTouchEvent(MotionEvent me) { - if (((LatinKeyboard) getKeyboard()).getExtension() == 0) { + LatinKeyboard keyboard = (LatinKeyboard) getKeyboard(); + // Reset any bounding box controls in the keyboard + if (me.getAction() == MotionEvent.ACTION_DOWN) { + keyboard.keyReleased(); + } + + if (me.getAction() == MotionEvent.ACTION_UP) { + int languageDirection = keyboard.getLanguageChangeDirection(); + if (languageDirection != 0) { + getOnKeyboardActionListener().onKey( + languageDirection == 1 ? KEYCODE_NEXT_LANGUAGE : KEYCODE_PREV_LANGUAGE, + null); + me.setAction(MotionEvent.ACTION_CANCEL); + keyboard.keyReleased(); + return super.onTouchEvent(me); + } + } + + // If we don't have an extension keyboard, don't go any further. + if (keyboard.getExtension() == 0) { return super.onTouchEvent(me); } if (me.getY() < 0) { diff --git a/src/com/android/inputmethod/latin/TextEntryState.java b/src/com/android/inputmethod/latin/TextEntryState.java index 90c364a1c..c5e8ad9a1 100644 --- a/src/com/android/inputmethod/latin/TextEntryState.java +++ b/src/com/android/inputmethod/latin/TextEntryState.java @@ -123,6 +123,7 @@ public class TextEntryState { } public static void acceptedDefault(CharSequence typedWord, CharSequence actualWord) { + if (typedWord == null) return; if (!typedWord.equals(actualWord)) { sAutoSuggestCount++; }