diff options
Diffstat (limited to 'java/src')
13 files changed, 324 insertions, 109 deletions
diff --git a/java/src/com/android/inputmethod/compat/TextViewCompatUtils.java b/java/src/com/android/inputmethod/compat/TextViewCompatUtils.java index d4f1ea830..f8e1902c0 100644 --- a/java/src/com/android/inputmethod/compat/TextViewCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/TextViewCompatUtils.java @@ -22,23 +22,23 @@ import android.widget.TextView; import java.lang.reflect.Method; public final class TextViewCompatUtils { - // Note that TextView.setCompoundDrawablesRelative(Drawable,Drawable,Drawable,Drawable) has - // been introduced in API level 17 (Build.VERSION_CODE.JELLY_BEAN_MR1). - private static final Method METHOD_setCompoundDrawablesRelative = CompatUtils.getMethod( - TextView.class, "setCompoundDrawablesRelative", + // Note that TextView.setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable,Drawable, + // Drawable,Drawable) has been introduced in API level 17 (Build.VERSION_CODE.JELLY_BEAN_MR1). + private static final Method METHOD_setCompoundDrawablesRelativeWithIntrinsicBounds = + CompatUtils.getMethod(TextView.class, "setCompoundDrawablesRelativeWithIntrinsicBounds", Drawable.class, Drawable.class, Drawable.class, Drawable.class); private TextViewCompatUtils() { // This utility class is not publicly instantiable. } - public static void setCompoundDrawablesRelative(final TextView textView, final Drawable start, - final Drawable top, final Drawable end, final Drawable bottom) { - if (METHOD_setCompoundDrawablesRelative == null) { - textView.setCompoundDrawables(start, top, end, bottom); + public static void setCompoundDrawablesRelativeWithIntrinsicBounds(final TextView textView, + final Drawable start, final Drawable top, final Drawable end, final Drawable bottom) { + if (METHOD_setCompoundDrawablesRelativeWithIntrinsicBounds == null) { + textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom); return; } - CompatUtils.invoke(textView, null, METHOD_setCompoundDrawablesRelative, + CompatUtils.invoke(textView, null, METHOD_setCompoundDrawablesRelativeWithIntrinsicBounds, start, top, end, bottom); } } diff --git a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java index df4a52f4e..faf5d3c87 100644 --- a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java +++ b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java @@ -174,7 +174,7 @@ public final class ActionBatch { final long downloadId = UpdateHandler.registerDownloadRequest(manager, request, db, mWordList.mId, mWordList.mVersion); Utils.l("Starting download of", uri, "with id", downloadId); - PrivateLog.log("Starting download of " + uri + ", id : " + downloadId, context); + PrivateLog.log("Starting download of " + uri + ", id : " + downloadId); } } @@ -333,7 +333,7 @@ public final class ActionBatch { mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mChecksum, mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion); PrivateLog.log("Insert 'available' record for " + mWordList.mDescription - + " and locale " + mWordList.mLocale, context); + + " and locale " + mWordList.mLocale); db.insert(MetadataDbHelper.METADATA_TABLE_NAME, null, values); } } @@ -383,7 +383,7 @@ public final class ActionBatch { mWordList.mChecksum, mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion); PrivateLog.log("Insert 'preinstalled' record for " + mWordList.mDescription - + " and locale " + mWordList.mLocale, context); + + " and locale " + mWordList.mLocale); db.insert(MetadataDbHelper.METADATA_TABLE_NAME, null, values); } } @@ -424,7 +424,7 @@ public final class ActionBatch { mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mChecksum, mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion); PrivateLog.log("Updating record for " + mWordList.mDescription - + " and locale " + mWordList.mLocale, context); + + " and locale " + mWordList.mLocale); db.update(MetadataDbHelper.METADATA_TABLE_NAME, values, MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND " + MetadataDbHelper.VERSION_COLUMN + " = ?", diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java index f8d1c4fc9..4fbe16233 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java @@ -189,7 +189,7 @@ public final class DictionaryProvider extends ContentProvider { */ @Override public String getType(final Uri uri) { - PrivateLog.log("Asked for type of : " + uri, this); + PrivateLog.log("Asked for type of : " + uri); final int match = matchUri(uri); switch (match) { case NO_MATCH: return null; @@ -220,7 +220,7 @@ public final class DictionaryProvider extends ContentProvider { public Cursor query(final Uri uri, final String[] projection, final String selection, final String[] selectionArgs, final String sortOrder) { Utils.l("Uri =", uri); - PrivateLog.log("Query : " + uri, this); + PrivateLog.log("Query : " + uri); final String clientId = getClientId(uri); final int match = matchUri(uri); switch (match) { @@ -228,7 +228,7 @@ public final class DictionaryProvider extends ContentProvider { case DICTIONARY_V2_WHOLE_LIST: final Cursor c = MetadataDbHelper.queryDictionaries(getContext(), clientId); Utils.l("List of dictionaries with count", c.getCount()); - PrivateLog.log("Returned a list of " + c.getCount() + " items", this); + PrivateLog.log("Returned a list of " + c.getCount() + " items"); return c; case DICTIONARY_V2_DICT_INFO: // In protocol version 2, we return null if the client is unknown. Otherwise @@ -248,10 +248,10 @@ public final class DictionaryProvider extends ContentProvider { // TODO: pass clientId to the following function DictionaryService.updateNowIfNotUpdatedInAVeryLongTime(getContext()); if (null != dictFiles && dictFiles.size() > 0) { - PrivateLog.log("Returned " + dictFiles.size() + " files", this); + PrivateLog.log("Returned " + dictFiles.size() + " files"); return new ResourcePathCursor(dictFiles); } else { - PrivateLog.log("No dictionary files for this URL", this); + PrivateLog.log("No dictionary files for this URL"); return new ResourcePathCursor(Collections.<WordListInfo>emptyList()); } // V2_METADATA and V2_DATAFILE are not supported for query() @@ -488,7 +488,7 @@ public final class DictionaryProvider extends ContentProvider { public Uri insert(final Uri uri, final ContentValues values) throws UnsupportedOperationException { if (null == uri || null == values) return null; // Should never happen but let's be safe - PrivateLog.log("Insert, uri = " + uri.toString(), this); + PrivateLog.log("Insert, uri = " + uri.toString()); final String clientId = getClientId(uri); switch (matchUri(uri)) { case DICTIONARY_V2_METADATA: @@ -517,7 +517,7 @@ public final class DictionaryProvider extends ContentProvider { break; case DICTIONARY_V1_WHOLE_LIST: case DICTIONARY_V1_DICT_INFO: - PrivateLog.log("Attempt to insert : " + uri, this); + PrivateLog.log("Attempt to insert : " + uri); throw new UnsupportedOperationException( "Insertion in the dictionary is not supported in this version"); } @@ -532,7 +532,7 @@ public final class DictionaryProvider extends ContentProvider { @Override public int update(final Uri uri, final ContentValues values, final String selection, final String[] selectionArgs) throws UnsupportedOperationException { - PrivateLog.log("Attempt to update : " + uri, this); + PrivateLog.log("Attempt to update : " + uri); throw new UnsupportedOperationException("Updating dictionary words is not supported"); } } diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java index 5817eb498..46bb5543a 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java @@ -21,7 +21,6 @@ import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.IBinder; import android.text.format.DateUtils; import android.util.Log; @@ -190,7 +189,7 @@ public final class DictionaryService extends Service { // is still more recent than UPDATE_FREQUENCY, do nothing. if (!isLastUpdateAtLeastThisOld(context, UPDATE_FREQUENCY)) return; - PrivateLog.log("Date changed - registering alarm", context); + PrivateLog.log("Date changed - registering alarm"); AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); // Best effort to wake between midnight and MAX_ALARM_DELAY in the morning. @@ -215,7 +214,7 @@ public final class DictionaryService extends Service { private static boolean isLastUpdateAtLeastThisOld(final Context context, final long time) { final long now = System.currentTimeMillis(); final long lastUpdate = MetadataDbHelper.getOldestUpdateTime(context); - PrivateLog.log("Last update was " + lastUpdate, context); + PrivateLog.log("Last update was " + lastUpdate); return lastUpdate + time < now; } diff --git a/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java b/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java index c127ad540..c9e128d70 100644 --- a/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java +++ b/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java @@ -28,7 +28,8 @@ final class LogProblemReporter implements ProblemReporter { TAG = tag; } + @Override public void report(final Exception e) { - Log.e(TAG, "Reporting problem : " + e); + Log.e(TAG, "Reporting problem", e); } } diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java index 55f545aad..cb8e25e99 100644 --- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java +++ b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java @@ -16,13 +16,11 @@ package com.android.inputmethod.dictionarypack; -import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.provider.Settings; import android.text.TextUtils; import android.util.Log; @@ -309,8 +307,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper { * @param uri the metadata URI we just downloaded */ public static void saveLastUpdateTimeOfUri(final Context context, final String uri) { - PrivateLog.log("Save last update time of URI : " + uri + " " + System.currentTimeMillis(), - context); + PrivateLog.log("Save last update time of URI : " + uri + " " + System.currentTimeMillis()); final ContentValues values = new ContentValues(); values.put(CLIENT_LAST_UPDATE_DATE_COLUMN, System.currentTimeMillis()); final SQLiteDatabase defaultDb = getDb(context, null); diff --git a/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java b/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java index 8593c1c9b..67dd7b9b7 100644 --- a/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java +++ b/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java @@ -16,7 +16,6 @@ package com.android.inputmethod.dictionarypack; -import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; @@ -24,6 +23,7 @@ import android.database.sqlite.SQLiteOpenHelper; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Locale; /** * Class to keep long-term log. This is inactive in production, and is only for debug purposes. @@ -44,10 +44,10 @@ public class PrivateLog { + COLUMN_EVENT + " TEXT);"; private static final SimpleDateFormat sDateFormat = - new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US); private static PrivateLog sInstance = new PrivateLog(); - private static DebugHelper mDebugHelper = null; + private static DebugHelper sDebugHelper = null; private PrivateLog() { } @@ -55,8 +55,8 @@ public class PrivateLog { public static synchronized PrivateLog getInstance(final Context context) { if (!DEBUG) return sInstance; synchronized(PrivateLog.class) { - if (sInstance.mDebugHelper == null) { - sInstance.mDebugHelper = new DebugHelper(context); + if (sDebugHelper == null) { + sDebugHelper = new DebugHelper(context); } return sInstance; } @@ -94,16 +94,9 @@ public class PrivateLog { } - public static void log(String event, Context context) { + public static void log(String event) { if (!DEBUG) return; - final SQLiteDatabase l = getInstance(context).mDebugHelper.getWritableDatabase(); - mDebugHelper.insert(l, event); - } - - public static void log(String event, ContentProvider provider) { - if (!DEBUG) return; - final SQLiteDatabase l = - getInstance(provider.getContext()).mDebugHelper.getWritableDatabase(); - mDebugHelper.insert(l, event); + final SQLiteDatabase l = sDebugHelper.getWritableDatabase(); + DebugHelper.insert(l, event); } } diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java index e05a79b7b..a59660954 100644 --- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java @@ -181,10 +181,9 @@ public final class UpdateHandler { if (!cursor.moveToFirst()) return; do { final String clientId = cursor.getString(0); - if (TextUtils.isEmpty(clientId)) continue; // This probably can't happen final String metadataUri = MetadataDbHelper.getMetadataUriAsString(context, clientId); - PrivateLog.log("Update for clientId " + Utils.s(clientId), context); + PrivateLog.log("Update for clientId " + Utils.s(clientId)); Utils.l("Update for clientId", clientId, " which uses URI ", metadataUri); uris.add(metadataUri); } while (cursor.moveToNext()); @@ -212,7 +211,7 @@ public final class UpdateHandler { */ private static void updateClientsWithMetadataUri(final Context context, final boolean updateNow, final String metadataUri) { - PrivateLog.log("Update for metadata URI " + Utils.s(metadataUri), context); + PrivateLog.log("Update for metadata URI " + Utils.s(metadataUri)); final Request metadataRequest = new Request(Uri.parse(metadataUri)); Utils.l("Request =", metadataRequest); @@ -258,7 +257,7 @@ public final class UpdateHandler { // method will ignore it. writeMetadataDownloadId(context, metadataUri, downloadId); } - PrivateLog.log("Requested download with id " + downloadId, context); + PrivateLog.log("Requested download with id " + downloadId); } /** @@ -405,7 +404,7 @@ public final class UpdateHandler { /* package */ static void downloadFinished(final Context context, final Intent intent) { // Get and check the ID of the file that was downloaded final long fileId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, NOT_AN_ID); - PrivateLog.log("Download finished with id " + fileId, context); + PrivateLog.log("Download finished with id " + fileId); Utils.l("DownloadFinished with id", fileId); if (NOT_AN_ID == fileId) return; // Spurious wake-up: ignore @@ -492,7 +491,7 @@ public final class UpdateHandler { private static void publishUpdateCycleCompletedEvent(final Context context) { // Even if this is not successful, we have to publish the new state. - PrivateLog.log("Publishing update cycle completed event", context); + PrivateLog.log("Publishing update cycle completed event"); Utils.l("Publishing update cycle completed event"); for (UpdateEventListener listener : linkedCopyOfList(sUpdateEventListeners)) { listener.updateCycleCompleted(); @@ -583,7 +582,7 @@ public final class UpdateHandler { } Utils.l("Downloaded metadata :", newMetadata); - PrivateLog.log("Downloaded metadata\n" + newMetadata, context); + PrivateLog.log("Downloaded metadata\n" + newMetadata); final ActionBatch actions = computeUpgradeTo(context, clientId, newMetadata); // TODO: Check with UX how we should report to the user @@ -611,7 +610,7 @@ public final class UpdateHandler { MetadataDbHelper.DESCRIPTION_COLUMN), "for", downloadRecord.mClientId); PrivateLog.log("Downloaded a new word list with description : " + downloadRecord.mAttributes.getAsString(MetadataDbHelper.DESCRIPTION_COLUMN) - + " for " + downloadRecord.mClientId, context); + + " for " + downloadRecord.mClientId); final String locale = downloadRecord.mAttributes.getAsString(MetadataDbHelper.LOCALE_COLUMN); diff --git a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java index 0d923ae01..93f12d53e 100644 --- a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java +++ b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java @@ -23,7 +23,9 @@ import android.preference.DialogPreference; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.widget.Button; +import android.widget.ListView; import com.android.inputmethod.latin.R; @@ -42,6 +44,7 @@ public final class WordListPreference extends DialogPreference { // What to display in the "status" field when we receive unknown data as a status from // the content provider. Empty string sounds sensible. static final private String NO_STATUS_MESSAGE = ""; + static final private int NOT_AN_INDEX = -1; /// Actions static final private int ACTION_UNKNOWN = 0; @@ -64,7 +67,8 @@ public final class WordListPreference extends DialogPreference { static final private int ANIMATION_IN = 1; static final private int ANIMATION_OUT = 2; - private static Button sLastClickedActionButton = null; + private static int sLastClickedIndex = NOT_AN_INDEX; + private static String sLastClickedWordlistId = null; private final OnWordListPreferenceClick mPreferenceClickHandler = new OnWordListPreferenceClick(); private final OnActionButtonClick mActionButtonClickHandler = @@ -196,7 +200,8 @@ public final class WordListPreference extends DialogPreference { ((ViewGroup)view).setLayoutTransition(null); final Button button = (Button)view.findViewById(R.id.wordlist_button); button.setText(getButtonLabel(mStatus)); - button.setVisibility(View.INVISIBLE); + // String identity match. This is an ==, not an .equals, on purpose. + button.setVisibility(mWordlistId == sLastClickedWordlistId ? View.VISIBLE : View.INVISIBLE); button.setOnClickListener(mActionButtonClickHandler); view.setOnClickListener(mPreferenceClickHandler); } @@ -205,15 +210,31 @@ public final class WordListPreference extends DialogPreference { @Override public void onClick(final View v) { final Button button = (Button)v.findViewById(R.id.wordlist_button); - if (null != sLastClickedActionButton) { - animateButton(sLastClickedActionButton, ANIMATION_OUT); - } animateButton(button, ANIMATION_IN); - sLastClickedActionButton = button; + final ViewParent parent = v.getParent(); + // Just in case something changed in the framework, test for the concrete class + if (!(parent instanceof ListView)) return; + final ListView listView = (ListView)parent; + final int myIndex = listView.indexOfChild(v) + listView.getFirstVisiblePosition(); + if (NOT_AN_INDEX != sLastClickedIndex) { + animateButton(getButtonForIndex(listView, sLastClickedIndex), ANIMATION_OUT); + } + sLastClickedIndex = myIndex; + sLastClickedWordlistId = mWordlistId; + } + } + + private Button getButtonForIndex(final ListView listView, final int index) { + final int indexInChildren = index - listView.getFirstVisiblePosition(); + if (indexInChildren < 0 || index > listView.getLastVisiblePosition()) { + // The view is offscreen. + return null; } + return (Button)listView.getChildAt(indexInChildren).findViewById(R.id.wordlist_button); } private void animateButton(final Button button, final int direction) { + if (null == button) return; final float outerX = ((View)button.getParent()).getWidth(); final float innerX = button.getX() - button.getTranslationX(); if (View.INVISIBLE == button.getVisibility()) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 0e1c4dc31..16eab4bc4 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -439,6 +439,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mHandler.onCreate(); DEBUG = LatinImeLogger.sDBG; + // TODO: Resolve mutual dependencies of {@link #loadSettings()} and {@link #initSuggest()}. loadSettings(); initSuggest(); @@ -476,6 +477,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction final InputAttributes inputAttributes = new InputAttributes(getCurrentInputEditorInfo(), isFullscreenMode()); mSettings.loadSettings(locale, inputAttributes); + // May need to reset the contacts dictionary depending on the user settings. resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary()); } @@ -745,6 +747,11 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mRecapitalizeStatus.deactivate(); mCurrentlyPressedHardwareKeys.clear(); + // Note: the following does a round-trip IPC on the main thread: be careful + final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); + if (null != mSuggest && null != currentLocale && !currentLocale.equals(mSuggest.mLocale)) { + initSuggest(); + } if (mSuggestionStripView != null) { // This will set the punctuation suggestions if next word suggestion is off; // otherwise it will clear the suggestion strip. @@ -797,8 +804,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // to the user dictionary. if (null != mPositionalInfoForUserDictPendingAddition && mPositionalInfoForUserDictPendingAddition.tryReplaceWithActualWord( - mConnection, editorInfo, mLastSelectionEnd, - mSubtypeSwitcher.getCurrentSubtypeLocale())) { + mConnection, editorInfo, mLastSelectionEnd, currentLocale)) { mPositionalInfoForUserDictPendingAddition = null; } // If tryReplaceWithActualWord returns false, we don't know what word was @@ -1678,7 +1684,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private SuggestedWords getSuggestedWordsGestureLocked(final InputPointers batchPointers) { mLatinIme.mWordComposer.setBatchInputPointers(batchPointers); final SuggestedWords suggestedWords = - mLatinIme.getSuggestedWords(Suggest.SESSION_GESTURE); + mLatinIme.getSuggestedWordsOrOlderSuggestions(Suggest.SESSION_GESTURE); final int suggestionCount = suggestedWords.size(); if (suggestionCount <= 1) { final String mostProbableSuggestion = (suggestionCount == 0) ? null @@ -1970,9 +1976,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // If we have a recapitalize in progress, use it; otherwise, create a new one. if (!mRecapitalizeStatus.isActive() || !mRecapitalizeStatus.isSetAt(mLastSelectionStart, mLastSelectionEnd)) { + final CharSequence selectedText = + mConnection.getSelectedText(0 /* flags, 0 for no styles */); + if (TextUtils.isEmpty(selectedText)) return; // Race condition with the input connection mRecapitalizeStatus.initialize(mLastSelectionStart, mLastSelectionEnd, - mConnection.getSelectedText(0 /* flags, 0 for no styles */).toString(), - mSettings.getCurrentLocale(), mSettings.getWordSeparators()); + selectedText.toString(), mSettings.getCurrentLocale(), + mSettings.getWordSeparators()); // We trim leading and trailing whitespace. mRecapitalizeStatus.trim(); // Trimming the object may have changed the length of the string, and we need to @@ -2152,7 +2161,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction return; } - final SuggestedWords suggestedWords = getSuggestedWords(Suggest.SESSION_TYPING); + final SuggestedWords suggestedWords = + getSuggestedWordsOrOlderSuggestions(Suggest.SESSION_TYPING); final String typedWord = mWordComposer.getTypedWord(); showSuggestionStrip(suggestedWords, typedWord); } @@ -2162,7 +2172,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (keyboard == null || mSuggest == null) { return SuggestedWords.EMPTY; } - final String typedWord = mWordComposer.getTypedWord(); // Get the word on which we should search the bigrams. If we are composing a word, it's // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we // should just skip whitespace if any, so 1. @@ -2170,10 +2179,13 @@ public final class LatinIME extends InputMethodService implements KeyboardAction final String prevWord = mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators, mWordComposer.isComposingWord() ? 2 : 1); - final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer, - prevWord, keyboard.getProximityInfo(), mSettings.getCurrent().mCorrectionEnabled, - sessionId); - return maybeRetrieveOlderSuggestions(typedWord, suggestedWords); + return mSuggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(), + mSettings.getCurrent().mCorrectionEnabled, sessionId); + } + + private SuggestedWords getSuggestedWordsOrOlderSuggestions(final int sessionId) { + return maybeRetrieveOlderSuggestions(mWordComposer.getTypedWord(), + getSuggestedWords(sessionId)); } private SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord, @@ -2186,7 +2198,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // old suggestions. Also, if we are showing the "add to dictionary" hint, we need to // revert to suggestions - although it is unclear how we can come here if it's displayed. if (suggestedWords.size() > 1 || typedWord.length() <= 1 - || suggestedWords.mTypedWordValid + || suggestedWords.mTypedWordValid || null == mSuggestionStripView || mSuggestionStripView.isShowingAddToDictionaryHint()) { return suggestedWords; } else { @@ -2577,8 +2589,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // Outside LatinIME, only used by the {@link InputTestsBase} test suite. @UsedForTesting void loadKeyboard() { - // When the device locale is changed in SetupWizard etc., this method may get called via - // onConfigurationChanged before SoftInputWindow is shown. + // TODO: Why are we calling {@link #loadSettings()} and {@link #initSuggest()} in a + // different order than in {@link #onStartInputView}? initSuggest(); loadSettings(); if (mKeyboardSwitcher.getMainKeyboardView() != null) { diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 671d7146b..59d0207f6 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -68,7 +68,7 @@ public final class Suggest { private float mAutoCorrectionThreshold; // Locale used for upper- and title-casing words - private final Locale mLocale; + public final Locale mLocale; public Suggest(final Context context, final Locale locale, final SuggestInitializationListener listener) { diff --git a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java index 099169aa9..a7a41719e 100644 --- a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java +++ b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java @@ -17,10 +17,12 @@ package com.android.inputmethod.latin.setup; import android.app.Activity; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.res.Resources; -import android.graphics.drawable.Drawable; +import android.media.MediaPlayer; +import android.net.Uri; import android.os.Bundle; import android.os.Message; import android.provider.Settings; @@ -28,6 +30,7 @@ import android.view.View; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.widget.TextView; +import android.widget.VideoView; import com.android.inputmethod.compat.TextViewCompatUtils; import com.android.inputmethod.compat.ViewCompatUtils; @@ -39,14 +42,23 @@ import com.android.inputmethod.latin.StaticInnerHandlerWrapper; import java.util.HashMap; -public final class SetupActivity extends Activity { +// TODO: Use Fragment to implement welcome screen and setup steps. +public final class SetupActivity extends Activity implements View.OnClickListener { + private View mWelcomeScreen; + private View mSetupScreen; private SetupStepIndicatorView mStepIndicatorView; - private final SetupStepGroup mSetupSteps = new SetupStepGroup(); + private Uri mWelcomeVideoUri; + private VideoView mWelcomeVideoView; + private View mActionStart; + private TextView mActionFinish; + private final SetupStepGroup mSetupStepGroup = new SetupStepGroup(); private static final String STATE_STEP = "step"; private int mStepNumber; + private static final int STEP_0 = 0; private static final int STEP_1 = 1; private static final int STEP_2 = 2; private static final int STEP_3 = 3; + private boolean mWasLanguageAndInputSettingsInvoked; private final SettingsPoolingHandler mHandler = new SettingsPoolingHandler(this); @@ -108,16 +120,20 @@ public final class SetupActivity extends Activity { return; } - // TODO: Use sans-serif-thin font family depending on the system locale white list and - // the SDK version. - final TextView titleView = (TextView)findViewById(R.id.setup_title); - final int appName = getApplicationInfo().labelRes; - titleView.setText(getString(R.string.setup_steps_title, getString(appName))); + final String applicationName = getResources().getString(getApplicationInfo().labelRes); + mWelcomeScreen = findViewById(R.id.setup_welcome_screen); + final TextView welcomeTitle = (TextView)findViewById(R.id.setup_welcome_title); + welcomeTitle.setText(getString(R.string.setup_welcome_title, applicationName)); + + mSetupScreen = findViewById(R.id.setup_steps_screen); + final TextView stepsTitle = (TextView)findViewById(R.id.setup_title); + stepsTitle.setText(getString(R.string.setup_steps_title, applicationName)); mStepIndicatorView = (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator); - final SetupStep step1 = new SetupStep(findViewById(R.id.setup_step1), - appName, R.string.setup_step1_title, R.string.setup_step1_instruction, + final SetupStep step1 = new SetupStep(applicationName, + (TextView)findViewById(R.id.setup_step1_bullet), findViewById(R.id.setup_step1), + R.string.setup_step1_title, R.string.setup_step1_instruction, R.drawable.ic_setup_step1, R.string.setup_step1_action); step1.setAction(new Runnable() { @Override @@ -126,10 +142,11 @@ public final class SetupActivity extends Activity { mHandler.startPollingImeSettings(); } }); - mSetupSteps.addStep(STEP_1, step1); + mSetupStepGroup.addStep(STEP_1, step1); - final SetupStep step2 = new SetupStep(findViewById(R.id.setup_step2), - appName, R.string.setup_step2_title, R.string.setup_step2_instruction, + final SetupStep step2 = new SetupStep(applicationName, + (TextView)findViewById(R.id.setup_step2_bullet), findViewById(R.id.setup_step2), + R.string.setup_step2_title, R.string.setup_step2_instruction, R.drawable.ic_setup_step2, R.string.setup_step2_action); step2.setAction(new Runnable() { @Override @@ -139,10 +156,11 @@ public final class SetupActivity extends Activity { .showInputMethodPicker(); } }); - mSetupSteps.addStep(STEP_2, step2); + mSetupStepGroup.addStep(STEP_2, step2); - final SetupStep step3 = new SetupStep(findViewById(R.id.setup_step3), - appName, R.string.setup_step3_title, R.string.setup_step3_instruction, + final SetupStep step3 = new SetupStep(applicationName, + (TextView)findViewById(R.id.setup_step3_bullet), findViewById(R.id.setup_step3), + R.string.setup_step3_title, R.string.setup_step3_instruction, R.drawable.ic_setup_step3, R.string.setup_step3_action); step3.setAction(new Runnable() { @Override @@ -150,7 +168,40 @@ public final class SetupActivity extends Activity { invokeSubtypeEnablerOfThisIme(); } }); - mSetupSteps.addStep(STEP_3, step3); + mSetupStepGroup.addStep(STEP_3, step3); + + mWelcomeVideoUri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(getPackageName()) + .path(Integer.toString(R.raw.setup_welcome_video)) + .build(); + mWelcomeVideoView = (VideoView)findViewById(R.id.setup_welcome_video); + mWelcomeVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + @Override + public void onCompletion(final MediaPlayer mp) { + mp.start(); + } + }); + + mActionStart = findViewById(R.id.setup_start_label); + mActionStart.setOnClickListener(this); + mActionFinish = (TextView)findViewById(R.id.setup_finish); + TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(mActionFinish, + getResources().getDrawable(R.drawable.ic_setup_finish), null, null, null); + mActionFinish.setOnClickListener(this); + } + + @Override + public void onClick(final View v) { + if (v == mActionStart) { + mStepNumber = STEP_1; + updateSetupStepView(); + return; + } + if (v == mActionFinish) { + finish(); + return; + } } private void invokeSetupWizardOfThisIme() { @@ -165,7 +216,8 @@ public final class SetupActivity extends Activity { final Intent intent = new Intent(); intent.setClass(this, SettingsActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED - | Intent.FLAG_ACTIVITY_CLEAR_TOP); + | Intent.FLAG_ACTIVITY_CLEAR_TOP + | Intent.FLAG_ACTIVITY_NO_HISTORY); startActivity(intent); } @@ -174,6 +226,7 @@ public final class SetupActivity extends Activity { intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS); intent.addCategory(Intent.CATEGORY_DEFAULT); startActivity(intent); + mWasLanguageAndInputSettingsInvoked = true; } private void invokeSubtypeEnablerOfThisIme() { @@ -224,7 +277,7 @@ public final class SetupActivity extends Activity { private int determineSetupStepNumber() { mHandler.cancelPollingImeSettings(); if (!isThisImeEnabled(this)) { - return STEP_1; + return mWasLanguageAndInputSettingsInvoked ? STEP_1 : STEP_0; } if (!isThisImeCurrent(this)) { return STEP_2; @@ -263,6 +316,22 @@ public final class SetupActivity extends Activity { } @Override + public void onBackPressed() { + if (mStepNumber == STEP_1) { + mStepNumber = STEP_0; + updateSetupStepView(); + return; + } + super.onBackPressed(); + } + + @Override + protected void onPause() { + mWelcomeVideoView.stopPlayback(); + super.onPause(); + } + + @Override public void onWindowFocusChanged(final boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (!hasFocus) { @@ -273,10 +342,20 @@ public final class SetupActivity extends Activity { } private void updateSetupStepView() { + final boolean welcomeScreen = (mStepNumber == STEP_0); + mWelcomeScreen.setVisibility(welcomeScreen ? View.VISIBLE : View.GONE); + mSetupScreen.setVisibility(welcomeScreen ? View.GONE: View.VISIBLE); + if (welcomeScreen) { + mWelcomeVideoView.setVideoURI(mWelcomeVideoUri); + mWelcomeVideoView.start(); + return; + } + mWelcomeVideoView.stopPlayback(); final int layoutDirection = ViewCompatUtils.getLayoutDirection(mStepIndicatorView); mStepIndicatorView.setIndicatorPosition( - getIndicatorPosition(mStepNumber, mSetupSteps.getTotalStep(), layoutDirection)); - mSetupSteps.enableStep(mStepNumber); + getIndicatorPosition(mStepNumber, mSetupStepGroup.getTotalStep(), layoutDirection)); + mSetupStepGroup.enableStep(mStepNumber); + mActionFinish.setVisibility((mStepNumber == STEP_3) ? View.VISIBLE : View.GONE); } private static float getIndicatorPosition(final int step, final int totalStep, @@ -286,20 +365,26 @@ public final class SetupActivity extends Activity { } static final class SetupStep implements View.OnClickListener { - private final View mRootView; + private final View mStepView; + private final TextView mBulletView; + private final int mActivatedColor; + private final int mDeactivatedColor; private final TextView mActionLabel; private Runnable mAction; - public SetupStep(final View rootView, final int appName, final int title, - final int instruction, final int actionIcon, final int actionLabel) { - mRootView = rootView; - final Resources res = rootView.getResources(); - final String applicationName = res.getString(appName); + public SetupStep(final String applicationName, final TextView bulletView, + final View stepView, final int title, final int instruction, final int actionIcon, + final int actionLabel) { + mStepView = stepView; + mBulletView = bulletView; + final Resources res = stepView.getResources(); + mActivatedColor = res.getColor(R.color.setup_text_action); + mDeactivatedColor = res.getColor(R.color.setup_text_dark); - final TextView titleView = (TextView)rootView.findViewById(R.id.setup_step_title); + final TextView titleView = (TextView)mStepView.findViewById(R.id.setup_step_title); titleView.setText(res.getString(title, applicationName)); - final TextView instructionView = (TextView)rootView.findViewById( + final TextView instructionView = (TextView)mStepView.findViewById( R.id.setup_step_instruction); if (instruction == 0) { instructionView.setVisibility(View.GONE); @@ -307,21 +392,20 @@ public final class SetupActivity extends Activity { instructionView.setText(res.getString(instruction, applicationName)); } - mActionLabel = (TextView)rootView.findViewById(R.id.setup_step_action_label); + mActionLabel = (TextView)mStepView.findViewById(R.id.setup_step_action_label); mActionLabel.setText(res.getString(actionLabel)); if (actionIcon == 0) { final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel); ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0); } else { - final Drawable icon = res.getDrawable(actionIcon); - icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); - TextViewCompatUtils.setCompoundDrawablesRelative( - mActionLabel, icon, null, null, null); + TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds( + mActionLabel, res.getDrawable(actionIcon), null, null, null); } } public void setEnabled(final boolean enabled) { - mRootView.setVisibility(enabled ? View.VISIBLE : View.GONE); + mStepView.setVisibility(enabled ? View.VISIBLE : View.GONE); + mBulletView.setTextColor(enabled ? mActivatedColor : mDeactivatedColor); } public void setAction(final Runnable action) { @@ -331,8 +415,9 @@ public final class SetupActivity extends Activity { @Override public void onClick(final View v) { - if (mAction != null) { + if (v == mActionLabel && mAction != null) { mAction.run(); + return; } } } diff --git a/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java b/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java new file mode 100644 index 000000000..ca974f6b8 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java @@ -0,0 +1,108 @@ +/* + * 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 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.setup; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.inputmethod.compat.ViewCompatUtils; +import com.android.inputmethod.latin.R; + +public final class SetupStartIndicatorView extends LinearLayout { + public SetupStartIndicatorView(final Context context, final AttributeSet attrs) { + super(context, attrs); + setOrientation(HORIZONTAL); + LayoutInflater.from(context).inflate(R.layout.setup_start_indicator_label, this); + + final LabelView labelView = (LabelView)findViewById(R.id.setup_start_label); + labelView.setIndicatorView(findViewById(R.id.setup_start_indicator)); + } + + public static final class LabelView extends TextView { + private View mIndicatorView; + + public LabelView(final Context context, final AttributeSet attrs) { + super(context, attrs); + } + + public void setIndicatorView(final View indicatorView) { + mIndicatorView = indicatorView; + } + + @Override + public void setPressed(final boolean pressed) { + super.setPressed(pressed); + if (mIndicatorView != null) { + mIndicatorView.setPressed(pressed); + } + } + } + + public static final class IndicatorView extends View { + private final Path mIndicatorPath = new Path(); + private final Paint mIndicatorPaint = new Paint(); + private final ColorStateList mIndicatorColor; + + public IndicatorView(final Context context, final AttributeSet attrs) { + super(context, attrs); + mIndicatorColor = getResources().getColorStateList( + R.color.setup_step_action_background); + mIndicatorPaint.setStyle(Paint.Style.FILL); + } + + @Override + public void setPressed(final boolean pressed) { + super.setPressed(pressed); + invalidate(); + } + + @Override + protected void onDraw(final Canvas canvas) { + super.onDraw(canvas); + final int layoutDirection = ViewCompatUtils.getLayoutDirection(this); + final int width = getWidth(); + final int height = getHeight(); + final float halfHeight = height / 2.0f; + final Path path = mIndicatorPath; + path.rewind(); + if (layoutDirection == ViewCompatUtils.LAYOUT_DIRECTION_RTL) { + // Left arrow + path.moveTo(width, 0.0f); + path.lineTo(0.0f, halfHeight); + path.lineTo(width, height); + } else { // LAYOUT_DIRECTION_LTR + // Right arrow + path.moveTo(0.0f, 0.0f); + path.lineTo(width, halfHeight); + path.lineTo(0.0f, height); + } + path.close(); + final int[] stateSet = getDrawableState(); + final int color = mIndicatorColor.getColorForState(stateSet, 0); + mIndicatorPaint.setColor(color); + canvas.drawPath(path, mIndicatorPaint); + } + } +} |