From: Thad Hughes Date: Fri, 5 Nov 2010 19:26:13 +0000 (-0700) Subject: go X-Git-Url: http://gitweb.fperrin.net/?a=commitdiff_plain;h=daed9b15f5ae067f0c03fc2bb942b3171aed9b4f;p=Dictionary.git go --- diff --git a/res/drawable/icon_old.png b/res/drawable/icon_old.png new file mode 100644 index 0000000..17ccb38 Binary files /dev/null and b/res/drawable/icon_old.png differ diff --git a/src/com/hughes/android/dictionary/DictionaryActivityOld.java.old b/src/com/hughes/android/dictionary/DictionaryActivityOld.java.old new file mode 100644 index 0000000..662ed15 --- /dev/null +++ b/src/com/hughes/android/dictionary/DictionaryActivityOld.java.old @@ -0,0 +1,739 @@ +package com.hughes.android.dictionary; + +import java.io.File; +import java.io.FileWriter; +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.ListActivity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.graphics.Typeface; +import android.os.Bundle; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.text.ClipboardManager; +import android.text.Editable; +import android.text.Spannable; +import android.text.TextWatcher; +import android.text.style.StyleSpan; +import android.util.Log; +import android.view.ContextMenu; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.View.OnClickListener; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.EditText; +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.engine.Dictionary; +import com.hughes.android.dictionary.engine.Language; +import com.ibm.icu.text.Collator; + +public class DictionaryActivityOld extends ListActivity { + + // TO DO: + // * Easy reverse lookup. + // * 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"; + static final String PREF_ACTIVE_SEARCH_TEXT = "ACTIVE_WORD_PREF"; + + // 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 boolean prefsMightHaveChanged = true; + + // Never null. + private File wordList = null; + private RandomAccessFile dictRaf = null; + private Dictionary dictionary = null; + private boolean saveOnlyFirstSubentry = false; + + // Visible for testing. + IndexAdapter indexAdapter = 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)); + } + }); + + } + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.d(LOG, "onCreate:" + this); + + try { + initDictionaryAndPrefs(); + } catch (Exception e) { + return; + } + + // UI init. + + setContentView(R.layout.dictionary_activity); + searchText = (EditText) findViewById(R.id.SearchText); + langButton = (Button) findViewById(R.id.LangButton); + + searchText.addTextChangedListener(new SearchTextWatcher()); + + 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) { + onClearSearchTextButton(clearSearchTextButton); + } + }); + 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) { + onUpButton(); + } + }); + final Button downButton = (Button) findViewById(R.id.DownButton); + downButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + onDownButton(); + } + }); + + // ContextMenu. + registerForContextMenu(getListView()); + + updateLangButton(); + } + + private void initDictionaryAndPrefs() throws Exception { + if (!prefsMightHaveChanged) { + return; + } + closeCurrentDictionary(); + + final SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + 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); + + 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); + Log.d(LOG, "Read dictionary millis: " + (System.currentTimeMillis() - startMillis)); + } catch (IOException e) { + Log.e(LOG, "Couldn't open dictionary.", e); + + this.startActivity(new asdfIntent(this, DictionaryEditActivity.class)); + finish(); + } + + final byte lang = prefs.getInt(PREF_DICT_ACTIVE_LANG, SimpleEntry.LANG1) == SimpleEntry.LANG1 ? SimpleEntry.LANG1 + : SimpleEntry.LANG2; + + indexAdapter = new IndexAdapter(dictionary.languageDatas[lang]); + setListAdapter(indexAdapter); + prefsMightHaveChanged = false; + } + + @Override + public void onResume() { + super.onResume(); + Log.d(LOG, "onResume:" + this); + + try { + initDictionaryAndPrefs(); + } catch (Exception e) { + return; + } + + final SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(this); + 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, indexAdapter.languageData.lang); + prefs.putString(PREF_ACTIVE_SEARCH_TEXT, searchText.getText().toString()); + prefs.commit(); + } + + @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 (dictionary == null) { + return; + } + waitForSearchEnd(); + indexAdapter = null; + setListAdapter(null); + Log.d(LOG, "setListAdapter finished."); + dictionary = null; + try { + if (dictRaf != null) { + dictRaf.close(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + dictRaf = null; + } + + public String getSelectedRowRawText(final boolean onlyFirstSubentry) { + final Row row = indexAdapter.languageData.rows.get(getSelectedRow()); + return indexAdapter.languageData.rowToString(row, onlyFirstSubentry); + } + + // ---------------------------------------------------------------- + // OptionsMenu + // ---------------------------------------------------------------- + + private MenuItem switchLanguageMenuItem = null; + + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + switchLanguageMenuItem = menu.add(getString(R.string.switchToLanguage)); + switchLanguageMenuItem + .setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(final MenuItem menuItem) { + onLanguageButton(); + return false; + } + }); + + final MenuItem preferences = menu.add(getString(R.string.preferences)); + preferences.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(final MenuItem menuItem) { + prefsMightHaveChanged = true; + startActivity(new Intent(DictionaryActivity.this, + PreferenceActivity.class)); + return false; + } + }); + + 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()); + final String currentDictInfo; + if (dictionary == null) { + currentDictInfo = getString(R.string.noDictLoaded); + } else { + 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()); + startActivity(intent); + return false; + } + }); + + 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; + } + }); + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(final Menu menu) { + switchLanguageMenuItem.setTitle(getString(R.string.switchToLanguage, + dictionary.languageDatas[SimpleEntry + .otherLang(indexAdapter.languageData.lang)].language.symbol)); + return super.onPrepareOptionsMenu(menu); + } + + void updateLangButton() { + langButton.setText(indexAdapter.languageData.language.symbol); + } + + // ---------------------------------------------------------------- + // Event handlers. + // ---------------------------------------------------------------- + + void onLanguageButton() { + waitForSearchEnd(); + indexAdapter = new IndexAdapter( + dictionary.languageDatas[(indexAdapter.languageData == dictionary.languageDatas[0]) ? 1 + : 0]); + Log.d(LOG, "onLanguageButton, newLang=" + indexAdapter.languageData.language.symbol); + setListAdapter(indexAdapter); + updateLangButton(); + onSearchTextChange(searchText.getText().toString()); + } + + void onUpButton() { + final int destRowIndex = indexAdapter.languageData.getPrevTokenRow(getSelectedRow()); + Log.d(LOG, "onUpButton, destRowIndex=" + destRowIndex); + jumpToRow(indexAdapter, destRowIndex); + } + + void onDownButton() { + final int destRowIndex = indexAdapter.languageData.getNextTokenRow(getSelectedRow()); + Log.d(LOG, "onDownButton, destRowIndex=" + destRowIndex); + jumpToRow(indexAdapter, destRowIndex); + } + + void onAppendToWordList() { + final int row = getSelectedRow(); + if (row < 0) { + return; + } + final StringBuilder rawText = new StringBuilder(); + final String word = indexAdapter.languageData.getIndexEntryForRow(row).word; + rawText.append( + new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(new Date())) + .append("\t"); + rawText.append(word).append("\t"); + rawText.append(getSelectedRowRawText(saveOnlyFirstSubentry)); + Log.d(LOG, "Writing : " + rawText); + try { + wordList.getParentFile().mkdirs(); + final PrintWriter out = new PrintWriter( + new FileWriter(wordList, true)); + out.println(rawText.toString()); + out.close(); + } catch (IOException e) { + Log.e(LOG, "Unable to append to " + wordList.getAbsolutePath(), e); + Toast.makeText(this, getString(R.string.failedAddingToWordList, wordList.getAbsolutePath()), Toast.LENGTH_LONG); + } + return; + } + + void onCopy() { + final int row = getSelectedRow(); + if (row < 0) { + return; + } + Log.d(LOG, "Copy." + DictionaryActivity.this.getSelectedRow()); + final StringBuilder result = new StringBuilder(); + result.append(getSelectedRowRawText(false)); + final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + clipboardManager.setText(result.toString()); + Log.d(LOG, "Copied: " + result); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (event.getUnicodeChar() != 0) { + if (!searchText.hasFocus()) { + searchText.setText("" + (char) event.getUnicodeChar()); + onSearchTextChange(searchText.getText().toString()); + searchText.requestFocus(); + } + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + protected void onListItemClick(ListView l, View v, int row, long id) { + setSelectedRow(row); + openContextMenu(getListView()); + } + + void onSearchTextChange(final String searchText) { + Log.d(LOG, "onSearchTextChange: " + searchText); + synchronized (this) { + searchOperation = new SearchOperation(indexAdapter, searchText.trim(), searchOperation); + searchExecutor.execute(searchOperation); + } + } + + private void onClearSearchTextButton(final Button clearSearchTextButton) { + clearSearchTextButton.requestFocus(); + searchText.setText(""); + searchText.requestFocus(); + Log.d(LOG, "Trying to show soft keyboard."); + final InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + manager.showSoftInput(searchText, InputMethodManager.SHOW_IMPLICIT); + } + + // ---------------------------------------------------------------- + // ContextMenu + // ---------------------------------------------------------------- + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + final int row = getSelectedRow(); + if (row < 0) { + return; + } + + final MenuItem addToWordlist = menu.add(getString(R.string.addToWordList, wordList.getName())); + addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + onAppendToWordList(); + return false; + } + }); + + final MenuItem copy = menu.add(android.R.string.copy); + copy.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + onCopy(); + return false; + } + }); + + } + + private void jumpToRow(final IndexAdapter dictionaryListAdapter, + final int rowIndex) { + Log.d(LOG, "jumpToRow: " + rowIndex); + if (dictionaryListAdapter != this.indexAdapter) { + Log.w(LOG, "skipping jumpToRow for old list adapter: " + rowIndex); + return; + } + setSelection(rowIndex); + 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()) { + if (selectedRowIndex >= 0) { + final String word = indexAdapter.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."); + } + } + } + + static void startDownloadDictActivity(final Context context) { + final Intent intent = new Intent(context, DownloadActivity.class); + final SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context); + final String dictFetchUrl = prefs.getString(context + .getString(R.string.dictFetchUrlKey), context + .getString(R.string.dictFetchUrlDefault)); + final String dictFileName = prefs.getString(context + .getString(R.string.dictFileKey), context + .getString(R.string.dictFileDefault)); + intent.putExtra(DownloadActivity.SOURCE, dictFetchUrl); + intent.putExtra(DownloadActivity.DEST, dictFileName); + context.startActivity(intent); + } + + class IndexAdapter extends BaseAdapter { + + // Visible for testing. + final LanguageData languageData; + + IndexAdapter(final LanguageData languageData) { + this.languageData = languageData; + } + + public int getCount() { + return languageData.rows.size(); + } + + public Dictionary.Row getItem(int rowIndex) { + assert rowIndex < languageData.rows.size(); + return languageData.rows.get(rowIndex); + } + + public long getItemId(int rowIndex) { + return rowIndex; + } + + public View getView(final int rowIndex, final View convertView, + final ViewGroup parent) { + final Row row = getItem(rowIndex); + + // Token row. + if (row.isToken()) { + TextView result = null; + if (convertView instanceof TextView) { + result = (TextView) convertView; + } else { + result = new TextView(parent.getContext()); + } + if (row == null) { + return result; + } + result.setText(languageData.rowToString(row, false)); + result.setTextAppearance(parent.getContext(), + android.R.style.TextAppearance_Large); + result.setClickable(false); + return result; + } + + // Entry row(s). + final TableLayout result = new TableLayout(parent.getContext()); + + final SimpleEntry entry = new SimpleEntry(null, null);//.entries.get(row.getIndex()); + final int rowCount = entry.getRowCount(); + for (int r = 0; r < rowCount; ++r) { + final TableRow tableRow = new TableRow(result.getContext()); + + TextView column1 = new TextView(tableRow.getContext()); + TextView column2 = new TextView(tableRow.getContext()); + final TableRow.LayoutParams layoutParams = new TableRow.LayoutParams(); + layoutParams.weight = 0.5f; + + if (r > 0) { + final TextView spacer = new TextView(tableRow.getContext()); + spacer.setText(r == 0 ? "� " : " � "); + tableRow.addView(spacer); + } + tableRow.addView(column1, layoutParams); + if (r > 0) { + final TextView spacer = new TextView(tableRow.getContext()); + spacer.setText(r == 0 ? "� " : " � "); + tableRow.addView(spacer); + } + tableRow.addView(column2, layoutParams); + + column1.setWidth(1); + column2.setWidth(1); + // column1.setTextAppearance(parent.getContext(), android.R.style.Text); + + // TODO: color words by gender + final String col1Text = entry.getAllText(languageData.lang)[r]; + column1.setText(col1Text, TextView.BufferType.SPANNABLE); + final Spannable col1Spannable = (Spannable) column1.getText(); + int startPos = 0; + final String token = languageData.getIndexEntryForRow(rowIndex).word; + while ((startPos = col1Text.indexOf(token, startPos)) != -1) { + col1Spannable.setSpan(new StyleSpan(Typeface.BOLD), startPos, + startPos + token.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + startPos += token.length(); + } + + column2.setText( + entry.getAllText(SimpleEntry.otherLang(languageData.lang))[r], + TextView.BufferType.NORMAL); + + result.addView(tableRow); + } + + return result; + } + + } // DictionaryListAdapter + + private final class SearchOperation implements Runnable { + SearchOperation previousSearchOperation; + + final IndexAdapter listAdapter; + final LanguageData languageData; + final String searchText; + final AtomicBoolean interrupted = new AtomicBoolean(false); + boolean searchFinished = false; + + SearchOperation(final IndexAdapter listAdapter, + final String searchText, final SearchOperation previousSearchOperation) { + this.listAdapter = listAdapter; + this.languageData = listAdapter.languageData; + this.searchText = searchText; + this.previousSearchOperation = previousSearchOperation; + } + + public void run() { + if (previousSearchOperation != null) { + previousSearchOperation.stopAndWait(); + } + previousSearchOperation = null; + + Log.d(LOG, "SearchOperation: " + searchText); + final int indexLocation = languageData.lookup(searchText, interrupted); + 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) { + searchFinished = true; + this.notifyAll(); + } + } + + private void stopAndWait() { + interrupted.set(true); + synchronized (this) { + while (!searchFinished) { + Log.d(LOG, "stopAndWait: " + searchText); + try { + this.wait(); + } catch (InterruptedException e) { + Log.e(LOG, "Interrupted", e); + } + } + } + } + } // 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 { + public void afterTextChanged(final Editable searchTextEditable) { + Log.d(LOG, "Search text changed: " + searchText.getText().toString()); + if (searchText.hasFocus()) { + // If they were typing to cause the change, update the UI. + onSearchTextChange(searchText.getText().toString()); + } + } + + public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, + int arg3) { + } + + public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { + } + } + +} \ No newline at end of file