| 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 |