Merge "Fix Farsi keyboard"
diff --git a/java/proguard.flags b/java/proguard.flags
index 701786a..e33706c 100644
--- a/java/proguard.flags
+++ b/java/proguard.flags
@@ -47,6 +47,10 @@
   <init>(...);
 }
 
+-keep class com.android.inputmethod.latin.ResearchLogger {
+  void setLogFileManager(...);
+}
+
 # The support library contains references to newer platform versions.
 # Don't warn about those in case this app is linking against an older
 # platform version.  We know about them, and they are safe.
diff --git a/java/res/xml/key_styles_currency.xml b/java/res/xml/key_styles_currency.xml
index 9dec7af..bd1d959 100644
--- a/java/res/xml/key_styles_currency.xml
+++ b/java/res/xml/key_styles_currency.xml
@@ -79,23 +79,27 @@
         <case
             latin:languageCode="iw"
         >
+            <!-- U+20AA: "₪" NEW SHEQEL SIGN
+                 U+00A3: "£" POUND SIGN
+                 U+20AC: "€" EURO SIGN
+                 U+00A2: "¢" CENT SIGN -->
             <key-style
                 latin:styleName="currencyKeyStyle"
-                latin:keyLabel="₪"
+                latin:keyLabel="&#x20AA;"
                 latin:moreKeys="@string/more_keys_for_currency_general" />
             <key-style
                 latin:styleName="moreCurrency1KeyStyle"
-                latin:keyLabel="£" />
+                latin:keyLabel="&#x00A3;" />
             <key-style
                 latin:styleName="moreCurrency2KeyStyle"
-                latin:keyLabel="€" />
+                latin:keyLabel="&#x20AC;" />
             <key-style
                 latin:styleName="moreCurrency3KeyStyle"
                 latin:keyLabel="$"
-                latin:moreKeys="¢" />
+                latin:moreKeys="&#x00A2;" />
             <key-style
                 latin:styleName="moreCurrency4KeyStyle"
-                latin:keyLabel="¢" />
+                latin:keyLabel="&#x00A2;" />
         </case>
         <case
             latin:languageCode="fa"
@@ -127,23 +131,27 @@
         <case
             latin:countryCode="GB"
         >
+            <!-- U+00A3: "£" POUND SIGN
+                 U+20AC: "€" EURO SIGN
+                 U+00A5: "¥" YEN SIGN
+                 U+00A2: "¢" CENT SIGN -->
             <key-style
                 latin:styleName="currencyKeyStyle"
-                latin:keyLabel="£"
+                latin:keyLabel="&#x00A3;"
                 latin:moreKeys="@string/more_keys_for_currency_pound" />
             <key-style
                 latin:styleName="moreCurrency1KeyStyle"
-                latin:keyLabel="€" />
+                latin:keyLabel="&#x20AC;" />
             <key-style
                 latin:styleName="moreCurrency2KeyStyle"
-                latin:keyLabel="¥" />
+                latin:keyLabel="&#x00A5;" />
             <key-style
                 latin:styleName="moreCurrency3KeyStyle"
                 latin:keyLabel="$"
-                latin:moreKeys="¢" />
+                latin:moreKeys="&#x00A2;" />
             <key-style
                 latin:styleName="moreCurrency4KeyStyle"
-                latin:keyLabel="¢" />
+                latin:keyLabel="&#x00A2;" />
         </case>
         <default>
             <include
diff --git a/java/res/xml/key_styles_currency_dollar.xml b/java/res/xml/key_styles_currency_dollar.xml
index d5dca2a..8dd8498 100644
--- a/java/res/xml/key_styles_currency_dollar.xml
+++ b/java/res/xml/key_styles_currency_dollar.xml
@@ -19,20 +19,24 @@
 -->
 
 <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <!-- U+00A3: "£" POUND SIGN
+         U+00A2: "¢" CENT SIGN
+         U+20AC: "€" EURO SIGN
+         U+00A5: "¥" YEN SIGN -->
     <key-style
         latin:styleName="currencyKeyStyle"
         latin:keyLabel="$"
         latin:moreKeys="@string/more_keys_for_currency_dollar" />
     <key-style
         latin:styleName="moreCurrency1KeyStyle"
-        latin:keyLabel="£" />
+        latin:keyLabel="&#x00A3;" />
     <key-style
         latin:styleName="moreCurrency2KeyStyle"
-        latin:keyLabel="¢" />
+        latin:keyLabel="&#x00A2;" />
     <key-style
         latin:styleName="moreCurrency3KeyStyle"
-        latin:keyLabel="€" />
+        latin:keyLabel="&#x20AC;" />
     <key-style
         latin:styleName="moreCurrency4KeyStyle"
-        latin:keyLabel="¥" />
+        latin:keyLabel="&#x00A5;" />
 </merge>
diff --git a/java/res/xml/key_styles_currency_euro.xml b/java/res/xml/key_styles_currency_euro.xml
index 6edddf0..0573e09 100644
--- a/java/res/xml/key_styles_currency_euro.xml
+++ b/java/res/xml/key_styles_currency_euro.xml
@@ -19,21 +19,25 @@
 -->
 
 <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <!-- U+20AC: "€" EURO SIGN
+         U+00A3: "£" POUND SIGN
+         U+00A5: "¥" YEN SIGN
+         U+00A2: "¢" CENT SIGN -->
     <key-style
         latin:styleName="currencyKeyStyle"
-        latin:keyLabel="€"
+        latin:keyLabel="&#x20AC;"
         latin:moreKeys="@string/more_keys_for_currency_euro" />
     <key-style
         latin:styleName="moreCurrency1KeyStyle"
-        latin:keyLabel="£" />
+        latin:keyLabel="&#x00A3;" />
     <key-style
         latin:styleName="moreCurrency2KeyStyle"
-        latin:keyLabel="¥" />
+        latin:keyLabel="&#x00A5;" />
     <key-style
         latin:styleName="moreCurrency3KeyStyle"
         latin:keyLabel="$"
-        latin:moreKeys="¢" />
+        latin:moreKeys="&#x00A2;" />
     <key-style
         latin:styleName="moreCurrency4KeyStyle"
-        latin:keyLabel="¢" />
+        latin:keyLabel="&#x00A2;" />
 </merge>
diff --git a/java/src/com/android/inputmethod/latin/ResearchLogger.java b/java/src/com/android/inputmethod/latin/ResearchLogger.java
index 6ba9118..3b110bd 100644
--- a/java/src/com/android/inputmethod/latin/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/latin/ResearchLogger.java
@@ -49,7 +49,7 @@
 
     private static final ResearchLogger sInstance = new ResearchLogger(new LogFileManager());
     public static boolean sIsLogging = false;
-    private final Handler mLoggingHandler;
+    /* package */ final Handler mLoggingHandler;
     private InputMethodService mIms;
     private final Date mDate;
     private final SimpleDateFormat mDateFormat;
@@ -183,11 +183,13 @@
     }
 
     /**
-     * Change to a different logFileManager.  Will not allow it to be set to null.
+     * Change to a different logFileManager.
+     *
+     * @throws IllegalArgumentException if logFileManager is null
      */
-    /* package */ void setLogFileManager(ResearchLogger.LogFileManager manager) {
+    void setLogFileManager(LogFileManager manager) {
         if (manager == null) {
-            Log.w(TAG, "warning: trying to set null logFileManager.  ignoring.");
+            throw new IllegalArgumentException("warning: trying to set null logFileManager");
         } else {
             mLogFileManager = manager;
         }
@@ -241,8 +243,6 @@
         sb.append('\t'); sb.append(x);
         sb.append('\t'); sb.append(y);
         write(LogGroup.KEY, sb.toString());
-
-        LatinImeLogger.onPrintAllUsabilityStudyLogs();
     }
 
     public void logCorrection(String subgroup, String before, String after, int position) {
diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
new file mode 100644
index 0000000..cfb1d09
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 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.define;
+
+public class ProductionFlag {
+    public static final boolean IS_EXPERIMENTAL = false;
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index cd34ba8..5a17385 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -353,6 +353,11 @@
 
     @Override
     public boolean onUnbind(final Intent intent) {
+        closeAllDictionaries();
+        return false;
+    }
+
+    private void closeAllDictionaries() {
         final Map<String, DictionaryPool> oldPools = mDictionaryPools;
         mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>());
         final Map<String, Dictionary> oldUserDictionaries = mUserDictionaries;
@@ -378,7 +383,6 @@
                 dictToClose.close();
             }
         }
-        return false;
     }
 
     private DictionaryPool getDictionaryPool(final String locale) {
diff --git a/native/src/bigram_dictionary.cpp b/native/src/bigram_dictionary.cpp
index fa69de8..3704c47 100644
--- a/native/src/bigram_dictionary.cpp
+++ b/native/src/bigram_dictionary.cpp
@@ -151,8 +151,9 @@
 
     int *inputCodes = mInputCodes;
     int maxAlt = MAX_ALTERNATIVES;
+    const unsigned short firstBaseChar = toBaseLowerCase(*word);
     while (maxAlt > 0) {
-        if ((unsigned int) *inputCodes == (unsigned int) *word) {
+        if (toBaseLowerCase(*inputCodes) == firstBaseChar) {
             return true;
         }
         inputCodes++;
diff --git a/tests/src/com/android/inputmethod/latin/ResearchLoggerTests.java b/tests/src/com/android/inputmethod/latin/ResearchLoggerTests.java
new file mode 100644
index 0000000..6ccc4f2
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/ResearchLoggerTests.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.inputmethodservice.InputMethodService;
+import android.os.Handler;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.inputmethod.latin.ResearchLogger.LogFileManager;
+
+import java.io.FileNotFoundException;
+
+public class ResearchLoggerTests extends InputTestsBase {
+
+    private static final String TAG = ResearchLoggerTests.class.getSimpleName();
+    private static final int TEST_INT = 0x12345678;
+    private static final long TEST_LONG = 0x1234567812345678L;
+
+    private static ResearchLogger sLogger;
+    private MockLogFileManager mMockLogFileManager;
+
+    @Override
+    protected void setUp() {
+        super.setUp();
+        sLogger = ResearchLogger.getInstance();
+        mMockLogFileManager = new MockLogFileManager();
+        sLogger.setLogFileManager(mMockLogFileManager);
+        ResearchLogger.sIsLogging = true;
+    }
+
+    public static class MockLogFileManager extends LogFileManager {
+        private final StringBuilder mContents = new StringBuilder();
+
+        @Override
+        public void init(InputMethodService ims) {
+        }
+
+        @Override
+        public synchronized void createLogFile() {
+            mContents.setLength(0);
+        }
+
+        @Override
+        public synchronized void createLogFile(String dir, String filename)
+                throws FileNotFoundException {
+            mContents.setLength(0);
+        }
+
+        @Override
+        public synchronized boolean append(String s) {
+            mContents.append(s);
+            return true;
+        }
+
+        @Override
+        public synchronized void reset() {
+            mContents.setLength(0);
+        }
+
+        @Override
+        public synchronized void close() {
+            mContents.setLength(0);
+        }
+
+        private String getAppendedString() {
+            return mContents.toString();
+        }
+    }
+
+    private void waitOnResearchLogger() {
+        // post another Runnable that notify()'s the test that it may proceed.
+        // assumes that the MessageQueue is processed in-order
+        Handler handler = sLogger.mLoggingHandler;
+        handler.post(new Runnable() {
+            @Override
+            public void run() {
+                synchronized (ResearchLoggerTests.this) {
+                    ResearchLoggerTests.this.notify();
+                }
+            }
+        });
+        synchronized (this) {
+            try {
+                wait();
+            } catch (InterruptedException e) {
+                Log.i(TAG, "interrupted when waiting for handler to finish.", e);
+            }
+        }
+    }
+
+    /*********************** Tests *********************/
+    public void testLogStartsEmpty() {
+        waitOnResearchLogger();
+        String result = mMockLogFileManager.getAppendedString();
+        assertEquals(result, "");
+    }
+
+    public void testMotionEvent() {
+        // verify that input values appear somewhere in output
+        sLogger.logMotionEvent(MotionEvent.ACTION_CANCEL,
+                TEST_LONG, TEST_INT, 1111, 3333, 5555, 7777);
+        waitOnResearchLogger();
+        String output = mMockLogFileManager.getAppendedString();
+        assertTrue(output.matches("(?sui).*\\bcancel\\b.*"));
+        assertFalse(output.matches("(?sui).*\\bdown\\b.*"));
+        assertTrue(output.matches("(?s).*\\b" + TEST_LONG + "\\b.*"));
+        assertTrue(output.matches("(?s).*\\b" + TEST_INT + "\\b.*"));
+        assertTrue(output.matches("(?s).*\\b1111\\b.*"));
+        assertTrue(output.matches("(?s).*\\b3333\\b.*"));
+        assertTrue(output.matches("(?s).*\\b5555\\b.*"));
+        assertTrue(output.matches("(?s).*\\b7777\\b.*"));
+    }
+
+    public void testKeyEvent() {
+        type("abc");
+        waitOnResearchLogger();
+        String output = mMockLogFileManager.getAppendedString();
+        assertTrue(output.matches("(?s).*\\ba\\b.*"));
+        assertTrue(output.matches("(?s).*\\bb\\b.*"));
+        assertTrue(output.matches("(?s).*\\bc\\b.*"));
+    }
+
+    public void testCorrection() {
+        sLogger.logCorrection("aaaa", "thos", "this", 1);
+        waitOnResearchLogger();
+        String output = mMockLogFileManager.getAppendedString();
+        assertTrue(output.matches("(?sui).*\\baaaa\\b.*"));
+        assertTrue(output.matches("(?sui).*\\bthos\\b.*"));
+        assertTrue(output.matches("(?sui).*\\bthis\\b.*"));
+    }
+
+    public void testStateChange() {
+        sLogger.logStateChange("aaaa", "bbbb");
+        waitOnResearchLogger();
+        String output = mMockLogFileManager.getAppendedString();
+        assertTrue(output.matches("(?sui).*\\baaaa\\b.*"));
+        assertTrue(output.matches("(?sui).*\\bbbbb\\b.*"));
+    }
+
+    // TODO: add integration tests that start at point of event generation.
+}