1fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard/* 2fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard * Copyright (C) 2013 The Android Open Source Project 3fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard * 48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License. 68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at 7fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard * 88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 9fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard * 10fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard * Unless required by applicable law or agreed to in writing, software 118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and 148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License. 15fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard */ 16fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard 17a7d2fc6befa1b16883200a653fc01deb4d94944dKen Wakasapackage com.android.inputmethod.latin.debug; 18fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard 19fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalardimport android.app.AlertDialog; 20fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalardimport android.content.Context; 21fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalardimport android.content.DialogInterface; 22bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataokaimport android.content.DialogInterface.OnCancelListener; 23fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalardimport android.content.DialogInterface.OnClickListener; 24fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalardimport android.os.Environment; 25fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard 26a7d2fc6befa1b16883200a653fc01deb4d94944dKen Wakasaimport com.android.inputmethod.latin.BinaryDictionaryFileDumper; 27a7d2fc6befa1b16883200a653fc01deb4d94944dKen Wakasaimport com.android.inputmethod.latin.BinaryDictionaryGetter; 28a7d2fc6befa1b16883200a653fc01deb4d94944dKen Wakasaimport com.android.inputmethod.latin.R; 29fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalardimport com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; 30e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CollectionUtils; 31e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.DictionaryInfoUtils; 32e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.LocaleUtils; 33fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard 34b56260b8917233a902c8cce45364850afc0b99b2Jean Chalardimport java.io.BufferedInputStream; 35b56260b8917233a902c8cce45364850afc0b99b2Jean Chalardimport java.io.BufferedOutputStream; 36fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalardimport java.io.File; 37b56260b8917233a902c8cce45364850afc0b99b2Jean Chalardimport java.io.FileInputStream; 38b56260b8917233a902c8cce45364850afc0b99b2Jean Chalardimport java.io.FileOutputStream; 39fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalardimport java.io.IOException; 40fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalardimport java.util.ArrayList; 41fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalardimport java.util.Locale; 42fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard 43fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard/** 44fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard * A class to read a local file as a dictionary for debugging purposes. 45fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard */ 46fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalardpublic class ExternalDictionaryGetterForDebug { 47fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard private static final String SOURCE_FOLDER = Environment.getExternalStorageDirectory().getPath() 48fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard + "/Download"; 49fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard 50fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard private static String[] findDictionariesInTheDownloadedFolder() { 51fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard final File[] files = new File(SOURCE_FOLDER).listFiles(); 52fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard final ArrayList<String> eligibleList = CollectionUtils.newArrayList(); 53fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard for (File f : files) { 54d515f134f726c432c0bab5600e7b31ed989fb1b5Jean Chalard final FileHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(f); 55fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard if (null == header) continue; 56fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard eligibleList.add(f.getName()); 57fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 58fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard return eligibleList.toArray(new String[0]); 59fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 60fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard 61fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard public static void chooseAndInstallDictionary(final Context context) { 62fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard final String[] fileNames = findDictionariesInTheDownloadedFolder(); 63fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard if (0 == fileNames.length) { 64fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard showNoFileDialog(context); 65fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } else if (1 == fileNames.length) { 66bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka askInstallFile(context, SOURCE_FOLDER, fileNames[0], null /* completeRunnable */); 67fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } else { 68fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard showChooseFileDialog(context, fileNames); 69fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 70fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 71fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard 72fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard private static void showNoFileDialog(final Context context) { 73fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard new AlertDialog.Builder(context) 74fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard .setMessage(R.string.read_external_dictionary_no_files_message) 75fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard .setPositiveButton(android.R.string.ok, new OnClickListener() { 76fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard @Override 77fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard public void onClick(final DialogInterface dialog, final int which) { 78fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard dialog.dismiss(); 79fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 80fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard }).create().show(); 81fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 82fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard 83fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard private static void showChooseFileDialog(final Context context, final String[] fileNames) { 84fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard final AlertDialog.Builder builder = new AlertDialog.Builder(context); 85fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard builder.setTitle(R.string.read_external_dictionary_multiple_files_title) 86fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard .setItems(fileNames, new OnClickListener() { 87fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard @Override 88fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard public void onClick(final DialogInterface dialog, final int which) { 89bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka askInstallFile(context, SOURCE_FOLDER, fileNames[which], 90bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka null /* completeRunnable */); 91fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 92fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard }) 93fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard .create().show(); 94fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 95fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard 96bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka /** 97bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka * Shows a dialog which offers the user to install the external dictionary. 98bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka */ 99bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka public static void askInstallFile(final Context context, final String dirPath, 100bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka final String fileName, final Runnable completeRunnable) { 101bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka final File file = new File(dirPath, fileName.toString()); 102d515f134f726c432c0bab5600e7b31ed989fb1b5Jean Chalard final FileHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file); 103fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard final StringBuilder message = new StringBuilder(); 104af4a7e8c4b2a41e9be48965133ab489cc9484764Jean Chalard final String locale = header.getLocaleString(); 105fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard for (String key : header.mDictionaryOptions.mAttributes.keySet()) { 106fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard message.append(key + " = " + header.mDictionaryOptions.mAttributes.get(key)); 107fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard message.append("\n"); 108fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 109fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard final String languageName = LocaleUtils.constructLocaleFromString(locale) 110fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard .getDisplayName(Locale.getDefault()); 111fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard final String title = String.format( 112fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard context.getString(R.string.read_external_dictionary_confirm_install_message), 113fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard languageName); 114fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard new AlertDialog.Builder(context) 115fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard .setTitle(title) 116fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard .setMessage(message) 117fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard .setNegativeButton(android.R.string.cancel, new OnClickListener() { 118fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard @Override 119fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard public void onClick(final DialogInterface dialog, final int which) { 120fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard dialog.dismiss(); 121bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka if (completeRunnable != null) { 122bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka completeRunnable.run(); 123bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka } 124fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 125fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard }).setPositiveButton(android.R.string.ok, new OnClickListener() { 126fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard @Override 127fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard public void onClick(final DialogInterface dialog, final int which) { 128b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard installFile(context, file, header); 129fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard dialog.dismiss(); 130bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka if (completeRunnable != null) { 131bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka completeRunnable.run(); 132bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka } 133bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka } 134bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka }).setOnCancelListener(new OnCancelListener() { 135bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka @Override 136bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka public void onCancel(DialogInterface dialog) { 137bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka // Canceled by the user by hitting the back key 138bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka if (completeRunnable != null) { 139bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka completeRunnable.run(); 140bd84a00fc140d2d02b8eafbf6c94c009da62c9cdSatoshi Kataoka } 141fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 142fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard }).create().show(); 143fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 144fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard 145b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard private static void installFile(final Context context, final File file, 146b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard final FileHeader header) { 147b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard BufferedOutputStream outputStream = null; 148b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard File tempFile = null; 149b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard try { 150af4a7e8c4b2a41e9be48965133ab489cc9484764Jean Chalard final String locale = header.getLocaleString(); 151b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard // Create the id for a main dictionary for this locale 152b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard final String id = BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY 153b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard + BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + locale; 154af4a7e8c4b2a41e9be48965133ab489cc9484764Jean Chalard final String finalFileName = DictionaryInfoUtils.getCacheFileName(id, locale, context); 155b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard final String tempFileName = BinaryDictionaryGetter.getTempFileName(id, context); 156b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard tempFile = new File(tempFileName); 157b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard tempFile.delete(); 158b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard outputStream = new BufferedOutputStream(new FileOutputStream(tempFile)); 159b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard final BufferedInputStream bufferedStream = new BufferedInputStream( 160b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard new FileInputStream(file)); 161b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard BinaryDictionaryFileDumper.checkMagicAndCopyFileTo(bufferedStream, outputStream); 162b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard outputStream.flush(); 163b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard final File finalFile = new File(finalFileName); 164b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard finalFile.delete(); 165b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard if (!tempFile.renameTo(finalFile)) { 166b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard throw new IOException("Can't move the file to its final name"); 167b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard } 168b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard } catch (IOException e) { 169b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard // There was an error: show a dialog 170b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard new AlertDialog.Builder(context) 171b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard .setTitle(R.string.error) 172b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard .setMessage(e.toString()) 173b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard .setPositiveButton(android.R.string.ok, new OnClickListener() { 174b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard @Override 175b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard public void onClick(final DialogInterface dialog, final int which) { 176b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard dialog.dismiss(); 177b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard } 178b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard }).create().show(); 179b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard return; 180b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard } finally { 181b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard try { 182b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard if (null != outputStream) outputStream.close(); 183b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard if (null != tempFile) tempFile.delete(); 184b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard } catch (IOException e) { 185b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard // Don't do anything 186b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard } 187b56260b8917233a902c8cce45364850afc0b99b2Jean Chalard } 188fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard } 189fbc5e9b33469f989a745003c8e242c3f002ed165Jean Chalard} 190