diff options
Diffstat (limited to 'java/src/com/android/inputmethod')
10 files changed, 170 insertions, 108 deletions
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java index 43c19d7eb..d97989d9c 100644 --- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java +++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java @@ -104,8 +104,9 @@ public class SuggestionSpanUtils { } public static CharSequence getTextWithSuggestionSpan(Context context, - CharSequence pickedWord, SuggestedWords suggestedWords) { - if (TextUtils.isEmpty(pickedWord) || CONSTRUCTOR_SuggestionSpan == null + CharSequence pickedWord, SuggestedWords suggestedWords, boolean dictionaryAvailable) { + if (!dictionaryAvailable || TextUtils.isEmpty(pickedWord) + || CONSTRUCTOR_SuggestionSpan == null || suggestedWords == null || suggestedWords.size() == 0 || OBJ_SUGGESTIONS_MAX_SIZE == null) { return pickedWord; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index b51dbb906..804ccf658 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -260,7 +260,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { mKeyHintLabelSize = (int)(keyHeight * mKeyHintLabelRatio); } - public void brendAlpha(Paint paint) { + public void blendAlpha(Paint paint) { final int color = paint.getColor(); paint.setARGB((paint.getAlpha() * mAnimAlpha) / ALPHA_OPAQUE, Color.red(color), Color.green(color), Color.blue(color)); @@ -600,7 +600,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { // Make label invisible paint.setColor(Color.TRANSPARENT); } - params.brendAlpha(paint); + params.blendAlpha(paint); canvas.drawText(label, 0, label.length(), positionX, baseline, paint); // Turn off drop shadow and reset x-scale. paint.setShadowLayer(0, 0, 0, 0); @@ -645,7 +645,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { hintSize = params.mKeyHintLetterSize; } paint.setColor(hintColor); - params.brendAlpha(paint); + params.blendAlpha(paint); paint.setTextSize(hintSize); final float hintX, hintY; if (key.hasHintLabel()) { @@ -715,7 +715,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { paint.setTextSize(params.mKeyHintLetterSize); paint.setColor(params.mKeyHintLabelColor); - params.brendAlpha(paint); + params.blendAlpha(paint); paint.setTextAlign(Align.CENTER); final float hintX = keyWidth - params.mKeyHintLetterPadding - getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2; diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index 5ebf92cf7..7fadb3b8a 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -227,26 +227,22 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke removeMessages(MSG_LONGPRESS_KEY); } - public static void cancelAndStartAnimators(ObjectAnimator animatorToCancel, - ObjectAnimator animatorToStart) { - if (animatorToCancel != null && animatorToCancel.isStarted()) { + public static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel, + final ObjectAnimator animatorToStart) { + float startFraction = 0.0f; + if (animatorToCancel.isStarted()) { animatorToCancel.cancel(); + startFraction = 1.0f - animatorToCancel.getAnimatedFraction(); } - // TODO: Start the animation with an initial value that is the same as the final value - // of the above animation when it gets cancelled. - if (animatorToStart != null && !animatorToStart.isStarted()) { - animatorToStart.start(); - } - } - - private void cancelTypingStateTimer() { - removeMessages(MSG_TYPING_STATE_EXPIRED); + final long startTime = (long)(animatorToStart.getDuration() * startFraction); + animatorToStart.start(); + animatorToStart.setCurrentPlayTime(startTime); } @Override public void startTypingStateTimer() { final boolean isTyping = isTypingState(); - cancelTypingStateTimer(); + removeMessages(MSG_TYPING_STATE_EXPIRED); sendMessageDelayed( obtainMessage(MSG_TYPING_STATE_EXPIRED), mParams.mIgnoreAltCodeKeyTimeout); if (isTyping) { @@ -286,7 +282,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke public void cancelAllMessages() { cancelKeyTimers(); - cancelTypingStateTimer(); } } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 24ab54730..37d9b6ac7 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -248,6 +248,9 @@ public class PointerTracker { mListener.onPressKey(key.mCode); final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged; mKeyboardLayoutHasBeenChanged = false; + if (!key.altCodeWhileTyping() && !key.isModifier()) { + mTimerProxy.startTypingStateTimer(); + } return keyboardLayoutHasBeenChanged; } return false; @@ -279,9 +282,6 @@ public class PointerTracker { } else if (code != Keyboard.CODE_UNSPECIFIED) { mListener.onCodeInput(code, x, y); } - if (!key.altCodeWhileTyping() && !key.isModifier()) { - mTimerProxy.startTypingStateTimer(); - } } } @@ -734,6 +734,9 @@ public class PointerTracker { public void onRepeatKey(Key key) { if (key != null) { detectAndSendKey(key, key.mX, key.mY); + if (!key.altCodeWhileTyping() && !key.isModifier()) { + mTimerProxy.startTypingStateTimer(); + } } } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 311d3dc9d..e4d081b56 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -53,17 +53,23 @@ public class BinaryDictionaryFileDumper { private static final String DICTIONARY_PROJECTION[] = { "id" }; + public static final String QUERY_PARAMETER_MAY_PROMPT_USER = "mayPrompt"; + public static final String QUERY_PARAMETER_TRUE = "true"; + // Prevents this class to be accidentally instantiated. private BinaryDictionaryFileDumper() { } /** - * Return for a given locale or dictionary id the provider URI to get the dictionary. + * Returns a URI builder pointing to the dictionary pack. + * + * This creates a URI builder able to build a URI pointing to the dictionary + * pack content provider for a specific dictionary id. */ - private static Uri getProviderUri(String path) { + private static Uri.Builder getProviderUriBuilder(final String path) { return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) .authority(BinaryDictionary.DICTIONARY_PACK_AUTHORITY).appendPath( - path).build(); + path); } /** @@ -71,9 +77,13 @@ public class BinaryDictionaryFileDumper { * available to copy into Latin IME. */ private static List<WordListInfo> getWordListWordListInfos(final Locale locale, - final Context context) { + final Context context, final boolean hasDefaultWordList) { final ContentResolver resolver = context.getContentResolver(); - final Uri dictionaryPackUri = getProviderUri(locale.toString()); + final Uri.Builder builder = getProviderUriBuilder(locale.toString()); + if (!hasDefaultWordList) { + builder.appendQueryParameter(QUERY_PARAMETER_MAY_PROMPT_USER, QUERY_PARAMETER_TRUE); + } + final Uri dictionaryPackUri = builder.build(); final Cursor c = resolver.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null, null); if (null == c) return Collections.<WordListInfo>emptyList(); @@ -132,7 +142,7 @@ public class BinaryDictionaryFileDumper { final int MODE_MIN = COMPRESSED_CRYPTED_COMPRESSED; final int MODE_MAX = NONE; - final Uri wordListUri = getProviderUri(id); + final Uri wordListUri = getProviderUriBuilder(id).build(); final String outputFileName = BinaryDictionaryGetter.getCacheFileName(id, locale, context); for (int mode = MODE_MIN; mode <= MODE_MAX; ++mode) { @@ -231,9 +241,10 @@ public class BinaryDictionaryFileDumper { * @throw IOException if the provider-returned data could not be read. */ public static List<AssetFileAddress> cacheWordListsFromContentProvider(final Locale locale, - final Context context) { + final Context context, final boolean hasDefaultWordList) { final ContentResolver resolver = context.getContentResolver(); - final List<WordListInfo> idList = getWordListWordListInfos(locale, context); + final List<WordListInfo> idList = getWordListWordListInfos(locale, context, + hasDefaultWordList); final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>(); for (WordListInfo id : idList) { final AssetFileAddress afd = cacheWordList(id.mId, id.mLocale, resolver, context); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index b0c2adc79..072dec9d1 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -263,10 +263,12 @@ class BinaryDictionaryGetter { public static ArrayList<AssetFileAddress> getDictionaryFiles(final Locale locale, final Context context) { + final boolean hasDefaultWordList = DictionaryFactory.isDictionaryAvailable(context, locale); // cacheWordListsFromContentProvider returns the list of files it copied to local // storage, but we don't really care about what was copied NOW: what we want is the // list of everything we ever cached, so we ignore the return value. - BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context); + BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context, + hasDefaultWordList); final File[] cachedWordLists = getCachedWordLists(locale.toString(), context); final String mainDictId = getMainDictId(locale); diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index bf05f3bc3..17d75368e 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -94,13 +94,14 @@ public class DictionaryFactory { final Locale locale) { AssetFileDescriptor afd = null; try { - final int resId = getMainDictionaryResourceId(context.getResources(), locale); + final int resId = + getMainDictionaryResourceIdIfAvailableForLocale(context.getResources(), locale); + if (0 == resId) return null; afd = context.getResources().openRawResourceFd(resId); if (afd == null) { Log.e(TAG, "Found the resource but it is compressed. resId=" + resId); return null; } - if (!isFullDictionary(afd)) return null; final String sourceDir = context.getApplicationInfo().sourceDir; final File packagePath = new File(sourceDir); // TODO: Come up with a way to handle a directory. @@ -152,55 +153,19 @@ public class DictionaryFactory { */ public static boolean isDictionaryAvailable(Context context, Locale locale) { final Resources res = context.getResources(); - final int resourceId = getMainDictionaryResourceId(res, locale); - final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); - final boolean hasDictionary = isFullDictionary(afd); - try { - if (null != afd) afd.close(); - } catch (java.io.IOException e) { - /* Um, what can we do here exactly? */ - } - return hasDictionary; - } - - // TODO: Do not use the size of the dictionary as an unique dictionary ID. - public static Long getDictionaryId(final Context context, final Locale locale) { - final Resources res = context.getResources(); - final int resourceId = getMainDictionaryResourceId(res, locale); - final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); - final Long size = (afd != null && afd.getLength() > PLACEHOLDER_LENGTH) - ? afd.getLength() - : null; - try { - if (null != afd) afd.close(); - } catch (java.io.IOException e) { - } - return size; - } - - // TODO: Find the Right Way to find out whether the resource is a placeholder or not. - // Suggestion : strip the locale, open the placeholder file and store its offset. - // Upon opening the file, if it's the same offset, then it's the placeholder. - private static final long PLACEHOLDER_LENGTH = 34; - /** - * Finds out whether the data pointed out by an AssetFileDescriptor is a full - * dictionary (as opposed to null, or to a place holder). - * @param afd the file descriptor to test, or null - * @return true if the dictionary is a real full dictionary, false if it's null or a placeholder - */ - protected static boolean isFullDictionary(final AssetFileDescriptor afd) { - return (afd != null && afd.getLength() > PLACEHOLDER_LENGTH); + return 0 != getMainDictionaryResourceIdIfAvailableForLocale(res, locale); } private static final String DEFAULT_MAIN_DICT = "main"; private static final String MAIN_DICT_PREFIX = "main_"; /** - * Returns a main dictionary resource id + * Helper method to return a dictionary res id for a locale, or 0 if none. * @param locale dictionary locale * @return main dictionary resource id */ - public static int getMainDictionaryResourceId(Resources res, Locale locale) { + private static int getMainDictionaryResourceIdIfAvailableForLocale(final Resources res, + final Locale locale) { final String packageName = LatinIME.class.getPackage().getName(); int resId; @@ -218,6 +183,19 @@ public class DictionaryFactory { return resId; } + // Not found, return 0 + return 0; + } + + /** + * Returns a main dictionary resource id + * @param locale dictionary locale + * @return main dictionary resource id + */ + public static int getMainDictionaryResourceId(final Resources res, final Locale locale) { + int resourceId = getMainDictionaryResourceIdIfAvailableForLocale(res, locale); + if (0 != resourceId) return resourceId; + final String packageName = LatinIME.class.getPackage().getName(); return res.getIdentifier(DEFAULT_MAIN_DICT, "raw", packageName); } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 7cdeef897..187252a31 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1895,7 +1895,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mSettingsValues.mEnableSuggestionSpanInsertion) { final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions(); ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( - this, bestWord, suggestedWords), 1); + this, bestWord, suggestedWords, mSubtypeSwitcher.isDictionaryAvailable()), + 1); } else { ic.commitText(bestWord, 1); } diff --git a/java/src/com/android/inputmethod/latin/ResearchLogger.java b/java/src/com/android/inputmethod/latin/ResearchLogger.java index 08c26a5df..2d9f3ceb6 100644 --- a/java/src/com/android/inputmethod/latin/ResearchLogger.java +++ b/java/src/com/android/inputmethod/latin/ResearchLogger.java @@ -35,10 +35,16 @@ import com.android.inputmethod.keyboard.internal.AlphabetShiftState; import com.android.inputmethod.keyboard.internal.KeyboardState; import com.android.inputmethod.latin.define.ProductionFlag; +import java.io.BufferedWriter; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; /** * Logs the use of the LatinIME keyboard. @@ -70,15 +76,15 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang * Handles creation, deletion, and provides Readers, Writers, and InputStreams to access * the logs. */ - public static class LogFileManager { - private static final String DEFAULT_FILENAME = "log.txt"; - private static final String DEFAULT_LOG_DIRECTORY = "researchLogger"; + /* package */ static class LogFileManager { + public static final String RESEARCH_LOG_FILENAME_KEY = "RESEARCH_LOG_FILENAME"; + private static final String DEFAULT_FILENAME = "researchLog.txt"; private static final long LOGFILE_PURGE_INTERVAL = 1000 * 60 * 60 * 24; - private InputMethodService mIms; - private File mFile; - private PrintWriter mPrintWriter; + protected InputMethodService mIms; + protected File mFile; + protected PrintWriter mPrintWriter; /* package */ LogFileManager() { } @@ -89,15 +95,28 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public synchronized boolean createLogFile() { try { - return createLogFile(DEFAULT_LOG_DIRECTORY, DEFAULT_FILENAME); - } catch (final FileNotFoundException e) { + return createLogFile(DEFAULT_FILENAME); + } catch (IOException e) { + e.printStackTrace(); Log.w(TAG, e); return false; } } - public synchronized boolean createLogFile(final String dir, final String filename) - throws FileNotFoundException { + public synchronized boolean createLogFile(final SharedPreferences prefs) { + try { + final String filename = + prefs.getString(RESEARCH_LOG_FILENAME_KEY, DEFAULT_FILENAME); + return createLogFile(filename); + } catch (IOException e) { + Log.w(TAG, e); + e.printStackTrace(); + } + return false; + } + + public synchronized boolean createLogFile(final String filename) + throws IOException { if (mIms == null) { Log.w(TAG, "InputMethodService is not configured. Logging is off."); return false; @@ -107,29 +126,22 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang Log.w(TAG, "Storage directory does not exist. Logging is off."); return false; } - final File directory = new File(filesDir, dir); - if (!directory.exists()) { - final boolean wasCreated = directory.mkdirs(); - if (!wasCreated) { - Log.w(TAG, "Log directory cannot be created. Logging is off."); - return false; - } - } - close(); - mFile = new File(directory, filename); - mFile.setReadable(false, false); + final File file = new File(filesDir, filename); + mFile = file; + file.setReadable(false, false); boolean append = true; - if (mFile.exists() && mFile.lastModified() + LOGFILE_PURGE_INTERVAL < + if (file.exists() && file.lastModified() + LOGFILE_PURGE_INTERVAL < System.currentTimeMillis()) { append = false; } - mPrintWriter = new PrintWriter(new FileOutputStream(mFile, append), true); + mPrintWriter = new PrintWriter(new BufferedWriter(new FileWriter(file, append)), true); return true; } public synchronized boolean append(final String s) { - if (mPrintWriter == null) { + final PrintWriter printWriter = mPrintWriter; + if (printWriter == null) { if (DEBUG) { Log.w(TAG, "PrintWriter is null... attempting to create default log file"); } @@ -140,8 +152,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } } - mPrintWriter.print(s); - return !mPrintWriter.checkError(); + printWriter.print(s); + printWriter.flush(); + return !printWriter.checkError(); } public synchronized void reset() { @@ -149,7 +162,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mPrintWriter.close(); mPrintWriter = null; } - if (mFile != null && mFile.exists()) { + if (mFile != null) { mFile.delete(); mFile = null; } @@ -162,6 +175,53 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mFile = null; } } + + /* package */ synchronized void flush() { + if (mPrintWriter != null) { + mPrintWriter.flush(); + } + } + + /* package */ synchronized String getContents() { + final File file = mFile; + if (file == null) { + return ""; + } + if (mPrintWriter != null) { + mPrintWriter.flush(); + } + FileInputStream stream = null; + FileChannel fileChannel = null; + String s = ""; + try { + stream = new FileInputStream(file); + fileChannel = stream.getChannel(); + final ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length()); + fileChannel.read(byteBuffer); + byteBuffer.rewind(); + CharBuffer charBuffer = Charset.defaultCharset().decode(byteBuffer); + s = charBuffer.toString(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (fileChannel != null) { + fileChannel.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (stream != null) { + stream.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return s; + } } private ResearchLogger(final LogFileManager logFileManager) { @@ -182,9 +242,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public void initInternal(final InputMethodService ims, final SharedPreferences prefs) { mIms = ims; - if (mLogFileManager != null) { - mLogFileManager.init(ims); - mLogFileManager.createLogFile(); + final LogFileManager logFileManager = mLogFileManager; + if (logFileManager != null) { + logFileManager.init(ims); + logFileManager.createLogFile(prefs); } if (prefs != null) { sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false); @@ -359,6 +420,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang }); } + /* package */ LogFileManager getLogFileManager() { + return mLogFileManager; + } + @Override public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { if (key == null || prefs == null) { @@ -644,7 +709,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } - public static void suggestionsView_setSuggestions(SuggestedWords mSuggestedWords) { + public static void suggestionsView_setSuggestions(final SuggestedWords mSuggestedWords) { if (UnsLogGroup.SUGGESTIONSVIEW_SETSUGGESTIONS_ENABLED) { logUnstructured("SuggestionsView_setSuggestions", mSuggestedWords.toString()); } diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 6612c24cd..99f0ee184 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -63,6 +63,7 @@ public class SubtypeSwitcher { /*-----------------------------------------------------------*/ // Variants which should be changed only by reload functions. private boolean mNeedsToDisplayLanguage; + private boolean mIsDictionaryAvailable; private boolean mIsSystemLanguageSameAsInputLanguage; private InputMethodInfo mShortcutInputMethodInfo; private InputMethodSubtype mShortcutSubtype; @@ -260,6 +261,7 @@ public class SubtypeSwitcher { getInputLocale().getLanguage()); mNeedsToDisplayLanguage = !(getEnabledKeyboardLocaleCount() <= 1 && mIsSystemLanguageSameAsInputLanguage); + mIsDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(mService, mInputLocale); } //////////////////////////// @@ -379,6 +381,10 @@ public class SubtypeSwitcher { } } + public boolean isDictionaryAvailable() { + return mIsDictionaryAvailable; + } + // TODO: Remove this method private boolean isKeyboardMode() { return KEYBOARD_MODE.equals(getCurrentSubtypeMode()); |