Java tutorial
/* This file is part of Aard Dictionary for Android <http://aarddict.org>. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License <http://www.gnu.org/licenses/gpl-3.0.txt> * for more details. * * Copyright (C) 2010 Igor Tkach */ package aarddict.android; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import aarddict.Article; import aarddict.ArticleNotFound; import aarddict.Entry; import aarddict.LookupWord; import aarddict.RedirectTooManyLevels; import aarddict.Volume; import android.annotation.TargetApi; import android.app.AlertDialog; import android.app.SearchManager; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v4.view.MenuItemCompat; import android.util.Log; import android.view.ActionMode; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.Surface; import android.view.View; import android.view.Window; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.webkit.JsResult; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.Toast; public class ArticleViewActivity extends BaseDictionaryActivity { private final static String TAG = ArticleViewActivity.class.getName(); public static final int NOOK_KEY_PREV_LEFT = 92; public static final int NOOK_KEY_NEXT_LEFT = 93; public static final int NOOK_KEY_PREV_RIGHT = 94; public static final int NOOK_KEY_NEXT_RIGHT = 95; private ArticleView articleView; private String sharedCSS; private String mediawikiSharedCSS; private String mediawikiMonobookCSS; private String js; private List<HistoryItem> backItems; private Timer timer; private TimerTask currentTask; private TimerTask currentHideNextButtonTask; private AlphaAnimation fadeOutAnimation; private boolean useAnimation = false; private Map<Article, ScrollXY> scrollPositionsH; private Map<Article, ScrollXY> scrollPositionsV; private boolean saveScrollPos = true; static class AnimationAdapter implements AnimationListener { public void onAnimationEnd(Animation animation) { } public void onAnimationRepeat(Animation animation) { } public void onAnimationStart(Animation animation) { } } @Override void initUI() { this.scrollPositionsH = Collections.synchronizedMap(new HashMap<Article, ScrollXY>()); this.scrollPositionsV = Collections.synchronizedMap(new HashMap<Article, ScrollXY>()); loadAssets(); if (DeviceInfo.EINK_SCREEN) { useAnimation = false; setContentView(R.layout.eink_article_view); articleView = (ArticleView) findViewById(R.id.EinkArticleView); N2EpdController.n2MainActivity = this; EinkScreen.ResetController(2, articleView); // force full screen // refresh when changing // articles } // Setup animations only on non-eink screens else { useAnimation = true; fadeOutAnimation = new AlphaAnimation(1f, 0f); fadeOutAnimation.setDuration(600); fadeOutAnimation.setAnimationListener(new AnimationAdapter() { public void onAnimationEnd(Animation animation) { Button nextButton = (Button) findViewById(R.id.NextButton); nextButton.setVisibility(Button.GONE); } }); getWindow().requestFeature(Window.FEATURE_PROGRESS); setContentView(R.layout.article_view); articleView = (ArticleView) findViewById(R.id.ArticleView); } timer = new Timer(); backItems = Collections.synchronizedList(new LinkedList<HistoryItem>()); if (android.os.Build.VERSION.SDK_INT >= 11) { try { showFindDialogMethod = articleView.getClass().getMethod("showFindDialog", String.class, boolean.class); } catch (NoSuchMethodException e1) { Log.d(TAG, "showFindDialog method not found"); } } articleView.setOnScrollListener(new ArticleView.ScrollListener() { public void onScroll(int l, int t, int oldl, int oldt) { saveScrollPos(l, t); } }); articleView.getSettings().setJavaScriptEnabled(true); articleView.addJavascriptInterface(new SectionMatcher(), "matcher"); articleView.addJavascriptInterface(articleView, "scrollControl"); articleView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { Log.d(TAG + ".js", String.format("[%s]: %s", url, message)); result.cancel(); return true; } public void onProgressChanged(WebView view, int newProgress) { Log.d(TAG, "Progress: " + newProgress); setProgress(5000 + newProgress * 50); } }); articleView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { Log.d(TAG, "Page finished: " + url); currentTask = null; String section = null; if (url.contains("#")) { LookupWord lookupWord = LookupWord.splitWord(url); section = lookupWord.section; if (backItems.size() > 0) { HistoryItem currentHistoryItem = backItems.get(backItems.size() - 1); HistoryItem h = new HistoryItem(currentHistoryItem); h.article.section = section; backItems.add(h); } } else if (backItems.size() > 0) { Article current = backItems.get(backItems.size() - 1).article; section = current.section; } if (!restoreScrollPos()) { goToSection(section); } updateNextButtonVisibility(); } @Override public boolean shouldOverrideUrlLoading(WebView view, final String url) { Log.d(TAG, "URL clicked: " + url); String urlLower = url.toLowerCase(); if (urlLower.startsWith("http://") || urlLower.startsWith("https://") || urlLower.startsWith("ftp://") || urlLower.startsWith("sftp://") || urlLower.startsWith("mailto:")) { Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(browserIntent); } else { if (currentTask == null) { currentTask = new TimerTask() { public void run() { try { Article currentArticle = backItems.get(backItems.size() - 1).article; try { Iterator<Entry> currentIterator = dictionaryService.followLink(url, currentArticle.volumeId); List<Entry> result = new ArrayList<Entry>(); while (currentIterator.hasNext() && result.size() < 20) { result.add(currentIterator.next()); } showNext(new HistoryItem(result)); } catch (ArticleNotFound e) { showMessage(getString(R.string.msgArticleNotFound, e.word.toString())); } } catch (Exception e) { StringBuilder msgBuilder = new StringBuilder( "There was an error following link ").append("\"").append(url) .append("\""); if (e.getMessage() != null) { msgBuilder.append(": ").append(e.getMessage()); } final String msg = msgBuilder.toString(); Log.e(TAG, msg, e); showError(msg); } } }; try { timer.schedule(currentTask, 0); } catch (Exception e) { Log.d(TAG, "Failed to schedule task", e); } } } return true; } }); final Button nextButton = (Button) findViewById(R.id.NextButton); nextButton.getBackground().setAlpha(180); nextButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (nextButton.getVisibility() == View.VISIBLE) { updateNextButtonVisibility(); nextArticle(); updateNextButtonVisibility(); } } }); articleView.setOnTouchListener(new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { updateNextButtonVisibility(); return false; } }); setProgressBarVisibility(true); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public void onActionModeStarted(ActionMode mode) { super.onActionModeStarted(mode); articleView.mustBeArmedToScroll = false; } @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public void onActionModeFinished(ActionMode mode) { super.onActionModeFinished(mode); articleView.mustBeArmedToScroll = true; } private void scrollTo(ScrollXY s) { scrollTo(s.x, s.y); } private void scrollTo(int x, int y) { saveScrollPos = false; Log.d(TAG, "Scroll to " + x + ", " + y); articleView.realScrollTo(x, y); saveScrollPos = true; } private void goToSection(String section) { Log.d(TAG, "Go to section " + section); if (section == null || section.trim().equals("")) { scrollTo(0, 0); } else { articleView.loadUrl(String.format("javascript:scrollToMatch(\"%s\")", section)); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: goBack(); break; case NOOK_KEY_PREV_LEFT: case NOOK_KEY_PREV_RIGHT: case KeyEvent.KEYCODE_VOLUME_UP: if (!articleView.pageUp(false)) { goBack(); } break; case KeyEvent.KEYCODE_VOLUME_DOWN: case NOOK_KEY_NEXT_LEFT: case NOOK_KEY_NEXT_RIGHT: if (!articleView.pageDown(false)) { nextArticle(); } ; break; default: return super.onKeyDown(keyCode, event); } return true; } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { // eat key ups corresponding to key downs so that volume keys don't beep switch (keyCode) { case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: break; default: return super.onKeyDown(keyCode, event); } return true; } private boolean zoomIn() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { return textZoomIn(); } else { return articleView.zoomIn(); } } @TargetApi(android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) private boolean textZoomIn() { int newZoom = articleView.getSettings().getTextZoom() + 20; if (newZoom <= 200) { articleView.getSettings().setTextZoom(newZoom); return true; } else { return false; } } private boolean zoomOut() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { return textZoomOut(); } else { return articleView.zoomOut(); } } @TargetApi(android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) private boolean textZoomOut() { int newZoom = articleView.getSettings().getTextZoom() - 20; if (newZoom >= 40) { articleView.getSettings().setTextZoom(newZoom); return true; } else { return false; } } private void goBack() { if (backItems.size() == 1) { finish(); } if (currentTask != null) { return; } if (backItems.size() > 1) { HistoryItem current = backItems.remove(backItems.size() - 1); HistoryItem prev = backItems.get(backItems.size() - 1); Article prevArticle = prev.article; if (prevArticle.equalsIgnoreSection(current.article)) { resetTitleToCurrent(); if (!prevArticle.sectionEquals(current.article) && !restoreScrollPos()) { goToSection(prevArticle.section); } } else { showCurrentArticle(); } } } private void nextArticle() { HistoryItem current = backItems.get(backItems.size() - 1); if (current.hasNext()) { showNext(current); } } @Override public boolean onSearchRequested() { Intent intent = getIntent(); String action = intent == null ? null : intent.getAction(); if (action != null) { String word = null; if (action.equals(Intent.ACTION_SEARCH)) { word = intent.getStringExtra("query"); } else if (action.equals(Intent.ACTION_SEND)) { word = intent.getStringExtra(Intent.EXTRA_TEXT); } if (word != null) { Intent next = new Intent(); next.setClass(this, LookupActivity.class); next.setAction(Intent.ACTION_SEARCH); next.putExtra(SearchManager.QUERY, word); startActivity(next); } } finish(); return true; } public boolean onKeyLongPress(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_SEARCH) { finish(); return true; } return super.onKeyLongPress(keyCode, event); } final static int MENU_VIEW_ONLINE = 1; final static int MENU_NEW_LOOKUP = 2; final static int MENU_ZOOM_IN = 3; final static int MENU_ZOOM_OUT = 4; final static int MENU_NEXT = 5; final static int MENU_FIND_IN_PAGE = 6; private MenuItem miViewOnline; private MenuItem miNextArticle; private Method showFindDialogMethod; @Override public boolean onCreateOptionsMenu(Menu menu) { if (showFindDialogMethod != null) { MenuItem miFindInPage = menu.add(0, MENU_FIND_IN_PAGE, 0, R.string.mnFindInPage) .setIcon(android.R.drawable.ic_menu_search); MenuItemCompat.setShowAsAction(miFindInPage, MenuItemCompat.SHOW_AS_ACTION_ALWAYS); } miViewOnline = menu.add(0, MENU_VIEW_ONLINE, 0, R.string.mnViewOnline) .setIcon(android.R.drawable.ic_menu_view); menu.add(0, MENU_NEW_LOOKUP, 0, R.string.mnNewLookup).setIcon(android.R.drawable.ic_menu_search); menu.add(0, MENU_ZOOM_OUT, 0, R.string.mnZoomOut).setIcon(R.drawable.ic_menu_zoom_out); menu.add(0, MENU_ZOOM_IN, 0, R.string.mnZoomIn).setIcon(R.drawable.ic_menu_zoom_in); miNextArticle = menu.add(0, MENU_NEXT, 0, R.string.mnNext).setIcon(android.R.drawable.ic_media_next); MenuItemCompat.setShowAsAction(miNextArticle, MenuItemCompat.SHOW_AS_ACTION_ALWAYS); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { boolean enableViewOnline = false; boolean hasNextArticle = false; if (this.backItems.size() > 0) { HistoryItem historyItem = backItems.get(backItems.size() - 1); Article current = historyItem.article; Volume d = dictionaryService.getVolume(current.volumeId); enableViewOnline = d != null && d.getArticleURLTemplate() != null; hasNextArticle = historyItem.hasNext(); } miViewOnline.setEnabled(enableViewOnline); miNextArticle.setVisible(hasNextArticle); return true; } private void showFindDialog() { if (showFindDialogMethod != null) { try { showFindDialogMethod.invoke(articleView, "", true); } catch (Exception e) { Log.e(TAG, "", e); } } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_VIEW_ONLINE: viewOnline(); break; case android.R.id.home: case MENU_NEW_LOOKUP: onSearchRequested(); break; case MENU_ZOOM_IN: zoomIn(); break; case MENU_ZOOM_OUT: zoomOut(); break; case MENU_NEXT: nextArticle(); break; case MENU_FIND_IN_PAGE: showFindDialog(); break; default: return super.onOptionsItemSelected(item); } return true; } private void viewOnline() { if (this.backItems.size() > 0) { Article current = this.backItems.get(this.backItems.size() - 1).article; Volume d = dictionaryService.getVolume(current.volumeId); String url = d == null ? null : d.getArticleURL(current.title); if (url != null) { Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(browserIntent); } } } private void showArticle(String volumeId, long articlePointer, String word, String section) { Log.d(TAG, "word: " + word); Log.d(TAG, "dictionaryId: " + volumeId); Log.d(TAG, "articlePointer: " + articlePointer); Log.d(TAG, "section: " + section); Volume d = dictionaryService.getVolume(volumeId); Entry entry = new Entry(d.getId(), word, articlePointer); entry.section = section; this.showArticle(entry); } private void showArticle(Entry entry) { List<Entry> result = new ArrayList<Entry>(); result.add(entry); try { Iterator<Entry> currentIterator = dictionaryService.followLink(entry.title, entry.volumeId); while (currentIterator.hasNext() && result.size() < 20) { Entry next = currentIterator.next(); if (!next.equals(entry)) { result.add(next); } } } catch (ArticleNotFound e) { Log.d(TAG, String.format("Article \"%s\" not found - unexpected", e.word)); } showNext(new HistoryItem(result)); } private Map<Article, ScrollXY> getScrollPositions() { int orientation = getWindowManager().getDefaultDisplay().getOrientation(); switch (orientation) { case Surface.ROTATION_0: case Surface.ROTATION_180: return scrollPositionsV; default: return scrollPositionsH; } } private void saveScrollPos(int x, int y) { if (!saveScrollPos) { // Log.d(TAG, "Not saving scroll position (disabled)"); return; } if (backItems.size() > 0) { Article a = backItems.get(backItems.size() - 1).article; Map<Article, ScrollXY> positions = getScrollPositions(); ScrollXY s = positions.get(a); if (s == null) { s = new ScrollXY(x, y); positions.put(a, s); } else { s.x = x; s.y = y; } // Log.d(TAG, String.format("Saving scroll position %s for %s", s, // a.title)); getScrollPositions().put(a, s); } } private boolean restoreScrollPos() { if (backItems.size() > 0) { Article a = backItems.get(backItems.size() - 1).article; ScrollXY s = getScrollPositions().get(a); if (s == null) { return false; } scrollTo(s); return true; } return false; } private void showNext(HistoryItem item_) { final HistoryItem item = new HistoryItem(item_); final Entry entry = item.next(); runOnUiThread(new Runnable() { public void run() { setTitle(item); setProgress(1000); } }); currentTask = new TimerTask() { public void run() { try { Article a = dictionaryService.getArticle(entry); try { a = dictionaryService.redirect(a); item.article = new Article(a); } catch (ArticleNotFound e) { showMessage(getString(R.string.msgRedirectNotFound, e.word.toString())); return; } catch (RedirectTooManyLevels e) { showMessage(getString(R.string.msgTooManyRedirects, a.getRedirect())); return; } catch (Exception e) { Log.e(TAG, "Redirect failed", e); showError(getString(R.string.msgErrorLoadingArticle, a.title)); return; } HistoryItem oldCurrent = null; if (!backItems.isEmpty()) oldCurrent = backItems.get(backItems.size() - 1); backItems.add(item); if (oldCurrent != null) { HistoryItem newCurrent = item; if (newCurrent.article.equalsIgnoreSection(oldCurrent.article)) { final String section = oldCurrent.article.sectionEquals(newCurrent.article) ? null : newCurrent.article.section; runOnUiThread(new Runnable() { public void run() { resetTitleToCurrent(); if (section != null) { goToSection(section); } setProgress(10000); currentTask = null; } }); } else { showCurrentArticle(); } } else { showCurrentArticle(); } } catch (Exception e) { String msg = getString(R.string.msgErrorLoadingArticle, entry.title); Log.e(TAG, msg, e); showError(msg); } } }; try { timer.schedule(currentTask, 0); } catch (Exception e) { Log.d(TAG, "Failed to schedule task", e); } } private void showCurrentArticle() { runOnUiThread(new Runnable() { public void run() { setProgress(5000); resetTitleToCurrent(); Article a = backItems.get(backItems.size() - 1).article; Log.d(TAG, "Show article: " + a.text); articleView.loadDataWithBaseURL("", wrap(a.text), "text/html", "utf-8", null); } }); } private void updateNextButtonVisibility() { if (android.os.Build.VERSION.SDK_INT >= 11) { return; } if (currentHideNextButtonTask != null) { currentHideNextButtonTask.cancel(); currentHideNextButtonTask = null; } boolean hasNextArticle = false; if (backItems.size() > 0) { HistoryItem historyItem = backItems.get(backItems.size() - 1); hasNextArticle = historyItem.hasNext(); } final Button nextButton = (Button) findViewById(R.id.NextButton); if (hasNextArticle) { if (nextButton.getVisibility() == View.GONE) { nextButton.setVisibility(View.VISIBLE); } currentHideNextButtonTask = new TimerTask() { @Override public void run() { runOnUiThread(new Runnable() { public void run() { if (useAnimation) { nextButton.startAnimation(fadeOutAnimation); } else { nextButton.setVisibility(View.GONE); } currentHideNextButtonTask = null; } }); } }; try { timer.schedule(currentHideNextButtonTask, 1800); } catch (IllegalStateException e) { // this may happen if orientation changes while users touches // screen Log.d(TAG, "Failed to schedule \"Next\" button hide", e); } } else { nextButton.setVisibility(View.GONE); } } private void showMessage(final String message) { runOnUiThread(new Runnable() { public void run() { currentTask = null; setProgress(10000); resetTitleToCurrent(); Toast.makeText(ArticleViewActivity.this, message, Toast.LENGTH_LONG).show(); if (backItems.isEmpty()) { finish(); } } }); } private void showError(final String message) { runOnUiThread(new Runnable() { public void run() { currentTask = null; setProgress(10000); resetTitleToCurrent(); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(ArticleViewActivity.this); dialogBuilder.setTitle(R.string.titleError).setMessage(message) .setNeutralButton(R.string.btnDismiss, new OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); if (backItems.isEmpty()) { finish(); } } }); dialogBuilder.show(); } }); } private void setTitle(CharSequence articleTitle, CharSequence dictTitle) { setTitle(getString(R.string.titleArticleViewActivity, articleTitle, dictTitle)); } private void resetTitleToCurrent() { if (!backItems.isEmpty()) { HistoryItem current = backItems.get(backItems.size() - 1); setTitle(current); } } private void setTitle(HistoryItem item) { StringBuilder title = new StringBuilder(); boolean hasNextArticle = false; if (item.entries.size() > 1) { title.append(item.entryIndex + 1).append("/").append(item.entries.size()).append(" "); hasNextArticle = item.hasNext(); } if (miNextArticle != null) { miNextArticle.setVisible(hasNextArticle); } Entry entry = item.current(); title.append(entry.title); setTitle(title, dictionaryService.getDisplayTitle(entry.volumeId)); } private String wrap(String articleText) { StringBuilder sb = new StringBuilder("<html>").append("<head>"); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { sb.append("<meta name='viewport' content='width=device-width, initial-scale=1.0, user-scalable=0'/>"); } return sb.append(this.sharedCSS).append(this.mediawikiSharedCSS).append(this.mediawikiMonobookCSS) .append(this.js).append("</head>").append("<body>").append("<div id=\"globalWrapper\">") .append(articleText).append("</div>").append("</body>").append("</html>").toString(); } private String wrapCSS(String css) { return String.format("<style type=\"text/css\">%s</style>", css); } private String wrapJS(String js) { return String.format("<script type=\"text/javascript\">%s</script>", js); } private void loadAssets() { try { this.sharedCSS = wrapCSS(readFile("shared.css")); this.mediawikiSharedCSS = wrapCSS(readFile("mediawiki_shared.css")); this.mediawikiMonobookCSS = wrapCSS(readFile("mediawiki_monobook.css")); this.js = wrapJS(readFile("aar.js")); } catch (IOException e) { Log.e(TAG, "Failed to load assets", e); } } private String readFile(String name) throws IOException { final char[] buffer = new char[0x1000]; StringBuilder out = new StringBuilder(); InputStream is = getResources().getAssets().open(name); Reader in = new InputStreamReader(is, "UTF-8"); int read; do { read = in.read(buffer, 0, buffer.length); if (read > 0) { out.append(buffer, 0, read); } } while (read >= 0); return out.toString(); } @Override protected void onPause() { super.onPause(); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { saveTextZoomPref(); } else { SharedPreferences prefs = getPreferences(MODE_PRIVATE); Editor e = prefs.edit(); e.putFloat("articleView.scale", articleView.getScale()); boolean success = e.commit(); if (!success) { Log.w(TAG, "Failed to save article view scale pref"); } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { applyTextZoomPref(); } else { SharedPreferences prefs = getPreferences(MODE_PRIVATE); float scale = prefs.getFloat("articleView.scale", 1.0f); int initialScale = Math.round(scale * 100); Log.d(TAG, "Setting initial article view scale to " + initialScale); articleView.setInitialScale(initialScale); } if (android.os.Build.VERSION.SDK_INT >= 11) { try { Method getActionBar = getClass().getMethod("getActionBar"); Object actionBar = getActionBar.invoke(this); Method setDisplayHomeAsUpEnabled = actionBar.getClass().getMethod("setDisplayHomeAsUpEnabled", boolean.class); setDisplayHomeAsUpEnabled.invoke(actionBar, true); } catch (Exception e) { } } } @TargetApi(android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) private void applyTextZoomPref() { SharedPreferences prefs = getPreferences(MODE_PRIVATE); int textZoom = prefs.getInt("articleView.textZoom", 100); articleView.getSettings().setTextZoom(textZoom); } @TargetApi(android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) private void saveTextZoomPref() { SharedPreferences prefs = getPreferences(MODE_PRIVATE); int textZoom = articleView.getSettings().getTextZoom(); Editor e = prefs.edit(); e.putInt("articleView.textZoom", textZoom); boolean success = e.commit(); if (!success) { Log.w(TAG, "Failed to save article view text zoom pref"); } } @Override protected void onDestroy() { super.onDestroy(); timer.cancel(); scrollPositionsH.clear(); scrollPositionsV.clear(); backItems.clear(); } @Override void onDictionaryServiceReady() { if (this.backItems.isEmpty()) { final Intent intent = getIntent(); if (intent != null && intent.getAction() != null) { String action = intent.getAction(); String _word = null; if (action.equals(Intent.ACTION_SEARCH)) { _word = intent.getStringExtra("query"); } else if (action.equals(Intent.ACTION_SEND)) { _word = intent.getStringExtra(Intent.EXTRA_TEXT); } final String word = _word; if (word != null) { if (currentTask != null) { currentTask.cancel(); } currentTask = new TimerTask() { @Override public void run() { setProgress(500); String currentWord = word; Log.d(TAG, "intent.getDataString(): " + intent.getDataString()); while (currentWord.length() > 0) { Iterator<Entry> results = dictionaryService.lookup(currentWord); Log.d(TAG, "Looked up " + word); if (results.hasNext()) { currentTask = null; Entry entry = results.next(); showArticle(entry); break; } else { currentWord = currentWord.substring(0, currentWord.length() - 1); } } if (currentWord.length() == 0) { onSearchRequested(); } } }; try { timer.schedule(currentTask, 0); } catch (Exception e) { Log.d(TAG, "Failed to schedule task", e); showError(getString(R.string.msgErrorLoadingArticle, word)); } } } else { String word = intent.getStringExtra("word"); String section = intent.getStringExtra("section"); String volumeId = intent.getStringExtra("volumeId"); long articlePointer = intent.getLongExtra("articlePointer", -1); dictionaryService.setPreferred(volumeId); showArticle(volumeId, articlePointer, word, section); } } else { showCurrentArticle(); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putSerializable("backItems", new LinkedList(backItems)); outState.putSerializable("scrollPositionsH", new HashMap(scrollPositionsH)); outState.putSerializable("scrollPositionsV", new HashMap(scrollPositionsV)); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); backItems = Collections.synchronizedList((List) savedInstanceState.getSerializable("backItems")); scrollPositionsH = Collections .synchronizedMap((Map) savedInstanceState.getSerializable("scrollPositionsH")); scrollPositionsV = Collections .synchronizedMap((Map) savedInstanceState.getSerializable("scrollPositionsV")); } }