<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.hughes.android.dictionary" android:versionCode="9"
+
+ package="com.hughes.android.dictionary"
+
+ android:versionCode="9"
android:versionName="2.0"
android:installLocation="auto">
<string name="contactMe">Wenn QuickDic dir gefällt, würde ich gern von dir hören. Bitte schicke Bemerkungen, Aufforderungen, oder Programmfehler an:</string>
<string name="currentDictInfo">Wörterbuch Info:</string>
<string name="noDictLoaded">Kein Wörterbuch geöffnet.</string>
- <string name="aboutText"><![CDATA[%s\n
-\n
-Einträgezahl: %d\n
-%s:\n
- Wörterzahl: %d\n
- Reihezahl: %d\n
-%s:\n
- Wörterzahl: %d\n
- Reihezahl: %d\n
-]]></string>
<!-- Download. -->
- <string name="downloading">%d von %d Bytes heruntergeladen...</string>
+ <string name="downloading">%1$d von %2$d Bytes heruntergeladen...</string>
<string name="downloadFinished">Herunterladen fertig, %d Bytes heruntergeladen.</string>
<string name="errorDownloadingFile">"Fehler während des Herunterladens: \n%s"</string>
<string name="localFile">Dictionary file</string>
<string name="wordListFile">Word list file</string>
<string name="fileNotFound">File not found: \'%s\'</string>
- <string name="invalidDictionary">Invalid dictionary: file=%s, error=%s</string>
+ <string name="invalidDictionary">Invalid dictionary: file=%1$s, error=%2$s</string>
<string name="numPairEntries">Entries: %d</string>
<string name="numTokens">Tokens: %d</string>
<string name="numRows">Rows: %d</string>
<string name="webPageUrl">http://sites.google.com/site/quickdic/home</string>
<!-- Download. -->
- <string name="downloading">Downloading, %d of %d bytes.</string>
+ <string name="downloading">Downloading, %1$d of %2$d bytes.</string>
<string name="downloadFinished">Download finished, %d bytes downloaded.</string>
<string name="errorDownloadingFile">"Error downloading file: \n%s"</string>
import java.util.Date;\r
import java.util.concurrent.Executor;\r
import java.util.concurrent.Executors;\r
+import java.util.concurrent.ThreadFactory;\r
import java.util.concurrent.atomic.AtomicBoolean;\r
\r
import android.app.ListActivity;\r
import com.hughes.android.dictionary.engine.Dictionary;\r
import com.hughes.android.dictionary.engine.Index;\r
import com.hughes.android.dictionary.engine.PairEntry;\r
+import com.hughes.android.dictionary.engine.PairEntry.Pair;\r
import com.hughes.android.dictionary.engine.RowBase;\r
import com.hughes.android.dictionary.engine.TokenRow;\r
+import com.hughes.android.dictionary.engine.TransliteratorManager;\r
import com.hughes.android.util.PersistentObjectCache;\r
\r
public class DictionaryActivity extends ListActivity {\r
\r
// package for test.\r
final Handler uiHandler = new Handler();\r
- private final Executor searchExecutor = Executors.newSingleThreadExecutor();\r
+ private final Executor searchExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {\r
+ @Override\r
+ public Thread newThread(Runnable r) {\r
+ return new Thread(r, "searchExecutor");\r
+ }\r
+ });\r
private SearchOperation currentSearchOperation = null;\r
\r
EditText searchText;\r
@Override\r
public void onCreate(Bundle savedInstanceState) {\r
super.onCreate(savedInstanceState);\r
+ Log.d(LOG, "onCreate:" + this);\r
\r
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);\r
\r
return;\r
}\r
\r
+ indexIndex = prefs.getInt(C.INDEX_INDEX, 0) % dictionary.indices.size();\r
+ Log.d(LOG, "Loading index.");\r
+ index = dictionary.indices.get(indexIndex);\r
+ setListAdapter(new IndexAdapter(index));\r
+\r
// Pre-load the collators.\r
searchExecutor.execute(new Runnable() {\r
public void run() {\r
final long startMillis = System.currentTimeMillis();\r
- Log.d(LOG, "Constructing index for lang=" + index.sortLanguage.getSymbol());\r
+ \r
+ TransliteratorManager.init(new TransliteratorManager.Callback() {\r
+ @Override\r
+ public void onTransliteratorReady() {\r
+ uiHandler.post(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ onSearchTextChange(searchText.getText().toString());\r
+ }\r
+ });\r
+ }\r
+ });\r
+ \r
for (final Index index : dictionary.indices) {\r
Log.d(LOG, "Starting collator load for lang=" + index.sortLanguage.getSymbol());\r
- final com.ibm.icu.text.Collator c = index.sortLanguage.getCollator();\r
+ \r
+ final com.ibm.icu.text.Collator c = index.sortLanguage.getCollator(); \r
if (c.compare("pre-print", "preppy") >= 0) {\r
Log.e(LOG, c.getClass()\r
+ " is buggy, lookups may not work properly.");\r
}\r
});\r
\r
- indexIndex = prefs.getInt(C.INDEX_INDEX, 0) % dictionary.indices.size();\r
- index = dictionary.indices.get(indexIndex);\r
- setListAdapter(new IndexAdapter(index));\r
- \r
+\r
setContentView(R.layout.dictionary_activity);\r
searchText = (EditText) findViewById(R.id.SearchText);\r
langButton = (Button) findViewById(R.id.LangButton);\r
if (!searchText.isFocused()) {\r
// TODO: don't do this if multi words are entered.\r
final RowBase row = (RowBase) getListAdapter().getItem(position);\r
+ Log.d(LOG, "onItemSelected: " + row.index());\r
final TokenRow tokenRow = row.getTokenRow(true);\r
searchText.setText(tokenRow.getToken());\r
}\r
searchText.removeTextChangedListener(searchTextWatcher);\r
searchText.setText(dest.token);\r
jumpToRow(index.sortedIndexEntries.get(destIndexEntry).startRow);\r
- searchText.removeTextChangedListener(searchTextWatcher);\r
+ searchText.addTextChangedListener(searchTextWatcher);\r
}\r
\r
// --------------------------------------------------------------------------\r
final Index.IndexEntry searchResult = searchOperation.searchResult;\r
Log.d(LOG, "searchFinished: " + searchOperation + ", searchResult=" + searchResult);\r
\r
- jumpToRow(searchResult.startRow);\r
+ currentSearchOperation = null;\r
+\r
+ uiHandler.postDelayed(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ if (currentSearchOperation == null) {\r
+ jumpToRow(searchResult.startRow);\r
+ } else {\r
+ Log.d(LOG, "More coming, waiting for currentSearchOperation.");\r
+ }\r
+ }\r
+ }, 50);\r
\r
// if (!searchResult.success) {\r
// if (vibrator != null) {\r
// searchText.setSelection(searchResult.longestPrefixString.length());\r
// return;\r
// }\r
+ \r
}\r
\r
private final void jumpToRow(final int row) {\r
private View getView(PairEntry.Row row, ViewGroup parent) {\r
final TableLayout result = new TableLayout(parent.getContext());\r
final PairEntry entry = row.getEntry();\r
- final int rowCount = entry.pairs.length;\r
+ final int rowCount = entry.pairs.size();\r
for (int r = 0; r < rowCount; ++r) {\r
final TableRow tableRow = new TableRow(result.getContext());\r
\r
column2.setWidth(1);\r
\r
// TODO: color words by gender\r
- final String col1Text = index.swapPairEntries ? entry.pairs[r].lang2 : entry.pairs[r].lang1;\r
+ final Pair pair = entry.pairs.get(r);\r
+ final String col1Text = index.swapPairEntries ? pair.lang2 : pair.lang1;\r
column1.setText(col1Text, TextView.BufferType.SPANNABLE);\r
final Spannable col1Spannable = (Spannable) column1.getText();\r
\r
startPos += token.length();\r
}\r
\r
- final String col2Text = index.swapPairEntries ? entry.pairs[r].lang1 : entry.pairs[r].lang2;\r
+ final String col2Text = index.swapPairEntries ? pair.lang1 : pair.lang2;\r
column2.setText(col2Text, TextView.BufferType.NORMAL);\r
\r
result.addView(tableRow);\r
private static final long serialVersionUID = -1323165134846120269L;
final String name;
+ final int pairEntryStart;
- public EntrySource(final int index, final String name) {
+ public EntrySource(final int index, final String name, final int pairEntryStart) {
super(index);
this.name = name;
+ this.pairEntryStart = pairEntryStart;
}
@Override
public EntrySource read(RandomAccessFile raf, int readIndex)
throws IOException {
final String name = raf.readUTF();
- return new EntrySource(readIndex, name);
+ final int pairEntryStart = raf.readInt();
+ return new EntrySource(readIndex, name, pairEntryStart);
}
@Override
public void write(RandomAccessFile raf, EntrySource t) throws IOException {
raf.writeUTF(t.name);
+ raf.writeInt(t.pairEntryStart);
}
};
MULTIROW_TAIL_ONE_WORD(0),
WIKTIONARY_TITLE_MULTI_WORD(0),
+ WIKTIONARY_PRONUNCIATION(0),
WIKTIONARY_MEANING_MULTI_WORD(0),
WIKTIONARY_FORM_OF(0),
WIKTIONARY_EXAMPLE_HEADWORDS(0),
final String normalizerRules;
// Built from the two above.
- final Transliterator normalizer;
+ private Transliterator normalizer;
// persisted
public final List<IndexEntry> sortedIndexEntries;
sortedIndexEntries = new ArrayList<IndexEntry>();
rows = new ArrayList<RowBase>();
- normalizer = Transliterator.createFromRules("", normalizerRules, Transliterator.FORWARD);
+ normalizer = null;
+ }
+
+ public synchronized Transliterator normalizer() {
+ if (normalizer == null) {
+ normalizer = Transliterator.createFromRules("", normalizerRules, Transliterator.FORWARD);
+ }
+ return normalizer;
}
public Index(final Dictionary dict, final RandomAccessFile raf) throws IOException {
}
sortedIndexEntries = CachingList.create(RAFList.create(raf, IndexEntry.SERIALIZER, raf.getFilePointer()), CACHE_SIZE);
rows = CachingList.create(UniformRAFList.create(raf, new RowBase.Serializer(this), raf.getFilePointer()), CACHE_SIZE);
-
- normalizer = Transliterator.createFromRules("", normalizerRules, Transliterator.FORWARD);
}
@Override
public static final class IndexEntry implements RAFSerializable<Index.IndexEntry> {
public final String token;
+ private final String normalizedToken;
public final int startRow;
public final int numRows;
- private String normalizedToken;
static final RAFSerializer<IndexEntry> SERIALIZER = new RAFSerializer<IndexEntry> () {
@Override
t.write(raf);
}};
- public IndexEntry(final String token, final int startRow, final int numRows) {
+ public IndexEntry(final String token, final String normalizedToken, final int startRow, final int numRows) {
assert token.equals(token.trim());
assert token.length() > 0;
this.token = token;
+ this.normalizedToken = normalizedToken;
this.startRow = startRow;
this.numRows = numRows;
}
token = raf.readUTF();
startRow = raf.readInt();
numRows = raf.readInt();
+ final boolean hasNormalizedForm = raf.readBoolean();
+ normalizedToken = hasNormalizedForm ? raf.readUTF() : token;
}
public void write(RandomAccessFile raf) throws IOException {
raf.writeUTF(token);
raf.writeInt(startRow);
raf.writeInt(numRows);
+ final boolean hasNormalizedForm = !token.equals(normalizedToken);
+ raf.writeBoolean(hasNormalizedForm);
+ if (hasNormalizedForm) {
+ raf.writeUTF(normalizedToken);
+ }
}
public String toString() {
return String.format("%s@%d(%d)", token, startRow, numRows);
}
- public synchronized String normalizedToken(final Transliterator normalizer) {
- if (normalizedToken == null) {
- normalizedToken = normalizer.transform(token);
- }
+ public String normalizedToken() {
return normalizedToken;
}
}
public IndexEntry findInsertionPoint(String token, final AtomicBoolean interrupted) {
- token = normalizer.transliterate(token);
+ final Transliterator normalizer = normalizer();
+ if (TransliteratorManager.init(null)) {
+ token = normalizer.transliterate(token);
+ }
int start = 0;
int end = sortedIndexEntries.size();
}
final IndexEntry midEntry = sortedIndexEntries.get(mid);
- final int comp = sortCollator.compare(token, midEntry.normalizedToken(normalizer));
+ final int comp = sortCollator.compare(token, midEntry.normalizedToken());
if (comp == 0) {
final int result = windBackCase(token, mid, interrupted);
return sortedIndexEntries.get(result);
} else if (comp < 0) {
- System.out.println("Upper bound: " + midEntry + ", norm=" + midEntry.normalizedToken(normalizer) + ", mid=" + mid);
+ //System.out.println("Upper bound: " + midEntry + ", norm=" + midEntry.normalizedToken() + ", mid=" + mid);
end = mid;
} else {
- System.out.println("Lower bound: " + midEntry + ", norm=" + midEntry.normalizedToken(normalizer) + ", mid=" + mid);
+ //System.out.println("Lower bound: " + midEntry + ", norm=" + midEntry.normalizedToken() + ", mid=" + mid);
start = mid + 1;
}
}
// If we search for a substring of a string that's in there, return that.
int result = Math.min(start, sortedIndexEntries.size() - 1);
- result = windBackCase(sortedIndexEntries.get(result).normalizedToken(normalizer), result, interrupted);
+ result = windBackCase(sortedIndexEntries.get(result).normalizedToken(), result, interrupted);
return sortedIndexEntries.get(result);
}
// }
private final int windBackCase(final String token, int result, final AtomicBoolean interrupted) {
- while (result > 0 && sortedIndexEntries.get(result - 1).normalizedToken(normalizer).equals(token)) {
+ while (result > 0 && sortedIndexEntries.get(result - 1).normalizedToken().equals(token)) {
--result;
if (interrupted.get()) {
return result;
final String symbol;\r
final Locale locale;\r
\r
- final Collator collator;\r
+ private Collator collator;\r
\r
public Language(final Locale locale) {\r
this.symbol = locale.getLanguage();\r
this.locale = locale;\r
- this.collator = Collator.getInstance(locale);\r
- this.collator.setStrength(Collator.IDENTICAL);\r
\r
symbolToLangauge.put(symbol.toLowerCase(), this);\r
}\r
return symbol;\r
}\r
\r
- public Collator getCollator() {\r
+ public synchronized Collator getCollator() {\r
+ if (collator == null) {\r
+ this.collator = Collator.getInstance(locale);\r
+ this.collator.setStrength(Collator.IDENTICAL);\r
+ }\r
return collator;\r
}\r
\r
}\r
};\r
\r
- static {\r
- for (final String lang : Locale.getISOLanguages()) {\r
- if (lookup(lang) == null) {\r
- new Language(new Locale(lang));\r
- }\r
- }\r
- }\r
-\r
// ----------------------------------------------------------------\r
\r
- public static Language lookup(final String symbol) {\r
- return symbolToLangauge.get(symbol.toLowerCase());\r
+ public static synchronized Language lookup(final String symbol) {\r
+ Language lang = symbolToLangauge.get(symbol.toLowerCase());\r
+ if (lang == null) {\r
+ lang = new Language(new Locale(symbol));\r
+ }\r
+ return lang;\r
}\r
\r
}\r
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
+import java.util.ArrayList;
+import java.util.List;
import com.hughes.util.raf.RAFSerializable;
import com.hughes.util.raf.RAFSerializer;
public class PairEntry extends Entry implements RAFSerializable<PairEntry>, Comparable<PairEntry> {
- public static final class Pair {
-
- public final String lang1;
- public final String lang2;
-
- public Pair(final String lang1, final String lang2) {
- this.lang1 = lang1;
- this.lang2 = lang2;
- }
+ public final List<Pair> pairs;
- public Pair(final String lang1, final String lang2, final boolean swap) {
- this(swap ? lang2 : lang1, swap ? lang1 : lang2);
- }
-
- public String toString() {
- return lang1 + " :: " + lang2;
- }
-
- public String toStringTab() {
- return lang1 + "\t" + lang2;
- }
-
- public String get(int i) {
- if (i == 0) {
- return lang1;
- } else if (i == 1) {
- return lang2;
- }
- throw new IllegalArgumentException();
- }
-
- }
-
- public final Pair[] pairs;
-
- public PairEntry(final Pair[] pairs) {
- this.pairs = pairs;
+ public PairEntry() {
+ pairs = new ArrayList<Pair>(1);
}
-
+
public PairEntry(final String lang1, final String lang2) {
- this.pairs = new Pair[] { new Pair(lang1, lang2) };
+ pairs = new ArrayList<Pair>(1);
+ this.pairs.add(new Pair(lang1, lang2));
}
public PairEntry(final RandomAccessFile raf) throws IOException {
- pairs = new Pair[raf.readInt()];
- for (int i = 0; i < pairs.length; ++i) {
- pairs[i] = new Pair(raf.readUTF(), raf.readUTF());
+ final int size = raf.readInt();
+ pairs = new ArrayList<PairEntry.Pair>(size);
+ for (int i = 0; i < size; ++i) {
+ pairs.add(new Pair(raf.readUTF(), raf.readUTF()));
}
}
@Override
public void write(RandomAccessFile raf) throws IOException {
// TODO: this could be a short.
- raf.writeInt(pairs.length);
- for (int i = 0; i < pairs.length; ++i) {
- raf.writeUTF(pairs[i].lang1);
- raf.writeUTF(pairs[i].lang2);
+ raf.writeInt(pairs.size());
+ for (int i = 0; i < pairs.size(); ++i) {
+ raf.writeUTF(pairs.get(i).lang1);
+ raf.writeUTF(pairs.get(i).lang2);
}
}
@Override
public void print(PrintStream out) {
final PairEntry pairEntry = getEntry();
- for (int i = 0; i < pairEntry.pairs.length; ++i) {
- out.print((i == 0 ? " " : " ") + pairEntry.pairs[i]);
+ for (int i = 0; i < pairEntry.pairs.size(); ++i) {
+ out.print((i == 0 ? " " : " ") + pairEntry.pairs.get(i));
out.println();
}
}
public String getRawText(final boolean compact) {
if (compact) {
- return this.pairs[0].toStringTab();
+ return this.pairs.get(0).toStringTab();
}
final StringBuilder builder = new StringBuilder();
- for (int i = 0; i < this.pairs.length; ++i) {
+ for (int i = 0; i < this.pairs.size(); ++i) {
if (i > 0) {
builder.append(" | ");
}
- builder.append(this.pairs[i].lang1);
+ builder.append(this.pairs.get(i).lang1);
}
builder.append("\t");
- for (int i = 0; i < this.pairs.length; ++i) {
+ for (int i = 0; i < this.pairs.size(); ++i) {
if (i > 0) {
builder.append(" | ");
}
- builder.append(this.pairs[i].lang2);
+ builder.append(this.pairs.get(i).lang2);
}
return builder.toString();
}
public String toString() {
return getRawText(false);
}
+
+ // -----------------------------------------------------------------------
+
+ public static final class Pair {
+
+ public final String lang1;
+ public final String lang2;
+
+ public Pair(final String lang1, final String lang2) {
+ this.lang1 = lang1;
+ this.lang2 = lang2;
+ //assert lang1.trim().length() > 0 || lang2.trim().length() > 0 : "Empty pair!!!";
+ }
+
+ public Pair(final String lang1, final String lang2, final boolean swap) {
+ this(swap ? lang2 : lang1, swap ? lang1 : lang2);
+ }
+
+ public String toString() {
+ return lang1 + " :: " + lang2;
+ }
+
+ public String toStringTab() {
+ return lang1 + "\t" + lang2;
+ }
+
+ public String get(int i) {
+ if (i == 0) {
+ return lang1;
+ } else if (i == 1) {
+ return lang2;
+ }
+ throw new IllegalArgumentException();
+ }
+
+ }
}
}
if (rUp < index.rows.size()) {
final RowBase rowUp = index.rows.get(rUp);
- final TokenRow candidateUp = rowUp.getTokenRow(false);
+ TokenRow candidateUp = rowUp.getTokenRow(false);
if (candidateUp != null) {
+ // Did we hit the next set of TokenRows?
+ if (candidateUp.index() > this.index()) {
+ final int tokenIndex = index.sortedIndexEntries.get(candidateUp.referenceIndex - 1).startRow;
+ candidateUp = (TokenRow) index.rows.get(tokenIndex);
+ }
for (--rUp; rUp >= index(); --rUp) {
index.rows.get(rUp).setTokenRow(candidateUp);
}
@Override
public void print(final PrintStream out) {
- out.println(getToken());
+ out.println("===" + getToken() + "===");
}
@Override
--- /dev/null
+package com.hughes.android.dictionary.engine;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.util.Log;
+
+import com.ibm.icu.text.Transliterator;
+
+public class TransliteratorManager {
+
+ private static boolean starting = false;
+ private static boolean ready = false;
+
+ private static List<Callback> callbacks = new ArrayList<TransliteratorManager.Callback>();
+
+ public static synchronized boolean init(final Callback callback) {
+ if (ready) {
+ return true;
+ }
+ if (callback != null) {
+ callbacks.add(callback);
+ }
+ if (!starting) {
+ starting = true;
+ new Thread(init).start();
+ }
+ return false;
+ }
+
+ private static final Runnable init = new Runnable() {
+ @Override
+ public void run() {
+ System.out.println("Starting Transliterator load.");
+ final String transliterated =
+ Transliterator.createFromRules("", ":: Any-Latin; :: Lower; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC ;",
+ Transliterator.FORWARD).transliterate("Îñţérñåţîöñåļîžåţîờñ");
+ if (!"internationalization".equals(transliterated)) {
+ System.out.println("Wrong transliteratation: " + transliterated);
+ }
+
+ final List<Callback> callbacks = new ArrayList<TransliteratorManager.Callback>();
+ synchronized (TransliteratorManager.class) {
+ callbacks.addAll(TransliteratorManager.callbacks);
+ ready = true;
+ }
+ for (final Callback callback : callbacks) {
+ callback.onTransliteratorReady();
+ }
+ }
+ };
+
+
+ public interface Callback {
+ void onTransliteratorReady();
+ }
+
+}