]> gitweb.fperrin.net Git - Dictionary.git/blob - src/com/hughes/android/dictionary/DictionaryApplication.java
Added filtering to manager, remove timeout on auto-launch (and finish
[Dictionary.git] / src / com / hughes / android / dictionary / DictionaryApplication.java
1 // Copyright 2011 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package com.hughes.android.dictionary;
16
17 import java.io.BufferedReader;
18 import java.io.File;
19 import java.io.IOException;
20 import java.io.InputStreamReader;
21 import java.io.Serializable;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.Comparator;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27 import java.util.Map;
28
29 import android.app.Application;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.SharedPreferences;
33 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
34 import android.net.Uri;
35 import android.preference.PreferenceManager;
36 import android.util.Log;
37 import android.view.Menu;
38 import android.view.MenuItem;
39 import android.view.MenuItem.OnMenuItemClickListener;
40
41 import com.hughes.android.dictionary.DictionaryInfo.IndexInfo;
42 import com.hughes.android.dictionary.engine.Dictionary;
43 import com.hughes.android.dictionary.engine.Language;
44 import com.hughes.android.dictionary.engine.TransliteratorManager;
45 import com.hughes.android.util.PersistentObjectCache;
46 import com.ibm.icu.text.Collator;
47
48 public class DictionaryApplication extends Application {
49   
50   static final String LOG = "QuickDicApp";
51   
52   // Static, determined by resources (and locale).
53   // Unordered.
54   static Map<String,DictionaryInfo> DOWNLOADABLE_NAME_TO_INFO = null;
55   
56   static final class DictionaryConfig implements Serializable {
57     private static final long serialVersionUID = -1444177164708201263L;
58     // User-ordered list, persisted, just the ones that are/have been present.
59     final List<String> dictionaryFilesOrdered = new ArrayList<String>();
60     final Map<String, DictionaryInfo> dictionaryInfoCache = new LinkedHashMap<String, DictionaryInfo>();
61   }
62   DictionaryConfig dictionaryConfig = null;
63
64   static final class DictionaryHistory implements Serializable {
65     private static final long serialVersionUID = -4842995032541390284L;
66     // User-ordered list, persisted, just the ones that are/have been present.
67     final List<DictionaryLink> dictionaryLinks = new ArrayList<DictionaryLink>();
68   }
69   DictionaryHistory dictionaryHistory = null;
70   
71   private File dictDir;
72
73   static synchronized void staticInit(final Context context) {
74     if (DOWNLOADABLE_NAME_TO_INFO != null) {
75       return;
76     }
77     DOWNLOADABLE_NAME_TO_INFO = new LinkedHashMap<String,DictionaryInfo>();
78     final BufferedReader reader = new BufferedReader(new InputStreamReader(context.getResources().openRawResource(R.raw.dictionary_info)));
79     try {
80       String line;
81       while ((line = reader.readLine()) != null) {
82         if (line.startsWith("#") || line.length() == 0) {
83           continue;
84         }
85         final DictionaryInfo dictionaryInfo = new DictionaryInfo(line);
86         DOWNLOADABLE_NAME_TO_INFO.put(dictionaryInfo.uncompressedFilename, dictionaryInfo);
87       }
88       reader.close();
89     } catch (IOException e) {
90       Log.e(LOG, "Failed to load downloadable dictionary lists.", e);
91     }
92   }
93
94   
95   @Override
96   public void onCreate() {
97     super.onCreate();
98     Log.d("QuickDic", "Application: onCreate");
99     TransliteratorManager.init(null);
100     staticInit(getApplicationContext());
101     
102     // Load the dictionaries we know about.
103     dictionaryConfig = PersistentObjectCache.init(getApplicationContext()).read(C.DICTIONARY_CONFIGS, DictionaryConfig.class);
104     if (dictionaryConfig == null) {
105       dictionaryConfig = new DictionaryConfig();
106     }
107     
108     // Theme stuff.
109     setTheme(getSelectedTheme().themeId);
110     final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
111     prefs.registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener() {
112       @Override
113       public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
114           String key) {
115         Log.d("THAD", "prefs changed: " + key);
116         if (key.equals(getString(R.string.themeKey))) {
117           setTheme(getSelectedTheme().themeId);
118         }
119       }
120     });
121   }
122   
123   public void onCreateGlobalOptionsMenu(
124       final Context context, final Menu menu) {
125     final MenuItem about = menu.add(getString(R.string.about));
126     about.setOnMenuItemClickListener(new OnMenuItemClickListener() {
127       public boolean onMenuItemClick(final MenuItem menuItem) {
128         final Intent intent = new Intent().setClassName(AboutActivity.class
129             .getPackage().getName(), AboutActivity.class.getCanonicalName());
130         context.startActivity(intent);
131         return false;
132       }
133     });
134
135     final MenuItem help = menu.add(getString(R.string.help));
136     help.setOnMenuItemClickListener(new OnMenuItemClickListener() {
137       public boolean onMenuItemClick(final MenuItem menuItem) {
138         context.startActivity(HelpActivity.getLaunchIntent());
139         return false;
140       }
141     });
142
143     final MenuItem preferences = menu.add(getString(R.string.preferences));
144     preferences.setOnMenuItemClickListener(new OnMenuItemClickListener() {
145       public boolean onMenuItemClick(final MenuItem menuItem) {
146         PreferenceActivity.prefsMightHaveChanged = true;
147         final Intent intent = new Intent().setClassName(PreferenceActivity.class
148             .getPackage().getName(), PreferenceActivity.class.getCanonicalName());
149         context.startActivity(intent);
150         return false;
151       }
152     });
153     
154     
155     final MenuItem reportIssue = menu.add(getString(R.string.reportIssue));
156     reportIssue.setOnMenuItemClickListener(new OnMenuItemClickListener() {
157       public boolean onMenuItemClick(final MenuItem menuItem) {
158         final Intent intent = new Intent(Intent.ACTION_VIEW);
159         intent.setData(Uri.parse("http://code.google.com/p/quickdic-dictionary/issues/entry"));
160         context.startActivity(intent);
161         return false;
162       }
163     });
164   }
165   
166   public synchronized File getDictDir() {
167     // This metaphore doesn't work, because we've already reset prefsMightHaveChanged.
168 //    if (dictDir == null || PreferenceActivity.prefsMightHaveChanged) {
169       final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
170       final String dir = prefs.getString(getString(R.string.quickdicDirectoryKey), getString(R.string.quickdicDirectoryDefault));
171       dictDir = new File(dir);
172       dictDir.mkdirs();
173 //    }
174     return dictDir;
175   }
176   
177   public C.Theme getSelectedTheme() {
178     final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
179     final String theme = prefs.getString(getString(R.string.themeKey), "themeLight");
180     if (theme.equals("themeLight")) {
181       return C.Theme.LIGHT;
182     } else {
183       return C.Theme.DEFAULT;
184     }
185   }
186     
187   public File getPath(String uncompressedFilename) {
188     return new File(getDictDir(), uncompressedFilename);
189   }
190   
191   
192   final Map<String, String> fileToNameCache = new LinkedHashMap<String, String>();
193
194   public String getLanguageName(final String isoCode) {
195     final Language.LanguageResources languageResources = Language.isoCodeToResources.get(isoCode); 
196     final String lang = languageResources != null ? getApplicationContext().getString(languageResources.nameId) : isoCode;
197     return lang;
198   }
199
200   public synchronized String getDictionaryName(final String uncompressedFilename) {
201     String name = fileToNameCache.get(uncompressedFilename);
202     if (name != null) {
203       return name;
204     }
205     
206     final DictionaryInfo dictionaryInfo = DOWNLOADABLE_NAME_TO_INFO.get(uncompressedFilename);
207     if (dictionaryInfo != null) {
208       final StringBuilder nameBuilder = new StringBuilder();
209       for (int i = 0; i < dictionaryInfo.indexInfos.size(); ++i) {
210         if (i > 0) {
211           nameBuilder.append("-");
212         }
213         nameBuilder.append(getLanguageName(dictionaryInfo.indexInfos.get(i).shortName));
214       }
215       name = nameBuilder.toString();
216     } else {
217       name = uncompressedFilename.replace(".quickdic", "");
218     }
219     fileToNameCache.put(uncompressedFilename, name);
220     return name;
221   }
222
223   public synchronized void moveDictionaryToTop(final DictionaryInfo dictionaryInfo) {
224     dictionaryConfig.dictionaryFilesOrdered.remove(dictionaryInfo.uncompressedFilename);
225     dictionaryConfig.dictionaryFilesOrdered.add(0, dictionaryInfo.uncompressedFilename);
226     PersistentObjectCache.getInstance().write(C.DICTIONARY_CONFIGS, dictionaryConfig);
227   }
228
229   public synchronized void deleteDictionary(final DictionaryInfo dictionaryInfo) {
230     while (dictionaryConfig.dictionaryFilesOrdered.remove(dictionaryInfo.uncompressedFilename)) {};
231     dictionaryConfig.dictionaryInfoCache.remove(dictionaryInfo.uncompressedFilename);
232     getPath(dictionaryInfo.uncompressedFilename).delete();
233     PersistentObjectCache.getInstance().write(C.DICTIONARY_CONFIGS, dictionaryConfig);
234   }
235
236   final Collator collator = Collator.getInstance();
237   final Comparator<String> uncompressedFilenameComparator = new Comparator<String>() {
238     @Override
239     public int compare(String uncompressedFilename1, String uncompressedFilename2) {
240       return collator.compare(getDictionaryName(uncompressedFilename1), getDictionaryName(uncompressedFilename2));
241     }
242   };
243   final Comparator<DictionaryInfo> dictionaryInfoComparator = new Comparator<DictionaryInfo>() {
244     @Override
245     public int compare(DictionaryInfo d1, DictionaryInfo d2) {
246       return uncompressedFilenameComparator.compare(d1.uncompressedFilename, d2.uncompressedFilename);
247     }
248   };
249   
250   public void backgroundUpdateDictionaries(final Runnable onUpdateFinished) {
251     new Thread(new Runnable() {
252       @Override
253       public void run() {
254         final DictionaryConfig oldDictionaryConfig = new DictionaryConfig();
255         synchronized(this) {
256           oldDictionaryConfig.dictionaryFilesOrdered.addAll(dictionaryConfig.dictionaryFilesOrdered);
257         }
258         final DictionaryConfig newDictionaryConfig = new DictionaryConfig();
259         for (final String uncompressedFilename : oldDictionaryConfig.dictionaryFilesOrdered) {
260           final File dictFile = getPath(uncompressedFilename);
261           final DictionaryInfo dictionaryInfo = Dictionary.getDictionaryInfo(dictFile);
262           if (dictionaryInfo != null) {
263             newDictionaryConfig.dictionaryFilesOrdered.add(uncompressedFilename);
264             newDictionaryConfig.dictionaryInfoCache.put(uncompressedFilename, dictionaryInfo);
265           }
266         }
267         
268         // Are there dictionaries on the device that we didn't know about already?
269         // Pick them up and put them at the end of the list.
270         final List<String> toAddSorted = new ArrayList<String>();
271         final File[] dictDirFiles = getDictDir().listFiles();
272         if (dictDirFiles != null) {
273           for (final File file : dictDirFiles) {
274             if (file.getName().endsWith(".zip")) {
275               if (DOWNLOADABLE_NAME_TO_INFO.containsKey(file.getName().replace(".zip", ""))) {
276                 file.delete();
277               }
278             }
279             if (!file.getName().endsWith(".quickdic")) {
280               continue;
281             }
282             if (newDictionaryConfig.dictionaryInfoCache.containsKey(file.getName())) {
283               // We have it in our list already.
284               continue;
285             }
286             final DictionaryInfo dictionaryInfo = Dictionary.getDictionaryInfo(file);
287             if (dictionaryInfo == null) {
288               Log.e(LOG, "Unable to parse dictionary: " + file.getPath());
289               continue;
290             }
291             
292             toAddSorted.add(file.getName());
293             newDictionaryConfig.dictionaryInfoCache.put(file.getName(), dictionaryInfo);
294           }
295         } else {
296           Log.w(LOG, "dictDir is not a diretory: " + getDictDir().getPath());
297         }
298         if (!toAddSorted.isEmpty()) {
299           Collections.sort(toAddSorted, uncompressedFilenameComparator);
300           newDictionaryConfig.dictionaryFilesOrdered.addAll(toAddSorted);
301         }
302
303         PersistentObjectCache.getInstance().write(C.DICTIONARY_CONFIGS, newDictionaryConfig);
304         synchronized (this) {
305           dictionaryConfig = newDictionaryConfig;
306         }
307         
308         try {
309           onUpdateFinished.run();
310         } catch (Exception e) {
311           Log.e(LOG, "Exception running callback.", e);
312         }
313       }}).start();
314   }
315
316   public synchronized List<DictionaryInfo> getUsableDicts() {
317     final List<DictionaryInfo> result = new ArrayList<DictionaryInfo>(dictionaryConfig.dictionaryFilesOrdered.size());
318     for (final String uncompressedFilename : dictionaryConfig.dictionaryFilesOrdered) {
319       final DictionaryInfo dictionaryInfo = dictionaryConfig.dictionaryInfoCache.get(uncompressedFilename);
320       if (dictionaryInfo != null) {
321         result.add(dictionaryInfo);
322       }
323     }
324     return result;
325   }
326
327   public synchronized List<DictionaryInfo> getAllDictionaries() {
328     final List<DictionaryInfo> result = getUsableDicts();
329     
330     // The downloadable ones.
331     final Map<String,DictionaryInfo> remaining = new LinkedHashMap<String, DictionaryInfo>(DOWNLOADABLE_NAME_TO_INFO);
332     remaining.keySet().removeAll(dictionaryConfig.dictionaryFilesOrdered);
333     final List<DictionaryInfo> toAddSorted = new ArrayList<DictionaryInfo>(remaining.values());
334     Collections.sort(toAddSorted, dictionaryInfoComparator);
335     result.addAll(toAddSorted);
336     
337     return result;
338   }
339
340   public synchronized boolean isDictionaryOnDevice(String uncompressedFilename) {
341     return dictionaryConfig.dictionaryInfoCache.get(uncompressedFilename) != null;
342   }
343
344   public boolean updateAvailable(final DictionaryInfo dictionaryInfo) {
345     final DictionaryInfo downloadable = DOWNLOADABLE_NAME_TO_INFO.get(dictionaryInfo.uncompressedFilename);
346     return downloadable != null && downloadable.creationMillis > dictionaryInfo.creationMillis;
347   }
348
349   public DictionaryInfo getDownloadable(final String uncompressedFilename) {
350     final DictionaryInfo downloadable = DOWNLOADABLE_NAME_TO_INFO.get(uncompressedFilename);
351     return downloadable;
352   }
353
354 }