Left: | ||
Right: |
OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |