]> gitweb.fperrin.net Git - Dictionary.git/blobdiff - src/com/hughes/android/dictionary/DictionaryApplication.java
Lots of bug fixes! Workaround for ICS OOM, background loading of which
[Dictionary.git] / src / com / hughes / android / dictionary / DictionaryApplication.java
index fc80df6d854a5d6d74f51003ded40933906bb2ad..d36e99d718cdbfe77f7438b622668b6d89384132 100644 (file)
@@ -23,17 +23,14 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import android.app.Application;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.os.Environment;
 import android.preference.PreferenceManager;
 import android.util.Log;
 import android.view.Menu;
@@ -50,17 +47,15 @@ public class DictionaryApplication extends Application {
   
   static final String LOG = "QuickDicApp";
   
-  private static final File DICT_DIR = new File(Environment.getExternalStorageDirectory().getName(), "quickdic");
-
   // Static, determined by resources (and locale).
   // Unordered.
   static Map<String,DictionaryInfo> DOWNLOADABLE_NAME_TO_INFO = null;
   
   static final class DictionaryConfig implements Serializable {
-    private static final long serialVersionUID = -1444177164708201262L;
+    private static final long serialVersionUID = -1444177164708201263L;
     // User-ordered list, persisted, just the ones that are/have been present.
-    final List<DictionaryInfo> dictionaryFilesOrdered = new ArrayList<DictionaryInfo>();
-    final Set<String> invalidatedFilenames = new LinkedHashSet<String>();
+    final List<String> dictionaryFilesOrdered = new ArrayList<String>();
+    final Map<String, DictionaryInfo> dictionaryInfoCache = new LinkedHashMap<String, DictionaryInfo>();
   }
   DictionaryConfig dictionaryConfig = null;
 
@@ -70,6 +65,29 @@ public class DictionaryApplication extends Application {
     final List<DictionaryLink> dictionaryLinks = new ArrayList<DictionaryLink>();
   }
   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<String,DictionaryInfo>();
+    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
@@ -85,7 +103,6 @@ public class DictionaryApplication extends Application {
       dictionaryConfig = new DictionaryConfig();
     }
     
-
     // Theme stuff.
     setTheme(getSelectedTheme().themeId);
     final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
@@ -101,6 +118,49 @@ 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(HelpActivity.getLaunchIntent());
+        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;
+      }
+    });
+  }
+  
+  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");
@@ -111,60 +171,19 @@ public class DictionaryApplication extends Application {
     }
   }
     
-  static synchronized void staticInit(final Context context) {
-    if (DOWNLOADABLE_NAME_TO_INFO != null) {
-      return;
-    }
-    DOWNLOADABLE_NAME_TO_INFO = new LinkedHashMap<String,DictionaryInfo>();
-    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);
-    }
-  }
-
   public File getPath(String uncompressedFilename) {
-    return new File(DICT_DIR, uncompressedFilename);
-  }
-
-
-  public List<DictionaryInfo> getUsableDicts() {
-    final List<DictionaryInfo> result = new ArrayList<DictionaryInfo>(dictionaryConfig.dictionaryFilesOrdered.size());
-    for (int i = 0; i < dictionaryConfig.dictionaryFilesOrdered.size(); ++i) {
-      DictionaryInfo dictionaryInfo = dictionaryConfig.dictionaryFilesOrdered.get(i);
-      if (dictionaryConfig.invalidatedFilenames.contains(dictionaryInfo.uncompressedFilename)) {
-        dictionaryInfo = Dictionary.getDictionaryInfo(getPath(dictionaryInfo.uncompressedFilename));
-        if (dictionaryInfo != null) {
-          dictionaryConfig.dictionaryFilesOrdered.set(i, dictionaryInfo);
-        }
-      }
-      if (dictionaryInfo != null) {
-        result.add(dictionaryInfo);
-      }
-    }
-    if (!dictionaryConfig.invalidatedFilenames.isEmpty()) {
-      dictionaryConfig.invalidatedFilenames.clear();
-      PersistentObjectCache.getInstance().write(C.DICTIONARY_CONFIGS, dictionaryConfig);
-    }
-    return result;
+    return new File(getDictDir(), uncompressedFilename);
   }
   
+  
+  final Map<String, String> fileToNameCache = new LinkedHashMap<String, String>();
+
   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;
   }
 
-  final Map<String, String> fileToNameCache = new LinkedHashMap<String, String>();
   public synchronized String getDictionaryName(final String uncompressedFilename) {
     String name = fileToNameCache.get(uncompressedFilename);
     if (name != null) {
@@ -188,81 +207,121 @@ public class DictionaryApplication extends Application {
     return name;
   }
 
-  public void moveDictionaryToTop(final DictionaryInfo dictionaryInfo) {
-    dictionaryConfig.dictionaryFilesOrdered.remove(dictionaryInfo);
-    dictionaryConfig.dictionaryFilesOrdered.add(0, dictionaryInfo);
+  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 void deleteDictionary(final DictionaryInfo dictionaryInfo) {
-    while (dictionaryConfig.dictionaryFilesOrdered.remove(dictionaryInfo)) {};
+  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<DictionaryInfo> comparator = new Comparator<DictionaryInfo>() {
+  final Comparator<String> uncompressedFilenameComparator = new Comparator<String>() {
     @Override
-    public int compare(DictionaryInfo object1, DictionaryInfo object2) {
-      return collator.compare(getDictionaryName(object1.uncompressedFilename), getDictionaryName(object2.uncompressedFilename));
+    public int compare(String uncompressedFilename1, String uncompressedFilename2) {
+      return collator.compare(getDictionaryName(uncompressedFilename1), getDictionaryName(uncompressedFilename2));
     }
   };
-
-  public List<DictionaryInfo> getAllDictionaries() {
-    final List<DictionaryInfo> result = getUsableDicts();
-    
-    // The ones we knew about...
-    final Set<String> known = new LinkedHashSet<String>();
-    for (final DictionaryInfo usable : result) {
-      known.add(usable.uncompressedFilename);
-    }
-    if (!dictionaryConfig.invalidatedFilenames.isEmpty()) {
-      dictionaryConfig.invalidatedFilenames.clear();
+  final Comparator<DictionaryInfo> dictionaryInfoComparator = new Comparator<DictionaryInfo>() {
+    @Override
+    public int compare(DictionaryInfo d1, DictionaryInfo d2) {
+      return uncompressedFilenameComparator.compare(d1.uncompressedFilename, d2.uncompressedFilename);
     }
-    
-    // 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<DictionaryInfo> toAddSorted = new ArrayList<DictionaryInfo>();
-    final File[] dictDirFiles = DICT_DIR.listFiles();
-    for (final File file : dictDirFiles) {
-      // TODO: delete zip files here.
-      if (!file.getName().endsWith(".quickdic")) {
-        continue;
-      }
-      if (known.contains(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;
+  };
+  
+  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<String> toAddSorted = new ArrayList<String>();
+        final File[] dictDirFiles = getDictDir().listFiles();
+        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);
+        }
+        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<DictionaryInfo> getUsableDicts() {
+    final List<DictionaryInfo> result = new ArrayList<DictionaryInfo>(dictionaryConfig.dictionaryFilesOrdered.size());
+    for (final String uncompressedFilename : dictionaryConfig.dictionaryFilesOrdered) {
+      final DictionaryInfo dictionaryInfo = dictionaryConfig.dictionaryInfoCache.get(uncompressedFilename);
+      if (dictionaryInfo != null) {
+        result.add(dictionaryInfo);
       }
-      known.add(file.getName());
-      toAddSorted.add(dictionaryInfo);
-    }
-    if (!toAddSorted.isEmpty()) {
-      Collections.sort(toAddSorted, comparator);
-      result.addAll(toAddSorted);
-//      for (final DictionaryInfo dictionaryInfo : toAddSorted) {
-//        dictionaryConfig.dictionaryFilesOrdered.add(dictionaryInfo.uncompressedFilename);
-//      }
-      dictionaryConfig.dictionaryFilesOrdered.addAll(toAddSorted);
-      PersistentObjectCache.getInstance().write(C.DICTIONARY_CONFIGS, dictionaryConfig);
     }
+    return result;
+  }
 
+  public synchronized List<DictionaryInfo> getAllDictionaries() {
+    final List<DictionaryInfo> result = getUsableDicts();
+    
     // The downloadable ones.
     final Map<String,DictionaryInfo> remaining = new LinkedHashMap<String, DictionaryInfo>(DOWNLOADABLE_NAME_TO_INFO);
-    remaining.keySet().removeAll(known);
-    toAddSorted.clear();
-    toAddSorted.addAll(remaining.values());
-    Collections.sort(toAddSorted, comparator);
+    remaining.keySet().removeAll(dictionaryConfig.dictionaryFilesOrdered);
+    final List<DictionaryInfo> toAddSorted = new ArrayList<DictionaryInfo>(remaining.values());
+    Collections.sort(toAddSorted, dictionaryInfoComparator);
     result.addAll(toAddSorted);
+    
     return result;
   }
 
-  public boolean isDictionaryOnDevice(String uncompressedFilename) {
-    return getPath(uncompressedFilename).canRead();
+  public synchronized boolean isDictionaryOnDevice(String uncompressedFilename) {
+    return dictionaryConfig.dictionaryInfoCache.get(uncompressedFilename) != null;
   }
 
   public boolean updateAvailable(final DictionaryInfo dictionaryInfo) {
@@ -275,41 +334,4 @@ public class DictionaryApplication extends Application {
     return downloadable;
   }
 
-  public void invalidateDictionaryInfo(final String uncompressedFilename) {
-    dictionaryConfig.invalidatedFilenames.add(uncompressedFilename);
-    PersistentObjectCache.getInstance().write(C.DICTIONARY_CONFIGS, dictionaryConfig);
-  }
-
-  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(HelpActivity.getLaunchIntent());
-        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;
-      }
-    });
-  }
-  
 }