Merge remote-tracking branch 'goog/master' into merge

main
satok 2011-04-15 23:37:38 +09:00
commit abcbe57252
21 changed files with 1827 additions and 629 deletions

View File

@ -32,14 +32,14 @@
<LinearLayout <LinearLayout
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/candidate_strip_height" android:layout_height="wrap_content"
android:background="@drawable/keyboard_suggest_strip_holo" android:background="@drawable/keyboard_suggest_strip_holo"
android:paddingRight="@dimen/candidate_strip_padding" android:paddingRight="@dimen/candidate_strip_padding"
android:paddingLeft="@dimen/candidate_strip_padding" android:paddingLeft="@dimen/candidate_strip_padding"
> >
<HorizontalScrollView <HorizontalScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:fadingEdge="horizontal" android:fadingEdge="horizontal"
android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length" android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length"
android:scrollbars="none" android:scrollbars="none"
@ -47,7 +47,8 @@
<com.android.inputmethod.latin.CandidateView <com.android.inputmethod.latin.CandidateView
android:id="@+id/candidates" android:id="@+id/candidates"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="@dimen/candidate_strip_height"
android:gravity="center_vertical" />
</HorizontalScrollView> </HorizontalScrollView>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -30,7 +30,7 @@
> >
<HorizontalScrollView <HorizontalScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/candidate_strip_height" android:layout_height="wrap_content"
android:background="@drawable/keyboard_suggest_strip" android:background="@drawable/keyboard_suggest_strip"
android:fadingEdge="horizontal" android:fadingEdge="horizontal"
android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length" android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length"
@ -39,6 +39,7 @@
<com.android.inputmethod.latin.CandidateView <com.android.inputmethod.latin.CandidateView
android:id="@+id/candidates" android:id="@+id/candidates"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="@dimen/candidate_strip_height"
android:gravity="center_vertical" />
</HorizontalScrollView> </HorizontalScrollView>
</LinearLayout> </LinearLayout>

View File

@ -18,11 +18,11 @@
*/ */
--> -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="alternates_for_a">1,à,â,æ,á,ä,ã,å,ā,ª</string> <string name="alternates_for_a">à,â,1,æ,á,ä,ã,å,ā,ª</string>
<string name="alternates_for_e">3,é,è,ê,ë,ę,ė,ē</string> <string name="alternates_for_e">é,è,ê,ë,3,ę,ė,ē</string>
<string name="alternates_for_i">8,î,ï,ì,í,į,ī</string> <string name="alternates_for_i">î,8,ï,ì,í,į,ī</string>
<string name="alternates_for_o">9,ô,œ,ö,ò,ó,õ,ø,ō,º</string> <string name="alternates_for_o">ô,œ,9,ö,ò,ó,õ,ø,ō,º</string>
<string name="alternates_for_u">7,û,ù,ü,ú,ū</string> <string name="alternates_for_u">ù,û,7,ü,ú,ū</string>
<string name="alternates_for_c">ç,ć,č</string> <string name="alternates_for_c">ç,ć,č</string>
<string name="alternates_for_y">6,ÿ</string> <string name="alternates_for_y">6,ÿ</string>
<string name="alternates_for_q"></string> <string name="alternates_for_q"></string>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
<!-- Screen metrics for logging. 0 = "mdpi", 1 = "hdpi", 2 = "xlarge" -->
<integer name="log_screen_metrics">1</integer>
</resources>

View File

@ -43,4 +43,6 @@
<string name="config_default_keyboard_theme_id" translatable="false">5</string> <string name="config_default_keyboard_theme_id" translatable="false">5</string>
<string name="config_text_size_of_language_on_spacebar" translatable="false">medium</string> <string name="config_text_size_of_language_on_spacebar" translatable="false">medium</string>
<integer name="config_max_popup_keyboard_column">5</integer> <integer name="config_max_popup_keyboard_column">5</integer>
<!-- Screen metrics for logging. 0 = "mdpi", 1 = "hdpi", 2 = "xlarge" -->
<integer name="log_screen_metrics">2</integer>
</resources> </resources>

View File

@ -63,7 +63,7 @@
<!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. --> <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
<string name="config_default_keyboard_theme_id" translatable="false">4</string> <string name="config_default_keyboard_theme_id" translatable="false">4</string>
<string name="config_text_size_of_language_on_spacebar" translatable="false">small</string> <string name="config_text_size_of_language_on_spacebar" translatable="false">small</string>
<integer name="config_max_popup_keyboard_column">10</integer> <integer name="config_max_popup_keyboard_column">5</integer>
<!-- Whether or not auto-correction should be enabled by default --> <!-- Whether or not auto-correction should be enabled by default -->
<bool name="enable_autocorrect">true</bool> <bool name="enable_autocorrect">true</bool>
<string-array name="auto_correction_threshold_values" translatable="false"> <string-array name="auto_correction_threshold_values" translatable="false">
@ -76,4 +76,6 @@
will be subject to auto-correction. --> will be subject to auto-correction. -->
<item>0</item> <item>0</item>
</string-array> </string-array>
<!-- Screen metrics for logging. 0 = "mdpi", 1 = "hdpi", 2 = "xlarge" -->
<integer name="log_screen_metrics">0</integer>
</resources> </resources>

View File

@ -16,36 +16,35 @@
package com.android.inputmethod.keyboard; package com.android.inputmethod.keyboard;
import android.util.Log;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
public abstract class KeyDetector { public class KeyDetector {
public static final int NOT_A_KEY = -1; private static final String TAG = KeyDetector.class.getSimpleName();
private static final boolean DEBUG = false;
public static final int NOT_A_CODE = -1; public static final int NOT_A_CODE = -1;
public static final int NOT_A_KEY = -1;
protected Keyboard mKeyboard; private Keyboard mKeyboard;
private int mCorrectionX;
private int mCorrectionY;
private boolean mProximityCorrectOn;
private int mProximityThresholdSquare;
private Key[] mKeys; // working area
private static final int MAX_NEARBY_KEYS = 12;
private final int[] mDistances = new int[MAX_NEARBY_KEYS];
private final int[] mIndices = new int[MAX_NEARBY_KEYS];
protected int mCorrectionX; public void setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
protected int mCorrectionY;
protected boolean mProximityCorrectOn;
protected int mProximityThresholdSquare;
public Key[] setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
if (keyboard == null) if (keyboard == null)
throw new NullPointerException(); throw new NullPointerException();
mCorrectionX = (int)correctionX; mCorrectionX = (int)correctionX;
mCorrectionY = (int)correctionY; mCorrectionY = (int)correctionY;
mKeyboard = keyboard; mKeyboard = keyboard;
List<Key> keys = mKeyboard.getKeys();
Key[] array = keys.toArray(new Key[keys.size()]);
mKeys = array;
return array;
} }
protected int getTouchX(int x) { protected int getTouchX(int x) {
@ -56,11 +55,11 @@ public abstract class KeyDetector {
return y + mCorrectionY; return y + mCorrectionY;
} }
protected Key[] getKeys() { protected List<Key> getKeys() {
if (mKeys == null) if (mKeyboard == null)
throw new IllegalStateException("keyboard isn't set"); throw new IllegalStateException("keyboard isn't set");
// mKeyboard is guaranteed not to be null at setKeybaord() method if mKeys is not null // mKeyboard is guaranteed not to be null at setKeybaord() method if mKeys is not null
return mKeys; return mKeyboard.getKeys();
} }
public void setProximityCorrectionEnabled(boolean enabled) { public void setProximityCorrectionEnabled(boolean enabled) {
@ -75,6 +74,17 @@ public abstract class KeyDetector {
mProximityThresholdSquare = threshold * threshold; mProximityThresholdSquare = threshold * threshold;
} }
/**
* Computes maximum size of the array that can contain all nearby key indices returned by
* {@link #getKeyIndexAndNearbyCodes}.
*
* @return Returns maximum size of the array that can contain all nearby key indices returned
* by {@link #getKeyIndexAndNearbyCodes}.
*/
protected int getMaxNearbyKeys() {
return MAX_NEARBY_KEYS;
}
/** /**
* Allocates array that can hold all key indices returned by {@link #getKeyIndexAndNearbyCodes} * Allocates array that can hold all key indices returned by {@link #getKeyIndexAndNearbyCodes}
* method. The maximum size of the array should be computed by {@link #getMaxNearbyKeys}. * method. The maximum size of the array should be computed by {@link #getMaxNearbyKeys}.
@ -89,14 +99,60 @@ public abstract class KeyDetector {
return codes; return codes;
} }
private void initializeNearbyKeys() {
Arrays.fill(mDistances, Integer.MAX_VALUE);
Arrays.fill(mIndices, NOT_A_KEY);
}
/** /**
* Computes maximum size of the array that can contain all nearby key indices returned by * Insert the key into nearby keys buffer and sort nearby keys by ascending order of distance.
* {@link #getKeyIndexAndNearbyCodes}.
* *
* @return Returns maximum size of the array that can contain all nearby key indices returned * @param keyIndex index of the key.
* by {@link #getKeyIndexAndNearbyCodes}. * @param distance distance between the key's edge and user touched point.
* @return order of the key in the nearby buffer, 0 if it is the nearest key.
*/ */
abstract protected int getMaxNearbyKeys(); private int sortNearbyKeys(int keyIndex, int distance) {
final int[] distances = mDistances;
final int[] indices = mIndices;
for (int insertPos = 0; insertPos < distances.length; insertPos++) {
if (distance < distances[insertPos]) {
final int nextPos = insertPos + 1;
if (nextPos < distances.length) {
System.arraycopy(distances, insertPos, distances, nextPos,
distances.length - nextPos);
System.arraycopy(indices, insertPos, indices, nextPos,
indices.length - nextPos);
}
distances[insertPos] = distance;
indices[insertPos] = keyIndex;
return insertPos;
}
}
return distances.length;
}
private void getNearbyKeyCodes(final int[] allCodes) {
final List<Key> keys = getKeys();
final int[] indices = mIndices;
// allCodes[0] should always have the key code even if it is a non-letter key.
if (indices[0] == NOT_A_KEY) {
allCodes[0] = NOT_A_CODE;
return;
}
int numCodes = 0;
for (int j = 0; j < indices.length && numCodes < allCodes.length; j++) {
final int index = indices[j];
if (index == NOT_A_KEY)
break;
final int code = keys.get(index).mCode;
// filter out a non-letter key from nearby keys
if (code < Keyboard.CODE_SPACE)
continue;
allCodes[numCodes++] = code;
}
}
/** /**
* Finds all possible nearby key indices around a touch event point and returns the nearest key * Finds all possible nearby key indices around a touch event point and returns the nearest key
@ -109,32 +165,34 @@ public abstract class KeyDetector {
* @param allCodes All nearby key code except functional key are returned in this array * @param allCodes All nearby key code except functional key are returned in this array
* @return The nearest key index * @return The nearest key index
*/ */
abstract public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes); public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
final List<Key> keys = getKeys();
final int touchX = getTouchX(x);
final int touchY = getTouchY(y);
/** initializeNearbyKeys();
* Compute the most common key width in order to use it as proximity key detection threshold. int primaryIndex = NOT_A_KEY;
* for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) {
* @param keyboard The keyboard to compute the most common key width final Key key = keys.get(index);
* @return The most common key width in the keyboard final boolean isInside = key.isInside(touchX, touchY);
*/ final int distance = key.squaredDistanceToEdge(touchX, touchY);
public static int getMostCommonKeyWidth(final Keyboard keyboard) { if (isInside || (mProximityCorrectOn && distance < mProximityThresholdSquare)) {
if (keyboard == null) return 0; final int insertedPosition = sortNearbyKeys(index, distance);
final List<Key> keys = keyboard.getKeys(); if (insertedPosition == 0 && isInside)
if (keys == null || keys.size() == 0) return 0; primaryIndex = index;
final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
int maxCount = 0;
int mostCommonWidth = 0;
for (final Key key : keys) {
final Integer width = key.mWidth + key.mGap;
Integer count = histogram.get(width);
if (count == null)
count = 0;
histogram.put(width, ++count);
if (count > maxCount) {
maxCount = count;
mostCommonWidth = width;
} }
} }
return mostCommonWidth;
if (allCodes != null && allCodes.length > 0) {
getNearbyKeyCodes(allCodes);
if (DEBUG) {
Log.d(TAG, "x=" + x + " y=" + y
+ " primary="
+ (primaryIndex == NOT_A_KEY ? "none" : keys.get(primaryIndex).mCode)
+ " codes=" + Arrays.toString(allCodes));
}
}
return primaryIndex;
} }
} }

View File

@ -128,6 +128,8 @@ public class Keyboard {
/** Height of keyboard */ /** Height of keyboard */
private int mKeyboardHeight; private int mKeyboardHeight;
private int mMostCommonKeyWidth = 0;
public final KeyboardId mId; public final KeyboardId mId;
// Variables for pre-computing nearest keys. // Variables for pre-computing nearest keys.
@ -405,6 +407,41 @@ public class Keyboard {
return EMPTY_INT_ARRAY; return EMPTY_INT_ARRAY;
} }
/**
* Compute the most common key width in order to use it as proximity key detection threshold.
*
* @return The most common key width in the keyboard
*/
public int getMostCommonKeyWidth() {
if (mMostCommonKeyWidth == 0) {
final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
int maxCount = 0;
int mostCommonWidth = 0;
for (final Key key : mKeys) {
final Integer width = key.mWidth + key.mGap;
Integer count = histogram.get(width);
if (count == null)
count = 0;
histogram.put(width, ++count);
if (count > maxCount) {
maxCount = count;
mostCommonWidth = width;
}
}
mMostCommonKeyWidth = mostCommonWidth;
}
return mMostCommonKeyWidth;
}
/**
* Return true if spacebar needs showing preview even when "popup on keypress" is off.
* @param keyIndex index of the pressing key
* @return true if spacebar needs showing preview
*/
public boolean needSpacebarPreview(int keyIndex) {
return false;
}
private void loadKeyboard(Context context, int xmlLayoutResId) { private void loadKeyboard(Context context, int xmlLayoutResId) {
try { try {
KeyboardParser parser = new KeyboardParser(this, context.getResources()); KeyboardParser parser = new KeyboardParser(this, context.getResources());

View File

@ -18,7 +18,6 @@ package com.android.inputmethod.keyboard;
import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SubtypeSwitcher;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -47,6 +46,7 @@ import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.PopupWindow; import android.widget.PopupWindow;
import android.widget.TextView; import android.widget.TextView;
@ -105,7 +105,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
// Main keyboard // Main keyboard
private Keyboard mKeyboard; private Keyboard mKeyboard;
private Key[] mKeys;
// Key preview popup // Key preview popup
private boolean mInForeground; private boolean mInForeground;
@ -146,7 +145,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
// Accessibility // Accessibility
private boolean mIsAccessibilityEnabled; private boolean mIsAccessibilityEnabled;
protected KeyDetector mKeyDetector = new ProximityKeyDetector(); protected KeyDetector mKeyDetector = new KeyDetector();
// Swipe gesture detector // Swipe gesture detector
private GestureDetector mGestureDetector; private GestureDetector mGestureDetector;
@ -224,7 +223,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
public void popupPreview(long delay, int keyIndex, PointerTracker tracker) { public void popupPreview(long delay, int keyIndex, PointerTracker tracker) {
removeMessages(MSG_POPUP_PREVIEW); removeMessages(MSG_POPUP_PREVIEW);
if (mPreviewText.getVisibility() == VISIBLE) { if (mPreviewText.getVisibility() == VISIBLE || delay == 0) {
// Show right away, if it's already visible and finger is moving around // Show right away, if it's already visible and finger is moving around
showKey(keyIndex, tracker); showKey(keyIndex, tracker);
} else { } else {
@ -492,15 +491,15 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
mHandler.cancelPopupPreview(); mHandler.cancelPopupPreview();
mKeyboard = keyboard; mKeyboard = keyboard;
LatinImeLogger.onSetKeyboard(keyboard); LatinImeLogger.onSetKeyboard(keyboard);
mKeys = mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
-getPaddingTop() + mVerticalCorrection); -getPaddingTop() + mVerticalCorrection);
for (PointerTracker tracker : mPointerTrackers) { for (PointerTracker tracker : mPointerTrackers) {
tracker.setKeyboard(keyboard, mKeys, mKeyHysteresisDistance); tracker.setKeyboard(keyboard, mKeyHysteresisDistance);
} }
requestLayout(); requestLayout();
mKeyboardChanged = true; mKeyboardChanged = true;
invalidateAllKeys(); invalidateAllKeys();
mKeyDetector.setProximityThreshold(KeyDetector.getMostCommonKeyWidth(keyboard)); mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth());
mMiniKeyboardCache.clear(); mMiniKeyboardCache.clear();
} }
@ -637,154 +636,32 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
} }
final Canvas canvas = mCanvas; final Canvas canvas = mCanvas;
canvas.clipRect(mDirtyRect, Op.REPLACE); canvas.clipRect(mDirtyRect, Op.REPLACE);
canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
if (mKeyboard == null) return; if (mKeyboard == null) return;
final Paint paint = mPaint; if (mInvalidatedKey != null && mInvalidatedKeyRect.contains(mDirtyRect)) {
final Drawable keyBackground = mKeyBackground; // Draw a single key.
final Rect padding = mPadding; onBufferDrawKey(canvas, mInvalidatedKey);
final int kbdPaddingLeft = getPaddingLeft(); } else {
final int kbdPaddingTop = getPaddingTop(); // Draw all keys.
final Key[] keys = mKeys; for (final Key key : mKeyboard.getKeys()) {
final boolean isManualTemporaryUpperCase = mKeyboard.isManualTemporaryUpperCase(); onBufferDrawKey(canvas, key);
final boolean drawSingleKey = (mInvalidatedKey != null
&& mInvalidatedKeyRect.contains(mDirtyRect));
canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
final int keyCount = keys.length;
for (int i = 0; i < keyCount; i++) {
final Key key = keys[i];
if (drawSingleKey && key != mInvalidatedKey) {
continue;
} }
int[] drawableState = key.getCurrentDrawableState();
keyBackground.setState(drawableState);
// Switch the character to uppercase if shift is pressed
String label = key.mLabel == null? null : adjustCase(key.mLabel).toString();
final int keyDrawX = key.mX + key.mVisualInsetsLeft;
final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
final Rect bounds = keyBackground.getBounds();
if (keyDrawWidth != bounds.right || key.mHeight != bounds.bottom) {
keyBackground.setBounds(0, 0, keyDrawWidth, key.mHeight);
}
canvas.translate(keyDrawX + kbdPaddingLeft, key.mY + kbdPaddingTop);
keyBackground.draw(canvas);
final int rowHeight = padding.top + key.mHeight;
// Draw key label
if (label != null) {
// For characters, use large font. For labels like "Done", use small font.
final int labelSize = getLabelSizeAndSetPaint(label, key.mLabelOption, paint);
final int labelCharHeight = getLabelCharHeight(labelSize, paint);
// Vertical label text alignment.
final float baseline;
if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_BOTTOM) != 0) {
baseline = key.mHeight -
+ labelCharHeight * KEY_LABEL_VERTICAL_PADDING_FACTOR;
if (DEBUG_SHOW_ALIGN)
drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
new Paint());
} else { // Align center
final float centerY = (key.mHeight + padding.top - padding.bottom) / 2;
baseline = centerY
+ labelCharHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER;
if (DEBUG_SHOW_ALIGN)
drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
new Paint());
}
// Horizontal label text alignment
final int positionX;
if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
positionX = mKeyLabelHorizontalPadding + padding.left;
paint.setTextAlign(Align.LEFT);
if (DEBUG_SHOW_ALIGN)
drawVerticalLine(canvas, positionX, rowHeight, 0xc0800080, new Paint());
} else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
positionX = keyDrawWidth - mKeyLabelHorizontalPadding - padding.right;
paint.setTextAlign(Align.RIGHT);
if (DEBUG_SHOW_ALIGN)
drawVerticalLine(canvas, positionX, rowHeight, 0xc0808000, new Paint());
} else {
positionX = (keyDrawWidth + padding.left - padding.right) / 2;
paint.setTextAlign(Align.CENTER);
if (DEBUG_SHOW_ALIGN) {
if (label.length() > 1)
drawVerticalLine(canvas, positionX, rowHeight, 0xc0008080, new Paint());
}
}
if (key.mManualTemporaryUpperCaseHintIcon != null && isManualTemporaryUpperCase) {
paint.setColor(mKeyTextColorDisabled);
} else {
paint.setColor(mKeyTextColor);
}
if (key.mEnabled) {
// Set a drop shadow for the text
paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor);
} else {
// Make label invisible
paint.setColor(Color.TRANSPARENT);
}
canvas.drawText(label, positionX, baseline, paint);
// Turn off drop shadow
paint.setShadowLayer(0, 0, 0, 0);
}
// Draw key icon
final Drawable icon = key.getIcon();
if (key.mLabel == null && icon != null) {
final int drawableWidth = icon.getIntrinsicWidth();
final int drawableHeight = icon.getIntrinsicHeight();
final int drawableX;
final int drawableY = (
key.mHeight + padding.top - padding.bottom - drawableHeight) / 2;
if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
drawableX = padding.left + mKeyLabelHorizontalPadding;
if (DEBUG_SHOW_ALIGN)
drawVerticalLine(canvas, drawableX, rowHeight, 0xc0800080, new Paint());
} else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
drawableX = keyDrawWidth - padding.right - mKeyLabelHorizontalPadding
- drawableWidth;
if (DEBUG_SHOW_ALIGN)
drawVerticalLine(canvas, drawableX + drawableWidth, rowHeight,
0xc0808000, new Paint());
} else { // Align center
drawableX = (keyDrawWidth + padding.left - padding.right - drawableWidth) / 2;
if (DEBUG_SHOW_ALIGN)
drawVerticalLine(canvas, drawableX + drawableWidth / 2, rowHeight,
0xc0008080, new Paint());
}
drawIcon(canvas, icon, drawableX, drawableY, drawableWidth, drawableHeight);
if (DEBUG_SHOW_ALIGN)
drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
0x80c00000, new Paint());
}
if (key.mHintIcon != null) {
final int drawableWidth = keyDrawWidth;
final int drawableHeight = key.mHeight;
final int drawableX = 0;
final int drawableY = HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL;
Drawable hintIcon = (isManualTemporaryUpperCase
&& key.mManualTemporaryUpperCaseHintIcon != null)
? key.mManualTemporaryUpperCaseHintIcon : key.mHintIcon;
drawIcon(canvas, hintIcon, drawableX, drawableY, drawableWidth, drawableHeight);
if (DEBUG_SHOW_ALIGN)
drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
0x80c0c000, new Paint());
}
canvas.translate(-keyDrawX - kbdPaddingLeft, -key.mY - kbdPaddingTop);
} }
// TODO: Move this function to ProximityInfo for getting rid of public declarations for // TODO: Move this function to ProximityInfo for getting rid of
// public declarations for
// GRID_WIDTH and GRID_HEIGHT // GRID_WIDTH and GRID_HEIGHT
if (DEBUG_KEYBOARD_GRID) { if (DEBUG_KEYBOARD_GRID) {
Paint p = new Paint(); Paint p = new Paint();
p.setStyle(Paint.Style.STROKE); p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(1.0f); p.setStrokeWidth(1.0f);
p.setColor(0x800000c0); p.setColor(0x800000c0);
int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1) / mKeyboard.GRID_WIDTH; int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1)
int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1) / mKeyboard.GRID_HEIGHT; / mKeyboard.GRID_WIDTH;
int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1)
/ mKeyboard.GRID_HEIGHT;
for (int i = 0; i <= mKeyboard.GRID_WIDTH; i++) for (int i = 0; i <= mKeyboard.GRID_WIDTH; i++)
canvas.drawLine(i * cw, 0, i * cw, ch * mKeyboard.GRID_HEIGHT, p); canvas.drawLine(i * cw, 0, i * cw, ch * mKeyboard.GRID_HEIGHT, p);
for (int i = 0; i <= mKeyboard.GRID_HEIGHT; i++) for (int i = 0; i <= mKeyboard.GRID_HEIGHT; i++)
@ -793,8 +670,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
// Overlay a dark rectangle to dim the keyboard // Overlay a dark rectangle to dim the keyboard
if (mMiniKeyboardView != null) { if (mMiniKeyboardView != null) {
paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24); mPaint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
canvas.drawRect(0, 0, width, height, paint); canvas.drawRect(0, 0, width, height, mPaint);
} }
mInvalidatedKey = null; mInvalidatedKey = null;
@ -802,6 +679,134 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
mDirtyRect.setEmpty(); mDirtyRect.setEmpty();
} }
private void onBufferDrawKey(final Canvas canvas, final Key key) {
final Paint paint = mPaint;
final Drawable keyBackground = mKeyBackground;
final Rect padding = mPadding;
final int kbdPaddingLeft = getPaddingLeft();
final int kbdPaddingTop = getPaddingTop();
final int keyDrawX = key.mX + key.mVisualInsetsLeft;
final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
final int rowHeight = padding.top + key.mHeight;
final boolean isManualTemporaryUpperCase = mKeyboard.isManualTemporaryUpperCase();
canvas.translate(keyDrawX + kbdPaddingLeft, key.mY + kbdPaddingTop);
// Draw key background.
final int[] drawableState = key.getCurrentDrawableState();
keyBackground.setState(drawableState);
final Rect bounds = keyBackground.getBounds();
if (keyDrawWidth != bounds.right || key.mHeight != bounds.bottom) {
keyBackground.setBounds(0, 0, keyDrawWidth, key.mHeight);
}
keyBackground.draw(canvas);
// Draw key label.
if (key.mLabel != null) {
// Switch the character to uppercase if shift is pressed
final String label = key.mLabel == null ? null : adjustCase(key.mLabel).toString();
// For characters, use large font. For labels like "Done", use small font.
final int labelSize = getLabelSizeAndSetPaint(label, key.mLabelOption, paint);
final int labelCharHeight = getLabelCharHeight(labelSize, paint);
// Vertical label text alignment.
final float baseline;
if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_BOTTOM) != 0) {
baseline = key.mHeight - labelCharHeight * KEY_LABEL_VERTICAL_PADDING_FACTOR;
if (DEBUG_SHOW_ALIGN)
drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
new Paint());
} else { // Align center
final float centerY = (key.mHeight + padding.top - padding.bottom) / 2;
baseline = centerY + labelCharHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER;
if (DEBUG_SHOW_ALIGN)
drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
new Paint());
}
// Horizontal label text alignment
final int positionX;
if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
positionX = mKeyLabelHorizontalPadding + padding.left;
paint.setTextAlign(Align.LEFT);
if (DEBUG_SHOW_ALIGN)
drawVerticalLine(canvas, positionX, rowHeight, 0xc0800080, new Paint());
} else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
positionX = keyDrawWidth - mKeyLabelHorizontalPadding - padding.right;
paint.setTextAlign(Align.RIGHT);
if (DEBUG_SHOW_ALIGN)
drawVerticalLine(canvas, positionX, rowHeight, 0xc0808000, new Paint());
} else {
positionX = (keyDrawWidth + padding.left - padding.right) / 2;
paint.setTextAlign(Align.CENTER);
if (DEBUG_SHOW_ALIGN) {
if (label.length() > 1)
drawVerticalLine(canvas, positionX, rowHeight, 0xc0008080, new Paint());
}
}
if (key.mManualTemporaryUpperCaseHintIcon != null && isManualTemporaryUpperCase) {
paint.setColor(mKeyTextColorDisabled);
} else {
paint.setColor(mKeyTextColor);
}
if (key.mEnabled) {
// Set a drop shadow for the text
paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor);
} else {
// Make label invisible
paint.setColor(Color.TRANSPARENT);
}
canvas.drawText(label, positionX, baseline, paint);
// Turn off drop shadow
paint.setShadowLayer(0, 0, 0, 0);
}
// Draw key icon.
final Drawable icon = key.getIcon();
if (key.mLabel == null && icon != null) {
final int drawableWidth = icon.getIntrinsicWidth();
final int drawableHeight = icon.getIntrinsicHeight();
final int drawableX;
final int drawableY = (key.mHeight + padding.top - padding.bottom - drawableHeight) / 2;
if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
drawableX = padding.left + mKeyLabelHorizontalPadding;
if (DEBUG_SHOW_ALIGN)
drawVerticalLine(canvas, drawableX, rowHeight, 0xc0800080, new Paint());
} else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
drawableX = keyDrawWidth - padding.right - mKeyLabelHorizontalPadding
- drawableWidth;
if (DEBUG_SHOW_ALIGN)
drawVerticalLine(canvas, drawableX + drawableWidth, rowHeight,
0xc0808000, new Paint());
} else { // Align center
drawableX = (keyDrawWidth + padding.left - padding.right - drawableWidth) / 2;
if (DEBUG_SHOW_ALIGN)
drawVerticalLine(canvas, drawableX + drawableWidth / 2, rowHeight,
0xc0008080, new Paint());
}
drawIcon(canvas, icon, drawableX, drawableY, drawableWidth, drawableHeight);
if (DEBUG_SHOW_ALIGN)
drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
0x80c00000, new Paint());
}
// Draw hint icon.
if (key.mHintIcon != null) {
final int drawableWidth = keyDrawWidth;
final int drawableHeight = key.mHeight;
final int drawableX = 0;
final int drawableY = HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL;
Drawable hintIcon = (isManualTemporaryUpperCase
&& key.mManualTemporaryUpperCaseHintIcon != null)
? key.mManualTemporaryUpperCaseHintIcon : key.mHintIcon;
drawIcon(canvas, hintIcon, drawableX, drawableY, drawableWidth, drawableHeight);
if (DEBUG_SHOW_ALIGN)
drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
0x80c0c000, new Paint());
}
canvas.translate(-keyDrawX - kbdPaddingLeft, -key.mY - kbdPaddingTop);
}
public int getLabelSizeAndSetPaint(CharSequence label, int keyLabelOption, Paint paint) { public int getLabelSizeAndSetPaint(CharSequence label, int keyLabelOption, Paint paint) {
// For characters, use large font. For labels like "Done", use small font. // For characters, use large font. For labels like "Done", use small font.
final int labelSize; final int labelSize;
@ -883,15 +888,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
public void showPreview(int keyIndex, PointerTracker tracker) { public void showPreview(int keyIndex, PointerTracker tracker) {
int oldKeyIndex = mOldPreviewKeyIndex; int oldKeyIndex = mOldPreviewKeyIndex;
mOldPreviewKeyIndex = keyIndex; mOldPreviewKeyIndex = keyIndex;
// We should re-draw popup preview when 1) we need to hide the preview, 2) we will show if ((mShowPreview && oldKeyIndex != keyIndex) || mKeyboard.needSpacebarPreview(keyIndex)) {
// the space key preview and 3) pointer moves off the space key to other letter key, we
// should hide the preview of the previous key.
final boolean hidePreviewOrShowSpaceKeyPreview = (tracker == null)
|| (SubtypeSwitcher.getInstance().useSpacebarLanguageSwitcher()
&& SubtypeSwitcher.getInstance().needsToDisplayLanguage()
&& (tracker.isSpaceKey(keyIndex) || tracker.isSpaceKey(oldKeyIndex)));
// If key changed and preview is on or the key is space (language switch is enabled)
if (oldKeyIndex != keyIndex && (mShowPreview || (hidePreviewOrShowSpaceKeyPreview))) {
if (keyIndex == KeyDetector.NOT_A_KEY) { if (keyIndex == KeyDetector.NOT_A_KEY) {
mHandler.cancelPopupPreview(); mHandler.cancelPopupPreview();
mHandler.dismissPreview(mDelayAfterPreview); mHandler.dismissPreview(mDelayAfterPreview);
@ -908,10 +905,18 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
if (mPreviewText.getParent() == null) { if (mPreviewText.getParent() == null) {
final FrameLayout screenContent = (FrameLayout) getRootView() final FrameLayout screenContent = (FrameLayout) getRootView()
.findViewById(android.R.id.content); .findViewById(android.R.id.content);
screenContent.addView(mPreviewText, new FrameLayout.LayoutParams(0, 0)); if (android.os.Build.VERSION.SDK_INT >= /* HONEYCOMB */ 11) {
screenContent.addView(mPreviewText, new FrameLayout.LayoutParams(0, 0));
} else {
// Insert LinearLayout to be able to setMargin because pre-Honeycomb FrameLayout
// could not handle setMargin properly.
final LinearLayout placer = new LinearLayout(getContext());
screenContent.addView(placer);
placer.addView(mPreviewText, new LinearLayout.LayoutParams(0, 0));
}
} }
Key key = tracker.getKey(keyIndex); final Key key = tracker.getKey(keyIndex);
// If keyIndex is invalid or IME is already closed, we must not show key preview. // If keyIndex is invalid or IME is already closed, we must not show key preview.
// Trying to show preview PopupWindow while root window is closed causes // Trying to show preview PopupWindow while root window is closed causes
// WindowManager.BadTokenException. // WindowManager.BadTokenException.
@ -943,10 +948,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
+ mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight()); + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
final int popupHeight = mPreviewHeight; final int popupHeight = mPreviewHeight;
final ViewGroup.LayoutParams lp = mPreviewText.getLayoutParams(); final ViewGroup.LayoutParams lp = mPreviewText.getLayoutParams();
if (lp != null) { lp.width = popupWidth;
lp.width = popupWidth; lp.height = popupHeight;
lp.height = popupHeight;
}
int popupPreviewX = keyDrawX - (popupWidth - keyDrawWidth) / 2; int popupPreviewX = keyDrawX - (popupWidth - keyDrawWidth) / 2;
int popupPreviewY = key.mY - popupHeight + mPreviewOffset; int popupPreviewY = key.mY - popupHeight + mPreviewOffset;
@ -1172,15 +1175,14 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
private PointerTracker getPointerTracker(final int id) { private PointerTracker getPointerTracker(final int id) {
final ArrayList<PointerTracker> pointers = mPointerTrackers; final ArrayList<PointerTracker> pointers = mPointerTrackers;
final Key[] keys = mKeys;
final KeyboardActionListener listener = mKeyboardActionListener; final KeyboardActionListener listener = mKeyboardActionListener;
// Create pointer trackers until we can get 'id+1'-th tracker, if needed. // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
for (int i = pointers.size(); i <= id; i++) { for (int i = pointers.size(); i <= id; i++) {
final PointerTracker tracker = final PointerTracker tracker =
new PointerTracker(i, mHandler, mKeyDetector, this, getResources()); new PointerTracker(i, mHandler, mKeyDetector, this, getResources());
if (keys != null) if (mKeyboard != null)
tracker.setKeyboard(mKeyboard, keys, mKeyHysteresisDistance); tracker.setKeyboard(mKeyboard, mKeyHysteresisDistance);
if (listener != null) if (listener != null)
tracker.setOnKeyboardActionListener(listener); tracker.setOnKeyboardActionListener(listener);
pointers.add(tracker); pointers.add(tracker);

View File

@ -26,6 +26,9 @@ import android.content.res.TypedArray;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Paint.Align; import android.graphics.Paint.Align;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
@ -33,15 +36,18 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
// TODO: We should remove this class // TODO: We should remove this class
public class LatinKeyboard extends Keyboard { public class LatinKeyboard extends Keyboard {
public static final int OPACITY_FULLY_OPAQUE = 255;
private static final int SPACE_LED_LENGTH_PERCENT = 80; private static final int SPACE_LED_LENGTH_PERCENT = 80;
private final Context mContext; private final Context mContext;
private final SubtypeSwitcher mSubtypeSwitcher = SubtypeSwitcher.getInstance();
/* Space key and its icons, drawables and colors. */ /* Space key and its icons, drawables and colors. */
private final Key mSpaceKey; private final Key mSpaceKey;
@ -59,14 +65,18 @@ public class LatinKeyboard extends Keyboard {
private int mSpaceDragLastDiff; private int mSpaceDragLastDiff;
private boolean mCurrentlyInSpace; private boolean mCurrentlyInSpace;
private SlidingLocaleDrawable mSlidingLocaleIcon; private SlidingLocaleDrawable mSlidingLocaleIcon;
private final HashMap<Integer, SoftReference<BitmapDrawable>> mSpaceDrawableCache =
new HashMap<Integer, SoftReference<BitmapDrawable>>();
/* Shortcut key and its icons if available */ /* Shortcut key and its icons if available */
private final Key mShortcutKey; private final Key mShortcutKey;
private final Drawable mEnabledShortcutIcon; private final Drawable mEnabledShortcutIcon;
private final Drawable mDisabledShortcutIcon; private final Drawable mDisabledShortcutIcon;
private static final float SPACEBAR_DRAG_THRESHOLD = 0.8f; // Minimum width of spacebar dragging to trigger the language switch (represented by the number
// Minimum width of space key preview (proportional to keyboard width) // of the most common key width of this keyboard).
private static final int SPACEBAR_DRAG_WIDTH = 3;
// Minimum width of space key preview (proportional to keyboard width).
private static final float SPACEBAR_POPUP_MIN_RATIO = 0.4f; private static final float SPACEBAR_POPUP_MIN_RATIO = 0.4f;
// Height in space key the language name will be drawn. (proportional to space key height) // Height in space key the language name will be drawn. (proportional to space key height)
public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f; public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f;
@ -137,6 +147,12 @@ public class LatinKeyboard extends Keyboard {
return newColor; return newColor;
} }
private static ColorFilter getSpacebarDrawableFilter(float fadeFactor) {
final ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setScale(1, 1, 1, fadeFactor);
return new ColorMatrixColorFilter(colorMatrix);
}
public void updateShortcutKey(boolean available, LatinKeyboardView view) { public void updateShortcutKey(boolean available, LatinKeyboardView view) {
if (mShortcutKey == null) if (mShortcutKey == null)
return; return;
@ -157,19 +173,14 @@ public class LatinKeyboard extends Keyboard {
private void updateSpacebarForLocale(boolean isAutoCorrection) { private void updateSpacebarForLocale(boolean isAutoCorrection) {
if (mSpaceKey == null) if (mSpaceKey == null)
return; return;
final Resources res = mContext.getResources();
// If application locales are explicitly selected. // If application locales are explicitly selected.
if (SubtypeSwitcher.getInstance().needsToDisplayLanguage()) { if (mSubtypeSwitcher.needsToDisplayLanguage()) {
mSpaceKey.setIcon(new BitmapDrawable(res, mSpaceKey.setIcon(getSpaceDrawable(
drawSpacebar(OPACITY_FULLY_OPAQUE, isAutoCorrection))); mSubtypeSwitcher.getInputLocale(), isAutoCorrection));
} else if (isAutoCorrection) {
mSpaceKey.setIcon(getSpaceDrawable(null, true));
} else { } else {
// sym_keyboard_space_led can be shared with Black and White symbol themes. mSpaceKey.setIcon(mSpaceIcon);
if (isAutoCorrection) {
mSpaceKey.setIcon(new BitmapDrawable(res,
drawSpacebar(OPACITY_FULLY_OPAQUE, isAutoCorrection)));
} else {
mSpaceKey.setIcon(mSpaceIcon);
}
} }
} }
@ -223,19 +234,31 @@ public class LatinKeyboard extends Keyboard {
return language; return language;
} }
private Bitmap drawSpacebar(int opacity, boolean isAutoCorrection) { private BitmapDrawable getSpaceDrawable(Locale locale, boolean isAutoCorrection) {
final Integer hashCode = Arrays.hashCode(
new Object[] { locale, isAutoCorrection, mSpacebarTextFadeFactor });
final SoftReference<BitmapDrawable> ref = mSpaceDrawableCache.get(hashCode);
BitmapDrawable drawable = (ref == null) ? null : ref.get();
if (drawable == null) {
drawable = new BitmapDrawable(mContext.getResources(), drawSpacebar(
locale, isAutoCorrection, mSpacebarTextFadeFactor));
mSpaceDrawableCache.put(hashCode, new SoftReference<BitmapDrawable>(drawable));
}
return drawable;
}
private Bitmap drawSpacebar(Locale inputLocale, boolean isAutoCorrection,
float textFadeFactor) {
final int width = mSpaceKey.mWidth; final int width = mSpaceKey.mWidth;
final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : mSpaceKey.mHeight; final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : mSpaceKey.mHeight;
final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(buffer); final Canvas canvas = new Canvas(buffer);
final Resources res = mContext.getResources(); final Resources res = mContext.getResources();
canvas.drawColor(res.getColor(android.R.color.transparent), PorterDuff.Mode.CLEAR); canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
// If application locales are explicitly selected. // If application locales are explicitly selected.
if (subtypeSwitcher.needsToDisplayLanguage()) { if (inputLocale != null) {
final Paint paint = new Paint(); final Paint paint = new Paint();
paint.setAlpha(opacity);
paint.setAntiAlias(true); paint.setAntiAlias(true);
paint.setTextAlign(Align.CENTER); paint.setTextAlign(Align.CENTER);
@ -253,7 +276,7 @@ public class LatinKeyboard extends Keyboard {
} }
final boolean allowVariableTextSize = true; final boolean allowVariableTextSize = true;
final String language = layoutSpacebar(paint, subtypeSwitcher.getInputLocale(), final String language = layoutSpacebar(paint, inputLocale,
mButtonArrowLeftIcon, mButtonArrowRightIcon, width, height, mButtonArrowLeftIcon, mButtonArrowRightIcon, width, height,
getTextSizeFromTheme(mContext.getTheme(), textStyle, defaultTextSize), getTextSizeFromTheme(mContext.getTheme(), textStyle, defaultTextSize),
allowVariableTextSize); allowVariableTextSize);
@ -265,14 +288,16 @@ public class LatinKeyboard extends Keyboard {
final float textHeight = -paint.ascent() + descent; final float textHeight = -paint.ascent() + descent;
final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE
: height / 2 + textHeight / 2; : height / 2 + textHeight / 2;
paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, mSpacebarTextFadeFactor)); paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, textFadeFactor));
canvas.drawText(language, width / 2, baseline - descent - 1, paint); canvas.drawText(language, width / 2, baseline - descent - 1, paint);
paint.setColor(getSpacebarTextColor(mSpacebarTextColor, mSpacebarTextFadeFactor)); paint.setColor(getSpacebarTextColor(mSpacebarTextColor, textFadeFactor));
canvas.drawText(language, width / 2, baseline - descent, paint); canvas.drawText(language, width / 2, baseline - descent, paint);
// Put arrows that are already layed out on either side of the text // Put arrows that are already layed out on either side of the text
if (subtypeSwitcher.useSpacebarLanguageSwitcher() if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()
&& subtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) { && mSubtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
mButtonArrowLeftIcon.setColorFilter(getSpacebarDrawableFilter(textFadeFactor));
mButtonArrowRightIcon.setColorFilter(getSpacebarDrawableFilter(textFadeFactor));
mButtonArrowLeftIcon.draw(canvas); mButtonArrowLeftIcon.draw(canvas);
mButtonArrowRightIcon.draw(canvas); mButtonArrowRightIcon.draw(canvas);
} }
@ -316,9 +341,25 @@ public class LatinKeyboard extends Keyboard {
mSpaceKey.getPreviewIcon().invalidateSelf(); mSpaceKey.getPreviewIcon().invalidateSelf();
} }
// This method is called when "popup on keypress" is off.
@Override
public boolean needSpacebarPreview(int keyIndex) {
if (!mSubtypeSwitcher.useSpacebarLanguageSwitcher())
return false;
// Dismiss key preview.
if (keyIndex == KeyDetector.NOT_A_KEY)
return true;
// Key is not a spacebar.
if (keyIndex != mSpaceKeyIndexArray[0])
return false;
// The language switcher will be displayed only when the dragging distance is greater
// than average key width of this keyboard.
return Math.abs(mSpaceDragLastDiff) > getMostCommonKeyWidth();
}
public int getLanguageChangeDirection() { public int getLanguageChangeDirection() {
if (mSpaceKey == null || SubtypeSwitcher.getInstance().getEnabledKeyboardLocaleCount() <= 1 if (mSpaceKey == null || SubtypeSwitcher.getInstance().getEnabledKeyboardLocaleCount() <= 1
|| Math.abs(mSpaceDragLastDiff) < mSpaceKey.mWidth * SPACEBAR_DRAG_THRESHOLD) { || Math.abs(mSpaceDragLastDiff) < getMostCommonKeyWidth() * SPACEBAR_DRAG_WIDTH) {
return 0; // No change return 0; // No change
} }
return mSpaceDragLastDiff > 0 ? 1 : -1; return mSpaceDragLastDiff > 0 ? 1 : -1;

View File

@ -34,7 +34,7 @@ public class MiniKeyboardBuilder {
/* package */ static class MiniKeyboardLayoutParams { /* package */ static class MiniKeyboardLayoutParams {
public final int mKeyWidth; public final int mKeyWidth;
public final int mRowHeight; public final int mRowHeight;
/* package */ final boolean mTopRowNeedsCentering; /* package */ final int mTopRowAdjustment;
public final int mNumRows; public final int mNumRows;
public final int mNumColumns; public final int mNumColumns;
public final int mLeftKeys; public final int mLeftKeys;
@ -55,29 +55,52 @@ public class MiniKeyboardBuilder {
if (parentKeyboardWidth / keyWidth < maxColumns) if (parentKeyboardWidth / keyWidth < maxColumns)
throw new IllegalArgumentException("Keyboard is too small to hold mini keyboard: " throw new IllegalArgumentException("Keyboard is too small to hold mini keyboard: "
+ parentKeyboardWidth + " " + keyWidth + " " + maxColumns); + parentKeyboardWidth + " " + keyWidth + " " + maxColumns);
final int numRows = (numKeys + maxColumns - 1) / maxColumns;
mKeyWidth = keyWidth; mKeyWidth = keyWidth;
mRowHeight = rowHeight; mRowHeight = rowHeight;
mNumRows = numRows;
final int numColumns = Math.min(numKeys, maxColumns); final int numRows = (numKeys + maxColumns - 1) / maxColumns;
final int topRowKeys = numKeys % numColumns; mNumRows = numRows;
final int numColumns = getOptimizedColumns(numKeys, maxColumns);
mNumColumns = numColumns; mNumColumns = numColumns;
mTopRowNeedsCentering = topRowKeys != 0 && (numColumns - topRowKeys) % 2 != 0;
final int numLeftKeys = (numColumns - 1) / 2; final int numLeftKeys = (numColumns - 1) / 2;
final int numRightKeys = numColumns - numLeftKeys; // including default key. final int numRightKeys = numColumns - numLeftKeys; // including default key.
final int maxLeftKeys = coordXInParent / keyWidth; final int maxLeftKeys = coordXInParent / keyWidth;
final int maxRightKeys = Math.max(1, (parentKeyboardWidth - coordXInParent) / keyWidth); final int maxRightKeys = Math.max(1, (parentKeyboardWidth - coordXInParent) / keyWidth);
int leftKeys, rightKeys;
if (numLeftKeys > maxLeftKeys) { if (numLeftKeys > maxLeftKeys) {
mLeftKeys = maxLeftKeys; leftKeys = maxLeftKeys;
mRightKeys = numColumns - maxLeftKeys; rightKeys = numColumns - maxLeftKeys;
} else if (numRightKeys > maxRightKeys) { } else if (numRightKeys > maxRightKeys) {
mLeftKeys = numColumns - maxRightKeys; leftKeys = numColumns - maxRightKeys;
mRightKeys = maxRightKeys; rightKeys = maxRightKeys;
} else { } else {
mLeftKeys = numLeftKeys; leftKeys = numLeftKeys;
mRightKeys = numRightKeys; rightKeys = numRightKeys;
}
// Shift right if the left edge of mini keyboard is on the edge of parent keyboard
// unless the parent key is on the left edge.
if (leftKeys * keyWidth >= coordXInParent && leftKeys > 0) {
leftKeys--;
rightKeys++;
}
// Shift left if the right edge of mini keyboard is on the edge of parent keyboard
// unless the parent key is on the right edge.
if (rightKeys * keyWidth + coordXInParent >= parentKeyboardWidth && rightKeys > 1) {
leftKeys++;
rightKeys--;
}
mLeftKeys = leftKeys;
mRightKeys = rightKeys;
// Centering of the top row.
final boolean onEdge = (leftKeys == 0 || rightKeys == 1);
if (numRows < 2 || onEdge || getTopRowEmptySlots(numKeys, numColumns) % 2 == 0) {
mTopRowAdjustment = 0;
} else if (mLeftKeys < mRightKeys - 1) {
mTopRowAdjustment = 1;
} else {
mTopRowAdjustment = -1;
} }
} }
@ -113,14 +136,32 @@ public class MiniKeyboardBuilder {
return pos; return pos;
} }
private static int getTopRowEmptySlots(int numKeys, int numColumns) {
final int remainingKeys = numKeys % numColumns;
if (remainingKeys == 0) {
return 0;
} else {
return numColumns - remainingKeys;
}
}
private int getOptimizedColumns(int numKeys, int maxColumns) {
int numColumns = Math.min(numKeys, maxColumns);
while (getTopRowEmptySlots(numKeys, numColumns) >= mNumRows) {
numColumns--;
}
return numColumns;
}
public int getDefaultKeyCoordX() { public int getDefaultKeyCoordX() {
return mLeftKeys * mKeyWidth; return mLeftKeys * mKeyWidth;
} }
public int getX(int n, int row) { public int getX(int n, int row) {
final int x = getColumnPos(n) * mKeyWidth + getDefaultKeyCoordX(); final int x = getColumnPos(n) * mKeyWidth + getDefaultKeyCoordX();
if (isLastRow(row) && mTopRowNeedsCentering) if (isTopRow(row)) {
return x - mKeyWidth / 2; return x + mTopRowAdjustment * (mKeyWidth / 2);
}
return x; return x;
} }
@ -131,27 +172,27 @@ public class MiniKeyboardBuilder {
public int getRowFlags(int row) { public int getRowFlags(int row) {
int rowFlags = 0; int rowFlags = 0;
if (row == 0) rowFlags |= Keyboard.EDGE_TOP; if (row == 0) rowFlags |= Keyboard.EDGE_TOP;
if (isLastRow(row)) rowFlags |= Keyboard.EDGE_BOTTOM; if (isTopRow(row)) rowFlags |= Keyboard.EDGE_BOTTOM;
return rowFlags; return rowFlags;
} }
private boolean isLastRow(int rowCount) { private boolean isTopRow(int rowCount) {
return rowCount == mNumRows - 1; return rowCount == mNumRows - 1;
} }
} }
public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key popupKey) { public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key parentKey) {
final Context context = view.getContext(); final Context context = view.getContext();
mRes = context.getResources(); mRes = context.getResources();
final MiniKeyboard keyboard = new MiniKeyboard(context, layoutTemplateResId, null); final MiniKeyboard keyboard = new MiniKeyboard(context, layoutTemplateResId, null);
mKeyboard = keyboard; mKeyboard = keyboard;
mPopupCharacters = popupKey.mPopupCharacters; mPopupCharacters = parentKey.mPopupCharacters;
final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, keyboard.getKeyWidth()); final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, keyboard.getKeyWidth());
final MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams( final MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
mPopupCharacters.length, popupKey.mMaxPopupColumn, mPopupCharacters.length, parentKey.mMaxPopupColumn,
keyWidth, keyboard.getRowHeight(), keyWidth, keyboard.getRowHeight(),
popupKey.mX + (popupKey.mWidth + popupKey.mGap) / 2 - keyWidth / 2, parentKey.mX + (parentKey.mWidth + parentKey.mGap) / 2 - keyWidth / 2,
view.getMeasuredWidth()); view.getMeasuredWidth());
mParams = params; mParams = params;

View File

@ -16,9 +16,9 @@
package com.android.inputmethod.keyboard; package com.android.inputmethod.keyboard;
public class MiniKeyboardKeyDetector extends KeyDetector { import java.util.List;
private static final int MAX_NEARBY_KEYS = 1;
public class MiniKeyboardKeyDetector extends KeyDetector {
private final int mSlideAllowanceSquare; private final int mSlideAllowanceSquare;
private final int mSlideAllowanceSquareTop; private final int mSlideAllowanceSquareTop;
@ -31,20 +31,21 @@ public class MiniKeyboardKeyDetector extends KeyDetector {
@Override @Override
protected int getMaxNearbyKeys() { protected int getMaxNearbyKeys() {
return MAX_NEARBY_KEYS; // No nearby key will be returned.
return 1;
} }
@Override @Override
public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) { public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
final Key[] keys = getKeys(); final List<Key> keys = getKeys();
final int touchX = getTouchX(x); final int touchX = getTouchX(x);
final int touchY = getTouchY(y); final int touchY = getTouchY(y);
int nearestIndex = NOT_A_KEY; int nearestIndex = NOT_A_KEY;
int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare; int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
final int keyCount = keys.length; final int keyCount = keys.size();
for (int index = 0; index < keyCount; index++) { for (int index = 0; index < keyCount; index++) {
final int dist = keys[index].squaredDistanceToEdge(touchX, touchY); final int dist = keys.get(index).squaredDistanceToEdge(touchX, touchY);
if (dist < nearestDist) { if (dist < nearestDist) {
nearestIndex = index; nearestIndex = index;
nearestDist = dist; nearestDist = dist;
@ -52,7 +53,7 @@ public class MiniKeyboardKeyDetector extends KeyDetector {
} }
if (allCodes != null && nearestIndex != NOT_A_KEY) if (allCodes != null && nearestIndex != NOT_A_KEY)
allCodes[0] = keys[nearestIndex].mCode; allCodes[0] = keys.get(nearestIndex).mCode;
return nearestIndex; return nearestIndex;
} }
} }

View File

@ -25,6 +25,7 @@ import android.util.Log;
import android.view.MotionEvent; import android.view.MotionEvent;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
public class PointerTracker { public class PointerTracker {
private static final String TAG = PointerTracker.class.getSimpleName(); private static final String TAG = PointerTracker.class.getSimpleName();
@ -63,7 +64,7 @@ public class PointerTracker {
private final int mTouchNoiseThresholdDistanceSquared; private final int mTouchNoiseThresholdDistanceSquared;
private Keyboard mKeyboard; private Keyboard mKeyboard;
private Key[] mKeys; private List<Key> mKeys;
private int mKeyHysteresisDistanceSquared = -1; private int mKeyHysteresisDistanceSquared = -1;
private int mKeyQuarterWidthSquared; private int mKeyQuarterWidthSquared;
@ -109,8 +110,8 @@ public class PointerTracker {
public void onSwipeDown() {} public void onSwipeDown() {}
}; };
public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy, public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector,
Resources res) { UIProxy proxy, Resources res) {
if (proxy == null || handler == null || keyDetector == null) if (proxy == null || handler == null || keyDetector == null)
throw new NullPointerException(); throw new NullPointerException();
mPointerId = id; mPointerId = id;
@ -197,11 +198,11 @@ public class PointerTracker {
mListener.onCancelInput(); mListener.onCancelInput();
} }
public void setKeyboard(Keyboard keyboard, Key[] keys, float keyHysteresisDistance) { public void setKeyboard(Keyboard keyboard, float keyHysteresisDistance) {
if (keyboard == null || keys == null || keyHysteresisDistance < 0) if (keyboard == null || keyHysteresisDistance < 0)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
mKeyboard = keyboard; mKeyboard = keyboard;
mKeys = keys; mKeys = keyboard.getKeys();
mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance); mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance);
final int keyQuarterWidth = keyboard.getKeyWidth() / 4; final int keyQuarterWidth = keyboard.getKeyWidth() / 4;
mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth; mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
@ -214,11 +215,11 @@ public class PointerTracker {
} }
private boolean isValidKeyIndex(int keyIndex) { private boolean isValidKeyIndex(int keyIndex) {
return keyIndex >= 0 && keyIndex < mKeys.length; return keyIndex >= 0 && keyIndex < mKeys.size();
} }
public Key getKey(int keyIndex) { public Key getKey(int keyIndex) {
return isValidKeyIndex(keyIndex) ? mKeys[keyIndex] : null; return isValidKeyIndex(keyIndex) ? mKeys.get(keyIndex) : null;
} }
private static boolean isModifierCode(int primaryCode) { private static boolean isModifierCode(int primaryCode) {
@ -258,12 +259,14 @@ public class PointerTracker {
mPreviousKey = keyIndex; mPreviousKey = keyIndex;
if (keyIndex != oldKeyIndex) { if (keyIndex != oldKeyIndex) {
if (isValidKeyIndex(oldKeyIndex)) { if (isValidKeyIndex(oldKeyIndex)) {
mKeys[oldKeyIndex].onReleased(); final Key oldKey = mKeys.get(oldKeyIndex);
mProxy.invalidateKey(mKeys[oldKeyIndex]); oldKey.onReleased();
mProxy.invalidateKey(oldKey);
} }
if (isValidKeyIndex(keyIndex)) { if (isValidKeyIndex(keyIndex)) {
mKeys[keyIndex].onPressed(); final Key newKey = mKeys.get(keyIndex);
mProxy.invalidateKey(mKeys[keyIndex]); newKey.onPressed();
mProxy.invalidateKey(newKey);
} }
} }
} }
@ -488,9 +491,6 @@ public class PointerTracker {
if (!mIsRepeatableKey) { if (!mIsRepeatableKey) {
detectAndSendKey(keyIndex, x, y); detectAndSendKey(keyIndex, x, y);
} }
if (isValidKeyIndex(keyIndex))
mProxy.invalidateKey(mKeys[keyIndex]);
} }
public void onCancelEvent(int x, int y, long eventTime, PointerTrackerQueue queue) { public void onCancelEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
@ -508,9 +508,6 @@ public class PointerTracker {
mHandler.cancelPopupPreview(); mHandler.cancelPopupPreview();
showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY);
mIsInSlidingKeyInput = false; mIsInSlidingKeyInput = false;
int keyIndex = mKeyState.getKeyIndex();
if (isValidKeyIndex(keyIndex))
mProxy.invalidateKey(mKeys[keyIndex]);
} }
public void repeatKey(int keyIndex) { public void repeatKey(int keyIndex) {
@ -539,7 +536,7 @@ public class PointerTracker {
if (newKey == curKey) { if (newKey == curKey) {
return true; return true;
} else if (isValidKeyIndex(curKey)) { } else if (isValidKeyIndex(curKey)) {
return mKeys[curKey].squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared; return mKeys.get(curKey).squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared;
} else { } else {
return false; return false;
} }

View File

@ -1,124 +0,0 @@
/*
* 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.keyboard;
import android.util.Log;
import java.util.Arrays;
public class ProximityKeyDetector extends KeyDetector {
private static final String TAG = ProximityKeyDetector.class.getSimpleName();
private static final boolean DEBUG = false;
private static final int MAX_NEARBY_KEYS = 12;
// working area
private final int[] mDistances = new int[MAX_NEARBY_KEYS];
private final int[] mIndices = new int[MAX_NEARBY_KEYS];
@Override
protected int getMaxNearbyKeys() {
return MAX_NEARBY_KEYS;
}
private void initializeNearbyKeys() {
Arrays.fill(mDistances, Integer.MAX_VALUE);
Arrays.fill(mIndices, NOT_A_KEY);
}
/**
* Insert the key into nearby keys buffer and sort nearby keys by ascending order of distance.
*
* @param keyIndex index of the key.
* @param distance distance between the key's edge and user touched point.
* @return order of the key in the nearby buffer, 0 if it is the nearest key.
*/
private int sortNearbyKeys(int keyIndex, int distance) {
final int[] distances = mDistances;
final int[] indices = mIndices;
for (int insertPos = 0; insertPos < distances.length; insertPos++) {
if (distance < distances[insertPos]) {
final int nextPos = insertPos + 1;
if (nextPos < distances.length) {
System.arraycopy(distances, insertPos, distances, nextPos,
distances.length - nextPos);
System.arraycopy(indices, insertPos, indices, nextPos,
indices.length - nextPos);
}
distances[insertPos] = distance;
indices[insertPos] = keyIndex;
return insertPos;
}
}
return distances.length;
}
private void getNearbyKeyCodes(final int[] allCodes) {
final Key[] keys = getKeys();
final int[] indices = mIndices;
// allCodes[0] should always have the key code even if it is a non-letter key.
if (indices[0] == NOT_A_KEY) {
allCodes[0] = NOT_A_CODE;
return;
}
int numCodes = 0;
for (int j = 0; j < indices.length && numCodes < allCodes.length; j++) {
final int index = indices[j];
if (index == NOT_A_KEY)
break;
final int code = keys[index].mCode;
// filter out a non-letter key from nearby keys
if (code < Keyboard.CODE_SPACE)
continue;
allCodes[numCodes++] = code;
}
}
@Override
public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
final Key[] keys = getKeys();
final int touchX = getTouchX(x);
final int touchY = getTouchY(y);
initializeNearbyKeys();
int primaryIndex = NOT_A_KEY;
for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) {
final Key key = keys[index];
final boolean isInside = key.isInside(touchX, touchY);
final int distance = key.squaredDistanceToEdge(touchX, touchY);
if (isInside || (mProximityCorrectOn && distance < mProximityThresholdSquare)) {
final int insertedPosition = sortNearbyKeys(index, distance);
if (insertedPosition == 0 && isInside)
primaryIndex = index;
}
}
if (allCodes != null && allCodes.length > 0) {
getNearbyKeyCodes(allCodes);
if (DEBUG) {
Log.d(TAG, "x=" + x + " y=" + y
+ " primary="
+ (primaryIndex == NOT_A_KEY ? "none" : keys[primaryIndex].mCode)
+ " codes=" + Arrays.toString(allCodes));
}
}
return primaryIndex;
}
}

View File

@ -23,6 +23,7 @@ import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter; import android.graphics.ColorFilter;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
@ -63,9 +64,8 @@ public class SlidingLocaleDrawable extends Drawable {
mHeight = height; mHeight = height;
final TextPaint textPaint = new TextPaint(); final TextPaint textPaint = new TextPaint();
textPaint.setTextSize(getTextSizeFromTheme(android.R.style.TextAppearance_Medium, 18)); textPaint.setTextSize(getTextSizeFromTheme(android.R.style.TextAppearance_Medium, 18));
textPaint.setColor(android.R.color.transparent); textPaint.setColor(Color.TRANSPARENT);
textPaint.setTextAlign(Align.CENTER); textPaint.setTextAlign(Align.CENTER);
textPaint.setAlpha(LatinKeyboard.OPACITY_FULLY_OPAQUE);
textPaint.setAntiAlias(true); textPaint.setAntiAlias(true);
mTextPaint = textPaint; mTextPaint = textPaint;
mMiddleX = (mWidth - mBackground.getIntrinsicWidth()) / 2; mMiddleX = (mWidth - mBackground.getIntrinsicWidth()) / 2;

View File

@ -30,6 +30,11 @@ import android.net.Uri;
public class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver { public class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver {
final LatinIME mService; final LatinIME mService;
/**
* The action of the intent for publishing that new dictionary data is available.
*/
/* package */ static final String NEW_DICTIONARY_INTENT_ACTION =
"com.android.inputmethod.latin.dictionarypack.newdict";
public DictionaryPackInstallBroadcastReceiver(final LatinIME service) { public DictionaryPackInstallBroadcastReceiver(final LatinIME service) {
mService = service; mService = service;
@ -77,6 +82,8 @@ public class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver {
// TODO: Only reload dictionary on REMOVED when the removed package is the one we // TODO: Only reload dictionary on REMOVED when the removed package is the one we
// read dictionary from? // read dictionary from?
mService.resetSuggestMainDict(); mService.resetSuggestMainDict();
} else if (action.equals(NEW_DICTIONARY_INTENT_ACTION)) {
mService.resetSuggestMainDict();
} }
} }
} }

View File

@ -440,6 +440,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
packageFilter.addDataScheme(SCHEME_PACKAGE); packageFilter.addDataScheme(SCHEME_PACKAGE);
registerReceiver(mDictionaryPackInstallReceiver, packageFilter); registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
final IntentFilter newDictFilter = new IntentFilter();
newDictFilter.addAction(
DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION);
registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
} }
private void initSuggest() { private void initSuggest() {

View File

@ -145,7 +145,7 @@ static void prof_out(void) {
#define WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE 75 #define WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE 75
#define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 60 #define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 60
#define FULL_MATCHED_WORDS_PROMOTION_RATE 120 #define FULL_MATCHED_WORDS_PROMOTION_RATE 120
#define WORDS_WITH_JUST_ONE_PROXIMITY_CHARACTER_PROMOTION_RATE 110 #define WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE 90
// This should be greater than or equal to MAX_WORD_LENGTH defined in BinaryDictionary.java // This should be greater than or equal to MAX_WORD_LENGTH defined in BinaryDictionary.java
// This is only used for the size of array. Not to be used in c functions. // This is only used for the size of array. Not to be used in c functions.

View File

@ -552,13 +552,13 @@ inline int UnigramDictionary::calculateFinalFreq(const int inputIndex, const int
if (sameLength && transposedPos < 0 && skipPos < 0 && excessivePos < 0) { if (sameLength && transposedPos < 0 && skipPos < 0 && excessivePos < 0) {
finalFreq = capped255MultForFullMatchAccentsOrCapitalizationDifference(finalFreq); finalFreq = capped255MultForFullMatchAccentsOrCapitalizationDifference(finalFreq);
} }
} else if (lengthFreq / 2 == matchWeight && transposedPos < 0 && skipPos < 0 } else if (sameLength && transposedPos < 0 && skipPos < 0 && excessivePos < 0 && depth > 1) {
&& excessivePos < 0 && depth > 1) { // A word with proximity corrections
// Full match except only one proximity correction
if (DEBUG_DICT) { if (DEBUG_DICT) {
LOGI("Found one proximity correction."); LOGI("Found one proximity correction.");
} }
multiplyRate(WORDS_WITH_JUST_ONE_PROXIMITY_CHARACTER_PROMOTION_RATE, &finalFreq); finalFreq *= 2;
multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
} }
if (sameLength) finalFreq *= FULL_WORD_MULTIPLIER; if (sameLength) finalFreq *= FULL_WORD_MULTIPLIER;
return finalFreq; return finalFreq;

View File

@ -20,7 +20,6 @@ import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.keyboard.LatinKeyboard; import com.android.inputmethod.keyboard.LatinKeyboard;
import com.android.inputmethod.keyboard.ProximityKeyDetector;
import android.content.Context; import android.content.Context;
import android.text.TextUtils; import android.text.TextUtils;
@ -38,7 +37,7 @@ public class SuggestHelper {
// (and not try to find a dictionary provider for a specified locale) // (and not try to find a dictionary provider for a specified locale)
mSuggest = new Suggest(context, dictionaryId, null); mSuggest = new Suggest(context, dictionaryId, null);
mKeyboard = new LatinKeyboard(context, keyboardId); mKeyboard = new LatinKeyboard(context, keyboardId);
mKeyDetector = new ProximityKeyDetector(); mKeyDetector = new KeyDetector();
init(); init();
} }
@ -46,7 +45,7 @@ public class SuggestHelper {
KeyboardId keyboardId) { KeyboardId keyboardId) {
mSuggest = new Suggest(dictionaryPath, startOffset, length, null); mSuggest = new Suggest(dictionaryPath, startOffset, length, null);
mKeyboard = new LatinKeyboard(context, keyboardId); mKeyboard = new LatinKeyboard(context, keyboardId);
mKeyDetector = new ProximityKeyDetector(); mKeyDetector = new KeyDetector();
init(); init();
} }
@ -55,7 +54,7 @@ public class SuggestHelper {
mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL); mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL);
mKeyDetector.setKeyboard(mKeyboard, 0, 0); mKeyDetector.setKeyboard(mKeyboard, 0, 0);
mKeyDetector.setProximityCorrectionEnabled(true); mKeyDetector.setProximityCorrectionEnabled(true);
mKeyDetector.setProximityThreshold(KeyDetector.getMostCommonKeyWidth(mKeyboard)); mKeyDetector.setProximityThreshold(mKeyboard.getMostCommonKeyWidth());
} }
public void setCorrectionMode(int correctionMode) { public void setCorrectionMode(int correctionMode) {