+ dictRaf = null;
+ }
+
+ // --------------------------------------------------------------------------
+ // Buttons
+ // --------------------------------------------------------------------------
+
+ private void showKeyboard() {
+ // For some reason, this doesn't always work the first time.
+ // One way to replicate the problem:
+ // Press the "task switch" button repeatedly to pause and resume
+ for (int delay = 1; delay <= 101; delay += 100) {
+ searchView.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ Log.d(LOG, "Trying to show soft keyboard.");
+ final boolean searchTextHadFocus = searchView.hasFocus();
+ searchView.requestFocusFromTouch();
+ final InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ manager.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT);
+ if (!searchTextHadFocus) {
+ defocusSearchText();
+ }
+ }
+ }, delay);
+ }
+ }
+
+ private void hideKeyboard() {
+ Log.d(LOG, "Hide soft keyboard.");
+ searchView.clearFocus();
+ InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ manager.hideSoftInputFromWindow(searchView.getWindowToken(), 0);
+ }
+
+ void updateLangButton() {
+ final int flagId = IsoUtils.INSTANCE.getFlagIdForIsoCode(index.shortName);
+ if (flagId != 0) {
+ languageButton.setImageResource(flagId);
+ } else {
+ if (indexIndex % 2 == 0) {
+ languageButton.setImageResource(android.R.drawable.ic_media_next);
+ } else {
+ languageButton.setImageResource(android.R.drawable.ic_media_previous);
+ }
+ }
+ updateTTSLanguage(indexIndex);
+ }
+
+ private void updateTTSLanguage(int i) {
+ if (!ttsReady || index == null || textToSpeech == null) {
+ Log.d(LOG, "Can't updateTTSLanguage.");
+ return;
+ }
+ final Locale locale = new Locale(dictionary.indices.get(i).sortLanguage.getIsoCode());
+ Log.d(LOG, "Setting TTS locale to: " + locale);
+ try {
+ final int ttsResult = textToSpeech.setLanguage(locale);
+ if (ttsResult != TextToSpeech.LANG_AVAILABLE &&
+ ttsResult != TextToSpeech.LANG_COUNTRY_AVAILABLE) {
+ Log.e(LOG, "TTS not available in this language: ttsResult=" + ttsResult);
+ }
+ } catch (Exception e) {
+ Toast.makeText(this, getString(R.string.TTSbroken), Toast.LENGTH_LONG).show();
+ }
+ }
+
+ public void onSearchButtonClick(View dummy) {
+ if (!searchView.hasFocus()) {
+ searchView.requestFocus();
+ }
+ if (searchView.getQuery().toString().length() > 0) {
+ searchView.setQuery("", false);
+ }
+ showKeyboard();
+ searchView.setIconified(false);
+ }
+
+ public void onLanguageButtonClick(View dummy) {
+ if (dictionary.indices.size() == 1) {
+ // No need to work to switch indices.
+ return;
+ }
+ if (currentSearchOperation != null) {
+ currentSearchOperation.interrupted.set(true);
+ currentSearchOperation = null;
+ }
+ setIndexAndSearchText((indexIndex + 1) % dictionary.indices.size(),
+ searchView.getQuery().toString(), false);
+ }
+
+ void onLanguageButtonLongClick(final Context context) {
+ final Dialog dialog = new Dialog(context);
+ dialog.setContentView(R.layout.select_dictionary_dialog);
+ dialog.setTitle(R.string.selectDictionary);
+
+ final List<DictionaryInfo> installedDicts = application.getDictionariesOnDevice(null);
+
+ ListView listView = (ListView) dialog.findViewById(android.R.id.list);
+ final Button button = new Button(listView.getContext());
+ final String name = getString(R.string.dictionaryManager);
+ button.setText(name);
+ final IntentLauncher intentLauncher = new IntentLauncher(listView.getContext(),
+ DictionaryManagerActivity.getLaunchIntent(getApplicationContext())) {
+ @Override
+ protected void onGo() {
+ dialog.dismiss();
+ DictionaryActivity.this.finish();
+ }
+ };
+ button.setOnClickListener(intentLauncher);
+ listView.addHeaderView(button);
+
+ listView.setItemsCanFocus(true);
+ listView.setAdapter(new BaseAdapter() {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final DictionaryInfo dictionaryInfo = getItem(position);
+
+ final LinearLayout result = new LinearLayout(parent.getContext());
+
+ for (int i = 0; dictionaryInfo.indexInfos != null && i < dictionaryInfo.indexInfos.size(); ++i) {
+ final IndexInfo indexInfo = dictionaryInfo.indexInfos.get(i);
+ final View button = IsoUtils.INSTANCE.createButton(parent.getContext(),
+ dictionaryInfo, indexInfo, application.languageButtonPixels);
+ final IntentLauncher intentLauncher = new IntentLauncher(parent.getContext(),
+ getLaunchIntent(getApplicationContext(),
+ application.getPath(dictionaryInfo.uncompressedFilename),
+ indexInfo.shortName, searchView.getQuery().toString())) {
+ @Override
+ protected void onGo() {
+ dialog.dismiss();
+ DictionaryActivity.this.finish();
+ }
+ };
+ button.setOnClickListener(intentLauncher);
+ if (i == indexIndex && dictFile != null &&
+ dictFile.getName().equals(dictionaryInfo.uncompressedFilename)) {
+ button.setPressed(true);
+ }
+ result.addView(button);
+ }
+
+ final TextView nameView = new TextView(parent.getContext());
+ final String name = application
+ .getDictionaryName(dictionaryInfo.uncompressedFilename);
+ nameView.setText(name);
+ final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ layoutParams.width = 0;
+ layoutParams.weight = 1.0f;
+ nameView.setLayoutParams(layoutParams);
+ nameView.setGravity(Gravity.CENTER_VERTICAL);
+ result.addView(nameView);
+ return result;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public DictionaryInfo getItem(int position) {
+ return installedDicts.get(position);
+ }
+
+ @Override
+ public int getCount() {
+ return installedDicts.size();
+ }
+ });
+ dialog.show();
+ }
+
+ void onUpDownButton(final boolean up) {
+ if (isFiltered()) {
+ return;
+ }
+ final int firstVisibleRow = getListView().getFirstVisiblePosition();
+ final RowBase row = index.rows.get(firstVisibleRow);
+ final TokenRow tokenRow = row.getTokenRow(true);
+ final int destIndexEntry;
+ if (up) {
+ if (row != tokenRow) {
+ destIndexEntry = tokenRow.referenceIndex;
+ } else {
+ destIndexEntry = Math.max(tokenRow.referenceIndex - 1, 0);
+ }
+ } else {
+ // Down
+ destIndexEntry = Math.min(tokenRow.referenceIndex + 1, index.sortedIndexEntries.size() - 1);
+ }
+ final Index.IndexEntry dest = index.sortedIndexEntries.get(destIndexEntry);
+ Log.d(LOG, "onUpDownButton, destIndexEntry=" + dest.token);
+ setSearchText(dest.token, false);
+ jumpToRow(index.sortedIndexEntries.get(destIndexEntry).startRow);
+ defocusSearchText();
+ }
+
+ void onRandomWordButton() {
+ int destIndexEntry = rand.nextInt(index.sortedIndexEntries.size());
+ final Index.IndexEntry dest = index.sortedIndexEntries.get(destIndexEntry);
+ setSearchText(dest.token, false);
+ jumpToRow(index.sortedIndexEntries.get(destIndexEntry).startRow);
+ defocusSearchText();
+ }
+
+ // --------------------------------------------------------------------------
+ // Options Menu
+ // --------------------------------------------------------------------------
+
+ final Random random = new Random();
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+
+ if (PreferenceManager.getDefaultSharedPreferences(this)
+ .getBoolean(getString(R.string.showPrevNextButtonsKey), true)) {
+ // Next word.
+ nextWordMenuItem = menu.add(getString(R.string.nextWord))
+ .setIcon(R.drawable.arrow_down_float);
+ MenuItemCompat.setShowAsAction(nextWordMenuItem, MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ nextWordMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ onUpDownButton(false);
+ return true;
+ }
+ });
+
+ // Previous word.
+ previousWordMenuItem = menu.add(getString(R.string.previousWord))
+ .setIcon(R.drawable.arrow_up_float);
+ MenuItemCompat.setShowAsAction(previousWordMenuItem, MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ previousWordMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ onUpDownButton(true);
+ return true;
+ }
+ });
+ }
+
+ randomWordMenuItem = menu.add(getString(R.string.randomWord));
+ randomWordMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ onRandomWordButton();
+ return true;
+ }
+ });
+
+ application.onCreateGlobalOptionsMenu(this, menu);
+
+ {
+ final MenuItem dictionaryManager = menu.add(getString(R.string.dictionaryManager));
+ MenuItemCompat.setShowAsAction(dictionaryManager, MenuItem.SHOW_AS_ACTION_NEVER);
+ dictionaryManager.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(final MenuItem menuItem) {
+ startActivity(DictionaryManagerActivity.getLaunchIntent(getApplicationContext()));
+ finish();
+ return false;
+ }
+ });
+ }
+
+ {
+ final MenuItem aboutDictionary = menu.add(getString(R.string.aboutDictionary));
+ MenuItemCompat.setShowAsAction(aboutDictionary, MenuItem.SHOW_AS_ACTION_NEVER);
+ aboutDictionary.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(final MenuItem menuItem) {
+ final Context context = getListView().getContext();
+ final Dialog dialog = new Dialog(context);
+ dialog.setContentView(R.layout.about_dictionary_dialog);
+ final TextView textView = (TextView) dialog.findViewById(R.id.text);
+
+ dialog.setTitle(dictFileTitleName);
+
+ final StringBuilder builder = new StringBuilder();
+ final DictionaryInfo dictionaryInfo = dictionary.getDictionaryInfo();
+ if (dictionaryInfo != null) {
+ try {
+ dictionaryInfo.uncompressedBytes = dictRaf.size();
+ } catch (IOException e) {
+ }
+ builder.append(dictionaryInfo.dictInfo).append("\n\n");
+ if (dictFile != null) {
+ builder.append(getString(R.string.dictionaryPath, dictFile.getPath()))
+ .append("\n");
+ }
+ builder.append(
+ getString(R.string.dictionarySize, dictionaryInfo.uncompressedBytes))
+ .append("\n");
+ builder.append(
+ getString(R.string.dictionaryCreationTime,
+ dictionaryInfo.creationMillis)).append("\n");
+ for (final IndexInfo indexInfo : dictionaryInfo.indexInfos) {
+ builder.append("\n");
+ builder.append(getString(R.string.indexName, indexInfo.shortName))
+ .append("\n");
+ builder.append(
+ getString(R.string.mainTokenCount, indexInfo.mainTokenCount))
+ .append("\n");
+ }
+ builder.append("\n");
+ builder.append(getString(R.string.sources)).append("\n");
+ for (final EntrySource source : dictionary.sources) {
+ builder.append(
+ getString(R.string.sourceInfo, source.getName(),
+ source.getNumEntries())).append("\n");
+ }
+ }
+ textView.setText(builder.toString());
+
+ dialog.show();
+ final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
+ layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
+ layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;
+ dialog.getWindow().setAttributes(layoutParams);
+ return false;
+ }
+ });
+ }
+
+ return true;
+ }
+
+ // --------------------------------------------------------------------------
+ // Context Menu + clicks
+ // --------------------------------------------------------------------------
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ final AdapterContextMenuInfo adapterContextMenuInfo = (AdapterContextMenuInfo) menuInfo;
+ final RowBase row = (RowBase) getListAdapter().getItem(adapterContextMenuInfo.position);
+
+ if (clickOpensContextMenu && (row instanceof HtmlEntry.Row ||
+ (row instanceof TokenRow && ((TokenRow)row).getIndexEntry().htmlEntries.size() > 0))) {
+ final List<HtmlEntry> html = row instanceof TokenRow ? ((TokenRow)row).getIndexEntry().htmlEntries : Collections.singletonList(((HtmlEntry.Row)row).getEntry());
+ final String highlight = row instanceof HtmlEntry.Row ? ((HtmlEntry.Row)row).getTokenRow(true).getToken() : null;
+ final MenuItem open = menu.add("Open");
+ open.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ showHtml(html, highlight);
+ return false;
+ }
+ });
+ }
+
+ final android.view.MenuItem addToWordlist = menu.add(getString(R.string.addToWordList,
+ wordList.getName()));
+ addToWordlist
+ .setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(android.view.MenuItem item) {
+ onAppendToWordList(row);
+ return false;
+ }
+ });
+
+ final android.view.MenuItem share = menu.add("Share");
+ share.setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(android.view.MenuItem item) {
+ Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND);
+ shareIntent.setType("text/plain");
+ shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, row.getTokenRow(true)
+ .getToken());
+ shareIntent.putExtra(android.content.Intent.EXTRA_TEXT,
+ row.getRawText(saveOnlyFirstSubentry));
+ startActivity(shareIntent);
+ return false;
+ }
+ });
+
+ final android.view.MenuItem copy = menu.add(android.R.string.copy);
+ copy.setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(android.view.MenuItem item) {
+ onCopy(row);
+ return false;
+ }
+ });
+
+ if (selectedSpannableText != null) {
+ final String selectedText = selectedSpannableText;
+ final android.view.MenuItem searchForSelection = menu.add(getString(
+ R.string.searchForSelection,
+ selectedSpannableText));
+ searchForSelection
+ .setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(android.view.MenuItem item) {
+ jumpToTextFromHyperLink(selectedText, selectedSpannableIndex);
+ return false;
+ }
+ });
+ // Rats, this won't be shown:
+ //searchForSelection.setIcon(R.drawable.abs__ic_search);
+ }
+
+ if ((row instanceof TokenRow || selectedSpannableText != null) && ttsReady) {
+ final android.view.MenuItem speak = menu.add(R.string.speak);
+ final String textToSpeak = row instanceof TokenRow ? ((TokenRow) row).getToken() : selectedSpannableText;
+ updateTTSLanguage(row instanceof TokenRow ? indexIndex : selectedSpannableIndex);
+ speak.setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(android.view.MenuItem item) {
+ textToSpeech.speak(textToSpeak, TextToSpeech.QUEUE_FLUSH,
+ new HashMap<String, String>());
+ return false;
+ }
+ });
+ }
+ if (row instanceof PairEntry.Row && ttsReady) {
+ final List<Pair> pairs = ((PairEntry.Row)row).getEntry().pairs;
+ final MenuItem speakLeft = menu.add(R.string.speak_left);
+ speakLeft.setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(android.view.MenuItem item) {
+ int idx = index.swapPairEntries ? 1 : 0;
+ updateTTSLanguage(idx);
+ String text = "";
+ for (Pair p : pairs) text += p.get(idx);
+ text = text.replaceAll("\\{[^{}]*\\}", "").replace("{", "").replace("}", "");
+ textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH,
+ new HashMap<String, String>());
+ return false;
+ }
+ });
+ final MenuItem speakRight = menu.add(R.string.speak_right);
+ speakRight.setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(android.view.MenuItem item) {
+ int idx = index.swapPairEntries ? 0 : 1;
+ updateTTSLanguage(idx);
+ String text = "";
+ for (Pair p : pairs) text += p.get(idx);
+ text = text.replaceAll("\\{[^{}]*\\}", "").replace("{", "").replace("}", "");
+ textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH,
+ new HashMap<String, String>());
+ return false;
+ }
+ });
+ }
+ }
+
+ private void jumpToTextFromHyperLink(
+ final String selectedText, final int defaultIndexToUse) {
+ int indexToUse = -1;
+ int numFound = 0;
+ for (int i = 0; i < dictionary.indices.size(); ++i) {
+ final Index index = dictionary.indices.get(i);
+ if (indexPrepFinished) {
+ System.out.println("Doing index lookup: on " + selectedText);
+ final IndexEntry indexEntry = index.findExact(selectedText);
+ if (indexEntry != null) {
+ final TokenRow tokenRow = index.rows.get(indexEntry.startRow)
+ .getTokenRow(false);
+ if (tokenRow != null && tokenRow.hasMainEntry) {
+ indexToUse = i;
+ ++numFound;
+ }
+ }
+ } else {
+ Log.w(LOG, "Skipping findExact on index " + index.shortName);
+ }
+ }
+ if (numFound != 1 || indexToUse == -1) {
+ indexToUse = defaultIndexToUse;
+ }
+ // Without this extra delay, the call to jumpToRow that this
+ // invokes doesn't always actually have any effect.
+ final int actualIndexToUse = indexToUse;
+ getListView().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ setIndexAndSearchText(actualIndexToUse, selectedText, true);
+ }
+ }, 100);
+ }
+
+ /**
+ * Called when user clicks outside of search text, so that they can start
+ * typing again immediately.
+ */
+ void defocusSearchText() {
+ // Log.d(LOG, "defocusSearchText");
+ // Request focus so that if we start typing again, it clears the text
+ // input.
+ getListView().requestFocus();
+
+ // Visual indication that a new keystroke will clear the search text.
+ // Doesn't seem to work unless searchText has focus.
+ // searchView.selectAll();
+ }
+
+ protected void onListItemClick(ListView l, View v, int rowIdx, long id) {
+ defocusSearchText();
+ if (clickOpensContextMenu && dictRaf != null) {
+ openContextMenu(v);
+ } else {
+ final RowBase row = (RowBase)getListAdapter().getItem(rowIdx);
+ if (!(row instanceof PairEntry.Row)) {
+ v.performClick();
+ }
+ }
+ }
+
+ @SuppressLint("SimpleDateFormat")
+ void onAppendToWordList(final RowBase row) {
+ defocusSearchText();
+
+ final StringBuilder rawText = new StringBuilder();
+ rawText.append(new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(new Date())).append("\t");
+ rawText.append(index.longName).append("\t");
+ rawText.append(row.getTokenRow(true).getToken()).append("\t");
+ rawText.append(row.getRawText(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 (Exception e) {
+ Log.e(LOG, "Unable to append to " + wordList.getAbsolutePath(), e);
+ Toast.makeText(this,
+ getString(R.string.failedAddingToWordList, wordList.getAbsolutePath()),
+ Toast.LENGTH_LONG).show();
+ }
+ return;
+ }
+
+ @SuppressWarnings("deprecation")
+ void onCopy(final RowBase row) {
+ defocusSearchText();
+
+ Log.d(LOG, "Copy, row=" + row);
+ final StringBuilder result = new StringBuilder();
+ result.append(row.getRawText(false));
+ final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ clipboardManager.setText(result.toString());
+ Log.d(LOG, "Copied: " + result);
+ }
+
+ @Override
+ public boolean onKeyDown(final int keyCode, final KeyEvent event) {
+ if (event.getUnicodeChar() != 0) {
+ if (!searchView.hasFocus()) {
+ setSearchText("" + (char) event.getUnicodeChar(), true);
+ searchView.requestFocus();
+ }
+ return true;
+ }
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ // Log.d(LOG, "Clearing dictionary prefs.");
+ // Pretend that we just autolaunched so that we won't do it again.
+ // DictionaryManagerActivity.lastAutoLaunchMillis =
+ // System.currentTimeMillis();
+ }
+ if (keyCode == KeyEvent.KEYCODE_ENTER) {
+ Log.d(LOG, "Trying to hide soft keyboard.");
+ final InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ View focus = getCurrentFocus();
+ if (focus != null) {
+ inputManager.hideSoftInputFromWindow(focus.getWindowToken(),
+ InputMethodManager.HIDE_NOT_ALWAYS);
+ }
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private void setIndexAndSearchText(int newIndex, String newSearchText, boolean hideKeyboard) {
+ Log.d(LOG, "Changing index to: " + newIndex);
+ if (newIndex == -1) {
+ Log.e(LOG, "Invalid index.");
+ newIndex = 0;
+ }
+ if (newIndex != indexIndex) {
+ indexIndex = newIndex;
+ index = dictionary.indices.get(indexIndex);
+ indexAdapter = new IndexAdapter(index);
+ setListAdapter(indexAdapter);
+ Log.d(LOG, "changingIndex, newLang=" + index.longName);
+ setDictionaryPrefs(this, dictFile, index.shortName);
+ updateLangButton();
+ }
+ setSearchText(newSearchText, true, hideKeyboard);
+ }
+
+ private void setSearchText(final String text, final boolean triggerSearch, boolean hideKeyboard) {
+ Log.d(LOG, "setSearchText, text=" + text + ", triggerSearch=" + triggerSearch);
+ // Disable the listener, because sometimes it doesn't work.
+ searchView.setOnQueryTextListener(null);
+ searchView.setQuery(text, false);
+ moveCursorToRight();
+ searchView.setOnQueryTextListener(onQueryTextListener);
+
+ if (triggerSearch) {
+ onSearchTextChange(text);
+ }
+
+ // We don't want to show virtual keyboard when we're changing searchView text programatically:
+ if (hideKeyboard) {
+ hideKeyboard();
+ }
+ }
+
+ private void setSearchText(final String text, final boolean triggerSearch) {
+ setSearchText(text, triggerSearch, true);
+ }
+
+ // private long cursorDelayMillis = 100;
+ private void moveCursorToRight() {
+ // if (searchText.getLayout() != null) {
+ // cursorDelayMillis = 100;
+ // // Surprising, but this can crash when you rotate...
+ // Selection.moveToRightEdge(searchView.getQuery(),
+ // searchText.getLayout());
+ // } else {
+ // uiHandler.postDelayed(new Runnable() {
+ // @Override
+ // public void run() {
+ // moveCursorToRight();
+ // }
+ // }, cursorDelayMillis);
+ // cursorDelayMillis = Math.min(10 * 1000, 2 * cursorDelayMillis);
+ // }
+ }
+
+ // --------------------------------------------------------------------------
+ // SearchOperation
+ // --------------------------------------------------------------------------
+
+ private void searchFinished(final SearchOperation searchOperation) {
+ if (searchOperation.interrupted.get()) {
+ Log.d(LOG, "Search operation was interrupted: " + searchOperation);
+ return;
+ }
+ if (searchOperation != this.currentSearchOperation) {
+ Log.d(LOG, "Stale searchOperation finished: " + searchOperation);
+ return;
+ }
+
+ final Index.IndexEntry searchResult = searchOperation.searchResult;
+ Log.d(LOG, "searchFinished: " + searchOperation + ", searchResult=" + searchResult);
+
+ currentSearchOperation = null;
+ uiHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (currentSearchOperation == null) {
+ if (searchResult != null) {
+ if (isFiltered()) {
+ clearFiltered();
+ }
+ jumpToRow(searchResult.startRow);
+ } else if (searchOperation.multiWordSearchResult != null) {
+ // Multi-row search....
+ setFiltered(searchOperation);
+ } else {
+ throw new IllegalStateException("This should never happen.");
+ }
+ } else {
+ Log.d(LOG, "More coming, waiting for currentSearchOperation.");
+ }
+ }
+ }, 20);
+ }
+
+ private final void jumpToRow(final int row) {
+ Log.d(LOG, "jumpToRow: " + row + ", refocusSearchText=" + false);
+ // getListView().requestFocusFromTouch();
+ getListView().setSelectionFromTop(row, 0);
+ getListView().setSelected(true);
+ }
+
+ static final Pattern WHITESPACE = Pattern.compile("\\s+");
+
+ final class SearchOperation implements Runnable {
+
+ final AtomicBoolean interrupted = new AtomicBoolean(false);
+
+ final String searchText;
+
+ List<String> searchTokens; // filled in for multiWord.
+
+ final Index index;
+
+ long searchStartMillis;
+
+ Index.IndexEntry searchResult;
+
+ List<RowBase> multiWordSearchResult;
+
+ boolean done = false;
+
+ SearchOperation(final String searchText, final Index index) {
+ this.searchText = StringUtil.normalizeWhitespace(searchText);
+ this.index = index;
+ }
+
+ public String toString() {
+ return String.format("SearchOperation(%s,%s)", searchText, interrupted.toString());
+ }
+
+ @Override