diff options
Diffstat (limited to 'java/src/com/android')
6 files changed, 369 insertions, 116 deletions
diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java index a02203d31..0517bc814 100644 --- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java @@ -32,6 +32,7 @@ import android.database.sqlite.SQLiteDatabase; import android.net.ConnectivityManager; import android.net.Uri; import android.os.ParcelFileDescriptor; +import android.provider.Settings; import android.text.TextUtils; import android.util.Log; @@ -956,14 +957,23 @@ public final class UpdateHandler { WordListMetadata metadata = WordListMetadata.createFromContentValues(installCandidate); actions.add(new ActionBatch.StartDownloadAction(clientId, metadata)); final String localeString = installCandidate.getAsString(MetadataDbHelper.LOCALE_COLUMN); + // We are in a content provider: we can't do any UI at all. We have to defer the displaying // itself to the service. Also, we only display this when the user does not have a - // dictionary for this language already. - final Intent intent = new Intent(); - intent.setClass(context, DictionaryService.class); - intent.setAction(DictionaryService.SHOW_DOWNLOAD_TOAST_INTENT_ACTION); - intent.putExtra(DictionaryService.LOCALE_INTENT_ARGUMENT, localeString); - context.startService(intent); + // dictionary for this language already. During setup wizard, however, this UI is + // suppressed. + final boolean deviceProvisioned = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.DEVICE_PROVISIONED, 0) != 0; + if (deviceProvisioned) { + final Intent intent = new Intent(); + intent.setClass(context, DictionaryService.class); + intent.setAction(DictionaryService.SHOW_DOWNLOAD_TOAST_INTENT_ACTION); + intent.putExtra(DictionaryService.LOCALE_INTENT_ARGUMENT, localeString); + context.startService(intent); + } else { + Log.i(TAG, "installIfNeverRequested() : Don't show download toast"); + } + Log.i(TAG, "installIfNeverRequested() : StartDownloadAction for " + metadata); actions.execute(context, new LogProblemReporter(TAG)); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index a1f7bf0e1..7352f911b 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2015 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. @@ -60,6 +60,16 @@ public final class KeyboardId { public static final int ELEMENT_EMOJI_CATEGORY4 = 14; public static final int ELEMENT_EMOJI_CATEGORY5 = 15; public static final int ELEMENT_EMOJI_CATEGORY6 = 16; + public static final int ELEMENT_EMOJI_CATEGORY7 = 17; + public static final int ELEMENT_EMOJI_CATEGORY8 = 18; + public static final int ELEMENT_EMOJI_CATEGORY9 = 19; + public static final int ELEMENT_EMOJI_CATEGORY10 = 20; + public static final int ELEMENT_EMOJI_CATEGORY11 = 21; + public static final int ELEMENT_EMOJI_CATEGORY12 = 22; + public static final int ELEMENT_EMOJI_CATEGORY13 = 23; + public static final int ELEMENT_EMOJI_CATEGORY14 = 24; + public static final int ELEMENT_EMOJI_CATEGORY15 = 25; + public static final int ELEMENT_EMOJI_CATEGORY16 = 26; public final RichInputMethodSubtype mSubtype; public final int mWidth; @@ -225,6 +235,16 @@ public final class KeyboardId { case ELEMENT_EMOJI_CATEGORY4: return "emojiCategory4"; case ELEMENT_EMOJI_CATEGORY5: return "emojiCategory5"; case ELEMENT_EMOJI_CATEGORY6: return "emojiCategory6"; + case ELEMENT_EMOJI_CATEGORY7: return "emojiCategory7"; + case ELEMENT_EMOJI_CATEGORY8: return "emojiCategory8"; + case ELEMENT_EMOJI_CATEGORY9: return "emojiCategory9"; + case ELEMENT_EMOJI_CATEGORY10: return "emojiCategory10"; + case ELEMENT_EMOJI_CATEGORY11: return "emojiCategory11"; + case ELEMENT_EMOJI_CATEGORY12: return "emojiCategory12"; + case ELEMENT_EMOJI_CATEGORY13: return "emojiCategory13"; + case ELEMENT_EMOJI_CATEGORY14: return "emojiCategory14"; + case ELEMENT_EMOJI_CATEGORY15: return "emojiCategory15"; + case ELEMENT_EMOJI_CATEGORY16: return "emojiCategory16"; default: return null; } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 92e5dfceb..4d337b6f3 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -44,6 +44,8 @@ import com.android.inputmethod.latin.utils.RecapitalizeStatus; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.ScriptUtils; +import javax.annotation.Nonnull; + public final class KeyboardSwitcher implements KeyboardState.SwitchActions { private static final String TAG = KeyboardSwitcher.class.getSimpleName(); @@ -139,15 +141,18 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } } - private void setKeyboard(final Keyboard keyboard) { + private void setKeyboard( + @Nonnull final int keyboardId, + @Nonnull final KeyboardSwitchState toggleState) { // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}. final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent(); - setMainKeyboardFrame(currentSettingsValues); + setMainKeyboardFrame(currentSettingsValues, toggleState); // TODO: pass this object to setKeyboard instead of getting the current values. final MainKeyboardView keyboardView = mKeyboardView; final Keyboard oldKeyboard = keyboardView.getKeyboard(); - keyboardView.setKeyboard(keyboard); - mCurrentInputView.setKeyboardTopPadding(keyboard.mTopPadding); + final Keyboard newKeyboard = mKeyboardLayoutSet.getKeyboard(keyboardId); + keyboardView.setKeyboard(newKeyboard); + mCurrentInputView.setKeyboardTopPadding(newKeyboard.mTopPadding); keyboardView.setKeyPreviewPopupEnabled( currentSettingsValues.mKeyPreviewPopupOn, currentSettingsValues.mKeyPreviewPopupDismissDelay); @@ -161,9 +166,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { currentSettingsValues.mKeyPreviewDismissDuration); keyboardView.updateShortcutKey(mRichImm.isShortcutImeReady()); final boolean subtypeChanged = (oldKeyboard == null) - || !keyboard.mId.mSubtype.equals(oldKeyboard.mId.mSubtype); + || !newKeyboard.mId.mSubtype.equals(oldKeyboard.mId.mSubtype); final int languageOnSpacebarFormatType = LanguageOnSpacebarUtils - .getLanguageOnSpacebarFormatType(keyboard.mId.mSubtype); + .getLanguageOnSpacebarFormatType(newKeyboard.mId.mSubtype); final boolean hasMultipleEnabledIMEsOrSubtypes = mRichImm .hasMultipleEnabledIMEsOrSubtypes(true /* shouldIncludeAuxiliarySubtypes */); keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, languageOnSpacebarFormatType, @@ -205,7 +210,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { if (DEBUG_ACTION) { Log.d(TAG, "setAlphabetKeyboard"); } - setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET)); + setKeyboard(KeyboardId.ELEMENT_ALPHABET, KeyboardSwitchState.OTHER); } // Implements {@link KeyboardState.SwitchActions}. @@ -214,7 +219,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { if (DEBUG_ACTION) { Log.d(TAG, "setAlphabetManualShiftedKeyboard"); } - setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED)); + setKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED, KeyboardSwitchState.OTHER); } // Implements {@link KeyboardState.SwitchActions}. @@ -223,7 +228,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { if (DEBUG_ACTION) { Log.d(TAG, "setAlphabetAutomaticShiftedKeyboard"); } - setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED)); + setKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED, KeyboardSwitchState.OTHER); } // Implements {@link KeyboardState.SwitchActions}. @@ -232,7 +237,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { if (DEBUG_ACTION) { Log.d(TAG, "setAlphabetShiftLockedKeyboard"); } - setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED)); + setKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED, KeyboardSwitchState.OTHER); } // Implements {@link KeyboardState.SwitchActions}. @@ -241,7 +246,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { if (DEBUG_ACTION) { Log.d(TAG, "setAlphabetShiftLockShiftedKeyboard"); } - setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)); + setKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED, KeyboardSwitchState.OTHER); } // Implements {@link KeyboardState.SwitchActions}. @@ -250,11 +255,29 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { if (DEBUG_ACTION) { Log.d(TAG, "setSymbolsKeyboard"); } - setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS)); + setKeyboard(KeyboardId.ELEMENT_SYMBOLS, KeyboardSwitchState.OTHER); + } + + // Implements {@link KeyboardState.SwitchActions}. + @Override + public void setSymbolsShiftedKeyboard() { + if (DEBUG_ACTION) { + Log.d(TAG, "setSymbolsShiftedKeyboard"); + } + setKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED, KeyboardSwitchState.SYMBOLS_SHIFTED); } - private void setMainKeyboardFrame(final SettingsValues settingsValues) { - final int visibility = settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE; + public boolean isImeSuppressedByHardwareKeyboard( + @Nonnull final SettingsValues settingsValues, + @Nonnull final KeyboardSwitchState toggleState) { + return settingsValues.mHasHardwareKeyboard && toggleState == KeyboardSwitchState.HIDDEN; + } + + private void setMainKeyboardFrame( + @Nonnull final SettingsValues settingsValues, + @Nonnull final KeyboardSwitchState toggleState) { + final int visibility = isImeSuppressedByHardwareKeyboard(settingsValues, toggleState) + ? View.GONE : View.VISIBLE; mKeyboardView.setVisibility(visibility); // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}. // @see #getVisibleKeyboardView() and @@ -282,24 +305,55 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { mEmojiPalettesView.setVisibility(View.VISIBLE); } - public void onToggleEmojiKeyboard() { - final boolean needsToLoadKeyboard = (mKeyboardLayoutSet == null); - if (needsToLoadKeyboard || !isShowingEmojiPalettes()) { - mLatinIME.startShowingInputView(needsToLoadKeyboard); - setEmojiKeyboard(); - } else { - mLatinIME.stopShowingInputView(); - setAlphabetKeyboard(); + public enum KeyboardSwitchState { + HIDDEN(-1), + SYMBOLS_SHIFTED(KeyboardId.ELEMENT_SYMBOLS_SHIFTED), + EMOJI(KeyboardId.ELEMENT_EMOJI_RECENTS), + OTHER(-1); + + final int mKeyboardId; + + KeyboardSwitchState(int keyboardId) { + mKeyboardId = keyboardId; } } - // Implements {@link KeyboardState.SwitchActions}. - @Override - public void setSymbolsShiftedKeyboard() { - if (DEBUG_ACTION) { - Log.d(TAG, "setSymbolsShiftedKeyboard"); + public KeyboardSwitchState getKeyboardSwitchState() { + boolean hidden = !isShowingEmojiPalettes() + && (mKeyboardLayoutSet == null + || mKeyboardView == null + || !mKeyboardView.isShown()); + KeyboardSwitchState state; + if (hidden) { + return KeyboardSwitchState.HIDDEN; + } else if (isShowingEmojiPalettes()) { + return KeyboardSwitchState.EMOJI; + } else if (isShowingKeyboardId(KeyboardId.ELEMENT_SYMBOLS_SHIFTED)) { + return KeyboardSwitchState.SYMBOLS_SHIFTED; + } + return KeyboardSwitchState.OTHER; + } + + public void onToggleKeyboard(@Nonnull final KeyboardSwitchState toggleState) { + KeyboardSwitchState currentState = getKeyboardSwitchState(); + Log.w(TAG, "onToggleKeyboard() : Current = " + currentState + " : Toggle = " + toggleState); + if (currentState == toggleState) { + mLatinIME.stopShowingInputView(); + mLatinIME.hideWindow(); + setAlphabetKeyboard(); + } else { + mLatinIME.startShowingInputView(true); + if (toggleState == KeyboardSwitchState.EMOJI) { + setEmojiKeyboard(); + } else { + mEmojiPalettesView.stopEmojiPalettes(); + mEmojiPalettesView.setVisibility(View.GONE); + + mMainKeyboardFrame.setVisibility(View.VISIBLE); + mKeyboardView.setVisibility(View.VISIBLE); + setKeyboard(toggleState.mKeyboardId, toggleState); + } } - setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED)); } // Future method for requesting an updating to the shift state. @@ -355,6 +409,19 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { mState.onEvent(event, currentAutoCapsState, currentRecapitalizeState); } + public boolean isShowingKeyboardId(@Nonnull int... keyboardIds) { + if (mKeyboardView == null || !mKeyboardView.isShown()) { + return false; + } + int activeKeyboardId = mKeyboardView.getKeyboard().mId.mElementId; + for (int keyboardId : keyboardIds) { + if (activeKeyboardId == keyboardId) { + return true; + } + } + return false; + } + public boolean isShowingEmojiPalettes() { return mEmojiPalettesView != null && mEmojiPalettesView.isShown(); } diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java index a9711aed2..75b7962cb 100644 --- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2015 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. @@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.emoji; import android.content.SharedPreferences; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.Paint; import android.graphics.Rect; import android.os.Build; import android.util.Log; @@ -50,6 +51,16 @@ final class EmojiCategory { private static final int ID_PLACES = 4; private static final int ID_SYMBOLS = 5; private static final int ID_EMOTICONS = 6; + private static final int ID_FLAGS = 7; + private static final int ID_EIGHT_SMILEY_PEOPLE = 8; + private static final int ID_EIGHT_ANIMALS_NATURE = 9; + private static final int ID_EIGHT_FOOD_DRINK = 10; + private static final int ID_EIGHT_TRAVEL_PLACES = 11; + private static final int ID_EIGHT_ACTIVITY = 12; + private static final int ID_EIGHT_OBJECTS = 13; + private static final int ID_EIGHT_SYMBOLS = 14; + private static final int ID_EIGHT_FLAGS = 15; + private static final int ID_EIGHT_SMILEY_PEOPLE_BORING = 16; public final class CategoryProperties { public final int mCategoryId; @@ -67,7 +78,17 @@ final class EmojiCategory { "nature", "places", "symbols", - "emoticons" }; + "emoticons", + "flags", + "smiley & people", + "animals & nature", + "food & drink", + "travel & places", + "activity", + "objects2", + "symbols2", + "flags2", + "smiley & people2" }; private static final int[] sCategoryTabIconAttr = { R.styleable.EmojiPalettesView_iconEmojiRecentsTab, @@ -76,7 +97,17 @@ final class EmojiCategory { R.styleable.EmojiPalettesView_iconEmojiCategory3Tab, R.styleable.EmojiPalettesView_iconEmojiCategory4Tab, R.styleable.EmojiPalettesView_iconEmojiCategory5Tab, - R.styleable.EmojiPalettesView_iconEmojiCategory6Tab }; + R.styleable.EmojiPalettesView_iconEmojiCategory6Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory7Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory8Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory9Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory10Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory11Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory12Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory13Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory14Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory15Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory16Tab }; private static final int[] sAccessibilityDescriptionResourceIdsForCategories = { R.string.spoken_descrption_emoji_category_recents, @@ -85,7 +116,17 @@ final class EmojiCategory { R.string.spoken_descrption_emoji_category_nature, R.string.spoken_descrption_emoji_category_places, R.string.spoken_descrption_emoji_category_symbols, - R.string.spoken_descrption_emoji_category_emoticons }; + R.string.spoken_descrption_emoji_category_emoticons, + R.string.spoken_descrption_emoji_category_flags, + R.string.spoken_descrption_emoji_category_eight_smiley_people, + R.string.spoken_descrption_emoji_category_eight_animals_nature, + R.string.spoken_descrption_emoji_category_eight_food_drink, + R.string.spoken_descrption_emoji_category_eight_travel_places, + R.string.spoken_descrption_emoji_category_eight_activity, + R.string.spoken_descrption_emoji_category_objects, + R.string.spoken_descrption_emoji_category_symbols, + R.string.spoken_descrption_emoji_category_flags, + R.string.spoken_descrption_emoji_category_eight_smiley_people }; private static final int[] sCategoryElementId = { KeyboardId.ELEMENT_EMOJI_RECENTS, @@ -94,7 +135,17 @@ final class EmojiCategory { KeyboardId.ELEMENT_EMOJI_CATEGORY3, KeyboardId.ELEMENT_EMOJI_CATEGORY4, KeyboardId.ELEMENT_EMOJI_CATEGORY5, - KeyboardId.ELEMENT_EMOJI_CATEGORY6 }; + KeyboardId.ELEMENT_EMOJI_CATEGORY6, + KeyboardId.ELEMENT_EMOJI_CATEGORY7, + KeyboardId.ELEMENT_EMOJI_CATEGORY8, + KeyboardId.ELEMENT_EMOJI_CATEGORY9, + KeyboardId.ELEMENT_EMOJI_CATEGORY10, + KeyboardId.ELEMENT_EMOJI_CATEGORY11, + KeyboardId.ELEMENT_EMOJI_CATEGORY12, + KeyboardId.ELEMENT_EMOJI_CATEGORY13, + KeyboardId.ELEMENT_EMOJI_CATEGORY14, + KeyboardId.ELEMENT_EMOJI_CATEGORY15, + KeyboardId.ELEMENT_EMOJI_CATEGORY16 }; private final SharedPreferences mPrefs; private final Resources mRes; @@ -120,27 +171,49 @@ final class EmojiCategory { mCategoryTabIconId[i] = emojiPaletteViewAttr.getResourceId( sCategoryTabIconAttr[i], 0); } + + int defaultCategoryId = EmojiCategory.ID_SYMBOLS; addShownCategoryId(EmojiCategory.ID_RECENTS); if (BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.KITKAT) { - addShownCategoryId(EmojiCategory.ID_PEOPLE); - addShownCategoryId(EmojiCategory.ID_OBJECTS); - addShownCategoryId(EmojiCategory.ID_NATURE); - addShownCategoryId(EmojiCategory.ID_PLACES); - mCurrentCategoryId = - Settings.readLastShownEmojiCategoryId(mPrefs, EmojiCategory.ID_PEOPLE); - } else { - mCurrentCategoryId = - Settings.readLastShownEmojiCategoryId(mPrefs, EmojiCategory.ID_SYMBOLS); + if (canShowUnicodeEightEmoji()) { + defaultCategoryId = EmojiCategory.ID_EIGHT_SMILEY_PEOPLE; + addShownCategoryId(EmojiCategory.ID_EIGHT_SMILEY_PEOPLE); + addShownCategoryId(EmojiCategory.ID_EIGHT_ANIMALS_NATURE); + addShownCategoryId(EmojiCategory.ID_EIGHT_FOOD_DRINK); + addShownCategoryId(EmojiCategory.ID_EIGHT_TRAVEL_PLACES); + addShownCategoryId(EmojiCategory.ID_EIGHT_ACTIVITY); + addShownCategoryId(EmojiCategory.ID_EIGHT_OBJECTS); + addShownCategoryId(EmojiCategory.ID_EIGHT_SYMBOLS); + addShownCategoryId(EmojiCategory.ID_FLAGS); // Exclude combinations without glyphs. + } else { + defaultCategoryId = EmojiCategory.ID_PEOPLE; + addShownCategoryId(EmojiCategory.ID_PEOPLE); + addShownCategoryId(EmojiCategory.ID_OBJECTS); + addShownCategoryId(EmojiCategory.ID_NATURE); + addShownCategoryId(EmojiCategory.ID_PLACES); + addShownCategoryId(EmojiCategory.ID_SYMBOLS); + if (canShowFlagEmoji()) { + addShownCategoryId(EmojiCategory.ID_FLAGS); + } + } } - addShownCategoryId(EmojiCategory.ID_SYMBOLS); addShownCategoryId(EmojiCategory.ID_EMOTICONS); - getKeyboard(EmojiCategory.ID_RECENTS, 0 /* cagetoryPageId */) - .loadRecentKeys(mCategoryKeyboardMap.values()); + + DynamicGridKeyboard recentsKbd = + getKeyboard(EmojiCategory.ID_RECENTS, 0 /* categoryPageId */); + recentsKbd.loadRecentKeys(mCategoryKeyboardMap.values()); + + mCurrentCategoryId = Settings.readLastShownEmojiCategoryId(mPrefs, defaultCategoryId); + if (mCurrentCategoryId == EmojiCategory.ID_RECENTS && + recentsKbd.getSortedKeys().isEmpty()) { + Log.i(TAG, "No recent emojis found, starting in category " + mCurrentCategoryId); + mCurrentCategoryId = defaultCategoryId; + } } private void addShownCategoryId(final int categoryId) { // Load a keyboard of categoryId - getKeyboard(categoryId, 0 /* cagetoryPageId */); + getKeyboard(categoryId, 0 /* categoryPageId */); final CategoryProperties properties = new CategoryProperties(categoryId, getCategoryPageCount(categoryId)); mShownCategories.add(properties); @@ -275,16 +348,16 @@ final class EmojiCategory { public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) { synchronized (mCategoryKeyboardMap) { - final Long categotyKeyboardMapKey = getCategoryKeyboardMapKey(categoryId, id); - if (mCategoryKeyboardMap.containsKey(categotyKeyboardMapKey)) { - return mCategoryKeyboardMap.get(categotyKeyboardMapKey); + final Long categoryKeyboardMapKey = getCategoryKeyboardMapKey(categoryId, id); + if (mCategoryKeyboardMap.containsKey(categoryKeyboardMapKey)) { + return mCategoryKeyboardMap.get(categoryKeyboardMapKey); } if (categoryId == EmojiCategory.ID_RECENTS) { final DynamicGridKeyboard kbd = new DynamicGridKeyboard(mPrefs, mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), mMaxPageKeyCount, categoryId); - mCategoryKeyboardMap.put(categotyKeyboardMapKey, kbd); + mCategoryKeyboardMap.put(categoryKeyboardMapKey, kbd); return kbd; } @@ -304,7 +377,7 @@ final class EmojiCategory { mCategoryKeyboardMap.put( getCategoryKeyboardMapKey(categoryId, pageId), tempKeyboard); } - return mCategoryKeyboardMap.get(categotyKeyboardMapKey); + return mCategoryKeyboardMap.get(categoryKeyboardMapKey); } } @@ -348,4 +421,34 @@ final class EmojiCategory { } return retval; } + + private static boolean canShowFlagEmoji() { + Paint paint = new Paint(); + String switzerland = "\uD83C\uDDE8\uD83C\uDDED"; // U+1F1E8 U+1F1ED Flag for Switzerland + try { + return paint.hasGlyph(switzerland); + } catch (NoSuchMethodError e) { + // Compare display width of single-codepoint emoji to width of flag emoji to determine + // whether flag is rendered as single glyph or two adjacent regional indicator symbols. + float flagWidth = paint.measureText(switzerland); + float standardWidth = paint.measureText("\uD83D\uDC27"); // U+1F427 Penguin + return flagWidth < standardWidth * 1.25; + // This assumes that a valid glyph for the flag emoji must be less than 1.25 times + // the width of the penguin. + } + } + + private static boolean canShowUnicodeEightEmoji() { + Paint paint = new Paint(); + String cheese = "\uD83E\uDDC0"; // U+1F9C0 Cheese wedge + try { + return paint.hasGlyph(cheese); + } catch (NoSuchMethodError e) { + float cheeseWidth = paint.measureText(cheese); + float tofuWidth = paint.measureText("\uFFFE"); + return cheeseWidth > tofuWidth; + // This assumes that a valid glyph for the cheese wedge must be greater than the width + // of the noncharacter. + } + } } diff --git a/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java b/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java index 9b271116d..2529424c0 100644 --- a/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java +++ b/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java @@ -16,92 +16,140 @@ package com.android.inputmethod.latin; +import android.content.res.Resources; import android.util.Log; import android.view.KeyEvent; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.latin.settings.Settings; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nonnull; + /** * A class for detecting Emoji-Alt physical key. */ final class EmojiAltPhysicalKeyDetector { private static final String TAG = "EmojiAltPhysicalKeyDetector"; - private final RichInputConnection mRichInputConnection; + private final Map<Integer, Integer> mEmojiSwitcherMap; + private final Map<Integer, Integer> mSymbolsShiftedSwitcherMap; + private final Map<Integer, Integer> mCombinedSwitcherMap; - // True if the Alt key has been used as a modifier. In this case the Alt key up isn't - // recognized as an emoji key. - private boolean mAltHasBeenUsedAsAModifier; + // Set of keys codes that have been used as modifiers. + private Set<Integer> mActiveModifiers; - public EmojiAltPhysicalKeyDetector(final RichInputConnection richInputConnection) { - mRichInputConnection = richInputConnection; + public EmojiAltPhysicalKeyDetector(@Nonnull final Resources resources) { + mEmojiSwitcherMap = parseSwitchDefinition(resources, R.array.keyboard_switcher_emoji); + mSymbolsShiftedSwitcherMap = parseSwitchDefinition( + resources, R.array.keyboard_switcher_symbols_shifted); + mCombinedSwitcherMap = new HashMap<>(); + mCombinedSwitcherMap.putAll(mEmojiSwitcherMap); + mCombinedSwitcherMap.putAll(mSymbolsShiftedSwitcherMap); + mActiveModifiers = new HashSet<>(); } - /** - * Record a down key event. - * @param keyEvent a down key event. - */ - public void onKeyDown(final KeyEvent keyEvent) { - if (isAltKey(keyEvent)) { - mAltHasBeenUsedAsAModifier = false; - } - if (containsAltModifier(keyEvent)) { - mAltHasBeenUsedAsAModifier = true; + private static Map<Integer, Integer> parseSwitchDefinition( + @Nonnull final Resources resources, + final int resourceId) { + final Map<Integer, Integer> definition = new HashMap<>(); + final String name = resources.getResourceEntryName(resourceId); + final String[] values = resources.getStringArray(resourceId); + for (int i = 0; values != null && i < values.length; i++) { + String[] valuePair = values[i].split(","); + if (valuePair.length != 2) { + Log.w(TAG, "Expected 2 integers in " + name + "[" + i + "] : " + values[i]); + } + try { + definition.put(Integer.parseInt(valuePair[0]), Integer.parseInt(valuePair[1])); + } catch (NumberFormatException e) { + Log.w(TAG, "Failed to parse " + name + "[" + i + "] : " + values[i], e); + } } + return definition; } /** - * Determine whether an up key event is a special key up or not. + * Determine whether an up key event came from a mapped modifier key. + * * @param keyEvent an up key event. */ - public void onKeyUp(final KeyEvent keyEvent) { - if (keyEvent.isCanceled()) { - // This key up event was a part of key combinations and should be ignored. + public void onKeyUp(@Nonnull final KeyEvent keyEvent) { + Log.d(TAG, "onKeyUp() : " + keyEvent); + if (!Settings.getInstance().getCurrent().mEnableEmojiAltPhysicalKey) { + // The feature is disabled. + Log.d(TAG, "onKeyUp() : Disabled"); return; } - if (!isAltKey(keyEvent)) { - mAltHasBeenUsedAsAModifier |= containsAltModifier(keyEvent); + if (keyEvent.isCanceled()) { + // This key up event was a part of key combinations and should be ignored. + Log.d(TAG, "onKeyUp() : Canceled"); return; } - if (containsAltModifier(keyEvent)) { - mAltHasBeenUsedAsAModifier = true; + final Integer mappedModifier = getMappedModifier(keyEvent); + if (mappedModifier != null) { + // If the key was modified by a mapped key, then ignore the next time + // the same modifier key comes up. + Log.d(TAG, "onKeyUp() : Using Modifier: " + mappedModifier); + mActiveModifiers.add(mappedModifier); return; } - if (!Settings.getInstance().getCurrent().mEnableEmojiAltPhysicalKey) { + final int keyCode = keyEvent.getKeyCode(); + if (mActiveModifiers.contains(keyCode)) { + // Used as a modifier, not a standalone key press. + Log.d(TAG, "onKeyUp() : Used as Modifier: " + keyCode); + mActiveModifiers.remove(keyCode); return; } - if (mAltHasBeenUsedAsAModifier) { + if (!isMappedKeyCode(keyEvent)) { + // Nothing special about this key. + Log.d(TAG, "onKeyUp() : Not Mapped: " + keyCode); return; } - if (!mRichInputConnection.isConnected()) { - Log.w(TAG, "onKeyUp() : No connection to text view"); - return; + final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance(); + if (mEmojiSwitcherMap.keySet().contains(keyCode)) { + switcher.onToggleKeyboard(KeyboardSwitcher.KeyboardSwitchState.EMOJI); + } else if (mSymbolsShiftedSwitcherMap.keySet().contains(keyCode)) { + switcher.onToggleKeyboard(KeyboardSwitcher.KeyboardSwitchState.SYMBOLS_SHIFTED); + } else { + Log.w(TAG, "Cannot toggle on keyCode: " + keyCode); } - onEmojiAltKeyDetected(); } - private static void onEmojiAltKeyDetected() { - KeyboardSwitcher.getInstance().onToggleEmojiKeyboard(); + /** + * @param keyEvent pressed key event + * @return true iff the user pressed a mapped modifier key. + */ + private boolean isMappedKeyCode(@Nonnull final KeyEvent keyEvent) { + return mCombinedSwitcherMap.get(keyEvent.getKeyCode()) != null; } - private static boolean isAltKey(final KeyEvent keyEvent) { + /** + * @param keyEvent pressed key event + * @return the mapped modifier used with this key opress, if any. + */ + private Integer getMappedModifier(@Nonnull final KeyEvent keyEvent) { final int keyCode = keyEvent.getKeyCode(); - return keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT; - } - - private static boolean containsAltModifier(final KeyEvent keyEvent) { final int metaState = keyEvent.getMetaState(); - // TODO: Support multiple keyboards. Take device id into account. - switch (keyEvent.getKeyCode()) { - case KeyEvent.KEYCODE_ALT_LEFT: - // Return true if Left-Alt is pressed with Right-Alt pressed. - return (metaState & KeyEvent.META_ALT_RIGHT_ON) != 0; - case KeyEvent.KEYCODE_ALT_RIGHT: - // Return true if Right-Alt is pressed with Left-Alt pressed. - return (metaState & KeyEvent.META_ALT_LEFT_ON) != 0; - default: - return (metaState & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON)) != 0; + for (int mappedKeyCode : mCombinedSwitcherMap.keySet()) { + if (keyCode == mappedKeyCode) { + Log.d(TAG, "getMappedModifier() : KeyCode = MappedKeyCode = " + mappedKeyCode); + continue; + } + final Integer mappedMeta = mCombinedSwitcherMap.get(mappedKeyCode); + if (mappedMeta == null || mappedMeta.intValue() == -1) { + continue; + } + if ((metaState & mappedMeta) != 0) { + Log.d(TAG, "getMappedModifier() : MetaState(" + metaState + + ") contains MappedMeta(" + mappedMeta + ")"); + return mappedKeyCode; + } } + return null; } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 1f2b6f25d..25a5de250 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -142,8 +142,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private RichInputMethodManager mRichImm; @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher; private final SubtypeState mSubtypeState = new SubtypeState(); - private final EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector = - new EmojiAltPhysicalKeyDetector(mInputLogic.mConnection); + private EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector; private StatsUtilsManager mStatsUtilsManager; // Working variable for {@link #startShowingInputView()} and // {@link #onEvaluateInputViewShown()}. @@ -702,6 +701,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mInputLogic.recycle(); } + private boolean isImeSuppressedByHardwareKeyboard() { + final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance(); + return switcher.isImeSuppressedByHardwareKeyboard( + mSettings.getCurrent(), switcher.getKeyboardSwitchState()); + } + @Override public void onConfigurationChanged(final Configuration conf) { SettingsValues settingsValues = mSettings.getCurrent(); @@ -716,7 +721,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // have a change in hardware keyboard configuration. loadSettings(); settingsValues = mSettings.getCurrent(); - if (settingsValues.mHasHardwareKeyboard) { + if (isImeSuppressedByHardwareKeyboard()) { // We call cleanupInternalStateForFinishInput() because it's the right thing to do; // however, it seems at the moment the framework is passing us a seemingly valid // but actually non-functional InputConnection object. So if this bug ever gets @@ -874,7 +879,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // can go into the correct mode, so we need to do some housekeeping here. final boolean needToCallLoadKeyboardLater; final Suggest suggest = mInputLogic.mSuggest; - if (!currentSettingsValues.mHasHardwareKeyboard) { + if (!isImeSuppressedByHardwareKeyboard()) { // The app calling setText() has the effect of clearing the composing // span, so we should reset our state unconditionally, even if restarting is true. // We also tell the input logic about the combining rules for the current subtype, so @@ -1118,8 +1123,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } final int inputHeight = mInputView.getHeight(); - final boolean hasHardwareKeyboard = settingsValues.mHasHardwareKeyboard; - if (hasHardwareKeyboard && visibleKeyboardView.getVisibility() == View.GONE) { + if (isImeSuppressedByHardwareKeyboard() && !visibleKeyboardView.isShown()) { // If there is a hardware keyboard and a visible software keyboard view has been hidden, // no visual element will be shown on the screen. outInsets.contentTopInsets = inputHeight; @@ -1165,7 +1169,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public boolean onShowInputRequested(final int flags, final boolean configChange) { - if (Settings.getInstance().getCurrent().mHasHardwareKeyboard) { + if (isImeSuppressedByHardwareKeyboard()) { return true; } return super.onShowInputRequested(flags, configChange); @@ -1182,7 +1186,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public boolean onEvaluateFullscreenMode() { final SettingsValues settingsValues = mSettings.getCurrent(); - if (settingsValues.mHasHardwareKeyboard) { + if (isImeSuppressedByHardwareKeyboard()) { // If there is a hardware keyboard, disable full screen mode. return false; } @@ -1646,8 +1650,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Hooks for hardware keyboard @Override public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) { - // TODO: This should be processed in {@link InputLogic}. - mEmojiAltPhysicalKeyDetector.onKeyDown(keyEvent); if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { return super.onKeyDown(keyCode, keyEvent); } @@ -1668,7 +1670,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) { - // TODO: This should be processed in {@link InputLogic}. + if (mEmojiAltPhysicalKeyDetector == null) { + mEmojiAltPhysicalKeyDetector = new EmojiAltPhysicalKeyDetector( + getApplicationContext().getResources()); + } mEmojiAltPhysicalKeyDetector.onKeyUp(keyEvent); if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { return super.onKeyUp(keyCode, keyEvent); |