From f39fccbd0fd63647c52e8eabcb60df69f97492b5 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 18 Jul 2012 14:27:51 +0900 Subject: [PATCH 01/36] Make GestureStroke as top level class And make PointerTracker object has GestureStroke object. Change-Id: Ibf5cfd593c4f13468368e01acb847589b0ab12e7 --- .../inputmethod/keyboard/PointerTracker.java | 45 ++++- .../keyboard/internal/GestureStroke.java | 149 ++++++++++++++ .../keyboard/internal/GestureTracker.java | 188 +----------------- 3 files changed, 199 insertions(+), 183 deletions(-) create mode 100644 java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 733d3b09b..437bbf06b 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -22,8 +22,10 @@ import android.view.MotionEvent; import android.view.View; import android.widget.TextView; +import com.android.inputmethod.keyboard.internal.GestureStroke; import com.android.inputmethod.keyboard.internal.GestureTracker; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; +import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.ResearchLogger; import com.android.inputmethod.latin.define.ProductionFlag; @@ -165,6 +167,8 @@ public class PointerTracker { // Gesture tracker singleton instance private static final GestureTracker sGestureTracker = GestureTracker.getInstance(); + private final GestureStroke mGestureStroke; + public static void init(boolean hasDistinctMultitouch, boolean needsPhantomSuddenMoveEventHack) { if (hasDistinctMultitouch) { @@ -222,10 +226,43 @@ public class PointerTracker { } } - public PointerTracker(int id, KeyEventHandler handler) { + // The working and returning object of the following methods, + // {@link #getIncrementalBatchPoints()} and {@link #getAllBatchPoints()}. + private static final InputPointers mAggregatedPointers = new InputPointers(); + + // TODO: This method is called only from GestureTracker and should address the thread-safty + // issue soon. + public static InputPointers getIncrementalBatchPoints() { + final InputPointers pointers = mAggregatedPointers; + pointers.reset(); + for (final PointerTracker tracker : sTrackers) { + tracker.getGestureStroke().appendIncrementalBatchPoints(pointers); + } + return pointers; + } + + // TODO: This method is called only from GestureTracker and should address the thread-safety + // issue soon. + public static InputPointers getAllBatchPoints() { + final InputPointers pointers = mAggregatedPointers; + pointers.reset(); + for (final PointerTracker tracker : sTrackers) { + tracker.getGestureStroke().appendAllBatchPoints(pointers); + } + return pointers; + } + + public static void clearBatchInputPoints() { + for (final PointerTracker tracker : sTrackers) { + tracker.getGestureStroke().reset(); + } + } + + private PointerTracker(int id, KeyEventHandler handler) { if (handler == null) throw new NullPointerException(); mPointerId = id; + mGestureStroke = new GestureStroke(id); setKeyDetectorInner(handler.getKeyDetector()); mListener = handler.getKeyboardActionListener(); mDrawingProxy = handler.getDrawingProxy(); @@ -237,6 +274,10 @@ public class PointerTracker { return mKeyPreviewText; } + public GestureStroke getGestureStroke() { + return mGestureStroke; + } + // Returns true if keyboard has been changed by this callback. private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) { if (sGestureTracker.isInGesture()) { @@ -328,6 +369,8 @@ public class PointerTracker { private void setKeyDetectorInner(KeyDetector keyDetector) { mKeyDetector = keyDetector; mKeyboard = keyDetector.getKeyboard(); + mGestureStroke.setGestureSampleLength( + mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight); final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY); if (newKey != mCurrentKey) { if (mDrawingProxy != null) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java new file mode 100644 index 000000000..14e99487d --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2012 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. + */ + +package com.android.inputmethod.keyboard.internal; + +import android.util.FloatMath; + +import com.android.inputmethod.latin.InputPointers; + +public class GestureStroke { + private final int mPointerId; + private final InputPointers mInputPointers = new InputPointers(); + private float mLength; + private float mAngle; + private int mIncrementalRecognitionPoint; + private boolean mHasSharpCorner; + private long mLastPointTime; + private int mLastPointX; + private int mLastPointY; + + private int mMinGestureLength; + private int mMinGestureSampleLength; + + // TODO: Tune these parameters. + private static final float MIN_GESTURE_DETECTION_RATIO_TO_KEY_WIDTH = 1.0f / 4.0f; + private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT = 1.0f / 6.0f; + private static final int MIN_GESTURE_DURATION = 100; // msec + private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f; // dip/msec + private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float)(Math.PI / 4.0f); + + private static final float DOUBLE_PI = (float)(2 * Math.PI); + + public GestureStroke(int pointerId) { + mPointerId = pointerId; + reset(); + } + + public void setGestureSampleLength(final int keyWidth, final int keyHeight) { + mMinGestureLength = (int)(keyWidth * MIN_GESTURE_DETECTION_RATIO_TO_KEY_WIDTH); + mMinGestureSampleLength = (int)(keyHeight * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT); + } + + public boolean isStartOfAGesture(int downDuration) { + return downDuration > MIN_GESTURE_DURATION && mLength > mMinGestureLength; + } + + public void reset() { + mLength = 0; + mAngle = 0; + mIncrementalRecognitionPoint = 0; + mHasSharpCorner = false; + mLastPointTime = 0; + mInputPointers.reset(); + } + + private void updateLastPoint(final int x, final int y, final int time) { + mLastPointTime = time; + mLastPointX = x; + mLastPointY = y; + } + + public void addPoint(final int x, final int y, final int time, final boolean isHistorical) { + final int size = mInputPointers.getPointerSize(); + if (size == 0) { + mInputPointers.addPointer(x, y, mPointerId, time); + if (!isHistorical) { + updateLastPoint(x, y, time); + } + return; + } + + final int[] xCoords = mInputPointers.getXCoordinates(); + final int[] yCoords = mInputPointers.getYCoordinates(); + final int lastX = xCoords[size - 1]; + final int lastY = yCoords[size - 1]; + final float dist = getDistance(lastX, lastY, x, y); + if (dist > mMinGestureSampleLength) { + mInputPointers.addPointer(x, y, mPointerId, time); + mLength += dist; + final float angle = getAngle(lastX, lastY, x, y); + if (size > 1) { + float curvature = getAngleDiff(angle, mAngle); + if (curvature > GESTURE_RECOG_CURVATURE_THRESHOLD) { + if (size > mIncrementalRecognitionPoint) { + mIncrementalRecognitionPoint = size; + } + mHasSharpCorner = true; + } + if (!mHasSharpCorner) { + mIncrementalRecognitionPoint = size; + } + } + mAngle = angle; + } + + if (!isHistorical) { + final int duration = (int)(time - mLastPointTime); + if (mLastPointTime != 0 && duration > 0) { + final float speed = getDistance(mLastPointX, mLastPointY, x, y) / duration; + if (speed < GESTURE_RECOG_SPEED_THRESHOLD) { + mIncrementalRecognitionPoint = size; + } + } + updateLastPoint(x, y, time); + } + } + + public void appendAllBatchPoints(final InputPointers out) { + out.append(mInputPointers, 0, mInputPointers.getPointerSize()); + } + + public void appendIncrementalBatchPoints(final InputPointers out) { + out.append(mInputPointers, 0, mIncrementalRecognitionPoint); + } + + private static float getDistance(final int p1x, final int p1y, + final int p2x, final int p2y) { + final float dx = p1x - p2x; + final float dy = p1y - p2y; + // TODO: Optimize out this {@link FloatMath#sqrt(float)} call. + return FloatMath.sqrt(dx * dx + dy * dy); + } + + private static float getAngle(final int p1x, final int p1y, final int p2x, final int p2y) { + final int dx = p1x - p2x; + final int dy = p1y - p2y; + if (dx == 0 && dy == 0) return 0; + return (float)Math.atan2(dy, dx); + } + + private static float getAngleDiff(final float a1, final float a2) { + final float diff = Math.abs(a1 - a2); + if (diff > Math.PI) { + return DOUBLE_PI - diff; + } + return diff; + } +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java index dfd697a7a..0f14dcef4 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java @@ -15,7 +15,6 @@ package com.android.inputmethod.keyboard.internal; import android.util.Log; -import android.util.SparseArray; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; @@ -35,12 +34,6 @@ public class GestureTracker { private static final GestureTracker sInstance = new GestureTracker(); private static final int MIN_RECOGNITION_TIME = 100; - private static final int MIN_GESTURE_DURATION = 200; - - private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f; - private static final float SQUARED_GESTURE_RECOG_SPEED_THRESHOLD = - GESTURE_RECOG_SPEED_THRESHOLD * GESTURE_RECOG_SPEED_THRESHOLD; - private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float) (Math.PI / 4); private boolean mIsAlphabetKeyboard; private boolean mIsPossibleGesture = false; @@ -49,8 +42,6 @@ public class GestureTracker { private KeyboardActionListener mListener; private SuggestedWords mSuggestions; - private final SparseArray mGestureStrokes = new SparseArray(); - private int mLastRecognitionPointSize = 0; private long mLastRecognitionTime = 0; @@ -67,8 +58,6 @@ public class GestureTracker { public void setKeyboard(Keyboard keyboard) { mIsAlphabetKeyboard = keyboard.mId.isAlphabetKeyboard(); - GestureStroke.setGestureSampleLength(keyboard.mMostCommonKeyWidth / 2, - keyboard.mMostCommonKeyHeight / 6); } private void startBatchInput() { @@ -107,7 +96,7 @@ public class GestureTracker { // A gesture should start only from the letter key. if (GESTURE_ON && mIsAlphabetKeyboard && key != null && Keyboard.isLetterCode(key.mCode)) { mIsPossibleGesture = true; - addPointToStroke(x, y, 0, tracker.mPointerId, false); + tracker.getGestureStroke().addPoint(x, y, 0, false); } } @@ -115,15 +104,15 @@ public class GestureTracker { boolean isHistorical, Key key) { final int gestureTime = (int)(eventTime - tracker.getDownTime()); if (GESTURE_ON && mIsPossibleGesture) { - final GestureStroke stroke = addPointToStroke(x, y, gestureTime, tracker.mPointerId, - isHistorical); + final GestureStroke stroke = tracker.getGestureStroke(); + stroke.addPoint(x, y, gestureTime, isHistorical); if (!isInGesture() && stroke.isStartOfAGesture(gestureTime)) { startBatchInput(); } } if (key != null && isInGesture()) { - final InputPointers batchPoints = getIncrementalBatchPoints(); + final InputPointers batchPoints = PointerTracker.getIncrementalBatchPoints(); if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) { if (DEBUG_LISTENER) { Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize()); @@ -135,7 +124,7 @@ public class GestureTracker { public void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) { if (isInGesture()) { - final InputPointers batchPoints = getAllBatchPoints(); + final InputPointers batchPoints = PointerTracker.getAllBatchPoints(); if (DEBUG_LISTENER) { Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize()); } @@ -143,49 +132,8 @@ public class GestureTracker { } } - private GestureStroke addPointToStroke(int x, int y, int time, int pointerId, - boolean isHistorical) { - GestureStroke stroke = mGestureStrokes.get(pointerId); - if (stroke == null) { - stroke = new GestureStroke(pointerId); - mGestureStrokes.put(pointerId, stroke); - } - stroke.addPoint(x, y, time, isHistorical); - return stroke; - } - - // The working and return object of the following methods, {@link #getIncrementalBatchPoints()} - // and {@link #getAllBatchPoints()}. - private final InputPointers mAggregatedPointers = new InputPointers(); - - private InputPointers getIncrementalBatchPoints() { - final InputPointers pointers = mAggregatedPointers; - pointers.reset(); - final int strokeSize = mGestureStrokes.size(); - for (int index = 0; index < strokeSize; index++) { - final GestureStroke stroke = mGestureStrokes.valueAt(index); - stroke.appendIncrementalBatchPoints(pointers); - } - return pointers; - } - - private InputPointers getAllBatchPoints() { - final InputPointers pointers = mAggregatedPointers; - pointers.reset(); - final int strokeSize = mGestureStrokes.size(); - for (int index = 0; index < strokeSize; index++) { - final GestureStroke stroke = mGestureStrokes.valueAt(index); - stroke.appendAllBatchPoints(pointers); - } - return pointers; - } - private void clearBatchInputPoints() { - final int strokeSize = mGestureStrokes.size(); - for (int index = 0; index < strokeSize; index++) { - final GestureStroke stroke = mGestureStrokes.valueAt(index); - stroke.reset(); - } + PointerTracker.clearBatchInputPoints(); mLastRecognitionPointSize = 0; mLastRecognitionTime = 0; } @@ -199,128 +147,4 @@ public class GestureTracker { } return false; } - - private static class GestureStroke { - private final int mPointerId; - private final InputPointers mInputPointers = new InputPointers(); - private float mLength; - private float mAngle; - private int mIncrementalRecognitionPoint; - private boolean mHasSharpCorner; - private long mLastPointTime; - private int mLastPointX; - private int mLastPointY; - - private static int sMinGestureLength; - private static int sSquaredGestureSampleLength; - - private static final float DOUBLE_PI = (float)(2 * Math.PI); - - public static void setGestureSampleLength(final int minGestureLength, - final int sampleLength) { - sMinGestureLength = minGestureLength; - sSquaredGestureSampleLength = sampleLength * sampleLength; - } - - public GestureStroke(int pointerId) { - mPointerId = pointerId; - reset(); - } - - public boolean isStartOfAGesture(int downDuration) { - return downDuration > MIN_GESTURE_DURATION / 2 && mLength > sMinGestureLength / 2; - } - - public void reset() { - mLength = 0; - mAngle = 0; - mIncrementalRecognitionPoint = 0; - mHasSharpCorner = false; - mLastPointTime = 0; - mInputPointers.reset(); - } - - private void updateLastPoint(final int x, final int y, final int time) { - mLastPointTime = time; - mLastPointX = x; - mLastPointY = y; - } - - public void addPoint(final int x, final int y, final int time, final boolean isHistorical) { - final int size = mInputPointers.getPointerSize(); - if (size == 0) { - mInputPointers.addPointer(x, y, mPointerId, time); - if (!isHistorical) { - updateLastPoint(x, y, time); - } - return; - } - - final int[] xCoords = mInputPointers.getXCoordinates(); - final int[] yCoords = mInputPointers.getYCoordinates(); - final int lastX = xCoords[size - 1]; - final int lastY = yCoords[size - 1]; - final float dist = squaredDistance(lastX, lastY, x, y); - if (dist > sSquaredGestureSampleLength) { - mInputPointers.addPointer(x, y, mPointerId, time); - mLength += dist; - final float angle = angle(lastX, lastY, x, y); - if (size > 1) { - float curvature = getAngleDiff(angle, mAngle); - if (curvature > GESTURE_RECOG_CURVATURE_THRESHOLD) { - if (size > mIncrementalRecognitionPoint) { - mIncrementalRecognitionPoint = size; - } - mHasSharpCorner = true; - } - if (!mHasSharpCorner) { - mIncrementalRecognitionPoint = size; - } - } - mAngle = angle; - } - - if (!isHistorical) { - final int duration = (int)(time - mLastPointTime); - if (mLastPointTime != 0 && duration > 0) { - final int squaredDuration = duration * duration; - final float squaredSpeed = - squaredDistance(mLastPointX, mLastPointY, x, y) / squaredDuration; - if (squaredSpeed < SQUARED_GESTURE_RECOG_SPEED_THRESHOLD) { - mIncrementalRecognitionPoint = size; - } - } - updateLastPoint(x, y, time); - } - } - - private float getAngleDiff(float a1, float a2) { - final float diff = Math.abs(a1 - a2); - if (diff > Math.PI) { - return DOUBLE_PI - diff; - } - return diff; - } - - public void appendAllBatchPoints(InputPointers out) { - out.append(mInputPointers, 0, mInputPointers.getPointerSize()); - } - - public void appendIncrementalBatchPoints(InputPointers out) { - out.append(mInputPointers, 0, mIncrementalRecognitionPoint); - } - } - - static float squaredDistance(int p1x, int p1y, int p2x, int p2y) { - final float dx = p1x - p2x; - final float dy = p1y - p2y; - return dx * dx + dy * dy; - } - - static float angle(int p1x, int p1y, int p2x, int p2y) { - final int dx = p1x - p2x; - final int dy = p1y - p2y; - if (dx == 0 && dy == 0) return 0; - return (float)Math.atan2(dy, dx); - } } From 10102f02af1216cfca115d1742740f567b909e2c Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 18 Jul 2012 18:14:51 +0900 Subject: [PATCH 02/36] Change the batch input methods of KeyboardActionListener This change also removes the reference of SuggestedWords from GestureTracker and KeyboardActionListener. Change-Id: I25ef8756007986abf99a931afd665bbfe6fa387f --- .../keyboard/KeyboardActionListener.java | 16 +++++++--------- .../keyboard/MoreKeysKeyboardView.java | 10 ++++++++-- .../keyboard/internal/GestureTracker.java | 15 ++++++--------- .../com/android/inputmethod/latin/LatinIME.java | 15 ++++++++++++--- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java index 1f3ee7680..b1621a55b 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java @@ -17,7 +17,6 @@ package com.android.inputmethod.keyboard; import com.android.inputmethod.latin.InputPointers; -import com.android.inputmethod.latin.SuggestedWords; public interface KeyboardActionListener { @@ -73,18 +72,17 @@ public interface KeyboardActionListener { public void onStartBatchInput(); /** - * Sends the batch input points data to get updated suggestions + * Sends the ongoing batch input points data. * @param batchPointers the batch input points representing the user input - * @return updated suggestions that reflects the user input */ - public SuggestedWords onUpdateBatchInput(InputPointers batchPointers); + public void onUpdateBatchInput(InputPointers batchPointers); /** - * Sends a sequence of characters to the listener as batch input. + * Sends the final batch input points data. * - * @param text the sequence of characters to be displayed as composing text. + * @param batchPointers the batch input points representing the user input */ - public void onEndBatchInput(CharSequence text); + public void onEndBatchInput(InputPointers batchPointers); /** * Called when user released a finger outside any key. @@ -109,9 +107,9 @@ public interface KeyboardActionListener { @Override public void onStartBatchInput() {} @Override - public SuggestedWords onUpdateBatchInput(InputPointers batchPointers) { return null; } + public void onUpdateBatchInput(InputPointers batchPointers) {} @Override - public void onEndBatchInput(CharSequence text) {} + public void onEndBatchInput(InputPointers batchPointers) {} @Override public void onCancelInput() {} @Override diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index 9c8069194..870eff29f 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -25,6 +25,7 @@ import android.widget.PopupWindow; import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; +import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.R; /** @@ -63,8 +64,13 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel } @Override - public void onEndBatchInput(CharSequence text) { - mListener.onEndBatchInput(text); + public void onUpdateBatchInput(InputPointers batchPointers) { + mListener.onUpdateBatchInput(batchPointers); + } + + @Override + public void onEndBatchInput(InputPointers batchPointers) { + mListener.onEndBatchInput(batchPointers); } @Override diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java index 0f14dcef4..fbfa66332 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java @@ -21,7 +21,6 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.latin.InputPointers; -import com.android.inputmethod.latin.SuggestedWords; // TODO: Remove this class by consolidating with PointerTracker public class GestureTracker { @@ -40,7 +39,6 @@ public class GestureTracker { private boolean mInGesture = false; private KeyboardActionListener mListener; - private SuggestedWords mSuggestions; private int mLastRecognitionPointSize = 0; private long mLastRecognitionTime = 0; @@ -66,17 +64,16 @@ public class GestureTracker { } mInGesture = true; mListener.onStartBatchInput(); - mSuggestions = null; } // TODO: The corresponding startBatchInput() is a private method. Reorganize the code. public void endBatchInput() { - if (isInGesture() && mSuggestions != null && mSuggestions.size() > 0) { - final CharSequence text = mSuggestions.getWord(0); + if (isInGesture()) { + final InputPointers batchPoints = PointerTracker.getAllBatchPoints(); if (DEBUG_LISTENER) { - Log.d(TAG, "onEndBatchInput: text=" + text); + Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize()); } - mListener.onEndBatchInput(text); + mListener.onEndBatchInput(batchPoints); } mInGesture = false; clearBatchInputPoints(); @@ -117,7 +114,7 @@ public class GestureTracker { if (DEBUG_LISTENER) { Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize()); } - mSuggestions = mListener.onUpdateBatchInput(batchPoints); + mListener.onUpdateBatchInput(batchPoints); } } } @@ -128,7 +125,7 @@ public class GestureTracker { if (DEBUG_LISTENER) { Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize()); } - mSuggestions = mListener.onUpdateBatchInput(batchPoints); + mListener.onUpdateBatchInput(batchPoints); } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 518bcd5ce..0f8b6c48c 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1329,13 +1329,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } @Override - public SuggestedWords onUpdateBatchInput(InputPointers batchPointers) { + public void onUpdateBatchInput(InputPointers batchPointers) { mWordComposer.setBatchInputPointers(batchPointers); - return updateSuggestionsOrPredictions(); + updateSuggestionsOrPredictions(); } @Override - public void onEndBatchInput(CharSequence text) { + public void onEndBatchInput(InputPointers batchPointers) { + mWordComposer.setBatchInputPointers(batchPointers); + final SuggestedWords suggestedWords = updateSuggestionsOrPredictions(); + if (suggestedWords == null || suggestedWords.size() == 0) { + return; + } + final CharSequence text = suggestedWords.getWord(0); + if (TextUtils.isEmpty(text)) { + return; + } mWordComposer.setBatchInputWord(text); mConnection.beginBatchEdit(); if (SPACE_STATE_PHANTOM == mSpaceState) { From 9580c467f96c542c66af86a2c376612ba4d91434 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 18 Jul 2012 17:56:30 +0900 Subject: [PATCH 03/36] Consolidate GestureTracker into PointerTracker Change-Id: Ib28fae10493a9142ba4dff6cf57f52c59766b209 --- .../inputmethod/keyboard/PointerTracker.java | 158 ++++++++++++------ .../keyboard/internal/GestureTracker.java | 147 ---------------- .../inputmethod/latin/InputPointers.java | 3 + 3 files changed, 112 insertions(+), 196 deletions(-) delete mode 100644 java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 437bbf06b..4f6af98ca 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -23,7 +23,6 @@ import android.view.View; import android.widget.TextView; import com.android.inputmethod.keyboard.internal.GestureStroke; -import com.android.inputmethod.keyboard.internal.GestureTracker; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinImeLogger; @@ -39,6 +38,10 @@ public class PointerTracker { private static final boolean DEBUG_LISTENER = false; private static boolean DEBUG_MODE = LatinImeLogger.sDBG; + // TODO: There should be an option to turn on/off the gesture input. + private static final boolean GESTURE_ON = true; + private static final int MIN_RECOGNITION_TIME = 100; // msec + public interface KeyEventHandler { /** * Get KeyDetector object that is used for this PointerTracker. @@ -128,6 +131,13 @@ public class PointerTracker { private int mKeyQuarterWidthSquared; private final TextView mKeyPreviewText; + private boolean mIsAlphabetKeyboard; + private boolean mIsPossibleGesture = false; + private boolean mInGesture = false; + + private int mLastRecognitionPointSize = 0; + private long mLastRecognitionTime = 0; + // The position and time at which first down event occurred. private long mDownTime; private long mUpTime; @@ -164,9 +174,6 @@ public class PointerTracker { private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener.Adapter(); - // Gesture tracker singleton instance - private static final GestureTracker sGestureTracker = GestureTracker.getInstance(); - private final GestureStroke mGestureStroke; public static void init(boolean hasDistinctMultitouch, @@ -207,7 +214,6 @@ public class PointerTracker { for (final PointerTracker tracker : sTrackers) { tracker.mListener = listener; } - GestureTracker.init(listener); } public static void setKeyDetector(KeyDetector keyDetector) { @@ -216,7 +222,6 @@ public class PointerTracker { // Mark that keyboard layout has been changed. tracker.mKeyboardLayoutHasBeenChanged = true; } - sGestureTracker.setKeyboard(keyDetector.getKeyboard()); } public static void dismissAllKeyPreviews() { @@ -226,35 +231,35 @@ public class PointerTracker { } } - // The working and returning object of the following methods, - // {@link #getIncrementalBatchPoints()} and {@link #getAllBatchPoints()}. - private static final InputPointers mAggregatedPointers = new InputPointers(); - - // TODO: This method is called only from GestureTracker and should address the thread-safty - // issue soon. - public static InputPointers getIncrementalBatchPoints() { - final InputPointers pointers = mAggregatedPointers; - pointers.reset(); + // TODO: To handle multi-touch gestures we may want to move this method to + // {@link PointerTrackerQueue}. + private static InputPointers getIncrementalBatchPoints() { + final InputPointers pointers = new InputPointers(); + // TODO: Add a default capacity parameter for the InputPointers' constructor. + // TODO: Avoid creating a new instance here? for (final PointerTracker tracker : sTrackers) { - tracker.getGestureStroke().appendIncrementalBatchPoints(pointers); + tracker.mGestureStroke.appendIncrementalBatchPoints(pointers); } return pointers; } - // TODO: This method is called only from GestureTracker and should address the thread-safety - // issue soon. - public static InputPointers getAllBatchPoints() { - final InputPointers pointers = mAggregatedPointers; - pointers.reset(); + // TODO: To handle multi-touch gestures we may want to move this method to + // {@link PointerTrackerQueue}. + private static InputPointers getAllBatchPoints() { + // TODO: Add a default capacity parameter for the InputPointers' constructor. + // TODO: Avoid creating a new instance here? + final InputPointers pointers = new InputPointers(); for (final PointerTracker tracker : sTrackers) { - tracker.getGestureStroke().appendAllBatchPoints(pointers); + tracker.mGestureStroke.appendAllBatchPoints(pointers); } return pointers; } + // TODO: To handle multi-touch gestures we may want to move this method to + // {@link PointerTrackerQueue}. public static void clearBatchInputPoints() { for (final PointerTracker tracker : sTrackers) { - tracker.getGestureStroke().reset(); + tracker.mGestureStroke.reset(); } } @@ -274,13 +279,9 @@ public class PointerTracker { return mKeyPreviewText; } - public GestureStroke getGestureStroke() { - return mGestureStroke; - } - // Returns true if keyboard has been changed by this callback. private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) { - if (sGestureTracker.isInGesture()) { + if (mInGesture) { return false; } final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier(); @@ -336,7 +337,7 @@ public class PointerTracker { // Note that we need primaryCode argument because the keyboard may in shifted state and the // primaryCode is different from {@link Key#mCode}. private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) { - if (sGestureTracker.isInGesture()) { + if (mInGesture) { return; } final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier(); @@ -369,6 +370,7 @@ public class PointerTracker { private void setKeyDetectorInner(KeyDetector keyDetector) { mKeyDetector = keyDetector; mKeyboard = keyDetector.getKeyboard(); + mIsAlphabetKeyboard = mKeyboard.mId.isAlphabetKeyboard(); mGestureStroke.setGestureSampleLength( mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight); final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY); @@ -441,7 +443,7 @@ public class PointerTracker { return; } - if (!key.noKeyPreview() && !sGestureTracker.isInGesture()) { + if (!key.noKeyPreview() && !mInGesture) { mDrawingProxy.showKeyPreview(this); } updatePressKeyGraphics(key); @@ -512,6 +514,45 @@ public class PointerTracker { return newKey; } + private void startBatchInput() { + if (DEBUG_LISTENER) { + Log.d(TAG, "onStartBatchInput"); + } + mInGesture = true; + mListener.onStartBatchInput(); + } + + private void updateBatchInput(InputPointers batchPoints) { + if (DEBUG_LISTENER) { + Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize()); + } + mListener.onUpdateBatchInput(batchPoints); + } + + private void endBatchInput(InputPointers batchPoints) { + if (DEBUG_LISTENER) { + Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize()); + } + mListener.onEndBatchInput(batchPoints); + mInGesture = false; + clearBatchInputPoints(); + } + + private void abortBatchInput() { + mIsPossibleGesture = false; + mInGesture = false; + } + + private boolean updateBatchInputRecognitionState(long eventTime, int size) { + if (size > mLastRecognitionPointSize + && eventTime > mLastRecognitionTime + MIN_RECOGNITION_TIME) { + mLastRecognitionPointSize = size; + mLastRecognitionTime = eventTime; + return true; + } + return false; + } + public void processMotionEvent(int action, int x, int y, long eventTime, KeyEventHandler handler) { switch (action) { @@ -570,7 +611,13 @@ public class PointerTracker { } onDownEventInternal(x, y, eventTime); if (queue != null && queue.size() == 1) { - sGestureTracker.onDownEvent(this, x, y, eventTime, key); + mIsPossibleGesture = false; + // A gesture should start only from the letter key. + if (GESTURE_ON && mIsAlphabetKeyboard && key != null + && Keyboard.isLetterCode(key.mCode)) { + mIsPossibleGesture = true; + mGestureStroke.addPoint(x, y, 0, false); + } } } @@ -606,6 +653,25 @@ public class PointerTracker { mIsInSlidingKeyInput = true; } + private void onGestureMoveEvent(PointerTracker tracker, int x, int y, long eventTime, + boolean isHistorical, Key key) { + final int gestureTime = (int)(eventTime - tracker.getDownTime()); + if (GESTURE_ON && mIsPossibleGesture) { + final GestureStroke stroke = mGestureStroke; + stroke.addPoint(x, y, gestureTime, isHistorical); + if (!mInGesture && stroke.isStartOfAGesture(gestureTime)) { + startBatchInput(); + } + } + + if (key != null && mInGesture) { + final InputPointers batchPoints = getIncrementalBatchPoints(); + if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) { + updateBatchInput(batchPoints); + } + } + } + public void onMoveEvent(int x, int y, long eventTime, MotionEvent me) { if (DEBUG_MOVE_EVENT) printTouchEvent("onMoveEvent:", x, y, eventTime); @@ -620,7 +686,7 @@ public class PointerTracker { final int historicalX = (int)me.getHistoricalX(pointerIndex, h); final int historicalY = (int)me.getHistoricalY(pointerIndex, h); final long historicalTime = me.getHistoricalEventTime(h); - sGestureTracker.onMoveEvent(this, historicalX, historicalY, historicalTime, + onGestureMoveEvent(this, historicalX, historicalY, historicalTime, true /* isHistorical */, null); } } @@ -631,8 +697,8 @@ public class PointerTracker { Key key = onMoveKey(x, y); // Register move event on gesture tracker. - sGestureTracker.onMoveEvent(this, x, y, eventTime, false, key); - if (sGestureTracker.isInGesture()) { + onGestureMoveEvent(this, x, y, eventTime, false /* isHistorical */, key); + if (mInGesture) { mIgnoreModifierKey = true; mTimerProxy.cancelLongPressTimer(); mIsInSlidingKeyInput = true; @@ -729,7 +795,7 @@ public class PointerTracker { final PointerTrackerQueue queue = sPointerTrackerQueue; if (queue != null) { - if (!sGestureTracker.isInGesture()) { + if (!mInGesture) { if (mCurrentKey != null && mCurrentKey.isModifier()) { // Before processing an up event of modifier key, all pointers already being // tracked should be released. @@ -763,21 +829,16 @@ public class PointerTracker { mIsShowingMoreKeysPanel = false; } - if (sGestureTracker.isInGesture()) { + if (mInGesture) { // Register up event on gesture tracker. - sGestureTracker.onUpEvent(this, x, y, eventTime); - if (!sPointerTrackerQueue.isAnyInSlidingKeyInput()) { - // TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code. - sGestureTracker.endBatchInput(); - } + // TODO: Figure out how to deal with multiple fingers that are in gesture, sliding, + // and/or tapping mode? + endBatchInput(getAllBatchPoints()); if (mCurrentKey != null) { callListenerOnRelease(mCurrentKey, mCurrentKey.mCode, true); + mCurrentKey = null; } - mCurrentKey = null; return; - } else { - // TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code. - sGestureTracker.endBatchInput(); } if (mKeyAlreadyProcessed) @@ -791,8 +852,7 @@ public class PointerTracker { onLongPressed(); onDownEvent(x, y, SystemClock.uptimeMillis(), handler); mIsShowingMoreKeysPanel = true; - // TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code. - sGestureTracker.abortBatchInput(); + abortBatchInput(); } public void onLongPressed() { @@ -827,7 +887,7 @@ public class PointerTracker { } private void startRepeatKey(Key key) { - if (key != null && key.isRepeatable() && !sGestureTracker.isInGesture()) { + if (key != null && key.isRepeatable() && !mInGesture) { onRegisterKey(key); mTimerProxy.startKeyRepeatTimer(this); } @@ -857,7 +917,7 @@ public class PointerTracker { } private void startLongPressTimer(Key key) { - if (key != null && key.isLongPressEnabled() && !sGestureTracker.isInGesture()) { + if (key != null && key.isLongPressEnabled() && !mInGesture) { mTimerProxy.startLongPressTimer(this); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java deleted file mode 100644 index fbfa66332..000000000 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -package com.android.inputmethod.keyboard.internal; - -import android.util.Log; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardActionListener; -import com.android.inputmethod.keyboard.PointerTracker; -import com.android.inputmethod.latin.InputPointers; - -// TODO: Remove this class by consolidating with PointerTracker -public class GestureTracker { - private static final String TAG = GestureTracker.class.getSimpleName(); - private static final boolean DEBUG_LISTENER = false; - - // TODO: There should be an option to turn on/off the gesture input. - private static final boolean GESTURE_ON = true; - - private static final GestureTracker sInstance = new GestureTracker(); - - private static final int MIN_RECOGNITION_TIME = 100; - - private boolean mIsAlphabetKeyboard; - private boolean mIsPossibleGesture = false; - private boolean mInGesture = false; - - private KeyboardActionListener mListener; - - private int mLastRecognitionPointSize = 0; - private long mLastRecognitionTime = 0; - - public static void init(KeyboardActionListener listner) { - sInstance.mListener = listner; - } - - public static GestureTracker getInstance() { - return sInstance; - } - - private GestureTracker() { - } - - public void setKeyboard(Keyboard keyboard) { - mIsAlphabetKeyboard = keyboard.mId.isAlphabetKeyboard(); - } - - private void startBatchInput() { - if (DEBUG_LISTENER) { - Log.d(TAG, "onStartBatchInput"); - } - mInGesture = true; - mListener.onStartBatchInput(); - } - - // TODO: The corresponding startBatchInput() is a private method. Reorganize the code. - public void endBatchInput() { - if (isInGesture()) { - final InputPointers batchPoints = PointerTracker.getAllBatchPoints(); - if (DEBUG_LISTENER) { - Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize()); - } - mListener.onEndBatchInput(batchPoints); - } - mInGesture = false; - clearBatchInputPoints(); - } - - public void abortBatchInput() { - mIsPossibleGesture = false; - mInGesture = false; - } - - public boolean isInGesture() { - return mInGesture; - } - - public void onDownEvent(PointerTracker tracker, int x, int y, long eventTime, Key key) { - mIsPossibleGesture = false; - // A gesture should start only from the letter key. - if (GESTURE_ON && mIsAlphabetKeyboard && key != null && Keyboard.isLetterCode(key.mCode)) { - mIsPossibleGesture = true; - tracker.getGestureStroke().addPoint(x, y, 0, false); - } - } - - public void onMoveEvent(PointerTracker tracker, int x, int y, long eventTime, - boolean isHistorical, Key key) { - final int gestureTime = (int)(eventTime - tracker.getDownTime()); - if (GESTURE_ON && mIsPossibleGesture) { - final GestureStroke stroke = tracker.getGestureStroke(); - stroke.addPoint(x, y, gestureTime, isHistorical); - if (!isInGesture() && stroke.isStartOfAGesture(gestureTime)) { - startBatchInput(); - } - } - - if (key != null && isInGesture()) { - final InputPointers batchPoints = PointerTracker.getIncrementalBatchPoints(); - if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) { - if (DEBUG_LISTENER) { - Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize()); - } - mListener.onUpdateBatchInput(batchPoints); - } - } - } - - public void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) { - if (isInGesture()) { - final InputPointers batchPoints = PointerTracker.getAllBatchPoints(); - if (DEBUG_LISTENER) { - Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize()); - } - mListener.onUpdateBatchInput(batchPoints); - } - } - - private void clearBatchInputPoints() { - PointerTracker.clearBatchInputPoints(); - mLastRecognitionPointSize = 0; - mLastRecognitionTime = 0; - } - - private boolean updateBatchInputRecognitionState(long eventTime, int size) { - if (size > mLastRecognitionPointSize - && eventTime > mLastRecognitionTime + MIN_RECOGNITION_TIME) { - mLastRecognitionPointSize = size; - mLastRecognitionTime = eventTime; - return true; - } - return false; - } -} diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java index 5ad53480f..298e2b213 100644 --- a/java/src/com/android/inputmethod/latin/InputPointers.java +++ b/java/src/com/android/inputmethod/latin/InputPointers.java @@ -60,6 +60,9 @@ public class InputPointers { * @param length the number of pointers to be appended. */ public void append(InputPointers src, int startPos, int length) { + if (length == 0) { + return; + } mXCoordinates.append(src.mXCoordinates, startPos, length); mYCoordinates.append(src.mYCoordinates, startPos, length); mPointerIds.append(src.mPointerIds, startPos, length); From 71b772ec585e74aeb7451dbe56f3be4eafa88bd2 Mon Sep 17 00:00:00 2001 From: Ying Wang Date: Wed, 18 Jul 2012 17:05:01 -0700 Subject: [PATCH 04/36] Import translations. DO NOT MERGE Change-Id: I4ba29a164c70eb3db5afbb502a81d32088813ba4 Auto-generated-cl: translation import --- java/res/values-da/strings.xml | 2 +- java/res/values-es-rUS/strings.xml | 4 ++-- java/res/values-ko/strings.xml | 4 ++-- java/res/values-pl/strings.xml | 2 +- java/res/values-sk/strings.xml | 4 ++-- java/res/values-sw/strings.xml | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index 5a128ee58..3b194ec93 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -113,7 +113,7 @@ "Sessionslogfil slettet" "Sessionslog IKKE slettet" "Sessionshistorikken er logført" - "Fejl: Sessionshistorik IKKE logført" + "Fejl: sessionshistorik IKKE logført" "Logføring af sessioner er aktiveret" "Inputsprog" "Tryk igen for at gemme" diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index 00fce612b..e5d5548ae 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -108,12 +108,12 @@ "Marca tiempo registrada" "No registrar esta sesión" "Activar registro de sesión" - "Registrar his. de sesión completo" + "Registrar hist. de sesión completo" "Eliminando registro" "Registro sesión eliminado" "NO se eliminó el registro" "Se registró el historial de sesión." - "Error al registrar his. de sesión" + "Error al registrar hist. de sesión" "Se activó el historial de sesión." "Idiomas de entrada" "Vuelve a tocar para guardar." diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index 38090d1b9..88cbdaa83 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -107,8 +107,8 @@ "로그에 타임스탬프를 기록" "타임스탬프를 기록함" "이 세션을 로그하지 마세요." - "세션 로깅을 사용하도록 설정합니다." - "전체 세션 기록을 로그합니다." + "세션 로깅 사용 설정" + "전체 세션 기록 로그" "세션 로그 삭제" "세션 로그가 삭제됨" "세션 로그가 삭제되지 않음" diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index 4e25b709c..7ecaf4fc5 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -113,7 +113,7 @@ "Usunięto dziennik sesji" "Dziennik sesji NIEUSUNIĘTY" "Historia sesji została zarejestrowana" - "Błąd: historia sesji nie została zarejestrowana." + "Błąd: historia sesji niezarejestrowana" "Włączono rejestrowanie sesji" "Języki wprowadzania" "Dotknij ponownie, aby zapisać" diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml index af98da656..efd6ea143 100644 --- a/java/res/values-sk/strings.xml +++ b/java/res/values-sk/strings.xml @@ -108,12 +108,12 @@ "Časová pečiatka zaznamenaná" "Neukl. reláciu do denníka" "Povoliť zápis relácií do denníkov" - "Zapísať hist. relácií do denníkov" + "Celá história relácií v denníkoch" "Odstraň. denníka relácie" "Denník relácie odstránený" "Denník relácie NIE JE odstr." "Hist. relácií zapísaná do denníkov" - "Chyba: hist. relácii NEBOLA zapísaná" + "Chyba: Hist. relácií NIE JE zapísaná" "Zápis relácií do denníkov povolený" "Jazyky vstupu" "Opätovným dotykom uložíte" diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index 545c41157..373d294c2 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -113,8 +113,8 @@ "Kumbukumbu za kipindi zimefutwa" "Kumbukumbu za kipindi HAZIJAFUTWA" "Historia ya kipindi imeingia" - "Hitilafu: Historia ya kipindi HAIJAINGIA" - "Kuingia kwa kipindi kumewezeshwa" + "Hitilafu: Historia ya kipindi HAIJAINGIWA" + "Kuingia katika kipindi kumewezeshwa" "Lugha zinazoruhusiwa" "Gusa tena ili kuhifadhi" "Kamusi inapatikana" From 57f7de0ba664187e13bcea5adff7f5f65eddd823 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 18 Jul 2012 20:31:09 +0900 Subject: [PATCH 05/36] Add default capacity parameter to InputPointers' constructor Change-Id: I02f23096f0682d30effe4dfc1ca57881a1e4aedc --- .../inputmethod/keyboard/PointerTracker.java | 6 ++-- .../keyboard/internal/GestureStroke.java | 5 ++- .../inputmethod/latin/InputPointers.java | 35 ++++++++++++------- .../inputmethod/latin/LastComposedWord.java | 2 +- .../inputmethod/latin/WordComposer.java | 2 +- .../inputmethod/latin/InputPointersTests.java | 24 +++++++------ 6 files changed, 43 insertions(+), 31 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 4f6af98ca..cc6789d13 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -234,9 +234,8 @@ public class PointerTracker { // TODO: To handle multi-touch gestures we may want to move this method to // {@link PointerTrackerQueue}. private static InputPointers getIncrementalBatchPoints() { - final InputPointers pointers = new InputPointers(); - // TODO: Add a default capacity parameter for the InputPointers' constructor. // TODO: Avoid creating a new instance here? + final InputPointers pointers = new InputPointers(GestureStroke.DEFAULT_CAPACITY); for (final PointerTracker tracker : sTrackers) { tracker.mGestureStroke.appendIncrementalBatchPoints(pointers); } @@ -246,9 +245,8 @@ public class PointerTracker { // TODO: To handle multi-touch gestures we may want to move this method to // {@link PointerTrackerQueue}. private static InputPointers getAllBatchPoints() { - // TODO: Add a default capacity parameter for the InputPointers' constructor. // TODO: Avoid creating a new instance here? - final InputPointers pointers = new InputPointers(); + final InputPointers pointers = new InputPointers(GestureStroke.DEFAULT_CAPACITY); for (final PointerTracker tracker : sTrackers) { tracker.mGestureStroke.appendAllBatchPoints(pointers); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 14e99487d..8740e6972 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -19,8 +19,11 @@ import android.util.FloatMath; import com.android.inputmethod.latin.InputPointers; public class GestureStroke { + public static final int DEFAULT_CAPACITY = 128; + private final int mPointerId; - private final InputPointers mInputPointers = new InputPointers(); + // TODO: Replace this {@link InputPointers} with a set of {@link ScalableIntArray}s. + private final InputPointers mInputPointers = new InputPointers(DEFAULT_CAPACITY); private float mLength; private float mAngle; private int mIncrementalRecognitionPoint; diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java index 298e2b213..febabadf7 100644 --- a/java/src/com/android/inputmethod/latin/InputPointers.java +++ b/java/src/com/android/inputmethod/latin/InputPointers.java @@ -20,10 +20,19 @@ import java.util.Arrays; // TODO: This class is not thread-safe. public class InputPointers { - private final ScalableIntArray mXCoordinates = new ScalableIntArray(); - private final ScalableIntArray mYCoordinates = new ScalableIntArray(); - private final ScalableIntArray mPointerIds = new ScalableIntArray(); - private final ScalableIntArray mTimes = new ScalableIntArray(); + private final int mDefaultCapacity; + private final ScalableIntArray mXCoordinates; + private final ScalableIntArray mYCoordinates; + private final ScalableIntArray mPointerIds; + private final ScalableIntArray mTimes; + + public InputPointers(int defaultCapacity) { + mDefaultCapacity = defaultCapacity; + mXCoordinates = new ScalableIntArray(defaultCapacity); + mYCoordinates = new ScalableIntArray(defaultCapacity); + mPointerIds = new ScalableIntArray(defaultCapacity); + mTimes = new ScalableIntArray(defaultCapacity); + } public void addPointer(int index, int x, int y, int pointerId, int time) { mXCoordinates.add(index, x); @@ -70,10 +79,11 @@ public class InputPointers { } public void reset() { - mXCoordinates.reset(); - mYCoordinates.reset(); - mPointerIds.reset(); - mTimes.reset(); + final int defaultCapacity = mDefaultCapacity; + mXCoordinates.reset(defaultCapacity); + mYCoordinates.reset(defaultCapacity); + mPointerIds.reset(defaultCapacity); + mTimes.reset(defaultCapacity); } public int getPointerSize() { @@ -97,12 +107,11 @@ public class InputPointers { } private static class ScalableIntArray { - private static final int DEFAULT_SIZE = BinaryDictionary.MAX_WORD_LENGTH; private int[] mArray; private int mLength; - public ScalableIntArray() { - reset(); + public ScalableIntArray(int capacity) { + reset(capacity); } public void add(int index, int val) { @@ -136,8 +145,8 @@ public class InputPointers { return mLength; } - public void reset() { - mArray = new int[DEFAULT_SIZE]; + public void reset(int capacity) { + mArray = new int[capacity]; mLength = 0; } diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java index 974af2584..bb39ce4f7 100644 --- a/java/src/com/android/inputmethod/latin/LastComposedWord.java +++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java @@ -45,7 +45,7 @@ public class LastComposedWord { public final String mCommittedWord; public final int mSeparatorCode; public final CharSequence mPrevWord; - public final InputPointers mInputPointers = new InputPointers(); + public final InputPointers mInputPointers = new InputPointers(BinaryDictionary.MAX_WORD_LENGTH); private boolean mActive; diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index ca9dbaf05..6d346d179 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -33,7 +33,7 @@ public class WordComposer { private static final int N = BinaryDictionary.MAX_WORD_LENGTH; private int[] mPrimaryKeyCodes; - private final InputPointers mInputPointers = new InputPointers(); + private final InputPointers mInputPointers = new InputPointers(N); private final StringBuilder mTypedWord; private CharSequence mAutoCorrection; private boolean mIsResumed; diff --git a/tests/src/com/android/inputmethod/latin/InputPointersTests.java b/tests/src/com/android/inputmethod/latin/InputPointersTests.java index 524921e25..0ab3cb970 100644 --- a/tests/src/com/android/inputmethod/latin/InputPointersTests.java +++ b/tests/src/com/android/inputmethod/latin/InputPointersTests.java @@ -19,8 +19,10 @@ package com.android.inputmethod.latin; import android.test.AndroidTestCase; public class InputPointersTests extends AndroidTestCase { + private static final int DEFAULT_CAPACITY = 48; + public void testNewInstance() { - final InputPointers src = new InputPointers(); + final InputPointers src = new InputPointers(DEFAULT_CAPACITY); assertEquals("newInstance size", 0, src.getPointerSize()); assertNotNull("new instance xCoordinates", src.getXCoordinates()); assertNotNull("new instance yCoordinates", src.getYCoordinates()); @@ -29,7 +31,7 @@ public class InputPointersTests extends AndroidTestCase { } public void testReset() { - final InputPointers src = new InputPointers(); + final InputPointers src = new InputPointers(DEFAULT_CAPACITY); final int[] xCoordinates = src.getXCoordinates(); final int[] yCoordinates = src.getXCoordinates(); final int[] pointerIds = src.getXCoordinates(); @@ -44,7 +46,7 @@ public class InputPointersTests extends AndroidTestCase { } public void testAdd() { - final InputPointers src = new InputPointers(); + final InputPointers src = new InputPointers(DEFAULT_CAPACITY); final int limit = src.getXCoordinates().length * 2 + 10; for (int i = 0; i < limit; i++) { src.addPointer(i, i * 2, i * 3, i * 4); @@ -59,7 +61,7 @@ public class InputPointersTests extends AndroidTestCase { } public void testAddAt() { - final InputPointers src = new InputPointers(); + final InputPointers src = new InputPointers(DEFAULT_CAPACITY); final int limit = 1000, step = 100; for (int i = 0; i < limit; i += step) { src.addPointer(i, i, i * 2, i * 3, i * 4); @@ -74,12 +76,12 @@ public class InputPointersTests extends AndroidTestCase { } public void testSet() { - final InputPointers src = new InputPointers(); + final InputPointers src = new InputPointers(DEFAULT_CAPACITY); final int limit = src.getXCoordinates().length * 2 + 10; for (int i = 0; i < limit; i++) { src.addPointer(i, i * 2, i * 3, i * 4); } - final InputPointers dst = new InputPointers(); + final InputPointers dst = new InputPointers(DEFAULT_CAPACITY); dst.set(src); assertEquals("after set size", dst.getPointerSize(), src.getPointerSize()); assertSame("after set xCoordinates", dst.getXCoordinates(), src.getXCoordinates()); @@ -89,12 +91,12 @@ public class InputPointersTests extends AndroidTestCase { } public void testCopy() { - final InputPointers src = new InputPointers(); + final InputPointers src = new InputPointers(DEFAULT_CAPACITY); final int limit = 100; for (int i = 0; i < limit; i++) { src.addPointer(i, i * 2, i * 3, i * 4); } - final InputPointers dst = new InputPointers(); + final InputPointers dst = new InputPointers(DEFAULT_CAPACITY); dst.copy(src); assertEquals("after copy size", dst.getPointerSize(), src.getPointerSize()); assertNotSame("after copy xCoordinates", dst.getXCoordinates(), src.getXCoordinates()); @@ -113,18 +115,18 @@ public class InputPointersTests extends AndroidTestCase { } public void testAppend() { - final InputPointers src = new InputPointers(); + final InputPointers src = new InputPointers(DEFAULT_CAPACITY); final int srcLen = 100; for (int i = 0; i < srcLen; i++) { src.addPointer(i, i * 2, i * 3, i * 4); } final int dstLen = 50; - final InputPointers dst = new InputPointers(); + final InputPointers dst = new InputPointers(DEFAULT_CAPACITY); for (int i = 0; i < dstLen; i++) { final int value = -i - 1; dst.addPointer(value * 4, value * 3, value * 2, value); } - final InputPointers dstCopy = new InputPointers(); + final InputPointers dstCopy = new InputPointers(DEFAULT_CAPACITY); dstCopy.copy(dst); dst.append(src, 0, 0); From 918e420d1becc1389b9895538eceff85fe882c99 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 19 Jul 2012 12:33:51 +0900 Subject: [PATCH 06/36] Gesture input should be turned off depending on the configuration The gesture input will be disabled when * It is AOSP build. * Accessibility mode is on. * The input field is password mode. Bug: 6844755 Bug: 6844763 Bug: 6845011 Change-Id: I74972cc765d15c08059e0c9014f863ffb2a57c6c --- java/res/values/gesture-input.xml | 22 ++++++++++++++++ .../keyboard/LatinKeyboardView.java | 8 ++++-- .../inputmethod/keyboard/PointerTracker.java | 26 ++++++++++++++++--- 3 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 java/res/values/gesture-input.xml diff --git a/java/res/values/gesture-input.xml b/java/res/values/gesture-input.xml new file mode 100644 index 000000000..235616fbe --- /dev/null +++ b/java/res/values/gesture-input.xml @@ -0,0 +1,22 @@ + + + + false + diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index 7714ba892..1eae2c1d4 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -341,10 +341,14 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke mHasDistinctMultitouch = context.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT); + final Resources res = getResources(); final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean( - Utils.getDeviceOverrideValue(context.getResources(), + Utils.getDeviceOverrideValue(res, R.array.phantom_sudden_move_event_device_list, "false")); - PointerTracker.init(mHasDistinctMultitouch, needsPhantomSuddenMoveEventHack); + final boolean gestureInputEnabledByBuildConfig = res.getBoolean( + R.bool.config_gesture_input_enabled_by_build_config); + PointerTracker.init(mHasDistinctMultitouch, needsPhantomSuddenMoveEventHack, + gestureInputEnabledByBuildConfig); final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.LatinKeyboardView, defStyle, R.style.LatinKeyboardView); diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index cc6789d13..c925227b9 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -22,6 +22,7 @@ import android.view.MotionEvent; import android.view.View; import android.widget.TextView; +import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.keyboard.internal.GestureStroke; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.latin.InputPointers; @@ -39,7 +40,8 @@ public class PointerTracker { private static boolean DEBUG_MODE = LatinImeLogger.sDBG; // TODO: There should be an option to turn on/off the gesture input. - private static final boolean GESTURE_ON = true; + private static boolean sIsGestureEnabled = true; + private static final int MIN_RECOGNITION_TIME = 100; // msec public interface KeyEventHandler { @@ -116,6 +118,7 @@ public class PointerTracker { private static LatinKeyboardView.PointerTrackerParams sParams; private static int sTouchNoiseThresholdDistanceSquared; private static boolean sNeedsPhantomSuddenMoveEventHack; + private static boolean sConfigGestureInputEnabledByBuildConfig; private static final ArrayList sTrackers = new ArrayList(); private static PointerTrackerQueue sPointerTrackerQueue; @@ -177,15 +180,18 @@ public class PointerTracker { private final GestureStroke mGestureStroke; public static void init(boolean hasDistinctMultitouch, - boolean needsPhantomSuddenMoveEventHack) { + boolean needsPhantomSuddenMoveEventHack, + boolean gestureInputEnabledByBuildConfig) { if (hasDistinctMultitouch) { sPointerTrackerQueue = new PointerTrackerQueue(); } else { sPointerTrackerQueue = null; } sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack; + sConfigGestureInputEnabledByBuildConfig = gestureInputEnabledByBuildConfig; setParameters(LatinKeyboardView.PointerTrackerParams.DEFAULT); + updateGestureInputEnabledState(null); } public static void setParameters(LatinKeyboardView.PointerTrackerParams params) { @@ -194,6 +200,16 @@ public class PointerTracker { params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance); } + private static void updateGestureInputEnabledState(Keyboard keyboard) { + if (!sConfigGestureInputEnabledByBuildConfig + || AccessibilityUtils.getInstance().isTouchExplorationEnabled() + || (keyboard != null && keyboard.mId.passwordInput())) { + sIsGestureEnabled = false; + } else { + sIsGestureEnabled = true; + } + } + public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) { final ArrayList trackers = sTrackers; @@ -222,6 +238,8 @@ public class PointerTracker { // Mark that keyboard layout has been changed. tracker.mKeyboardLayoutHasBeenChanged = true; } + final Keyboard keyboard = keyDetector.getKeyboard(); + updateGestureInputEnabledState(keyboard); } public static void dismissAllKeyPreviews() { @@ -611,7 +629,7 @@ public class PointerTracker { if (queue != null && queue.size() == 1) { mIsPossibleGesture = false; // A gesture should start only from the letter key. - if (GESTURE_ON && mIsAlphabetKeyboard && key != null + if (sIsGestureEnabled && mIsAlphabetKeyboard && key != null && Keyboard.isLetterCode(key.mCode)) { mIsPossibleGesture = true; mGestureStroke.addPoint(x, y, 0, false); @@ -654,7 +672,7 @@ public class PointerTracker { private void onGestureMoveEvent(PointerTracker tracker, int x, int y, long eventTime, boolean isHistorical, Key key) { final int gestureTime = (int)(eventTime - tracker.getDownTime()); - if (GESTURE_ON && mIsPossibleGesture) { + if (sIsGestureEnabled && mIsPossibleGesture) { final GestureStroke stroke = mGestureStroke; stroke.addPoint(x, y, gestureTime, isHistorical); if (!mInGesture && stroke.isStartOfAGesture(gestureTime)) { From eb2fe2ab101c80f6e3d23010385a68dd3e9688ef Mon Sep 17 00:00:00 2001 From: Tom Ouyang Date: Wed, 18 Jul 2012 17:08:20 +0900 Subject: [PATCH 07/36] Add wrapper for incremental decoder. Change-Id: Ie11e2b83c2602c0d5a2739a7d4f4994f80d7e298 --- native/jni/Android.mk | 3 +- .../gesture/incremental_decoder_wrapper.cpp | 22 +++++ .../src/gesture/incremental_decoder_wrapper.h | 92 +++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 native/jni/src/gesture/incremental_decoder_wrapper.cpp create mode 100644 native/jni/src/gesture/incremental_decoder_wrapper.h diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 54f61d90d..06ed5718e 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -49,7 +49,8 @@ LATIN_IME_CORE_SRC_FILES := \ proximity_info.cpp \ proximity_info_state.cpp \ unigram_dictionary.cpp \ - gesture/gesture_decoder_wrapper.cpp + gesture/gesture_decoder_wrapper.cpp \ + gesture/incremental_decoder_wrapper.cpp LOCAL_SRC_FILES := \ $(LATIN_IME_JNI_SRC_FILES) \ diff --git a/native/jni/src/gesture/incremental_decoder_wrapper.cpp b/native/jni/src/gesture/incremental_decoder_wrapper.cpp new file mode 100644 index 000000000..8fcda6c9e --- /dev/null +++ b/native/jni/src/gesture/incremental_decoder_wrapper.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 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. + */ + +#include "incremental_decoder_wrapper.h" + +namespace latinime { + IncrementalDecoderInterface * + (*IncrementalDecoderWrapper::sIncrementalDecoderFactoryMethod)(int, int) = 0; +} // namespace latinime diff --git a/native/jni/src/gesture/incremental_decoder_wrapper.h b/native/jni/src/gesture/incremental_decoder_wrapper.h new file mode 100644 index 000000000..698061548 --- /dev/null +++ b/native/jni/src/gesture/incremental_decoder_wrapper.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2012 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. + */ + +#ifndef LATINIME_INCREMENTAL_DECODER_WRAPPER_H +#define LATINIME_INCREMENTAL_DECODER_WRAPPER_H + +#include +#include "defines.h" +#include "incremental_decoder_interface.h" + +namespace latinime { + +class UnigramDictionary; +class BigramDictionary; +class ProximityInfo; + +class IncrementalDecoderWrapper : public IncrementalDecoderInterface { + public: + IncrementalDecoderWrapper(const int maxWordLength, const int maxWords) { + mIncrementalDecoderInterface = getIncrementalDecoderInstance(maxWordLength, maxWords); + } + + virtual ~IncrementalDecoderWrapper() { + delete mIncrementalDecoderInterface; + } + + int getSuggestions(ProximityInfo *pInfo, int *inputXs, int *inputYs, int *times, + int *pointerIds, int *codes, int inputSize, int commitPoint, + unsigned short *outWords, int *frequencies, int *outputIndices, int *outputTypes) { + if (!mIncrementalDecoderInterface) { + return 0; + } + return mIncrementalDecoderInterface->getSuggestions( + pInfo, inputXs, inputYs, times, pointerIds, codes, inputSize, commitPoint, + outWords, frequencies, outputIndices, outputTypes); + } + + void reset() { + if (!mIncrementalDecoderInterface) { + return; + } + mIncrementalDecoderInterface->reset(); + } + + void setDict(const UnigramDictionary *dict, const BigramDictionary *bigram, + const uint8_t *dictRoot, int rootPos) { + if (!mIncrementalDecoderInterface) { + return; + } + mIncrementalDecoderInterface->setDict(dict, bigram, dictRoot, rootPos); + } + + void setPrevWord(const int32_t *prevWord, int prevWordLength) { + if (!mIncrementalDecoderInterface) { + return; + } + mIncrementalDecoderInterface->setPrevWord(prevWord, prevWordLength); + } + + static void setIncrementalDecoderFactoryMethod( + IncrementalDecoderInterface *(*factoryMethod)(int, int)) { + sIncrementalDecoderFactoryMethod = factoryMethod; + } + + private: + DISALLOW_COPY_AND_ASSIGN(IncrementalDecoderWrapper); + static IncrementalDecoderInterface *getIncrementalDecoderInstance(int maxWordLength, + int maxWords) { + if (sIncrementalDecoderFactoryMethod) { + return sIncrementalDecoderFactoryMethod(maxWordLength, maxWords); + } + return 0; + } + + static IncrementalDecoderInterface *(*sIncrementalDecoderFactoryMethod)(int, int); + IncrementalDecoderInterface *mIncrementalDecoderInterface; +}; +} // namespace latinime +#endif // LATINIME_INCREMENTAL_DECODER_WRAPPER_H From 1e6f39a9f994e21b749a1cbae55a3adbfb5640e9 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 19 Jul 2012 14:47:55 +0900 Subject: [PATCH 08/36] Tune the gesture detection logic a bit Change-Id: Ia8e8c15fdbbd49768d57cafd50325e7e45af6251 --- .../inputmethod/keyboard/PointerTracker.java | 28 +++++++++---- .../keyboard/internal/GestureStroke.java | 42 +++++++++++-------- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index c925227b9..ea4d93a4a 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -42,7 +42,7 @@ public class PointerTracker { // TODO: There should be an option to turn on/off the gesture input. private static boolean sIsGestureEnabled = true; - private static final int MIN_RECOGNITION_TIME = 100; // msec + private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec public interface KeyEventHandler { /** @@ -122,6 +122,10 @@ public class PointerTracker { private static final ArrayList sTrackers = new ArrayList(); private static PointerTrackerQueue sPointerTrackerQueue; + // HACK: Change gesture detection criteria depending on this variable. + // TODO: Find more comprehensive ways to detect a gesture start. + // True when the previous user input was a gesture input, not a typing input. + private static boolean sWasInGesture; public final int mPointerId; @@ -138,6 +142,7 @@ public class PointerTracker { private boolean mIsPossibleGesture = false; private boolean mInGesture = false; + // TODO: Remove these variables private int mLastRecognitionPointSize = 0; private long mLastRecognitionTime = 0; @@ -273,7 +278,7 @@ public class PointerTracker { // TODO: To handle multi-touch gestures we may want to move this method to // {@link PointerTrackerQueue}. - public static void clearBatchInputPoints() { + public static void clearBatchInputPointsOfAllPointerTrackers() { for (final PointerTracker tracker : sTrackers) { tracker.mGestureStroke.reset(); } @@ -550,18 +555,26 @@ public class PointerTracker { Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize()); } mListener.onEndBatchInput(batchPoints); - mInGesture = false; - clearBatchInputPoints(); + clearBatchInputRecognitionStateOfThisPointerTracker(); + clearBatchInputPointsOfAllPointerTrackers(); + sWasInGesture = true; } private void abortBatchInput() { + clearBatchInputRecognitionStateOfThisPointerTracker(); + clearBatchInputPointsOfAllPointerTrackers(); + } + + private void clearBatchInputRecognitionStateOfThisPointerTracker() { mIsPossibleGesture = false; mInGesture = false; + mLastRecognitionPointSize = 0; + mLastRecognitionTime = 0; } private boolean updateBatchInputRecognitionState(long eventTime, int size) { if (size > mLastRecognitionPointSize - && eventTime > mLastRecognitionTime + MIN_RECOGNITION_TIME) { + && eventTime > mLastRecognitionTime + MIN_GESTURE_RECOGNITION_TIME) { mLastRecognitionPointSize = size; mLastRecognitionTime = eventTime; return true; @@ -675,7 +688,7 @@ public class PointerTracker { if (sIsGestureEnabled && mIsPossibleGesture) { final GestureStroke stroke = mGestureStroke; stroke.addPoint(x, y, gestureTime, isHistorical); - if (!mInGesture && stroke.isStartOfAGesture(gestureTime)) { + if (!mInGesture && stroke.isStartOfAGesture(gestureTime, sWasInGesture)) { startBatchInput(); } } @@ -865,10 +878,10 @@ public class PointerTracker { } public void onShowMoreKeysPanel(int x, int y, KeyEventHandler handler) { + abortBatchInput(); onLongPressed(); onDownEvent(x, y, SystemClock.uptimeMillis(), handler); mIsShowingMoreKeysPanel = true; - abortBatchInput(); } public void onLongPressed() { @@ -947,6 +960,7 @@ public class PointerTracker { int code = key.mCode; callListenerOnCodeInput(key, code, x, y); callListenerOnRelease(key, code, false); + sWasInGesture = false; } private void printTouchEvent(String title, int x, int y, long eventTime) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 8740e6972..6f392f145 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -26,19 +26,21 @@ public class GestureStroke { private final InputPointers mInputPointers = new InputPointers(DEFAULT_CAPACITY); private float mLength; private float mAngle; - private int mIncrementalRecognitionPoint; - private boolean mHasSharpCorner; + private int mIncrementalRecognitionSize; private long mLastPointTime; private int mLastPointX; private int mLastPointY; private int mMinGestureLength; + private int mMinGestureLengthWhileInGesture; private int mMinGestureSampleLength; - // TODO: Tune these parameters. - private static final float MIN_GESTURE_DETECTION_RATIO_TO_KEY_WIDTH = 1.0f / 4.0f; + // TODO: Move some of these to resource. + private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 1.0f; + private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH_WHILE_IN_GESTURE = 0.5f; + private static final int MIN_GESTURE_DURATION = 150; // msec + private static final int MIN_GESTURE_DURATION_WHILE_IN_GESTURE = 75; // msec private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT = 1.0f / 6.0f; - private static final int MIN_GESTURE_DURATION = 100; // msec private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f; // dip/msec private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float)(Math.PI / 4.0f); @@ -50,19 +52,27 @@ public class GestureStroke { } public void setGestureSampleLength(final int keyWidth, final int keyHeight) { - mMinGestureLength = (int)(keyWidth * MIN_GESTURE_DETECTION_RATIO_TO_KEY_WIDTH); + // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key? + mMinGestureLength = (int)(keyWidth * MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH); + mMinGestureLengthWhileInGesture = (int)( + keyWidth * MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH_WHILE_IN_GESTURE); mMinGestureSampleLength = (int)(keyHeight * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT); } - public boolean isStartOfAGesture(int downDuration) { + public boolean isStartOfAGesture(final int downDuration, final boolean wasInGesture) { + // The tolerance of the time duration and the stroke length to detect the start of a + // gesture stroke should be eased when the previous input was a gesture input. + if (wasInGesture) { + return downDuration > MIN_GESTURE_DURATION_WHILE_IN_GESTURE + && mLength > mMinGestureLengthWhileInGesture; + } return downDuration > MIN_GESTURE_DURATION && mLength > mMinGestureLength; } public void reset() { mLength = 0; mAngle = 0; - mIncrementalRecognitionPoint = 0; - mHasSharpCorner = false; + mIncrementalRecognitionSize = 0; mLastPointTime = 0; mInputPointers.reset(); } @@ -93,15 +103,11 @@ public class GestureStroke { mLength += dist; final float angle = getAngle(lastX, lastY, x, y); if (size > 1) { - float curvature = getAngleDiff(angle, mAngle); + final float curvature = getAngleDiff(angle, mAngle); if (curvature > GESTURE_RECOG_CURVATURE_THRESHOLD) { - if (size > mIncrementalRecognitionPoint) { - mIncrementalRecognitionPoint = size; + if (size > mIncrementalRecognitionSize) { + mIncrementalRecognitionSize = size; } - mHasSharpCorner = true; - } - if (!mHasSharpCorner) { - mIncrementalRecognitionPoint = size; } } mAngle = angle; @@ -112,7 +118,7 @@ public class GestureStroke { if (mLastPointTime != 0 && duration > 0) { final float speed = getDistance(mLastPointX, mLastPointY, x, y) / duration; if (speed < GESTURE_RECOG_SPEED_THRESHOLD) { - mIncrementalRecognitionPoint = size; + mIncrementalRecognitionSize = size; } } updateLastPoint(x, y, time); @@ -124,7 +130,7 @@ public class GestureStroke { } public void appendIncrementalBatchPoints(final InputPointers out) { - out.append(mInputPointers, 0, mIncrementalRecognitionPoint); + out.append(mInputPointers, 0, mIncrementalRecognitionSize); } private static float getDistance(final int p1x, final int p1y, From f5830988878e8b8fba1916fa4c8e8c2d589f2500 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 19 Jul 2012 19:44:54 +0900 Subject: [PATCH 09/36] Update InputPointers unit test comments --- .../inputmethod/latin/InputPointersTests.java | 98 +++++++++---------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/tests/src/com/android/inputmethod/latin/InputPointersTests.java b/tests/src/com/android/inputmethod/latin/InputPointersTests.java index 0ab3cb970..6f04f3ebb 100644 --- a/tests/src/com/android/inputmethod/latin/InputPointersTests.java +++ b/tests/src/com/android/inputmethod/latin/InputPointersTests.java @@ -23,7 +23,7 @@ public class InputPointersTests extends AndroidTestCase { public void testNewInstance() { final InputPointers src = new InputPointers(DEFAULT_CAPACITY); - assertEquals("newInstance size", 0, src.getPointerSize()); + assertEquals("new instance size", 0, src.getPointerSize()); assertNotNull("new instance xCoordinates", src.getXCoordinates()); assertNotNull("new instance yCoordinates", src.getYCoordinates()); assertNotNull("new instance pointerIds", src.getPointerIds()); @@ -38,11 +38,11 @@ public class InputPointersTests extends AndroidTestCase { final int[] times = src.getXCoordinates(); src.reset(); - assertEquals("after reset size", 0, src.getPointerSize()); - assertNotSame("after reset xCoordinates", xCoordinates, src.getXCoordinates()); - assertNotSame("after reset yCoordinates", yCoordinates, src.getYCoordinates()); - assertNotSame("after reset pointerIds", pointerIds, src.getPointerIds()); - assertNotSame("after reset times", times, src.getTimes()); + assertEquals("size after reset", 0, src.getPointerSize()); + assertNotSame("xCoordinates after reset", xCoordinates, src.getXCoordinates()); + assertNotSame("yCoordinates after reset", yCoordinates, src.getYCoordinates()); + assertNotSame("pointerIds after reset", pointerIds, src.getPointerIds()); + assertNotSame("times after reset", times, src.getTimes()); } public void testAdd() { @@ -50,7 +50,7 @@ public class InputPointersTests extends AndroidTestCase { final int limit = src.getXCoordinates().length * 2 + 10; for (int i = 0; i < limit; i++) { src.addPointer(i, i * 2, i * 3, i * 4); - assertEquals("after add " + i, i + 1, src.getPointerSize()); + assertEquals("size after add " + i, i + 1, src.getPointerSize()); } for (int i = 0; i < limit; i++) { assertEquals("xCoordinates at " + i, i, src.getXCoordinates()[i]); @@ -65,7 +65,7 @@ public class InputPointersTests extends AndroidTestCase { final int limit = 1000, step = 100; for (int i = 0; i < limit; i += step) { src.addPointer(i, i, i * 2, i * 3, i * 4); - assertEquals("after add at " + i, i + 1, src.getPointerSize()); + assertEquals("size after add at " + i, i + 1, src.getPointerSize()); } for (int i = 0; i < limit; i += step) { assertEquals("xCoordinates at " + i, i, src.getXCoordinates()[i]); @@ -83,11 +83,11 @@ public class InputPointersTests extends AndroidTestCase { } final InputPointers dst = new InputPointers(DEFAULT_CAPACITY); dst.set(src); - assertEquals("after set size", dst.getPointerSize(), src.getPointerSize()); - assertSame("after set xCoordinates", dst.getXCoordinates(), src.getXCoordinates()); - assertSame("after set yCoordinates", dst.getYCoordinates(), src.getYCoordinates()); - assertSame("after set pointerIds", dst.getPointerIds(), src.getPointerIds()); - assertSame("after set times", dst.getTimes(), src.getTimes()); + assertEquals("size after set", dst.getPointerSize(), src.getPointerSize()); + assertSame("xCoordinates after set", dst.getXCoordinates(), src.getXCoordinates()); + assertSame("yCoordinates after set", dst.getYCoordinates(), src.getYCoordinates()); + assertSame("pointerIds after set", dst.getPointerIds(), src.getPointerIds()); + assertSame("times after set", dst.getTimes(), src.getTimes()); } public void testCopy() { @@ -98,19 +98,19 @@ public class InputPointersTests extends AndroidTestCase { } final InputPointers dst = new InputPointers(DEFAULT_CAPACITY); dst.copy(src); - assertEquals("after copy size", dst.getPointerSize(), src.getPointerSize()); - assertNotSame("after copy xCoordinates", dst.getXCoordinates(), src.getXCoordinates()); - assertNotSame("after copy yCoordinates", dst.getYCoordinates(), src.getYCoordinates()); - assertNotSame("after copy pointerIds", dst.getPointerIds(), src.getPointerIds()); - assertNotSame("after copy times", dst.getTimes(), src.getTimes()); + assertEquals("size after copy", dst.getPointerSize(), src.getPointerSize()); + assertNotSame("xCoordinates after copy", dst.getXCoordinates(), src.getXCoordinates()); + assertNotSame("yCoordinates after copy", dst.getYCoordinates(), src.getYCoordinates()); + assertNotSame("pointerIds after copy", dst.getPointerIds(), src.getPointerIds()); + assertNotSame("times after copy", dst.getTimes(), src.getTimes()); final int size = dst.getPointerSize(); - assertArrayEquals("after copy xCoordinates values", + assertArrayEquals("xCoordinates values after copy", dst.getXCoordinates(), 0, src.getXCoordinates(), 0, size); - assertArrayEquals("after copy yCoordinates values", + assertArrayEquals("yCoordinates values after copy", dst.getYCoordinates(), 0, src.getYCoordinates(), 0, size); - assertArrayEquals("after copy pointerIds values", + assertArrayEquals("pointerIds values after copy", dst.getPointerIds(), 0, src.getPointerIds(), 0, size); - assertArrayEquals("after copy times values", + assertArrayEquals("times values after copy", dst.getTimes(), 0, src.getTimes(), 0, size); } @@ -130,36 +130,36 @@ public class InputPointersTests extends AndroidTestCase { dstCopy.copy(dst); dst.append(src, 0, 0); - assertEquals("after append zero size", dstLen, dst.getPointerSize()); - assertArrayEquals("after append zero xCoordinates", dstCopy.getXCoordinates(), 0, - dst.getXCoordinates(), 0, dstLen); - assertArrayEquals("after append zero yCoordinates", dstCopy.getYCoordinates(), 0, - dst.getYCoordinates(), 0, dstLen); - assertArrayEquals("after append zero pointerIds", dstCopy.getPointerIds(), 0, - dst.getPointerIds(), 0, dstLen); - assertArrayEquals("after append zero times", dstCopy.getTimes(), 0, - dst.getTimes(), 0, dstLen); + assertEquals("size after append zero", dstLen, dst.getPointerSize()); + assertArrayEquals("xCoordinates after append zero", + dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLen); + assertArrayEquals("yCoordinates after append zero", + dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLen); + assertArrayEquals("pointerIds after append zero", + dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLen); + assertArrayEquals("times after append zero", + dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLen); dst.append(src, 0, srcLen); - assertEquals("after append size", dstLen + srcLen, dst.getPointerSize()); - assertTrue("after append size primitive length", + assertEquals("size after append", dstLen + srcLen, dst.getPointerSize()); + assertTrue("primitive length after append", dst.getPointerIds().length >= dstLen + srcLen); - assertArrayEquals("after append xCoordinates", dstCopy.getXCoordinates(), 0, - dst.getXCoordinates(), 0, dstLen); - assertArrayEquals("after append yCoordinates", dstCopy.getYCoordinates(), 0, - dst.getYCoordinates(), 0, dstLen); - assertArrayEquals("after append pointerIds", dstCopy.getPointerIds(), 0, - dst.getPointerIds(), 0, dstLen); - assertArrayEquals("after append times", dstCopy.getTimes(), 0, - dst.getTimes(), 0, dstLen); - assertArrayEquals("after append xCoordinates", dst.getXCoordinates(), dstLen, - src.getXCoordinates(), 0, srcLen); - assertArrayEquals("after append yCoordinates", dst.getYCoordinates(), dstLen, - src.getYCoordinates(), 0, srcLen); - assertArrayEquals("after append pointerIds", dst.getPointerIds(), dstLen, - src.getPointerIds(), 0, srcLen); - assertArrayEquals("after append times", dst.getTimes(), dstLen, - src.getTimes(), 0, srcLen); + assertArrayEquals("original xCoordinates values after append", + dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLen); + assertArrayEquals("original yCoordinates values after append", + dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLen); + assertArrayEquals("original pointerIds values after append", + dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLen); + assertArrayEquals("original times values after append", + dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLen); + assertArrayEquals("appended xCoordinates values after append", + src.getXCoordinates(), 0, dst.getXCoordinates(), dstLen, srcLen); + assertArrayEquals("appended yCoordinates values after append", + src.getYCoordinates(), 0, dst.getYCoordinates(), dstLen, srcLen); + assertArrayEquals("appended pointerIds values after append", + src.getPointerIds(), 0, dst.getPointerIds(), dstLen, srcLen); + assertArrayEquals("appended times values after append", + src.getTimes(), 0, dst.getTimes(), dstLen, srcLen); } private static void assertArrayEquals(String message, int[] expecteds, int expectedPos, From 9370ab9adad3b4bc3af8bde52b6422b8d2b873e7 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 19 Jul 2012 19:45:20 +0900 Subject: [PATCH 10/36] Make ScalableIntArray public as ResizableIntArray Change-Id: Ibbbc117214912ffa192c694bde5b7d55154f40c4 --- .../inputmethod/latin/InputPointers.java | 86 ++--------- .../inputmethod/latin/ResizableIntArray.java | 95 +++++++++++++ .../latin/ResizableIntArrayTests.java | 133 ++++++++++++++++++ 3 files changed, 236 insertions(+), 78 deletions(-) create mode 100644 java/src/com/android/inputmethod/latin/ResizableIntArray.java create mode 100644 tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java index febabadf7..2bccdee48 100644 --- a/java/src/com/android/inputmethod/latin/InputPointers.java +++ b/java/src/com/android/inputmethod/latin/InputPointers.java @@ -16,22 +16,20 @@ package com.android.inputmethod.latin; -import java.util.Arrays; - // TODO: This class is not thread-safe. public class InputPointers { private final int mDefaultCapacity; - private final ScalableIntArray mXCoordinates; - private final ScalableIntArray mYCoordinates; - private final ScalableIntArray mPointerIds; - private final ScalableIntArray mTimes; + private final ResizableIntArray mXCoordinates; + private final ResizableIntArray mYCoordinates; + private final ResizableIntArray mPointerIds; + private final ResizableIntArray mTimes; public InputPointers(int defaultCapacity) { mDefaultCapacity = defaultCapacity; - mXCoordinates = new ScalableIntArray(defaultCapacity); - mYCoordinates = new ScalableIntArray(defaultCapacity); - mPointerIds = new ScalableIntArray(defaultCapacity); - mTimes = new ScalableIntArray(defaultCapacity); + mXCoordinates = new ResizableIntArray(defaultCapacity); + mYCoordinates = new ResizableIntArray(defaultCapacity); + mPointerIds = new ResizableIntArray(defaultCapacity); + mTimes = new ResizableIntArray(defaultCapacity); } public void addPointer(int index, int x, int y, int pointerId, int time) { @@ -105,72 +103,4 @@ public class InputPointers { public int[] getTimes() { return mTimes.getPrimitiveArray(); } - - private static class ScalableIntArray { - private int[] mArray; - private int mLength; - - public ScalableIntArray(int capacity) { - reset(capacity); - } - - public void add(int index, int val) { - if (mLength < index + 1) { - mLength = index; - add(val); - } else { - mArray[index] = val; - } - } - - public void add(int val) { - final int nextLength = mLength + 1; - ensureCapacity(nextLength); - mArray[mLength] = val; - mLength = nextLength; - } - - private void ensureCapacity(int minimumCapacity) { - if (mArray.length < minimumCapacity) { - final int nextCapacity = mArray.length * 2; - // The following is the same as newLength = Math.max(minimumCapacity, nextCapacity); - final int newLength = minimumCapacity > nextCapacity - ? minimumCapacity - : nextCapacity; - mArray = Arrays.copyOf(mArray, newLength); - } - } - - public int getLength() { - return mLength; - } - - public void reset(int capacity) { - mArray = new int[capacity]; - mLength = 0; - } - - public int[] getPrimitiveArray() { - return mArray; - } - - public void set(ScalableIntArray ip) { - mArray = ip.mArray; - mLength = ip.mLength; - } - - public void copy(ScalableIntArray ip) { - ensureCapacity(ip.mLength); - System.arraycopy(ip.mArray, 0, mArray, 0, ip.mLength); - mLength = ip.mLength; - } - - public void append(ScalableIntArray src, int startPos, int length) { - final int currentLength = mLength; - final int newLength = currentLength + length; - ensureCapacity(newLength); - System.arraycopy(src.mArray, startPos, mArray, currentLength, length); - mLength = newLength; - } - } } diff --git a/java/src/com/android/inputmethod/latin/ResizableIntArray.java b/java/src/com/android/inputmethod/latin/ResizableIntArray.java new file mode 100644 index 000000000..2079c0e99 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/ResizableIntArray.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2012 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. + */ + +package com.android.inputmethod.latin; + +import java.util.Arrays; + +// TODO: This class is not thread-safe. +public class ResizableIntArray { + private int[] mArray; + private int mLength; + + public ResizableIntArray(int capacity) { + reset(capacity); + } + + public void add(int index, int val) { + if (mLength < index + 1) { + mLength = index; + add(val); + } else { + mArray[index] = val; + } + } + + public void add(int val) { + final int nextLength = mLength + 1; + ensureCapacity(nextLength); + mArray[mLength] = val; + mLength = nextLength; + } + + private void ensureCapacity(int minimumCapacity) { + if (mArray.length < minimumCapacity) { + final int nextCapacity = mArray.length * 2; + // The following is the same as newLength = + // Math.max(minimumCapacity, nextCapacity); + final int newLength = minimumCapacity > nextCapacity + ? minimumCapacity + : nextCapacity; + // TODO: Implement primitive array pool. + mArray = Arrays.copyOf(mArray, newLength); + } + } + + public int getLength() { + return mLength; + } + + // TODO: Implement setLength(int). + + public void reset(int capacity) { + // TODO: Implement primitive array pool. + mArray = new int[capacity]; + mLength = 0; + } + + public int[] getPrimitiveArray() { + return mArray; + } + + public void set(ResizableIntArray ip) { + // TODO: Implement primitive array pool. + mArray = ip.mArray; + mLength = ip.mLength; + } + + public void copy(ResizableIntArray ip) { + // TODO: Avoid useless coping of values. + ensureCapacity(ip.mLength); + System.arraycopy(ip.mArray, 0, mArray, 0, ip.mLength); + mLength = ip.mLength; + } + + public void append(ResizableIntArray src, int startPos, int length) { + final int currentLength = mLength; + final int newLength = currentLength + length; + ensureCapacity(newLength); + System.arraycopy(src.mArray, startPos, mArray, currentLength, length); + mLength = newLength; + } +} diff --git a/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java b/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java new file mode 100644 index 000000000..8b869b6ca --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2012 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. + */ + +package com.android.inputmethod.latin; + +import android.test.AndroidTestCase; + +public class ResizableIntArrayTests extends AndroidTestCase { + private static final int DEFAULT_CAPACITY = 48; + + public void testNewInstance() { + final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); + assertEquals("new instance length", 0, src.getLength()); + assertNotNull("new instance array", src.getPrimitiveArray()); + } + + public void testReset() { + final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); + final int[] array = src.getPrimitiveArray(); + + src.reset(DEFAULT_CAPACITY); + assertEquals("length after reset", 0, src.getLength()); + assertNotSame("array after reset", array, src.getPrimitiveArray()); + } + + public void testAdd() { + final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); + final int limit = src.getPrimitiveArray().length * 2 + 10; + for (int i = 0; i < limit; i++) { + src.add(i); + assertEquals("length after add " + i, i + 1, src.getLength()); + } + for (int i = 0; i < limit; i++) { + assertEquals("value at " + i, i, src.getPrimitiveArray()[i]); + } + } + + public void testAddAt() { + final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); + final int limit = 1000, step = 100; + for (int i = 0; i < limit; i += step) { + src.add(i, i); + assertEquals("length after add at " + i, i + 1, src.getLength()); + } + for (int i = 0; i < limit; i += step) { + assertEquals("value at " + i, i, src.getPrimitiveArray()[i]); + } + } + + public void testSet() { + final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); + final int limit = src.getPrimitiveArray().length * 2 + 10; + for (int i = 0; i < limit; i++) { + src.add(i); + } + final ResizableIntArray dst = new ResizableIntArray(DEFAULT_CAPACITY); + dst.set(src); + assertEquals("length after set", dst.getLength(), src.getLength()); + assertSame("array after set", dst.getPrimitiveArray(), src.getPrimitiveArray()); + } + + public void testCopy() { + final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); + final int limit = 100; + for (int i = 0; i < limit; i++) { + src.add(i); + } + final ResizableIntArray dst = new ResizableIntArray(DEFAULT_CAPACITY); + dst.copy(src); + assertEquals("length after copy", dst.getLength(), src.getLength()); + assertNotSame("array after copy", dst.getPrimitiveArray(), src.getPrimitiveArray()); + final int length = dst.getLength(); + assertArrayEquals("values after copy", + dst.getPrimitiveArray(), 0, src.getPrimitiveArray(), 0, length); + } + + public void testAppend() { + final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); + final int srcLen = 100; + for (int i = 0; i < srcLen; i++) { + src.add(i); + } + final int dstLen = 50; + final ResizableIntArray dst = new ResizableIntArray(DEFAULT_CAPACITY); + for (int i = 0; i < dstLen; i++) { + final int value = -i - 1; + dst.add(value); + } + final ResizableIntArray dstCopy = new ResizableIntArray(dst.getLength()); + dstCopy.copy(dst); + + dst.append(src, 0, 0); + assertEquals("length after append zero", dstLen, dst.getLength()); + assertArrayEquals("values after append zero", + dstCopy.getPrimitiveArray(), 0, dst.getPrimitiveArray(), 0, dstLen); + + dst.append(src, 0, srcLen); + assertEquals("length after append", dstLen + srcLen, dst.getLength()); + assertTrue("primitive length after append", + dst.getPrimitiveArray().length >= dstLen + srcLen); + assertArrayEquals("original values after append", + dstCopy.getPrimitiveArray(), 0, dst.getPrimitiveArray(), 0, dstLen); + assertArrayEquals("appended values after append", + src.getPrimitiveArray(), 0, dst.getPrimitiveArray(), dstLen, srcLen); + } + + private static void assertArrayEquals(String message, int[] expecteds, int expectedPos, + int[] actuals, int actualPos, int length) { + if (expecteds == null && actuals == null) { + return; + } + if (expecteds == null || actuals == null) { + fail(message + ": expecteds=" + expecteds + " actuals=" + actuals); + } + for (int i = 0; i < length; i++) { + assertEquals(message + ": element at " + i, + expecteds[i + expectedPos], actuals[i + actualPos]); + } + } +} From 0df487678eca58bd4732cfd2b6fd03b3c712eb48 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 18 Jul 2012 13:52:41 -0700 Subject: [PATCH 11/36] ResearchLogger: make logging more reliable (esp on startup) Bug: 6188932 Change-Id: I692e427ba2e6da7bb15f48208304c4a034392a22 --- java/res/values/strings.xml | 7 +- .../android/inputmethod/latin/LatinIME.java | 3 +- .../inputmethod/latin/ResearchLog.java | 48 ++-- .../inputmethod/latin/ResearchLogger.java | 243 ++++++++++++------ .../android/inputmethod/latin/Settings.java | 3 +- 5 files changed, 197 insertions(+), 107 deletions(-) diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 7272cfe97..cf7c1c5bd 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -223,15 +223,15 @@ Recorded timestamp - Do not log this session + Suspend logging - Enable session logging + Enable logging Log whole session history Deleting session log - Session log deleted + Logging temporarily suspended. To disable permanently, go to Android Keyboard Settings Session log NOT deleted @@ -240,7 +240,6 @@ Error: Session history NOT logged Session logging enabled - Input languages diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 0f8b6c48c..63aed7f58 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -618,7 +618,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0)); } if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().start(); ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, mPrefs); } if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) { @@ -711,7 +710,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen LatinImeLogger.commit(); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().stop(); + ResearchLogger.getInstance().latinIME_onFinishInputInternal(); } KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); diff --git a/java/src/com/android/inputmethod/latin/ResearchLog.java b/java/src/com/android/inputmethod/latin/ResearchLog.java index 1de5cb36a..917ee84a2 100644 --- a/java/src/com/android/inputmethod/latin/ResearchLog.java +++ b/java/src/com/android/inputmethod/latin/ResearchLog.java @@ -55,13 +55,14 @@ public class ResearchLog { final ScheduledExecutorService mExecutor; /* package */ final File mFile; - private JsonWriter mJsonWriter = NULL_JSON_WRITER; // should never be null + private JsonWriter mJsonWriter = NULL_JSON_WRITER; private int mLoggingState; private static final int LOGGING_STATE_UNSTARTED = 0; - private static final int LOGGING_STATE_RUNNING = 1; - private static final int LOGGING_STATE_STOPPING = 2; - private static final int LOGGING_STATE_STOPPED = 3; + private static final int LOGGING_STATE_READY = 1; // don't create file until necessary + private static final int LOGGING_STATE_RUNNING = 2; + private static final int LOGGING_STATE_STOPPING = 3; + private static final int LOGGING_STATE_STOPPED = 4; private static final long FLUSH_DELAY_IN_MS = 1000 * 5; private static class NullOutputStream extends OutputStream { @@ -94,11 +95,9 @@ public class ResearchLog { public synchronized void start() throws IOException { switch (mLoggingState) { case LOGGING_STATE_UNSTARTED: - mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile))); - mJsonWriter.setLenient(true); - mJsonWriter.beginArray(); - mLoggingState = LOGGING_STATE_RUNNING; + mLoggingState = LOGGING_STATE_READY; break; + case LOGGING_STATE_READY: case LOGGING_STATE_RUNNING: case LOGGING_STATE_STOPPING: case LOGGING_STATE_STOPPED: @@ -111,6 +110,7 @@ public class ResearchLog { case LOGGING_STATE_UNSTARTED: mLoggingState = LOGGING_STATE_STOPPED; break; + case LOGGING_STATE_READY: case LOGGING_STATE_RUNNING: mExecutor.submit(new Callable() { @Override @@ -120,14 +120,13 @@ public class ResearchLog { mJsonWriter.flush(); mJsonWriter.close(); } finally { - // the contentprovider only exports data if the writable - // bit is cleared. boolean success = mFile.setWritable(false, false); mLoggingState = LOGGING_STATE_STOPPED; } return null; } }); + removeAnyScheduledFlush(); mExecutor.shutdown(); mLoggingState = LOGGING_STATE_STOPPING; break; @@ -139,27 +138,26 @@ public class ResearchLog { public boolean isAlive() { switch (mLoggingState) { case LOGGING_STATE_UNSTARTED: + case LOGGING_STATE_READY: case LOGGING_STATE_RUNNING: return true; } return false; } - public void waitUntilStopped(int timeoutInMs) throws InterruptedException { + public void waitUntilStopped(final int timeoutInMs) throws InterruptedException { + removeAnyScheduledFlush(); + mExecutor.shutdown(); mExecutor.awaitTermination(timeoutInMs, TimeUnit.MILLISECONDS); } - private boolean isAbortSuccessful; - public boolean isAbortSuccessful() { - return isAbortSuccessful; - } - public synchronized void abort() { switch (mLoggingState) { case LOGGING_STATE_UNSTARTED: mLoggingState = LOGGING_STATE_STOPPED; isAbortSuccessful = true; break; + case LOGGING_STATE_READY: case LOGGING_STATE_RUNNING: mExecutor.submit(new Callable() { @Override @@ -173,6 +171,7 @@ public class ResearchLog { return null; } }); + removeAnyScheduledFlush(); mExecutor.shutdown(); mLoggingState = LOGGING_STATE_STOPPING; break; @@ -181,10 +180,16 @@ public class ResearchLog { } } + private boolean isAbortSuccessful; + public boolean isAbortSuccessful() { + return isAbortSuccessful; + } + /* package */ synchronized void flush() { switch (mLoggingState) { case LOGGING_STATE_UNSTARTED: break; + case LOGGING_STATE_READY: case LOGGING_STATE_RUNNING: removeAnyScheduledFlush(); mExecutor.submit(mFlushCallable); @@ -197,7 +202,9 @@ public class ResearchLog { private Callable mFlushCallable = new Callable() { @Override public Object call() throws Exception { - mJsonWriter.flush(); + if (mLoggingState == LOGGING_STATE_RUNNING) { + mJsonWriter.flush(); + } return null; } }; @@ -220,6 +227,7 @@ public class ResearchLog { switch (mLoggingState) { case LOGGING_STATE_UNSTARTED: break; + case LOGGING_STATE_READY: case LOGGING_STATE_RUNNING: mExecutor.submit(new Callable() { @Override @@ -239,6 +247,7 @@ public class ResearchLog { switch (mLoggingState) { case LOGGING_STATE_UNSTARTED: break; + case LOGGING_STATE_READY: case LOGGING_STATE_RUNNING: mExecutor.submit(new Callable() { @Override @@ -260,6 +269,11 @@ public class ResearchLog { void outputEvent(final String[] keys, final Object[] values) { // not thread safe. try { + if (mJsonWriter == NULL_JSON_WRITER) { + mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile))); + mJsonWriter.setLenient(true); + mJsonWriter.beginArray(); + } mJsonWriter.beginObject(); mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis()); mJsonWriter.name(UPTIME_KEY).value(SystemClock.uptimeMillis()); diff --git a/java/src/com/android/inputmethod/latin/ResearchLogger.java b/java/src/com/android/inputmethod/latin/ResearchLogger.java index 1abfbad13..e409c5a08 100644 --- a/java/src/com/android/inputmethod/latin/ResearchLogger.java +++ b/java/src/com/android/inputmethod/latin/ResearchLogger.java @@ -44,7 +44,6 @@ import com.android.inputmethod.latin.RichInputConnection.Range; import com.android.inputmethod.latin.define.ProductionFlag; import java.io.File; -import java.io.FileFilter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -64,13 +63,15 @@ import java.util.UUID; public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = ResearchLogger.class.getSimpleName(); private static final boolean OUTPUT_ENTIRE_BUFFER = false; // true may disclose private info + /* package */ static final boolean DEFAULT_USABILITY_STUDY_MODE = false; /* package */ static boolean sIsLogging = false; private static final int OUTPUT_FORMAT_VERSION = 1; private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; - private static final String FILENAME_PREFIX = "researchLog"; + /* package */ static final String FILENAME_PREFIX = "researchLog"; private static final String FILENAME_SUFFIX = ".txt"; private static final SimpleDateFormat TIMESTAMP_DATEFORMAT = new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US); + private static final boolean IS_SHOWING_INDICATOR = false; // constants related to specific log points private static final String WHITESPACE_SEPARATORS = " \t\n\r"; @@ -92,6 +93,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private boolean mIsPasswordView = false; private boolean mIsLoggingSuspended = false; + private SharedPreferences mPrefs; // digits entered by the user are replaced with this codepoint. /* package for test */ static final int DIGIT_REPLACEMENT_CODEPOINT = @@ -101,6 +103,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private static final String PREF_LAST_CLEANUP_TIME = "pref_last_cleanup_time"; private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = DateUtils.DAY_IN_MILLIS; private static final long MAX_LOGFILE_AGE_IN_MS = DateUtils.DAY_IN_MILLIS; + protected static final int SUSPEND_DURATION_IN_MINUTES = 1; // set when LatinIME should ignore an onUpdateSelection() callback that // arises from operations in this class private static boolean sLatinIMEExpectingUpdateSelection = false; @@ -124,7 +127,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (ims == null) { Log.w(TAG, "IMS is null; logging is off"); } else { - mContext = ims; mFilesDir = ims.getFilesDir(); if (mFilesDir == null || !mFilesDir.exists()) { Log.w(TAG, "IME storage directory does not exist."); @@ -132,6 +134,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } if (prefs != null) { mUUIDString = getUUID(prefs); + if (!prefs.contains(PREF_USABILITY_STUDY_MODE)) { + Editor e = prefs.edit(); + e.putBoolean(PREF_USABILITY_STUDY_MODE, DEFAULT_USABILITY_STUDY_MODE); + e.apply(); + } sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false); prefs.registerOnSharedPreferenceChangeListener(this); @@ -146,6 +153,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } mKeyboardSwitcher = keyboardSwitcher; + mContext = ims; + mPrefs = prefs; + + // TODO: force user to decide at splash screen instead of defaulting to on. + setLoggingAllowed(true); } private void cleanupLoggingDir(final File dir, final long time) { @@ -166,8 +178,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return new File(filesDir, sb.toString()); } - public void start() { - if (!sIsLogging) { + private void start() { + updateSuspendedState(); + requestIndicatorRedraw(); + if (!isAllowedToLog()) { // Log.w(TAG, "not in usability mode; not logging"); return; } @@ -175,10 +189,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang Log.w(TAG, "IME storage directory does not exist. Cannot start logging."); return; } - if (mMainResearchLog == null || !mMainResearchLog.isAlive()) { - mMainResearchLog = new ResearchLog(createLogFile(mFilesDir)); - } try { + if (mMainResearchLog == null || !mMainResearchLog.isAlive()) { + mMainResearchLog = new ResearchLog(createLogFile(mFilesDir)); + } mMainResearchLog.start(); if (mIntentionalResearchLog == null || !mIntentionalResearchLog.isAlive()) { mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir)); @@ -189,15 +203,26 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } - public void stop() { + /* package */ void stop() { if (mMainResearchLog != null) { mMainResearchLog.stop(); } + if (mIntentionalResearchLog != null) { + mIntentionalResearchLog.stop(); + } + } + + private void setLoggingAllowed(boolean enableLogging) { + if (mPrefs == null) { + return; + } + Editor e = mPrefs.edit(); + e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging); + e.apply(); + sIsLogging = enableLogging; } public boolean abort() { - mIsLoggingSuspended = true; - requestIndicatorRedraw(); boolean didAbortMainLog = false; if (mMainResearchLog != null) { mMainResearchLog.abort(); @@ -209,6 +234,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (mMainResearchLog.isAbortSuccessful()) { didAbortMainLog = true; } + mMainResearchLog = null; } boolean didAbortIntentionalLog = false; if (mIntentionalResearchLog != null) { @@ -221,6 +247,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (mIntentionalResearchLog.isAbortSuccessful()) { didAbortIntentionalLog = true; } + mIntentionalResearchLog = null; } return didAbortMainLog && didAbortIntentionalLog; } @@ -247,6 +274,34 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } + private void restart() { + stop(); + start(); + } + + private long mResumeTime = 0L; + private void suspendLoggingUntil(long time) { + mIsLoggingSuspended = true; + mResumeTime = time; + requestIndicatorRedraw(); + } + + private void resumeLogging() { + mResumeTime = 0L; + updateSuspendedState(); + requestIndicatorRedraw(); + if (isAllowedToLog()) { + restart(); + } + } + + private void updateSuspendedState() { + final long time = System.currentTimeMillis(); + if (time > mResumeTime) { + mIsLoggingSuspended = false; + } + } + @Override public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { if (key == null || prefs == null) { @@ -256,13 +311,15 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (sIsLogging == false) { abort(); } + requestIndicatorRedraw(); } /* package */ void presentResearchDialog(final LatinIME latinIME) { final CharSequence title = latinIME.getString(R.string.english_ime_research_log); + final boolean showEnable = mIsLoggingSuspended || !sIsLogging; final CharSequence[] items = new CharSequence[] { latinIME.getString(R.string.note_timestamp_for_researchlog), - mIsLoggingSuspended ? latinIME.getString(R.string.enable_session_logging) : + showEnable ? latinIME.getString(R.string.enable_session_logging) : latinIME.getString(R.string.do_not_log_this_session), latinIME.getString(R.string.log_whole_session_history), }; @@ -277,25 +334,25 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang Toast.LENGTH_LONG).show(); break; case 1: - if (mIsLoggingSuspended) { - mIsLoggingSuspended = false; - requestIndicatorRedraw(); - Toast toast = Toast.makeText(latinIME, - R.string.notify_session_logging_enabled, Toast.LENGTH_LONG); + if (showEnable) { + if (!sIsLogging) { + setLoggingAllowed(true); + } + resumeLogging(); + Toast.makeText(latinIME, R.string.notify_session_logging_enabled, + Toast.LENGTH_LONG).show(); } else { Toast toast = Toast.makeText(latinIME, R.string.notify_session_log_deleting, Toast.LENGTH_LONG); toast.show(); boolean isLogDeleted = abort(); + final long currentTime = System.currentTimeMillis(); + final long resumeTime = currentTime + 1000 * 60 * + SUSPEND_DURATION_IN_MINUTES; + suspendLoggingUntil(resumeTime); toast.cancel(); - if (isLogDeleted) { - Toast.makeText(latinIME, R.string.notify_session_log_deleted, - Toast.LENGTH_LONG).show(); - } else { - Toast.makeText(latinIME, - R.string.notify_session_log_not_deleted, Toast.LENGTH_LONG) - .show(); - } + Toast.makeText(latinIME, R.string.notify_logging_suspended, + Toast.LENGTH_LONG).show(); } break; case 2: @@ -328,13 +385,15 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private boolean isAllowedToLog() { - return !mIsPasswordView && !mIsLoggingSuspended; + return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging; } public void requestIndicatorRedraw() { // invalidate any existing graphics - if (mKeyboardSwitcher != null) { - mKeyboardSwitcher.getKeyboardView().invalidateAllKeys(); + if (IS_SHOWING_INDICATOR) { + if (mKeyboardSwitcher != null) { + mKeyboardSwitcher.getKeyboardView().invalidateAllKeys(); + } } } @@ -467,6 +526,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) { + if (!isAllowedToLog()) { + return; + } + if (mMainResearchLog == null) { + return; + } if (isPrivacySensitive) { mMainResearchLog.publishPublicEvents(logUnit); } else { @@ -536,6 +601,18 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } + private static String getUUID(final SharedPreferences prefs) { + String uuidString = prefs.getString(PREF_RESEARCH_LOGGER_UUID_STRING, null); + if (null == uuidString) { + UUID uuid = UUID.randomUUID(); + uuidString = uuid.toString(); + Editor editor = prefs.edit(); + editor.putString(PREF_RESEARCH_LOGGER_UUID_STRING, uuidString); + editor.apply(); + } + return uuidString; + } + private String scrubWord(String word) { if (mDictionary == null) { return WORD_REPLACEMENT_STRING; @@ -546,9 +623,62 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return WORD_REPLACEMENT_STRING; } + // Special methods related to startup, shutdown, logging itself + private static final String[] EVENTKEYS_INTENTIONAL_LOG = { "IntentionalLog" }; + + private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = { + "LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions", + "fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion" + }; + public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo, + final SharedPreferences prefs) { + final ResearchLogger researchLogger = getInstance(); + researchLogger.start(); + if (editorInfo != null) { + final Context context = researchLogger.mContext; + try { + final PackageInfo packageInfo; + packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), + 0); + final Integer versionCode = packageInfo.versionCode; + final String versionName = packageInfo.versionName; + final Object[] values = { + researchLogger.mUUIDString, editorInfo.packageName, + Integer.toHexString(editorInfo.inputType), + Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId, + Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName, + OUTPUT_FORMAT_VERSION + }; + researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values); + } catch (NameNotFoundException e) { + e.printStackTrace(); + } + } + } + + public void latinIME_onFinishInputInternal() { + stop(); + } + + private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { + "LatinIMECommitText", "typedWord" + }; + + public static void latinIME_commitText(final CharSequence typedWord) { + final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); + final Object[] values = { + scrubbedWord + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); + researchLogger.onWordComplete(scrubbedWord); + } + + // Regular logging methods + private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT = { "LatinKeyboardViewProcessMotionEvent", "action", "eventTime", "id", "x", "y", "size", "pressure" @@ -611,19 +741,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values); } - private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { - "LatinIMECommitText", "typedWord" - }; - public static void latinIME_commitText(final CharSequence typedWord) { - final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); - final Object[] values = { - scrubbedWord - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); - researchLogger.onWordComplete(scrubbedWord); - } - private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = { "LatinIMEDeleteSurroundingText", "length" }; @@ -702,51 +819,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // Play it safe. Remove privacy-sensitive events. researchLogger.publishLogUnit(researchLogger.mCurrentLogUnit, true); researchLogger.mCurrentLogUnit = new LogUnit(); + getInstance().restart(); } } - private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = { - "LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions", - "fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion" - }; - public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo, - final SharedPreferences prefs) { - final ResearchLogger researchLogger = getInstance(); - researchLogger.start(); - if (editorInfo != null) { - final Context context = researchLogger.mContext; - try { - final PackageInfo packageInfo; - packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), - 0); - final Integer versionCode = packageInfo.versionCode; - final String versionName = packageInfo.versionName; - final Object[] values = { - researchLogger.mUUIDString, editorInfo.packageName, - Integer.toHexString(editorInfo.inputType), - Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId, - Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName, - OUTPUT_FORMAT_VERSION - }; - researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values); - } catch (NameNotFoundException e) { - e.printStackTrace(); - } - } - } - - private static String getUUID(final SharedPreferences prefs) { - String uuidString = prefs.getString(PREF_RESEARCH_LOGGER_UUID_STRING, null); - if (null == uuidString) { - UUID uuid = UUID.randomUUID(); - uuidString = uuid.toString(); - Editor editor = prefs.edit(); - editor.putString(PREF_RESEARCH_LOGGER_UUID_STRING, uuidString); - editor.apply(); - } - return uuidString; - } - private static final String[] EVENTKEYS_LATINIME_ONUPDATESELECTION = { "LatinIMEOnUpdateSelection", "lastSelectionStart", "lastSelectionEnd", "oldSelStart", "oldSelEnd", "newSelStart", "newSelEnd", "composingSpanStart", "composingSpanEnd", @@ -873,6 +949,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (keyboard != null) { final KeyboardId kid = keyboard.mId; final boolean isPasswordView = kid.passwordInput(); + getInstance().setIsPasswordView(isPasswordView); final Object[] values = { KeyboardId.elementIdToName(kid.mElementId), kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 70acdc771..a07d286b8 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -208,7 +208,8 @@ public class Settings extends InputMethodSettingsFragment if (ProductionFlag.IS_EXPERIMENTAL) { if (usabilityStudyPref instanceof CheckBoxPreference) { CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref; - checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, true)); + checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, + ResearchLogger.DEFAULT_USABILITY_STUDY_MODE)); checkbox.setSummary(R.string.settings_warning_researcher_mode); } } From 721fd573f0f375b745df6b785bd31ae087da0f57 Mon Sep 17 00:00:00 2001 From: Ying Wang Date: Thu, 19 Jul 2012 14:56:45 -0700 Subject: [PATCH 12/36] Import translations. DO NOT MERGE Change-Id: I0122e62e7f9cf50d3987dc80018f2e9e78ed3f87 Auto-generated-cl: translation import --- java/res/values-fa/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index 22a0d2ced..9896864ec 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -118,7 +118,7 @@ "گزارش جلسه حذف نشد" "سابقه جلسه گزارش شد" "خطا: سابقه جلسه گزارش نشد" - "گزارش جلسه توانا شد" + "گزارش جلسه فعال شد" "زبان‌های ورودی" "برای ذخیره دوباره لمس کنید" "دیکشنری موجود است" From 0c5f72e2bf22df48af051827f97ab6052026d531 Mon Sep 17 00:00:00 2001 From: Tom Ouyang Date: Thu, 19 Jul 2012 21:53:42 +0900 Subject: [PATCH 13/36] Improve incremental gesture tracking. Eliminates need to recreate batch InputPointers on each gesture move event. Fixes issue where batch points from previous tapping input get mixed into next gesture. Change-Id: I9ecac66db88f5a87c6dde2138408906dd3d11139 --- .../inputmethod/keyboard/PointerTracker.java | 21 +++++++++++-------- .../keyboard/internal/GestureStroke.java | 10 +++++++-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index ea4d93a4a..b002ae992 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -121,6 +121,8 @@ public class PointerTracker { private static boolean sConfigGestureInputEnabledByBuildConfig; private static final ArrayList sTrackers = new ArrayList(); + private static final InputPointers sAggregratedPointers = new InputPointers( + GestureStroke.DEFAULT_CAPACITY); private static PointerTrackerQueue sPointerTrackerQueue; // HACK: Change gesture detection criteria depending on this variable. // TODO: Find more comprehensive ways to detect a gesture start. @@ -257,23 +259,19 @@ public class PointerTracker { // TODO: To handle multi-touch gestures we may want to move this method to // {@link PointerTrackerQueue}. private static InputPointers getIncrementalBatchPoints() { - // TODO: Avoid creating a new instance here? - final InputPointers pointers = new InputPointers(GestureStroke.DEFAULT_CAPACITY); for (final PointerTracker tracker : sTrackers) { - tracker.mGestureStroke.appendIncrementalBatchPoints(pointers); + tracker.mGestureStroke.appendIncrementalBatchPoints(sAggregratedPointers); } - return pointers; + return sAggregratedPointers; } // TODO: To handle multi-touch gestures we may want to move this method to // {@link PointerTrackerQueue}. private static InputPointers getAllBatchPoints() { - // TODO: Avoid creating a new instance here? - final InputPointers pointers = new InputPointers(GestureStroke.DEFAULT_CAPACITY); for (final PointerTracker tracker : sTrackers) { - tracker.mGestureStroke.appendAllBatchPoints(pointers); + tracker.mGestureStroke.appendAllBatchPoints(sAggregratedPointers); } - return pointers; + return sAggregratedPointers; } // TODO: To handle multi-touch gestures we may want to move this method to @@ -282,6 +280,7 @@ public class PointerTracker { for (final PointerTracker tracker : sTrackers) { tracker.mGestureStroke.reset(); } + sAggregratedPointers.reset(); } private PointerTracker(int id, KeyEventHandler handler) { @@ -645,6 +644,8 @@ public class PointerTracker { if (sIsGestureEnabled && mIsAlphabetKeyboard && key != null && Keyboard.isLetterCode(key.mCode)) { mIsPossibleGesture = true; + // TODO: pointer times should be relative to first down even in entire batch input + // instead of resetting to 0 for each new down event. mGestureStroke.addPoint(x, y, 0, false); } } @@ -869,7 +870,9 @@ public class PointerTracker { } return; } - + // This event will be recognized as a regular code input. Clear unused batch points so they + // are not mistakenly included in the next batch event. + clearBatchInputPointsOfAllPointerTrackers(); if (mKeyAlreadyProcessed) return; if (mCurrentKey != null && !mCurrentKey.isRepeatable()) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 6f392f145..16c8410df 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -27,6 +27,7 @@ public class GestureStroke { private float mLength; private float mAngle; private int mIncrementalRecognitionSize; + private int mLastIncrementalBatchSize; private long mLastPointTime; private int mLastPointX; private int mLastPointY; @@ -73,6 +74,7 @@ public class GestureStroke { mLength = 0; mAngle = 0; mIncrementalRecognitionSize = 0; + mLastIncrementalBatchSize = 0; mLastPointTime = 0; mInputPointers.reset(); } @@ -126,11 +128,15 @@ public class GestureStroke { } public void appendAllBatchPoints(final InputPointers out) { - out.append(mInputPointers, 0, mInputPointers.getPointerSize()); + final int size = mInputPointers.getPointerSize(); + out.append(mInputPointers, mLastIncrementalBatchSize, size - mLastIncrementalBatchSize); + mLastIncrementalBatchSize = size; } public void appendIncrementalBatchPoints(final InputPointers out) { - out.append(mInputPointers, 0, mIncrementalRecognitionSize); + out.append(mInputPointers, mLastIncrementalBatchSize, + mIncrementalRecognitionSize - mLastIncrementalBatchSize); + mLastIncrementalBatchSize = mIncrementalRecognitionSize; } private static float getDistance(final int p1x, final int p1y, From c49c85f835ecd14d09abb6d88c85a3303c566741 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 20 Jul 2012 12:01:33 +0900 Subject: [PATCH 14/36] Implement ResizableIntArray.setLength and .get This change revises ResizableIntArrayTests as well. --- java/proguard.flags | 4 + .../inputmethod/latin/ResizableIntArray.java | 28 ++- .../latin/ResizableIntArrayTests.java | 165 +++++++++++++++--- 3 files changed, 165 insertions(+), 32 deletions(-) diff --git a/java/proguard.flags b/java/proguard.flags index 163352287..1711b99df 100644 --- a/java/proguard.flags +++ b/java/proguard.flags @@ -24,6 +24,10 @@ *; } +-keep class com.android.inputmethod.latin.ResizableIntArray { + *; +} + -keep class com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment { *; } diff --git a/java/src/com/android/inputmethod/latin/ResizableIntArray.java b/java/src/com/android/inputmethod/latin/ResizableIntArray.java index 2079c0e99..6feae9c5a 100644 --- a/java/src/com/android/inputmethod/latin/ResizableIntArray.java +++ b/java/src/com/android/inputmethod/latin/ResizableIntArray.java @@ -23,11 +23,18 @@ public class ResizableIntArray { private int[] mArray; private int mLength; - public ResizableIntArray(int capacity) { + public ResizableIntArray(final int capacity) { reset(capacity); } - public void add(int index, int val) { + public int get(final int index) { + if (index < 0 || index >= mLength) { + throw new ArrayIndexOutOfBoundsException("length=" + mLength + "; index=" + index); + } + return mArray[index]; + } + + public void add(final int index, final int val) { if (mLength < index + 1) { mLength = index; add(val); @@ -36,14 +43,14 @@ public class ResizableIntArray { } } - public void add(int val) { + public void add(final int val) { final int nextLength = mLength + 1; ensureCapacity(nextLength); mArray[mLength] = val; mLength = nextLength; } - private void ensureCapacity(int minimumCapacity) { + private void ensureCapacity(final int minimumCapacity) { if (mArray.length < minimumCapacity) { final int nextCapacity = mArray.length * 2; // The following is the same as newLength = @@ -60,9 +67,12 @@ public class ResizableIntArray { return mLength; } - // TODO: Implement setLength(int). + public void setLength(final int newLength) { + ensureCapacity(newLength); + mLength = newLength; + } - public void reset(int capacity) { + public void reset(final int capacity) { // TODO: Implement primitive array pool. mArray = new int[capacity]; mLength = 0; @@ -72,20 +82,20 @@ public class ResizableIntArray { return mArray; } - public void set(ResizableIntArray ip) { + public void set(final ResizableIntArray ip) { // TODO: Implement primitive array pool. mArray = ip.mArray; mLength = ip.mLength; } - public void copy(ResizableIntArray ip) { + public void copy(final ResizableIntArray ip) { // TODO: Avoid useless coping of values. ensureCapacity(ip.mLength); System.arraycopy(ip.mArray, 0, mArray, 0, ip.mLength); mLength = ip.mLength; } - public void append(ResizableIntArray src, int startPos, int length) { + public void append(final ResizableIntArray src, final int startPos, final int length) { final int currentLength = mLength; final int newLength = currentLength + length; ensureCapacity(newLength); diff --git a/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java b/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java index 8b869b6ca..80e5f9cb1 100644 --- a/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java +++ b/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java @@ -22,50 +22,142 @@ public class ResizableIntArrayTests extends AndroidTestCase { private static final int DEFAULT_CAPACITY = 48; public void testNewInstance() { - final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); - assertEquals("new instance length", 0, src.getLength()); - assertNotNull("new instance array", src.getPrimitiveArray()); - } - - public void testReset() { final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); final int[] array = src.getPrimitiveArray(); - - src.reset(DEFAULT_CAPACITY); - assertEquals("length after reset", 0, src.getLength()); - assertNotSame("array after reset", array, src.getPrimitiveArray()); + assertEquals("new instance length", 0, src.getLength()); + assertNotNull("new instance array", array); + assertEquals("new instance array length", DEFAULT_CAPACITY, array.length); } public void testAdd() { final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); - final int limit = src.getPrimitiveArray().length * 2 + 10; + final int[] array = src.getPrimitiveArray(); + int[] array2 = null, array3 = null; + final int limit = DEFAULT_CAPACITY * 2 + 10; for (int i = 0; i < limit; i++) { src.add(i); assertEquals("length after add " + i, i + 1, src.getLength()); + if (i == DEFAULT_CAPACITY) array2 = src.getPrimitiveArray(); + if (i == DEFAULT_CAPACITY * 2) array3 = src.getPrimitiveArray(); + if (i < DEFAULT_CAPACITY) { + assertSame("array after add " + i, array, src.getPrimitiveArray()); + } else if (i < DEFAULT_CAPACITY * 2) { + assertSame("array after add " + i, array2, src.getPrimitiveArray()); + } else if (i < DEFAULT_CAPACITY * 3) { + assertSame("array after add " + i, array3, src.getPrimitiveArray()); + } } for (int i = 0; i < limit; i++) { - assertEquals("value at " + i, i, src.getPrimitiveArray()[i]); + assertEquals("value at " + i, i, src.get(i)); } } public void testAddAt() { final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); - final int limit = 1000, step = 100; + final int limit = DEFAULT_CAPACITY * 10, step = DEFAULT_CAPACITY * 2; for (int i = 0; i < limit; i += step) { src.add(i, i); assertEquals("length after add at " + i, i + 1, src.getLength()); } for (int i = 0; i < limit; i += step) { - assertEquals("value at " + i, i, src.getPrimitiveArray()[i]); + assertEquals("value at " + i, i, src.get(i)); + } + } + + public void testGet() { + final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); + try { + final int value = src.get(0); + fail("get(0) shouldn't succeed"); + } catch (ArrayIndexOutOfBoundsException e) { + // success + } + try { + final int value = src.get(DEFAULT_CAPACITY); + fail("get(DEFAULT_CAPACITY) shouldn't succeed"); + } catch (ArrayIndexOutOfBoundsException e) { + // success + } + + final int index = DEFAULT_CAPACITY / 2; + src.add(index, 100); + assertEquals("legth after add at " + index, index + 1, src.getLength()); + assertEquals("value after add at " + index, 100, src.get(index)); + assertEquals("value after add at 0", 0, src.get(0)); + try { + final int value = src.get(src.getLength()); + fail("get(length) shouldn't succeed"); + } catch (ArrayIndexOutOfBoundsException e) { + // success + } + } + + public void testReset() { + final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); + final int[] array = src.getPrimitiveArray(); + for (int i = 0; i < DEFAULT_CAPACITY; i++) { + src.add(i); + assertEquals("length after add " + i, i + 1, src.getLength()); + } + + final int smallerLength = DEFAULT_CAPACITY / 2; + src.reset(smallerLength); + final int[] array2 = src.getPrimitiveArray(); + assertEquals("length after reset", 0, src.getLength()); + assertNotSame("array after reset", array, array2); + + int[] array3 = null; + for (int i = 0; i < DEFAULT_CAPACITY; i++) { + src.add(i); + assertEquals("length after add " + i, i + 1, src.getLength()); + if (i == smallerLength) array3 = src.getPrimitiveArray(); + if (i < smallerLength) { + assertSame("array after add " + i, array2, src.getPrimitiveArray()); + } else if (i < smallerLength * 2) { + assertSame("array after add " + i, array3, src.getPrimitiveArray()); + } + } + } + + public void testSetLength() { + final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); + final int[] array = src.getPrimitiveArray(); + for (int i = 0; i < DEFAULT_CAPACITY; i++) { + src.add(i); + assertEquals("length after add " + i, i + 1, src.getLength()); + } + + final int largerLength = DEFAULT_CAPACITY * 2; + src.setLength(largerLength); + final int[] array2 = src.getPrimitiveArray(); + assertEquals("length after larger setLength", largerLength, src.getLength()); + assertNotSame("array after larger setLength", array, array2); + assertEquals("array length after larger setLength", largerLength, array2.length); + for (int i = 0; i < DEFAULT_CAPACITY; i++) { + assertEquals("value at " + i, i, src.get(i)); + } + for (int i = DEFAULT_CAPACITY; i < largerLength; i++) { + assertEquals("value at " + i, 0, src.get(i)); + } + + final int smallerLength = DEFAULT_CAPACITY / 2; + src.setLength(smallerLength); + final int[] array3 = src.getPrimitiveArray(); + assertEquals("length after smaller setLength", smallerLength, src.getLength()); + assertSame("array after smaller setLength", array2, array3); + assertEquals("array length after smaller setLength", largerLength, array3.length); + for (int i = 0; i < smallerLength; i++) { + assertEquals("value at " + i, i, src.get(i)); } } public void testSet() { final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); - final int limit = src.getPrimitiveArray().length * 2 + 10; + final int limit = DEFAULT_CAPACITY * 2 + 10; for (int i = 0; i < limit; i++) { src.add(i); } + final ResizableIntArray dst = new ResizableIntArray(DEFAULT_CAPACITY); dst.set(src); assertEquals("length after set", dst.getLength(), src.getLength()); @@ -74,27 +166,40 @@ public class ResizableIntArrayTests extends AndroidTestCase { public void testCopy() { final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); - final int limit = 100; - for (int i = 0; i < limit; i++) { + for (int i = 0; i < DEFAULT_CAPACITY; i++) { src.add(i); } + final ResizableIntArray dst = new ResizableIntArray(DEFAULT_CAPACITY); + final int[] array = dst.getPrimitiveArray(); dst.copy(src); assertEquals("length after copy", dst.getLength(), src.getLength()); + assertSame("array after copy", array, dst.getPrimitiveArray()); assertNotSame("array after copy", dst.getPrimitiveArray(), src.getPrimitiveArray()); - final int length = dst.getLength(); assertArrayEquals("values after copy", - dst.getPrimitiveArray(), 0, src.getPrimitiveArray(), 0, length); + dst.getPrimitiveArray(), 0, src.getPrimitiveArray(), 0, dst.getLength()); + + final int smallerLength = DEFAULT_CAPACITY / 2; + dst.reset(smallerLength); + final int[] array2 = dst.getPrimitiveArray(); + dst.copy(src); + final int[] array3 = dst.getPrimitiveArray(); + assertEquals("length after copy to smaller", dst.getLength(), src.getLength()); + assertNotSame("array after copy to smaller", array2, array3); + assertNotSame("array after copy to smaller", array3, src.getPrimitiveArray()); + assertArrayEquals("values after copy to smaller", + dst.getPrimitiveArray(), 0, src.getPrimitiveArray(), 0, dst.getLength()); } public void testAppend() { - final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); - final int srcLen = 100; + final int srcLen = DEFAULT_CAPACITY; + final ResizableIntArray src = new ResizableIntArray(srcLen); for (int i = 0; i < srcLen; i++) { src.add(i); } - final int dstLen = 50; - final ResizableIntArray dst = new ResizableIntArray(DEFAULT_CAPACITY); + final ResizableIntArray dst = new ResizableIntArray(DEFAULT_CAPACITY * 2); + final int[] array = dst.getPrimitiveArray(); + final int dstLen = DEFAULT_CAPACITY / 2; for (int i = 0; i < dstLen; i++) { final int value = -i - 1; dst.add(value); @@ -104,17 +209,31 @@ public class ResizableIntArrayTests extends AndroidTestCase { dst.append(src, 0, 0); assertEquals("length after append zero", dstLen, dst.getLength()); + assertSame("array after append zero", array, dst.getPrimitiveArray()); assertArrayEquals("values after append zero", dstCopy.getPrimitiveArray(), 0, dst.getPrimitiveArray(), 0, dstLen); dst.append(src, 0, srcLen); assertEquals("length after append", dstLen + srcLen, dst.getLength()); + assertSame("array after append", array, dst.getPrimitiveArray()); assertTrue("primitive length after append", dst.getPrimitiveArray().length >= dstLen + srcLen); assertArrayEquals("original values after append", dstCopy.getPrimitiveArray(), 0, dst.getPrimitiveArray(), 0, dstLen); assertArrayEquals("appended values after append", src.getPrimitiveArray(), 0, dst.getPrimitiveArray(), dstLen, srcLen); + + dst.append(src, 0, srcLen); + assertEquals("length after 2nd append", dstLen + srcLen * 2, dst.getLength()); + assertNotSame("array after 2nd append", array, dst.getPrimitiveArray()); + assertTrue("primitive length after 2nd append", + dst.getPrimitiveArray().length >= dstLen + srcLen * 2); + assertArrayEquals("original values after 2nd append", + dstCopy.getPrimitiveArray(), 0, dst.getPrimitiveArray(), 0, dstLen); + assertArrayEquals("appended values after 2nd append", + src.getPrimitiveArray(), 0, dst.getPrimitiveArray(), dstLen, srcLen); + assertArrayEquals("appended values after 2nd append", + src.getPrimitiveArray(), 0, dst.getPrimitiveArray(), dstLen + srcLen, srcLen); } private static void assertArrayEquals(String message, int[] expecteds, int expectedPos, From 2fc127698aa11a5d8dab928dc701442e0d09efaa Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Fri, 20 Jul 2012 12:17:31 +0900 Subject: [PATCH 15/36] Make ALPHA_OPAQUE public Change-Id: I904685be07e23292dd95296617a4b64c366f06f2 --- java/src/com/android/inputmethod/keyboard/KeyboardView.java | 2 +- .../src/com/android/inputmethod/keyboard/LatinKeyboardView.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index fb98af3e6..b05cc70b7 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -94,7 +94,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { // The maximum key label width in the proportion to the key width. private static final float MAX_LABEL_RATIO = 0.90f; - private final static int ALPHA_OPAQUE = 255; + public final static int ALPHA_OPAQUE = 255; // Main keyboard private Keyboard mKeyboard; diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index 1eae2c1d4..36d3664de 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -80,7 +80,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke // Stuff to draw language name on spacebar. private final int mLanguageOnSpacebarFinalAlpha; private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator; - private static final int ALPHA_OPAQUE = 255; private boolean mNeedsToDisplayLanguage; private boolean mHasMultipleEnabledIMEsOrSubtypes; private int mLanguageOnSpacebarAnimAlpha = ALPHA_OPAQUE; From d6a18cdedb74a76e00c50af48eb7d5743358823f Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 20 Jul 2012 14:51:41 +0900 Subject: [PATCH 16/36] Fix NPE where the current subtype is null Bug: 6847999 --- java/src/com/android/inputmethod/latin/ImfUtils.java | 7 +++++++ .../src/com/android/inputmethod/latin/SubtypeSwitcher.java | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/ImfUtils.java b/java/src/com/android/inputmethod/latin/ImfUtils.java index b882a4860..1461c0240 100644 --- a/java/src/com/android/inputmethod/latin/ImfUtils.java +++ b/java/src/com/android/inputmethod/latin/ImfUtils.java @@ -90,6 +90,13 @@ public class ImfUtils { return false; } + public static InputMethodSubtype getCurrentInputMethodSubtype(Context context, + InputMethodSubtype defaultSubtype) { + final InputMethodManager imm = getInputMethodManager(context); + final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype(); + return (currentSubtype != null) ? currentSubtype : defaultSubtype; + } + public static boolean hasMultipleEnabledIMEsOrSubtypes(Context context, final boolean shouldIncludeAuxiliarySubtypes) { final InputMethodManager imm = getInputMethodManager(context); diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 664de6774..a7a5fcb5f 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -98,9 +98,9 @@ public class SubtypeSwitcher { mConnectivityManager = (ConnectivityManager) service.getSystemService( Context.CONNECTIVITY_SERVICE); mCurrentSystemLocale = mResources.getConfiguration().locale; - mCurrentSubtype = mImm.getCurrentInputMethodSubtype(); mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY); + mCurrentSubtype = ImfUtils.getCurrentInputMethodSubtype(service, mNoLanguageSubtype); if (mNoLanguageSubtype == null) { throw new RuntimeException("Can't find no lanugage with QWERTY subtype"); } @@ -113,7 +113,7 @@ public class SubtypeSwitcher { // Only configuration changed event is allowed to call this because this is heavy. private void updateAllParameters() { mCurrentSystemLocale = mResources.getConfiguration().locale; - updateSubtype(mImm.getCurrentInputMethodSubtype()); + updateSubtype(ImfUtils.getCurrentInputMethodSubtype(mService, mNoLanguageSubtype)); updateParametersOnStartInputView(); } @@ -142,7 +142,7 @@ public class SubtypeSwitcher { + currentSubtype.getLocale() + "/" + currentSubtype.getExtraValue()); Log.w(TAG, "Last subtype was disabled. Update to the current one."); } - updateSubtype(mImm.getCurrentInputMethodSubtype()); + updateSubtype(ImfUtils.getCurrentInputMethodSubtype(mService, mNoLanguageSubtype)); } } From 7abdcf1ed3113d3c121f6ff1b87a7464f079e141 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 20 Jul 2012 12:45:15 +0900 Subject: [PATCH 17/36] Implement ResizableIntArray.fill Change-Id: I570641bc2f32d016c247db6c065a138d8235ab8c --- .../inputmethod/latin/ResizableIntArray.java | 69 +++++++++---- .../latin/ResizableIntArrayTests.java | 97 +++++++++++++++++-- 2 files changed, 138 insertions(+), 28 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/ResizableIntArray.java b/java/src/com/android/inputmethod/latin/ResizableIntArray.java index 6feae9c5a..387d45a53 100644 --- a/java/src/com/android/inputmethod/latin/ResizableIntArray.java +++ b/java/src/com/android/inputmethod/latin/ResizableIntArray.java @@ -28,38 +28,49 @@ public class ResizableIntArray { } public int get(final int index) { - if (index < 0 || index >= mLength) { - throw new ArrayIndexOutOfBoundsException("length=" + mLength + "; index=" + index); + if (index < mLength) { + return mArray[index]; } - return mArray[index]; + throw new ArrayIndexOutOfBoundsException("length=" + mLength + "; index=" + index); } public void add(final int index, final int val) { - if (mLength < index + 1) { + if (index < mLength) { + mArray[index] = val; + } else { mLength = index; add(val); - } else { - mArray[index] = val; } } public void add(final int val) { - final int nextLength = mLength + 1; - ensureCapacity(nextLength); - mArray[mLength] = val; - mLength = nextLength; + final int currentLength = mLength; + ensureCapacity(currentLength + 1); + mArray[currentLength] = val; + mLength = currentLength + 1; + } + + /** + * Calculate the new capacity of {@code mArray}. + * @param minimumCapacity the minimum capacity that the {@code mArray} should have. + * @return the new capacity that the {@code mArray} should have. Returns zero when there is no + * need to expand {@code mArray}. + */ + private int calculateCapacity(final int minimumCapacity) { + final int currentCapcity = mArray.length; + if (currentCapcity < minimumCapacity) { + final int nextCapacity = currentCapcity * 2; + // The following is the same as return Math.max(minimumCapacity, nextCapacity); + return minimumCapacity > nextCapacity ? minimumCapacity : nextCapacity; + } + return 0; } private void ensureCapacity(final int minimumCapacity) { - if (mArray.length < minimumCapacity) { - final int nextCapacity = mArray.length * 2; - // The following is the same as newLength = - // Math.max(minimumCapacity, nextCapacity); - final int newLength = minimumCapacity > nextCapacity - ? minimumCapacity - : nextCapacity; + final int newCapacity = calculateCapacity(minimumCapacity); + if (newCapacity > 0) { // TODO: Implement primitive array pool. - mArray = Arrays.copyOf(mArray, newLength); + mArray = Arrays.copyOf(mArray, newCapacity); } } @@ -89,17 +100,35 @@ public class ResizableIntArray { } public void copy(final ResizableIntArray ip) { - // TODO: Avoid useless coping of values. - ensureCapacity(ip.mLength); + final int newCapacity = calculateCapacity(ip.mLength); + if (newCapacity > 0) { + // TODO: Implement primitive array pool. + mArray = new int[newCapacity]; + } System.arraycopy(ip.mArray, 0, mArray, 0, ip.mLength); mLength = ip.mLength; } public void append(final ResizableIntArray src, final int startPos, final int length) { + if (length == 0) { + return; + } final int currentLength = mLength; final int newLength = currentLength + length; ensureCapacity(newLength); System.arraycopy(src.mArray, startPos, mArray, currentLength, length); mLength = newLength; } + + public void fill(final int value, final int startPos, final int length) { + if (startPos < 0 || length < 0) { + throw new IllegalArgumentException("startPos=" + startPos + "; length=" + length); + } + final int endPos = startPos + length; + ensureCapacity(endPos); + Arrays.fill(mArray, startPos, endPos, value); + if (mLength < endPos) { + mLength = endPos; + } + } } diff --git a/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java b/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java index 80e5f9cb1..995fc14ea 100644 --- a/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java +++ b/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java @@ -37,8 +37,12 @@ public class ResizableIntArrayTests extends AndroidTestCase { for (int i = 0; i < limit; i++) { src.add(i); assertEquals("length after add " + i, i + 1, src.getLength()); - if (i == DEFAULT_CAPACITY) array2 = src.getPrimitiveArray(); - if (i == DEFAULT_CAPACITY * 2) array3 = src.getPrimitiveArray(); + if (i == DEFAULT_CAPACITY) { + array2 = src.getPrimitiveArray(); + } + if (i == DEFAULT_CAPACITY * 2) { + array3 = src.getPrimitiveArray(); + } if (i < DEFAULT_CAPACITY) { assertSame("array after add " + i, array, src.getPrimitiveArray()); } else if (i < DEFAULT_CAPACITY * 2) { @@ -110,7 +114,9 @@ public class ResizableIntArrayTests extends AndroidTestCase { for (int i = 0; i < DEFAULT_CAPACITY; i++) { src.add(i); assertEquals("length after add " + i, i + 1, src.getLength()); - if (i == smallerLength) array3 = src.getPrimitiveArray(); + if (i == smallerLength) { + array3 = src.getPrimitiveArray(); + } if (i < smallerLength) { assertSame("array after add " + i, array2, src.getPrimitiveArray()); } else if (i < smallerLength * 2) { @@ -133,11 +139,13 @@ public class ResizableIntArrayTests extends AndroidTestCase { assertEquals("length after larger setLength", largerLength, src.getLength()); assertNotSame("array after larger setLength", array, array2); assertEquals("array length after larger setLength", largerLength, array2.length); - for (int i = 0; i < DEFAULT_CAPACITY; i++) { - assertEquals("value at " + i, i, src.get(i)); - } - for (int i = DEFAULT_CAPACITY; i < largerLength; i++) { - assertEquals("value at " + i, 0, src.get(i)); + for (int i = 0; i < largerLength; i++) { + final int v = src.get(i); + if (i < DEFAULT_CAPACITY) { + assertEquals("value at " + i, i, v); + } else { + assertEquals("value at " + i, 0, v); + } } final int smallerLength = DEFAULT_CAPACITY / 2; @@ -236,6 +244,79 @@ public class ResizableIntArrayTests extends AndroidTestCase { src.getPrimitiveArray(), 0, dst.getPrimitiveArray(), dstLen + srcLen, srcLen); } + public void testFill() { + final int srcLen = DEFAULT_CAPACITY; + final ResizableIntArray src = new ResizableIntArray(srcLen); + for (int i = 0; i < srcLen; i++) { + src.add(i); + } + final int[] array = src.getPrimitiveArray(); + + final int startPos = srcLen / 3; + final int length = srcLen / 3; + final int endPos = startPos + length; + assertTrue(startPos >= 1); + final int value = 123; + try { + src.fill(value, -1, length); + fail("fill from -1 shouldn't succeed"); + } catch (IllegalArgumentException e) { + // success + } + try { + src.fill(value, startPos, -1); + fail("fill negative length shouldn't succeed"); + } catch (IllegalArgumentException e) { + // success + } + + src.fill(value, startPos, length); + assertEquals("length after fill", srcLen, src.getLength()); + assertSame("array after fill", array, src.getPrimitiveArray()); + for (int i = 0; i < srcLen; i++) { + final int v = src.get(i); + if (i >= startPos && i < endPos) { + assertEquals("new values after fill at " + i, value, v); + } else { + assertEquals("unmodified values after fill at " + i, i, v); + } + } + + final int length2 = srcLen * 2 - startPos; + final int largeEnd = startPos + length2; + assertTrue(largeEnd > srcLen); + final int value2 = 456; + src.fill(value2, startPos, length2); + assertEquals("length after large fill", largeEnd, src.getLength()); + assertNotSame("array after large fill", array, src.getPrimitiveArray()); + for (int i = 0; i < largeEnd; i++) { + final int v = src.get(i); + if (i >= startPos && i < largeEnd) { + assertEquals("new values after large fill at " + i, value2, v); + } else { + assertEquals("unmodified values after large fill at " + i, i, v); + } + } + + final int startPos2 = largeEnd + length2; + final int endPos2 = startPos2 + length2; + final int value3 = 789; + src.fill(value3, startPos2, length2); + assertEquals("length after disjoint fill", endPos2, src.getLength()); + for (int i = 0; i < endPos2; i++) { + final int v = src.get(i); + if (i >= startPos2 && i < endPos2) { + assertEquals("new values after disjoint fill at " + i, value3, v); + } else if (i >= startPos && i < largeEnd) { + assertEquals("unmodified values after disjoint fill at " + i, value2, v); + } else if (i < startPos) { + assertEquals("unmodified values after disjoint fill at " + i, i, v); + } else { + assertEquals("gap values after disjoint fill at " + i, 0, v); + } + } + } + private static void assertArrayEquals(String message, int[] expecteds, int expectedPos, int[] actuals, int actualPos, int length) { if (expecteds == null && actuals == null) { From 4daf32b6c0358f0273a99b622a259ecdf6b44fa4 Mon Sep 17 00:00:00 2001 From: Tom Ouyang Date: Thu, 19 Jul 2012 17:20:54 +0900 Subject: [PATCH 18/36] Add gesture trail feedback. Change-Id: I32709fac0dec3165678a052aa286e2fb3d90721b --- .../inputmethod/keyboard/KeyboardView.java | 69 +++++++++++++++++-- .../keyboard/LatinKeyboardView.java | 12 ++-- .../inputmethod/keyboard/PointerTracker.java | 18 +++++ .../keyboard/internal/GestureStroke.java | 39 ++++++++++- .../android/inputmethod/latin/Constants.java | 7 ++ 5 files changed, 132 insertions(+), 13 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index b05cc70b7..721931e98 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -38,6 +38,7 @@ import android.view.ViewGroup; import android.widget.RelativeLayout; import android.widget.TextView; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; @@ -94,7 +95,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { // The maximum key label width in the proportion to the key width. private static final float MAX_LABEL_RATIO = 0.90f; - public final static int ALPHA_OPAQUE = 255; + private final static int GESTURE_DRAWING_WIDTH = 5; + private final static int GESTURE_DRAWING_COLOR = 0xff33b5e5; // Main keyboard private Keyboard mKeyboard; @@ -118,11 +120,14 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { private final HashSet mInvalidatedKeys = new HashSet(); /** The region of invalidated keys */ private final Rect mInvalidatedKeysRect = new Rect(); + /** The region of invalidated gestures */ + private final Rect mInvalidatedGesturesRect = new Rect(); /** The keyboard bitmap buffer for faster updates */ private Bitmap mBuffer; /** The canvas for the above mutable keyboard bitmap */ private Canvas mCanvas; private final Paint mPaint = new Paint(); + private final Paint mGesturePaint = new Paint(); private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); // This sparse array caches key label text height in pixel indexed by key label text size. private static final SparseArray sTextHeightCache = new SparseArray(); @@ -264,7 +269,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { public void blendAlpha(Paint paint) { final int color = paint.getColor(); - paint.setARGB((paint.getAlpha() * mAnimAlpha) / ALPHA_OPAQUE, + paint.setARGB((paint.getAlpha() * mAnimAlpha) / Constants.Color.ALPHA_OPAQUE, Color.red(color), Color.green(color), Color.blue(color)); } } @@ -372,6 +377,13 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout; mPaint.setAntiAlias(true); + + // TODO: These paint parameters should be specified via attribute of the view and styleable. + mGesturePaint.setAntiAlias(true); + mGesturePaint.setStyle(Paint.Style.STROKE); + mGesturePaint.setStrokeJoin(Paint.Join.ROUND); + mGesturePaint.setColor(GESTURE_DRAWING_COLOR); + mGesturePaint.setStrokeWidth(GESTURE_DRAWING_WIDTH); } // Read fraction value in TypedArray as float. @@ -517,7 +529,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { final int keyDrawY = key.mY + getPaddingTop(); canvas.translate(keyDrawX, keyDrawY); - params.mAnimAlpha = ALPHA_OPAQUE; + params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE; if (!key.isSpacer()) { onDrawKeyBackground(key, canvas, params); } @@ -860,17 +872,60 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker); } + private static class PreviewView extends RelativeLayout { + KeyPreviewDrawParams mParams; + Paint mGesturePaint; + + public PreviewView(Context context, KeyPreviewDrawParams params, Paint gesturePaint) { + super(context); + setWillNotDraw(false); + mParams = params; + mGesturePaint = gesturePaint; + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.translate(mParams.mCoordinates[0], mParams.mCoordinates[1]); + PointerTracker.drawGestureTrailForAllPointerTrackers(canvas, mGesturePaint); + } + } + private void addKeyPreview(TextView keyPreview) { if (mPreviewPlacer == null) { - mPreviewPlacer = new RelativeLayout(getContext()); - final ViewGroup windowContentView = - (ViewGroup)getRootView().findViewById(android.R.id.content); - windowContentView.addView(mPreviewPlacer); + createPreviewPlacer(); } mPreviewPlacer.addView( keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacer, 0, 0)); } + private void createPreviewPlacer() { + mPreviewPlacer = new PreviewView(getContext(), mKeyPreviewDrawParams, mGesturePaint); + final ViewGroup windowContentView = + (ViewGroup)getRootView().findViewById(android.R.id.content); + windowContentView.addView(mPreviewPlacer); + } + + @Override + public void showGestureTrail(PointerTracker tracker) { + if (mPreviewPlacer == null) { + createPreviewPlacer(); + } + final Rect r = tracker.getDrawingRect(); + if (!r.isEmpty()) { + // Invalidate the rectangular region encompassing the gesture. This is needed because + // past points along the gesture will fade and gradually disappear. + final KeyPreviewDrawParams params = mKeyPreviewDrawParams; + mInvalidatedGesturesRect.set(r.left + params.mCoordinates[0] - GESTURE_DRAWING_WIDTH, + r.top + params.mCoordinates[1] - GESTURE_DRAWING_WIDTH, + r.right + params.mCoordinates[0] + GESTURE_DRAWING_WIDTH, + r.bottom + params.mCoordinates[1] + GESTURE_DRAWING_WIDTH); + mPreviewPlacer.invalidate(mInvalidatedGesturesRect); + } else { + mPreviewPlacer.invalidate(); + } + } + @SuppressWarnings("deprecation") // setBackgroundDrawable is replaced by setBackground in API16 @Override public void showKeyPreview(PointerTracker tracker) { diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index 36d3664de..8e904c6f9 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -43,6 +43,7 @@ import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; @@ -82,7 +83,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator; private boolean mNeedsToDisplayLanguage; private boolean mHasMultipleEnabledIMEsOrSubtypes; - private int mLanguageOnSpacebarAnimAlpha = ALPHA_OPAQUE; + private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE; private final float mSpacebarTextRatio; private float mSpacebarTextSize; private final int mSpacebarTextColor; @@ -98,7 +99,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke // Stuff to draw altCodeWhileTyping keys. private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator; private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator; - private int mAltCodeKeyWhileTypingAnimAlpha = ALPHA_OPAQUE; + private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE; // More keys keyboard private PopupWindow mMoreKeysWindow; @@ -361,7 +362,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke mSpacebarTextShadowColor = a.getColor( R.styleable.LatinKeyboardView_spacebarTextShadowColor, 0); mLanguageOnSpacebarFinalAlpha = a.getInt( - R.styleable.LatinKeyboardView_languageOnSpacebarFinalAlpha, ALPHA_OPAQUE); + R.styleable.LatinKeyboardView_languageOnSpacebarFinalAlpha, + Constants.Color.ALPHA_OPAQUE); final int languageOnSpacebarFadeoutAnimatorResId = a.getResourceId( R.styleable.LatinKeyboardView_languageOnSpacebarFadeoutAnimator, 0); final int altCodeKeyWhileTypingFadeoutAnimatorResId = a.getResourceId( @@ -468,7 +470,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE); mSpaceIcon = (mSpaceKey != null) - ? mSpaceKey.getIcon(keyboard.mIconsSet, ALPHA_OPAQUE) : null; + ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null; final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; mSpacebarTextSize = keyHeight * mSpacebarTextRatio; if (ProductionFlag.IS_EXPERIMENTAL) { @@ -870,7 +872,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke mNeedsToDisplayLanguage = false; } else { if (subtypeChanged && needsToDisplayLanguage) { - setLanguageOnSpacebarAnimAlpha(ALPHA_OPAQUE); + setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE); if (animator.isStarted()) { animator.cancel(); } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index b002ae992..29aaa1b53 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -16,6 +16,9 @@ package com.android.inputmethod.keyboard; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; import android.os.SystemClock; import android.util.Log; import android.view.MotionEvent; @@ -76,6 +79,7 @@ public class PointerTracker { public TextView inflateKeyPreviewText(); public void showKeyPreview(PointerTracker tracker); public void dismissKeyPreview(PointerTracker tracker); + public void showGestureTrail(PointerTracker tracker); } public interface TimerProxy { @@ -283,6 +287,15 @@ public class PointerTracker { sAggregratedPointers.reset(); } + // TODO: To handle multi-touch gestures we may want to move this method to + // {@link PointerTrackerQueue}. + public static void drawGestureTrailForAllPointerTrackers(Canvas canvas, Paint paint) { + for (final PointerTracker tracker : sTrackers) { + tracker.mGestureStroke.drawGestureTrail(canvas, paint, tracker.getLastX(), + tracker.getLastY()); + } + } + private PointerTracker(int id, KeyEventHandler handler) { if (handler == null) throw new NullPointerException(); @@ -511,6 +524,9 @@ public class PointerTracker { public long getDownTime() { return mDownTime; } + public Rect getDrawingRect() { + return mGestureStroke.getDrawingRect(); + } private Key onDownKey(int x, int y, long eventTime) { mDownTime = eventTime; @@ -696,6 +712,7 @@ public class PointerTracker { if (key != null && mInGesture) { final InputPointers batchPoints = getIncrementalBatchPoints(); + mDrawingProxy.showGestureTrail(this); if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) { updateBatchInput(batchPoints); } @@ -868,6 +885,7 @@ public class PointerTracker { callListenerOnRelease(mCurrentKey, mCurrentKey.mCode, true); mCurrentKey = null; } + mDrawingProxy.showGestureTrail(this); return; } // This event will be recognized as a regular code input. Clear unused batch points so they diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 16c8410df..7b100d729 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -14,8 +14,12 @@ package com.android.inputmethod.keyboard.internal; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; import android.util.FloatMath; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.InputPointers; public class GestureStroke { @@ -35,6 +39,7 @@ public class GestureStroke { private int mMinGestureLength; private int mMinGestureLengthWhileInGesture; private int mMinGestureSampleLength; + private final Rect mDrawingRect = new Rect(); // TODO: Move some of these to resource. private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 1.0f; @@ -47,6 +52,10 @@ public class GestureStroke { private static final float DOUBLE_PI = (float)(2 * Math.PI); + // Fade based on number of gesture samples, see MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT + private static final int DRAWING_GESTURE_FADE_START = 10; + private static final int DRAWING_GESTURE_FADE_RATE = 6; + public GestureStroke(int pointerId) { mPointerId = pointerId; reset(); @@ -77,6 +86,7 @@ public class GestureStroke { mLastIncrementalBatchSize = 0; mLastPointTime = 0; mInputPointers.reset(); + mDrawingRect.setEmpty(); } private void updateLastPoint(final int x, final int y, final int time) { @@ -94,7 +104,6 @@ public class GestureStroke { } return; } - final int[] xCoords = mInputPointers.getXCoordinates(); final int[] yCoords = mInputPointers.getYCoordinates(); final int lastX = xCoords[size - 1]; @@ -102,6 +111,11 @@ public class GestureStroke { final float dist = getDistance(lastX, lastY, x, y); if (dist > mMinGestureSampleLength) { mInputPointers.addPointer(x, y, mPointerId, time); + if (mDrawingRect.isEmpty()) { + mDrawingRect.set(x - 1, y - 1, x + 1, y + 1); + } else { + mDrawingRect.union(x, y); + } mLength += dist; final float angle = getAngle(lastX, lastY, x, y); if (size > 1) { @@ -161,4 +175,27 @@ public class GestureStroke { } return diff; } + + public void drawGestureTrail(Canvas canvas, Paint paint, int lastX, int lastY) { + // TODO: These paint parameter interpolation should be tunable, possibly introduce an object + // that implements an interface such as Paint getPaint(int step, int strokePoints) + final int size = mInputPointers.getPointerSize(); + int[] xCoords = mInputPointers.getXCoordinates(); + int[] yCoords = mInputPointers.getYCoordinates(); + int alpha = Constants.Color.ALPHA_OPAQUE; + for (int i = size - 1; i > 0 && alpha > 0; i--) { + paint.setAlpha(alpha); + if (size - i > DRAWING_GESTURE_FADE_START) { + alpha -= DRAWING_GESTURE_FADE_RATE; + } + canvas.drawLine(xCoords[i - 1], yCoords[i - 1], xCoords[i], yCoords[i], paint); + if (i == size - 1) { + canvas.drawLine(lastX, lastY, xCoords[i], yCoords[i], paint); + } + } + } + + public Rect getDrawingRect() { + return mDrawingRect; + } } diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index e79db367c..1242967ad 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -19,6 +19,13 @@ package com.android.inputmethod.latin; import android.view.inputmethod.EditorInfo; public final class Constants { + public static final class Color { + /** + * The alpha value for fully opaque. + */ + public final static int ALPHA_OPAQUE = 255; + } + public static final class ImeOption { /** * The private IME option used to indicate that no microphone should be shown for a given From afed0567e91b9411fa61b03f5ac17812db56fd18 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Fri, 20 Jul 2012 17:51:52 +0900 Subject: [PATCH 19/36] Performance improvements - Avoid using iterators Change-Id: Iab604aa1ef67acf5d54208a6bc44635632845ae0 --- .../inputmethod/keyboard/PointerTracker.java | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 29aaa1b53..da4158295 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -238,13 +238,17 @@ public class PointerTracker { } public static void setKeyboardActionListener(KeyboardActionListener listener) { - for (final PointerTracker tracker : sTrackers) { + final int trackersSize = sTrackers.size(); + for (int i = 0; i < trackersSize; ++i) { + final PointerTracker tracker = sTrackers.get(i); tracker.mListener = listener; } } public static void setKeyDetector(KeyDetector keyDetector) { - for (final PointerTracker tracker : sTrackers) { + final int trackersSize = sTrackers.size(); + for (int i = 0; i < trackersSize; ++i) { + final PointerTracker tracker = sTrackers.get(i); tracker.setKeyDetectorInner(keyDetector); // Mark that keyboard layout has been changed. tracker.mKeyboardLayoutHasBeenChanged = true; @@ -254,7 +258,9 @@ public class PointerTracker { } public static void dismissAllKeyPreviews() { - for (final PointerTracker tracker : sTrackers) { + final int trackersSize = sTrackers.size(); + for (int i = 0; i < trackersSize; ++i) { + final PointerTracker tracker = sTrackers.get(i); tracker.getKeyPreviewText().setVisibility(View.INVISIBLE); tracker.setReleasedKeyGraphics(tracker.mCurrentKey); } @@ -263,7 +269,9 @@ public class PointerTracker { // TODO: To handle multi-touch gestures we may want to move this method to // {@link PointerTrackerQueue}. private static InputPointers getIncrementalBatchPoints() { - for (final PointerTracker tracker : sTrackers) { + final int trackersSize = sTrackers.size(); + for (int i = 0; i < trackersSize; ++i) { + final PointerTracker tracker = sTrackers.get(i); tracker.mGestureStroke.appendIncrementalBatchPoints(sAggregratedPointers); } return sAggregratedPointers; @@ -272,7 +280,9 @@ public class PointerTracker { // TODO: To handle multi-touch gestures we may want to move this method to // {@link PointerTrackerQueue}. private static InputPointers getAllBatchPoints() { - for (final PointerTracker tracker : sTrackers) { + final int trackersSize = sTrackers.size(); + for (int i = 0; i < trackersSize; ++i) { + final PointerTracker tracker = sTrackers.get(i); tracker.mGestureStroke.appendAllBatchPoints(sAggregratedPointers); } return sAggregratedPointers; @@ -281,7 +291,9 @@ public class PointerTracker { // TODO: To handle multi-touch gestures we may want to move this method to // {@link PointerTrackerQueue}. public static void clearBatchInputPointsOfAllPointerTrackers() { - for (final PointerTracker tracker : sTrackers) { + final int trackersSize = sTrackers.size(); + for (int i = 0; i < trackersSize; ++i) { + final PointerTracker tracker = sTrackers.get(i); tracker.mGestureStroke.reset(); } sAggregratedPointers.reset(); @@ -290,7 +302,9 @@ public class PointerTracker { // TODO: To handle multi-touch gestures we may want to move this method to // {@link PointerTrackerQueue}. public static void drawGestureTrailForAllPointerTrackers(Canvas canvas, Paint paint) { - for (final PointerTracker tracker : sTrackers) { + final int trackersSize = sTrackers.size(); + for (int i = 0; i < trackersSize; ++i) { + final PointerTracker tracker = sTrackers.get(i); tracker.mGestureStroke.drawGestureTrail(canvas, paint, tracker.getLastX(), tracker.getLastY()); } From c022a9f8d4aeb11f600f79408086633d777df363 Mon Sep 17 00:00:00 2001 From: Tom Ouyang Date: Fri, 20 Jul 2012 17:57:04 +0900 Subject: [PATCH 20/36] Fix bug where key previews do not show up. Bug: 6852705 Change-Id: Ib90107a5dde774b4d6c1e570629ce7f7eaff44bc --- java/src/com/android/inputmethod/keyboard/KeyboardView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 721931e98..62696219b 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -888,6 +888,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { super.onDraw(canvas); canvas.translate(mParams.mCoordinates[0], mParams.mCoordinates[1]); PointerTracker.drawGestureTrailForAllPointerTrackers(canvas, mGesturePaint); + canvas.translate(-mParams.mCoordinates[0], -mParams.mCoordinates[1]); } } From cc3500b0c8f11e8de1ad7376dda466d30637e462 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 20 Jul 2012 18:40:31 +0900 Subject: [PATCH 21/36] Disable gesture input detection when more keys keyboard is showing Bug: 6852441 --- java/src/com/android/inputmethod/keyboard/PointerTracker.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index da4158295..9b14210aa 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -671,7 +671,7 @@ public class PointerTracker { if (queue != null && queue.size() == 1) { mIsPossibleGesture = false; // A gesture should start only from the letter key. - if (sIsGestureEnabled && mIsAlphabetKeyboard && key != null + if (sIsGestureEnabled && mIsAlphabetKeyboard && !mIsShowingMoreKeysPanel && key != null && Keyboard.isLetterCode(key.mCode)) { mIsPossibleGesture = true; // TODO: pointer times should be relative to first down even in entire batch input @@ -915,8 +915,8 @@ public class PointerTracker { public void onShowMoreKeysPanel(int x, int y, KeyEventHandler handler) { abortBatchInput(); onLongPressed(); - onDownEvent(x, y, SystemClock.uptimeMillis(), handler); mIsShowingMoreKeysPanel = true; + onDownEvent(x, y, SystemClock.uptimeMillis(), handler); } public void onLongPressed() { From 62b8dddb6ddb057555a1665759f9cf197e480c9f Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 20 Jul 2012 17:49:06 +0900 Subject: [PATCH 22/36] Add gesture input enable settings Bug: 6845325 Change-Id: I3165465b0b280e32a9288da16abb75baa67288dc --- java/res/values/strings.xml | 5 +++++ java/res/xml/prefs.xml | 6 ++++++ .../inputmethod/keyboard/KeyboardSwitcher.java | 5 ++++- .../inputmethod/keyboard/KeyboardView.java | 7 +++++++ .../inputmethod/keyboard/LatinKeyboardView.java | 7 ++----- .../inputmethod/keyboard/PointerTracker.java | 16 +++++++--------- .../com/android/inputmethod/latin/Settings.java | 7 +++++++ .../inputmethod/latin/SettingsValues.java | 5 +++++ 8 files changed, 43 insertions(+), 15 deletions(-) diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index cf7c1c5bd..98c891894 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -111,6 +111,11 @@ Based on previous word + + Gesture input + + Input a word by tracing the letters of a word + %s : Saved diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml index bf8805875..4f11cb7e5 100644 --- a/java/res/xml/prefs.xml +++ b/java/res/xml/prefs.xml @@ -86,6 +86,12 @@ android:summary="@string/bigram_prediction_summary" android:persistent="true" android:defaultValue="true" /> + sTrackers = new ArrayList(); private static final InputPointers sAggregratedPointers = new InputPointers( @@ -191,18 +190,16 @@ public class PointerTracker { private final GestureStroke mGestureStroke; public static void init(boolean hasDistinctMultitouch, - boolean needsPhantomSuddenMoveEventHack, - boolean gestureInputEnabledByBuildConfig) { + boolean needsPhantomSuddenMoveEventHack) { if (hasDistinctMultitouch) { sPointerTrackerQueue = new PointerTrackerQueue(); } else { sPointerTrackerQueue = null; } sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack; - sConfigGestureInputEnabledByBuildConfig = gestureInputEnabledByBuildConfig; setParameters(LatinKeyboardView.PointerTrackerParams.DEFAULT); - updateGestureInputEnabledState(null); + updateGestureInputEnabledState(null, false /* gestureInputEnabled */); } public static void setParameters(LatinKeyboardView.PointerTrackerParams params) { @@ -211,8 +208,9 @@ public class PointerTracker { params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance); } - private static void updateGestureInputEnabledState(Keyboard keyboard) { - if (!sConfigGestureInputEnabledByBuildConfig + private static void updateGestureInputEnabledState(Keyboard keyboard, + boolean gestureInputEnabled) { + if (!gestureInputEnabled || AccessibilityUtils.getInstance().isTouchExplorationEnabled() || (keyboard != null && keyboard.mId.passwordInput())) { sIsGestureEnabled = false; @@ -245,7 +243,7 @@ public class PointerTracker { } } - public static void setKeyDetector(KeyDetector keyDetector) { + public static void setKeyDetector(KeyDetector keyDetector, boolean gestureInputEnabledByUser) { final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); @@ -254,7 +252,7 @@ public class PointerTracker { tracker.mKeyboardLayoutHasBeenChanged = true; } final Keyboard keyboard = keyDetector.getKeyboard(); - updateGestureInputEnabledState(keyboard); + updateGestureInputEnabledState(keyboard, gestureInputEnabledByUser); } public static void dismissAllKeyPreviews() { diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index a07d286b8..b67cc852e 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -70,6 +70,7 @@ public class Settings extends InputMethodSettingsFragment "pref_key_preview_popup_dismiss_delay"; public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict"; public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction"; + public static final String PREF_GESTURE_INPUT = "gesture_input"; public static final String PREF_VIBRATION_DURATION_SETTINGS = "pref_vibration_duration_settings"; public static final String PREF_KEYPRESS_SOUND_VOLUME = @@ -196,6 +197,12 @@ public class Settings extends InputMethodSettingsFragment textCorrectionGroup.removePreference(dictionaryLink); } + final boolean gestureInputEnabledByBuildConfig = res.getBoolean( + R.bool.config_gesture_input_enabled_by_build_config); + if (!gestureInputEnabledByBuildConfig) { + final Preference gestureInputPref = findPreference(PREF_GESTURE_INPUT); + miscSettings.removePreference(gestureInputPref); + } final boolean showUsabilityStudyModeOption = res.getBoolean(R.bool.config_enable_usability_study_mode_option) || ProductionFlag.IS_EXPERIMENTAL || ENABLE_EXPERIMENTAL_SETTINGS; diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 10025daf8..3ed981375 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -83,6 +83,7 @@ public class SettingsValues { @SuppressWarnings("unused") // TODO: Use this private final float mKeypressSoundVolumeRawValue; private final InputMethodSubtype[] mAdditionalSubtypes; + public final boolean mGestureInputEnabled; // From the input box private final InputAttributes mInputAttributes; @@ -169,6 +170,10 @@ public class SettingsValues { mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain); mAdditionalSubtypes = AdditionalSubtype.createAdditionalSubtypesArray( getPrefAdditionalSubtypes(prefs, res)); + final boolean gestureInputEnabledByBuildConfig = res.getBoolean( + R.bool.config_gesture_input_enabled_by_build_config); + mGestureInputEnabled = gestureInputEnabledByBuildConfig + && prefs.getBoolean(Settings.PREF_GESTURE_INPUT, true); mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; mSuggestionVisibility = createSuggestionVisibility(res); } From 7519091f7c15c50a9a1e50d82fa92400335852ec Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 20 Jul 2012 16:24:54 +0900 Subject: [PATCH 23/36] Use ResizableIntArray in GestureStroke Change-Id: I034e80df03c7c6c0895fdf2c03763627d410d425 --- .../keyboard/internal/GestureStroke.java | 52 ++++++++++--------- .../inputmethod/latin/InputPointers.java | 21 ++++++++ 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 7b100d729..6e3295e59 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -21,13 +21,15 @@ import android.util.FloatMath; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.InputPointers; +import com.android.inputmethod.latin.ResizableIntArray; public class GestureStroke { public static final int DEFAULT_CAPACITY = 128; private final int mPointerId; - // TODO: Replace this {@link InputPointers} with a set of {@link ScalableIntArray}s. - private final InputPointers mInputPointers = new InputPointers(DEFAULT_CAPACITY); + private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY); + private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY); + private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY); private float mLength; private float mAngle; private int mIncrementalRecognitionSize; @@ -85,7 +87,9 @@ public class GestureStroke { mIncrementalRecognitionSize = 0; mLastIncrementalBatchSize = 0; mLastPointTime = 0; - mInputPointers.reset(); + mEventTimes.setLength(0); + mXCoordinates.setLength(0); + mYCoordinates.setLength(0); mDrawingRect.setEmpty(); } @@ -96,26 +100,24 @@ public class GestureStroke { } public void addPoint(final int x, final int y, final int time, final boolean isHistorical) { - final int size = mInputPointers.getPointerSize(); + final int size = mEventTimes.getLength(); if (size == 0) { - mInputPointers.addPointer(x, y, mPointerId, time); + mEventTimes.add(time); + mXCoordinates.add(x); + mYCoordinates.add(y); if (!isHistorical) { updateLastPoint(x, y, time); } return; } - final int[] xCoords = mInputPointers.getXCoordinates(); - final int[] yCoords = mInputPointers.getYCoordinates(); - final int lastX = xCoords[size - 1]; - final int lastY = yCoords[size - 1]; + + final int lastX = mXCoordinates.get(size - 1); + final int lastY = mYCoordinates.get(size - 1); final float dist = getDistance(lastX, lastY, x, y); if (dist > mMinGestureSampleLength) { - mInputPointers.addPointer(x, y, mPointerId, time); - if (mDrawingRect.isEmpty()) { - mDrawingRect.set(x - 1, y - 1, x + 1, y + 1); - } else { - mDrawingRect.union(x, y); - } + mEventTimes.add(time); + mXCoordinates.add(x); + mYCoordinates.add(y); mLength += dist; final float angle = getAngle(lastX, lastY, x, y); if (size > 1) { @@ -142,15 +144,17 @@ public class GestureStroke { } public void appendAllBatchPoints(final InputPointers out) { - final int size = mInputPointers.getPointerSize(); - out.append(mInputPointers, mLastIncrementalBatchSize, size - mLastIncrementalBatchSize); - mLastIncrementalBatchSize = size; + appendBatchPoints(out, mEventTimes.getLength()); } public void appendIncrementalBatchPoints(final InputPointers out) { - out.append(mInputPointers, mLastIncrementalBatchSize, - mIncrementalRecognitionSize - mLastIncrementalBatchSize); - mLastIncrementalBatchSize = mIncrementalRecognitionSize; + appendBatchPoints(out, mIncrementalRecognitionSize); + } + + private void appendBatchPoints(final InputPointers out, final int size) { + out.append(mPointerId, mEventTimes, mXCoordinates, mYCoordinates, + mLastIncrementalBatchSize, size - mLastIncrementalBatchSize); + mLastIncrementalBatchSize = size; } private static float getDistance(final int p1x, final int p1y, @@ -179,9 +183,9 @@ public class GestureStroke { public void drawGestureTrail(Canvas canvas, Paint paint, int lastX, int lastY) { // TODO: These paint parameter interpolation should be tunable, possibly introduce an object // that implements an interface such as Paint getPaint(int step, int strokePoints) - final int size = mInputPointers.getPointerSize(); - int[] xCoords = mInputPointers.getXCoordinates(); - int[] yCoords = mInputPointers.getYCoordinates(); + final int size = mXCoordinates.getLength(); + int[] xCoords = mXCoordinates.getPrimitiveArray(); + int[] yCoords = mYCoordinates.getPrimitiveArray(); int alpha = Constants.Color.ALPHA_OPAQUE; for (int i = size - 1; i > 0 && alpha > 0; i--) { paint.setAlpha(alpha); diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java index 2bccdee48..cbc916a7e 100644 --- a/java/src/com/android/inputmethod/latin/InputPointers.java +++ b/java/src/com/android/inputmethod/latin/InputPointers.java @@ -76,6 +76,27 @@ public class InputPointers { mTimes.append(src.mTimes, startPos, length); } + /** + * Append the times, x-coordinates and y-coordinates in the specified {@link ResizableIntArray} + * to the end of this. + * @param pointerId the pointer id of the source. + * @param times the source {@link ResizableIntArray} to read the event times from. + * @param xCoordinates the source {@link ResizableIntArray} to read the x-coordinates from. + * @param yCoordinates the source {@link ResizableIntArray} to read the y-coordinates from. + * @param startPos the starting index of the data in {@code times} and etc. + * @param length the number of data to be appended. + */ + public void append(int pointerId, ResizableIntArray times, ResizableIntArray xCoordinates, + ResizableIntArray yCoordinates, int startPos, int length) { + if (length == 0) { + return; + } + mXCoordinates.append(xCoordinates, startPos, length); + mYCoordinates.append(yCoordinates, startPos, length); + mPointerIds.fill(pointerId, startPos, length); + mTimes.append(times, startPos, length); + } + public void reset() { final int defaultCapacity = mDefaultCapacity; mXCoordinates.reset(defaultCapacity); From 2d097dd5677b5d998a3ae270f906bd67f3b1effc Mon Sep 17 00:00:00 2001 From: Ying Wang Date: Fri, 20 Jul 2012 13:28:37 -0700 Subject: [PATCH 24/36] Import translations. DO NOT MERGE Change-Id: Ie15a817f13d3ce0a87eeff8334824fd3a337c1bc Auto-generated-cl: translation import --- java/res/values-af/strings.xml | 13 ++++++++++--- java/res/values-am/strings.xml | 13 ++++++++++--- java/res/values-ar/strings.xml | 13 ++++++++++--- java/res/values-be/strings.xml | 13 ++++++++++--- java/res/values-bg/strings.xml | 13 ++++++++++--- java/res/values-ca/strings.xml | 13 ++++++++++--- java/res/values-cs/strings.xml | 13 ++++++++++--- java/res/values-da/strings.xml | 13 ++++++++++--- java/res/values-de/strings.xml | 13 ++++++++++--- java/res/values-el/strings.xml | 13 ++++++++++--- java/res/values-en-rGB/strings.xml | 13 ++++++++++--- java/res/values-es-rUS/strings.xml | 13 ++++++++++--- java/res/values-es/strings.xml | 13 ++++++++++--- java/res/values-et/strings.xml | 13 ++++++++++--- java/res/values-fa/strings-appname.xml | 4 ++-- java/res/values-fa/strings.xml | 25 ++++++++++++++++--------- java/res/values-fi/strings.xml | 13 ++++++++++--- java/res/values-fr/strings.xml | 13 ++++++++++--- java/res/values-hi/strings.xml | 13 ++++++++++--- java/res/values-hr/strings.xml | 13 ++++++++++--- java/res/values-hu/strings.xml | 13 ++++++++++--- java/res/values-in/strings.xml | 13 ++++++++++--- java/res/values-it/strings.xml | 13 ++++++++++--- java/res/values-iw/strings.xml | 13 ++++++++++--- java/res/values-ja/strings.xml | 13 ++++++++++--- java/res/values-ko/strings.xml | 13 ++++++++++--- java/res/values-lt/strings.xml | 13 ++++++++++--- java/res/values-lv/strings.xml | 13 ++++++++++--- java/res/values-ms/strings.xml | 13 ++++++++++--- java/res/values-nb/strings.xml | 13 ++++++++++--- java/res/values-nl/strings.xml | 13 ++++++++++--- java/res/values-pl/strings.xml | 13 ++++++++++--- java/res/values-pt-rPT/strings.xml | 13 ++++++++++--- java/res/values-pt/strings.xml | 13 ++++++++++--- java/res/values-rm/strings.xml | 10 +++++++--- java/res/values-ro/strings.xml | 13 ++++++++++--- java/res/values-ru/strings.xml | 13 ++++++++++--- java/res/values-sk/strings.xml | 13 ++++++++++--- java/res/values-sl/strings.xml | 13 ++++++++++--- java/res/values-sr/strings.xml | 13 ++++++++++--- java/res/values-sv/strings.xml | 13 ++++++++++--- java/res/values-sw/strings.xml | 13 ++++++++++--- java/res/values-th/strings.xml | 13 ++++++++++--- java/res/values-tl/strings.xml | 13 ++++++++++--- java/res/values-tr/strings.xml | 13 ++++++++++--- java/res/values-uk/strings.xml | 13 ++++++++++--- java/res/values-vi/strings.xml | 13 ++++++++++--- java/res/values-zh-rCN/strings.xml | 13 ++++++++++--- java/res/values-zh-rTW/strings.xml | 13 ++++++++++--- java/res/values-zu/strings.xml | 13 ++++++++++--- 50 files changed, 495 insertions(+), 155 deletions(-) diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml index 65bcaf2e9..c499d850d 100644 --- a/java/res/values-af/strings.xml +++ b/java/res/values-af/strings.xml @@ -58,6 +58,10 @@ "Baie aggressief" "Stel volgende woord voor" "Gebaseer op vorige woord" + + + + "%s : Gestoor" "Gaan" "Volgende" @@ -106,11 +110,14 @@ "Invoertale" "let op die tydstempel in die loglêer" "Aangetekende tydstempel" - "Moenie hierdie sessie aanteken nie" - "Aktiveer sessie-rekordhouding" + + + + "Hou rekord van hele sessiegeskiedenis" "Sessie se loglêer uitgevee" - "Sessie se loglêer uitgevee" + + "Sessie se loglêer NIE uitgevee nie" "Rekord gehou van sessiegeskiedenis" "Fout: daar is NIE rekord gehou van die sessiegeskiedenis nie" diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml index 1f72f05fc..44f465660 100644 --- a/java/res/values-am/strings.xml +++ b/java/res/values-am/strings.xml @@ -58,6 +58,10 @@ "በጣም ቁጡ" "የቀጣይ ቃል አስተያየቶች" "በቀዳሚው ቃል ላይ የተመሠረተ" + + + + "%s : ተቀምጧል" "ሂድ" "በመቀጠል" @@ -106,11 +110,14 @@ "ቋንቋዎች አግቤት" "የምዝግብ ማስታወሻ ጊዜ ማህተም ማስታወሻ" "የጊዜ ማህተም ተመዝግቧል" - "ይህን ክፍለ ጊዜ እንዳትመዘግበው" - "ክፍለ ጊዜ ምዝገባን አንቃ" + + + + "ሙሉውን የክፍለጊዜ ታሪክ መዝግብ" "የክፍለጊዜ ምዝግብ ማስታወሻ በመሰረዝ ላይ" - "የክፍለ ጊዜ ምዝግብ ማስታወሻ ተሰርዟል" + + "የክፍለጊዜ ምዝግብ ማስታወሻ አልተሰረዘም" "የክፍለጊዜ ታሪክ ተመዝግቧል" "ስህተት፦ ክፍለጊዜ ታሪክ አልተመዘገበም" diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml index 9490704a6..c4211f594 100644 --- a/java/res/values-ar/strings.xml +++ b/java/res/values-ar/strings.xml @@ -58,6 +58,10 @@ "شديد الصرامة" "اقتراحات الكلمات التالية" "استنادًا إلى الكلمة السابقة" + + + + "%s : تم الحفظ" "تنفيذ" "التالي" @@ -106,11 +110,14 @@ "لغات الإدخال" "ملاحظة طابع زمني في سجل" "تم تسجيل الطابع الزمني" - "عدم تسجيل هذه الجلسة" - "تمكين تسجيل الجلسة" + + + + "تسجيل سجل الجلسة بالكامل" "جارٍ حذف سجل الجلسة" - "تم حذف سجل الجلسة" + + "لم يتم حذف سجل الجلسة" "تم تسجيل سجل الجلسة" "الخطأ: لم يتم تسجيل سجل الجلسة" diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml index 0d563f330..5b0ab8f94 100644 --- a/java/res/values-be/strings.xml +++ b/java/res/values-be/strings.xml @@ -58,6 +58,10 @@ "Вельмі агрэсіўны" "Падказкi для наступнага слова" "На аснове папярэдняга слова" + + + + "%s : Захаваныя" "Пачаць" "Далей" @@ -106,11 +110,14 @@ "Мовы ўводу" "Пазначыць час у гiсторыi" "Запiсаныя пазнакі" - "Не рэгістраваць гэты сеанс" - "Уключыць гiсторыю сеанса" + + + + "Запiс усёй гiсторыi сеанса" "Выдаленне гiсторыi сеанса" - "Гiсторыя сеанса выдалена" + + "Гiсторыя сеанса НЕ выдалена" "Гiсторыя сеанса запiсана" "Памылка: гiсторыя сеанса НЕ запiсана" diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml index 52c2fc6a8..5ccab5b71 100644 --- a/java/res/values-bg/strings.xml +++ b/java/res/values-bg/strings.xml @@ -58,6 +58,10 @@ "Много агресивно" "Предложения за следващата дума" "Въз основа на предишната дума" + + + + "%s : Запазено" "Старт" "Напред" @@ -106,11 +110,14 @@ "Входни езици" "Отбелязване на часа в рег. файл" "Часът е записан" - "Без регистр. на сесията" - "Записване на сесията: Активиране" + + + + "Цялата история на сесията: Записв." "Рег. файл на сесията се изтрива" - "Рег. файл на сесията е изтрит" + + "Рег. файл на сесията НЕ Е изтрит" "Историята на сесията е записана" "Грешка: Ист. на сесията НЕ е записана" diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml index 252441062..df85612d6 100644 --- a/java/res/values-ca/strings.xml +++ b/java/res/values-ca/strings.xml @@ -58,6 +58,10 @@ "Molt agressiu" "Suggeriments de paraula següent" "En funció de la paraula anterior" + + + + "%s: desada" "Vés" "Següent" @@ -106,11 +110,14 @@ "Idiomes d\'entrada" "Indica m. horària al reg." "Marca horària enregistrada" - "No enregistris la sessió" - "Activa el registre de sessió" + + + + "Registra tot l\'historial de sessió" "Suprimint registre de ses." - "Registre de ses. suprimit" + + "Registre de ses. NO sup." "Historial de sessió registrat" "Error: historial de sessió NO registrat" diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml index 4697d43ec..bcc970d26 100644 --- a/java/res/values-cs/strings.xml +++ b/java/res/values-cs/strings.xml @@ -58,6 +58,10 @@ "Velmi agresivní" "Návrhy dalšího slova" "Na základě předchozího slova" + + + + "%s: Uloženo" "Přejít" "Další" @@ -106,11 +110,14 @@ "Vstupní jazyky" "Uložit čas do protokolu" "Časové razítko vloženo" - "Neprotokolovat relaci" - "Povolit protokolování relace" + + + + "Protokolovat celou historii relace" "Mazání protokolu relace" - "Protokol relace smazán" + + "Protokol relace nesmazán" "Historie relace protokolována" "Historie relace NENÍ protokolována" diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index 3b194ec93..06dc29a4b 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -58,6 +58,10 @@ "Meget aggressiv" "Forslag til næste ord" "Baseret på tidligere ord" + + + + "%s: Gemt" "Gå" "Næste" @@ -106,11 +110,14 @@ "Inputsprog" "Notér tidsstempel i log" "Noteret tidsstempel" - "Logfør ikke denne session" - "Aktivér logføring af sessioner" + + + + "Logfør hele sessionshistorikken" "Sletter sessionslogfil" - "Sessionslogfil slettet" + + "Sessionslog IKKE slettet" "Sessionshistorikken er logført" "Fejl: sessionshistorik IKKE logført" diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index 9381ecb56..a072847b1 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -58,6 +58,10 @@ "Sehr stark" "Vorschläge für nächstes Wort" "Auf Grundlage des vorherigen Wortes" + + + + "%s: gespeichert" "Los" "Weiter" @@ -106,11 +110,14 @@ "Eingabesprachen" "Zeitstempel im Protokoll" "Zeitstempel aufgenommen" - "Nicht protokollieren" - "Sitzungsprotokoll aktivieren" + + + + "Gesamten Sitzungsverlauf speichern" "Protokoll wird gelöscht..." - "Protokoll gelöscht" + + "Protokoll NICHT gelöscht" "Sitzungsverlauf gespeichert" "Sitzungsverlauf NICHT gespeichert" diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml index 8125bc4bd..ad89781c4 100644 --- a/java/res/values-el/strings.xml +++ b/java/res/values-el/strings.xml @@ -58,6 +58,10 @@ "Πολύ επιθετική" "Προτάσεις επόμενων λέξεων" "Βάσει προηγούμενης λέξης" + + + + "%s : Αποθηκεύτηκε" "Μετ." "Επόμενο" @@ -106,11 +110,14 @@ "Γλώσσες εισόδου" "Χρόνος στο αρχείο καταγρ." "Καταγεγραμμένος χρόνος" - "Χωρίς αρχείο καταγραφής" - "Ενεργοποίηση καταγραφής περιόδου" + + + + "Καταγραφή ολόκλ. ιστορικού περιόδου" "Διαγραφή αρχείου σύνδεσης" - "Αρχείο καταγρ. διαγράφηκε" + + "Αρχείο καταγρ. ΔΕΝ διαγρ." "Το ιστορικό περιόδου καταγράφηκε" "Σφάλμα: ΜΗ καταγραφή ιστορ. περιόδου" diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml index 9697cc792..61a6a0e21 100644 --- a/java/res/values-en-rGB/strings.xml +++ b/java/res/values-en-rGB/strings.xml @@ -58,6 +58,10 @@ "Very aggressive" "Next word suggestions" "Based on previous word" + + + + "%s : Saved" "Go" "Next" @@ -106,11 +110,14 @@ "Input languages" "Note timestamp in log" "Recorded timestamp" - "Do not log this session" - "Enable session logging" + + + + "Log whole session history" "Deleting session log" - "Session log deleted" + + "Session log NOT deleted" "Session history logged" "Error: Session history NOT logged" diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index e5d5548ae..fae75c5ee 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -58,6 +58,10 @@ "Muy agresivo" "Sugerencias de palabra siguiente" "Según la palabra anterior" + + + + "%s: guardada" "Ir" "Siguiente" @@ -106,11 +110,14 @@ "Idiomas de entrada" "Marcar tiempo en registro" "Marca tiempo registrada" - "No registrar esta sesión" - "Activar registro de sesión" + + + + "Registrar hist. de sesión completo" "Eliminando registro" - "Registro sesión eliminado" + + "NO se eliminó el registro" "Se registró el historial de sesión." "Error al registrar hist. de sesión" diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml index 7625003c8..15c46d0f3 100644 --- a/java/res/values-es/strings.xml +++ b/java/res/values-es/strings.xml @@ -58,6 +58,10 @@ "Muy agresiva" "Sugerir siguiente palabra" "Según la palabra anterior" + + + + "%s: guardada" "Ir" "Sig." @@ -106,11 +110,14 @@ "Idiomas" "Anotar marca tiempo en registro" "Marca de tiempo registrada" - "No registrar esta sesión" - "Habilitar registro de sesión" + + + + "Registrar historial de sesión" "Eliminando registro..." - "Registro eliminado" + + "Registro no eliminado" "Historial de sesión registrado" "Error: historial NO registrado" diff --git a/java/res/values-et/strings.xml b/java/res/values-et/strings.xml index 6dd86aaf2..8b431fadc 100644 --- a/java/res/values-et/strings.xml +++ b/java/res/values-et/strings.xml @@ -58,6 +58,10 @@ "Väga agressiivne" "Järgmise sõna soovitused" "Eelmise sõna põhjal" + + + + "%s : salvestatud" "Mine" "Edasi" @@ -106,11 +110,14 @@ "Sisestuskeeled" "Märgi ajatempel logisse" "Salvestatud ajatemplid" - "Ära logi seda seanssi" - "Luba seansi logimine" + + + + "Logi kogu seansi ajalugu" "Seansi logi kustutamine" - "Seansi logi kustutatud" + + "Seansi logi EI kustutatud" "Seansi ajalugu on logitud" "Viga: seansi ajalugu EI logitud" diff --git a/java/res/values-fa/strings-appname.xml b/java/res/values-fa/strings-appname.xml index 366d56d92..ba2a76ff1 100644 --- a/java/res/values-fa/strings-appname.xml +++ b/java/res/values-fa/strings-appname.xml @@ -20,8 +20,8 @@ - "صفحه کلید Android" + "صفحه‌کلید Android" "غلط‌گیر املای Android" - "تنظیمات صفحه کلید Android" + "تنظیمات صفحه‌کلید Android" "تنظیمات غلط‌‌ گیر املا" diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index 9896864ec..346757847 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -20,7 +20,7 @@ - "صفحه کلید (Android (AOSP" + "صفحه‌کلید (Android (AOSP" "گزینه‌های ورودی" "فرمان‌های گزارش‌گیری پژوهش" "غلط‌گیر املای Android (AOSP)" @@ -58,6 +58,10 @@ "بسیار پرخاشگرانه" "پیشنهادات کلمه بعدی" "بر اساس کلمه قبلی" + + + + "%s : ذخیره شد" "برو" "بعدی" @@ -100,21 +104,24 @@ "حالت تلفن" "حالت نمادهای تلفن" "کلید ورودی صدا" - "در صفحه کلید اصلی" - "در صفحه کلید نمادها" + "در صفحه‌کلید اصلی" + "در صفحه‌کلید نمادها" "خاموش" - "میکروفن در صفحه کلید اصلی" - "میکروفن در صفحه کلید نمادها" + "میکروفن در صفحه‌کلید اصلی" + "میکروفن در صفحه‌کلید نمادها" "ورودی صدا غیرفعال است" "پیکربندی روش‌های ورودی" "زبان‌های ورودی" "یادداشت مهر زمان در گزارش" "مهر زمان ثبت شده" - "از این جلسه گزارش‌گیری نشود" - "تواناساختن گزارش جلسه" + + + + "گزارش همه سابقه جلسه" "در حال حذف گزارش جلسه" - "گزارش جلسه حذف شد" + + "گزارش جلسه حذف نشد" "سابقه جلسه گزارش شد" "خطا: سابقه جلسه گزارش نشد" @@ -124,7 +131,7 @@ "دیکشنری موجود است" "فعال کردن بازخورد کاربر" "با ارسال خودکار آمارهای کاربرد و گزارش‌های خرابی به Google، به بهبود این ویرایشگر روش ورودی کمک کنید." - "طرح زمینه صفحه کلید" + "طرح زمینه صفحه‌کلید" "انگلیسی (بریتانیا)" "انگلیسی (امریکا)" "انگلیسی (انگلستان) (%s)" diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml index b19b0624e..d7d983488 100644 --- a/java/res/values-fi/strings.xml +++ b/java/res/values-fi/strings.xml @@ -58,6 +58,10 @@ "Hyvin aggressiivinen" "Seuraavan sanan ehdotukset" "Perustuu edelliseen sanan" + + + + "%s : Tallennettu" "Siirry" "Seur." @@ -106,11 +110,14 @@ "Syöttökielet" "Merkitse aikaleima lokiin" "Merkitty aikaleima" - "Älä tallenna tätä käyttök." - "Ota käyttökertaloki käyttöön" + + + + "Kirjaa koko käyttökerran historia" "Poistetaan lokia" - "Käyttökertaloki poistettu" + + "Lokia EI poistettu" "Käyttökerran historia kirjattu" "Virhe: käyttök. historiaa EI kirj." diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml index 99c6e0337..22b4f79d2 100644 --- a/java/res/values-fr/strings.xml +++ b/java/res/values-fr/strings.xml @@ -58,6 +58,10 @@ "Très exigeante" "Suggestions pour le mot suivant" "Suggestions basées sur le mot précédent" + + + + "%s : enregistré" "OK" "Suiv." @@ -106,11 +110,14 @@ "Langues de saisie" "Noter heure dans journal" "Heure enregistrée." - "Ne pas enregistrer session" - "Activer l\'enregistrement de session" + + + + "Enregistrer historique de la session" "Suppr. journal session…" - "Journal session supprimé." + + "Journal session PAS suppr." "Historique de la session enregistré." "Historique session NON enregistré." diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml index dfed256b1..639b2564a 100644 --- a/java/res/values-hi/strings.xml +++ b/java/res/values-hi/strings.xml @@ -58,6 +58,10 @@ "बहुत तीव्र" "अगले शब्द सुझाव" "पिछले शब्द के आधार पर" + + + + "%s: सहेजा गया" "जाएं" "अगला" @@ -106,11 +110,14 @@ "इनपुट भाषा" "लॉग में टाइमस्‍टैम्‍प नोट करें" "रिकॉर्ड किया गया टाइमस्टैम्प" - "इस सत्र को लॉग न करें" - "सत्र लॉगिंग सक्षम करें" + + + + "संपूर्ण सत्र इतिहास को लॉग करें" "सत्र लॉग हटाया जा रहा है" - "सत्र लॉग हटाया गया" + + "सत्र लॉग हटाया नहीं गया" "सत्र इतिहास को लॉग लिया गया" "त्रुटि: सत्र इतिहास लॉग नहीं किया" diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml index 5a1eefc5e..10cbdf026 100644 --- a/java/res/values-hr/strings.xml +++ b/java/res/values-hr/strings.xml @@ -58,6 +58,10 @@ "Vrlo agresivno" "Prijedlozi za sljedeću riječ" "Na temelju prethodne riječi" + + + + "%s : Spremljeno" "Idi" "Dalje" @@ -106,11 +110,14 @@ "Jezici unosa" "Zabilježi razdoblje u dnevniku" "Zabilježeno razdoblje" - "Ne bilježi ovu sesiju" - "Omogući bilježenje sesije" + + + + "Bilježi cijelu povijest sesije" "Brisanje dnevnika sesije" - "Izbrisan dnevnik sesije" + + "Dnevnik sesije NIJE izbrisan" "Povijest sesije zabilježena je" "Pogr.: pov. sesije NIJE zabilježena" diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml index cd9893e62..40238eecc 100644 --- a/java/res/values-hu/strings.xml +++ b/java/res/values-hu/strings.xml @@ -58,6 +58,10 @@ "Nagyon agresszív" "Következő szóra vonatkozó javaslatok" "Az előző szó alapján" + + + + "%s : mentve" "Ugrás" "Tovább" @@ -106,11 +110,14 @@ "Beviteli nyelvek" "Időbélyegző naplózáskor" "Rögzített időbélyegzők" - "Ne naplózza" - "Programfolyamat-napló engedélyezése" + + + + "Összes előzmény naplózása" "Napló törlése folyamatban" - "Napló törölve" + + "Napló NINCS törölve" "Programfolyamatok naplózva" "Hiba: előzmények NINCSENEK naplózva" diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index 31c901461..4a765d678 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -58,6 +58,10 @@ "Sangat agresif" "Saran kata berikutnya" "Berdasarkan kata sebelumnya" + + + + "%s : Telah disimpan" "Buka" "Berikutnya" @@ -106,11 +110,14 @@ "Bahasa masukan" "Catat cap waktu di log" "Cap waktu yang direkam" - "Jangan simpan log sesi ini" - "Aktifkan log sesi" + + + + "Catat seluruh riwayat sesi" "Menghapus log sesi" - "Log sesi dihapus" + + "Log sesi BELUM dihapus" "Riwayat sesi dicatat" "Kesalahan: Riwayat sesi TAK dicatat" diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml index 6522bb91c..a879fb655 100644 --- a/java/res/values-it/strings.xml +++ b/java/res/values-it/strings.xml @@ -58,6 +58,10 @@ "Massima" "Suggerimenti parola successiva" "In base alla parola precedente" + + + + "%s : parola salvata" "Vai" "Avanti" @@ -106,11 +110,14 @@ "Lingue comandi" "Indicazione temporale log" "Indicazione temporale registrata" - "Non registrare la sessione" - "Attiva registrazione sessioni" + + + + "Registra intera cronologia sessione" "Eliminazione log sessione" - "Log di sessione eliminato" + + "Log sessione non eliminato" "Cronologia sessione registrata" "Errore: cronologia NON registrata" diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml index 29bd615b4..d3d318a55 100644 --- a/java/res/values-iw/strings.xml +++ b/java/res/values-iw/strings.xml @@ -58,6 +58,10 @@ "מחמיר מאוד" "הצעות המילה הבאה" "בהתבסס על המילה הקודמת" + + + + "%s : נשמרה" "בצע" "הבא" @@ -106,11 +110,14 @@ "שפות קלט" "ציין חותמת זמן ביומן" "חותמת זמן מתועדת" - "אל תרשום הפעלה זו ביומן" - "הפעל רישום הפעלה" + + + + "רשום את כל היסטוריית ההפעלה" "מוחק יומן הפעלה" - "יומן הפעלה נמחק" + + "יומן הפעלה לא נמחק" "היסטוריית הפעלה נרשמה" "שגיאה: היסטוריית ההפעלה לא נרשמה" diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml index 66ba90094..eb75c9479 100644 --- a/java/res/values-ja/strings.xml +++ b/java/res/values-ja/strings.xml @@ -58,6 +58,10 @@ "最も強い" "次の入力候補" "前の語句に基づいた入力候補表示" + + + + "%s:保存しました" "実行" "次へ" @@ -106,11 +110,14 @@ "入力言語" "タイムスタンプを記録" "タイムスタンプ記録済み" - "セッションを記録しない" - "セッションログを有効にする" + + + + "セッション履歴全体を記録する" "セッションログ削除中" - "セッションログ削除済み" + + "セッションログ未削除" "セッション履歴が記録されました" "エラー: 履歴が記録されていません" diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index 88cbdaa83..10ff28004 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -58,6 +58,10 @@ "강" "다음 검색어 추천" "이전 단어에 기반한 추천" + + + + "%s: 저장됨" "이동" "다음" @@ -106,11 +110,14 @@ "입력 언어" "로그에 타임스탬프를 기록" "타임스탬프를 기록함" - "이 세션을 로그하지 마세요." - "세션 로깅 사용 설정" + + + + "전체 세션 기록 로그" "세션 로그 삭제" - "세션 로그가 삭제됨" + + "세션 로그가 삭제되지 않음" "세션 기록이 로그되었습니다." "오류: 세션 기록을 로그하지 못했습니다." diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml index 7d7b54c8f..8f5c0de68 100644 --- a/java/res/values-lt/strings.xml +++ b/java/res/values-lt/strings.xml @@ -58,6 +58,10 @@ "Labai agresyviai" "Kito žodžio pasiūlymai" "Pagal ankstesnį žodį" + + + + "%s: išsaugota" "Pradėti" "Kitas" @@ -106,11 +110,14 @@ "Įvesties kalbos" "Pažym. laiko žymę žurnale" "Įrašyta laiko žymė" - "Neįrašyti šios sesijos" - "Įgalinti sesijos įrašymą į žurnalą" + + + + "Įrašyti sesijos istoriją į žurnalą" "Ištrinam. sesijos žurnal." - "Sesijos žurnalas ištrint." + + "Sesij. žurnal. NEIŠTRINT." "Sesijos istorija įrašyta į žurnalą" "Klaida: sesijos istorija NEĮRAŠYTA" diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml index e5e61e39c..07ac5d252 100644 --- a/java/res/values-lv/strings.xml +++ b/java/res/values-lv/strings.xml @@ -58,6 +58,10 @@ "Ļoti radikāla" "Nākamie vārdu ieteikumi" "Pamatojoties uz iepriekšējo vārdu" + + + + "%s: saglabāts" "Sākt" "Tālāk" @@ -106,11 +110,14 @@ "Ievades valodas" "Atzīmēt laiksp. žurnālā" "Laikspied. ir reģistrēts." - "Nereģistrēt šo sesiju" - "Sesijas reģistrēšanas iespējošana" + + + + "Visas sesijas vēstures reģistrēšana" "Not. sesijas žurn. dzēš." - "Sesijas žurnāls ir dzēsts" + + "Sesijas žurn. NAV dzēsts" "Sesijas vēsture ir reģistrēta." "Kļūda: sesijas vēsture NAV reģistr." diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml index bd961025c..661063d8e 100644 --- a/java/res/values-ms/strings.xml +++ b/java/res/values-ms/strings.xml @@ -58,6 +58,10 @@ "Sangat agresif" "Cadangan perkataan seterusnya" "Berdasarkan perkataan sebelumnya" + + + + "%s : Disimpan" "Pergi" "Seterusnya" @@ -106,11 +110,14 @@ "Bahasa input" "Tanda cap waktu dalam log" "Cap waktu direkodkan" - "Jangan log sesi ini" - "Dayakan log sesi" + + + + "Log sejarah seluruh sesi" "Memadam log sesi" - "Log sesi dipadam" + + "Log sesi TIDAK dipadam" "Sejarah sesi dilog" "Ralat: Sejarah sesi TIDAK dilog" diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml index 5f7519a8d..7415e7631 100644 --- a/java/res/values-nb/strings.xml +++ b/java/res/values-nb/strings.xml @@ -58,6 +58,10 @@ "Veldig aggressiv" "Forslag til neste ord" "Basert på det forrige ordet" + + + + "%s: Lagret" "Utfør" "Neste" @@ -106,11 +110,14 @@ "Inndataspråk" "Notér tidsstempel i logg" "Registrerte tidsstempel" - "Ikke loggfør denne økten" - "Aktiver lagring av økter" + + + + "Lagre hele øktloggen" "Sletter øktloggen" - "Øktloggen ble slettet" + + "Øktloggen ble IKKE slettet" "Øktloggen er lagret" "Feil: Øktloggen er IKKE lagret" diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml index 19c9eda1c..e7f3f0704 100644 --- a/java/res/values-nl/strings.xml +++ b/java/res/values-nl/strings.xml @@ -58,6 +58,10 @@ "Zeer agressief" "Suggesties voor volgend woord" "Op basis van het vorige woord" + + + + "%s: opgeslagen" "Start" "Verder" @@ -106,11 +110,14 @@ "Invoertalen" "Tijdstempel opnemen in logbestand" "Opgenomen tijdstempel" - "Sessie niet registreren" - "Sessieregistratie inschakelen" + + + + "Hele sessiegeschiedenis registreren" "Sessielogbestand verwijderen" - "Sessielogbestand verwijderd" + + "Sessielogbestand NIET verwijderd" "Sessiegeschiedenis geregistreerd" "Fout: sessiegesch. NIET geregistr." diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index 7ecaf4fc5..58d4f56bc 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -58,6 +58,10 @@ "Bardzo agresywna" "Sugestie kolejnych słów" "Na podstawie poprzedniego słowa" + + + + "%s : Zapisano" "OK" "Dalej" @@ -106,11 +110,14 @@ "Języki wprowadzania" "Znacznik czasu uwagi w dzienniku" "Zapisano znacznik czasu" - "Nie rejestruj tej sesji" - "Włącz rejestrowanie sesji" + + + + "Rejestruj całą historię sesji" "Usuwanie dziennika sesji" - "Usunięto dziennik sesji" + + "Dziennik sesji NIEUSUNIĘTY" "Historia sesji została zarejestrowana" "Błąd: historia sesji niezarejestrowana" diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml index 4ffc7cac0..47c8ab719 100644 --- a/java/res/values-pt-rPT/strings.xml +++ b/java/res/values-pt-rPT/strings.xml @@ -58,6 +58,10 @@ "Muito agressivo" "Sugestões da palavra seguinte" "Com base na palavra anterior" + + + + "%s: guardada" "OK" "Avançar" @@ -106,11 +110,14 @@ "Idiomas de entrada" "Anotar car. data no reg." "Carimbo de data gravado" - "Não registar esta sessão" - "Ativar registos de sessão" + + + + "Registar hist. de sessão completo" "A eliminar reg. da sessão" - "Reg. de sessão eliminado" + + "Reg. de sessão NÃO elim." "Histórico de sessão registado" "Erro: hist. de sessão NÃO regist." diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml index d1ce05aa3..5ff8b4abf 100644 --- a/java/res/values-pt/strings.xml +++ b/java/res/values-pt/strings.xml @@ -58,6 +58,10 @@ "Muito agressivo" "Sugestões para a palavra seguinte" "Com base na palavra anterior" + + + + "%s : Salvo" "Ir" "Avançar" @@ -106,11 +110,14 @@ "Idiomas de entrada" "Indicar data/hora no reg." "Data/hora registrada" - "Não registrar esta sessão" - "Ativar registro de sessão" + + + + "Regist. histórico de sessão inteiro" "Excluindo reg. de sessão" - "Registro excluído" + + "Registro NÃO excluído" "Histórico de sessão registrado" "Histórico de sessão NÃO registrado" diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml index 670ce57f1..80ec92757 100644 --- a/java/res/values-rm/strings.xml +++ b/java/res/values-rm/strings.xml @@ -92,6 +92,10 @@ + + + + "%s : Memorisà" "Dai" "Vinavant" @@ -182,15 +186,15 @@ - + - + - + diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml index b7f491902..c5925031b 100644 --- a/java/res/values-ro/strings.xml +++ b/java/res/values-ro/strings.xml @@ -58,6 +58,10 @@ "Foarte exigentă" "Sugestii pentru cuvântul următor" "Bazate pe cuvântul precedent" + + + + "%s: salvat" "OK" "Înainte" @@ -106,11 +110,14 @@ "Selectaţi limba" "Înreg. marc. temp. jurnal" "Marcaj temporal înregis." - "Nu înregistraţi sesiunea" - "Activaţi înregistrarea sesiunii" + + + + "Înregistraţi istoric sesiune întreg" "Se șterge jurnal sesiune" - "Jurnal de sesiune șters" + + "Jurnal sesiune neşters" "Istoric sesiune înregistrat" "Eroare: istoric sesiune neînregis." diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index f3ef7aae0..6a156ed53 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -58,6 +58,10 @@ "Очень активно" "Подсказка для следующего слова" "Подсказки, основанные на предыдущих словах" + + + + "%s: сохранено" "Поиск" "Далее" @@ -106,11 +110,14 @@ "Языки ввода" "Закладка в журнале" "Закладка сохранена" - "Не сохранять этот сеанс" - "Включить ведение журнала сеансов" + + + + "Записать всю историю сеанса" "Удаление…" - "Запись сеанса удалена" + + "Запись сеанса НЕ удалена" "История сеанса записана" "Не удалось записать историю сеанса" diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml index efd6ea143..7055b6811 100644 --- a/java/res/values-sk/strings.xml +++ b/java/res/values-sk/strings.xml @@ -58,6 +58,10 @@ "Veľmi agresívne" "Návrhy ďalšieho slova" "Na základe predchádzajúceho slova" + + + + "%s : Uložené" "Hľadať" "Ďalej" @@ -106,11 +110,14 @@ "Jazyky vstupu" "Časová pečiatka denníka" "Časová pečiatka zaznamenaná" - "Neukl. reláciu do denníka" - "Povoliť zápis relácií do denníkov" + + + + "Celá história relácií v denníkoch" "Odstraň. denníka relácie" - "Denník relácie odstránený" + + "Denník relácie NIE JE odstr." "Hist. relácií zapísaná do denníkov" "Chyba: Hist. relácií NIE JE zapísaná" diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml index ad3e7395f..8df5c3874 100644 --- a/java/res/values-sl/strings.xml +++ b/java/res/values-sl/strings.xml @@ -58,6 +58,10 @@ "Zelo strogo" "Predlogi za naslednjo besedo" "Na podlagi prejšnje besede" + + + + "%s: shranjeno" "Pojdi" "Naprej" @@ -106,11 +110,14 @@ "Jeziki vnosa" "V dnev. zabeleži čas. žig" "Časovni žig zabeležen" - "Brez dnevnika za to sejo" - "Omogoči zapisovanje seje v dnevnik" + + + + "Zapiši celotno zgodovino seje v dnevnik" "Brisanje seje dnevnika" - "Dnevnik seje izbrisan" + + "Dnevnik seje NI izbrisan" "Zgodovina seje zapisana v dnevnik" "Napaka: zgodovina seje NI zapisana v dnevnik" diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index d1c440d07..48fbc2545 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -58,6 +58,10 @@ "Веома агресивно" "Предлози за следећу реч" "На основу претходне речи" + + + + "%s : Сачувано" "Иди" "Следеће" @@ -106,11 +110,14 @@ "Језици за унос" "Наведи временску ознаку у евиденцији" "Снимљена временска ознака" - "Не евидентирај ову сесију" - "Омогући евидентирање сесија" + + + + "Евидентирај целу историју сесија" "Брисање евиденције сесије" - "Евиденција сесије је обрисана" + + "Евиденција сесије НИЈЕ избрисана" "Историја сесија је евидентирана" "Грешка: Историја сесија НИЈЕ евидентирана" diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml index 0da487723..9af71b086 100644 --- a/java/res/values-sv/strings.xml +++ b/java/res/values-sv/strings.xml @@ -58,6 +58,10 @@ "Mycket aggressivt" "Föreslå nästa ord" "Baserat på föregående ord" + + + + "%s: sparat" "Kör" "Nästa" @@ -106,11 +110,14 @@ "Inmatningsspråk" "Markera tidpunkt i loggen" "Tidpunkten har sparats" - "Logga inte detta besök" - "Aktivera sessionsloggning" + + + + "Logga hela sessionshistoriken" "Besöksloggen tas bort" - "Besöksloggen togs bort" + + "Besöksloggen togs EJ bort" "Sessionshistoriken har loggats" "Fel: historiken har INTE loggats" diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index 373d294c2..7c343443b 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -58,6 +58,10 @@ "Changamfu zaidi" "Mapendekezo ya neno lifuatalo" "Kulingana na neno la awali" + + + + "%s : Imehifadhiwa" "Nenda" "Ifuatayo" @@ -106,11 +110,14 @@ "Lugha za uingizaji" "Dokeza mhuri wa muda kwenye kumbukumbu" "Mhuri wa muda uliorekodiwa" - "Usihifadhi kumbukumbu za kipindi hiki" - "Wezesha kuingia kwenye kipindi" + + + + "Ingia kwenye historia ya kipindi kizima" "Inafuta kumbukumbu za kipindi" - "Kumbukumbu za kipindi zimefutwa" + + "Kumbukumbu za kipindi HAZIJAFUTWA" "Historia ya kipindi imeingia" "Hitilafu: Historia ya kipindi HAIJAINGIWA" diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml index 329d3e542..8739bab4d 100644 --- a/java/res/values-th/strings.xml +++ b/java/res/values-th/strings.xml @@ -58,6 +58,10 @@ "เข้มงวดมาก" "คำแนะนำสำหรับคำถัดไป" "ตามคำก่อนหน้า" + + + + "%s : บันทึกแล้ว" "ไป" "ถัดไป" @@ -106,11 +110,14 @@ "ภาษาในการป้อนข้อมูล" "จดเวลาบันทึกไว้ในบันทึก" "บันทึกเวลาบันทึกแล้ว" - "อย่าบันทึกเซสชันนี้" - "เปิดใช้งานการบันทึกเซสชัน" + + + + "บันทึกประวัติเซสชันทั้งหมด" "กำลังลบบันทึกเซสชัน" - "ลบบันทึกเซสชันแล้ว" + + "บันทึกเซสชันไม่ถูกลบ" "บันทึกประวัติเซสชันแล้ว" "ข้อผิดพลาด: ไม่ได้บันทึกประวัติเซสชัน" diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml index a54a90887..f9471cf85 100644 --- a/java/res/values-tl/strings.xml +++ b/java/res/values-tl/strings.xml @@ -58,6 +58,10 @@ "Napaka-agresibo" "Mga suhestiyon sa susunod na salita" "Batay sa nakaraang salita" + + + + "%s : Na-save" "Punta" "Susunod" @@ -106,11 +110,14 @@ "Mag-input ng mga wika" "Tandaan timestamp sa log" "Na-record na timestamp" - "Huwag i-log ang session" - "Paganahin ang pag-log in sa session" + + + + "I-log buong kasaysayan ng session" "Tinatanggl log ng session" - "Tinanggal log ng session" + + "HND ntnggl log ng session" "Na-log ang kasaysayan ng session" "Error: DI na-log kasaysayan session" diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml index 2f5737726..e71b95522 100644 --- a/java/res/values-tr/strings.xml +++ b/java/res/values-tr/strings.xml @@ -58,6 +58,10 @@ "Çok geniş ölçekte" "Sonraki kelime önerileri" "Önceki kelimeye dayanarak" + + + + "%s : Kaydedildi" "Git" "İleri" @@ -106,11 +110,14 @@ "Giriş dilleri" "Günlüğe zaman damgası koy" "Zaman damgası kaydedildi" - "Bu oturumu günlüğe kaydetme" - "Oturumun günlüğe kaydını etkinleştir" + + + + "Tüm oturum geçmişini günlüğe kaydet" "Oturum günlüğü siliniyor" - "Oturum günlüğü silindi" + + "Oturum günlüğü SİLİNMEDİ" "Oturum geçmişi günlüğe kaydedildi" "Hata: Oturum geçmişi KAYDEDİLMEDİ" diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml index d2f9091f6..3bc99b8d1 100644 --- a/java/res/values-uk/strings.xml +++ b/java/res/values-uk/strings.xml @@ -58,6 +58,10 @@ "Дуже активне" "Пропозиції наступного слова" "На основі попереднього слова" + + + + "%s : збережено" "Іти" "Далі" @@ -106,11 +110,14 @@ "Мови вводу" "Мітка часу в журналі" "Записана мітка часу" - "Не реєструвати цю сесію" - "Увімкнути запис журналу сеансу" + + + + "Записувати історію всього сеансу" "Видалення журналу сесії" - "Журнал сесії видалено" + + "Журнал сесії НЕ видалено" "Історію сеансу записано" "Помилка. Історію сеансу НЕ записано" diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml index 94636aaff..be76ede4a 100644 --- a/java/res/values-vi/strings.xml +++ b/java/res/values-vi/strings.xml @@ -58,6 +58,10 @@ "Rất linh hoạt" "Đề xuất từ tiếp theo" "Dựa trên từ trước đó" + + + + "%s : Đã lưu" "Tìm" "Tiếp theo" @@ -106,11 +110,14 @@ "Ngôn ngữ nhập" "Dấu thời gian ghi chú trong nhật ký" "Dấu thời gian đã ghi" - "Không ghi nhật ký phiên này" - "Bật ghi nhật ký phiên" + + + + "Ghi nhật ký toàn bộ lịch sử phiên" "Đang xóa nhật ký phiên" - "Đã xóa nhật ký phiên" + + "Nhật ký phiên KHÔNG bị xóa" "Lịch sử phiên đã được ghi nhật ký" "Lỗi: Lịch sử phiên CHƯA được ghi" diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml index 3d7bffa21..d261befda 100644 --- a/java/res/values-zh-rCN/strings.xml +++ b/java/res/values-zh-rCN/strings.xml @@ -58,6 +58,10 @@ "改动极大" "下一字词建议" "根据上一个字词提供建议" + + + + "%s:已保存" "开始" "下一步" @@ -106,11 +110,14 @@ "输入语言" "标记记录中的时间" "已标记时间" - "不记录本次会话" - "启用会话记录" + + + + "保存整个会话历史记录" "正在删除会话记录" - "会话记录已删除" + + "未能删除会话记录" "会话历史记录已保存" "错误:会话历史记录未保存" diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml index a1ee3aebb..1afbd38aa 100644 --- a/java/res/values-zh-rTW/strings.xml +++ b/java/res/values-zh-rTW/strings.xml @@ -58,6 +58,10 @@ "更正範圍極大" "下一個字詞建議" "根據上一個字詞產生" + + + + "%s:已儲存" "開始" "繼續" @@ -106,11 +110,14 @@ "輸入語言" "在紀錄中加註時間戳記" "已記錄時間戳記" - "不要記錄這個工作階段" - "啟用工作階段記錄功能" + + + + "記錄完整工作階段紀錄" "正在刪除工作階段紀錄" - "已刪除工作階段紀錄" + + "「未」刪除工作階段紀錄" "已記錄工作階段紀錄" "錯誤:「未」記錄工作階段紀錄" diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml index 575c30d4a..219e2b304 100644 --- a/java/res/values-zu/strings.xml +++ b/java/res/values-zu/strings.xml @@ -58,6 +58,10 @@ "Nobudlova kakhulu" "Iziphakamiso zegama elilandelayo" "Ngokususela egameni langaphambilini" + + + + "%s : Kulondoloziwe" "Iya" "Okulandelayo" @@ -106,11 +110,14 @@ "Izilimi zokufakwayo" "Qaphela isitembu sesikhathi efayeleni lokungena" "Isitembu sesikhathi esirekhodiwe" - "Ungenzi ifayela lokungena lalesi sikhathi" - "Nika amandla ukungena ngemvume kwesikhathi" + + + + "Umlando wesikhathi sonke sefayela lokungena" "Isusa ifayela lokungena lesikhathi" - "Ifayela lokungena lesikhathi lisusiwe" + + "Ifayela lokungena lesikhathi alisusiwe" "Umlando wesikhathi ukufayela lokungena" "Iphutha: Umlando wesikhathi awufakwanga kufayela lokungena" From 6b966160ac8570271547bf63217efa5e228d4acc Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Fri, 20 Jul 2012 11:02:39 -0700 Subject: [PATCH 25/36] ResearchLog refactor - new package: com.android.inputmethod.research multi-project commit with Ic0a5744f3160d13218addd589890623c0d120ffc Bug: 6188932 Change-Id: Icf8d4a40a5725401799be6e209a640d99a5f34c4 --- java/proguard.flags | 2 +- .../inputmethod/keyboard/LatinKeyboardView.java | 2 +- .../inputmethod/keyboard/PointerTracker.java | 2 +- .../keyboard/SuddenJumpingTouchEventHandler.java | 2 +- .../com/android/inputmethod/latin/LatinIME.java | 3 ++- .../inputmethod/latin/RichInputConnection.java | 1 + .../com/android/inputmethod/latin/Settings.java | 1 + .../latin/suggestions/SuggestionsView.java | 2 +- .../{latin => research}/ResearchLog.java | 5 +++-- .../{latin => research}/ResearchLogger.java | 14 ++++++++++---- 10 files changed, 22 insertions(+), 12 deletions(-) rename java/src/com/android/inputmethod/{latin => research}/ResearchLog.java (98%) rename java/src/com/android/inputmethod/{latin => research}/ResearchLogger.java (98%) diff --git a/java/proguard.flags b/java/proguard.flags index 1711b99df..b66c9a91c 100644 --- a/java/proguard.flags +++ b/java/proguard.flags @@ -44,7 +44,7 @@ (...); } --keep class com.android.inputmethod.latin.ResearchLogger { +-keep class com.android.inputmethod.research.ResearchLogger { void flush(); void publishCurrentLogUnit(...); } diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index 537b4e245..cf196638d 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -47,13 +47,13 @@ import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.ResearchLogger; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.StringUtils; import com.android.inputmethod.latin.SubtypeLocale; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils; import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.research.ResearchLogger; import java.util.Locale; import java.util.WeakHashMap; diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 1c84cb947..fb795f2cd 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -30,8 +30,8 @@ import com.android.inputmethod.keyboard.internal.GestureStroke; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.ResearchLogger; import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.research.ResearchLogger; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java index 107138395..617207389 100644 --- a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java +++ b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java @@ -22,9 +22,9 @@ import android.view.MotionEvent; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.ResearchLogger; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.research.ResearchLogger; public class SuddenJumpingTouchEventHandler { private static final String TAG = SuddenJumpingTouchEventHandler.class.getSimpleName(); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 63aed7f58..8953cb8d1 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -71,6 +71,7 @@ import com.android.inputmethod.keyboard.LatinKeyboardView; import com.android.inputmethod.latin.LocaleUtils.RunInLocale; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.suggestions.SuggestionsView; +import com.android.inputmethod.research.ResearchLogger; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -2159,7 +2160,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen showOptionDialog(builder.create()); } - /* package */ void showOptionDialog(AlertDialog dialog) { + public void showOptionDialog(AlertDialog dialog) { final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken(); if (windowToken == null) return; diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 5786978a8..8b4c17322 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -28,6 +28,7 @@ import android.view.inputmethod.InputConnection; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.research.ResearchLogger; import java.util.regex.Pattern; diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index b67cc852e..45608f439 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -39,6 +39,7 @@ import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.research.ResearchLogger; import com.android.inputmethodcommon.InputMethodSettingsFragment; public class Settings extends InputMethodSettingsFragment diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java index 4d33f4ba5..52ceb8698 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java @@ -60,11 +60,11 @@ import com.android.inputmethod.keyboard.ViewLayoutUtils; import com.android.inputmethod.latin.AutoCorrection; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.ResearchLogger; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.research.ResearchLogger; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/latin/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java similarity index 98% rename from java/src/com/android/inputmethod/latin/ResearchLog.java rename to java/src/com/android/inputmethod/research/ResearchLog.java index 917ee84a2..18bf3c07f 100644 --- a/java/src/com/android/inputmethod/latin/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.research; import android.content.SharedPreferences; import android.os.SystemClock; @@ -23,9 +23,10 @@ import android.util.Log; import android.view.inputmethod.CompletionInfo; import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.latin.ResearchLogger.LogUnit; +import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.research.ResearchLogger.LogUnit; import java.io.BufferedWriter; import java.io.File; diff --git a/java/src/com/android/inputmethod/latin/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java similarity index 98% rename from java/src/com/android/inputmethod/latin/ResearchLogger.java rename to java/src/com/android/inputmethod/research/ResearchLogger.java index e409c5a08..e27e84f53 100644 --- a/java/src/com/android/inputmethod/latin/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.research; import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; @@ -40,7 +40,13 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.LatinIME; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.RichInputConnection; import com.android.inputmethod.latin.RichInputConnection.Range; +import com.android.inputmethod.latin.Suggest; +import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.define.ProductionFlag; import java.io.File; @@ -63,7 +69,7 @@ import java.util.UUID; public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = ResearchLogger.class.getSimpleName(); private static final boolean OUTPUT_ENTIRE_BUFFER = false; // true may disclose private info - /* package */ static final boolean DEFAULT_USABILITY_STUDY_MODE = false; + public static final boolean DEFAULT_USABILITY_STUDY_MODE = false; /* package */ static boolean sIsLogging = false; private static final int OUTPUT_FORMAT_VERSION = 1; private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; @@ -314,7 +320,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang requestIndicatorRedraw(); } - /* package */ void presentResearchDialog(final LatinIME latinIME) { + public void presentResearchDialog(final LatinIME latinIME) { final CharSequence title = latinIME.getString(R.string.english_ime_research_log); final boolean showEnable = mIsLoggingSuspended || !sIsLogging; final CharSequence[] items = new CharSequence[] { @@ -770,7 +776,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang values); } - /* package */ static boolean getAndClearLatinIMEExpectingUpdateSelection() { + public static boolean getAndClearLatinIMEExpectingUpdateSelection() { boolean returnValue = sLatinIMEExpectingUpdateSelection; sLatinIMEExpectingUpdateSelection = false; return returnValue; From 9c539d5a5c8e9b36be482fd7ebb2a71a22ef6af0 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 18 Jul 2012 17:05:48 -0700 Subject: [PATCH 26/36] ResearchLog uploading - uploads files in the background to server multi-project commit with Ie0d937773e04b2fbefc8d76c231aaa52ebc392c9 Bug: 6188932 Change-Id: I90bb0e237eeb567e4cbb51085f2229f17f1fe71c --- java/res/values/urls.xml | 22 ++ .../research/ResearchLogUploader.java | 223 ++++++++++++++++++ .../inputmethod/research/ResearchLogger.java | 7 +- 3 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 java/res/values/urls.xml create mode 100644 java/src/com/android/inputmethod/research/ResearchLogUploader.java diff --git a/java/res/values/urls.xml b/java/res/values/urls.xml new file mode 100644 index 000000000..a8e9ad7d3 --- /dev/null +++ b/java/res/values/urls.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/java/src/com/android/inputmethod/research/ResearchLogUploader.java b/java/src/com/android/inputmethod/research/ResearchLogUploader.java new file mode 100644 index 000000000..3b1213009 --- /dev/null +++ b/java/src/com/android/inputmethod/research/ResearchLogUploader.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2012 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. + */ + +package com.android.inputmethod.research; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.BatteryManager; +import android.util.Log; + +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.R.string; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public final class ResearchLogUploader { + private static final String TAG = ResearchLogUploader.class.getSimpleName(); + private static final int UPLOAD_INTERVAL_IN_MS = 1000 * 60 * 15; // every 15 min + private static final int BUF_SIZE = 1024 * 8; + + private final boolean mCanUpload; + private final Context mContext; + private final File mFilesDir; + private final URL mUrl; + private final ScheduledExecutorService mExecutor; + + private Runnable doUploadRunnable = new UploadRunnable(null, false); + + public ResearchLogUploader(final Context context, final File filesDir) { + mContext = context; + mFilesDir = filesDir; + final PackageManager packageManager = context.getPackageManager(); + final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET, + context.getPackageName()) == PackageManager.PERMISSION_GRANTED; + if (!hasPermission) { + mCanUpload = false; + mUrl = null; + mExecutor = null; + return; + } + URL tempUrl = null; + boolean canUpload = false; + ScheduledExecutorService executor = null; + try { + final String urlString = context.getString(R.string.research_logger_upload_url); + if (urlString == null || urlString.equals("")) { + return; + } + tempUrl = new URL(urlString); + canUpload = true; + executor = Executors.newSingleThreadScheduledExecutor(); + } catch (MalformedURLException e) { + tempUrl = null; + e.printStackTrace(); + return; + } finally { + mCanUpload = canUpload; + mUrl = tempUrl; + mExecutor = executor; + } + } + + public void start() { + if (mCanUpload) { + Log.d(TAG, "scheduling regular uploading"); + mExecutor.scheduleWithFixedDelay(doUploadRunnable, UPLOAD_INTERVAL_IN_MS, + UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS); + } else { + Log.d(TAG, "no permission to upload"); + } + } + + public void uploadNow(final Callback callback) { + // Perform an immediate upload. Note that this should happen even if there is + // another upload happening right now, as it may have missed the latest changes. + // TODO: Reschedule regular upload tests starting from now. + if (mCanUpload) { + mExecutor.submit(new UploadRunnable(callback, true)); + } + } + + public interface Callback { + public void onUploadCompleted(final boolean success); + } + + private boolean isExternallyPowered() { + final Intent intent = mContext.registerReceiver(null, new IntentFilter( + Intent.ACTION_BATTERY_CHANGED)); + final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); + return pluggedState == BatteryManager.BATTERY_PLUGGED_AC + || pluggedState == BatteryManager.BATTERY_PLUGGED_USB; + } + + private boolean hasWifiConnection() { + final ConnectivityManager manager = + (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + return wifiInfo.isConnected(); + } + + class UploadRunnable implements Runnable { + private final Callback mCallback; + private final boolean mForceUpload; + + public UploadRunnable(final Callback callback, final boolean forceUpload) { + mCallback = callback; + mForceUpload = forceUpload; + } + + @Override + public void run() { + doUpload(); + } + + private void doUpload() { + if (!mForceUpload && (!isExternallyPowered() || !hasWifiConnection())) { + return; + } + if (mFilesDir == null) { + return; + } + final File[] files = mFilesDir.listFiles(new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.getName().startsWith(ResearchLogger.FILENAME_PREFIX) + && !pathname.canWrite(); + } + }); + boolean success = true; + if (files.length == 0) { + success = false; + } + for (final File file : files) { + if (!uploadFile(file)) { + success = false; + } + } + if (mCallback != null) { + mCallback.onUploadCompleted(success); + } + } + + private boolean uploadFile(File file) { + Log.d(TAG, "attempting upload of " + file.getAbsolutePath()); + boolean success = false; + final int contentLength = (int) file.length(); + HttpURLConnection connection = null; + InputStream fileIs = null; + try { + fileIs = new FileInputStream(file); + connection = (HttpURLConnection) mUrl.openConnection(); + connection.setRequestMethod("PUT"); + connection.setDoOutput(true); + connection.setFixedLengthStreamingMode(contentLength); + final OutputStream os = connection.getOutputStream(); + final byte[] buf = new byte[BUF_SIZE]; + int numBytesRead; + while ((numBytesRead = fileIs.read(buf)) != -1) { + os.write(buf, 0, numBytesRead); + } + if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { + Log.d(TAG, "upload failed: " + connection.getResponseCode()); + InputStream netIs = connection.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(netIs)); + String line; + while ((line = reader.readLine()) != null) { + Log.d(TAG, "| " + reader.readLine()); + } + reader.close(); + return success; + } + file.delete(); + success = true; + Log.d(TAG, "upload successful"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (fileIs != null) { + try { + fileIs.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (connection != null) { + connection.disconnect(); + } + } + return success; + } + } +} diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index e27e84f53..b40bfe387 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -120,6 +120,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private KeyboardSwitcher mKeyboardSwitcher; private Context mContext; + private ResearchLogUploader mResearchLogUploader; + private ResearchLogger() { } @@ -158,6 +160,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang e.apply(); } } + mResearchLogUploader = new ResearchLogUploader(ims, mFilesDir); + mResearchLogUploader.start(); mKeyboardSwitcher = keyboardSwitcher; mContext = ims; mPrefs = prefs; @@ -327,7 +331,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang latinIME.getString(R.string.note_timestamp_for_researchlog), showEnable ? latinIME.getString(R.string.enable_session_logging) : latinIME.getString(R.string.do_not_log_this_session), - latinIME.getString(R.string.log_whole_session_history), + latinIME.getString(R.string.log_whole_session_history) }; final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { @Override @@ -536,6 +540,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return; } if (mMainResearchLog == null) { + Log.w(TAG, "ResearchLog was not properly set up"); return; } if (isPrivacySensitive) { From ccaa799ee9fd5c1fb9dd4d00cccc65ab9eee93e5 Mon Sep 17 00:00:00 2001 From: Tom Ouyang Date: Sun, 22 Jul 2012 08:36:13 +0900 Subject: [PATCH 27/36] Update gesture bounding box handling. Change-Id: I085611ce6fd82608f284d74973e5bb14258cdc24 --- .../com/android/inputmethod/keyboard/KeyboardView.java | 9 ++++----- .../com/android/inputmethod/keyboard/PointerTracker.java | 4 ++-- .../inputmethod/keyboard/internal/GestureStroke.java | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 61ed26b06..6d9f0139c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -919,15 +919,14 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { if (mPreviewPlacer == null) { createPreviewPlacer(); } - final Rect r = tracker.getDrawingRect(); + final Rect r = tracker.getBoundingBox(); if (!r.isEmpty()) { // Invalidate the rectangular region encompassing the gesture. This is needed because // past points along the gesture will fade and gradually disappear. final KeyPreviewDrawParams params = mKeyPreviewDrawParams; - mInvalidatedGesturesRect.set(r.left + params.mCoordinates[0] - GESTURE_DRAWING_WIDTH, - r.top + params.mCoordinates[1] - GESTURE_DRAWING_WIDTH, - r.right + params.mCoordinates[0] + GESTURE_DRAWING_WIDTH, - r.bottom + params.mCoordinates[1] + GESTURE_DRAWING_WIDTH); + mInvalidatedGesturesRect.set(r); + mInvalidatedGesturesRect.offset(params.mCoordinates[0], params.mCoordinates[1]); + mInvalidatedGesturesRect.inset(-GESTURE_DRAWING_WIDTH, -GESTURE_DRAWING_WIDTH); mPreviewPlacer.invalidate(mInvalidatedGesturesRect); } else { mPreviewPlacer.invalidate(); diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index fb795f2cd..9542ee7c4 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -536,8 +536,8 @@ public class PointerTracker { public long getDownTime() { return mDownTime; } - public Rect getDrawingRect() { - return mGestureStroke.getDrawingRect(); + public Rect getBoundingBox() { + return mGestureStroke.getBoundingBox(); } private Key onDownKey(int x, int y, long eventTime) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 6e3295e59..c16b70ef0 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -199,7 +199,7 @@ public class GestureStroke { } } - public Rect getDrawingRect() { + public Rect getBoundingBox() { return mDrawingRect; } } From 3c233bf1a5003c478a27964758afe2ca581d3d8b Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 18 Jul 2012 18:00:48 -0700 Subject: [PATCH 28/36] ResearchLogger feedback form - also cleaned up RLog menu multi-project commit with If0fd4fef89d390073e6939d5188ed5696866cb33 Bug: 6188932 Change-Id: I4f66f13bd366b4e8bde742ccd0704f812c6d33f9 --- .../res/layout/research_feedback_activity.xml | 31 ++++ .../research_feedback_fragment_layout.xml | 112 +++++++++++++ java/res/layout/research_feedback_layout.xml | 50 ++++++ java/res/values/strings.xml | 52 ++++-- .../android/inputmethod/latin/LatinIME.java | 20 ++- .../research/FeedbackActivity.java | 52 ++++++ .../research/FeedbackFragment.java | 73 +++++++++ .../inputmethod/research/FeedbackLayout.java | 62 ++++++++ .../inputmethod/research/ResearchLogger.java | 150 +++++++++++++----- 9 files changed, 539 insertions(+), 63 deletions(-) create mode 100644 java/res/layout/research_feedback_activity.xml create mode 100644 java/res/layout/research_feedback_fragment_layout.xml create mode 100644 java/res/layout/research_feedback_layout.xml create mode 100644 java/src/com/android/inputmethod/research/FeedbackActivity.java create mode 100644 java/src/com/android/inputmethod/research/FeedbackFragment.java create mode 100644 java/src/com/android/inputmethod/research/FeedbackLayout.java diff --git a/java/res/layout/research_feedback_activity.xml b/java/res/layout/research_feedback_activity.xml new file mode 100644 index 000000000..a6b8b8a43 --- /dev/null +++ b/java/res/layout/research_feedback_activity.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/java/res/layout/research_feedback_fragment_layout.xml b/java/res/layout/research_feedback_fragment_layout.xml new file mode 100644 index 000000000..cc04cedf4 --- /dev/null +++ b/java/res/layout/research_feedback_fragment_layout.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + +