Merge "Add language switch key only to the phone layouts"
diff --git a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
index 700709d..c1c6d31 100644
--- a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
+++ b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
@@ -92,6 +92,7 @@
     private static final boolean DEBUG = LatinImeLogger.sDBG;
 
     private boolean mAfterVoiceInput;
+    private boolean mConfigurationChanging;
     private boolean mHasUsedVoiceInput;
     private boolean mHasUsedVoiceInputUnsupportedLocale;
     private boolean mImmediatelyAfterVoiceInput;
@@ -159,11 +160,11 @@
         mPasswordText = isPasswordText;
     }
 
-    public void flushVoiceInputLogs(boolean configurationChanged) {
+    public void flushVoiceInputLogs() {
         if (!VOICE_INSTALLED) {
             return;
         }
-        if (!configurationChanged) {
+        if (!mConfigurationChanging) {
             if (mAfterVoiceInput) {
                 mVoiceInput.flushAllTextModificationCounters();
                 mVoiceInput.logInputEnded();
@@ -318,11 +319,11 @@
         mImmediatelyAfterVoiceInput = false;
     }
 
-    public void hideVoiceWindow(boolean configurationChanging) {
+    public void hideVoiceWindow() {
         if (!VOICE_INSTALLED) {
             return;
         }
-        if (!configurationChanging) {
+        if (!mConfigurationChanging) {
             if (mAfterVoiceInput)
                 mVoiceInput.logInputEnded();
             if (mVoiceWarningDialog != null && mVoiceWarningDialog.isShowing()) {
@@ -842,4 +843,12 @@
                 SettingsUtil.LATIN_IME_VOICE_INPUT_SUPPORTED_LOCALES,
                 DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES);
     }
+
+    public void startChangingConfiguration() {
+        mConfigurationChanging = true;
+    }
+
+    public void finishChangingConfiguration() {
+        mConfigurationChanging = false;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index cb22b49..cb2a275 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -195,8 +195,8 @@
     private InputMethodManagerCompatWrapper mImm;
     private Resources mResources;
     private SharedPreferences mPrefs;
-    private KeyboardSwitcher mKeyboardSwitcher;
-    private SubtypeSwitcher mSubtypeSwitcher;
+    private final KeyboardSwitcher mKeyboardSwitcher;
+    private final SubtypeSwitcher mSubtypeSwitcher;
     private VoiceProxy mVoiceProxy;
     private boolean mShouldSwitchToLastSubtype = true;
 
@@ -226,9 +226,6 @@
 
     private VibratorCompatWrapper mVibrator;
 
-    // TODO: Move this flag to VoiceProxy
-    private boolean mConfigurationChanging;
-
     // Member variables for remembering the current device orientation.
     private int mDisplayOrientation;
 
@@ -492,6 +489,12 @@
         }
     }
 
+    public LatinIME() {
+        super();
+        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
+        mKeyboardSwitcher = KeyboardSwitcher.getInstance();
+    }
+
     @Override
     public void onCreate() {
         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
@@ -506,8 +509,6 @@
         super.onCreate();
 
         mImm = InputMethodManagerCompatWrapper.getInstance();
-        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
-        mKeyboardSwitcher = KeyboardSwitcher.getInstance();
         mVibrator = VibratorCompatWrapper.getInstance(this);
         mHandler.onCreate();
         DEBUG = LatinImeLogger.sDBG;
@@ -555,7 +556,6 @@
     // Has to be package-visible for unit tests
     /* package */ void loadSettings() {
         if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
-        if (null == mSubtypeSwitcher) mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         mSettingsValues = new SettingsValues(mPrefs, this, mSubtypeSwitcher.getInputLocaleStr());
         resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
     }
@@ -667,10 +667,10 @@
                 mOptionsDialog.dismiss();
         }
 
-        mConfigurationChanging = true;
+        mVoiceProxy.startChangingConfiguration();
         super.onConfigurationChanged(conf);
         mVoiceProxy.onConfigurationChanged(conf);
-        mConfigurationChanging = false;
+        mVoiceProxy.finishChangingConfiguration();
 
         // This will work only when the subtype is not supported.
         LanguageSwitcherProxy.onConfigurationChanged(conf);
@@ -833,7 +833,7 @@
 
         LatinImeLogger.commit();
 
-        mVoiceProxy.flushVoiceInputLogs(mConfigurationChanging);
+        mVoiceProxy.flushVoiceInputLogs();
 
         KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
         if (inputView != null) inputView.closing();
@@ -963,7 +963,7 @@
             mOptionsDialog.dismiss();
             mOptionsDialog = null;
         }
-        mVoiceProxy.hideVoiceWindow(mConfigurationChanging);
+        mVoiceProxy.hideVoiceWindow();
         super.hideWindow();
     }
 
diff --git a/native/src/bigram_dictionary.cpp b/native/src/bigram_dictionary.cpp
index 19b6446..84048d7 100644
--- a/native/src/bigram_dictionary.cpp
+++ b/native/src/bigram_dictionary.cpp
@@ -28,7 +28,7 @@
 BigramDictionary::BigramDictionary(const unsigned char *dict, int maxWordLength,
         int maxAlternatives, const bool isLatestDictVersion, const bool hasBigram,
         Dictionary *parentDictionary)
-    : DICT(dict + NEW_DICTIONARY_HEADER_SIZE), MAX_WORD_LENGTH(maxWordLength),
+    : DICT(dict), MAX_WORD_LENGTH(maxWordLength),
     MAX_ALTERNATIVES(maxAlternatives), IS_LATEST_DICT_VERSION(isLatestDictVersion),
     HAS_BIGRAM(hasBigram), mParentDictionary(parentDictionary) {
     if (DEBUG_DICT) {
diff --git a/native/src/binary_format.h b/native/src/binary_format.h
index 1d74998..ab033ad 100644
--- a/native/src/binary_format.h
+++ b/native/src/binary_format.h
@@ -17,6 +17,7 @@
 #ifndef LATINIME_BINARY_FORMAT_H
 #define LATINIME_BINARY_FORMAT_H
 
+#include <limits>
 #include "unigram_dictionary.h"
 
 namespace latinime {
@@ -29,10 +30,18 @@
 
  public:
     const static int UNKNOWN_FORMAT = -1;
-    const static int FORMAT_VERSION_1 = 1;
-    const static uint16_t FORMAT_VERSION_1_MAGIC_NUMBER = 0x78B1;
+    // Originally, format version 1 had a 16-bit magic number, then the version number `01'
+    // then options that must be 0. Hence the first 32-bits of the format are always as follow
+    // and it's okay to consider them a magic number as a whole.
+    const static uint32_t FORMAT_VERSION_1_MAGIC_NUMBER = 0x78B10100;
+    const static unsigned int FORMAT_VERSION_1_HEADER_SIZE = 5;
+    // The versions of Latin IME that only handle format version 1 only test for the magic
+    // number, so we had to change it so that version 2 files would be rejected by older
+    // implementations. On this occasion, we made the magic number 32 bits long.
+    const static uint32_t FORMAT_VERSION_2_MAGIC_NUMBER = 0x9BC13AFE;
 
     static int detectFormat(const uint8_t* const dict);
+    static unsigned int getHeaderSize(const uint8_t* const dict);
     static int getGroupCountAndForwardPointer(const uint8_t* const dict, int* pos);
     static uint8_t getFlagsAndForwardPointer(const uint8_t* const dict, int* pos);
     static int32_t getCharCodeAndForwardPointer(const uint8_t* const dict, int* pos);
@@ -55,9 +64,37 @@
 };
 
 inline int BinaryFormat::detectFormat(const uint8_t* const dict) {
-    const uint16_t magicNumber = (dict[0] << 8) + dict[1]; // big endian
-    if (FORMAT_VERSION_1_MAGIC_NUMBER == magicNumber) return FORMAT_VERSION_1;
-    return UNKNOWN_FORMAT;
+    // The magic number is stored big-endian.
+    const uint32_t magicNumber = (dict[0] << 24) + (dict[1] << 16) + (dict[2] << 8) + dict[3];
+    switch (magicNumber) {
+    case FORMAT_VERSION_1_MAGIC_NUMBER:
+        // Format 1 header is exactly 5 bytes long and looks like:
+        // Magic number (2 bytes) 0x78 0xB1
+        // Version number (1 byte) 0x01
+        // Options (2 bytes) must be 0x00 0x00
+        return 1;
+    case FORMAT_VERSION_2_MAGIC_NUMBER:
+        // Format 2 header is as follows:
+        // Magic number (4 bytes) 0x9B 0xC1 0x3A 0xFE
+        // Version number (2 bytes) 0x00 0x02
+        // Options (2 bytes) must be 0x00 0x00
+        // Header size (4 bytes) : integer, big endian
+        return (dict[4] << 8) + dict[5];
+    default:
+        return UNKNOWN_FORMAT;
+    }
+}
+
+inline unsigned int BinaryFormat::getHeaderSize(const uint8_t* const dict) {
+    switch (detectFormat(dict)) {
+    case 1:
+        return FORMAT_VERSION_1_HEADER_SIZE;
+    case 2:
+        // See the format of the header in the comment in detectFormat() above
+        return (dict[8] << 24) + (dict[9] << 16) + (dict[10] << 8) + dict[11];
+    default:
+        return std::numeric_limits<unsigned int>::max();
+    }
 }
 
 inline int BinaryFormat::getGroupCountAndForwardPointer(const uint8_t* const dict, int* pos) {
diff --git a/native/src/defines.h b/native/src/defines.h
index f402efa..afa1e04 100644
--- a/native/src/defines.h
+++ b/native/src/defines.h
@@ -162,9 +162,6 @@
 #define FLAG_BIGRAM_FREQ 0x7F
 
 #define DICTIONARY_VERSION_MIN 200
-// TODO: remove this constant when the switch to the new dict format is over
-#define DICTIONARY_HEADER_SIZE 2
-#define NEW_DICTIONARY_HEADER_SIZE 5
 #define NOT_VALID_WORD -99
 #define NOT_A_CHARACTER -1
 #define NOT_A_DISTANCE -1
diff --git a/native/src/dictionary.cpp b/native/src/dictionary.cpp
index 822c215..8e252f7 100644
--- a/native/src/dictionary.cpp
+++ b/native/src/dictionary.cpp
@@ -19,6 +19,7 @@
 
 #define LOG_TAG "LatinIME: dictionary.cpp"
 
+#include "binary_format.h"
 #include "dictionary.h"
 
 namespace latinime {
@@ -41,10 +42,11 @@
     mCorrection = new Correction(typedLetterMultiplier, fullWordMultiplier);
     mWordsPriorityQueuePool = new WordsPriorityQueuePool(
             maxWords, SUB_QUEUE_MAX_WORDS, maxWordLength);
-    mUnigramDictionary = new UnigramDictionary(mDict, typedLetterMultiplier, fullWordMultiplier,
-            maxWordLength, maxWords, maxAlternatives, IS_LATEST_DICT_VERSION);
-    mBigramDictionary = new BigramDictionary(mDict, maxWordLength, maxAlternatives,
-            IS_LATEST_DICT_VERSION, hasBigram(), this);
+    const unsigned int headerSize = BinaryFormat::getHeaderSize(mDict);
+    mUnigramDictionary = new UnigramDictionary(mDict + headerSize, typedLetterMultiplier,
+            fullWordMultiplier, maxWordLength, maxWords, maxAlternatives, IS_LATEST_DICT_VERSION);
+    mBigramDictionary = new BigramDictionary(mDict + headerSize, maxWordLength, maxAlternatives,
+            IS_LATEST_DICT_VERSION, true /* hasBigram */, this);
 }
 
 Dictionary::~Dictionary() {
@@ -54,10 +56,6 @@
     delete mBigramDictionary;
 }
 
-bool Dictionary::hasBigram() {
-    return ((mDict[1] & 0xFF) == 1);
-}
-
 bool Dictionary::isValidWord(unsigned short *word, int length) {
     return mUnigramDictionary->isValidWord(word, length);
 }
diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp
index 155bdcb..0b646d1 100644
--- a/native/src/unigram_dictionary.cpp
+++ b/native/src/unigram_dictionary.cpp
@@ -38,8 +38,7 @@
 UnigramDictionary::UnigramDictionary(const uint8_t* const streamStart, int typedLetterMultiplier,
         int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars,
         const bool isLatestDictVersion)
-    : DICT_ROOT(streamStart + NEW_DICTIONARY_HEADER_SIZE),
-    MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords),
+    : DICT_ROOT(streamStart), MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords),
     MAX_PROXIMITY_CHARS(maxProximityChars), IS_LATEST_DICT_VERSION(isLatestDictVersion),
     TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier),
       // TODO : remove this variable.
diff --git a/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java b/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java
index 937e76c..485dba1 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java
@@ -115,6 +115,7 @@
     private static final int VERSION_2_MAGIC_NUMBER = 0x9BC13AFE;
     private static final int MINIMUM_SUPPORTED_VERSION = 1;
     private static final int MAXIMUM_SUPPORTED_VERSION = 2;
+    private static final int NOT_A_VERSION_NUMBER = -1;
     private static final int FIRST_VERSION_WITH_HEADER_SIZE = 2;
 
     // No options yet, reserved for future use.
@@ -829,24 +830,28 @@
             buffer[index++] = (byte) (0xFF & (VERSION_2_MAGIC_NUMBER >> 16));
             buffer[index++] = (byte) (0xFF & (VERSION_2_MAGIC_NUMBER >> 8));
             buffer[index++] = (byte) (0xFF & VERSION_2_MAGIC_NUMBER);
+            // Dictionary version.
+            buffer[index++] = (byte) (0xFF & (version >> 8));
+            buffer[index++] = (byte) (0xFF & version);
         } else {
             // Magic number for version 1.
             buffer[index++] = (byte) (0xFF & (VERSION_1_MAGIC_NUMBER >> 8));
             buffer[index++] = (byte) (0xFF & VERSION_1_MAGIC_NUMBER);
+            // Dictionary version.
+            buffer[index++] = (byte) (0xFF & version);
         }
-        // Dictionary version.
-        buffer[index++] = (byte) (0xFF & version);
         // Options flags
         buffer[index++] = (byte) (0xFF & (OPTIONS >> 8));
         buffer[index++] = (byte) (0xFF & OPTIONS);
         if (version >= FIRST_VERSION_WITH_HEADER_SIZE) {
             final int headerSizeOffset = index;
-            index += 3; // Size of the header size
+            index += 4; // Size of the header size
             // TODO: Write out the header contents here.
             // Write out the header size.
-            buffer[headerSizeOffset] = (byte) (0xFF & (index >> 16));
-            buffer[headerSizeOffset + 1] = (byte) (0xFF & (index >> 8));
-            buffer[headerSizeOffset + 2] = (byte) (0xFF & (index >> 0));
+            buffer[headerSizeOffset] = (byte) (0xFF & (index >> 24));
+            buffer[headerSizeOffset + 1] = (byte) (0xFF & (index >> 16));
+            buffer[headerSizeOffset + 2] = (byte) (0xFF & (index >> 8));
+            buffer[headerSizeOffset + 3] = (byte) (0xFF & (index >> 0));
         }
 
         destination.write(buffer, 0, index);
@@ -1122,14 +1127,14 @@
     }
 
     /**
-     * Helper function to test the magic number of the file.
+     * Helper function to get the binary format version from the header.
      */
-    private static boolean isExpectedMagicNumber(final RandomAccessFile source) throws IOException {
+    private static int getFormatVersion(final RandomAccessFile source) throws IOException {
         final int magic_v1 = source.readUnsignedShort();
-        if (VERSION_1_MAGIC_NUMBER == magic_v1) return true;
+        if (VERSION_1_MAGIC_NUMBER == magic_v1) return source.readUnsignedByte();
         final int magic_v2 = (magic_v1 << 16) + source.readUnsignedShort();
-        if (VERSION_2_MAGIC_NUMBER == magic_v2) return true;
-        return false;
+        if (VERSION_2_MAGIC_NUMBER == magic_v2) return source.readUnsignedShort();
+        return NOT_A_VERSION_NUMBER;
     }
 
     /**
@@ -1145,15 +1150,9 @@
      */
     public static FusionDictionary readDictionaryBinary(final RandomAccessFile source,
             final FusionDictionary dict) throws IOException, UnsupportedFormatException {
-        // Check magic number
-        if (!isExpectedMagicNumber(source)) {
-            throw new UnsupportedFormatException("The magic number in this file does not match "
-                    + "the expected value");
-        }
-
         // Check file version
-        final int version = source.readUnsignedByte();
-        if (version > MAXIMUM_SUPPORTED_VERSION) {
+        final int version = getFormatVersion(source);
+        if (version < MINIMUM_SUPPORTED_VERSION || version > MAXIMUM_SUPPORTED_VERSION ) {
             throw new UnsupportedFormatException("This file has version " + version
                     + ", but this implementation does not support versions above "
                     + MAXIMUM_SUPPORTED_VERSION);
@@ -1166,8 +1165,8 @@
         if (version < FIRST_VERSION_WITH_HEADER_SIZE) {
             headerSize = source.getFilePointer();
         } else {
-            headerSize = (source.readUnsignedByte() << 16) + (source.readUnsignedByte() << 8)
-                    + source.readUnsignedByte();
+            headerSize = (source.readUnsignedByte() << 24) + (source.readUnsignedByte() << 16)
+                    + (source.readUnsignedByte() << 8) + source.readUnsignedByte();
             // read the header body
             source.seek(headerSize);
         }
@@ -1198,7 +1197,8 @@
     public static boolean isBinaryDictionary(final String filename) {
         try {
             RandomAccessFile f = new RandomAccessFile(filename, "r");
-            return isExpectedMagicNumber(f);
+            final int version = getFormatVersion(f);
+            return (version >= MINIMUM_SUPPORTED_VERSION && version <= MAXIMUM_SUPPORTED_VERSION);
         } catch (FileNotFoundException e) {
             return false;
         } catch (IOException e) {