diff options
56 files changed, 148 insertions, 79 deletions
diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_holo.9.png Binary files differnew file mode 100644 index 000000000..680421eaf --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png Binary files differnew file mode 100644 index 000000000..ae2675053 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_off_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_off_holo.9.png Binary files differnew file mode 100644 index 000000000..c92a669f9 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_off_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_holo.9.png Binary files differnew file mode 100644 index 000000000..40f5011c0 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png Binary files differnew file mode 100644 index 000000000..6ff6319d3 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png Binary files differnew file mode 100644 index 000000000..818ea70fd --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png Binary files differnew file mode 100644 index 000000000..a476d2a9e --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png Binary files differnew file mode 100644 index 000000000..9c280a655 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png Binary files differnew file mode 100644 index 000000000..3c17c5eec --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_popup_selected_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_popup_selected_holo.9.png Binary files differnew file mode 100644 index 000000000..6d2af5942 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_popup_selected_holo.9.png diff --git a/java/res/drawable-xxhdpi/ic_notify_dictionary.png b/java/res/drawable-xxhdpi/ic_notify_dictionary.png Binary files differnew file mode 100644 index 000000000..b61d50472 --- /dev/null +++ b/java/res/drawable-xxhdpi/ic_notify_dictionary.png diff --git a/java/res/drawable-xxhdpi/ic_subtype_keyboard.png b/java/res/drawable-xxhdpi/ic_subtype_keyboard.png Binary files differnew file mode 100644 index 000000000..0bb4283b0 --- /dev/null +++ b/java/res/drawable-xxhdpi/ic_subtype_keyboard.png diff --git a/java/res/drawable-xxhdpi/keyboard_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_background_holo.9.png Binary files differnew file mode 100644 index 000000000..bcef0f839 --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_holo.9.png Binary files differnew file mode 100644 index 000000000..bd1ef3cd9 --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_left_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_left_background_holo.9.png Binary files differnew file mode 100644 index 000000000..65af4b569 --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_left_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_left_more_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_left_more_background_holo.9.png Binary files differnew file mode 100644 index 000000000..ac6750dcb --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_left_more_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_holo.9.png Binary files differnew file mode 100644 index 000000000..cea7c05f6 --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_right_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_right_background_holo.9.png Binary files differnew file mode 100644 index 000000000..520fa7c6b --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_right_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_right_more_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_right_more_background_holo.9.png Binary files differnew file mode 100644 index 000000000..eee221758 --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_right_more_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_popup_panel_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_popup_panel_background_holo.9.png Binary files differnew file mode 100644 index 000000000..721c24400 --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_popup_panel_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_suggest_strip_holo.9.png b/java/res/drawable-xxhdpi/keyboard_suggest_strip_holo.9.png Binary files differnew file mode 100644 index 000000000..08176fed0 --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_suggest_strip_holo.9.png diff --git a/java/res/drawable-xxhdpi/suggestions_strip_divider.png b/java/res/drawable-xxhdpi/suggestions_strip_divider.png Binary files differnew file mode 100644 index 000000000..d13ca42aa --- /dev/null +++ b/java/res/drawable-xxhdpi/suggestions_strip_divider.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_delete_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_delete_holo.png Binary files differnew file mode 100644 index 000000000..be3cb7ce7 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_delete_holo.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_language_switch.png b/java/res/drawable-xxhdpi/sym_keyboard_language_switch.png Binary files differnew file mode 100644 index 000000000..7cd0684a0 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_language_switch.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_return_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_return_holo.png Binary files differnew file mode 100644 index 000000000..7d9580796 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_return_holo.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_search_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_search_holo.png Binary files differnew file mode 100644 index 000000000..6b09d8e57 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_search_holo.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_settings_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo.png Binary files differnew file mode 100644 index 000000000..7041bb6ce --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_shift_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_shift_holo.png Binary files differnew file mode 100644 index 000000000..2b4fbbba6 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_shift_holo.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo.png Binary files differnew file mode 100644 index 000000000..91c8603fd --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_space_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_space_holo.png Binary files differnew file mode 100644 index 000000000..65aa5ea9b --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_space_holo.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_tab_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_tab_holo.png Binary files differnew file mode 100644 index 000000000..1f4ae3df7 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_tab_holo.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_holo.png Binary files differnew file mode 100644 index 000000000..f04cadf6f --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_voice_holo.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo.png Binary files differnew file mode 100644 index 000000000..e74d523bc --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo.png Binary files differnew file mode 100644 index 000000000..85289b2a3 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo.png Binary files differnew file mode 100644 index 000000000..e610678b1 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo.png diff --git a/java/res/values-fr/donottranslate.xml b/java/res/values-fr/donottranslate.xml index 2e916a734..f0644118a 100644 --- a/java/res/values-fr/donottranslate.xml +++ b/java/res/values-fr/donottranslate.xml @@ -19,9 +19,9 @@ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Symbols that are normally preceded by a space (used to add an auto-space before these) --> - <string name="symbols_preceded_by_space">([{*&;:!?</string> + <string name="symbols_preceded_by_space">([{&;:!?</string> <!-- Symbols that are normally followed by a space (used to add an auto-space after these) --> - <string name="symbols_followed_by_space">.,;:!?)]}*&</string> + <string name="symbols_followed_by_space">.,;:!?)]}&</string> <!-- Symbols that separate words --> <!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) --> <string name="symbols_word_separators">"	 \n"()[]{}*&<>+=|.,;:!?/_\"</string> diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml index d2554ee5f..5e990edd9 100644 --- a/java/res/values/donottranslate.xml +++ b/java/res/values/donottranslate.xml @@ -21,9 +21,9 @@ <!-- Symbols that are suggested between words --> <string name="suggested_punctuations">!,?,\\,,:,;,\",(,),\',-,/,@,_</string> <!-- Symbols that are normally preceded by a space (used to add an auto-space before these) --> - <string name="symbols_preceded_by_space">([{*&</string> + <string name="symbols_preceded_by_space">([{&</string> <!-- Symbols that are normally followed by a space (used to add an auto-space after these) --> - <string name="symbols_followed_by_space">.,;:!?)]}*&</string> + <string name="symbols_followed_by_space">.,;:!?)]}&</string> <!-- Symbols that separate words --> <!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) --> <string name="symbols_word_separators">"	 \n"()[]{}*&<>+=|.,;:!?/_\"</string> diff --git a/java/res/xml-sw600dp/keys_dvorak_123.xml b/java/res/xml-sw600dp/keys_dvorak_123.xml index 851c14b5e..58416abb4 100644 --- a/java/res/xml-sw600dp/keys_dvorak_123.xml +++ b/java/res/xml-sw600dp/keys_dvorak_123.xml @@ -53,8 +53,7 @@ latin:keyLabel="." latin:keyHintLabel="3" latin:additionalMoreKeys="3" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/more_keys_for_punctuation,%,>" /> + latin:moreKeys=">" /> </default> </switch> </merge> diff --git a/java/res/xml/keys_dvorak_123.xml b/java/res/xml/keys_dvorak_123.xml index 7124c89e7..fa94f1f28 100644 --- a/java/res/xml/keys_dvorak_123.xml +++ b/java/res/xml/keys_dvorak_123.xml @@ -77,8 +77,7 @@ latin:keyLabel="." latin:keyHintLabel="3" latin:additionalMoreKeys="3" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/more_keys_for_punctuation,%,>" /> + latin:moreKeys=">" /> </default> </switch> </merge> diff --git a/java/res/xml/row_dvorak4.xml b/java/res/xml/row_dvorak4.xml index 69bac1358..e6d487e1d 100644 --- a/java/res/xml/row_dvorak4.xml +++ b/java/res/xml/row_dvorak4.xml @@ -68,7 +68,8 @@ latin:keyboardLayout="@xml/key_space" /> <Key latin:keyLabel="z" - latin:moreKeys="!text/more_keys_for_z" /> + latin:keyLabelFlags="hasPopupHint" + latin:moreKeys="!text/more_keys_for_punctuation,!text/more_keys_for_z" /> <Key latin:keyStyle="enterKeyStyle" latin:keyWidth="fillRight" /> diff --git a/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java b/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java index c5aca174a..6d6c8f5c6 100644 --- a/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java +++ b/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java @@ -47,6 +47,7 @@ public class ButtonSwitcher extends FrameLayout { private Button mInstallButton; private Button mCancelButton; private Button mDeleteButton; + private DictionaryListInterfaceState mInterfaceState; private OnClickListener mOnClickListener; public ButtonSwitcher(Context context, AttributeSet attrs) { @@ -57,9 +58,10 @@ public class ButtonSwitcher extends FrameLayout { super(context, attrs, defStyle); } - public void reset() { + public void reset(final DictionaryListInterfaceState interfaceState) { mStatus = NOT_INITIALIZED; mAnimateToStatus = NOT_INITIALIZED; + mInterfaceState = interfaceState; } @Override @@ -153,6 +155,7 @@ public class ButtonSwitcher extends FrameLayout { private ViewPropertyAnimator animateButton(final View button, final int direction) { final float outerX = getWidth(); final float innerX = button.getX() - button.getTranslationX(); + mInterfaceState.removeFromCache((View)getParent()); if (ANIMATION_IN == direction) { button.setClickable(true); return button.animate().translationX(0); diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java index f1a2a8333..13c07de35 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java @@ -80,4 +80,8 @@ public class DictionaryListInterfaceState { mViewCache.add(view); return view; } + + public void removeFromCache(final View view) { + mViewCache.remove(view); + } } diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java index 767f895dc..939c25f10 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java @@ -22,13 +22,14 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; -import android.util.Log; import android.widget.Toast; import com.android.inputmethod.latin.R; import java.util.Locale; import java.util.Random; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** @@ -92,21 +93,27 @@ public final class DictionaryService extends Service { private static final long VERY_LONG_TIME = TimeUnit.DAYS.toMillis(14); /** - * The last seen start Id. This must be stored because we must only call stopSelfResult() with - * the last seen Id, or the service won't stop. + * An executor that serializes tasks given to it. */ - private int mLastSeenStartId; - - /** - * The command count. We need this because we need to not call stopSelfResult() while we still - * have commands running. - */ - private int mCommandCount; + private ThreadPoolExecutor mExecutor; + private static final int WORKER_THREAD_TIMEOUT_SECONDS = 15; @Override public void onCreate() { - mLastSeenStartId = 0; - mCommandCount = 0; + // By default, a thread pool executor does not timeout its core threads, so it will + // never kill them when there isn't any work to do any more. That would mean the service + // can never die! By creating it this way and calling allowCoreThreadTimeOut, we allow + // the single thread to time out after WORKER_THREAD_TIMEOUT_SECONDS = 15 seconds, allowing + // the process to be reclaimed by the system any time after that if it's not doing + // anything else. + // Executors#newSingleThreadExecutor creates a ThreadPoolExecutor but it returns the + // superclass ExecutorService which does not have the #allowCoreThreadTimeOut method, + // so we can't use that. + mExecutor = new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */, + WORKER_THREAD_TIMEOUT_SECONDS /* keepAliveTime */, + TimeUnit.SECONDS /* unit for keepAliveTime */, + new LinkedBlockingQueue<Runnable>() /* workQueue */); + mExecutor.allowCoreThreadTimeOut(true); } @Override @@ -131,33 +138,35 @@ public final class DictionaryService extends Service { * - Handle a finished download. * This executes the actions that must be taken after a file (metadata or dictionary data * has been downloaded (or failed to download). + * The commands that can be spun an another thread will be executed serially, in order, on + * a worker thread that is created on demand and terminates after a short while if there isn't + * any work left to do. */ @Override public synchronized int onStartCommand(final Intent intent, final int flags, final int startId) { final DictionaryService self = this; - mLastSeenStartId = startId; - mCommandCount += 1; if (SHOW_DOWNLOAD_TOAST_INTENT_ACTION.equals(intent.getAction())) { // This is a UI action, it can't be run in another thread showStartDownloadingToast(this, LocaleUtils.constructLocaleFromString( intent.getStringExtra(LOCALE_INTENT_ARGUMENT))); } else { - // If it's a command that does not require UI, create a thread to do the work - // and return right away. DATE_CHANGED or UPDATE_NOW are examples of such commands. - new Thread("updateOrFinishDownload") { + // If it's a command that does not require UI, arrange for the work to be done on a + // separate thread, so that we can return right away. The executor will spawn a thread + // if necessary, or reuse a thread that has become idle as appropriate. + // DATE_CHANGED or UPDATE_NOW are examples of commands that can be done on another + // thread. + mExecutor.submit(new Runnable() { @Override public void run() { dispatchBroadcast(self, intent); - synchronized(self) { - if (--mCommandCount <= 0) { - if (!stopSelfResult(mLastSeenStartId)) { - Log.e(TAG, "Can't stop ourselves"); - } - } - } + // Since calls to onStartCommand are serialized, the submissions to the executor + // are serialized. That means we are guaranteed to call the stopSelfResult() + // in the same order that we got them, so we don't need to take care of the + // order. + stopSelfResult(startId); } - }.start(); + }); } return Service.START_REDELIVER_INTENT; } diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java index 99cc5b924..ff5aba6d8 100644 --- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java +++ b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java @@ -199,6 +199,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper { final ContentValues defaultMetadataValues = new ContentValues(); defaultMetadataValues.put(CLIENT_CLIENT_ID_COLUMN, ""); defaultMetadataValues.put(CLIENT_METADATA_URI_COLUMN, defaultMetadataUri); + defaultMetadataValues.put(CLIENT_PENDINGID_COLUMN, UpdateHandler.NOT_AN_ID); db.insert(CLIENT_TABLE_NAME, null, defaultMetadataValues); } } @@ -358,21 +359,21 @@ public class MetadataDbHelper extends SQLiteOpenHelper { } /** - * Get the metadata download ID for a client ID. + * Get the metadata download ID for a metadata URI. * - * This will retrieve the download ID for the metadata file associated with a client ID. - * If there is no metadata download in progress for this client, it will return NOT_AN_ID. + * This will retrieve the download ID for the metadata file that has the passed URI. + * If this URI is not being downloaded right now, it will return NOT_AN_ID. * * @param context a context instance to open the database on - * @param clientId the client ID to retrieve the metadata download ID of + * @param uri the URI to retrieve the metadata download ID of * @return the metadata download ID, or NOT_AN_ID if no download is in progress */ - public static long getMetadataDownloadIdForClient(final Context context, - final String clientId) { + public static long getMetadataDownloadIdForURI(final Context context, + final String uri) { SQLiteDatabase defaultDb = getDb(context, null); final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME, new String[] { CLIENT_PENDINGID_COLUMN }, - CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId }, + CLIENT_METADATA_URI_COLUMN + " = ?", new String[] { uri }, null, null, null, null); try { if (!cursor.moveToFirst()) return UpdateHandler.NOT_AN_ID; @@ -782,6 +783,8 @@ public class MetadataDbHelper extends SQLiteOpenHelper { " but the values " + "contain a different ID : ", valuesClientId); return; } + // Default value for a pending ID is NOT_AN_ID + values.put(CLIENT_PENDINGID_COLUMN, UpdateHandler.NOT_AN_ID); final SQLiteDatabase defaultDb = getDb(context, ""); if (-1 == defaultDb.insert(CLIENT_TABLE_NAME, null, values)) { defaultDb.update(CLIENT_TABLE_NAME, values, diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java index f66ef8733..0e7c3bb7e 100644 --- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java @@ -272,23 +272,22 @@ public final class UpdateHandler { } /** - * Cancels a pending update, if there is one. + * Cancels downloading a file, if there is one for this URI. * - * If none, this is a no-op. + * If we are not currently downloading the file at this URI, this is a no-op. * * @param context the context to open the database on - * @param clientId the id of the client + * @param metadataUri the URI to cancel * @param manager an instance of DownloadManager */ private static void cancelUpdateWithDownloadManager(final Context context, - final String clientId, final DownloadManager manager) { + final String metadataUri, final DownloadManager manager) { synchronized (sSharedIdProtector) { final long metadataDownloadId = - MetadataDbHelper.getMetadataDownloadIdForClient(context, clientId); + MetadataDbHelper.getMetadataDownloadIdForURI(context, metadataUri); if (NOT_AN_ID == metadataDownloadId) return; manager.remove(metadataDownloadId); - writeMetadataDownloadId(context, - MetadataDbHelper.getMetadataUriAsString(context, clientId), NOT_AN_ID); + writeMetadataDownloadId(context, metadataUri, NOT_AN_ID); } // Consider a cancellation as a failure. As such, inform listeners that the download // has failed. @@ -298,10 +297,10 @@ public final class UpdateHandler { } /** - * Cancels a pending update, if there is one. + * Cancels a pending update for this client, if there is one. * - * If there is none, this is a no-op. This is a helper method that gets the - * download manager service. + * If we are not currently updating metadata for this client, this is a no-op. This is a helper + * method that gets the download manager service and the metadata URI for this client. * * @param context the context, to get an instance of DownloadManager * @param clientId the ID of the client we want to cancel the update of @@ -309,7 +308,8 @@ public final class UpdateHandler { public static void cancelUpdate(final Context context, final String clientId) { final DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); - if (null != manager) cancelUpdateWithDownloadManager(context, clientId, manager); + final String metadataUri = MetadataDbHelper.getMetadataUriAsString(context, clientId); + if (null != manager) cancelUpdateWithDownloadManager(context, metadataUri, manager); } /** @@ -773,7 +773,7 @@ public final class UpdateHandler { // We may come here if there is a new word list that we can't handle. Log.i(TAG, "Can't handle word list with id '" + id + "' because it has format" + " version " + metadataInfo.mFormatVersion + " and the maximum version" - + "we can handle is " + MAXIMUM_SUPPORTED_FORMAT_VERSION); + + " we can handle is " + MAXIMUM_SUPPORTED_FORMAT_VERSION); } continue; } else if (null == currentInfo) { diff --git a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java index 7ec7e9c13..ba1fce1a8 100644 --- a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java +++ b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java @@ -224,7 +224,7 @@ public final class WordListPreference extends Preference { (ButtonSwitcher)view.findViewById(R.id.wordlist_button_switcher); // We need to clear the state of the button switcher, because we reuse views; if we didn't // reset it would animate from whatever its old state was. - buttonSwitcher.reset(); + buttonSwitcher.reset(mInterfaceState); if (mInterfaceState.isOpen(mWordlistId)) { // The button is open. final int previousStatus = mInterfaceState.getStatus(mWordlistId); diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h index 607a74400..974bb483b 100644 --- a/native/jni/src/defines.h +++ b/native/jni/src/defines.h @@ -292,7 +292,7 @@ static inline void prof_out(void) { // of the binary dictionary where a {key,value} string pair scheme is used. #define LARGEST_INT_DIGIT_COUNT 11 -#define NOT_VALID_WORD (-99) +#define NOT_A_VALID_WORD_POS (-99) #define NOT_A_CODE_POINT (-1) #define NOT_A_DISTANCE (-1) #define NOT_A_COORDINATE (-1) diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h index 973da67e4..696be0aeb 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node.h +++ b/native/jni/src/suggest/core/dicnode/dic_node.h @@ -112,7 +112,7 @@ class DicNode { mIsUsed = true; mIsCachedForNextSuggestion = false; mDicNodeProperties.init( - NOT_A_DICT_POS, rootGroupPos, NOT_A_DICT_POS /* attributesPos */, + NOT_A_VALID_WORD_POS /* pos */, rootGroupPos, NOT_A_DICT_POS /* attributesPos */, NOT_A_CODE_POINT /* nodeCodePoint */, NOT_A_PROBABILITY /* probability */, false /* isTerminal */, true /* hasChildren */, false /* isBlacklistedOrNotAWord */, 0 /* depth */, 0 /* terminalDepth */); @@ -125,7 +125,7 @@ class DicNode { mIsUsed = true; mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; mDicNodeProperties.init( - NOT_A_DICT_POS, rootGroupPos, NOT_A_DICT_POS /* attributesPos */, + NOT_A_VALID_WORD_POS /* pos */, rootGroupPos, NOT_A_DICT_POS /* attributesPos */, NOT_A_CODE_POINT /* nodeCodePoint */, NOT_A_PROBABILITY /* probability */, false /* isTerminal */, true /* hasChildren */, false /* isBlacklistedOrNotAWord */, 0 /* depth */, 0 /* terminalDepth */); @@ -231,7 +231,7 @@ class DicNode { } bool isFirstWord() const { - return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos() == NOT_VALID_WORD; + return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos() == NOT_A_VALID_WORD_POS; } bool isCompletion(const int inputSize) const { diff --git a/native/jni/src/suggest/core/dicnode/dic_node_state_prevword.h b/native/jni/src/suggest/core/dicnode/dic_node_state_prevword.h index c3968c090..5854f4f6e 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_state_prevword.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_state_prevword.h @@ -29,7 +29,7 @@ class DicNodeStatePrevWord { public: AK_FORCE_INLINE DicNodeStatePrevWord() : mPrevWordCount(0), mPrevWordLength(0), mPrevWordStart(0), mPrevWordProbability(0), - mPrevWordNodePos(0) { + mPrevWordNodePos(NOT_A_VALID_WORD_POS) { memset(mPrevWord, 0, sizeof(mPrevWord)); memset(mPrevSpacePositions, 0, sizeof(mPrevSpacePositions)); } @@ -41,7 +41,7 @@ class DicNodeStatePrevWord { mPrevWordCount = 0; mPrevWordStart = 0; mPrevWordProbability = -1; - mPrevWordNodePos = NOT_VALID_WORD; + mPrevWordNodePos = NOT_A_VALID_WORD_POS; memset(mPrevSpacePositions, 0, sizeof(mPrevSpacePositions)); } diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp index 6c7f6667a..67fbc1a38 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp @@ -179,8 +179,9 @@ namespace latinime { const int unigramProbability = node->getProbability(); const int wordPos = node->getPos(); const int prevWordPos = node->getPrevWordPos(); - if (NOT_VALID_WORD == wordPos || NOT_VALID_WORD == prevWordPos) { - // Note: Normally wordPos comes from the dictionary and should never equal NOT_VALID_WORD. + if (NOT_A_VALID_WORD_POS == wordPos || NOT_A_VALID_WORD_POS == prevWordPos) { + // Note: Normally wordPos comes from the dictionary and should never equal + // NOT_A_VALID_WORD_POS. return ProbabilityUtils::backoff(unigramProbability); } if (multiBigramMap) { diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp index 708800938..748430233 100644 --- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp @@ -153,7 +153,7 @@ int BigramDictionary::getBigramListPositionForWord(const int *prevWord, const in if (0 >= prevWordLength) return 0; int pos = mBinaryDictionaryInfo->getStructurePolicy()->getTerminalNodePositionOfWord( mBinaryDictionaryInfo, prevWord, prevWordLength, forceLowerCaseSearch); - if (NOT_VALID_WORD == pos) return 0; + if (NOT_A_VALID_WORD_POS == pos) return 0; return BinaryFormat::getBigramListPositionForWordPosition( mBinaryDictionaryInfo->getDictRoot(), pos); } @@ -181,7 +181,7 @@ bool BigramDictionary::isValidBigram(const int *word0, int length0, const int *w if (0 == pos) return false; int nextWordPos = mBinaryDictionaryInfo->getStructurePolicy()->getTerminalNodePositionOfWord( mBinaryDictionaryInfo, word1, length1, false /* forceLowerCaseSearch */); - if (NOT_VALID_WORD == nextWordPos) return false; + if (NOT_A_VALID_WORD_POS == nextWordPos) return false; for (BinaryDictionaryBigramsIterator bigramsIt(mBinaryDictionaryInfo, pos); bigramsIt.hasNext(); /* no-op */) { diff --git a/native/jni/src/suggest/core/dictionary/binary_format.h b/native/jni/src/suggest/core/dictionary/binary_format.h index d3d597b5f..6a5afd12e 100644 --- a/native/jni/src/suggest/core/dictionary/binary_format.h +++ b/native/jni/src/suggest/core/dictionary/binary_format.h @@ -255,7 +255,7 @@ inline bool BinaryFormat::hasChildrenInFlags(const uint8_t flags) { } // This function gets the byte position of the last chargroup of the exact matching word in the -// dictionary. If no match is found, it returns NOT_VALID_WORD. +// dictionary. If no match is found, it returns NOT_A_VALID_WORD_POS. AK_FORCE_INLINE int BinaryFormat::getTerminalPosition(const uint8_t *const root, const int *const inWord, const int length, const bool forceLowerCaseSearch) { int pos = 0; @@ -264,22 +264,22 @@ AK_FORCE_INLINE int BinaryFormat::getTerminalPosition(const uint8_t *const root, while (true) { // If we already traversed the tree further than the word is long, there means // there was no match (or we would have found it). - if (wordPos >= length) return NOT_VALID_WORD; + if (wordPos >= length) return NOT_A_VALID_WORD_POS; int charGroupCount = BinaryFormat::getGroupCountAndForwardPointer(root, &pos); const int wChar = forceLowerCaseSearch ? CharUtils::toLowerCase(inWord[wordPos]) : inWord[wordPos]; while (true) { // If there are no more character groups in this node, it means we could not // find a matching character for this depth, therefore there is no match. - if (0 >= charGroupCount) return NOT_VALID_WORD; + if (0 >= charGroupCount) return NOT_A_VALID_WORD_POS; const int charGroupPos = pos; const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer(root, &pos); int character = BinaryFormat::getCodePointAndForwardPointer(root, &pos); if (character == wChar) { // This is the correct node. Only one character group may start with the same // char within a node, so either we found our match in this node, or there is - // no match and we can return NOT_VALID_WORD. So we will check all the characters - // in this character group indeed does match. + // no match and we can return NOT_A_VALID_WORD_POS. So we will check all the + // characters in this character group indeed does match. if (FLAG_HAS_MULTIPLE_CHARS & flags) { character = BinaryFormat::getCodePointAndForwardPointer(root, &pos); while (NOT_A_CODE_POINT != character) { @@ -288,8 +288,8 @@ AK_FORCE_INLINE int BinaryFormat::getTerminalPosition(const uint8_t *const root, // character that does not match, as explained above, it means the word is // not in the dictionary (by virtue of this chargroup being the only one to // match the word on the first character, but not matching the whole word). - if (wordPos >= length) return NOT_VALID_WORD; - if (inWord[wordPos] != character) return NOT_VALID_WORD; + if (wordPos >= length) return NOT_A_VALID_WORD_POS; + if (inWord[wordPos] != character) return NOT_A_VALID_WORD_POS; character = BinaryFormat::getCodePointAndForwardPointer(root, &pos); } } @@ -305,7 +305,7 @@ AK_FORCE_INLINE int BinaryFormat::getTerminalPosition(const uint8_t *const root, pos = BinaryFormat::skipProbability(FLAG_IS_TERMINAL, pos); } if (FLAG_GROUP_ADDRESS_TYPE_NOADDRESS == (MASK_GROUP_ADDRESS_TYPE & flags)) { - return NOT_VALID_WORD; + return NOT_A_VALID_WORD_POS; } // We have children and we are still shorter than the word we are searching for, so // we need to traverse children. Put the pointer on the children position, and @@ -474,7 +474,7 @@ AK_FORCE_INLINE int BinaryFormat::getCodePointsAndProbabilityAndReturnCodePointC AK_FORCE_INLINE int BinaryFormat::getBigramListPositionForWordPosition( const uint8_t *const root, int position) { - if (NOT_VALID_WORD == position) return 0; + if (NOT_A_VALID_WORD_POS == position) return 0; const uint8_t flags = getFlagsAndForwardPointer(root, &position); if (!(flags & FLAG_HAS_BIGRAMS)) return 0; if (flags & FLAG_HAS_MULTIPLE_CHARS) { diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp index f520a75b1..52e635975 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp @@ -89,7 +89,7 @@ int Dictionary::getProbability(const int *word, int length) const { mBinaryDictionaryInfo.getStructurePolicy(); int pos = structurePolicy->getTerminalNodePositionOfWord(&mBinaryDictionaryInfo, word, length, false /* forceLowerCaseSearch */); - if (NOT_VALID_WORD == pos) { + if (NOT_A_VALID_WORD_POS == pos) { return NOT_A_PROBABILITY; } return structurePolicy->getUnigramProbability(&mBinaryDictionaryInfo, pos); diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp index 71d369876..7651b19a0 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp +++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp @@ -32,13 +32,13 @@ void DicTraverseSession::init(const Dictionary *const dictionary, const int *pre mMultiWordCostMultiplier = binaryDictionaryInfo->getHeader()->getMultiWordCostMultiplier(); mSuggestOptions = suggestOptions; if (!prevWord) { - mPrevWordPos = NOT_VALID_WORD; + mPrevWordPos = NOT_A_VALID_WORD_POS; return; } // TODO: merge following similar calls to getTerminalPosition into one case-insensitive call. mPrevWordPos = binaryDictionaryInfo->getStructurePolicy()->getTerminalNodePositionOfWord( binaryDictionaryInfo, prevWord, prevWordLength, false /* forceLowerCaseSearch */); - if (mPrevWordPos == NOT_VALID_WORD) { + if (mPrevWordPos == NOT_A_VALID_WORD_POS) { // Check bigrams for lower-cased previous word if original was not found. Useful for // auto-capitalized words like "The [current_word]". mPrevWordPos = binaryDictionaryInfo->getStructurePolicy()->getTerminalNodePositionOfWord( diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h index f95a0b23d..de57e041a 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.h +++ b/native/jni/src/suggest/core/session/dic_traverse_session.h @@ -55,7 +55,7 @@ class DicTraverseSession { } AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr) - : mPrevWordPos(NOT_VALID_WORD), mProximityInfo(0), + : mPrevWordPos(NOT_A_VALID_WORD_POS), mProximityInfo(0), mDictionary(0), mSuggestOptions(0), mDicNodesCache(), mMultiBigramMap(), mInputSize(0), mPartiallyCommited(false), mMaxPointerCount(1), mMultiWordCostMultiplier(1.0f) { diff --git a/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java index 93befd7bf..e0a0863ae 100644 --- a/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java @@ -82,7 +82,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { File dictFile = null; try { Log.d(TAG, "This test can be used for profiling."); - Log.d(TAG, "Usage: please set UserHisotoryDictionary.PROFILE_SAVE_RESTORE to true."); + Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true."); final int numberOfWords = 1000; final Random random = new Random(123456); List<String> words = generateWords(numberOfWords, random); @@ -118,4 +118,54 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { } } } + + public void testStressTestForSwitchingLanguagesAndAddingWords() { + final int numberOfLanguages = 2; + final int numberOfLanguageSwitching = 100; + final int numberOfWordsIntertedForEachLanguageSwitch = 100; + + final File dictFiles[] = new File[numberOfLanguages]; + try { + final Random random = new Random(123456); + + // Create locales for this test. + String locales[] = new String[numberOfLanguages]; + for (int i = 0; i < numberOfLanguages; i++) { + locales[i] = "testSwitchingLanguages" + i; + final String fileName = "UserHistoryDictionary." + locales[i] + ".dict"; + dictFiles[i] = new File(getContext().getFilesDir(), fileName); + } + + final long now = System.currentTimeMillis(); + + for (int i = 0; i < numberOfLanguageSwitching; i++) { + final int index = i % numberOfLanguages; + // Switch languages to locales[index]. + final UserHistoryDictionary dict = UserHistoryDictionary.getInstance(getContext(), + locales[index], mPrefs); + final List<String> words = generateWords( + numberOfWordsIntertedForEachLanguageSwitch, random); + // Add random words to the user history dictionary. + addToDict(dict, words); + // write to file + dict.close(); + } + + final long end = System.currentTimeMillis(); + Log.d(TAG, "testStressTestForSwitchingLanguageAndAddingWords took " + + (end - now) + " ms"); + try { + Log.d(TAG, "waiting for writing ..."); + Thread.sleep(5000); + } catch (InterruptedException e) { + Log.d(TAG, "InterruptedException: " + e); + } + } finally { + for (final File file : dictFiles) { + if (file != null) { + file.delete(); + } + } + } + } } |