/randroid/src/com/google/marvin/randroid/randroid.java

http://eyes-free.googlecode.com/ · Java · 302 lines · 247 code · 48 blank · 7 comment · 18 complexity · 8fcad8c42722fd3ec0a80ded3f8ae12c MD5 · raw file

  1. package com.google.marvin.randroid;
  2. import com.google.tts.TTS;
  3. import org.htmlparser.Parser;
  4. import org.htmlparser.filters.NodeClassFilter;
  5. import org.htmlparser.util.NodeList;
  6. import org.htmlparser.util.ParserException;
  7. import org.htmlparser.tags.ImageTag;
  8. import org.htmlparser.tags.HeadingTag;
  9. import org.htmlparser.tags.TextareaTag;
  10. import org.htmlparser.tags.ParagraphTag;
  11. import android.app.Activity;
  12. import android.app.Dialog;
  13. import android.app.ProgressDialog;
  14. import android.app.AlertDialog.Builder;
  15. import android.content.ComponentName;
  16. import android.content.DialogInterface;
  17. import android.content.Intent;
  18. import android.content.SharedPreferences;
  19. import android.net.Uri;
  20. import android.os.Bundle;
  21. import android.preference.PreferenceManager;
  22. import android.view.Menu;
  23. import android.view.MenuItem;
  24. import android.view.View;
  25. import android.view.View.OnClickListener;
  26. import android.webkit.WebView;
  27. import android.widget.Button;
  28. import android.widget.ImageButton;
  29. import android.widget.TextView;
  30. import android.widget.Toast;
  31. public class randroid extends Activity {
  32. private static final int PREFS_UPDATED = 42;
  33. private static final String randomUrl = "http://dynamic.xkcd.com/comic/random/";
  34. private static final String comicImagesBaseUrl = "http://imgs.xkcd.com/comics/";
  35. private static final String transcriptsUrlStart =
  36. "http://www.ohnorobot.com/transcribe.pl?comicid=apKHvCCc66NMg&url=http:%2F%2Fxkcd.com%2F";
  37. private static final String transcriptsUrlEnd = "%2F";
  38. private static final String permaLinkText = "Permanent link to this comic: http://xkcd.com/";
  39. private randroid self;
  40. private TTS tts;
  41. private String speakButtonPref;
  42. private Boolean displayToastPref;
  43. private WebView web;
  44. private TextView title;
  45. private String currentComment = "";
  46. private String currentComicNumber = "";
  47. private String currentComicTitle = "";
  48. private ProgressDialog loadingDialog = null;
  49. /** Called when the activity is first created. */
  50. @Override
  51. public void onCreate(Bundle savedInstanceState) {
  52. super.onCreate(savedInstanceState);
  53. self = this;
  54. loadPrefs();
  55. setContentView(R.layout.main);
  56. web = (WebView) findViewById(R.id.webView);
  57. title = (TextView) findViewById(R.id.titleText);
  58. Button randomButton = (Button) findViewById(R.id.randomButton);
  59. randomButton.setOnClickListener(new OnClickListener() {
  60. public void onClick(View arg0) {
  61. loadRandomComic();
  62. }
  63. });
  64. ImageButton speakButton = (ImageButton) findViewById(R.id.speakButton);
  65. speakButton.setOnClickListener(new OnClickListener() {
  66. public void onClick(View arg0) {
  67. speak();
  68. }
  69. });
  70. tts = new TTS(this, null, false);
  71. loadRandomComic();
  72. }
  73. private void speak() {
  74. if (!speakButtonPref.equals(getString(R.string.none))) {
  75. if (tts.isSpeaking()) {
  76. tts.stop();
  77. } else {
  78. tts.speak(currentComment, 0, null);
  79. }
  80. }
  81. if (displayToastPref) {
  82. Toast.makeText(this, currentComment, 1).show();
  83. }
  84. }
  85. private void loadRandomComic() {
  86. // Clear the cache before loading so that the size of the app doesn't keep growing.
  87. web.clearCache(true);
  88. web.clearHistory();
  89. loadingDialog =
  90. ProgressDialog.show(self, getString(R.string.loading), getString(R.string.please_wait),
  91. true);
  92. class comicLoader implements Runnable {
  93. public void run() {
  94. String url = "";
  95. try {
  96. Parser p = new Parser();
  97. p.setURL(randomUrl.toString());
  98. NodeList headings = p.extractAllNodesThatMatch(new NodeClassFilter(HeadingTag.class));
  99. for (int i = 0; i < headings.size(); i++) {
  100. HeadingTag n = (HeadingTag) headings.elementAt(i);
  101. String headingText = n.getStringText();
  102. if (headingText.startsWith(permaLinkText)) {
  103. currentComicNumber =
  104. headingText.substring(permaLinkText.length(), headingText.length() - 1);
  105. break;
  106. }
  107. }
  108. p.reset();
  109. NodeList images = p.extractAllNodesThatMatch(new NodeClassFilter(ImageTag.class));
  110. for (int i = 0; i < images.size(); i++) {
  111. ImageTag n = (ImageTag) images.elementAt(i);
  112. String imageUrl = n.getImageURL();
  113. if (imageUrl.startsWith(comicImagesBaseUrl)) {
  114. url = imageUrl;
  115. currentComment = n.getAttribute("title");
  116. currentComicTitle = n.getAttribute("alt");
  117. break;
  118. }
  119. }
  120. if (speakButtonPref.equals(getString(R.string.transcript))) {
  121. fetchTranscript();
  122. }
  123. } catch (ParserException pce) {
  124. currentComicTitle = self.getString(R.string.net_error);
  125. }
  126. web.loadUrl(url);
  127. updateDisplay();
  128. loadingDialog.dismiss();
  129. }
  130. }
  131. Thread loadThread = (new Thread(new comicLoader()));
  132. loadThread.start();
  133. }
  134. private void updateDisplay() {
  135. class titleTextUpdater implements Runnable {
  136. public void run() {
  137. title.setText(currentComicTitle);
  138. }
  139. }
  140. title.post(new titleTextUpdater());
  141. }
  142. private void fetchTranscript() {
  143. String transcriptUrl = transcriptsUrlStart + currentComicNumber + transcriptsUrlEnd;
  144. String transcript = "";
  145. try {
  146. Parser p = new Parser();
  147. p.setURL(transcriptUrl.toString());
  148. // Sometimes transcripts are inside a textarea
  149. NodeList textareas = p.extractAllNodesThatMatch(new NodeClassFilter(TextareaTag.class));
  150. if (textareas.size() > 0) {
  151. TextareaTag n = (TextareaTag) textareas.elementAt(0);
  152. transcript = n.getStringText();
  153. if (transcript.length() > 0) {
  154. currentComment = transcript.toLowerCase().replaceAll("<br>", "\n");
  155. return;
  156. }
  157. }
  158. // Other times, they are inside a P
  159. p.reset();
  160. NodeList paragraphs = p.extractAllNodesThatMatch(new NodeClassFilter(ParagraphTag.class));
  161. if (paragraphs.size() > 0) {
  162. ParagraphTag n = (ParagraphTag) paragraphs.elementAt(0);
  163. transcript = n.getStringText();
  164. if (transcript.length() > 0) {
  165. currentComment = transcript.toLowerCase().replaceAll("<br>", "\n");
  166. return;
  167. }
  168. }
  169. } catch (ParserException pce) {
  170. // This can happen if there is a network error. Do nothing here.
  171. }
  172. }
  173. @Override
  174. protected void onDestroy() {
  175. if (tts != null) {
  176. tts.shutdown();
  177. }
  178. // Clean up the cache
  179. web.clearCache(true);
  180. web.clearHistory();
  181. super.onDestroy();
  182. }
  183. @Override
  184. public boolean onCreateOptionsMenu(Menu menu) {
  185. menu.add(0, R.string.preferences, 0, R.string.preferences).setIcon(
  186. android.R.drawable.ic_menu_preferences);
  187. menu.add(0, R.string.about, 0, R.string.about).setIcon(android.R.drawable.ic_menu_info_details);
  188. return super.onCreateOptionsMenu(menu);
  189. }
  190. @Override
  191. public boolean onOptionsItemSelected(MenuItem item) {
  192. Intent intent;
  193. switch (item.getItemId()) {
  194. case R.string.preferences:
  195. intent = new Intent(this, PrefsActivity.class);
  196. startActivityForResult(intent, PREFS_UPDATED);
  197. break;
  198. case R.string.about:
  199. displayAbout();
  200. break;
  201. }
  202. return super.onOptionsItemSelected(item);
  203. }
  204. @Override
  205. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  206. if (requestCode == PREFS_UPDATED) {
  207. loadPrefs();
  208. }
  209. super.onActivityResult(requestCode, resultCode, data);
  210. }
  211. private void loadPrefs() {
  212. SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
  213. // From the game mode setting
  214. speakButtonPref = prefs.getString("speak_pref", getString(R.string.commentary));
  215. displayToastPref = prefs.getBoolean("display_toast_pref", true);
  216. }
  217. private void displayAbout() {
  218. Builder about = new Builder(this);
  219. about.setTitle(R.string.app_name);
  220. about.setMessage(R.string.about_message);
  221. final Activity self = this;
  222. about.setNeutralButton(R.string.source, new Dialog.OnClickListener() {
  223. public void onClick(DialogInterface dialog, int which) {
  224. Intent i = new Intent();
  225. ComponentName comp =
  226. new ComponentName("com.android.browser", "com.android.browser.BrowserActivity");
  227. i.setComponent(comp);
  228. i.setAction("android.intent.action.VIEW");
  229. i.addCategory("android.intent.category.BROWSABLE");
  230. Uri uri = Uri.parse("http://eyes-free.googlecode.com");
  231. i.setData(uri);
  232. self.startActivity(i);
  233. }
  234. });
  235. about.setPositiveButton(R.string.xkcd_website, new Dialog.OnClickListener() {
  236. public void onClick(DialogInterface dialog, int which) {
  237. Intent i = new Intent();
  238. ComponentName comp =
  239. new ComponentName("com.android.browser", "com.android.browser.BrowserActivity");
  240. i.setComponent(comp);
  241. i.setAction("android.intent.action.VIEW");
  242. i.addCategory("android.intent.category.BROWSABLE");
  243. Uri uri = Uri.parse("http://xkcd.com");
  244. i.setData(uri);
  245. self.startActivity(i);
  246. }
  247. });
  248. about.setNegativeButton(R.string.close, new Dialog.OnClickListener() {
  249. public void onClick(DialogInterface dialog, int which) {
  250. }
  251. });
  252. about.show();
  253. }
  254. }