Hide the correct button when pressing a new button.

Bug: 7600384
Change-Id: Ic5dbbbbcf19d778dde1316ebb417f1a8ede2d25d
diff --git a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
index df4a52f..faf5d3c 100644
--- a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
+++ b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
@@ -174,7 +174,7 @@
             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 @@
                     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 @@
                     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 @@
                     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 f8d1c4f..4fbe162 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
@@ -189,7 +189,7 @@
      */
     @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 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 @@
             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 @@
                 // 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 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 @@
                 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 @@
     @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 5817eb4..46bb554 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.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 @@
         // 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 @@
     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 c127ad5..c9e128d 100644
--- a/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java
+++ b/java/src/com/android/inputmethod/dictionarypack/LogProblemReporter.java
@@ -28,7 +28,8 @@
         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 55f545a..cb8e25e 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 @@
      * @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 8593c1c..67dd7b9 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 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 @@
             + 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 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 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 3173e91..a596609 100644
--- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
+++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
@@ -183,7 +183,7 @@
                 final String clientId = cursor.getString(0);
                 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());
@@ -211,7 +211,7 @@
      */
     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);
 
@@ -257,7 +257,7 @@
             // method will ignore it.
             writeMetadataDownloadId(context, metadataUri, downloadId);
         }
-        PrivateLog.log("Requested download with id " + downloadId, context);
+        PrivateLog.log("Requested download with id " + downloadId);
     }
 
     /**
@@ -404,7 +404,7 @@
     /* 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
 
@@ -491,7 +491,7 @@
 
     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();
@@ -582,7 +582,7 @@
         }
 
         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
@@ -610,7 +610,7 @@
                 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 0d923ae..37b20b4 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.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 @@
     // 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,7 @@
     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 final OnWordListPreferenceClick mPreferenceClickHandler =
             new OnWordListPreferenceClick();
     private final OnActionButtonClick mActionButtonClickHandler =
@@ -205,15 +208,30 @@
         @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;
         }
     }
 
+    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()) {