diff options
Diffstat (limited to 'java')
21 files changed, 1113 insertions, 714 deletions
diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict Binary files differindex 5aed479c6..c2941b73a 100644 --- a/java/res/raw/main_fr.dict +++ b/java/res/raw/main_fr.dict diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 51517823a..1088fdab4 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -66,7 +66,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich), }; - private AudioAndHapticFeedbackManager mFeedbackManager; + private final AudioAndHapticFeedbackManager mFeedbackManager = + AudioAndHapticFeedbackManager.getInstance(); private SubtypeSwitcher mSubtypeSwitcher; private SharedPreferences mPrefs; @@ -104,7 +105,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) { mLatinIME = latinIme; mResources = latinIme.getResources(); - mFeedbackManager = new AudioAndHapticFeedbackManager(latinIme); mPrefs = prefs; mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mState = new KeyboardState(this); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 61d38745e..b7bee3430 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -50,6 +50,7 @@ import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.StringUtils; +import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.research.ResearchLogger; @@ -870,9 +871,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy, mPreviewPlacerView.dismissSlidingKeyInputPreview(); } - public void showGestureFloatingPreviewText(final String gestureFloatingPreviewText) { + public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) { locatePreviewPlacerView(); - mPreviewPlacerView.setGestureFloatingPreviewText(gestureFloatingPreviewText); + mPreviewPlacerView.setGestureFloatingPreviewText(suggestedWords); } public void dismissGestureFloatingPreviewText() { diff --git a/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java new file mode 100644 index 000000000..8a3f0645f --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java @@ -0,0 +1,49 @@ +/* + * 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.graphics.Canvas; + +import com.android.inputmethod.keyboard.PointerTracker; + +/** + * Abstract base class for previews that are drawn on PreviewPlacerView, e.g., + * GestureFloatingPrevewText, GestureTrail, and SlidingKeyInputPreview. + */ +public abstract class AbstractDrawingPreview { + private boolean mPreviewEnabled; + + public void setPreviewEnabled(final boolean enabled) { + mPreviewEnabled = enabled; + } + + public boolean isPreviewEnabled() { + return mPreviewEnabled; + } + + /** + * Draws the preview + * @param canvas The canvas where the preview is drawn. + */ + public abstract void onDraw(final Canvas canvas); + + /** + * Set the position of the preview. + * @param pt The new location of the preview is based on the points in PointerTracker pt. + */ + public abstract void setPreviewPosition(final PointerTracker pt); +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java new file mode 100644 index 000000000..84cfb389c --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java @@ -0,0 +1,179 @@ +/* + * 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.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Rect; +import android.graphics.RectF; +import android.text.TextUtils; + +import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.latin.CoordinateUtils; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.ResizableIntArray; +import com.android.inputmethod.latin.SuggestedWords; + +/** + * The class for single gesture preview text. The class for multiple gesture preview text will be + * derived from it. + */ +public class GestureFloatingPreviewText extends AbstractDrawingPreview { + private static final class GesturePreviewTextParams { + public final int mGesturePreviewTextSize; + public final int mGesturePreviewTextColor; + public final int mGesturePreviewTextDimmedColor; + public final int mGesturePreviewTextOffset; + public final int mGesturePreviewTextHeight; + public final int mGesturePreviewColor; + public final float mGesturePreviewHorizontalPadding; + public final float mGesturePreviewVerticalPadding; + public final float mGesturePreviewRoundRadius; + public final Paint mTextPaint; + + private static final char[] TEXT_HEIGHT_REFERENCE_CHAR = { 'M' }; + + public GesturePreviewTextParams(final TypedArray keyboardViewAttr) { + mGesturePreviewTextSize = keyboardViewAttr.getDimensionPixelSize( + R.styleable.KeyboardView_gestureFloatingPreviewTextSize, 0); + mGesturePreviewTextColor = keyboardViewAttr.getColor( + R.styleable.KeyboardView_gestureFloatingPreviewTextColor, 0); + mGesturePreviewTextOffset = keyboardViewAttr.getDimensionPixelOffset( + R.styleable.KeyboardView_gestureFloatingPreviewTextOffset, 0); + mGesturePreviewColor = keyboardViewAttr.getColor( + R.styleable.KeyboardView_gestureFloatingPreviewColor, 0); + mGesturePreviewHorizontalPadding = keyboardViewAttr.getDimension( + R.styleable.KeyboardView_gestureFloatingPreviewHorizontalPadding, 0.0f); + mGesturePreviewVerticalPadding = keyboardViewAttr.getDimension( + R.styleable.KeyboardView_gestureFloatingPreviewVerticalPadding, 0.0f); + mGesturePreviewRoundRadius = keyboardViewAttr.getDimension( + R.styleable.KeyboardView_gestureFloatingPreviewRoundRadius, 0.0f); + mGesturePreviewTextDimmedColor = Color.GRAY; + + final Paint textPaint = new Paint(); + textPaint.setAntiAlias(true); + textPaint.setTextAlign(Align.CENTER); + textPaint.setTextSize(mGesturePreviewTextSize); + mTextPaint = textPaint; + final Rect textRect = new Rect(); + textPaint.getTextBounds(TEXT_HEIGHT_REFERENCE_CHAR, 0, 1, textRect); + mGesturePreviewTextHeight = textRect.height(); + } + } + + protected final GesturePreviewTextParams mParams; + protected int mPreviewWordNum; + protected final RectF mGesturePreviewRectangle = new RectF(); + protected int mHighlightedWordIndex; + + private static final int PREVIEW_TEXT_ARRAY_CAPACITY = 10; + // These variables store the positions of preview words. In multi-preview mode, the gesture + // floating preview at most shows PREVIEW_TEXT_ARRAY_CAPACITY words. + protected final ResizableIntArray mPreviewTextXArray = new ResizableIntArray( + PREVIEW_TEXT_ARRAY_CAPACITY); + protected final ResizableIntArray mPreviewTextYArray = new ResizableIntArray( + PREVIEW_TEXT_ARRAY_CAPACITY); + + protected SuggestedWords mSuggestedWords = SuggestedWords.EMPTY; + protected final Context mContext; + public final int[] mLastPointerCoords = CoordinateUtils.newInstance(); + + public GestureFloatingPreviewText(final TypedArray typedArray, final Context context) { + mParams = new GesturePreviewTextParams(typedArray); + mHighlightedWordIndex = 0; + mContext = context; + } + + public void setSuggetedWords(final SuggestedWords suggestedWords) { + if (suggestedWords == null) { + mSuggestedWords = SuggestedWords.EMPTY; + } else { + mSuggestedWords = suggestedWords; + } + updatePreviewPosition(); + } + + protected void drawText(final Canvas canvas, final String text, final float textX, + final float textY, final int color) { + final Paint paint = mParams.mTextPaint; + paint.setColor(color); + canvas.drawText(text, textX, textY, paint); + } + + @Override + public void setPreviewPosition(final PointerTracker pt) { + pt.getLastCoordinates(mLastPointerCoords); + updatePreviewPosition(); + } + + /** + * Draws gesture preview text + * @param canvas The canvas where preview text is drawn. + */ + @Override + public void onDraw(final Canvas canvas) { + if (!isPreviewEnabled() || mSuggestedWords.isEmpty() + || TextUtils.isEmpty(mSuggestedWords.getWord(0))) { + return; + } + final Paint paint = mParams.mTextPaint; + paint.setColor(mParams.mGesturePreviewColor); + final float round = mParams.mGesturePreviewRoundRadius; + canvas.drawRoundRect(mGesturePreviewRectangle, round, round, paint); + final String text = mSuggestedWords.getWord(0); + final int textX = mPreviewTextXArray.get(0); + final int textY = mPreviewTextYArray.get(0); + drawText(canvas, text, textX, textY, mParams.mGesturePreviewTextColor); + } + + /** + * Updates gesture preview text position based on mLastPointerCoords. + */ + protected void updatePreviewPosition() { + if (mSuggestedWords.isEmpty() || TextUtils.isEmpty(mSuggestedWords.getWord(0))) { + return; + } + final String text = mSuggestedWords.getWord(0); + + final Paint paint = mParams.mTextPaint; + final RectF rectangle = mGesturePreviewRectangle; + + final int textHeight = mParams.mGesturePreviewTextHeight; + final float textWidth = paint.measureText(text); + final float hPad = mParams.mGesturePreviewHorizontalPadding; + final float vPad = mParams.mGesturePreviewVerticalPadding; + final float rectWidth = textWidth + hPad * 2.0f; + final float rectHeight = textHeight + vPad * 2.0f; + + final int displayWidth = mContext.getResources().getDisplayMetrics().widthPixels; + final float rectX = Math.min( + Math.max(CoordinateUtils.x(mLastPointerCoords) - rectWidth / 2.0f, 0.0f), + displayWidth - rectWidth); + final float rectY = CoordinateUtils.y(mLastPointerCoords) + - mParams.mGesturePreviewTextOffset - rectHeight; + rectangle.set(rectX, rectY, rectX + rectWidth, rectY + rectHeight); + + final int textX = (int)(rectX + hPad + textWidth / 2.0f); + final int textY = (int)(rectY + vPad) + textHeight; + mPreviewTextXArray.add(0, textX); + mPreviewTextYArray.add(0, textY); + } +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java index bc734b08d..a005dc975 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java @@ -22,13 +22,10 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; -import android.graphics.Paint.Align; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; -import android.graphics.RectF; import android.os.Message; -import android.text.TextUtils; import android.util.AttributeSet; import android.util.SparseArray; import android.widget.RelativeLayout; @@ -39,15 +36,9 @@ import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.CoordinateUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; +import com.android.inputmethod.latin.SuggestedWords; public final class PreviewPlacerView extends RelativeLayout { - private final int mGestureFloatingPreviewTextColor; - private final int mGestureFloatingPreviewTextOffset; - private final int mGestureFloatingPreviewColor; - private final float mGestureFloatingPreviewHorizontalPadding; - private final float mGestureFloatingPreviewVerticalPadding; - private final float mGestureFloatingPreviewRoundRadius; - private final int[] mKeyboardViewOrigin = CoordinateUtils.newInstance(); private final SparseArray<GesturePreviewTrail> mGesturePreviewTrails = @@ -62,16 +53,7 @@ public final class PreviewPlacerView extends RelativeLayout { private final Canvas mOffscreenCanvas = new Canvas(); private final Rect mOffscreenDirtyRect = new Rect(); private final Rect mGesturePreviewTrailBoundsRect = new Rect(); // per trail - - private final Paint mTextPaint; - private String mGestureFloatingPreviewText; - private final int mGestureFloatingPreviewTextHeight; - // {@link RectF} is needed for {@link Canvas#drawRoundRect(RectF, float, float, Paint)}. - private final RectF mGestureFloatingPreviewRectangle = new RectF(); - private final int[] mLastPointerCoords = CoordinateUtils.newInstance(); - private static final char[] TEXT_HEIGHT_REFERENCE_CHAR = { 'M' }; - private boolean mDrawsGestureFloatingPreviewText; - + private final GestureFloatingPreviewText mGestureFloatingPreviewText; private boolean mShowSlidingKeyInputPreview; private final int[] mRubberBandFrom = CoordinateUtils.newInstance(); private final int[] mRubberBandTo = CoordinateUtils.newInstance(); @@ -130,22 +112,11 @@ public final class PreviewPlacerView extends RelativeLayout { final TypedArray keyboardViewAttr = context.obtainStyledAttributes( attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView); - final int gestureFloatingPreviewTextSize = keyboardViewAttr.getDimensionPixelSize( - R.styleable.KeyboardView_gestureFloatingPreviewTextSize, 0); - mGestureFloatingPreviewTextColor = keyboardViewAttr.getColor( - R.styleable.KeyboardView_gestureFloatingPreviewTextColor, 0); - mGestureFloatingPreviewTextOffset = keyboardViewAttr.getDimensionPixelOffset( - R.styleable.KeyboardView_gestureFloatingPreviewTextOffset, 0); - mGestureFloatingPreviewColor = keyboardViewAttr.getColor( - R.styleable.KeyboardView_gestureFloatingPreviewColor, 0); - mGestureFloatingPreviewHorizontalPadding = keyboardViewAttr.getDimension( - R.styleable.KeyboardView_gestureFloatingPreviewHorizontalPadding, 0.0f); - mGestureFloatingPreviewVerticalPadding = keyboardViewAttr.getDimension( - R.styleable.KeyboardView_gestureFloatingPreviewVerticalPadding, 0.0f); - mGestureFloatingPreviewRoundRadius = keyboardViewAttr.getDimension( - R.styleable.KeyboardView_gestureFloatingPreviewRoundRadius, 0.0f); final int gestureFloatingPreviewTextLingerTimeout = keyboardViewAttr.getInt( R.styleable.KeyboardView_gestureFloatingPreviewTextLingerTimeout, 0); + // TODO: mGestureFloatingPreviewText could be an instance of GestureFloatingPreviewText or + // MultiGesturePreviewText, depending on the user's choice in the settings. + mGestureFloatingPreviewText = new GestureFloatingPreviewText(keyboardViewAttr, context); mGesturePreviewTrailParams = new Params(keyboardViewAttr); keyboardViewAttr.recycle(); @@ -157,15 +128,6 @@ public final class PreviewPlacerView extends RelativeLayout { gesturePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); mGesturePaint = gesturePaint; - final Paint textPaint = new Paint(); - textPaint.setAntiAlias(true); - textPaint.setTextAlign(Align.CENTER); - textPaint.setTextSize(gestureFloatingPreviewTextSize); - mTextPaint = textPaint; - final Rect textRect = new Rect(); - textPaint.getTextBounds(TEXT_HEIGHT_REFERENCE_CHAR, 0, 1, textRect); - mGestureFloatingPreviewTextHeight = textRect.height(); - final Paint layerPaint = new Paint(); layerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); setLayerType(LAYER_TYPE_HARDWARE, layerPaint); @@ -181,14 +143,14 @@ public final class PreviewPlacerView extends RelativeLayout { public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail, final boolean drawsGestureFloatingPreviewText) { mDrawsGesturePreviewTrail = drawsGesturePreviewTrail; - mDrawsGestureFloatingPreviewText = drawsGestureFloatingPreviewText; + mGestureFloatingPreviewText.setPreviewEnabled(drawsGestureFloatingPreviewText); } public void invalidatePointer(final PointerTracker tracker, final boolean isOldestTracker) { final boolean needsToUpdateLastPointer = - isOldestTracker && mDrawsGestureFloatingPreviewText; + isOldestTracker && mGestureFloatingPreviewText.isPreviewEnabled(); if (needsToUpdateLastPointer) { - tracker.getLastCoordinates(mLastPointerCoords); + mGestureFloatingPreviewText.setPreviewPosition(tracker); } if (mDrawsGesturePreviewTrail) { @@ -252,6 +214,7 @@ public final class PreviewPlacerView extends RelativeLayout { super.onDraw(canvas); final int originX = CoordinateUtils.x(mKeyboardViewOrigin); final int originY = CoordinateUtils.y(mKeyboardViewOrigin); + canvas.translate(originX, originY); if (mDrawsGesturePreviewTrail) { mayAllocateOffscreenBuffer(); // Draw gesture trails to offscreen buffer. @@ -259,11 +222,10 @@ public final class PreviewPlacerView extends RelativeLayout { mOffscreenCanvas, mGesturePaint, mOffscreenDirtyRect); // Transfer offscreen buffer to screen. if (!mOffscreenDirtyRect.isEmpty()) { - final int offsetY = originY - mOffscreenOffsetY; - canvas.translate(originX, offsetY); + canvas.translate(0, - mOffscreenOffsetY); canvas.drawBitmap(mOffscreenBuffer, mOffscreenDirtyRect, mOffscreenDirtyRect, mGesturePaint); - canvas.translate(-originX, -offsetY); + canvas.translate(0, mOffscreenOffsetY); // Note: Defer clearing the dirty rectangle here because we will get cleared // rectangle on the canvas. } @@ -271,16 +233,11 @@ public final class PreviewPlacerView extends RelativeLayout { mDrawingHandler.postUpdateGestureTrailPreview(); } } - if (mDrawsGestureFloatingPreviewText) { - canvas.translate(originX, originY); - drawGestureFloatingPreviewText(canvas, mGestureFloatingPreviewText); - canvas.translate(-originX, -originY); - } + mGestureFloatingPreviewText.onDraw(canvas); if (mShowSlidingKeyInputPreview) { - canvas.translate(originX, originY); drawSlidingKeyInputPreview(canvas); - canvas.translate(-originX, -originY); } + canvas.translate(-originX, -originY); } private boolean drawGestureTrails(final Canvas offscreenCanvas, final Paint paint, @@ -322,9 +279,9 @@ public final class PreviewPlacerView extends RelativeLayout { Math.min(out.bottom, bottom)); } - public void setGestureFloatingPreviewText(final String gestureFloatingPreviewText) { - if (!mDrawsGestureFloatingPreviewText) return; - mGestureFloatingPreviewText = gestureFloatingPreviewText; + public void setGestureFloatingPreviewText(final SuggestedWords suggestedWords) { + if (!mGestureFloatingPreviewText.isPreviewEnabled()) return; + mGestureFloatingPreviewText.setSuggetedWords(suggestedWords); invalidate(); } @@ -332,39 +289,6 @@ public final class PreviewPlacerView extends RelativeLayout { mDrawingHandler.dismissGestureFloatingPreviewText(); } - private void drawGestureFloatingPreviewText(final Canvas canvas, - final String gestureFloatingPreviewText) { - if (TextUtils.isEmpty(gestureFloatingPreviewText)) { - return; - } - - final Paint paint = mTextPaint; - final RectF rectangle = mGestureFloatingPreviewRectangle; - - // Paint the round rectangle background. - final int textHeight = mGestureFloatingPreviewTextHeight; - final float textWidth = paint.measureText(gestureFloatingPreviewText); - final float hPad = mGestureFloatingPreviewHorizontalPadding; - final float vPad = mGestureFloatingPreviewVerticalPadding; - final float rectWidth = textWidth + hPad * 2.0f; - final float rectHeight = textHeight + vPad * 2.0f; - final int canvasWidth = canvas.getWidth(); - final float rectX = Math.min( - Math.max(CoordinateUtils.x(mLastPointerCoords) - rectWidth / 2.0f, 0.0f), - canvasWidth - rectWidth); - final float rectY = CoordinateUtils.y(mLastPointerCoords) - - mGestureFloatingPreviewTextOffset - rectHeight; - rectangle.set(rectX, rectY, rectX + rectWidth, rectY + rectHeight); - final float round = mGestureFloatingPreviewRoundRadius; - paint.setColor(mGestureFloatingPreviewColor); - canvas.drawRoundRect(rectangle, round, round, paint); - // Paint the text preview - paint.setColor(mGestureFloatingPreviewTextColor); - final float textX = rectX + hPad + textWidth / 2.0f; - final float textY = rectY + vPad + textHeight; - canvas.drawText(gestureFloatingPreviewText, textX, textY, paint); - } - private void drawSlidingKeyInputPreview(final Canvas canvas) { // TODO: Implement rubber band preview } diff --git a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java index 0e7f891ff..6367156ef 100644 --- a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java +++ b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java @@ -18,11 +18,10 @@ package com.android.inputmethod.latin; import android.content.Context; import android.media.AudioManager; +import android.os.Vibrator; import android.view.HapticFeedbackConstants; import android.view.View; -import com.android.inputmethod.latin.VibratorUtils; - /** * This class gathers audio feedback and haptic feedback functions. * @@ -32,34 +31,61 @@ import com.android.inputmethod.latin.VibratorUtils; public final class AudioAndHapticFeedbackManager { public static final int MAX_KEYPRESS_VIBRATION_DURATION = 250; // millisecond - private final AudioManager mAudioManager; - private final VibratorUtils mVibratorUtils; + private AudioManager mAudioManager; + private Vibrator mVibrator; private SettingsValues mSettingsValues; private boolean mSoundOn; - public AudioAndHapticFeedbackManager(final LatinIME latinIme) { - mVibratorUtils = VibratorUtils.getInstance(latinIme); - mAudioManager = (AudioManager) latinIme.getSystemService(Context.AUDIO_SERVICE); + private static final AudioAndHapticFeedbackManager sInstance = + new AudioAndHapticFeedbackManager(); + + public static AudioAndHapticFeedbackManager getInstance() { + return sInstance; + } + + private AudioAndHapticFeedbackManager() { + // Intentional empty constructor for singleton. + } + + public static void init(final Context context) { + sInstance.initInternal(context); + } + + private void initInternal(final Context context) { + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); } public void hapticAndAudioFeedback(final int primaryCode, final View viewToPerformHapticFeedbackOn) { - vibrate(viewToPerformHapticFeedbackOn); + vibrateInternal(viewToPerformHapticFeedbackOn); playKeyClick(primaryCode); } + public boolean hasVibrator() { + return mVibrator != null && mVibrator.hasVibrator(); + } + + public void vibrate(final long milliseconds) { + if (mVibrator == null) { + return; + } + mVibrator.vibrate(milliseconds); + } + private boolean reevaluateIfSoundIsOn() { if (mSettingsValues == null || !mSettingsValues.mSoundOn || mAudioManager == null) { return false; - } else { - return mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL; } + return mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL; } - private void playKeyClick(int primaryCode) { + private void playKeyClick(final int primaryCode) { // if mAudioManager is null, we can't play a sound anyway, so return - if (mAudioManager == null) return; + if (mAudioManager == null) { + return; + } if (mSoundOn) { final int sound; switch (primaryCode) { @@ -80,7 +106,7 @@ public final class AudioAndHapticFeedbackManager { } } - private void vibrate(final View viewToPerformHapticFeedbackOn) { + private void vibrateInternal(final View viewToPerformHapticFeedbackOn) { if (!mSettingsValues.mVibrateOn) { return; } @@ -91,9 +117,9 @@ public final class AudioAndHapticFeedbackManager { HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); } - } else if (mVibratorUtils != null) { - mVibratorUtils.vibrate(mSettingsValues.mKeypressVibrationDuration); + return; } + vibrate(mSettingsValues.mKeypressVibrationDuration); } public void onSettingsChanged(final SettingsValues settingsValues) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 9d16eb7c5..6eeee9c2a 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -73,7 +73,6 @@ import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MainKeyboardView; -import com.android.inputmethod.latin.LocaleUtils.RunInLocale; import com.android.inputmethod.latin.Utils.Stats; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.suggestions.SuggestionStripView; @@ -128,7 +127,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // Current space state of the input method. This can be any of the above constants. private int mSpaceState; - private SettingsValues mCurrentSettings; + private final Settings mSettings; private View mExtractArea; private View mKeyPreviewBackingView; @@ -139,8 +138,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private ApplicationInfo mTargetApplicationInfo; private RichInputMethodManager mRichImm; - private Resources mResources; - private SharedPreferences mPrefs; @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher; private final SubtypeSwitcher mSubtypeSwitcher; private final SubtypeState mSubtypeState = new SubtypeState(); @@ -401,6 +398,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction public LatinIME() { super(); + mSettings = Settings.getInstance(); mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); mIsHardwareAcceleratedDrawingEnabled = @@ -410,9 +408,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction @Override public void onCreate() { - mPrefs = PreferenceManager.getDefaultSharedPreferences(this); - mResources = getResources(); - + Settings.init(this); LatinImeLogger.init(this); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.getInstance().init(this); @@ -421,6 +417,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mRichImm = RichInputMethodManager.getInstance(); SubtypeSwitcher.init(this); KeyboardSwitcher.init(this); + AudioAndHapticFeedbackManager.init(this); AccessibilityUtils.init(this); super.onCreate(); @@ -431,7 +428,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction loadSettings(); initSuggest(); - mDisplayOrientation = mResources.getConfiguration().orientation; + mDisplayOrientation = getResources().getConfiguration().orientation; // Register to receive ringer mode change and network state change. // Also receive installation and removal of a dictionary pack. @@ -458,18 +455,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // Has to be package-visible for unit tests @UsedForTesting void loadSettings() { - // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() - // is not guaranteed. It may even be called at the same time on a different thread. - if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this); + final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale(); final InputAttributes inputAttributes = new InputAttributes(getCurrentInputEditorInfo(), isFullscreenMode()); - final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() { - @Override - protected SettingsValues job(Resources res) { - return new SettingsValues(mPrefs, inputAttributes, LatinIME.this); - } - }; - mCurrentSettings = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale()); + mSettings.loadSettings(locale, inputAttributes); resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary()); } @@ -496,8 +485,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } mSuggest = new Suggest(this /* Context */, subtypeLocale, this /* SuggestInitializationListener */); - if (mCurrentSettings.mCorrectionEnabled) { - mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold); + if (mSettings.getCurrent().mCorrectionEnabled) { + mSuggest.setAutoCorrectionThreshold(mSettings.getCurrent().mAutoCorrectionThreshold); } mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale); @@ -511,10 +500,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction resetContactsDictionary(oldContactsDictionary); - // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() - // is not guaranteed. It may even be called at the same time on a different thread. - if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this); - mUserHistoryDictionary = UserHistoryDictionary.getInstance(this, localeStr, mPrefs); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + mUserHistoryDictionary = UserHistoryDictionary.getInstance(this, localeStr, prefs); mSuggest.setUserHistoryDictionary(mUserHistoryDictionary); } @@ -527,7 +514,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction * @param oldContactsDictionary an optional dictionary to use, or null */ private void resetContactsDictionary(final ContactsBinaryDictionary oldContactsDictionary) { - final boolean shouldSetDictionary = (null != mSuggest && mCurrentSettings.mUseContactsDict); + final boolean shouldSetDictionary = + (null != mSuggest && mSettings.getCurrent().mUseContactsDict); final ContactsBinaryDictionary dictionaryToUse; if (!shouldSetDictionary) { @@ -571,6 +559,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mSuggest.close(); mSuggest = null; } + mSettings.onDestroy(); unregisterReceiver(mReceiver); // TODO: The experimental version is not supported by the Dictionary Pack Service yet. if (!ProductionFlag.IS_EXPERIMENTAL) { @@ -681,7 +670,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0)); } if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, mPrefs); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, prefs); } if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) { Log.w(TAG, "Deprecated private IME option specified: " @@ -713,7 +703,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction accessUtils.onStartInputViewInternal(mainKeyboardView, editorInfo, restarting); } - final boolean inputTypeChanged = !mCurrentSettings.isSameInputType(editorInfo); + final boolean inputTypeChanged = !mSettings.getCurrent().isSameInputType(editorInfo); final boolean isDifferentTextField = !restarting || inputTypeChanged; if (isDifferentTextField) { mSubtypeSwitcher.updateParametersOnStartInputView(); @@ -743,11 +733,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mainKeyboardView.closing(); loadSettings(); - if (mSuggest != null && mCurrentSettings.mCorrectionEnabled) { - mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold); + if (mSuggest != null && mSettings.getCurrent().mCorrectionEnabled) { + mSuggest.setAutoCorrectionThreshold( + mSettings.getCurrent().mAutoCorrectionThreshold); } - switcher.loadKeyboard(editorInfo, mCurrentSettings); + switcher.loadKeyboard(editorInfo, mSettings.getCurrent()); } else if (restarting) { // TODO: Come up with a more comprehensive way to reset the keyboard layout when // a keyboard layout set doesn't get reloaded in this method. @@ -768,11 +759,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mHandler.cancelDoubleSpacePeriodTimer(); mainKeyboardView.setMainDictionaryAvailability(mIsMainDictionaryAvailable); - mainKeyboardView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn, - mCurrentSettings.mKeyPreviewPopupDismissDelay); - mainKeyboardView.setGestureHandlingEnabledByUser(mCurrentSettings.mGestureInputEnabled); - mainKeyboardView.setGesturePreviewMode(mCurrentSettings.mGesturePreviewTrailEnabled, - mCurrentSettings.mGestureFloatingPreviewTextEnabled); + mainKeyboardView.setKeyPreviewPopupEnabled(mSettings.getCurrent().mKeyPreviewPopupOn, + mSettings.getCurrent().mKeyPreviewPopupDismissDelay); + mainKeyboardView.setGestureHandlingEnabledByUser( + mSettings.getCurrent().mGestureInputEnabled); + mainKeyboardView.setGesturePreviewMode(mSettings.getCurrent().mGesturePreviewTrailEnabled, + mSettings.getCurrent().mGestureFloatingPreviewTextEnabled); // If we have a user dictionary addition in progress, we should check now if we should // replace the previously committed string with the word that has actually been added @@ -930,7 +922,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction */ @Override public void onExtractedTextClicked() { - if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return; + if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) return; super.onExtractedTextClicked(); } @@ -946,7 +938,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction */ @Override public void onExtractedCursorMovement(final int dx, final int dy) { - if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return; + if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) return; super.onExtractedCursorMovement(dx, dy); } @@ -974,7 +966,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } } } - if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return; + if (!mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) return; mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; if (applicationSpecifiedCompletions == null) { clearSuggestionStrip(); @@ -1039,7 +1031,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } final int keyboardHeight = mainKeyboardView.getHeight(); final int suggestionsHeight = mSuggestionsContainer.getHeight(); - final int displayHeight = mResources.getDisplayMetrics().heightPixels; + final int displayHeight = getResources().getDisplayMetrics().heightPixels; final Rect rect = new Rect(); mKeyPreviewBackingView.getWindowVisibleDisplayFrame(rect); final int notificationBarHeight = rect.top; @@ -1118,10 +1110,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // the composing word, reset the last composed word, tell the inputconnection about it. private void resetEntireInputState(final int newCursorPosition) { resetComposingState(true /* alsoResetLastComposedWord */); - if (mCurrentSettings.mBigramPredictionEnabled) { + if (mSettings.getCurrent().mBigramPredictionEnabled) { clearSuggestionStrip(); } else { - setSuggestionStrip(mCurrentSettings.mSuggestPuncList, false); + setSuggestionStrip(mSettings.getCurrent().mSuggestPuncList, false); } mConnection.resetCachesUponCursorMove(newCursorPosition); } @@ -1139,7 +1131,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, separatorString); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().onWordComplete(typedWord, Long.MAX_VALUE); + ResearchLogger.getInstance().onWordFinished(typedWord); } } } @@ -1147,7 +1139,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // Called from the KeyboardSwitcher which needs to know auto caps state to display // the right layout. public int getCurrentAutoCapsState() { - if (!mCurrentSettings.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF; + if (!mSettings.getCurrent().mAutoCap) return Constants.TextUtils.CAP_MODE_OFF; final EditorInfo ei = getCurrentInputEditorInfo(); if (ei == null) return Constants.TextUtils.CAP_MODE_OFF; @@ -1179,16 +1171,15 @@ public final class LatinIME extends InputMethodService implements KeyboardAction final String text = lastTwo.charAt(1) + " "; mConnection.commitText(text, 1); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().onWordComplete(text, Long.MAX_VALUE); - ResearchLogger.latinIME_swapSwapperAndSpace(); + ResearchLogger.latinIME_swapSwapperAndSpace(text); } mKeyboardSwitcher.updateShiftState(); } } private boolean maybeDoubleSpacePeriod() { - if (!mCurrentSettings.mCorrectionEnabled) return false; - if (!mCurrentSettings.mUseDoubleSpacePeriod) return false; + if (!mSettings.getCurrent().mCorrectionEnabled) return false; + if (!mSettings.getCurrent().mUseDoubleSpacePeriod) return false; if (!mHandler.isAcceptingDoubleSpacePeriod()) return false; final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0); if (lastThree != null && lastThree.length() == 3 @@ -1200,7 +1191,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction final String textToInsert = ". "; mConnection.commitText(textToInsert, 1); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().onWordComplete(textToInsert, Long.MAX_VALUE); + ResearchLogger.latinIME_maybeDoubleSpacePeriod(textToInsert); } mKeyboardSwitcher.updateShiftState(); return true; @@ -1291,7 +1282,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // TODO: Revise the language switch key behavior to make it much smarter and more reasonable. private void handleLanguageSwitchKey() { final IBinder token = getWindow().getWindow().getAttributes().token; - if (mCurrentSettings.mIncludesOtherImesInLanguageSwitchList) { + if (mSettings.getCurrent().mIncludesOtherImesInLanguageSwitchList) { mRichImm.switchToNextInputMethod(token, false /* onlyCurrentIme */); return; } @@ -1393,7 +1384,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction break; default: mSpaceState = SPACE_STATE_NONE; - if (mCurrentSettings.isWordSeparator(primaryCode)) { + if (mSettings.getCurrent().isWordSeparator(primaryCode)) { didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState); } else { if (SPACE_STATE_PHANTOM == spaceState) { @@ -1449,7 +1440,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } mConnection.commitText(text, 1); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().onWordComplete(text, Long.MAX_VALUE); + ResearchLogger.latinIME_onTextInput(text); } mConnection.endBatchEdit(); // Space state must be updated before calling updateShiftState @@ -1491,8 +1482,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // should usually be followed by a space, and it should be more readable. if (Constants.NOT_A_CODE != codePointBeforeCursor && !Character.isWhitespace(codePointBeforeCursor) - && !mCurrentSettings.isPhantomSpacePromotingSymbol(codePointBeforeCursor) - && !mCurrentSettings.isWeakSpaceStripper(codePointBeforeCursor)) { + && !mSettings.getCurrent().isPhantomSpacePromotingSymbol(codePointBeforeCursor) + && !mSettings.getCurrent().isWeakSpaceStripper(codePointBeforeCursor)) { mSpaceState = SPACE_STATE_PHANTOM; } } @@ -1599,9 +1590,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (dismissGestureFloatingPreviewText) { mainKeyboardView.dismissGestureFloatingPreviewText(); } else { - final String batchInputText = suggestedWords.isEmpty() - ? null : suggestedWords.getWord(0); - mainKeyboardView.showGestureFloatingPreviewText(batchInputText); + mainKeyboardView.showGestureFloatingPreviewText(suggestedWords); } } @@ -1710,7 +1699,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } if (SPACE_STATE_DOUBLE == spaceState) { mHandler.cancelDoubleSpacePeriodTimer(); - if (mConnection.revertDoubleSpace()) { + if (mConnection.revertDoubleSpacePeriod()) { // No need to reset mSpaceState, it has already be done (that's why we // receive it as a parameter) return; @@ -1749,7 +1738,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mConnection.deleteSurroundingText(1, 0); } } - if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { + if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) { restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(); } } @@ -1763,10 +1752,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } else if ((SPACE_STATE_WEAK == spaceState || SPACE_STATE_SWAP_PUNCTUATION == spaceState) && isFromSuggestionStrip) { - if (mCurrentSettings.isWeakSpaceSwapper(code)) { + if (mSettings.getCurrent().isWeakSpaceSwapper(code)) { return true; } else { - if (mCurrentSettings.isWeakSpaceStripper(code)) { + if (mSettings.getCurrent().isWeakSpaceStripper(code)) { mConnection.removeTrailingSpace(); } return false; @@ -1781,7 +1770,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction boolean isComposingWord = mWordComposer.isComposingWord(); if (SPACE_STATE_PHANTOM == spaceState && - !mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode)) { + !mSettings.getCurrent().isSymbolExcludedFromWordSeparators(primaryCode)) { if (isComposingWord) { // Sanity check throw new RuntimeException("Should not be composing here"); @@ -1793,9 +1782,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // dozen milliseconds. Avoid calling it as much as possible, since we are on the UI // thread here. if (!isComposingWord && (isAlphabet(primaryCode) - || mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode)) - && mCurrentSettings.isSuggestionsRequested(mDisplayOrientation) && - !mConnection.isCursorTouchingWord(mCurrentSettings)) { + || mSettings.getCurrent().isSymbolExcludedFromWordSeparators(primaryCode)) + && mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation) && + !mConnection.isCursorTouchingWord(mSettings.getCurrent())) { // Reset entirely the composing state anyway, then start composing a new word unless // the character is a single quote. The idea here is, single quote is not a // separator and it should be treated as a normal character, except in the first @@ -1849,7 +1838,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction boolean didAutoCorrect = false; // Handle separator if (mWordComposer.isComposingWord()) { - if (mCurrentSettings.mCorrectionEnabled) { + if (mSettings.getCurrent().mCorrectionEnabled) { // TODO: maybe cache Strings in an <String> sparse array or something commitCurrentAutoCorrection(new String(new int[]{primaryCode}, 0, 1)); didAutoCorrect = true; @@ -1862,13 +1851,13 @@ public final class LatinIME extends InputMethodService implements KeyboardAction Constants.SUGGESTION_STRIP_COORDINATE == x); if (SPACE_STATE_PHANTOM == spaceState && - mCurrentSettings.isPhantomSpacePromotingSymbol(primaryCode)) { + mSettings.getCurrent().isPhantomSpacePromotingSymbol(primaryCode)) { promotePhantomSpace(); } sendKeyCodePoint(primaryCode); if (Constants.CODE_SPACE == primaryCode) { - if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { + if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) { if (maybeDoubleSpacePeriod()) { mSpaceState = SPACE_STATE_DOUBLE; } else if (!isShowingPunctuationList()) { @@ -1877,7 +1866,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } mHandler.startDoubleSpacePeriodTimer(); - if (!mConnection.isCursorTouchingWord(mCurrentSettings)) { + if (!mConnection.isCursorTouchingWord(mSettings.getCurrent())) { mHandler.postUpdateSuggestionStrip(); } } else { @@ -1885,8 +1874,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction swapSwapperAndSpace(); mSpaceState = SPACE_STATE_SWAP_PUNCTUATION; } else if (SPACE_STATE_PHANTOM == spaceState - && !mCurrentSettings.isWeakSpaceStripper(primaryCode) - && !mCurrentSettings.isPhantomSpacePromotingSymbol(primaryCode)) { + && !mSettings.getCurrent().isWeakSpaceStripper(primaryCode) + && !mSettings.getCurrent().isPhantomSpacePromotingSymbol(primaryCode)) { // If we are in phantom space state, and the user presses a separator, we want to // stay in phantom space state so that the next keypress has a chance to add the // space. For example, if I type "Good dat", pick "day" from the suggestion strip @@ -1933,7 +1922,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction @UsedForTesting boolean isShowingPunctuationList() { if (mSuggestionStripView == null) return false; - return mCurrentSettings.mSuggestPuncList == mSuggestionStripView.getSuggestions(); + return mSettings.getCurrent().mSuggestPuncList == mSuggestionStripView.getSuggestions(); } private boolean isSuggestionsStripVisible() { @@ -1941,11 +1930,11 @@ public final class LatinIME extends InputMethodService implements KeyboardAction return false; if (mSuggestionStripView.isShowingAddToDictionaryHint()) return true; - if (!mCurrentSettings.isSuggestionStripVisibleInOrientation(mDisplayOrientation)) + if (!mSettings.getCurrent().isSuggestionStripVisibleInOrientation(mDisplayOrientation)) return false; - if (mCurrentSettings.isApplicationSpecifiedCompletionsOn()) + if (mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) return true; - return mCurrentSettings.isSuggestionsRequested(mDisplayOrientation); + return mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation); } private void clearSuggestionStrip() { @@ -1979,7 +1968,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mHandler.cancelUpdateSuggestionStrip(); // Check if we have a suggestion engine attached. - if (mSuggest == null || !mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { + if (mSuggest == null + || !mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) { if (mWordComposer.isComposingWord()) { Log.w(TAG, "Called updateSuggestionsOrPredictions but suggestions were not " + "requested!"); @@ -1987,7 +1977,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction return; } - if (!mWordComposer.isComposingWord() && !mCurrentSettings.mBigramPredictionEnabled) { + if (!mWordComposer.isComposingWord() && !mSettings.getCurrent().mBigramPredictionEnabled) { setPunctuationSuggestions(); return; } @@ -2008,10 +1998,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // should just skip whitespace if any, so 1. // TODO: this is slow (2-way IPC) - we should probably cache this instead. final String prevWord = - mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, + mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators, mWordComposer.isComposingWord() ? 2 : 1); final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer, - prevWord, keyboard.getProximityInfo(), mCurrentSettings.mCorrectionEnabled, + prevWord, keyboard.getProximityInfo(), mSettings.getCurrent().mCorrectionEnabled, sessionId); return maybeRetrieveOlderSuggestions(typedWord, suggestedWords); } @@ -2036,7 +2026,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private SuggestedWords getOlderSuggestions(final String typedWord) { SuggestedWords previousSuggestions = mSuggestionStripView.getSuggestions(); - if (previousSuggestions == mCurrentSettings.mSuggestPuncList) { + if (previousSuggestions == mSettings.getCurrent().mSuggestPuncList) { previousSuggestions = SuggestedWords.EMPTY; } if (typedWord == null) { @@ -2087,12 +2077,11 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (ProductionFlag.IS_INTERNAL) { Stats.onAutoCorrection(typedWord, autoCorrection, separatorString, mWordComposer); } - mExpectingUpdateSelection = true; if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection, separatorString); } - + mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separatorString); if (!typedWord.equals(autoCorrection)) { @@ -2135,13 +2124,13 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // the current batch input text and there is no need for a phantom space. && !mWordComposer.isBatchMode()) { int firstChar = Character.codePointAt(suggestion, 0); - if ((!mCurrentSettings.isWeakSpaceStripper(firstChar)) - && (!mCurrentSettings.isWeakSpaceSwapper(firstChar))) { + if ((!mSettings.getCurrent().isWeakSpaceStripper(firstChar)) + && (!mSettings.getCurrent().isWeakSpaceSwapper(firstChar))) { promotePhantomSpace(); } } - if (mCurrentSettings.isApplicationSpecifiedCompletionsOn() + if (mSettings.getCurrent().isApplicationSpecifiedCompletionsOn() && mApplicationSpecifiedCompletions != null && index >= 0 && index < mApplicationSpecifiedCompletions.length) { if (mSuggestionStripView != null) { @@ -2186,7 +2175,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) { mSuggestionStripView.showAddToDictionaryHint( - suggestion, mCurrentSettings.mHintToSaveText); + suggestion, mSettings.getCurrent().mHintToSaveText); } else { // If we're not showing the "Touch again to save", then update the suggestion strip. mHandler.postUpdateSuggestionStrip(); @@ -2212,10 +2201,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } private void setPunctuationSuggestions() { - if (mCurrentSettings.mBigramPredictionEnabled) { + if (mSettings.getCurrent().mBigramPredictionEnabled) { clearSuggestionStrip(); } else { - setSuggestionStrip(mCurrentSettings.mSuggestPuncList, false); + setSuggestionStrip(mSettings.getCurrent().mSuggestPuncList, false); } setAutoCorrectionIndicator(false); setSuggestionStripShown(isSuggestionsStripVisible()); @@ -2228,12 +2217,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // If correction is not enabled, we don't add words to the user history dictionary. // That's to avoid unintended additions in some sensitive fields, or fields that // expect to receive non-words. - if (!mCurrentSettings.mCorrectionEnabled) return null; + if (!mSettings.getCurrent().mCorrectionEnabled) return null; final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary; if (userHistoryDictionary != null) { final String prevWord - = mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, 2); + = mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators, 2); final String secondWord; if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) { secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale()); @@ -2256,7 +2245,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction * word, else do nothing. */ private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() { - final CharSequence word = mConnection.getWordBeforeCursorIfAtEndOfWord(mCurrentSettings); + final CharSequence word = + mConnection.getWordBeforeCursorIfAtEndOfWord(mSettings.getCurrent()); if (null != word) { restartSuggestionsOnWordBeforeCursor(word); } @@ -2303,7 +2293,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_revertCommit(committedWord, originallyTypedWord); - ResearchLogger.getInstance().onWordComplete(originallyTypedWord, Long.MAX_VALUE); } // Don't restart suggestion yet. We'll restart if the user deletes the // separator. @@ -2314,14 +2303,14 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // This essentially inserts a space, and that's it. public void promotePhantomSpace() { - if (mCurrentSettings.shouldInsertSpacesAutomatically()) { + if (mSettings.getCurrent().shouldInsertSpacesAutomatically()) { sendKeyCodePoint(Constants.CODE_SPACE); } } // Used by the RingCharBuffer public boolean isWordSeparator(final int code) { - return mCurrentSettings.isWordSeparator(code); + return mSettings.getCurrent().isWordSeparator(code); } // TODO: Make this private @@ -2334,7 +2323,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction loadSettings(); if (mKeyboardSwitcher.getMainKeyboardView() != null) { // Reload keyboard because the current language has been changed. - mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mCurrentSettings); + mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettings.getCurrent()); } // Since we just changed languages, we should re-evaluate suggestions with whatever word // we are currently composing. If we are not composing anything, we may want to display @@ -2495,7 +2484,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction .append("\nPackage : ").append(mTargetApplicationInfo.packageName) .append("\nTarget app sdk version : ") .append(mTargetApplicationInfo.targetSdkVersion) - .append("\nAttributes : ").append(mCurrentSettings.getInputAttributesDebugString()) + .append("\nAttributes : ").append(mSettings.getCurrent().mInputAttributes) .append("\nContext : ").append(context); throw new RuntimeException(s.toString()); } @@ -2509,13 +2498,14 @@ public final class LatinIME extends InputMethodService implements KeyboardAction final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1; p.println(" Keyboard mode = " + keyboardMode); + final SettingsValues settingsValues = mSettings.getCurrent(); p.println(" mIsSuggestionsSuggestionsRequested = " - + mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)); - p.println(" mCorrectionEnabled=" + mCurrentSettings.mCorrectionEnabled); + + settingsValues.isSuggestionsRequested(mDisplayOrientation)); + p.println(" mCorrectionEnabled=" + settingsValues.mCorrectionEnabled); p.println(" isComposingWord=" + mWordComposer.isComposingWord()); - p.println(" mSoundOn=" + mCurrentSettings.mSoundOn); - p.println(" mVibrateOn=" + mCurrentSettings.mVibrateOn); - p.println(" mKeyPreviewPopupOn=" + mCurrentSettings.mKeyPreviewPopupOn); - p.println(" inputAttributes=" + mCurrentSettings.getInputAttributesDebugString()); + p.println(" mSoundOn=" + settingsValues.mSoundOn); + p.println(" mVibrateOn=" + settingsValues.mVibrateOn); + p.println(" mKeyPreviewPopupOn=" + settingsValues.mKeyPreviewPopupOn); + p.println(" inputAttributes=" + settingsValues.mInputAttributes); } } diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 9cb24b54e..0d3ebacb1 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -643,7 +643,7 @@ public final class RichInputConnection { return word; } - public boolean revertDoubleSpace() { + public boolean revertDoubleSpacePeriod() { if (DEBUG_BATCH_NESTING) checkBatchEdit(); // Here we test whether we indeed have a period and a space before us. This should not // be needed, but it's there just in case something went wrong. @@ -660,7 +660,7 @@ public final class RichInputConnection { final String doubleSpace = " "; commitText(doubleSpace, 1); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().onWordComplete(doubleSpace, Long.MAX_VALUE); + ResearchLogger.richInputConnection_revertDoubleSpacePeriod(doubleSpace); } return true; } @@ -685,7 +685,7 @@ public final class RichInputConnection { final String text = " " + textBeforeCursor.subSequence(0, 1); commitText(text, 1); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().onWordComplete(text, Long.MAX_VALUE); + ResearchLogger.richInputConnection_revertSwapPunctuation(text); } return true; } diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 222adcb2e..1d9d85b47 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2013 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 @@ -16,27 +16,16 @@ package com.android.inputmethod.latin; -import android.app.backup.BackupManager; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; -import android.media.AudioManager; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceGroup; -import android.preference.PreferenceScreen; -import android.view.inputmethod.InputMethodSubtype; +import android.preference.PreferenceManager; -import com.android.inputmethod.latin.define.ProductionFlag; -import com.android.inputmethodcommon.InputMethodSettingsFragment; +import com.android.inputmethod.latin.LocaleUtils.RunInLocale; -public final class Settings extends InputMethodSettingsFragment - implements SharedPreferences.OnSharedPreferenceChangeListener { +import java.util.Locale; +public final class Settings implements SharedPreferences.OnSharedPreferenceChangeListener { // In the same order as xml/prefs.xml public static final String PREF_GENERAL_SETTINGS = "general_settings"; public static final String PREF_AUTO_CAP = "auto_cap"; @@ -77,338 +66,54 @@ public final class Settings extends InputMethodSettingsFragment public static final String PREF_SELECTED_LANGUAGES = "selected_languages"; public static final String PREF_DEBUG_SETTINGS = "debug_settings"; - private PreferenceScreen mKeypressVibrationDurationSettingsPref; - private PreferenceScreen mKeypressSoundVolumeSettingsPref; - private ListPreference mVoicePreference; - private ListPreference mShowCorrectionSuggestionsPreference; - private ListPreference mAutoCorrectionThresholdPreference; - private ListPreference mKeyPreviewPopupDismissDelay; - // Use bigrams to predict the next word when there is no input for it yet - private CheckBoxPreference mBigramPrediction; - private Preference mDebugSettingsPreference; + private Resources mRes; + private SharedPreferences mPrefs; + private Locale mCurrentLocale; + private SettingsValues mSettingsValues; - private static void setPreferenceEnabled(final Preference preference, final boolean enabled) { - if (preference != null) { - preference.setEnabled(enabled); - } - } + private static final Settings sInstance = new Settings(); - private void ensureConsistencyOfAutoCorrectionSettings() { - final String autoCorrectionOff = getResources().getString( - R.string.auto_correction_threshold_mode_index_off); - final String currentSetting = mAutoCorrectionThresholdPreference.getValue(); - setPreferenceEnabled(mBigramPrediction, !currentSetting.equals(autoCorrectionOff)); + public static Settings getInstance() { + return sInstance; } - @Override - public void onCreate(final Bundle icicle) { - super.onCreate(icicle); - setInputMethodSettingsCategoryTitle(R.string.language_selection_title); - setSubtypeEnablerTitle(R.string.select_language); - addPreferencesFromResource(R.xml.prefs); - - final Resources res = getResources(); - final Context context = getActivity(); - - // When we are called from the Settings application but we are not already running, the - // {@link SubtypeLocale} class may not have been initialized. It is safe to call - // {@link SubtypeLocale#init(Context)} multiple times. - SubtypeLocale.init(context); - mVoicePreference = (ListPreference) findPreference(PREF_VOICE_MODE); - mShowCorrectionSuggestionsPreference = - (ListPreference) findPreference(PREF_SHOW_SUGGESTIONS_SETTING); - final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); - prefs.registerOnSharedPreferenceChangeListener(this); - - mAutoCorrectionThresholdPreference = - (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD); - mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS); - ensureConsistencyOfAutoCorrectionSettings(); - - final PreferenceGroup generalSettings = - (PreferenceGroup) findPreference(PREF_GENERAL_SETTINGS); - final PreferenceGroup textCorrectionGroup = - (PreferenceGroup) findPreference(PREF_CORRECTION_SETTINGS); - final PreferenceGroup gestureTypingSettings = - (PreferenceGroup) findPreference(PREF_GESTURE_SETTINGS); - final PreferenceGroup miscSettings = - (PreferenceGroup) findPreference(PREF_MISC_SETTINGS); - - mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS); - if (mDebugSettingsPreference != null) { - if (ProductionFlag.IS_INTERNAL) { - final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN); - debugSettingsIntent.setClassName( - context.getPackageName(), DebugSettingsActivity.class.getName()); - mDebugSettingsPreference.setIntent(debugSettingsIntent); - } else { - miscSettings.removePreference(mDebugSettingsPreference); - } - } - - final boolean showVoiceKeyOption = res.getBoolean( - R.bool.config_enable_show_voice_key_option); - if (!showVoiceKeyOption) { - generalSettings.removePreference(mVoicePreference); - } - - final PreferenceGroup advancedSettings = - (PreferenceGroup) findPreference(PREF_ADVANCED_SETTINGS); - if (!VibratorUtils.getInstance(context).hasVibrator()) { - generalSettings.removePreference(findPreference(PREF_VIBRATE_ON)); - if (null != advancedSettings) { // Theoretically advancedSettings cannot be null - advancedSettings.removePreference(findPreference(PREF_VIBRATION_DURATION_SETTINGS)); - } - } - - final boolean showKeyPreviewPopupOption = res.getBoolean( - R.bool.config_enable_show_popup_on_keypress_option); - mKeyPreviewPopupDismissDelay = - (ListPreference) findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); - if (!showKeyPreviewPopupOption) { - generalSettings.removePreference(findPreference(PREF_POPUP_ON)); - if (null != advancedSettings) { // Theoretically advancedSettings cannot be null - advancedSettings.removePreference(mKeyPreviewPopupDismissDelay); - } - } else { - final String[] entries = new String[] { - res.getString(R.string.key_preview_popup_dismiss_no_delay), - res.getString(R.string.key_preview_popup_dismiss_default_delay), - }; - final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger( - R.integer.config_key_preview_linger_timeout)); - mKeyPreviewPopupDismissDelay.setEntries(entries); - mKeyPreviewPopupDismissDelay.setEntryValues( - new String[] { "0", popupDismissDelayDefaultValue }); - if (null == mKeyPreviewPopupDismissDelay.getValue()) { - mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); - } - setPreferenceEnabled(mKeyPreviewPopupDismissDelay, - SettingsValues.isKeyPreviewPopupEnabled(prefs, res)); - } - - setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST), - SettingsValues.showsLanguageSwitchKey(prefs)); - - final PreferenceScreen dictionaryLink = - (PreferenceScreen) findPreference(PREF_CONFIGURE_DICTIONARIES_KEY); - final Intent intent = dictionaryLink.getIntent(); - - final int number = context.getPackageManager().queryIntentActivities(intent, 0).size(); - // TODO: The experimental version is not supported by the Dictionary Pack Service yet - if (ProductionFlag.IS_EXPERIMENTAL || 0 >= number) { - textCorrectionGroup.removePreference(dictionaryLink); - } - - final boolean gestureInputEnabledByBuildConfig = res.getBoolean( - R.bool.config_gesture_input_enabled_by_build_config); - if (!gestureInputEnabledByBuildConfig) { - getPreferenceScreen().removePreference(gestureTypingSettings); - } - - mKeypressVibrationDurationSettingsPref = - (PreferenceScreen) findPreference(PREF_VIBRATION_DURATION_SETTINGS); - if (mKeypressVibrationDurationSettingsPref != null) { - mKeypressVibrationDurationSettingsPref.setOnPreferenceClickListener( - new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference arg0) { - showKeypressVibrationDurationSettingsDialog(); - return true; - } - }); - mKeypressVibrationDurationSettingsPref.setSummary( - res.getString(R.string.settings_keypress_vibration_duration, - SettingsValues.getCurrentVibrationDuration(prefs, res))); - } + public static void init(final Context context) { + sInstance.onCreate(context); + } - mKeypressSoundVolumeSettingsPref = - (PreferenceScreen) findPreference(PREF_KEYPRESS_SOUND_VOLUME); - if (mKeypressSoundVolumeSettingsPref != null) { - mKeypressSoundVolumeSettingsPref.setOnPreferenceClickListener( - new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference arg0) { - showKeypressSoundVolumeSettingDialog(); - return true; - } - }); - mKeypressSoundVolumeSettingsPref.setSummary(String.valueOf( - getCurrentKeyPressSoundVolumePercent(prefs, res))); - } - refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res); + private Settings() { + // Intentional empty constructor for singleton. } - @Override - public void onResume() { - super.onResume(); - final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled(); - if (isShortcutImeEnabled) { - updateVoiceModeSummary(); - } else { - getPreferenceScreen().removePreference(mVoicePreference); - } - updateShowCorrectionSuggestionsSummary(); - updateKeyPreviewPopupDelaySummary(); - updateCustomInputStylesSummary(); + private void onCreate(final Context context) { + mRes = context.getResources(); + mPrefs = PreferenceManager.getDefaultSharedPreferences(context); + mPrefs.registerOnSharedPreferenceChangeListener(this); } - @Override public void onDestroy() { - getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener( - this); - super.onDestroy(); + mPrefs.unregisterOnSharedPreferenceChangeListener(this); } @Override public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { - (new BackupManager(getActivity())).dataChanged(); - if (key.equals(PREF_POPUP_ON)) { - setPreferenceEnabled(findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY), - prefs.getBoolean(PREF_POPUP_ON, true)); - } else if (key.equals(PREF_SHOW_LANGUAGE_SWITCH_KEY)) { - setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST), - SettingsValues.showsLanguageSwitchKey(prefs)); - } else if (key.equals(PREF_GESTURE_INPUT)) { - final boolean gestureInputEnabledByConfig = getResources().getBoolean( - R.bool.config_gesture_input_enabled_by_build_config); - if (gestureInputEnabledByConfig) { - final boolean gestureInputEnabledByUser = prefs.getBoolean( - PREF_GESTURE_INPUT, true); - setPreferenceEnabled(findPreference(PREF_GESTURE_PREVIEW_TRAIL), - gestureInputEnabledByUser); - setPreferenceEnabled(findPreference(PREF_GESTURE_FLOATING_PREVIEW_TEXT), - gestureInputEnabledByUser); - } - } - ensureConsistencyOfAutoCorrectionSettings(); - updateVoiceModeSummary(); - updateShowCorrectionSuggestionsSummary(); - updateKeyPreviewPopupDelaySummary(); - refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources()); + loadSettings(mCurrentLocale, mSettingsValues.mInputAttributes); } - private void updateShowCorrectionSuggestionsSummary() { - mShowCorrectionSuggestionsPreference.setSummary( - getResources().getStringArray(R.array.prefs_suggestion_visibilities) - [mShowCorrectionSuggestionsPreference.findIndexOfValue( - mShowCorrectionSuggestionsPreference.getValue())]); - } - - private void updateCustomInputStylesSummary() { - final PreferenceScreen customInputStyles = - (PreferenceScreen)findPreference(PREF_CUSTOM_INPUT_STYLES); - final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); - final Resources res = getResources(); - final String prefSubtype = SettingsValues.getPrefAdditionalSubtypes(prefs, res); - final InputMethodSubtype[] subtypes = - AdditionalSubtype.createAdditionalSubtypesArray(prefSubtype); - final StringBuilder styles = new StringBuilder(); - for (final InputMethodSubtype subtype : subtypes) { - if (styles.length() > 0) styles.append(", "); - styles.append(SubtypeLocale.getSubtypeDisplayName(subtype, res)); - } - customInputStyles.setSummary(styles); - } - - private void updateKeyPreviewPopupDelaySummary() { - final ListPreference lp = mKeyPreviewPopupDismissDelay; - final CharSequence[] entries = lp.getEntries(); - if (entries == null || entries.length <= 0) return; - lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]); - } - - private void updateVoiceModeSummary() { - mVoicePreference.setSummary( - getResources().getStringArray(R.array.voice_input_modes_summary) - [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]); - } - - private void refreshEnablingsOfKeypressSoundAndVibrationSettings( - final SharedPreferences sp, final Resources res) { - if (mKeypressVibrationDurationSettingsPref != null) { - final boolean hasVibratorHardware = VibratorUtils.getInstance(getActivity()) - .hasVibrator(); - final boolean vibrateOnByUser = sp.getBoolean(Settings.PREF_VIBRATE_ON, - res.getBoolean(R.bool.config_default_vibration_enabled)); - setPreferenceEnabled(mKeypressVibrationDurationSettingsPref, - hasVibratorHardware && vibrateOnByUser); - } - - if (mKeypressSoundVolumeSettingsPref != null) { - final boolean soundOn = sp.getBoolean(Settings.PREF_SOUND_ON, - res.getBoolean(R.bool.config_default_sound_enabled)); - setPreferenceEnabled(mKeypressSoundVolumeSettingsPref, soundOn); - } - } - - private void showKeypressVibrationDurationSettingsDialog() { - final SharedPreferences sp = getPreferenceManager().getSharedPreferences(); - final Context context = getActivity(); - final PreferenceScreen settingsPref = mKeypressVibrationDurationSettingsPref; - final SeekBarDialog.Listener listener = new SeekBarDialog.Adapter() { + public void loadSettings(final Locale locale, final InputAttributes inputAttributes) { + mCurrentLocale = locale; + final SharedPreferences prefs = mPrefs; + final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() { @Override - public void onPositiveButtonClick(final SeekBarDialog dialog) { - final int ms = dialog.getValue(); - sp.edit().putInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, ms).apply(); - if (settingsPref != null) { - settingsPref.setSummary(dialog.getValueText()); - } - } - - @Override - public void onStopTrackingTouch(final SeekBarDialog dialog) { - final int ms = dialog.getValue(); - VibratorUtils.getInstance(context).vibrate(ms); + protected SettingsValues job(final Resources res) { + return new SettingsValues(prefs, res, inputAttributes); } }; - final int currentMs = SettingsValues.getCurrentVibrationDuration(sp, getResources()); - final SeekBarDialog.Builder builder = new SeekBarDialog.Builder(context); - builder.setTitle(R.string.prefs_keypress_vibration_duration_settings) - .setListener(listener) - .setMaxValue(AudioAndHapticFeedbackManager.MAX_KEYPRESS_VIBRATION_DURATION) - .setValueFromat(R.string.settings_keypress_vibration_duration) - .setValue(currentMs) - .create() - .show(); - } - - private static final int PERCENT_INT = 100; - private static final float PERCENT_FLOAT = 100.0f; - - private static int getCurrentKeyPressSoundVolumePercent(final SharedPreferences sp, - final Resources res) { - return (int)(SettingsValues.getCurrentKeypressSoundVolume(sp, res) * PERCENT_FLOAT); + mSettingsValues = job.runInLocale(mRes, locale); } - private void showKeypressSoundVolumeSettingDialog() { - final SharedPreferences sp = getPreferenceManager().getSharedPreferences(); - final Context context = getActivity(); - final AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - final PreferenceScreen settingsPref = mKeypressSoundVolumeSettingsPref; - final SeekBarDialog.Listener listener = new SeekBarDialog.Adapter() { - @Override - public void onPositiveButtonClick(final SeekBarDialog dialog) { - final float volume = dialog.getValue() / PERCENT_FLOAT; - sp.edit().putFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, volume).apply(); - if (settingsPref != null) { - settingsPref.setSummary(dialog.getValueText()); - } - } - - @Override - public void onStopTrackingTouch(final SeekBarDialog dialog) { - final float volume = dialog.getValue() / PERCENT_FLOAT; - am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, volume); - } - }; - final SeekBarDialog.Builder builder = new SeekBarDialog.Builder(context); - final int currentVolumeInt = getCurrentKeyPressSoundVolumePercent(sp, getResources()); - builder.setTitle(R.string.prefs_keypress_sound_volume_settings) - .setListener(listener) - .setMaxValue(PERCENT_INT) - .setValue(currentVolumeInt) - .create() - .show(); + // TODO: Remove this method and add proxy method to SettingsValues. + public SettingsValues getCurrent() { + return mSettingsValues; } } diff --git a/java/src/com/android/inputmethod/latin/SettingsActivity.java b/java/src/com/android/inputmethod/latin/SettingsActivity.java index 0d3c8ebb7..3aeb10113 100644 --- a/java/src/com/android/inputmethod/latin/SettingsActivity.java +++ b/java/src/com/android/inputmethod/latin/SettingsActivity.java @@ -20,7 +20,7 @@ import android.content.Intent; import android.preference.PreferenceActivity; public final class SettingsActivity extends PreferenceActivity { - private static final String DEFAULT_FRAGMENT = Settings.class.getName(); + private static final String DEFAULT_FRAGMENT = SettingsFragment.class.getName(); @Override public Intent getIntent() { diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java new file mode 100644 index 000000000..a2980bfa2 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2008 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.app.backup.BackupManager; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.media.AudioManager; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceGroup; +import android.preference.PreferenceScreen; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethodcommon.InputMethodSettingsFragment; + +public final class SettingsFragment extends InputMethodSettingsFragment + implements SharedPreferences.OnSharedPreferenceChangeListener { + private PreferenceScreen mKeypressVibrationDurationSettingsPref; + private PreferenceScreen mKeypressSoundVolumeSettingsPref; + private ListPreference mVoicePreference; + private ListPreference mShowCorrectionSuggestionsPreference; + private ListPreference mAutoCorrectionThresholdPreference; + private ListPreference mKeyPreviewPopupDismissDelay; + // Use bigrams to predict the next word when there is no input for it yet + private CheckBoxPreference mBigramPrediction; + private Preference mDebugSettingsPreference; + + private void setPreferenceEnabled(final String preferenceKey, final boolean enabled) { + final Preference preference = findPreference(preferenceKey); + if (preference != null) { + preference.setEnabled(enabled); + } + } + + private void ensureConsistencyOfAutoCorrectionSettings() { + final String autoCorrectionOff = getResources().getString( + R.string.auto_correction_threshold_mode_index_off); + final String currentSetting = mAutoCorrectionThresholdPreference.getValue(); + mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff)); + } + + @Override + public void onCreate(final Bundle icicle) { + super.onCreate(icicle); + setInputMethodSettingsCategoryTitle(R.string.language_selection_title); + setSubtypeEnablerTitle(R.string.select_language); + addPreferencesFromResource(R.xml.prefs); + + final Resources res = getResources(); + final Context context = getActivity(); + + // When we are called from the Settings application but we are not already running, the + // {@link SubtypeLocale} class may not have been initialized. It is safe to call + // {@link SubtypeLocale#init(Context)} multiple times. + SubtypeLocale.init(context); + mVoicePreference = (ListPreference) findPreference(Settings.PREF_VOICE_MODE); + mShowCorrectionSuggestionsPreference = + (ListPreference) findPreference(Settings.PREF_SHOW_SUGGESTIONS_SETTING); + final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); + prefs.registerOnSharedPreferenceChangeListener(this); + + mAutoCorrectionThresholdPreference = + (ListPreference) findPreference(Settings.PREF_AUTO_CORRECTION_THRESHOLD); + mBigramPrediction = (CheckBoxPreference) findPreference(Settings.PREF_BIGRAM_PREDICTIONS); + ensureConsistencyOfAutoCorrectionSettings(); + + final PreferenceGroup generalSettings = + (PreferenceGroup) findPreference(Settings.PREF_GENERAL_SETTINGS); + final PreferenceGroup textCorrectionGroup = + (PreferenceGroup) findPreference(Settings.PREF_CORRECTION_SETTINGS); + final PreferenceGroup gestureTypingSettings = + (PreferenceGroup) findPreference(Settings.PREF_GESTURE_SETTINGS); + final PreferenceGroup miscSettings = + (PreferenceGroup) findPreference(Settings.PREF_MISC_SETTINGS); + + mDebugSettingsPreference = findPreference(Settings.PREF_DEBUG_SETTINGS); + if (mDebugSettingsPreference != null) { + if (ProductionFlag.IS_INTERNAL) { + final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN); + debugSettingsIntent.setClassName( + context.getPackageName(), DebugSettingsActivity.class.getName()); + mDebugSettingsPreference.setIntent(debugSettingsIntent); + } else { + miscSettings.removePreference(mDebugSettingsPreference); + } + } + + final boolean showVoiceKeyOption = res.getBoolean( + R.bool.config_enable_show_voice_key_option); + if (!showVoiceKeyOption) { + generalSettings.removePreference(mVoicePreference); + } + + final PreferenceGroup advancedSettings = + (PreferenceGroup) findPreference(Settings.PREF_ADVANCED_SETTINGS); + if (!AudioAndHapticFeedbackManager.getInstance().hasVibrator()) { + generalSettings.removePreference(findPreference(Settings.PREF_VIBRATE_ON)); + if (null != advancedSettings) { // Theoretically advancedSettings cannot be null + advancedSettings.removePreference( + findPreference(Settings.PREF_VIBRATION_DURATION_SETTINGS)); + } + } + + final boolean showKeyPreviewPopupOption = res.getBoolean( + R.bool.config_enable_show_popup_on_keypress_option); + mKeyPreviewPopupDismissDelay = + (ListPreference) findPreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); + if (!showKeyPreviewPopupOption) { + generalSettings.removePreference(findPreference(Settings.PREF_POPUP_ON)); + if (null != advancedSettings) { // Theoretically advancedSettings cannot be null + advancedSettings.removePreference(mKeyPreviewPopupDismissDelay); + } + } else { + final String[] entries = new String[] { + res.getString(R.string.key_preview_popup_dismiss_no_delay), + res.getString(R.string.key_preview_popup_dismiss_default_delay), + }; + final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger( + R.integer.config_key_preview_linger_timeout)); + mKeyPreviewPopupDismissDelay.setEntries(entries); + mKeyPreviewPopupDismissDelay.setEntryValues( + new String[] { "0", popupDismissDelayDefaultValue }); + if (null == mKeyPreviewPopupDismissDelay.getValue()) { + mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); + } + mKeyPreviewPopupDismissDelay.setEnabled( + SettingsValues.isKeyPreviewPopupEnabled(prefs, res)); + } + + setPreferenceEnabled(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, + SettingsValues.showsLanguageSwitchKey(prefs)); + + final PreferenceScreen dictionaryLink = + (PreferenceScreen) findPreference(Settings.PREF_CONFIGURE_DICTIONARIES_KEY); + final Intent intent = dictionaryLink.getIntent(); + + final int number = context.getPackageManager().queryIntentActivities(intent, 0).size(); + // TODO: The experimental version is not supported by the Dictionary Pack Service yet + if (ProductionFlag.IS_EXPERIMENTAL || 0 >= number) { + textCorrectionGroup.removePreference(dictionaryLink); + } + + final boolean gestureInputEnabledByBuildConfig = res.getBoolean( + R.bool.config_gesture_input_enabled_by_build_config); + if (!gestureInputEnabledByBuildConfig) { + getPreferenceScreen().removePreference(gestureTypingSettings); + } + + mKeypressVibrationDurationSettingsPref = + (PreferenceScreen) findPreference(Settings.PREF_VIBRATION_DURATION_SETTINGS); + if (mKeypressVibrationDurationSettingsPref != null) { + mKeypressVibrationDurationSettingsPref.setOnPreferenceClickListener( + new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference arg0) { + showKeypressVibrationDurationSettingsDialog(); + return true; + } + }); + mKeypressVibrationDurationSettingsPref.setSummary( + res.getString(R.string.settings_keypress_vibration_duration, + SettingsValues.getCurrentVibrationDuration(prefs, res))); + } + + mKeypressSoundVolumeSettingsPref = + (PreferenceScreen) findPreference(Settings.PREF_KEYPRESS_SOUND_VOLUME); + if (mKeypressSoundVolumeSettingsPref != null) { + mKeypressSoundVolumeSettingsPref.setOnPreferenceClickListener( + new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference arg0) { + showKeypressSoundVolumeSettingDialog(); + return true; + } + }); + mKeypressSoundVolumeSettingsPref.setSummary(String.valueOf( + getCurrentKeyPressSoundVolumePercent(prefs, res))); + } + refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res); + } + + @Override + public void onResume() { + super.onResume(); + final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled(); + if (isShortcutImeEnabled) { + updateVoiceModeSummary(); + } else { + getPreferenceScreen().removePreference(mVoicePreference); + } + updateShowCorrectionSuggestionsSummary(); + updateKeyPreviewPopupDelaySummary(); + updateCustomInputStylesSummary(); + } + + @Override + public void onDestroy() { + getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener( + this); + super.onDestroy(); + } + + @Override + public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { + (new BackupManager(getActivity())).dataChanged(); + if (key.equals(Settings.PREF_POPUP_ON)) { + setPreferenceEnabled(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, + prefs.getBoolean(Settings.PREF_POPUP_ON, true)); + } else if (key.equals(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY)) { + setPreferenceEnabled(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, + SettingsValues.showsLanguageSwitchKey(prefs)); + } else if (key.equals(Settings.PREF_GESTURE_INPUT)) { + final boolean gestureInputEnabledByConfig = getResources().getBoolean( + R.bool.config_gesture_input_enabled_by_build_config); + if (gestureInputEnabledByConfig) { + final boolean gestureInputEnabledByUser = prefs.getBoolean( + Settings.PREF_GESTURE_INPUT, true); + setPreferenceEnabled(Settings.PREF_GESTURE_PREVIEW_TRAIL, + gestureInputEnabledByUser); + setPreferenceEnabled(Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, + gestureInputEnabledByUser); + } + } + ensureConsistencyOfAutoCorrectionSettings(); + updateVoiceModeSummary(); + updateShowCorrectionSuggestionsSummary(); + updateKeyPreviewPopupDelaySummary(); + refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources()); + } + + private void updateShowCorrectionSuggestionsSummary() { + mShowCorrectionSuggestionsPreference.setSummary( + getResources().getStringArray(R.array.prefs_suggestion_visibilities) + [mShowCorrectionSuggestionsPreference.findIndexOfValue( + mShowCorrectionSuggestionsPreference.getValue())]); + } + + private void updateCustomInputStylesSummary() { + final PreferenceScreen customInputStyles = + (PreferenceScreen)findPreference(Settings.PREF_CUSTOM_INPUT_STYLES); + final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); + final Resources res = getResources(); + final String prefSubtype = SettingsValues.getPrefAdditionalSubtypes(prefs, res); + final InputMethodSubtype[] subtypes = + AdditionalSubtype.createAdditionalSubtypesArray(prefSubtype); + final StringBuilder styles = new StringBuilder(); + for (final InputMethodSubtype subtype : subtypes) { + if (styles.length() > 0) styles.append(", "); + styles.append(SubtypeLocale.getSubtypeDisplayName(subtype, res)); + } + customInputStyles.setSummary(styles); + } + + private void updateKeyPreviewPopupDelaySummary() { + final ListPreference lp = mKeyPreviewPopupDismissDelay; + final CharSequence[] entries = lp.getEntries(); + if (entries == null || entries.length <= 0) return; + lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]); + } + + private void updateVoiceModeSummary() { + mVoicePreference.setSummary( + getResources().getStringArray(R.array.voice_input_modes_summary) + [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]); + } + + private void refreshEnablingsOfKeypressSoundAndVibrationSettings( + final SharedPreferences sp, final Resources res) { + if (mKeypressVibrationDurationSettingsPref != null) { + final boolean hasVibratorHardware = + AudioAndHapticFeedbackManager.getInstance().hasVibrator(); + final boolean vibrateOnByUser = sp.getBoolean(Settings.PREF_VIBRATE_ON, + res.getBoolean(R.bool.config_default_vibration_enabled)); + mKeypressVibrationDurationSettingsPref.setEnabled( + hasVibratorHardware && vibrateOnByUser); + } + + if (mKeypressSoundVolumeSettingsPref != null) { + final boolean soundOn = sp.getBoolean(Settings.PREF_SOUND_ON, + res.getBoolean(R.bool.config_default_sound_enabled)); + mKeypressSoundVolumeSettingsPref.setEnabled(soundOn); + } + } + + private void showKeypressVibrationDurationSettingsDialog() { + final SharedPreferences sp = getPreferenceManager().getSharedPreferences(); + final Context context = getActivity(); + final PreferenceScreen settingsPref = mKeypressVibrationDurationSettingsPref; + final SeekBarDialog.Listener listener = new SeekBarDialog.Adapter() { + @Override + public void onPositiveButtonClick(final SeekBarDialog dialog) { + final int ms = dialog.getValue(); + sp.edit().putInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, ms).apply(); + if (settingsPref != null) { + settingsPref.setSummary(dialog.getValueText()); + } + } + + @Override + public void onStopTrackingTouch(final SeekBarDialog dialog) { + final int ms = dialog.getValue(); + AudioAndHapticFeedbackManager.getInstance().vibrate(ms); + } + }; + final int currentMs = SettingsValues.getCurrentVibrationDuration(sp, getResources()); + final SeekBarDialog.Builder builder = new SeekBarDialog.Builder(context); + builder.setTitle(R.string.prefs_keypress_vibration_duration_settings) + .setListener(listener) + .setMaxValue(AudioAndHapticFeedbackManager.MAX_KEYPRESS_VIBRATION_DURATION) + .setValueFromat(R.string.settings_keypress_vibration_duration) + .setValue(currentMs) + .create() + .show(); + } + + private static final int PERCENT_INT = 100; + private static final float PERCENT_FLOAT = 100.0f; + + private static int getCurrentKeyPressSoundVolumePercent(final SharedPreferences sp, + final Resources res) { + return (int)(SettingsValues.getCurrentKeypressSoundVolume(sp, res) * PERCENT_FLOAT); + } + + private void showKeypressSoundVolumeSettingDialog() { + final SharedPreferences sp = getPreferenceManager().getSharedPreferences(); + final Context context = getActivity(); + final AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + final PreferenceScreen settingsPref = mKeypressSoundVolumeSettingsPref; + final SeekBarDialog.Listener listener = new SeekBarDialog.Adapter() { + @Override + public void onPositiveButtonClick(final SeekBarDialog dialog) { + final float volume = dialog.getValue() / PERCENT_FLOAT; + sp.edit().putFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, volume).apply(); + if (settingsPref != null) { + settingsPref.setSummary(dialog.getValueText()); + } + } + + @Override + public void onStopTrackingTouch(final SeekBarDialog dialog) { + final float volume = dialog.getValue() / PERCENT_FLOAT; + am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, volume); + } + }; + final SeekBarDialog.Builder builder = new SeekBarDialog.Builder(context); + final int currentVolumeInt = getCurrentKeyPressSoundVolumePercent(sp, getResources()); + builder.setTitle(R.string.prefs_keypress_sound_volume_settings) + .setListener(listener) + .setMaxValue(PERCENT_INT) + .setValue(currentVolumeInt) + .create() + .show(); + } +} diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 157684437..39406621c 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -16,7 +16,6 @@ package com.android.inputmethod.latin; -import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; @@ -83,7 +82,7 @@ public final class SettingsValues { public final boolean mGestureFloatingPreviewTextEnabled; // From the input box - private final InputAttributes mInputAttributes; + public final InputAttributes mInputAttributes; // Deduced settings public final int mKeypressVibrationDuration; @@ -96,10 +95,8 @@ public final class SettingsValues { private final boolean mVoiceKeyEnabled; private final boolean mVoiceKeyOnMain; - public SettingsValues(final SharedPreferences prefs, final InputAttributes inputAttributes, - final Context context) { - final Resources res = context.getResources(); - + public SettingsValues(final SharedPreferences prefs, final Resources res, + final InputAttributes inputAttributes) { // Get the resources mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions); mWeakSpaceStrippers = res.getString(R.string.weak_space_stripping_symbols); @@ -121,7 +118,7 @@ public final class SettingsValues { res.getString(R.string.symbols_excluded_from_word_separators); mWordSeparators = createWordSeparators(mWeakSpaceStrippers, mWeakSpaceSwappers, mSymbolsExcludedFromWordSeparators, res); - mHintToSaveText = context.getText(R.string.hint_add_to_dictionary); + mHintToSaveText = res.getText(R.string.hint_add_to_dictionary); // Store the input attributes if (null == inputAttributes) { @@ -132,7 +129,7 @@ public final class SettingsValues { // Get the settings preferences mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true); - mVibrateOn = isVibrateOn(context, prefs, res); + mVibrateOn = isVibrateOn(prefs, res); mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON, res.getBoolean(R.bool.config_default_sound_enabled)); mKeyPreviewPopupOn = isKeyPreviewPopupEnabled(prefs, res); @@ -214,9 +211,8 @@ public final class SettingsValues { throw new RuntimeException("Bug: visibility string is not configured correctly"); } - private static boolean isVibrateOn(final Context context, final SharedPreferences prefs, - final Resources res) { - final boolean hasVibrator = VibratorUtils.getInstance(context).hasVibrator(); + private static boolean isVibrateOn(final SharedPreferences prefs, final Resources res) { + final boolean hasVibrator = AudioAndHapticFeedbackManager.getInstance().hasVibrator(); return hasVibrator && prefs.getBoolean(Settings.PREF_VIBRATE_ON, res.getBoolean(R.bool.config_default_vibration_enabled)); } @@ -273,6 +269,7 @@ public final class SettingsValues { return !currentAutoCorrectionSetting.equals(autoCorrectionOff); } + // TODO: Clean up and move public helper methods to Settings class. // Public to access from KeyboardSwitcher. Should it have access to some // process-global instance instead? public static boolean isKeyPreviewPopupEnabled(final SharedPreferences prefs, @@ -423,9 +420,4 @@ public final class SettingsValues { public boolean isSameInputType(final EditorInfo editorInfo) { return mInputAttributes.isSameInputType(editorInfo); } - - // For debug. - public String getInputAttributesDebugString() { - return mInputAttributes.toString(); - } } diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java index 6cb39426d..a16784985 100644 --- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java @@ -31,14 +31,15 @@ import android.text.TextUtils; import java.util.Arrays; /** - * An expandable dictionary that stores the words in the user unigram dictionary. - * - * Largely a copy of UserDictionary, will replace that class in the future. + * An expandable dictionary that stores the words in the user dictionary provider into a binary + * dictionary file to use it from native code. */ public class UserBinaryDictionary extends ExpandableBinaryDictionary { // The user dictionary provider uses an empty string to mean "all languages". private static final String USER_DICTIONARY_ALL_LANGUAGES = ""; + private static final int HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY = 250; + private static final int LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY = 160; // TODO: use Words.SHORTCUT when we target JellyBean or above final static String SHORTCUT = "shortcut"; @@ -233,6 +234,19 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { mContext.startActivity(intent); } + private int scaleFrequencyFromDefaultToLatinIme(final int defaultFrequency) { + // The default frequency for the user dictionary is 250 for historical reasons. + // Latin IME considers a good value for the default user dictionary frequency + // is about 160 considering the scale we use. So we are scaling down the values. + if (defaultFrequency > Integer.MAX_VALUE / LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY) { + return (defaultFrequency / HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY) + * LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY; + } else { + return (defaultFrequency * LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY) + / HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY; + } + } + private void addWords(final Cursor cursor) { final boolean hasShortcutColumn = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; clearFusionDictionary(); @@ -245,12 +259,13 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { final String word = cursor.getString(indexWord); final String shortcut = hasShortcutColumn ? cursor.getString(indexShortcut) : null; final int frequency = cursor.getInt(indexFrequency); + final int adjustedFrequency = scaleFrequencyFromDefaultToLatinIme(frequency); // Safeguard against adding really long words. if (word.length() < MAX_WORD_LENGTH) { - super.addWord(word, null, frequency); + super.addWord(word, null, adjustedFrequency); } if (null != shortcut && shortcut.length() < MAX_WORD_LENGTH) { - super.addWord(shortcut, word, frequency); + super.addWord(shortcut, word, adjustedFrequency); } cursor.moveToNext(); } diff --git a/java/src/com/android/inputmethod/latin/VibratorUtils.java b/java/src/com/android/inputmethod/latin/VibratorUtils.java deleted file mode 100644 index b6696cec0..000000000 --- a/java/src/com/android/inputmethod/latin/VibratorUtils.java +++ /dev/null @@ -1,50 +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.latin; - -import android.content.Context; -import android.os.Vibrator; - -public final class VibratorUtils { - private static final VibratorUtils sInstance = new VibratorUtils(); - private Vibrator mVibrator; - - private VibratorUtils() { - // This utility class is not publicly instantiable. - } - - public static VibratorUtils getInstance(Context context) { - if (sInstance.mVibrator == null) { - sInstance.mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); - } - return sInstance; - } - - public boolean hasVibrator() { - if (mVibrator == null) { - return false; - } - return mVibrator.hasVibrator(); - } - - public void vibrate(long milliseconds) { - if (mVibrator == null) { - return; - } - mVibrator.vibrate(milliseconds); - } -} diff --git a/java/src/com/android/inputmethod/research/FixedLogBuffer.java b/java/src/com/android/inputmethod/research/FixedLogBuffer.java new file mode 100644 index 000000000..f3302d856 --- /dev/null +++ b/java/src/com/android/inputmethod/research/FixedLogBuffer.java @@ -0,0 +1,123 @@ +/* + * 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 java.util.LinkedList; + +/** + * A buffer that holds a fixed number of LogUnits. + * + * LogUnits are added in and shifted out in temporal order. Only a subset of the LogUnits are + * actual words; the other LogUnits do not count toward the word limit. Once the buffer reaches + * capacity, adding another LogUnit that is a word evicts the oldest LogUnits out one at a time to + * stay under the capacity limit. + * + * This variant of a LogBuffer has a limited memory footprint because of its limited size. This + * makes it useful, for example, for recording a window of the user's most recent actions in case + * they want to report an observed error that they do not know how to reproduce. + */ +public class FixedLogBuffer extends LogBuffer { + /* package for test */ int mWordCapacity; + // The number of members of mLogUnits that are actual words. + private int mNumActualWords; + + /** + * Create a new LogBuffer that can hold a fixed number of LogUnits that are words (and + * unlimited number of non-word LogUnits), and that outputs its result to a researchLog. + * + * @param wordCapacity maximum number of words + */ + public FixedLogBuffer(final int wordCapacity) { + super(); + if (wordCapacity <= 0) { + throw new IllegalArgumentException("wordCapacity must be 1 or greater."); + } + mWordCapacity = wordCapacity; + mNumActualWords = 0; + } + + protected int getNumActualWords() { + return mNumActualWords; + } + + /** + * Adds a new LogUnit to the front of the LIFO queue, evicting existing LogUnit's + * (oldest first) if word capacity is reached. + */ + @Override + public void shiftIn(final LogUnit newLogUnit) { + if (newLogUnit.getWord() == null) { + // This LogUnit isn't a word, so it doesn't count toward the word-limit. + super.shiftIn(newLogUnit); + return; + } + if (mNumActualWords == mWordCapacity) { + shiftOutThroughFirstWord(); + } + super.shiftIn(newLogUnit); + mNumActualWords++; // Must be a word, or we wouldn't be here. + } + + private void shiftOutThroughFirstWord() { + final LinkedList<LogUnit> logUnits = getLogUnits(); + while (!logUnits.isEmpty()) { + final LogUnit logUnit = logUnits.removeFirst(); + onShiftOut(logUnit); + if (logUnit.hasWord()) { + // Successfully shifted out a word-containing LogUnit and made space for the new + // LogUnit. + mNumActualWords--; + break; + } + } + } + + /** + * Removes all LogUnits from the buffer without calling onShiftOut(). + */ + @Override + public void clear() { + super.clear(); + mNumActualWords = 0; + } + + /** + * Called when a LogUnit is removed from the LogBuffer as a result of a shiftIn. LogUnits are + * removed in the order entered. This method is not called when shiftOut is called directly. + * + * Base class does nothing; subclasses may override if they want to record non-privacy sensitive + * events that fall off the end. + */ + protected void onShiftOut(final LogUnit logUnit) { + } + + /** + * Called to deliberately remove the oldest LogUnit. Usually called when draining the + * LogBuffer. + */ + @Override + public LogUnit shiftOut() { + if (isEmpty()) { + return null; + } + final LogUnit logUnit = super.shiftOut(); + if (logUnit.hasWord()) { + mNumActualWords--; + } + return logUnit; + } +} diff --git a/java/src/com/android/inputmethod/research/JsonUtils.java b/java/src/com/android/inputmethod/research/JsonUtils.java index cb331d7f9..1dfd01c69 100644 --- a/java/src/com/android/inputmethod/research/JsonUtils.java +++ b/java/src/com/android/inputmethod/research/JsonUtils.java @@ -18,6 +18,7 @@ package com.android.inputmethod.research; import android.content.SharedPreferences; import android.util.JsonWriter; +import android.view.MotionEvent; import android.view.inputmethod.CompletionInfo; import com.android.inputmethod.keyboard.Key; @@ -27,6 +28,9 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.io.IOException; import java.util.Map; +/** + * Routines for mapping classes and variables to JSON representations for logging. + */ /* package */ class JsonUtils { private JsonUtils() { // This utility class is not publicly instantiable. @@ -100,4 +104,54 @@ import java.util.Map; jsonWriter.endArray(); jsonWriter.endObject(); } + + /* package */ static void writeJson(final MotionEvent me, final JsonWriter jsonWriter) + throws IOException { + jsonWriter.beginObject(); + jsonWriter.name("pointerIds"); + jsonWriter.beginArray(); + final int pointerCount = me.getPointerCount(); + for (int index = 0; index < pointerCount; index++) { + jsonWriter.value(me.getPointerId(index)); + } + jsonWriter.endArray(); + + jsonWriter.name("xyt"); + jsonWriter.beginArray(); + final int historicalSize = me.getHistorySize(); + for (int index = 0; index < historicalSize; index++) { + jsonWriter.beginObject(); + jsonWriter.name("t"); + jsonWriter.value(me.getHistoricalEventTime(index)); + jsonWriter.name("d"); + jsonWriter.beginArray(); + for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++) { + jsonWriter.beginObject(); + jsonWriter.name("x"); + jsonWriter.value(me.getHistoricalX(pointerIndex, index)); + jsonWriter.name("y"); + jsonWriter.value(me.getHistoricalY(pointerIndex, index)); + jsonWriter.endObject(); + } + jsonWriter.endArray(); + jsonWriter.endObject(); + } + jsonWriter.beginObject(); + jsonWriter.name("t"); + jsonWriter.value(me.getEventTime()); + jsonWriter.name("d"); + jsonWriter.beginArray(); + for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++) { + jsonWriter.beginObject(); + jsonWriter.name("x"); + jsonWriter.value(me.getX(pointerIndex)); + jsonWriter.name("y"); + jsonWriter.value(me.getY(pointerIndex)); + jsonWriter.endObject(); + } + jsonWriter.endArray(); + jsonWriter.endObject(); + jsonWriter.endArray(); + jsonWriter.endObject(); + } } diff --git a/java/src/com/android/inputmethod/research/LogBuffer.java b/java/src/com/android/inputmethod/research/LogBuffer.java index a3c3e89de..14e8d08a2 100644 --- a/java/src/com/android/inputmethod/research/LogBuffer.java +++ b/java/src/com/android/inputmethod/research/LogBuffer.java @@ -16,102 +16,44 @@ package com.android.inputmethod.research; -import com.android.inputmethod.latin.CollectionUtils; - import java.util.LinkedList; /** - * A buffer that holds a fixed number of LogUnits. + * Maintain a FIFO queue of LogUnits. * - * LogUnits are added in and shifted out in temporal order. Only a subset of the LogUnits are - * actual words; the other LogUnits do not count toward the word limit. Once the buffer reaches - * capacity, adding another LogUnit that is a word evicts the oldest LogUnits out one at a time to - * stay under the capacity limit. + * This class provides an unbounded queue. This is useful when the user is aware that their actions + * are being recorded, such as when they are trying to reproduce a bug. In this case, there should + * not be artificial restrictions on how many events that can be saved. */ public class LogBuffer { - protected final LinkedList<LogUnit> mLogUnits; - /* package for test */ int mWordCapacity; - // The number of members of mLogUnits that are actual words. - protected int mNumActualWords; - - /** - * Create a new LogBuffer that can hold a fixed number of LogUnits that are words (and - * unlimited number of non-word LogUnits), and that outputs its result to a researchLog. - * - * @param wordCapacity maximum number of words - */ - LogBuffer(final int wordCapacity) { - if (wordCapacity <= 0) { - throw new IllegalArgumentException("wordCapacity must be 1 or greater."); - } - mLogUnits = CollectionUtils.newLinkedList(); - mWordCapacity = wordCapacity; - mNumActualWords = 0; - } + // TODO: Gracefully handle situations in which this LogBuffer is consuming too much memory. + // This may happen, for example, if the user has forgotten that data is being logged. + private final LinkedList<LogUnit> mLogUnits; - /** - * Adds a new LogUnit to the front of the LIFO queue, evicting existing LogUnit's - * (oldest first) if word capacity is reached. - */ - public void shiftIn(LogUnit newLogUnit) { - if (newLogUnit.getWord() == null) { - // This LogUnit isn't a word, so it doesn't count toward the word-limit. - mLogUnits.add(newLogUnit); - return; - } - if (mNumActualWords == mWordCapacity) { - shiftOutThroughFirstWord(); - } - mLogUnits.add(newLogUnit); - mNumActualWords++; // Must be a word, or we wouldn't be here. + public LogBuffer() { + mLogUnits = new LinkedList<LogUnit>(); } - private void shiftOutThroughFirstWord() { - while (!mLogUnits.isEmpty()) { - final LogUnit logUnit = mLogUnits.removeFirst(); - onShiftOut(logUnit); - if (logUnit.hasWord()) { - // Successfully shifted out a word-containing LogUnit and made space for the new - // LogUnit. - mNumActualWords--; - break; - } - } + protected LinkedList<LogUnit> getLogUnits() { + return mLogUnits; } - /** - * Removes all LogUnits from the buffer without calling onShiftOut(). - */ public void clear() { mLogUnits.clear(); - mNumActualWords = 0; } - /** - * Called when a LogUnit is removed from the LogBuffer as a result of a shiftIn. LogUnits are - * removed in the order entered. This method is not called when shiftOut is called directly. - * - * Base class does nothing; subclasses may override. - */ - protected void onShiftOut(LogUnit logUnit) { + public void shiftIn(final LogUnit logUnit) { + mLogUnits.add(logUnit); + } + + public boolean isEmpty() { + return mLogUnits.isEmpty(); } - /** - * Called to deliberately remove the oldest LogUnit. Usually called when draining the - * LogBuffer. - */ public LogUnit shiftOut() { - if (mLogUnits.isEmpty()) { + if (isEmpty()) { return null; } - final LogUnit logUnit = mLogUnits.removeFirst(); - if (logUnit.hasWord()) { - mNumActualWords--; - } - return logUnit; - } - - public boolean isEmpty() { - return mLogUnits.isEmpty(); + return mLogUnits.removeFirst(); } } diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java index 27c4027de..bcb144f5f 100644 --- a/java/src/com/android/inputmethod/research/LogUnit.java +++ b/java/src/com/android/inputmethod/research/LogUnit.java @@ -19,6 +19,7 @@ package com.android.inputmethod.research; import android.content.SharedPreferences; import android.util.JsonWriter; import android.util.Log; +import android.view.MotionEvent; import android.view.inputmethod.CompletionInfo; import com.android.inputmethod.keyboard.Key; @@ -189,6 +190,8 @@ import java.util.Map; JsonUtils.writeJson((Key[]) value, jsonWriter); } else if (value instanceof SuggestedWords) { JsonUtils.writeJson((SuggestedWords) value, jsonWriter); + } else if (value instanceof MotionEvent) { + JsonUtils.writeJson((MotionEvent) value, jsonWriter); } else if (value == null) { jsonWriter.nullValue(); } else { diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java index 0185e5fc0..bec21d7e0 100644 --- a/java/src/com/android/inputmethod/research/MainLogBuffer.java +++ b/java/src/com/android/inputmethod/research/MainLogBuffer.java @@ -22,15 +22,24 @@ import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.Suggest; import com.android.inputmethod.latin.define.ProductionFlag; +import java.util.LinkedList; import java.util.Random; -public class MainLogBuffer extends LogBuffer { +/** + * Provide a log buffer of fixed length that enforces privacy restrictions. + * + * The privacy restrictions include making sure that no numbers are logged, that all logged words + * are in the dictionary, and that words are recorded infrequently enough that the user's meaning + * cannot be easily determined. + */ +public class MainLogBuffer extends FixedLogBuffer { private static final String TAG = MainLogBuffer.class.getSimpleName(); private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG; // The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams. private static final int N_GRAM_SIZE = 2; - // The number of words between n-grams to omit from the log. + // The number of words between n-grams to omit from the log. If debugging, record 50% of all + // words. Otherwise, only record 10%. private static final int DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES = ProductionFlag.IS_EXPERIMENTAL_DEBUG ? 2 : 18; @@ -56,7 +65,7 @@ public class MainLogBuffer extends LogBuffer { mWordsUntilSafeToSample = random.nextInt(mMinWordPeriod); } - public void setSuggest(Suggest suggest) { + public void setSuggest(final Suggest suggest) { mSuggest = suggest; } @@ -108,9 +117,10 @@ public class MainLogBuffer extends LogBuffer { } // Check each word in the buffer. If any word poses a privacy threat, we cannot upload the // complete buffer contents in detail. - final int length = mLogUnits.size(); + final LinkedList<LogUnit> logUnits = getLogUnits(); + final int length = logUnits.size(); for (int i = 0; i < length; i++) { - final LogUnit logUnit = mLogUnits.get(i); + final LogUnit logUnit = logUnits.get(i); final String word = logUnit.getWord(); if (word == null) { // Digits outside words are a privacy threat. @@ -133,7 +143,7 @@ public class MainLogBuffer extends LogBuffer { } @Override - protected void onShiftOut(LogUnit logUnit) { + protected void onShiftOut(final LogUnit logUnit) { if (mResearchLog != null) { mResearchLog.publish(logUnit, false /* isIncludingPrivateData */); } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 709746ee3..b1484e696 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -154,6 +154,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private LogUnit mCurrentLogUnit = new LogUnit(); + // Gestured or tapped words may be committed after the gesture of the next word has started. + // To ensure that the gesture data of the next word is not associated with the previous word, + // thereby leaking private data, we store the time of the down event that started the second + // gesture, and when committing the earlier word, split the LogUnit. + private long mSavedDownEventTime; private ResearchLogger() { mStatistics = Statistics.getInstance(); } @@ -377,7 +382,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); // LogBuffer is one more than FEEDBACK_WORD_BUFFER_SIZE, because it must also hold // the feedback LogUnit itself. - mFeedbackLogBuffer = new LogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1); + mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1); } } @@ -638,7 +643,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mMainKeyboardView.invalidateAllKeys(); } - public void paintIndicator(KeyboardView view, Paint paint, Canvas canvas, int width, int height) { // TODO: Reimplement using a keyboard background image specific to the ResearchLogger @@ -741,9 +745,15 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return false; } + /** + * Commit the portion of mCurrentLogUnit before maxTime as a worded logUnit. + * + * After this operation completes, mCurrentLogUnit will hold any logStatements that happened + * after maxTime. + */ private static final LogStatement LOGSTATEMENT_COMMIT_RECORD_SPLIT_WORDS = new LogStatement("recordSplitWords", true, false); - public void onWordComplete(final String word, final long maxTime) { + /* package for test */ void commitCurrentLogUnitAsWord(final String word, final long maxTime) { final Dictionary dictionary = getDictionary(); if (word != null && word.length() > 0 && hasLetters(word)) { mCurrentLogUnit.setWord(word); @@ -757,6 +767,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mCurrentLogUnit = newLogUnit; } + public void onWordFinished(final String word) { + commitCurrentLogUnitAsWord(word, mSavedDownEventTime); + mSavedDownEventTime = Long.MAX_VALUE; + } + private static int scrubDigitFromCodePoint(int codePoint) { return Character.isDigit(codePoint) ? DIGIT_REPLACEMENT_CODEPOINT : codePoint; } @@ -880,8 +895,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang * */ private static final LogStatement LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT = - new LogStatement("MainKeyboardViewProcessMotionEvent", true, false, "action", - "eventTime", "id", "x", "y", "size", "pressure"); + new LogStatement("MotionEvent", true, false, "action", "MotionEvent"); public static void mainKeyboardView_processMotionEvent(final MotionEvent me, final int action, final long eventTime, final int index, final int id, final int x, final int y) { if (me != null) { @@ -896,10 +910,14 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang case MotionEvent.ACTION_OUTSIDE: actionString = "OUTSIDE"; break; default: actionString = "ACTION_" + action; break; } - final float size = me.getSize(index); - final float pressure = me.getPressure(index); - getInstance().enqueueEvent(LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT, - actionString, eventTime, id, x, y, size, pressure); + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueueEvent(LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT, + actionString, MotionEvent.obtain(me)); + if (action == MotionEvent.ACTION_DOWN) { + // Subtract 1 from eventTime so the down event is included in the later + // LogUnit, not the earlier (the test is for inequality). + researchLogger.mSavedDownEventTime = eventTime - 1; + } } } @@ -1038,6 +1056,16 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } /** + * Log a call to LatinIME.onTextInput(). + * + * SystemResponse: Raw text is added to the TextView. + */ + public static void latinIME_onTextInput(final String text) { + final ResearchLogger researchLogger = getInstance(); + researchLogger.commitCurrentLogUnitAsWord(text, Long.MAX_VALUE); + } + + /** * Log a call to LatinIME.pickSuggestionManually(). * * UserAction: The user has chosen a specific word from the suggestion strip. @@ -1053,7 +1081,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang scrubDigitsFromString(replacedWord), index, suggestion == null ? null : scrubbedWord, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE); - researchLogger.onWordComplete(scrubbedWord, Long.MAX_VALUE); + researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, Long.MAX_VALUE); researchLogger.mStatistics.recordManualSuggestion(); } @@ -1069,7 +1097,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION, index, suggestion, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE); - researchLogger.onWordComplete(suggestion, Long.MAX_VALUE); + researchLogger.commitCurrentLogUnitAsWord(suggestion, Long.MAX_VALUE); } /** @@ -1098,8 +1126,20 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang */ private static final LogStatement LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE = new LogStatement("LatinIMESwapSwapperAndSpace", false, false); - public static void latinIME_swapSwapperAndSpace() { - getInstance().enqueueEvent(LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE); + public static void latinIME_swapSwapperAndSpace(final String text) { + final ResearchLogger researchLogger = getInstance(); + researchLogger.commitCurrentLogUnitAsWord(text, Long.MAX_VALUE); + researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE); + } + + /** + * Log a call to LatinIME.maybeDoubleSpacePeriod(). + * + * SystemResponse: Two spaces have been replaced by period space. + */ + public static void latinIME_maybeDoubleSpacePeriod(final String text) { + final ResearchLogger researchLogger = getInstance(); + researchLogger.commitCurrentLogUnitAsWord(text, Long.MAX_VALUE); } /** @@ -1156,6 +1196,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_REVERTCOMMIT, committedWord, originallyTypedWord); researchLogger.mStatistics.recordRevertCommit(); + researchLogger.commitCurrentLogUnitAsWord(originallyTypedWord, Long.MAX_VALUE); } /** @@ -1250,6 +1291,26 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } /** + * Log a call to RichInputConnection.revertDoubleSpacePeriod(). + * + * SystemResponse: The IME has reverted ". ", which had previously replaced two typed spaces. + */ + public static void richInputConnection_revertDoubleSpacePeriod(final String doubleSpace) { + final ResearchLogger researchLogger = getInstance(); + researchLogger.commitCurrentLogUnitAsWord(doubleSpace, Long.MAX_VALUE); + } + + /** + * Log a call to RichInputConnection.revertSwapPunctuation(). + * + * SystemResponse: The IME has reverted a punctuation swap. + */ + public static void richInputConnection_revertSwapPunctuation(final String text) { + final ResearchLogger researchLogger = getInstance(); + researchLogger.commitCurrentLogUnitAsWord(text, Long.MAX_VALUE); + } + + /** * Log a call to LatinIME.commitCurrentAutoCorrection(). * * SystemResponse: The IME has committed an auto-correction. An auto-correction changes the raw @@ -1265,7 +1326,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_COMMITCURRENTAUTOCORRECTION, scrubbedTypedWord, scrubbedAutoCorrection, separatorString); - researchLogger.onWordComplete(scrubbedAutoCorrection, Long.MAX_VALUE); + researchLogger.commitCurrentLogUnitAsWord(scrubbedAutoCorrection, Long.MAX_VALUE); } private boolean isExpectingCommitText = false; @@ -1284,7 +1345,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); final String scrubbedWord = scrubDigitsFromString(committedWord.toString()); researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_COMMIT_PARTIAL_TEXT); - researchLogger.onWordComplete(scrubbedWord, lastTimestampOfWordData); + researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, lastTimestampOfWordData); researchLogger.mStatistics.recordSplitWords(); } @@ -1303,7 +1364,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (!researchLogger.isExpectingCommitText) { researchLogger.enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTIONCOMMITTEXT, newCursorPosition); - researchLogger.onWordComplete(scrubbedWord, Long.MAX_VALUE); + researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, Long.MAX_VALUE); } researchLogger.isExpectingCommitText = false; } |