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