]> gitweb.fperrin.net Git - Dictionary.git/blobdiff - src/com/hughes/android/dictionary/DictionaryActivity.java
go
[Dictionary.git] / src / com / hughes / android / dictionary / DictionaryActivity.java
index 0bfd39ac1c1687cc81586328e80bf9668f29791c..61061d57f1363b4432a450b33c0adbad591b77fb 100755 (executable)
@@ -6,12 +6,12 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.RandomAccessFile;
 import java.text.SimpleDateFormat;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import android.app.AlertDialog;
 import android.app.ListActivity;
 import android.content.Context;
 import android.content.Intent;
@@ -36,6 +36,7 @@ import android.view.ViewGroup;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.MenuItem.OnMenuItemClickListener;
 import android.view.View.OnClickListener;
+import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.Button;
 import android.widget.EditText;
@@ -43,18 +44,22 @@ import android.widget.ListView;
 import android.widget.TableLayout;
 import android.widget.TableRow;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import com.hughes.android.dictionary.Dictionary.IndexEntry;
 import com.hughes.android.dictionary.Dictionary.LanguageData;
 import com.hughes.android.dictionary.Dictionary.Row;
+import com.ibm.icu.text.Collator;
 
 public class DictionaryActivity extends ListActivity {
   
   // TODO:
-  // * Only have one live SearchActivity, and a way to wait for it to die.
-  // * Don't destroy dict unless we're really shutting down (not on screen rotate).
-  // * Move (re-)init code to a method, set a flag if prefs might have changed, invoke re-init in onResume, which clears flag and reloads prefs.
+  // * Download latest dicts.
+  //   * http://ftp.tu-chemnitz.de/pub/Local/urz/ding/de-en-devel/
+  //   * http://www1.dict.cc/translation_file_request.php?l=e
   // * Compress all the strings everywhere, put compression table in file.
+  // Done:
+  // * Only one way to way for current search to end. (won't do).
 
   static final String LOG = "QuickDic";
   static final String PREF_DICT_ACTIVE_LANG = "DICT_DIR_PREF";
@@ -62,40 +67,51 @@ public class DictionaryActivity extends ListActivity {
 
   // package for test.
   final Handler uiHandler = new Handler();
+  private final Executor searchExecutor = Executors.newSingleThreadExecutor();
 
   EditText searchText;
+  Button langButton;
+  int lastSelectedRow = 0;  // TODO: I'm evil.
 
-  private final Executor searchExecutor = Executors.newSingleThreadExecutor();
+  private boolean prefsMightHaveChanged = true;
 
   // Never null.
-  private boolean prefsMightHaveChanged = true;
   private File wordList;
-
   private RandomAccessFile dictRaf = null;
   private Dictionary dictionary = null;
+  private boolean saveOnlyFirstSubentry = false;
 
   // Visible for testing.
   LanguageListAdapter languageList = null;
-
   private SearchOperation searchOperation = null;
+  
+  public DictionaryActivity() {
+
+    searchExecutor.execute(new Runnable() {
+      public void run() {
+        final long startMillis = System.currentTimeMillis();
+        for (final String lang : Arrays.asList("EN", "DE")) {
+          Language.lookup(lang).getFindCollator(); 
+          final Collator c = Language.lookup(lang).getSortCollator(); 
+          if (c.compare("pre-print", "preppy") >= 0) {
+            Log.e(LOG, c.getClass() + " is buggy, lookups may not work properly.");
+          }
+        }
+        Log.d(LOG, "Loading collators took:" + (System.currentTimeMillis() - startMillis));
+      }
+    });
 
-  private int selectedRowIndex;
-  private int selectedTokenRowIndex;
+  }
 
   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
-    Log.d(LOG, "onCreate");
-
-    if (Language.EN.sortCollator.compare("pre-print", "preppy") >= 0) {
-      Log
-          .e(LOG,
-              "Android java.text.Collator is buggy, lookups may not work properly.");
-    }
+    Log.d(LOG, "onCreate:" + this);
 
-    initDictionaryAndPrefs();
-    if (dictRaf == null) {
+    try {
+      initDictionaryAndPrefs();
+    } catch (Exception e) {
       return;
     }
 
@@ -103,17 +119,47 @@ public class DictionaryActivity extends ListActivity {
 
     setContentView(R.layout.main);
     searchText = (EditText) findViewById(R.id.SearchText);
-
+    langButton = (Button) findViewById(R.id.LangButton);
+    
     Log.d(LOG, "adding text changed listener");
     searchText.addTextChangedListener(new SearchTextWatcher());
-
-    // Language button.
+    
+    getListView().setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+      public void onItemSelected(AdapterView<?> arg0, View arg1, int row,
+          long arg3) {
+        setSelectedRow(row);
+      }
+      public void onNothingSelected(AdapterView<?> arg0) {
+      }
+    });
+    
+    getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+      public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int row,
+          long arg3) {
+        setSelectedRow(row);
+        return false;
+      }
+    });
+    
+    final Button clearSearchTextButton = (Button) findViewById(R.id.ClearSearchTextButton);
+    clearSearchTextButton.setOnClickListener(new OnClickListener() {
+      public void onClick(View v) {
+        clearSearchTextButton.requestFocus();
+        searchText.setText("");
+        searchText.requestFocus();
+      }
+    });
+    clearSearchTextButton.setVisibility(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
+        getString(R.string.showClearSearchTextButtonKey), true) ? View.VISIBLE
+        : View.GONE);
+    
     final Button langButton = (Button) findViewById(R.id.LangButton);
     langButton.setOnClickListener(new OnClickListener() {
       public void onClick(View v) {
         onLanguageButton();
       }
     });
+    
     final Button upButton = (Button) findViewById(R.id.UpButton);
     upButton.setOnClickListener(new OnClickListener() {
       public void onClick(View v) {
@@ -132,8 +178,8 @@ public class DictionaryActivity extends ListActivity {
 
     updateLangButton();
   }
-
-  private void initDictionaryAndPrefs() {
+  
+  private void initDictionaryAndPrefs() throws Exception {
     if (!prefsMightHaveChanged) {
       return;
     }
@@ -144,26 +190,32 @@ public class DictionaryActivity extends ListActivity {
     wordList = new File(prefs.getString(getString(R.string.wordListFileKey),
         getString(R.string.wordListFileDefault)));
     Log.d(LOG, "wordList=" + wordList);
+    
+    saveOnlyFirstSubentry = prefs.getBoolean(getString(R.string.saveOnlyFirstSubentryKey), false);
 
     final File dictFile = new File(prefs.getString(getString(R.string.dictFileKey),
         getString(R.string.dictFileDefault)));
     Log.d(LOG, "dictFile=" + dictFile);
-    if (!dictFile.canRead()) {
-      Log.w(LOG, "Unable to read dictionary file.");
-      this.startActivity(new Intent(this, NoDictionaryActivity.class));
-      finish();
-    }
-
+    
     try {
+      if (!dictFile.canRead()) {
+        throw new IOException("Unable to read dictionary file.");
+      }
+      
       dictRaf = new RandomAccessFile(dictFile, "r");
+      final long startMillis = System.currentTimeMillis();
       dictionary = new Dictionary(dictRaf);
-    } catch (Exception e) {
-      throw new RuntimeException(e);
+      Log.d(LOG, "Read dictionary millis: " + (System.currentTimeMillis() - startMillis));
+    } catch (IOException e) {
+      Log.e(LOG, "Couldn't open dictionary.", e);
+      this.startActivity(new Intent(this, NoDictionaryActivity.class));
+      finish();
+      throw new Exception(e);
     }
-
+    
     final byte lang = prefs.getInt(PREF_DICT_ACTIVE_LANG, Entry.LANG1) == Entry.LANG1 ? Entry.LANG1
         : Entry.LANG2;
-
+    
     languageList = new LanguageListAdapter(dictionary.languageDatas[lang]);
     setListAdapter(languageList);
     prefsMightHaveChanged = false;
@@ -172,9 +224,12 @@ public class DictionaryActivity extends ListActivity {
   @Override
   public void onResume() {
     super.onResume();
-    
-    if (prefsMightHaveChanged) {
-      
+    Log.d(LOG, "onResume:" + this);
+
+    try {
+      initDictionaryAndPrefs();
+    } catch (Exception e) {
+      return;
     }
     
     final SharedPreferences prefs = PreferenceManager
@@ -182,12 +237,14 @@ public class DictionaryActivity extends ListActivity {
     final String searchTextString = prefs
         .getString(PREF_ACTIVE_SEARCH_TEXT, "");
     searchText.setText(searchTextString);
+    getListView().requestFocus();
     onSearchTextChange(searchTextString);
   }
 
   @Override
   public void onPause() {
     super.onPause();
+    Log.d(LOG, "onPause:" + this);
     final Editor prefs = PreferenceManager.getDefaultSharedPreferences(this)
         .edit();
     prefs.putInt(PREF_DICT_ACTIVE_LANG, languageList.languageData.lang);
@@ -198,19 +255,22 @@ public class DictionaryActivity extends ListActivity {
   @Override
   public void onStop() {
     super.onStop();
+    Log.d(LOG, "onStop:" + this);
     if (isFinishing()) {
+      Log.i(LOG, "isFinishing()==true, closing dictionary.");
       closeCurrentDictionary();
     }
   }
 
   private void closeCurrentDictionary() {
     Log.i(LOG, "closeCurrentDictionary");
-    if (searchOperation != null) {
-      searchOperation.stopAndWait();
-      searchOperation = null;
+    if (dictionary == null) {
+      return;
     }
+    waitForSearchEnd();
     languageList = null;
     setListAdapter(null);
+    Log.d(LOG, "setListAdapter finished.");
     dictionary = null;
     try {
       if (dictRaf != null) {
@@ -222,10 +282,9 @@ public class DictionaryActivity extends ListActivity {
     dictRaf = null;
   }
 
-  public String getSelectedRowRawText() {
-    final int row = getSelectedItemPosition();
-    return row < 0 ? "" : languageList.languageData
-        .rowToString(languageList.languageData.rows.get(row));
+  public String getSelectedRowRawText(final boolean onlyFirstSubentry) {
+    final Row row = languageList.languageData.rows.get(getSelectedRow());
+    return languageList.languageData.rowToString(row, onlyFirstSubentry);
   }
 
   // ----------------------------------------------------------------
@@ -260,22 +319,15 @@ public class DictionaryActivity extends ListActivity {
       public boolean onMenuItemClick(final MenuItem menuItem) {
         final Intent intent = new Intent().setClassName(AboutActivity.class
             .getPackage().getName(), AboutActivity.class.getCanonicalName());
-        final StringBuilder currentDictInfo = new StringBuilder();
+        final String currentDictInfo;
         if (dictionary == null) {
-          currentDictInfo.append(getString(R.string.noDictLoaded));
+          currentDictInfo = getString(R.string.noDictLoaded);
         } else {
-          currentDictInfo.append(dictionary.dictionaryInfo).append("\n\n");
-          currentDictInfo.append("Entry count: " + dictionary.entries.size())
-              .append("\n");
-          for (int i = 0; i < 2; ++i) {
-            final LanguageData languageData = dictionary.languageDatas[i];
-            currentDictInfo.append(languageData.language.symbol).append(":\n");
-            currentDictInfo.append(
-                "  Unique token count: " + languageData.sortedIndex.size())
-                .append("\n");
-            currentDictInfo.append("  Row count: " + languageData.rows.size())
-                .append("\n");
-          }
+          final LanguageData lang0 = dictionary.languageDatas[0];
+          final LanguageData lang1 = dictionary.languageDatas[1];
+          currentDictInfo = getString(R.string.aboutText, dictionary.dictionaryInfo, dictionary.entries.size(), 
+              lang0.language.symbol, lang0.sortedIndex.size(), lang0.rows.size(),
+              lang1.language.symbol, lang1.sortedIndex.size(), lang1.rows.size());
         }
         intent.putExtra(AboutActivity.CURRENT_DICT_INFO, currentDictInfo
             .toString());
@@ -287,6 +339,7 @@ public class DictionaryActivity extends ListActivity {
     final MenuItem download = menu.add(getString(R.string.downloadDictionary));
     download.setOnMenuItemClickListener(new OnMenuItemClickListener() {
       public boolean onMenuItemClick(final MenuItem menuItem) {
+        prefsMightHaveChanged = true;
         startDownloadDictActivity(DictionaryActivity.this);
         return false;
       }
@@ -297,14 +350,13 @@ public class DictionaryActivity extends ListActivity {
 
   @Override
   public boolean onPrepareOptionsMenu(final Menu menu) {
-    switchLanguageMenuItem.setTitle(String.format(
-        getString(R.string.switchToLanguage), dictionary.languageDatas[Entry
+    switchLanguageMenuItem.setTitle(getString(R.string.switchToLanguage,
+        dictionary.languageDatas[Entry
             .otherLang(languageList.languageData.lang)].language.symbol));
     return super.onPrepareOptionsMenu(menu);
   }
   
   void updateLangButton() {
-    final Button langButton = (Button) findViewById(R.id.LangButton);
     langButton.setText(languageList.languageData.language.symbol);
   }
 
@@ -313,6 +365,7 @@ public class DictionaryActivity extends ListActivity {
   // ----------------------------------------------------------------
   
   void onLanguageButton() {
+    waitForSearchEnd();
     languageList = new LanguageListAdapter(
         dictionary.languageDatas[(languageList.languageData == dictionary.languageDatas[0]) ? 1
             : 0]);
@@ -323,41 +376,19 @@ public class DictionaryActivity extends ListActivity {
   }
 
   void onUpButton() {
-    final int destRowIndex;
-    final Row tokenRow = languageList.languageData.rows
-        .get(selectedTokenRowIndex);
-    assert tokenRow.isToken();
-    final int prevTokenIndex = tokenRow.getIndex() - 1;
-    if (selectedRowIndex == selectedTokenRowIndex && selectedRowIndex > 0) {
-      destRowIndex = languageList.languageData.sortedIndex
-          .get(prevTokenIndex).startRow;
-      Log.d(LOG, "onUpButton, jumping back a word, destRowIndex=" + destRowIndex);
-    } else {
-      destRowIndex = selectedTokenRowIndex;
-      Log.d(LOG, "onUpButton, jumping to top of word, destRowIndex=" + destRowIndex);
-    }
+    final int destRowIndex = languageList.languageData.getPrevTokenRow(getSelectedRow());
+    Log.d(LOG, "onUpButton, destRowIndex=" + destRowIndex);
     jumpToRow(languageList, destRowIndex);
   }
 
   void onDownButton() {
-    final Row tokenRow = languageList.languageData.rows
-        .get(selectedTokenRowIndex);
-    assert tokenRow.isToken();
-    final int nextTokenIndex = tokenRow.getIndex() + 1;
-    final int destRowIndex;
-    if (nextTokenIndex < languageList.languageData.sortedIndex.size()) {
-      destRowIndex = languageList.languageData.sortedIndex
-          .get(nextTokenIndex).startRow;
-      Log.d(LOG, "onDownButton, jumping down a word, destRowIndex=" + destRowIndex);
-    } else {
-      destRowIndex = languageList.languageData.rows.size() - 1;
-      Log.d(LOG, "onDownButton, jumping to end of dict, destRowIndex=" + destRowIndex);
-    }
+    final int destRowIndex = languageList.languageData.getNextTokenRow(getSelectedRow());
+    Log.d(LOG, "onDownButton, destRowIndex=" + destRowIndex);
     jumpToRow(languageList, destRowIndex);
   }
 
   void onAppendToWordList() {
-    final int row = getSelectedItemPosition();
+    final int row = getSelectedRow();
     if (row < 0) {
       return;
     }
@@ -367,7 +398,7 @@ public class DictionaryActivity extends ListActivity {
         new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(new Date()))
         .append("\t");
     rawText.append(word).append("\t");
-    rawText.append(getSelectedRowRawText());
+    rawText.append(getSelectedRowRawText(saveOnlyFirstSubentry));
     Log.d(LOG, "Writing : " + rawText);
     try {
       wordList.getParentFile().mkdirs();
@@ -377,23 +408,19 @@ public class DictionaryActivity extends ListActivity {
       out.close();
     } catch (IOException e) {
       Log.e(LOG, "Unable to append to " + wordList.getAbsolutePath(), e);
-      final AlertDialog alert = new AlertDialog.Builder(
-          DictionaryActivity.this).create();
-      alert.setMessage("Failed to append to file: "
-          + wordList.getAbsolutePath());
-      alert.show();
+      Toast.makeText(this, getString(R.string.failedAddingToWordList, wordList.getAbsolutePath()), Toast.LENGTH_LONG);
     }
     return;
   }
 
   void onCopy() {
-    final int row = getSelectedItemPosition();
+    final int row = getSelectedRow();
     if (row < 0) {
       return;
     }
-    Log.d(LOG, "Copy." + DictionaryActivity.this.getSelectedItemPosition());
+    Log.d(LOG, "Copy." + DictionaryActivity.this.getSelectedRow());
     final StringBuilder result = new StringBuilder();
-    result.append(getSelectedRowRawText());
+    result.append(getSelectedRowRawText(false));
     final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
     clipboardManager.setText(result.toString());
     Log.d(LOG, "Copied: " + result);
@@ -414,16 +441,20 @@ public class DictionaryActivity extends ListActivity {
 
   @Override
   protected void onListItemClick(ListView l, View v, int row, long id) {
-    Log.d(LOG, "Clicked: " + getSelectedRowRawText());
+    setSelectedRow(row);
     openContextMenu(getListView());
   }
 
   void onSearchTextChange(final String searchText) {
     Log.d(LOG, "onSearchTextChange: " + searchText);
-    searchOperation = new SearchOperation(languageList, searchText, searchOperation);
-    searchExecutor.execute(searchOperation);
+    synchronized (this) {
+      searchOperation = new SearchOperation(languageList, searchText, searchOperation);
+      searchExecutor.execute(searchOperation);
+    }
   }
 
+  
+
   // ----------------------------------------------------------------
   // ContextMenu
   // ----------------------------------------------------------------
@@ -431,13 +462,12 @@ public class DictionaryActivity extends ListActivity {
   @Override
   public void onCreateContextMenu(ContextMenu menu, View v,
       ContextMenuInfo menuInfo) {
-    final int row = getSelectedItemPosition();
+    final int row = getSelectedRow();
     if (row < 0) {
       return;
     }
 
-    final MenuItem addToWordlist = menu.add(String.format(
-        getString(R.string.addToWordList), wordList.getName()));
+    final MenuItem addToWordlist = menu.add(getString(R.string.addToWordList, wordList.getName()));
     addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() {
       public boolean onMenuItemClick(MenuItem item) {
         onAppendToWordList();
@@ -462,21 +492,34 @@ public class DictionaryActivity extends ListActivity {
       Log.w(LOG, "skipping jumpToRow for old list adapter: " + rowIndex);
       return;
     }
-    // selectedTokenRowIndex =
-    // languageList.languageData.getIndexEntryForRow(rowIndex).startRow;
     setSelection(rowIndex);
-    getListView().setSelected(true); // TODO: is this doing anything?
+    setSelectedRow(rowIndex);
+    getListView().setSelected(true);
+  }
+  
+  // TODO: delete me somehow.
+  private int getSelectedRow() {
+    return lastSelectedRow;
+  }
+  private void setSelectedRow(final int row) {
+    lastSelectedRow = row;
+    Log.d(LOG, "Selected: " + getSelectedRowRawText(true));
     updateSearchText();
   }
 
   private void updateSearchText() {
     Log.d(LOG, "updateSearchText");
+    final int selectedRowIndex = getSelectedRow();
     if (!searchText.hasFocus()) {
-      final String word = languageList.languageData
-          .getIndexEntryForRow(selectedRowIndex).word;
-      if (!word.equals(searchText.getText().toString())) {
-        Log.d(LOG, "updateSearchText: setText: " + word);
-        searchText.setText(word);
+      if (selectedRowIndex >= 0) {
+        final String word = languageList.languageData
+            .getIndexEntryForRow(selectedRowIndex).word;
+        if (!word.equals(searchText.getText().toString())) {
+          Log.d(LOG, "updateSearchText: setText: " + word);
+          searchText.setText(word);
+        }
+      } else {
+        Log.w(LOG, "updateSearchText: nothing selected.");
       }
     }
   }
@@ -533,7 +576,7 @@ public class DictionaryActivity extends ListActivity {
         if (row == null) {
           return result;
         }
-        result.setText(languageData.rowToString(row));
+        result.setText(languageData.rowToString(row, false));
         result.setTextAppearance(parent.getContext(),
             android.R.style.TextAppearance_Large);
         result.setClickable(false);
@@ -601,7 +644,7 @@ public class DictionaryActivity extends ListActivity {
     final LanguageData languageData;
     final String searchText;
     final AtomicBoolean interrupted = new AtomicBoolean(false);
-    boolean finished = false;
+    boolean searchFinished = false;
 
     SearchOperation(final LanguageListAdapter listAdapter,
         final String searchText, final SearchOperation previousSearchOperation) {
@@ -619,32 +662,34 @@ public class DictionaryActivity extends ListActivity {
       
       Log.d(LOG, "SearchOperation: " + searchText);
       final int indexLocation = languageData.lookup(searchText, interrupted);
-      if (interrupted.get()) {
-        return;
-      }
-      final IndexEntry indexEntry = languageData.sortedIndex.get(indexLocation);
-      
-      Log.d(LOG, "SearchOperation completed: " + indexEntry.toString());
-      uiHandler.post(new Runnable() {
-        public void run() {
-          // Check is just a performance operation.
-          if (!interrupted.get()) {
-            // This is safe, because it checks that the listAdapter hasn't changed.
-            jumpToRow(listAdapter, indexEntry.startRow);
+      if (!interrupted.get()) {
+        final IndexEntry indexEntry = languageData.sortedIndex.get(indexLocation);
+        
+        Log.d(LOG, "SearchOperation completed: " + indexEntry.toString());
+        uiHandler.post(new Runnable() {
+          public void run() {
+            // Check is just a performance operation.
+            if (!interrupted.get()) {
+              // This is safe, because it checks that the listAdapter hasn't changed.
+              jumpToRow(listAdapter, indexEntry.startRow);
+            }
+            synchronized (DictionaryActivity.this) {
+              searchOperation = null;
+              DictionaryActivity.this.notifyAll();
+            }
           }
-        }
-      });
-      
+        });
+      }      
       synchronized (this) {
-        finished = true;
+        searchFinished = true;
         this.notifyAll();
       }
     }
     
-    public void stopAndWait() {
+    private void stopAndWait() {
       interrupted.set(true);
       synchronized (this) {
-        while (!finished) {
+        while (!searchFinished) {
           Log.d(LOG, "stopAndWait: " + searchText);
           try {
             this.wait();
@@ -654,8 +699,19 @@ public class DictionaryActivity extends ListActivity {
         }
       }
     }
-
-
+  }  // SearchOperation
+
+  void waitForSearchEnd() {
+    synchronized (this) {
+      while (searchOperation != null) {
+        Log.d(LOG, "waitForSearchEnd");
+        try {
+          this.wait();
+        } catch (InterruptedException e) {
+          Log.e(LOG, "Interrupted.", e);
+        }
+      }
+    }
   }
 
   private class SearchTextWatcher implements TextWatcher {