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