Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: src/org/adblockplus/android/AdblockPlus.java

Issue 11172036: ABP/Android libadblockplus integration (Closed)
Patch Set: Created July 22, 2013, 8:52 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <http://adblockplus.org/>, 2 * This file is part of Adblock Plus <http://adblockplus.org/>,
3 * Copyright (C) 2006-2013 Eyeo GmbH 3 * Copyright (C) 2006-2013 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 package org.adblockplus.android; 18 package org.adblockplus.android;
19 19
20 import java.io.BufferedReader; 20 import java.io.BufferedReader;
21 import java.io.File; 21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileNotFoundException; 22 import java.io.FileNotFoundException;
24 import java.io.FileOutputStream;
25 import java.io.IOException; 23 import java.io.IOException;
26 import java.io.InputStream; 24 import java.io.InputStream;
27 import java.io.InputStreamReader; 25 import java.io.InputStreamReader;
28 import java.net.HttpURLConnection;
29 import java.net.URL;
30 import java.util.ArrayList;
31 import java.util.Calendar; 26 import java.util.Calendar;
32 import java.util.LinkedList;
33 import java.util.List;
34 import java.util.Locale;
35 import java.util.Map;
36 import java.util.TimeZone; 27 import java.util.TimeZone;
37 import java.util.concurrent.Callable; 28 import java.util.regex.Pattern;
38 import java.util.concurrent.ExecutionException;
39 import java.util.concurrent.Future;
40 import java.util.concurrent.FutureTask;
41
42 import javax.xml.parsers.ParserConfigurationException;
43 import javax.xml.parsers.SAXParser;
44 import javax.xml.parsers.SAXParserFactory;
45 29
46 import org.adblockplus.android.updater.AlarmReceiver; 30 import org.adblockplus.android.updater.AlarmReceiver;
47 import org.apache.commons.lang.StringEscapeUtils;
48 import org.apache.commons.lang.StringUtils;
49 import org.json.JSONException;
50 import org.json.JSONObject;
51 import org.xml.sax.SAXException;
52 31
53 import android.app.ActivityManager; 32 import android.app.ActivityManager;
54 import android.app.ActivityManager.RunningServiceInfo; 33 import android.app.ActivityManager.RunningServiceInfo;
55 import android.app.AlarmManager; 34 import android.app.AlarmManager;
56 import android.app.Application; 35 import android.app.Application;
57 import android.app.PendingIntent; 36 import android.app.PendingIntent;
58 import android.content.Context; 37 import android.content.Context;
59 import android.content.Intent; 38 import android.content.Intent;
60 import android.content.SharedPreferences;
61 import android.content.pm.PackageInfo; 39 import android.content.pm.PackageInfo;
62 import android.content.pm.PackageManager; 40 import android.content.pm.PackageManager;
63 import android.content.pm.PackageManager.NameNotFoundException; 41 import android.content.pm.PackageManager.NameNotFoundException;
64 import android.content.res.AssetManager;
65 import android.net.ConnectivityManager; 42 import android.net.ConnectivityManager;
66 import android.net.NetworkInfo; 43 import android.net.NetworkInfo;
67 import android.net.Uri; 44 import android.net.Uri;
68 import android.os.AsyncTask;
69 import android.os.Build; 45 import android.os.Build;
70 import android.os.Bundle;
71 import android.os.Handler;
72 import android.os.Message;
73 import android.os.SystemClock;
74 import android.preference.PreferenceManager;
75 import android.provider.Settings; 46 import android.provider.Settings;
76 import android.util.Log; 47 import android.util.Log;
77 import android.widget.Toast;
78 48
79 public class AdblockPlus extends Application 49 public class AdblockPlus extends Application
80 { 50 {
81 private final static String TAG = "Application"; 51 private final static String TAG = "Application";
82 52
83 private final static int MSG_TOAST = 1; 53 private final static Pattern RE_JS = Pattern.compile(".*\\.js$", Pattern.CASE_ INSENSITIVE);
54 private final static Pattern RE_CSS = Pattern.compile(".*\\.css$", Pattern.CAS E_INSENSITIVE);
55 private final static Pattern RE_IMAGE = Pattern.compile(".*\\.(?:gif|png|jpe?g |bmp|ico)$", Pattern.CASE_INSENSITIVE);
56 private final static Pattern RE_FONT = Pattern.compile(".*\\.(?:ttf|woff)$", P attern.CASE_INSENSITIVE);
Wladimir Palant 2013/09/12 11:31:14 Wouldn't it make sense to drop .* at the beginning
84 57
85 /** 58 /**
86 * Broadcasted when filtering is enabled or disabled. 59 * Broadcasted when filtering is enabled or disabled.
87 */ 60 */
88 public static final String BROADCAST_FILTERING_CHANGE = "org.adblockplus.andro id.filtering.status"; 61 public static final String BROADCAST_FILTERING_CHANGE = "org.adblockplus.andro id.filtering.status";
89 /** 62 /**
90 * Broadcasted when subscription status changes. 63 * Broadcasted when subscription status changes.
91 */ 64 */
92 public final static String BROADCAST_SUBSCRIPTION_STATUS = "org.adblockplus.an droid.subscription.status"; 65 public final static String BROADCAST_SUBSCRIPTION_STATUS = "org.adblockplus.an droid.subscription.status";
93 /** 66 /**
94 * Broadcasted when filter match check is performed. 67 * Broadcasted when filter match check is performed.
95 */ 68 */
96 public final static String BROADCAST_FILTER_MATCHES = "org.adblockplus.android .filter.matches"; 69 public final static String BROADCAST_FILTER_MATCHES = "org.adblockplus.android .filter.matches";
97 70
98 private List<Subscription> subscriptions; 71 private Subscription[] subscriptions;
Wladimir Palant 2013/09/12 11:31:14 Add a comment explaining that this is the cached l
99
100 private JSThread js;
101
102 /**
103 * Indicates interactive mode (used to listen for subscription status
104 * changes).
105 */
106 private boolean interactive = false;
107 72
108 /** 73 /**
109 * Indicates whether filtering is enabled or not. 74 * Indicates whether filtering is enabled or not.
110 */ 75 */
111 private boolean filteringEnabled = false; 76 private boolean filteringEnabled = false;
77
78 private ABPEngine abpEngine;
112 79
113 private static AdblockPlus instance; 80 private static AdblockPlus instance;
114 81
115 /** 82 /**
116 * Returns pointer to itself (singleton pattern). 83 * Returns pointer to itself (singleton pattern).
117 */ 84 */
118 public static AdblockPlus getApplication() 85 public static AdblockPlus getApplication()
119 { 86 {
120 return instance; 87 return instance;
121 } 88 }
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
237 /** 204 /**
238 * Checks if application can write to external storage. 205 * Checks if application can write to external storage.
239 */ 206 */
240 public boolean checkWriteExternalPermission() 207 public boolean checkWriteExternalPermission()
241 { 208 {
242 String permission = "android.permission.WRITE_EXTERNAL_STORAGE"; 209 String permission = "android.permission.WRITE_EXTERNAL_STORAGE";
243 int res = checkCallingOrSelfPermission(permission); 210 int res = checkCallingOrSelfPermission(permission);
244 return res == PackageManager.PERMISSION_GRANTED; 211 return res == PackageManager.PERMISSION_GRANTED;
245 } 212 }
246 213
214 public boolean isFirstRun()
215 {
216 return abpEngine.isFirstRun();
217 }
218
247 /** 219 /**
248 * Returns list of known subscriptions. 220 * Returns list of known subscriptions.
249 */ 221 */
250 public List<Subscription> getSubscriptions() 222 public Subscription[] getRecommendedSubscriptions()
251 { 223 {
252 if (subscriptions == null) 224 if (subscriptions == null)
253 { 225 subscriptions = abpEngine.getRecommendedSubscriptions();
254 subscriptions = new ArrayList<Subscription>();
255
256 SAXParserFactory factory = SAXParserFactory.newInstance();
257 SAXParser parser;
258 try
259 {
260 parser = factory.newSAXParser();
261 parser.parse(getAssets().open("subscriptions.xml"), new SubscriptionPars er(subscriptions));
262 }
263 catch (ParserConfigurationException e)
264 {
265 // TODO Auto-generated catch block
266 Log.e(TAG, e.getMessage(), e);
267 }
268 catch (SAXException e)
269 {
270 // TODO Auto-generated catch block
271 Log.e(TAG, e.getMessage(), e);
272 }
273 catch (IOException e)
274 {
275 // TODO Auto-generated catch block
276 Log.e(TAG, e.getMessage(), e);
277 }
278 }
279 return subscriptions; 226 return subscriptions;
280 } 227 }
281 228
282 /** 229 /**
283 * Returns subscription information. 230 * Returns list of enabled subscriptions.
284 *
285 * @param url
286 * subscription url
287 */ 231 */
288 public Subscription getSubscription(String url) 232 public Subscription[] getListedSubscriptions()
289 { 233 {
290 List<Subscription> subscriptions = getSubscriptions(); 234 return abpEngine.getListedSubscriptions();
291
292 for (Subscription subscription : subscriptions)
293 {
294 if (subscription.url.equals(url))
295 return subscription;
296 }
297 return null;
298 } 235 }
299 236
300 /** 237 /**
301 * Adds provided subscription and removes previous subscriptions if any. 238 * Adds provided subscription and removes previous subscriptions if any.
302 * 239 *
303 * @param subscription 240 * @param url
304 * Subscription to add 241 * URL of subscription to add
305 */ 242 */
306 public void setSubscription(Subscription subscription) 243 public void setSubscription(String url)
307 { 244 {
308 if (subscription != null) 245 Subscription[] subscriptions = abpEngine.getListedSubscriptions();
246 for (Subscription subscription : subscriptions)
309 { 247 {
310 final JSONObject jsonSub = new JSONObject(); 248 abpEngine.removeSubscription(subscription.url);
311 try
312 {
313 jsonSub.put("url", subscription.url);
314 jsonSub.put("title", subscription.title);
315 jsonSub.put("homepage", subscription.homepage);
316 js.execute(new Runnable()
317 {
318 @Override
319 public void run()
320 {
321 js.evaluate("clearSubscriptions()");
322 js.evaluate("addSubscription(\"" + StringEscapeUtils.escapeJavaScrip t(jsonSub.toString()) + "\")");
323 }
324 });
325 }
326 catch (JSONException e)
327 {
328 // TODO Auto-generated catch block
329 Log.e(TAG, e.getMessage(), e);
330 }
331 } 249 }
250 abpEngine.addSubscription(url);
332 } 251 }
333 252
334 /** 253 /**
335 * Forces subscriptions refresh. 254 * Forces subscriptions refresh.
336 */ 255 */
337 public void refreshSubscription() 256 public void refreshSubscriptions()
338 { 257 {
339 js.execute(new Runnable() 258 Subscription[] subscriptions = abpEngine.getListedSubscriptions();
259 for (Subscription subscription : subscriptions)
340 { 260 {
341 @Override 261 abpEngine.refreshSubscription(subscription.url);
342 public void run() 262 }
343 { 263 }
344 js.evaluate("refreshSubscriptions()"); 264
345 } 265 /**
346 }); 266 * Enforces subscription status update.
267 *
268 * @param url Subscription url
269 */
270 public void actualizeSubscriptionStatus(String url)
271 {
272 abpEngine.actualizeSubscriptionStatus(url);
347 } 273 }
348 274
349 /** 275 /**
350 * Selects which subscription to offer for the first time.
351 *
352 * @return offered subscription
353 */
354 public Subscription offerSubscription()
355 {
356 Subscription selectedItem = null;
357 String selectedPrefix = null;
358 int matchCount = 0;
359 for (Subscription subscription : getSubscriptions())
360 {
361 if (selectedItem == null)
362 selectedItem = subscription;
363
364 String prefix = checkLocalePrefixMatch(subscription.prefixes);
365 if (prefix != null)
366 {
367 if (selectedPrefix == null || selectedPrefix.length() < prefix.length())
368 {
369 selectedItem = subscription;
370 selectedPrefix = prefix;
371 matchCount = 1;
372 }
373 else if (selectedPrefix != null && selectedPrefix.length() == prefix.len gth())
374 {
375 matchCount++;
376
377 // If multiple items have a matching prefix of the
378 // same length select one of the items randomly,
379 // probability should be the same for all items.
380 // So we replace the previous match here with
381 // probability 1/N (N being the number of matches).
382 if (Math.random() * matchCount < 1)
383 {
384 selectedItem = subscription;
385 selectedPrefix = prefix;
386 }
387 }
388 }
389 }
390 return selectedItem;
391 }
392
393 /**
394 * Verifies that subscriptions are loaded and returns flag of subscription
395 * presence.
396 *
397 * @return true if at least one subscription is present and downloaded
398 */
399 public boolean verifySubscriptions()
400 {
401 Future<Boolean> future = js.submit(new Callable<Boolean>()
402 {
403 @Override
404 public Boolean call() throws Exception
405 {
406 Boolean result = (Boolean) js.evaluate("verifySubscriptions()");
407 return result;
408 }
409 });
410 try
411 {
412 return future.get().booleanValue();
413 }
414 catch (InterruptedException e)
415 {
416 // TODO Auto-generated catch block
417 Log.e(TAG, e.getMessage(), e);
418 }
419 catch (ExecutionException e)
420 {
421 // TODO Auto-generated catch block
422 Log.e(TAG, e.getMessage(), e);
423 }
424 return false;
425 }
426
427 /**
428 * Returns ElemHide selectors for domain. 276 * Returns ElemHide selectors for domain.
429 * 277 *
430 * @return ready to use HTML element with CSS selectors 278 * @return ready to use HTML element with CSS selectors
Wladimir Palant 2013/09/12 11:31:14 This comment doesn't seem to match the actual retu
431 */ 279 */
432 public String getSelectorsForDomain(final String domain) 280 public String[] getSelectorsForDomain(final String domain)
433 { 281 {
434 if (!filteringEnabled) 282 if (!filteringEnabled)
435 return null; 283 return null;
436 284
437 Future<String> future = js.submit(new Callable<String>() 285 return abpEngine.getSelectorsForDomain(domain);
438 {
439 @Override
440 public String call() throws Exception
441 {
442 String result = (String) js.evaluate("ElemHide.getSelectorsForDomain('" + domain + "')");
443 return result;
444 }
445 });
446 try
447 {
448 return future.get();
449 }
450 catch (InterruptedException e)
451 {
452 // TODO Auto-generated catch block
453 Log.e(TAG, e.getMessage(), e);
454 }
455 catch (ExecutionException e)
456 {
457 // TODO Auto-generated catch block
458 Log.e(TAG, e.getMessage(), e);
459 }
460 return null;
461 }
462
463 private class MatchesCallable implements Callable<Boolean>
464 {
465 private String url;
466 private String query;
467 private String reqHost;
468 private String refHost;
469 private String accept;
470
471 MatchesCallable(String url, String query, String reqHost, String refHost, St ring accept)
472 {
473 this.url = StringEscapeUtils.escapeJavaScript(url);
474 this.query = StringEscapeUtils.escapeJavaScript(query);
475 this.reqHost = reqHost != null ? StringEscapeUtils.escapeJavaScript(reqHos t) : "";
476 this.refHost = refHost != null ? StringEscapeUtils.escapeJavaScript(refHos t) : "";
477 this.accept = accept != null ? StringEscapeUtils.escapeJavaScript(accept) : "";
478 }
479
480 @Override
481 public Boolean call() throws Exception
482 {
483 Boolean result = (Boolean) js.evaluate("matchesAny('" + url + "', '" + que ry + "', '" + reqHost + "', '" + refHost + "', '" + accept + "');");
484 return result;
485 }
486 } 286 }
487 287
488 /** 288 /**
489 * Checks if filters match request parameters. 289 * Checks if filters match request parameters.
490 * 290 *
491 * @param url 291 * @param url
492 * Request URL 292 * Request URL
493 * @param query 293 * @param query
494 * Request query string 294 * Request query string
495 * @param reqHost 295 * @param referrer
496 * Request host
497 * @param refHost
498 * Request referrer header 296 * Request referrer header
499 * @param accept 297 * @param accept
500 * Request accept header 298 * Request accept header
501 * @return true if matched filter was found 299 * @return true if matched filter was found
502 * @throws Exception 300 * @throws Exception
503 */ 301 */
504 public boolean matches(String url, String query, String reqHost, String refHos t, String accept) throws Exception 302 public boolean matches(String url, String query, String referrer, String accep t)
505 { 303 {
506 if (!filteringEnabled) 304 if (!filteringEnabled)
507 return false; 305 return false;
306
307 String contentType = null;
508 308
509 Callable<Boolean> callable = new MatchesCallable(url, query, reqHost, refHos t, accept); 309 if (accept != null)
510 Future<Boolean> future = js.submit(callable); 310 {
511 boolean matches = future.get().booleanValue(); 311 if (accept.contains("text/css"))
512 sendBroadcast(new Intent(BROADCAST_FILTER_MATCHES).putExtra("url", url).putE xtra("matches", matches)); 312 contentType = "STYLESHEET";
513 return matches; 313 else if (accept.contains("image/*"))
314 contentType = "IMAGE";
315 }
316
317 if (contentType == null)
318 {
319 if (RE_JS.matcher(url).matches())
320 contentType = "SCRIPT";
321 else if (RE_CSS.matcher(url).matches())
322 contentType = "STYLESHEET";
323 else if (RE_IMAGE.matcher(url).matches())
324 contentType = "IMAGE";
325 else if (RE_FONT.matcher(url).matches())
326 contentType = "FONT";
327 }
328 if (contentType == null)
329 contentType = "OTHER";
330
331 if (!"".equals(query))
332 url = url + "?" + query;
333
334 return abpEngine.matches(url, contentType, referrer);
514 } 335 }
515 336
516 /** 337 /**
517 * Checks if filtering is enabled. 338 * Checks if filtering is enabled.
518 */ 339 */
519 public boolean isFilteringEnabled() 340 public boolean isFilteringEnabled()
520 { 341 {
521 return filteringEnabled; 342 return filteringEnabled;
522 } 343 }
523 344
524 /** 345 /**
525 * Enables or disables filtering. 346 * Enables or disables filtering.
526 */ 347 */
527 public void setFilteringEnabled(boolean enable) 348 public void setFilteringEnabled(boolean enable)
528 { 349 {
529 filteringEnabled = enable; 350 filteringEnabled = enable;
530 sendBroadcast(new Intent(BROADCAST_FILTERING_CHANGE).putExtra("enabled", fil teringEnabled)); 351 sendBroadcast(new Intent(BROADCAST_FILTERING_CHANGE).putExtra("enabled", fil teringEnabled));
531 } 352 }
532 353
533 /** 354 /**
534 * Notifies JS code that application entered interactive mode. 355 * Starts ABP engine. It also initiates subscription refresh if it is enabled
535 */
536 public void startInteractive()
537 {
538 js.execute(new Runnable()
539 {
540 @Override
541 public void run()
542 {
543 js.evaluate("startInteractive()");
544 }
545 });
546 interactive = true;
547 }
548
549 /**
550 * Notifies JS code that application quit interactive mode.
551 */
552 public void stopInteractive()
553 {
554 // onStop is sometimes called without prior calling onStart
555 // by Android system
556 if (js == null)
557 return;
558
559 js.execute(new Runnable()
560 {
561 @Override
562 public void run()
563 {
564 js.evaluate("stopInteractive()");
565 }
566 });
567 interactive = false;
568 }
569
570 /**
571 * Returns prefixes that match current user locale.
572 */
573 public String checkLocalePrefixMatch(String[] prefixes)
574 {
575 if (prefixes == null || prefixes.length == 0)
576 return null;
577
578 String locale = Locale.getDefault().toString().toLowerCase();
579
580 for (int i = 0; i < prefixes.length; i++)
581 if (locale.startsWith(prefixes[i].toLowerCase()))
582 return prefixes[i];
583
584 return null;
585 }
586
587 /**
588 * Starts JS engine. It also initiates subscription refresh if it is enabled
589 * in user settings. 356 * in user settings.
590 */ 357 */
591 public void startEngine() 358 public void startEngine()
592 { 359 {
360 if (abpEngine == null)
361 {
362 File basePath = getFilesDir();
363 // jsEngine.put("_locale", Locale.getDefault().toString());
364 // jsEngine.put("_datapath", getFilesDir().getAbsolutePath());
365 // jsEngine.put("_separator", File.separator);
366 // jsEngine.put("_version", getVersion());
Wladimir Palant 2013/09/12 11:31:14 I guess you intend to remove that code or replace
367 abpEngine = new ABPEngine(this, basePath.getAbsolutePath());
368 }
369 /*
593 if (js == null) 370 if (js == null)
594 { 371 {
595 Log.i(TAG, "startEngine"); 372 Log.i(TAG, "startEngine");
596 js = new JSThread(this); 373 js = new JSThread(this);
597 js.start(); 374 js.start();
598 375
599 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferen ces(this); 376 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferen ces(this);
600 final int refresh = Integer.valueOf(prefs.getString(getString(R.string.pre f_refresh), Integer.toString(getResources().getInteger(R.integer.def_refresh)))) ; 377 final int refresh = Integer.valueOf(prefs.getString(getString(R.string.pre f_refresh), Integer.toString(getResources().getInteger(R.integer.def_refresh)))) ;
601 final boolean wifionly = prefs.getBoolean(getString(R.string.pref_wifirefr esh), getResources().getBoolean(R.bool.def_wifirefresh)); 378 final boolean wifionly = prefs.getBoolean(getString(R.string.pref_wifirefr esh), getResources().getBoolean(R.bool.def_wifirefresh));
602 // Refresh if user selected refresh on each start 379 // Refresh if user selected refresh on each start
603 if (refresh == 1 && (!wifionly || isWiFiConnected(this))) 380 if (refresh == 1 && (!wifionly || isWiFiConnected(this)))
604 { 381 {
605 refreshSubscription(); 382 refreshSubscriptions();
606 } 383 }
607 } 384 }
385 */
Wladimir Palant 2013/09/12 11:31:14 I guess you intend to remove that code?
386 }
387
388 /**
389 * Stops ABP engine.
390 */
391 public void stopEngine()
392 {
393 if (abpEngine != null)
394 {
395 abpEngine.release();
396 abpEngine = null;
397 Log.i(TAG, "stopEngine");
398 }
608 } 399 }
609 400
610 /**
611 * Stops JS engine.
612 *
613 * @param implicitly
614 * stop even in interactive mode
615 */
616 public void stopEngine(boolean implicitly)
617 {
618 if ((implicitly || !interactive) && js != null)
619 {
620 Log.i(TAG, "stopEngine");
621 js.stopEngine();
622 try
623 {
624 js.join();
625 }
626 catch (InterruptedException e)
627 {
628 Log.e(TAG, e.getMessage(), e);
629 }
630 Log.i(TAG, "Engine stopped");
631 js = null;
632 }
633 }
634
635 /** 401 /**
636 * Sets Alarm to call updater after specified number of minutes or after one 402 * Sets Alarm to call updater after specified number of minutes or after one
637 * day if 403 * day if
638 * minutes are set to 0. 404 * minutes are set to 0.
639 * 405 *
640 * @param minutes 406 * @param minutes
641 * number of minutes to wait 407 * number of minutes to wait
642 */ 408 */
643 public void scheduleUpdater(int minutes) 409 public void scheduleUpdater(int minutes)
644 { 410 {
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
705 // TODO Auto-generated catch block 471 // TODO Auto-generated catch block
706 Log.e(TAG, e.getMessage(), e); 472 Log.e(TAG, e.getMessage(), e);
707 } 473 }
708 474
709 // Set crash handler 475 // Set crash handler
710 Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(this)); 476 Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(this));
711 477
712 // Initiate update check 478 // Initiate update check
713 scheduleUpdater(0); 479 scheduleUpdater(0);
714 } 480 }
715
716 /**
717 * Handler for showing toast messages from JS code.
718 */
719 private static final Handler messageHandler = new Handler()
720 {
721 public void handleMessage(Message msg)
722 {
723 if (msg.what == MSG_TOAST)
724 {
725 Toast.makeText(AdblockPlus.getApplication(), msg.getData().getString("me ssage"), Toast.LENGTH_LONG).show();
726 }
727 }
728 };
729
730 /**
731 * JS execution thread.
732 */
733 private final class JSThread extends Thread
734 {
735 private JSEngine jsEngine;
736 private volatile boolean run = true;
737 private Context context;
738 private final LinkedList<Runnable> queue = new LinkedList<Runnable>();
739 private long delay = -1;
740
741 JSThread(Context context)
742 {
743 this.context = context;
744 }
745
746 // JS helper
747 @SuppressWarnings("unused")
748 public String readJSFile(String name)
749 {
750 String result = "";
751 AssetManager assetManager = getAssets();
752 try
753 {
754 InputStreamReader reader = new InputStreamReader(assetManager.open("js" + File.separator + name));
755 final char[] buffer = new char[0x10000];
756 StringBuilder out = new StringBuilder();
757 int read;
758 do
759 {
760 read = reader.read(buffer, 0, buffer.length);
761 if (read > 0)
762 out.append(buffer, 0, read);
763 }
764 while (read >= 0);
765 result = out.toString();
766 }
767 catch (IOException e)
768 {
769 Log.e(TAG, e.getMessage(), e);
770 }
771 return result;
772 }
773
774 // JS helper
775 public FileInputStream getInputStream(String path)
776 {
777 Log.d(TAG, path);
778 File f = new File(path);
779 try
780 {
781 return openFileInput(f.getName());
782 }
783 catch (FileNotFoundException e)
784 {
785 Log.e(TAG, e.getMessage(), e);
786 }
787 return null;
788 }
789
790 // JS helper
791 public FileOutputStream getOutputStream(String path)
792 {
793 Log.d(TAG, path);
794 File f = new File(path);
795 try
796 {
797 return openFileOutput(f.getName(), MODE_PRIVATE);
798 }
799 catch (FileNotFoundException e)
800 {
801 Log.e(TAG, e.getMessage(), e);
802 }
803 return null;
804 }
805
806 // JS helper
807 public String getVersion()
808 {
809 String versionName = null;
810 try
811 {
812 versionName = getPackageManager().getPackageInfo(getPackageName(), 0).ve rsionName;
813 }
814 catch (NameNotFoundException ex)
815 {
816 versionName = "n/a";
817 }
818 return versionName;
819 }
820
821 // JS helper
822 @SuppressWarnings("unused")
823 public boolean canAutoupdate()
824 {
825 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferen ces(context);
826 final int refresh = Integer.valueOf(prefs.getString(getString(R.string.pre f_refresh), Integer.toString(context.getResources().getInteger(R.integer.def_ref resh))));
827 final boolean wifionly = prefs.getBoolean(getString(R.string.pref_wifirefr esh), getResources().getBoolean(R.bool.def_wifirefresh));
828 return refresh == 2 && (!wifionly || isWiFiConnected(context));
829 }
830
831 // JS helper
832 @SuppressWarnings("unused")
833 public void httpSend(final String method, final String url, final String[][] headers, final boolean async, final long callback)
834 {
835 Log.d(TAG, "httpSend('" + method + "', '" + url + "')");
836 messageHandler.post(new Runnable()
837 {
838 @Override
839 public void run()
840 {
841 try
842 {
843 Task task = new Task();
844 task.callback = callback;
845 task.connection = (HttpURLConnection) new URL(url).openConnection();
846 task.connection.setRequestMethod(method);
847 for (int i = 0; i < headers.length; i++)
848 {
849 task.connection.setRequestProperty(headers[i][0], headers[i][1]);
850 }
851 DownloadTask downloadTask = new DownloadTask(context);
852 downloadTask.execute(task);
853 if (!async)
854 {
855 downloadTask.get();
856 }
857 }
858 catch (Exception e)
859 {
860 Log.e(TAG, e.getMessage(), e);
861 js.callback(callback, null);
862 }
863 }
864 });
865 }
866
867 // JS helper
868 @SuppressWarnings("unused")
869 public void setStatus(String text, long time)
870 {
871 sendBroadcast(new Intent(BROADCAST_SUBSCRIPTION_STATUS).putExtra("text", t ext).putExtra("time", time));
872 }
873
874 // JS helper
875 @SuppressWarnings("unused")
876 public void showToast(String text)
877 {
878 Log.d(TAG, "Toast: " + text);
879 Message msg = messageHandler.obtainMessage(MSG_TOAST);
880 Bundle data = new Bundle();
881 data.putString("message", text);
882 msg.setData(data);
883 messageHandler.sendMessage(msg);
884 }
885
886 // JS helper
887 @SuppressWarnings("unused")
888 public void notify(long delay)
889 {
890 if (this.delay < 0 || delay < this.delay)
891 {
892 this.delay = delay;
893 synchronized (queue)
894 {
895 queue.notify();
896 }
897 }
898 }
899
900 public Object evaluate(String script)
901 {
902 return jsEngine.evaluate(script);
903 }
904
905 public void callback(long callback, Object[] params)
906 {
907 jsEngine.callback(callback, params);
908 }
909
910 public final void stopEngine()
911 {
912 run = false;
913 synchronized (queue)
914 {
915 queue.notify();
916 }
917 }
918
919 public void execute(Runnable r)
920 {
921 synchronized (queue)
922 {
923 queue.addLast(r);
924 queue.notify();
925 }
926 }
927
928 public <T> Future<T> submit(Callable<T> callable)
929 {
930 FutureTask<T> ftask = new FutureTask<T>(callable);
931 execute(ftask);
932 return ftask;
933 }
934
935 @Override
936 public final void run()
937 {
938 if (Thread.currentThread().getName().startsWith("Thread-"))
939 {
940 Thread.currentThread().setName("javascript");
941 }
942
943 jsEngine = new JSEngine(this);
944
945 jsEngine.put("_locale", Locale.getDefault().toString());
946 jsEngine.put("_datapath", getFilesDir().getAbsolutePath());
947 jsEngine.put("_separator", File.separator);
948 jsEngine.put("_version", getVersion());
949
950 try
951 {
952 jsEngine.evaluate("Android.load(\"start.js\");");
953 }
954 catch (Exception e)
955 {
956 Log.e(TAG, e.getMessage(), e);
957 }
958
959 while (run)
960 {
961 try
962 {
963 Runnable r = null;
964 synchronized (queue)
965 {
966 r = queue.poll();
967 }
968 if (r != null)
969 {
970 r.run();
971 }
972 else if (delay > 0)
973 {
974 long t = SystemClock.uptimeMillis();
975 synchronized (queue)
976 {
977 try
978 {
979 queue.wait(delay);
980 }
981 catch (InterruptedException e)
982 {
983 }
984 }
985 delay -= SystemClock.uptimeMillis() - t;
986 }
987 else if (delay <= 0)
988 {
989 delay = jsEngine.runCallbacks();
990 }
991 else
992 {
993 synchronized (queue)
994 {
995 try
996 {
997 queue.wait();
998 }
999 catch (InterruptedException e)
1000 {
1001 Log.e(TAG, e.getMessage(), e);
1002 }
1003 }
1004 }
1005 }
1006 catch (Exception e)
1007 {
1008 Log.e(TAG, e.getMessage(), e);
1009 }
1010 }
1011
1012 jsEngine.release();
1013 }
1014 }
1015
1016 /**
1017 * Helper class for XMLHttpRequest implementation.
1018 */
1019 private class Task
1020 {
1021 HttpURLConnection connection;
1022 long callback;
1023 }
1024
1025 /**
1026 * Helper class for XMLHttpRequest implementation.
1027 */
1028 private class Result
1029 {
1030 long callback;
1031 int code;
1032 String message;
1033 String data;
1034 Map<String, List<String>> headers;
1035 }
1036
1037 /**
1038 * Helper class for XMLHttpRequest implementation.
1039 */
1040 private class DownloadTask extends AsyncTask<Task, Integer, Result>
1041 {
1042 public DownloadTask(Context context)
1043 {
1044 }
1045
1046 @Override
1047 protected void onPreExecute()
1048 {
1049 }
1050
1051 @Override
1052 protected void onPostExecute(Result result)
1053 {
1054 if (result != null)
1055 {
1056 final long callback = result.callback;
1057 final Object[] params = new Object[4];
1058
1059 String[][] headers = null;
1060 if (result.headers != null)
1061 {
1062 headers = new String[result.headers.size()][2];
1063 int i = 0;
1064 for (String header : result.headers.keySet())
1065 {
1066 headers[i][0] = header;
1067 headers[i][1] = StringUtils.join(result.headers.get(header).toArray( ), "; ");
1068 i++;
1069 }
1070 }
1071 params[0] = result.code;
1072 params[1] = result.message;
1073 params[2] = headers;
1074 params[3] = result.data;
1075
1076 // Do not run callback if engine was stopped
1077 if (js == null)
1078 return;
1079
1080 js.execute(new Runnable()
1081 {
1082 @Override
1083 public void run()
1084 {
1085 js.callback(callback, params);
1086 }
1087
1088 });
1089 }
1090 }
1091
1092 @Override
1093 protected void onCancelled()
1094 {
1095 }
1096
1097 @Override
1098 protected Result doInBackground(Task... tasks)
1099 {
1100 Task task = tasks[0];
1101 Result result = new Result();
1102 result.callback = task.callback;
1103 try
1104 {
1105 HttpURLConnection connection = task.connection;
1106 connection.connect();
1107 int lenghtOfFile = connection.getContentLength();
1108 Log.d("D", "S: " + lenghtOfFile);
1109
1110 result.code = connection.getResponseCode();
1111 result.message = connection.getResponseMessage();
1112 result.headers = connection.getHeaderFields();
1113
1114 // download the file
1115 String encoding = connection.getContentEncoding();
1116 if (encoding == null)
1117 encoding = "utf-8";
1118 BufferedReader in = new BufferedReader(new InputStreamReader(connection. getInputStream(), encoding));
1119
1120 final char[] buffer = new char[0x10000];
1121 StringBuilder out = new StringBuilder();
1122 long total = 0;
1123 int read;
1124 do
1125 {
1126 read = in.read(buffer, 0, buffer.length);
1127 if (read > 0)
1128 {
1129 out.append(buffer, 0, read);
1130 total += read;
1131 publishProgress((int) (total * 100. / lenghtOfFile));
1132 }
1133 }
1134 while (!isCancelled() && read >= 0);
1135 result.data = out.toString();
1136 in.close();
1137 }
1138 catch (Exception e)
1139 {
1140 Log.e(TAG, e.getMessage(), e);
1141 result.data = "";
1142 result.code = HttpURLConnection.HTTP_INTERNAL_ERROR;
1143 result.message = e.toString();
1144 }
1145 return result;
1146 }
1147
1148 protected void onProgressUpdate(Integer... progress)
1149 {
1150 Log.d("HTTP", "Progress: " + progress[0].intValue());
1151 }
1152 }
1153 } 481 }
OLDNEW

Powered by Google App Engine
This is Rietveld