aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionaryService.java4
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java25
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java10
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java1
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java9
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java7
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java34
-rw-r--r--java/src/com/android/inputmethod/research/LogUnit.java26
8 files changed, 81 insertions, 35 deletions
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
index 6e3dd7109..3b00723e3 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
@@ -170,7 +170,7 @@ public final class DictionaryService extends Service {
checkTimeAndMaybeSetupUpdateAlarm(context);
} else if (DictionaryPackConstants.UPDATE_NOW_INTENT_ACTION.equals(intent.getAction())) {
// Intent to trigger an update now.
- UpdateHandler.update(context, false);
+ UpdateHandler.tryUpdate(context, false);
} else {
UpdateHandler.downloadFinished(context, intent);
}
@@ -221,7 +221,7 @@ public final class DictionaryService extends Service {
*/
public static void updateNowIfNotUpdatedInAVeryLongTime(final Context context) {
if (!isLastUpdateAtLeastThisOld(context, VERY_LONG_TIME)) return;
- UpdateHandler.update(context, false);
+ UpdateHandler.tryUpdate(context, false);
}
/**
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
index 4b89d20bb..7bbd041e7 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
@@ -30,6 +30,7 @@ import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
+import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.animation.AnimationUtils;
@@ -104,9 +105,16 @@ public final class DictionarySettingsFragment extends PreferenceFragment
@Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
- mUpdateNowMenu = menu.add(Menu.NONE, MENU_UPDATE_NOW, 0, R.string.check_for_updates_now);
- mUpdateNowMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- refreshNetworkState();
+ final String metadataUri =
+ MetadataDbHelper.getMetadataUriAsString(getActivity(), mClientId);
+ // We only add the "Refresh" button if we have a non-empty URL to refresh from. If the
+ // URL is empty, of course we can't refresh so it makes no sense to display this.
+ if (!TextUtils.isEmpty(metadataUri)) {
+ mUpdateNowMenu =
+ menu.add(Menu.NONE, MENU_UPDATE_NOW, 0, R.string.check_for_updates_now);
+ mUpdateNowMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ refreshNetworkState();
+ }
}
@Override
@@ -353,7 +361,12 @@ public final class DictionarySettingsFragment extends PreferenceFragment
new Thread("updateByHand") {
@Override
public void run() {
- UpdateHandler.update(activity, true);
+ // We call tryUpdate(), which returns whether we could successfully start an update.
+ // If we couldn't, we'll never receive the end callback, so we stop the loading
+ // animation and return to the previous screen.
+ if (!UpdateHandler.tryUpdate(activity, true)) {
+ stopLoadingAnimation();
+ }
}
}.start();
}
@@ -368,7 +381,9 @@ public final class DictionarySettingsFragment extends PreferenceFragment
private void startLoadingAnimation() {
mLoadingView.setVisibility(View.VISIBLE);
getView().setVisibility(View.GONE);
- mUpdateNowMenu.setTitle(R.string.cancel);
+ // We come here when the menu element is pressed so presumably it can't be null. But
+ // better safe than sorry.
+ if (null != mUpdateNowMenu) mUpdateNowMenu.setTitle(R.string.cancel);
}
private void stopLoadingAnimation() {
diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
index 719f24e59..8a23acdef 100644
--- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
+++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
@@ -173,14 +173,15 @@ public final class UpdateHandler {
* Download latest metadata from the server through DownloadManager for all known clients
* @param context The context for retrieving resources
* @param updateNow Whether we should update NOW, or respect bandwidth policies
+ * @return true if an update successfully started, false otherwise.
*/
- public static void update(final Context context, final boolean updateNow) {
+ public static boolean tryUpdate(final Context context, final boolean updateNow) {
// TODO: loop through all clients instead of only doing the default one.
final TreeSet<String> uris = new TreeSet<String>();
final Cursor cursor = MetadataDbHelper.queryClientIds(context);
- if (null == cursor) return;
+ if (null == cursor) return false;
try {
- if (!cursor.moveToFirst()) return;
+ if (!cursor.moveToFirst()) return false;
do {
final String clientId = cursor.getString(0);
final String metadataUri =
@@ -192,6 +193,7 @@ public final class UpdateHandler {
} finally {
cursor.close();
}
+ boolean started = false;
for (final String metadataUri : uris) {
if (!TextUtils.isEmpty(metadataUri)) {
// If the metadata URI is empty, that means we should never update it at all.
@@ -200,8 +202,10 @@ public final class UpdateHandler {
// is a bug and it happens anyway, doing nothing is the right thing to do.
// For more information, {@see DictionaryProvider#insert(Uri, ContentValues)}.
updateClientsWithMetadataUri(context, updateNow, metadataUri);
+ started = true;
}
}
+ return started;
}
/**
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 321e80611..4b5d02716 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -439,7 +439,6 @@ public final class BinaryDictionaryFileDumper {
final ContentProviderClient client, final String clientId) throws RemoteException {
final String metadataFileUri = MetadataFileUriGetter.getMetadataUri(context);
final String metadataAdditionalId = MetadataFileUriGetter.getMetadataAdditionalId(context);
- if (TextUtils.isEmpty(metadataFileUri)) return;
// Tell the content provider to reset all information about this client id
final Uri metadataContentUri = getProviderUriBuilder(clientId)
.appendPath(QUERY_PATH_METADATA)
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 2e1e66140..31a892e19 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -287,13 +287,8 @@ final public class BinaryDictionaryGetter {
final Context context) {
final boolean hasDefaultWordList = DictionaryFactory.isDictionaryAvailable(context, locale);
- // We need internet access to do the following. Only do this if the package actually
- // has the permission.
- if (context.checkCallingOrSelfPermission(android.Manifest.permission.INTERNET)
- == PackageManager.PERMISSION_GRANTED) {
- BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context,
- hasDefaultWordList);
- }
+ BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context,
+ hasDefaultWordList);
final File[] cachedWordLists = getCachedWordLists(locale.toString(), context);
final String mainDictId = DictionaryInfoUtils.getMainDictId(locale);
final DictPackSettings dictPackSettings = new DictPackSettings(context);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 0560cf528..f3fc3177b 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -961,7 +961,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// TODO: is it still necessary to test for composingSpan related stuff?
final boolean selectionChangedOrSafeToReset = selectionChanged
|| (!mWordComposer.isComposingWord()) || noComposingSpan;
- if (selectionChangedOrSafeToReset) {
+ final boolean hasOrHadSelection = (oldSelStart != oldSelEnd
+ || newSelStart != newSelEnd);
+ final int moveAmount = newSelStart - oldSelStart;
+ if (selectionChangedOrSafeToReset && (hasOrHadSelection
+ || !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) {
// If we are composing a word and moving the cursor, we would want to set a
// suggestion span for recorrection to work correctly. Unfortunately, that
// would involve the keyboard committing some new text, which would move the
@@ -2535,6 +2539,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
mWordComposer.setComposingWord(typedWord, mKeyboardSwitcher.getKeyboard());
+ // TODO: this is in chars but the callee expects code points!
mWordComposer.setCursorPositionWithinWord(numberOfCharsInWordBeforeCursor);
mConnection.setComposingRegion(
mLastSelectionStart - numberOfCharsInWordBeforeCursor,
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index e078f03f4..2babe8b0c 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -192,6 +192,40 @@ public final class WordComposer {
return mCursorPositionWithinWord != mCodePointSize;
}
+ /**
+ * When the cursor is moved by the user, we need to update its position.
+ * If it falls inside the currently composing word, we don't reset the composition, and
+ * only update the cursor position.
+ *
+ * @param expectedMoveAmount How many java chars to move the cursor. Negative values move
+ * the cursor backward, positive values move the cursor forward.
+ * @return true if the cursor is still inside the composing word, false otherwise.
+ */
+ public boolean moveCursorByAndReturnIfInsideComposingWord(final int expectedMoveAmount) {
+ int actualMoveAmountWithinWord = 0;
+ int cursorPos = mCursorPositionWithinWord;
+ if (expectedMoveAmount >= 0) {
+ // Moving the cursor forward for the expected amount or until the end of the word has
+ // been reached, whichever comes first.
+ while (actualMoveAmountWithinWord < expectedMoveAmount && cursorPos < mCodePointSize) {
+ actualMoveAmountWithinWord += Character.charCount(mPrimaryKeyCodes[cursorPos]);
+ ++cursorPos;
+ }
+ } else {
+ // Moving the cursor backward for the expected amount or until the start of the word
+ // has been reached, whichever comes first.
+ while (actualMoveAmountWithinWord > expectedMoveAmount && cursorPos > 0) {
+ --cursorPos;
+ actualMoveAmountWithinWord -= Character.charCount(mPrimaryKeyCodes[cursorPos]);
+ }
+ }
+ // If the actual and expected amounts differ, we crossed the start or the end of the word
+ // so the result would not be inside the composing word.
+ if (actualMoveAmountWithinWord != expectedMoveAmount) return false;
+ mCursorPositionWithinWord = cursorPos;
+ return true;
+ }
+
public void setBatchInputPointers(final InputPointers batchPointers) {
mInputPointers.set(batchPointers);
mIsBatchMode = true;
diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java
index 164c7e8cc..3366df12a 100644
--- a/java/src/com/android/inputmethod/research/LogUnit.java
+++ b/java/src/com/android/inputmethod/research/LogUnit.java
@@ -146,7 +146,8 @@ public class LogUnit {
if (size != 0) {
// Note that jsonWriter is only set to a non-null value if the logUnit start text is
// output and at least one logStatement is output.
- JsonWriter jsonWriter = null;
+ JsonWriter jsonWriter = researchLog.getInitializedJsonWriterLocked();
+ outputLogUnitStart(jsonWriter, canIncludePrivateData);
for (int i = 0; i < size; i++) {
final LogStatement logStatement = mLogStatementList.get(i);
if (!canIncludePrivateData && logStatement.isPotentiallyPrivate()) {
@@ -155,42 +156,35 @@ public class LogUnit {
if (mIsPartOfMegaword && logStatement.isPotentiallyRevealing()) {
continue;
}
- // Only retrieve the jsonWriter if we need to. If we don't get this far, then
- // researchLog.getInitializedJsonWriterLocked() will not ever be called, and the
- // file will not have been opened for writing.
- if (jsonWriter == null) {
- jsonWriter = researchLog.getInitializedJsonWriterLocked();
- outputLogUnitStart(jsonWriter, canIncludePrivateData);
- }
logStatement.outputToLocked(jsonWriter, mTimeList.get(i), mValuesList.get(i));
}
- if (jsonWriter != null) {
- // We must have called logUnitStart earlier, so emit a logUnitStop.
- outputLogUnitStop(jsonWriter);
- }
+ outputLogUnitStop(jsonWriter);
}
}
private static final String WORD_KEY = "_wo";
+ private static final String NUM_WORDS_KEY = "_nw";
private static final String CORRECTION_TYPE_KEY = "_corType";
private static final String LOG_UNIT_BEGIN_KEY = "logUnitStart";
private static final String LOG_UNIT_END_KEY = "logUnitEnd";
final LogStatement LOGSTATEMENT_LOG_UNIT_BEGIN_WITH_PRIVATE_DATA =
new LogStatement(LOG_UNIT_BEGIN_KEY, false /* isPotentiallyPrivate */,
- false /* isPotentiallyRevealing */, WORD_KEY, CORRECTION_TYPE_KEY);
+ false /* isPotentiallyRevealing */, WORD_KEY, CORRECTION_TYPE_KEY,
+ NUM_WORDS_KEY);
final LogStatement LOGSTATEMENT_LOG_UNIT_BEGIN_WITHOUT_PRIVATE_DATA =
new LogStatement(LOG_UNIT_BEGIN_KEY, false /* isPotentiallyPrivate */,
- false /* isPotentiallyRevealing */);
+ false /* isPotentiallyRevealing */, NUM_WORDS_KEY);
private void outputLogUnitStart(final JsonWriter jsonWriter,
final boolean canIncludePrivateData) {
final LogStatement logStatement;
if (canIncludePrivateData) {
LOGSTATEMENT_LOG_UNIT_BEGIN_WITH_PRIVATE_DATA.outputToLocked(jsonWriter,
- SystemClock.uptimeMillis(), getWordsAsString(), getCorrectionType());
+ SystemClock.uptimeMillis(), getWordsAsString(), getCorrectionType(),
+ getNumWords());
} else {
LOGSTATEMENT_LOG_UNIT_BEGIN_WITHOUT_PRIVATE_DATA.outputToLocked(jsonWriter,
- SystemClock.uptimeMillis());
+ SystemClock.uptimeMillis(), getNumWords());
}
}