Merge "Add PointerTracker.isOldestPointerInQueue"
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 12ffdbb..54ff23b 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -383,6 +383,8 @@
     <string name="read_external_dictionary_multiple_files_title">Select a dictionary file to install</string>
     <!-- Title of the confirmation dialog to install a file as an external dictionary [CHAR LIMIT=50] -->
     <string name="read_external_dictionary_confirm_install_message">Really install this file for <xliff:g id="locale_name">%s</xliff:g>?</string>
+    <!-- Title for an error dialog that contains the details of the error in the body [CHAR LIMIT=80] -->
+    <string name="error">There was an error</string>
 
     <!-- Title of the button to revert to the default value of the device in the settings dialog [CHAR LIMIT=15] -->
     <string name="button_default">Default</string>
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 5eab292..d725b9f 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -25,6 +25,7 @@
 import android.util.Log;
 
 import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -162,9 +163,9 @@
             InputStream inputStream = null;
             InputStream uncompressedStream = null;
             InputStream decryptedStream = null;
-            BufferedInputStream bufferedStream = null;
+            BufferedInputStream bufferedInputStream = null;
             File outputFile = null;
-            FileOutputStream outputStream = null;
+            BufferedOutputStream bufferedOutputStream = null;
             AssetFileDescriptor afd = null;
             final Uri wordListUri = wordListUriBuilder.build();
             try {
@@ -178,7 +179,6 @@
                 // Just to be sure, delete the file. This may fail silently, and return false: this
                 // is the right thing to do, as we just want to continue anyway.
                 outputFile.delete();
-                outputStream = new FileOutputStream(outputFile);
                 // Get the appropriate decryption method for this try
                 switch (mode) {
                     case COMPRESSED_CRYPTED_COMPRESSED:
@@ -206,10 +206,11 @@
                         inputStream = originalSourceStream;
                         break;
                 }
-                bufferedStream = new BufferedInputStream(inputStream);
-                checkMagicAndCopyFileTo(bufferedStream, outputStream);
-                outputStream.flush();
-                outputStream.close();
+                bufferedInputStream = new BufferedInputStream(inputStream);
+                bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(outputFile));
+                checkMagicAndCopyFileTo(bufferedInputStream, bufferedOutputStream);
+                bufferedOutputStream.flush();
+                bufferedOutputStream.close();
                 final File finalFile = new File(finalFileName);
                 finalFile.delete();
                 if (!outputFile.renameTo(finalFile)) {
@@ -241,12 +242,12 @@
                     if (null != inputStream) inputStream.close();
                     if (null != uncompressedStream) uncompressedStream.close();
                     if (null != decryptedStream) decryptedStream.close();
-                    if (null != bufferedStream) bufferedStream.close();
+                    if (null != bufferedInputStream) bufferedInputStream.close();
                 } catch (Exception e) {
                     Log.e(TAG, "Exception while closing a file descriptor : " + e);
                 }
                 try {
-                    if (null != outputStream) outputStream.close();
+                    if (null != bufferedOutputStream) bufferedOutputStream.close();
                 } catch (Exception e) {
                     Log.e(TAG, "Exception while closing a file : " + e);
                 }
@@ -301,9 +302,8 @@
      * @param input the stream to be copied.
      * @param output an output stream to copy the data to.
      */
-    // TODO: make output a BufferedOutputStream
-    private static void checkMagicAndCopyFileTo(final BufferedInputStream input,
-            final FileOutputStream output) throws FileNotFoundException, IOException {
+    public static void checkMagicAndCopyFileTo(final BufferedInputStream input,
+            final BufferedOutputStream output) throws FileNotFoundException, IOException {
         // Check the magic number
         final int length = MAGIC_NUMBER_VERSION_2.length;
         final byte[] magicNumberBuffer = new byte[length];
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 83dabbe..ecb63e8 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -56,7 +56,7 @@
     private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs";
 
     // Name of the category for the main dictionary
-    private static final String MAIN_DICTIONARY_CATEGORY = "main";
+    public static final String MAIN_DICTIONARY_CATEGORY = "main";
     public static final String ID_CATEGORY_SEPARATOR = ":";
 
     // The key considered to read the version attribute in a dictionary file.
diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java
index 905852a..ad52adf 100644
--- a/java/src/com/android/inputmethod/latin/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/DebugSettings.java
@@ -77,6 +77,7 @@
                         public boolean onPreferenceClick(final Preference arg0) {
                             ExternalDictionaryGetterForDebug.chooseAndInstallDictionary(
                                     getActivity());
+                            mServiceNeedsRestart = true;
                             return true;
                         }
                     });
diff --git a/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java
index 5f91d03..03e8763 100644
--- a/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java
+++ b/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java
@@ -21,12 +21,17 @@
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.os.Environment;
+import android.util.Log;
 
 import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
 import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Locale;
@@ -121,13 +126,56 @@
                 }).setPositiveButton(android.R.string.ok, new OnClickListener() {
                     @Override
                     public void onClick(final DialogInterface dialog, final int which) {
-                        installFile(file, header);
+                        installFile(context, file, header);
                         dialog.dismiss();
                     }
                 }).create().show();
     }
 
-    private static void installFile(final File file, final FileHeader header) {
-        // TODO: actually install the dictionary
+    private static void installFile(final Context context, final File file,
+            final FileHeader header) {
+        BufferedOutputStream outputStream = null;
+        File tempFile = null;
+        try {
+            final String locale =
+                    header.mDictionaryOptions.mAttributes.get(DICTIONARY_LOCALE_ATTRIBUTE);
+            // Create the id for a main dictionary for this locale
+            final String id = BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY
+                    + BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + locale;
+            final String finalFileName =
+                    BinaryDictionaryGetter.getCacheFileName(id, locale, context);
+            final String tempFileName = BinaryDictionaryGetter.getTempFileName(id, context);
+            tempFile = new File(tempFileName);
+            tempFile.delete();
+            outputStream = new BufferedOutputStream(new FileOutputStream(tempFile));
+            final BufferedInputStream bufferedStream = new BufferedInputStream(
+                    new FileInputStream(file));
+            BinaryDictionaryFileDumper.checkMagicAndCopyFileTo(bufferedStream, outputStream);
+            outputStream.flush();
+            final File finalFile = new File(finalFileName);
+            finalFile.delete();
+            if (!tempFile.renameTo(finalFile)) {
+                throw new IOException("Can't move the file to its final name");
+            }
+        } catch (IOException e) {
+            // There was an error: show a dialog
+            new AlertDialog.Builder(context)
+                    .setTitle(R.string.error)
+                    .setMessage(e.toString())
+                    .setPositiveButton(android.R.string.ok, new OnClickListener() {
+                        @Override
+                        public void onClick(final DialogInterface dialog, final int which) {
+                            dialog.dismiss();
+                        }
+                    }).create().show();
+            return;
+        } finally {
+            try {
+                if (null != outputStream) outputStream.close();
+                if (null != tempFile) tempFile.delete();
+            } catch (IOException e) {
+                // Don't do anything
+            }
+        }
     }
 }