X-Git-Url: http://gitweb.fperrin.net/?a=blobdiff_plain;f=src%2Fcom%2Fhughes%2Fandroid%2Fdictionary%2FDictionaryApplication.java;h=b47fb49a62ca5694a263720a60c49be051a0faa7;hb=b50c9fe7fc55463a8ea69a66908410e6adc4219f;hp=d7109a062cc39d8376c155ce939e9f8439ecd839;hpb=d80531dd96b68d3c05c3825bbe732275b0f9fe02;p=Dictionary.git diff --git a/src/com/hughes/android/dictionary/DictionaryApplication.java b/src/com/hughes/android/dictionary/DictionaryApplication.java index d7109a0..b47fb49 100644 --- a/src/com/hughes/android/dictionary/DictionaryApplication.java +++ b/src/com/hughes/android/dictionary/DictionaryApplication.java @@ -14,32 +14,106 @@ package com.hughes.android.dictionary; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + import android.app.Application; +import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.net.Uri; import android.preference.PreferenceManager; import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import com.hughes.android.dictionary.engine.Dictionary; +import com.hughes.android.dictionary.engine.Language; import com.hughes.android.dictionary.engine.TransliteratorManager; +import com.hughes.android.util.PersistentObjectCache; +import com.hughes.util.ListUtil; +import com.ibm.icu.text.Collator; public class DictionaryApplication extends Application { + static final String LOG = "QuickDicApp"; + + // Static, determined by resources (and locale). + // Unordered. + static Map DOWNLOADABLE_NAME_TO_INFO = null; + + static final class DictionaryConfig implements Serializable { + private static final long serialVersionUID = -1444177164708201263L; + // User-ordered list, persisted, just the ones that are/have been present. + final List dictionaryFilesOrdered = new ArrayList(); + final Map dictionaryInfoCache = new LinkedHashMap(); + } + DictionaryConfig dictionaryConfig = null; + + static final class DictionaryHistory implements Serializable { + private static final long serialVersionUID = -4842995032541390284L; + // User-ordered list, persisted, just the ones that are/have been present. + final List dictionaryLinks = new ArrayList(); + } + DictionaryHistory dictionaryHistory = null; + + private File dictDir; + + static synchronized void staticInit(final Context context) { + if (DOWNLOADABLE_NAME_TO_INFO != null) { + return; + } + DOWNLOADABLE_NAME_TO_INFO = new LinkedHashMap(); + final BufferedReader reader = new BufferedReader(new InputStreamReader(context.getResources().openRawResource(R.raw.dictionary_info))); + try { + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith("#") || line.length() == 0) { + continue; + } + final DictionaryInfo dictionaryInfo = new DictionaryInfo(line); + DOWNLOADABLE_NAME_TO_INFO.put(dictionaryInfo.uncompressedFilename, dictionaryInfo); + } + reader.close(); + } catch (IOException e) { + Log.e(LOG, "Failed to load downloadable dictionary lists.", e); + } + } + + @Override public void onCreate() { super.onCreate(); Log.d("QuickDic", "Application: onCreate"); TransliteratorManager.init(null); + staticInit(getApplicationContext()); - setTheme(getSelectedTheme().themeId); - + // Load the dictionaries we know about. + dictionaryConfig = PersistentObjectCache.init(getApplicationContext()).read(C.DICTIONARY_CONFIGS, DictionaryConfig.class); + if (dictionaryConfig == null) { + dictionaryConfig = new DictionaryConfig(); + } + // Theme stuff. + setTheme(getSelectedTheme().themeId); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); prefs.registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - Log.d("THAD", "prefs changed: " + key); - + Log.d("QuickDic", "prefs changed: " + key); if (key.equals(getString(R.string.themeKey))) { setTheme(getSelectedTheme().themeId); } @@ -47,6 +121,60 @@ public class DictionaryApplication extends Application { }); } + public void onCreateGlobalOptionsMenu( + final Context context, final Menu menu) { + final MenuItem about = menu.add(getString(R.string.about)); + about.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(final MenuItem menuItem) { + final Intent intent = new Intent().setClassName(AboutActivity.class + .getPackage().getName(), AboutActivity.class.getCanonicalName()); + context.startActivity(intent); + return false; + } + }); + + final MenuItem help = menu.add(getString(R.string.help)); + help.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(final MenuItem menuItem) { + context.startActivity(HtmlDisplayActivity.getHelpLaunchIntent()); + return false; + } + }); + + final MenuItem preferences = menu.add(getString(R.string.preferences)); + preferences.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(final MenuItem menuItem) { + PreferenceActivity.prefsMightHaveChanged = true; + final Intent intent = new Intent().setClassName(PreferenceActivity.class + .getPackage().getName(), PreferenceActivity.class.getCanonicalName()); + context.startActivity(intent); + return false; + } + }); + + + final MenuItem reportIssue = menu.add(getString(R.string.reportIssue)); + reportIssue.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(final MenuItem menuItem) { + final Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("http://code.google.com/p/quickdic-dictionary/issues/entry")); + context.startActivity(intent); + return false; + } + }); + } + + public synchronized File getDictDir() { + // This metaphore doesn't work, because we've already reset prefsMightHaveChanged. +// if (dictDir == null || PreferenceActivity.prefsMightHaveChanged) { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + final String dir = prefs.getString(getString(R.string.quickdicDirectoryKey), getString(R.string.quickdicDirectoryDefault)); + dictDir = new File(dir); + dictDir.mkdirs(); +// } + return dictDir; + } + public C.Theme getSelectedTheme() { final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); final String theme = prefs.getString(getString(R.string.themeKey), "themeLight"); @@ -56,4 +184,206 @@ public class DictionaryApplication extends Application { return C.Theme.DEFAULT; } } + + public File getPath(String uncompressedFilename) { + return new File(getDictDir(), uncompressedFilename); + } + + + + String defaultLangISO2 = Locale.getDefault().getLanguage().toLowerCase(); + String defaultLangName = null; + final Map fileToNameCache = new LinkedHashMap(); + + public String getLanguageName(final String isoCode) { + final Language.LanguageResources languageResources = Language.isoCodeToResources.get(isoCode); + final String lang = languageResources != null ? getApplicationContext().getString(languageResources.nameId) : isoCode; + return lang; + } + + + public synchronized String getDictionaryName(final String uncompressedFilename) { + final String currentLocale = Locale.getDefault().getLanguage().toLowerCase(); + if (!currentLocale.equals(defaultLangISO2)) { + defaultLangISO2 = currentLocale; + fileToNameCache.clear(); + defaultLangName = null; + } + if (defaultLangName == null) { + defaultLangName = getLanguageName(defaultLangISO2); + } + + String name = fileToNameCache.get(uncompressedFilename); + if (name != null) { + return name; + } + + final DictionaryInfo dictionaryInfo = DOWNLOADABLE_NAME_TO_INFO.get(uncompressedFilename); + if (dictionaryInfo != null) { + final StringBuilder nameBuilder = new StringBuilder(); + + // Hack to put the default locale first in the name. + boolean swapped = false; + if (dictionaryInfo.indexInfos.size() > 1 && + dictionaryInfo.indexInfos.get(1).shortName.toLowerCase().equals(defaultLangISO2)) { + ListUtil.swap(dictionaryInfo.indexInfos, 0, 1); + swapped = true; + } + for (int i = 0; i < dictionaryInfo.indexInfos.size(); ++i) { + if (i > 0) { + nameBuilder.append("-"); + } + nameBuilder.append(getLanguageName(dictionaryInfo.indexInfos.get(i).shortName)); + } + if (swapped) { + ListUtil.swap(dictionaryInfo.indexInfos, 0, 1); + } + name = nameBuilder.toString(); + } else { + name = uncompressedFilename.replace(".quickdic", ""); + } + fileToNameCache.put(uncompressedFilename, name); + return name; + } + + public synchronized void moveDictionaryToTop(final DictionaryInfo dictionaryInfo) { + dictionaryConfig.dictionaryFilesOrdered.remove(dictionaryInfo.uncompressedFilename); + dictionaryConfig.dictionaryFilesOrdered.add(0, dictionaryInfo.uncompressedFilename); + PersistentObjectCache.getInstance().write(C.DICTIONARY_CONFIGS, dictionaryConfig); + } + + public synchronized void deleteDictionary(final DictionaryInfo dictionaryInfo) { + while (dictionaryConfig.dictionaryFilesOrdered.remove(dictionaryInfo.uncompressedFilename)) {}; + dictionaryConfig.dictionaryInfoCache.remove(dictionaryInfo.uncompressedFilename); + getPath(dictionaryInfo.uncompressedFilename).delete(); + PersistentObjectCache.getInstance().write(C.DICTIONARY_CONFIGS, dictionaryConfig); + } + + final Collator collator = Collator.getInstance(); + final Comparator uncompressedFilenameComparator = new Comparator() { + @Override + public int compare(String uncompressedFilename1, String uncompressedFilename2) { + final String name1 = getDictionaryName(uncompressedFilename1); + final String name2 = getDictionaryName(uncompressedFilename2); + if (defaultLangName.length() > 0) { + if (name1.startsWith(defaultLangName) && !name2.startsWith(defaultLangName)) { + return -1; + } else if (name2.startsWith(defaultLangName) && !name1.startsWith(defaultLangName)) { + return 1; + } + } + return collator.compare(name1, name2); + } + }; + final Comparator dictionaryInfoComparator = new Comparator() { + @Override + public int compare(DictionaryInfo d1, DictionaryInfo d2) { + return uncompressedFilenameComparator.compare(d1.uncompressedFilename, d2.uncompressedFilename); + } + }; + + public void backgroundUpdateDictionaries(final Runnable onUpdateFinished) { + new Thread(new Runnable() { + @Override + public void run() { + final DictionaryConfig oldDictionaryConfig = new DictionaryConfig(); + synchronized(this) { + oldDictionaryConfig.dictionaryFilesOrdered.addAll(dictionaryConfig.dictionaryFilesOrdered); + } + final DictionaryConfig newDictionaryConfig = new DictionaryConfig(); + for (final String uncompressedFilename : oldDictionaryConfig.dictionaryFilesOrdered) { + final File dictFile = getPath(uncompressedFilename); + final DictionaryInfo dictionaryInfo = Dictionary.getDictionaryInfo(dictFile); + if (dictionaryInfo != null) { + newDictionaryConfig.dictionaryFilesOrdered.add(uncompressedFilename); + newDictionaryConfig.dictionaryInfoCache.put(uncompressedFilename, dictionaryInfo); + } + } + + // Are there dictionaries on the device that we didn't know about already? + // Pick them up and put them at the end of the list. + final List toAddSorted = new ArrayList(); + final File[] dictDirFiles = getDictDir().listFiles(); + if (dictDirFiles != null) { + for (final File file : dictDirFiles) { + if (file.getName().endsWith(".zip")) { + if (DOWNLOADABLE_NAME_TO_INFO.containsKey(file.getName().replace(".zip", ""))) { + file.delete(); + } + } + if (!file.getName().endsWith(".quickdic")) { + continue; + } + if (newDictionaryConfig.dictionaryInfoCache.containsKey(file.getName())) { + // We have it in our list already. + continue; + } + final DictionaryInfo dictionaryInfo = Dictionary.getDictionaryInfo(file); + if (dictionaryInfo == null) { + Log.e(LOG, "Unable to parse dictionary: " + file.getPath()); + continue; + } + + toAddSorted.add(file.getName()); + newDictionaryConfig.dictionaryInfoCache.put(file.getName(), dictionaryInfo); + } + } else { + Log.w(LOG, "dictDir is not a diretory: " + getDictDir().getPath()); + } + if (!toAddSorted.isEmpty()) { + Collections.sort(toAddSorted, uncompressedFilenameComparator); + newDictionaryConfig.dictionaryFilesOrdered.addAll(toAddSorted); + } + + PersistentObjectCache.getInstance().write(C.DICTIONARY_CONFIGS, newDictionaryConfig); + synchronized (this) { + dictionaryConfig = newDictionaryConfig; + } + + try { + onUpdateFinished.run(); + } catch (Exception e) { + Log.e(LOG, "Exception running callback.", e); + } + }}).start(); + } + + public synchronized List getUsableDicts() { + final List result = new ArrayList(dictionaryConfig.dictionaryFilesOrdered.size()); + for (final String uncompressedFilename : dictionaryConfig.dictionaryFilesOrdered) { + final DictionaryInfo dictionaryInfo = dictionaryConfig.dictionaryInfoCache.get(uncompressedFilename); + if (dictionaryInfo != null) { + result.add(dictionaryInfo); + } + } + return result; + } + + public synchronized List getAllDictionaries() { + final List result = getUsableDicts(); + + // The downloadable ones. + final Map remaining = new LinkedHashMap(DOWNLOADABLE_NAME_TO_INFO); + remaining.keySet().removeAll(dictionaryConfig.dictionaryFilesOrdered); + final List toAddSorted = new ArrayList(remaining.values()); + Collections.sort(toAddSorted, dictionaryInfoComparator); + result.addAll(toAddSorted); + + return result; + } + + public synchronized boolean isDictionaryOnDevice(String uncompressedFilename) { + return dictionaryConfig.dictionaryInfoCache.get(uncompressedFilename) != null; + } + + public boolean updateAvailable(final DictionaryInfo dictionaryInfo) { + final DictionaryInfo downloadable = DOWNLOADABLE_NAME_TO_INFO.get(dictionaryInfo.uncompressedFilename); + return downloadable != null && downloadable.creationMillis > dictionaryInfo.creationMillis; + } + + public DictionaryInfo getDownloadable(final String uncompressedFilename) { + final DictionaryInfo downloadable = DOWNLOADABLE_NAME_TO_INFO.get(uncompressedFilename); + return downloadable; + } + }