<activity android:name=".DictionaryActivity" />
<activity android:name=".DictionaryEditActivity" />
<activity android:name=".AboutActivity" />
+ <activity android:name=".HelpActivity" />
<activity android:name=".PreferenceActivity" />
<activity android:name=".DownloadActivity"
android:configChanges="keyboardHidden|orientation"/>
</head>
<body>
<!-- Don't use links in the text below, it crashes the app. -->
- <h1>Dictionary manager</h1>
+ <h2>Dictionary manager</h2>
This screen lists all the available and installed dictionaries.
<ul>
<li> Click a dictionary to download or open it.
<li> Long-click on a dictionary to move it to the top of the list or delete it.
</ul>
- <h1>Dictionary</h1>
+ <h2>Dictionary</h2>
This screen shows dictionary entries. This is a massive list that you can
scroll all the way through, or you can type in the search text to jump
there in the list. Entries in the list are filed in multiple places,
under all relevant words.
- <p> What to do:
+ <h3>Searching</h3>
<ul>
<li> Type a single word to search for it (multi-word searches not yet supported).
+ <li> QuickDic tries to sort words using a romanized transliteration, so you can try searching for non-Latin words using the Latin alphabet.
+ </ul>
+ <h3>Other</h3>
+ <ul>
<li> If the search text box isn't focused, you can start typing and its contents will be replaced.
<li> Click the button to the right of the search box to switch dictionary directions: EN->DE to DE->EN.
<li> Long-click the button to the right of the search box to pick a new dictionary.
<string name="searchText">Search Text</string>
<string name="selectDictionary">Select dictionary…</string>
<string name="switchToLanguage">Switch to %s</string>
- <string name="preferences">Preferences…</string>
- <string name="about">About QuickDic…</string>
<string name="addToWordList">Add to word list: %s</string>
<string name="searchForSelection">Find: %s</string>
<string name="failedAddingToWordList">Failure adding to word list: %s</string>
<string name="downloading">Downloading: %1$,d of %2$,d bytes.</string>
<string name="unzipping">Unzipping: %1$,d of %2$,d bytes.</string>
<string name="downloadFinished">Finished: %,d bytes.</string>
- <string name="errorDownloadingFile">"Error downloading file: \n%s"</string>
+ <string name="errorDownloadingFile">Error downloading file: \n%s</string>
+ <!-- Global. -->
+ <string name="about">About QuickDic…</string>
+ <string name="preferences">Preferences…</string>
+ <string name="help">Help</string>
+
+
<!-- Preferences -->
<string name="wordListFileKey">wordListFile</string>
<string name="wordListFileTitle">Word list file</string>
return;\r
}\r
\r
- Log.d(LOG, "Loading index.");\r
indexIndex = intent.getIntExtra(C.INDEX_INDEX, 0) % dictionary.indices.size();\r
+ Log.d(LOG, "Loading index " + indexIndex);\r
index = dictionary.indices.get(indexIndex);\r
setListAdapter(new IndexAdapter(index));\r
\r
changeIndex((indexIndex + 1)% dictionary.indices.size());\r
}\r
\r
- static class OpenIndexButton extends Button implements OnClickListener {\r
-\r
- final Activity activity;\r
- final Intent intent;\r
-\r
- public OpenIndexButton(final Context context, final Activity activity, final String text, final Intent intent) {\r
- super(context);\r
- this.activity = activity;\r
- this.intent = intent;\r
- setOnClickListener(this);\r
- setText(text, BufferType.NORMAL);\r
- }\r
-\r
- @Override\r
- public void onClick(View v) {\r
- activity.finish();\r
- getContext().startActivity(intent);\r
- }\r
- \r
- }\r
-\r
void onLanguageButtonLongClick(final Context context) {\r
final Dialog dialog = new Dialog(context);\r
dialog.setContentView(R.layout.select_dictionary_dialog);\r
updateLangButton();\r
searchText.requestFocus(); // Otherwise, nothing may happen.\r
onSearchTextChange(searchText.getText().toString());\r
+ setDictionaryPrefs(this, dictFile, indexIndex, searchText.getText().toString());\r
}\r
\r
void onUpDownButton(final boolean up) {\r
@Override\r
protected void onListItemClick(ListView l, View v, int row, long id) {\r
defocusSearchText();\r
- \r
if (clickOpensContextMenu && dictRaf != null) {\r
openContextMenu(v);\r
}\r
public View getView(int position, final View convertView, ViewGroup parent) {\r
final RowBase row = index.rows.get(position);\r
if (row instanceof PairEntry.Row) {\r
- return getView((PairEntry.Row) row, parent, convertView);\r
+ return getView(position, (PairEntry.Row) row, parent, convertView);\r
} else if (row instanceof TokenRow) {\r
return getView((TokenRow) row, parent, convertView);\r
} else {\r
}\r
}\r
\r
- private View getView(PairEntry.Row row, ViewGroup parent, final View convertView) {\r
+ private View getView(final int position, PairEntry.Row row, ViewGroup parent, final View convertView) {\r
final TableLayout result = new TableLayout(parent.getContext());\r
final PairEntry entry = row.getEntry();\r
final int rowCount = entry.pairs.size();\r
col2.setOnLongClickListener(textViewLongClickListenerIndex1);\r
}\r
\r
+ // Because we have a Button inside a ListView row:\r
+ // http://groups.google.com/group/android-developers/browse_thread/thread/3d96af1530a7d62a?pli=1\r
+ result.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);\r
+ result.setClickable(true);\r
+ result.setFocusable(true);\r
+ result.setLongClickable(true);\r
+ result.setBackgroundResource(android.R.drawable.menuitem_background);\r
+ result.setOnClickListener(new TextView.OnClickListener() {\r
+ @Override\r
+ public void onClick(View v) {\r
+ DictionaryActivity.this.onListItemClick(null, v, position, position);\r
+ }\r
+ });\r
+ \r
result.addView(tableRow);\r
}\r
\r
public void onCreateGlobalOptionsMenu(
final Context context, final Menu menu) {
- final MenuItem help = menu.add(getString(R.string.about));
- help.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ final MenuItem about = menu.add(getString(R.string.about));
+ about.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(final MenuItem menuItem) {
- startActivity(HelpActivity.getLaunchIntent());
+ final Intent intent = new Intent().setClassName(AboutActivity.class
+ .getPackage().getName(), AboutActivity.class.getCanonicalName());
+ context.startActivity(intent);
return false;
}
});
- final MenuItem about = menu.add(getString(R.string.about));
- about.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ final MenuItem help = menu.add(getString(R.string.help));
+ help.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(final MenuItem menuItem) {
- final Intent intent = new Intent().setClassName(AboutActivity.class
- .getPackage().getName(), AboutActivity.class.getCanonicalName());
- startActivity(intent);
+ context.startActivity(HelpActivity.getLaunchIntent());
return false;
}
});
PreferenceActivity.prefsMightHaveChanged = true;
final Intent intent = new Intent().setClassName(PreferenceActivity.class
.getPackage().getName(), PreferenceActivity.class.getCanonicalName());
- startActivity(intent);
+ context.startActivity(intent);
return false;
}
});
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
-import android.view.ViewGroup.LayoutParams;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.Button;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
downloadButton.setLayoutParams(layoutParams);
row.addView(downloadButton);
+ } else {
+ final ImageView checkMark = new ImageView(parent.getContext());
+ checkMark.setImageResource(android.R.drawable.checkbox_on_background);
+ row.addView(checkMark);
}
final TextView textView = new TextView(parent.getContext());
super.onCreate(savedInstanceState);\r
setContentView(R.layout.help_activity);\r
final String html = StringUtil.readToString(getResources().openRawResource(R.raw.help));\r
- final WebView webView = (WebView) findViewById(R.layout.help_activity);\r
+ final WebView webView = (WebView) findViewById(R.id.helpWebView);\r
webView.loadData(html, "text/html", "utf-8");\r
}\r
\r
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.EnumMap;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import com.hughes.android.dictionary.DictionaryInfo;
}
public IndexEntry findInsertionPoint(String token, final AtomicBoolean interrupted) {
- if (TransliteratorManager.init(null)) {
- final Transliterator normalizer = normalizer();
- token = normalizer.transliterate(token);
- } else {
- // Do our best since the Transliterators aren't up yet.
- token = token.toLowerCase();
- }
+ final int index = findInsertionPointIndex(token, interrupted);
+ return index != -1 ? sortedIndexEntries.get(index) : null;
+ }
+
+ public int findInsertionPointIndex(String token, final AtomicBoolean interrupted) {
+ token = normalizeToken(token);
int start = 0;
int end = sortedIndexEntries.size();
while (start < end) {
final int mid = (start + end) / 2;
if (interrupted.get()) {
- return null;
+ return -1;
}
final IndexEntry midEntry = sortedIndexEntries.get(mid);
final int comp = sortCollator.compare(token, midEntry.normalizedToken());
if (comp == 0) {
final int result = windBackCase(token, mid, interrupted);
- return sortedIndexEntries.get(result);
+ return result;
} else if (comp < 0) {
//System.out.println("Upper bound: " + midEntry + ", norm=" + midEntry.normalizedToken() + ", mid=" + mid);
end = mid;
// 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(), result, interrupted);
- return sortedIndexEntries.get(result);
+ return result;
}
private final int windBackCase(final String token, int result, final AtomicBoolean interrupted) {
public IndexInfo getIndexInfo() {
return new DictionaryInfo.IndexInfo(shortName, sortedIndexEntries.size(), mainTokenCount);
}
+
+ final List<RowBase> multiWordSearch(final List<String> searchTokens, final AtomicBoolean interrupted) {
+ final List<RowBase> result = new ArrayList<RowBase>();
+
+ // Heuristic: use the longest searchToken as the base.
+ String searchToken = null;
+ for (int i = 0; i < searchTokens.size(); ++i) {
+ if (interrupted.get()) { return null; }
+ final String normalized = normalizeToken(searchTokens.get(i));
+ // Normalize them all.
+ searchTokens.set(i, normalized);
+ if (searchToken == null || normalized.length() > searchToken.length()) {
+ searchToken = normalized;
+ }
+ }
+
+ final int insertionPointIndex = findInsertionPointIndex(searchToken, interrupted);
+ if (insertionPointIndex == -1 || interrupted.get()) {
+ return null;
+ }
+
+ // The things that match.
+ // TODO: use a key
+ final Map<RowMatchType,Set<RowBase>> matches = new EnumMap<RowMatchType, Set<RowBase>>(RowMatchType.class);
+ for (final RowMatchType rowMatchType : RowMatchType.values()) {
+ matches.put(rowMatchType, new LinkedHashSet<RowBase>());
+ }
+
+ for (int index = insertionPointIndex; index < sortedIndexEntries.size(); ++index) {
+ if (interrupted.get()) { return null; }
+ final IndexEntry indexEntry = sortedIndexEntries.get(index);
+ if (!indexEntry.normalizedToken.equals(searchToken)) {
+ break;
+ }
+
+ for (int rowIndex = indexEntry.startRow; rowIndex < indexEntry.startRow + indexEntry.numRows; ++rowIndex) {
+ if (interrupted.get()) { return null; }
+ final RowBase row = rows.get(rowIndex);
+ final RowMatchType matchType = row.matches(searchTokens, normalizer, swapPairEntries);
+ if (matchType != RowMatchType.NO_MATCH) {
+ matches.get(matchType).add(row);
+ }
+ }
+ }
+
+ for (final Set<RowBase> rows : matches.values()) {
+ result.addAll(rows);
+ }
+
+ return result;
+ }
+
+ private String normalizeToken(final String searchToken) {
+ if (TransliteratorManager.init(null)) {
+ final Transliterator normalizer = normalizer();
+ return normalizer.transliterate(searchToken);
+ } else {
+ // Do our best since the Transliterators aren't up yet.
+ return searchToken.toLowerCase();
+ }
+ }
}
\ No newline at end of file
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
+import java.util.regex.Pattern;
import com.hughes.util.raf.RAFSerializable;
import com.hughes.util.raf.RAFSerializer;
+import com.ibm.icu.text.Transliterator;
public class PairEntry extends AbstractEntry implements RAFSerializable<PairEntry>, Comparable<PairEntry> {
final Index index) {
super(referenceIndex, thisRowIndex, index);
}
+
+ @Override
+ public String toString() {
+ return getRawText(false);
+ }
public PairEntry getEntry() {
return index.dict.pairEntries.get(referenceIndex);
final PairEntry pairEntry = getEntry();
return pairEntry.getRawText(compact);
}
+
+ @Override
+ public RowMatchType matches(final List<String> searchTokens, final Transliterator normalizer, final boolean swapPairEntries) {
+ final int side = swapPairEntries ? 1 : 0;
+ final List<Pair> pairs = getEntry().pairs;
+ final String[] pairSides = new String[pairs.size()];
+ for (int i = 0; i < pairs.size(); ++i) {
+ pairSides[i] = normalizer.transform(pairs.get(i).get(side));
+ }
+ for (int i = searchTokens.size() - 1; i >= 0; --i) {
+ final String searchToken = searchTokens.get(i);
+ boolean found = false;
+ for (final String pairSide : pairSides) {
+ found |= pairSide.contains(searchToken);
+ }
+ if (!found) {
+ return RowMatchType.NO_MATCH;
+ }
+ }
+ final StringBuilder regex = new StringBuilder();
+ for (final String searchToken : searchTokens) {
+ if (regex.length() > 0) {
+ regex.append("[\\s]*");
+ }
+ regex.append(Pattern.quote(searchToken));
+ }
+ final Pattern pattern = Pattern.compile(regex.toString());
+ for (final String pairSide : pairSides) {
+ if (pattern.matcher(pairSide).matches()) {
+ return RowMatchType.ORDERED_MATCH;
+ }
+ }
+ return RowMatchType.BAG_OF_WORDS_MATCH;
+ }
}
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
+import java.util.List;
import com.hughes.util.IndexedObject;
import com.hughes.util.raf.RAFListSerializer;
+import com.ibm.icu.text.Transliterator;
public abstract class RowBase extends IndexedObject {
/**
public abstract void print(PrintStream out);
public abstract String getRawText(final boolean compact);
+
+ public abstract RowMatchType matches(final List<String> searchTokens, final Transliterator normalizer, boolean swapPairEntries);
// RowBase must manage "disk-based" polymorphism. All other polymorphism is
// dealt with in the normal manner.
raf.writeInt(t.referenceIndex);
}
}
-
+
}
--- /dev/null
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.hughes.android.dictionary.engine;
+
+public enum RowMatchType {
+
+ ORDERED_MATCH,
+ BAG_OF_WORDS_MATCH,
+ NO_MATCH
+
+}
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
+import java.util.List;
import com.hughes.util.raf.RAFSerializable;
import com.hughes.util.raf.RAFSerializer;
+import com.ibm.icu.text.Transliterator;
public class TextEntry extends AbstractEntry implements RAFSerializable<TextEntry> {
public TextEntry(final Dictionary dictionary, final RandomAccessFile raf) throws IOException {
super(dictionary, raf);
text = raf.readUTF();
+ throw new RuntimeException();
}
@Override
public void write(RandomAccessFile raf) throws IOException {
public String getRawText(boolean compact) {
return getEntry().text;
}
+
+ @Override
+ public RowMatchType matches(List<String> searchTokens, Transliterator normalizer, boolean swapPairEntries) {
+ return null;
+ }
}
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
+import java.util.List;
+
+import com.ibm.icu.text.Transliterator;
public class TokenRow extends RowBase {
return getToken();
}
+ @Override
+ public RowMatchType matches(List<String> searchTokens, Transliterator normalizer, boolean swapPairEntries) {
+ return RowMatchType.NO_MATCH;
+ }
+
}