| Left: | ||
| Right: |
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 package org.adblockplus.android; | |
| 2 | |
| 3 import java.io.BufferedReader; | |
| 4 import java.io.File; | |
| 5 import java.io.FileInputStream; | |
| 6 import java.io.FileNotFoundException; | |
| 7 import java.io.FileOutputStream; | |
| 8 import java.io.IOException; | |
| 9 import java.io.InputStreamReader; | |
| 10 import java.net.HttpURLConnection; | |
| 11 import java.net.URL; | |
| 12 import java.util.ArrayList; | |
| 13 import java.util.Calendar; | |
| 14 import java.util.LinkedList; | |
| 15 import java.util.List; | |
| 16 import java.util.Locale; | |
| 17 import java.util.Map; | |
| 18 import java.util.TimeZone; | |
| 19 import java.util.concurrent.Callable; | |
| 20 import java.util.concurrent.ExecutionException; | |
| 21 import java.util.concurrent.Future; | |
| 22 import java.util.concurrent.FutureTask; | |
| 23 | |
| 24 import javax.xml.parsers.ParserConfigurationException; | |
| 25 import javax.xml.parsers.SAXParser; | |
| 26 import javax.xml.parsers.SAXParserFactory; | |
| 27 | |
| 28 import org.adblockplus.android.updater.AlarmReceiver; | |
| 29 import org.apache.commons.lang.StringEscapeUtils; | |
| 30 import org.apache.commons.lang.StringUtils; | |
| 31 import org.json.JSONException; | |
| 32 import org.json.JSONObject; | |
| 33 import org.xml.sax.SAXException; | |
| 34 | |
| 35 import android.app.AlarmManager; | |
| 36 import android.app.Application; | |
| 37 import android.app.PendingIntent; | |
| 38 import android.content.Context; | |
| 39 import android.content.Intent; | |
| 40 import android.content.SharedPreferences; | |
| 41 import android.content.pm.PackageManager; | |
| 42 import android.content.pm.PackageManager.NameNotFoundException; | |
| 43 import android.content.res.AssetManager; | |
| 44 import android.net.ConnectivityManager; | |
| 45 import android.net.NetworkInfo; | |
| 46 import android.os.AsyncTask; | |
| 47 import android.os.Bundle; | |
| 48 import android.os.Handler; | |
| 49 import android.os.Message; | |
| 50 import android.os.SystemClock; | |
| 51 import android.preference.PreferenceManager; | |
| 52 import android.util.Log; | |
| 53 import android.widget.Toast; | |
| 54 | |
| 55 public class AdblockPlus extends Application | |
| 56 { | |
| 57 private final static String TAG = "Application"; | |
| 58 | |
| 59 private final static int MSG_TOAST = 1; | |
| 60 | |
| 61 /** | |
| 62 * Broadcasted when subscription status changes. | |
| 63 */ | |
| 64 public final static String BROADCAST_SUBSCRIPTION_STATUS = "org.adblockplus.an droid.subscription.status"; | |
| 65 /** | |
| 66 * Broadcasted when filter match check is performed. | |
| 67 */ | |
| 68 public final static String BROADCAST_FILTER_MATCHES = "org.adblockplus.android .filter.matches"; | |
| 69 | |
| 70 private List<Subscription> subscriptions; | |
| 71 | |
| 72 private JSThread js; | |
| 73 | |
| 74 /** | |
| 75 * Indicates interactive mode (used to listen for subscription status | |
| 76 * changes). | |
| 77 */ | |
| 78 private boolean interactive = false; | |
| 79 | |
| 80 private boolean generateCrashReport = false; | |
| 81 | |
| 82 private static AdblockPlus myself; | |
|
Felix Dahlke
2012/10/29 15:06:36
Nit: Isn't "instance" a more typical name for a si
Andrey Novikov
2012/10/30 07:53:22
Done.
| |
| 83 | |
| 84 /** | |
| 85 * Returns pointer to itself (singleton pattern). | |
| 86 */ | |
| 87 public static AdblockPlus getApplication() | |
| 88 { | |
| 89 return myself; | |
| 90 } | |
| 91 | |
| 92 /** | |
| 93 * Checks if device has a WiFi connection available. | |
| 94 */ | |
| 95 public static boolean isWiFiConnected(Context context) | |
| 96 { | |
| 97 ConnectivityManager connectivityManager = (ConnectivityManager) context.getS ystemService(Context.CONNECTIVITY_SERVICE); | |
| 98 NetworkInfo networkInfo = null; | |
| 99 if (connectivityManager != null) | |
| 100 { | |
| 101 networkInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_ WIFI); | |
| 102 } | |
| 103 return networkInfo == null ? false : networkInfo.isConnected(); | |
| 104 } | |
| 105 | |
| 106 /** | |
| 107 * Checks if application can write to external storage. | |
| 108 */ | |
| 109 public boolean checkWriteExternalPermission() | |
| 110 { | |
| 111 | |
| 112 String permission = "android.permission.WRITE_EXTERNAL_STORAGE"; | |
| 113 int res = checkCallingOrSelfPermission(permission); | |
| 114 return res == PackageManager.PERMISSION_GRANTED; | |
| 115 } | |
| 116 | |
| 117 /** | |
| 118 * Returns list of known subscriptions. | |
| 119 */ | |
| 120 public List<Subscription> getSubscriptions() | |
| 121 { | |
| 122 if (subscriptions == null) | |
| 123 { | |
| 124 subscriptions = new ArrayList<Subscription>(); | |
| 125 | |
| 126 SAXParserFactory factory = SAXParserFactory.newInstance(); | |
| 127 SAXParser parser; | |
| 128 try | |
| 129 { | |
| 130 parser = factory.newSAXParser(); | |
| 131 parser.parse(getAssets().open("subscriptions.xml"), new SubscriptionPars er(subscriptions)); | |
| 132 } | |
| 133 catch (ParserConfigurationException e) | |
|
Felix Dahlke
2012/10/29 15:06:36
Can we log the following exceptions as an error in
Andrey Novikov
2012/10/30 07:53:22
printStackTrace does pretty much the same, I use i
Felix Dahlke
2012/10/30 08:08:42
I think Log.e() is the canonical way to log except
| |
| 134 { | |
| 135 // TODO Auto-generated catch block | |
| 136 e.printStackTrace(); | |
| 137 } | |
| 138 catch (SAXException e) | |
| 139 { | |
| 140 // TODO Auto-generated catch block | |
| 141 e.printStackTrace(); | |
| 142 } | |
| 143 catch (IOException e) | |
| 144 { | |
| 145 // TODO Auto-generated catch block | |
| 146 e.printStackTrace(); | |
| 147 } | |
| 148 } | |
| 149 return subscriptions; | |
| 150 } | |
| 151 | |
| 152 /** | |
| 153 * Returns subscription information. | |
| 154 * | |
| 155 * @param url | |
| 156 * subscription url | |
| 157 */ | |
| 158 public Subscription getSubscription(String url) | |
| 159 { | |
| 160 List<Subscription> subscriptions = getSubscriptions(); | |
| 161 | |
| 162 for (Subscription subscription : subscriptions) | |
| 163 { | |
| 164 if (subscription.url.equals(url)) | |
| 165 return subscription; | |
| 166 } | |
| 167 return null; | |
| 168 } | |
| 169 | |
| 170 /** | |
| 171 * Adds provided subscription and removes previous subscriptions if any. | |
| 172 * | |
| 173 * @param subscription | |
| 174 * Subscription to add | |
| 175 */ | |
| 176 public void setSubscription(Subscription subscription) | |
| 177 { | |
| 178 /* | |
| 179 * Subscription test = new Subscription(); | |
|
Felix Dahlke
2012/10/29 15:06:36
Can we remove this commented code? It's in source
Andrey Novikov
2012/10/30 07:53:22
Done.
| |
| 180 * test.url = | |
| 181 * "https://easylist-downloads.adblockplus.org/exceptionrules.txt"; | |
| 182 * test.title = "Test"; | |
| 183 * test.homepage = "https://easylist-downloads.adblockplus.org/"; | |
| 184 * selectedItem = test; | |
| 185 */ | |
| 186 | |
| 187 if (subscription != null) | |
| 188 { | |
| 189 final JSONObject jsonSub = new JSONObject(); | |
| 190 try | |
| 191 { | |
| 192 jsonSub.put("url", subscription.url); | |
| 193 jsonSub.put("title", subscription.title); | |
| 194 jsonSub.put("homepage", subscription.homepage); | |
| 195 js.execute(new Runnable() { | |
| 196 @Override | |
| 197 public void run() | |
| 198 { | |
| 199 js.evaluate("clearSubscriptions()"); | |
| 200 js.evaluate("addSubscription(\"" + StringEscapeUtils.escapeJavaScrip t(jsonSub.toString()) + "\")"); | |
| 201 } | |
| 202 }); | |
| 203 } | |
| 204 catch (JSONException e) | |
| 205 { | |
| 206 // TODO Auto-generated catch block | |
|
Felix Dahlke
2012/10/29 15:06:36
Can we log this exception as an error instead of u
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 207 e.printStackTrace(); | |
| 208 } | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 /** | |
| 213 * Forces subscriptions refresh. | |
| 214 */ | |
| 215 public void refreshSubscription() | |
| 216 { | |
| 217 js.execute(new Runnable() { | |
| 218 @Override | |
| 219 public void run() | |
| 220 { | |
| 221 js.evaluate("refreshSubscriptions()"); | |
| 222 } | |
| 223 }); | |
| 224 } | |
| 225 | |
| 226 /** | |
| 227 * Selects which subscription to offer for the first time. | |
| 228 * | |
| 229 * @return offered subscription | |
| 230 */ | |
| 231 public Subscription offerSubscription() | |
| 232 { | |
| 233 Subscription selectedItem = null; | |
| 234 String selectedPrefix = null; | |
| 235 int matchCount = 0; | |
| 236 for (Subscription subscription : getSubscriptions()) | |
| 237 { | |
| 238 if (selectedItem == null) | |
| 239 selectedItem = subscription; | |
| 240 | |
| 241 String prefix = checkLocalePrefixMatch(subscription.prefixes); | |
| 242 if (prefix != null) | |
| 243 { | |
| 244 if (selectedPrefix == null || selectedPrefix.length() < prefix.length()) | |
| 245 { | |
| 246 selectedItem = subscription; | |
| 247 selectedPrefix = prefix; | |
| 248 matchCount = 1; | |
| 249 } | |
| 250 else if (selectedPrefix != null && selectedPrefix.length() == prefix.len gth()) | |
| 251 { | |
| 252 matchCount++; | |
| 253 | |
| 254 // If multiple items have a matching prefix of the | |
| 255 // same length select one of the items randomly, | |
| 256 // probability should be the same for all items. | |
| 257 // So we replace the previous match here with | |
| 258 // probability 1/N (N being the number of matches). | |
| 259 if (Math.random() * matchCount < 1) | |
| 260 { | |
| 261 selectedItem = subscription; | |
| 262 selectedPrefix = prefix; | |
| 263 } | |
| 264 } | |
| 265 } | |
| 266 } | |
| 267 return selectedItem; | |
| 268 } | |
| 269 | |
| 270 /** | |
| 271 * Verifies that subscriptions are loaded and returns flag of subscription | |
| 272 * presence. | |
| 273 * | |
| 274 * @return true if at least one subscription is present and downloaded | |
| 275 */ | |
| 276 public boolean verifySubscriptions() | |
| 277 { | |
| 278 Future<Boolean> future = js.submit(new Callable<Boolean>() { | |
| 279 @Override | |
| 280 public Boolean call() throws Exception | |
| 281 { | |
| 282 Boolean result = (Boolean) js.evaluate("verifySubscriptions()"); | |
| 283 return result; | |
| 284 } | |
| 285 }); | |
| 286 try | |
| 287 { | |
| 288 return future.get().booleanValue(); | |
| 289 } | |
| 290 catch (InterruptedException e) | |
| 291 { | |
| 292 // TODO Auto-generated catch block | |
|
Felix Dahlke
2012/10/29 15:06:36
Like above, log an error instead?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 293 e.printStackTrace(); | |
| 294 } | |
| 295 catch (ExecutionException e) | |
| 296 { | |
| 297 // TODO Auto-generated catch block | |
| 298 e.printStackTrace(); | |
| 299 } | |
| 300 return false; | |
| 301 } | |
| 302 | |
| 303 /** | |
| 304 * Returns ElemHide selectors for domain. | |
| 305 * | |
| 306 * @return ready to use HTML element with CSS selectors | |
| 307 */ | |
| 308 public String getSelectorsForDomain(final String domain) | |
| 309 { | |
| 310 Future<String> future = js.submit(new Callable<String>() { | |
|
Felix Dahlke
2012/10/29 15:06:36
Is the future really necessary here? It seems you
Andrey Novikov
2012/10/30 07:53:22
No, I can't. JS runs in separate asynchronous thre
Felix Dahlke
2012/10/30 08:08:42
Oh, I see.
| |
| 311 @Override | |
| 312 public String call() throws Exception | |
| 313 { | |
| 314 String result = (String) js.evaluate("ElemHide.getSelectorsForDomain('" + domain + "')"); | |
| 315 return result; | |
| 316 } | |
| 317 }); | |
| 318 try | |
| 319 { | |
| 320 return future.get(); | |
| 321 } | |
| 322 catch (InterruptedException e) | |
| 323 { | |
| 324 // TODO Auto-generated catch block | |
|
Felix Dahlke
2012/10/29 15:06:36
Like above, error logging.
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 325 e.printStackTrace(); | |
| 326 } | |
| 327 catch (ExecutionException e) | |
| 328 { | |
| 329 // TODO Auto-generated catch block | |
| 330 e.printStackTrace(); | |
| 331 } | |
| 332 return null; | |
| 333 } | |
| 334 | |
| 335 private class MatchesCallable implements Callable<Boolean> | |
| 336 { | |
| 337 private String url; | |
| 338 private String query; | |
| 339 private String reqHost; | |
| 340 private String refHost; | |
| 341 private String accept; | |
| 342 | |
| 343 MatchesCallable(String url, String query, String reqHost, String refHost, St ring accept) | |
| 344 { | |
| 345 this.url = url; | |
| 346 this.query = query; | |
| 347 this.reqHost = reqHost != null ? reqHost : ""; | |
| 348 this.refHost = refHost != null ? refHost : ""; | |
| 349 this.accept = accept != null ? accept : ""; | |
| 350 } | |
| 351 | |
| 352 @Override | |
| 353 public Boolean call() throws Exception | |
| 354 { | |
| 355 Boolean result = (Boolean) js.evaluate("matchesAny('" + url + "', '" + que ry + "', '" + reqHost + "', '" + refHost + "', '" + accept + "');"); | |
|
Felix Dahlke
2012/10/29 15:06:36
Nit: You could just return this directly
Andrey Novikov
2012/10/30 07:53:22
See above.
| |
| 356 return result; | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 /** | |
| 361 * Checks if filters match request parameters. | |
| 362 * | |
| 363 * @param url | |
| 364 * Request URL | |
| 365 * @param query | |
| 366 * Request query string | |
| 367 * @param reqHost | |
| 368 * Request host | |
| 369 * @param refHost | |
| 370 * Request referrer header | |
| 371 * @param accept | |
| 372 * Request accept header | |
| 373 * @return true if matched filter was found | |
| 374 * @throws Exception | |
| 375 */ | |
| 376 public boolean matches(String url, String query, String reqHost, String refHos t, String accept) throws Exception | |
| 377 { | |
| 378 Callable<Boolean> callable = new MatchesCallable(url, query, reqHost, refHos t, accept); | |
| 379 Future<Boolean> future = js.submit(callable); | |
| 380 boolean matches = future.get().booleanValue(); | |
| 381 sendBroadcast(new Intent(BROADCAST_FILTER_MATCHES).putExtra("url", url).putE xtra("matches", matches)); | |
| 382 return matches; | |
| 383 } | |
| 384 | |
| 385 /** | |
| 386 * Notifies JS code that application entered interactive mode. | |
| 387 */ | |
| 388 public void startInteractive() | |
| 389 { | |
| 390 js.execute(new Runnable() { | |
| 391 @Override | |
| 392 public void run() | |
| 393 { | |
| 394 js.evaluate("startInteractive()"); | |
| 395 } | |
| 396 }); | |
| 397 interactive = true; | |
| 398 } | |
| 399 | |
| 400 /** | |
| 401 * Notifies JS code that application quit interactive mode. | |
| 402 */ | |
| 403 public void stopInteractive() | |
| 404 { | |
| 405 js.execute(new Runnable() { | |
| 406 @Override | |
| 407 public void run() | |
| 408 { | |
| 409 js.evaluate("stopInteractive()"); | |
| 410 } | |
| 411 }); | |
| 412 interactive = false; | |
| 413 } | |
| 414 | |
| 415 /** | |
| 416 * Returns prefixes that match current user locale. | |
| 417 */ | |
| 418 public String checkLocalePrefixMatch(String[] prefixes) | |
| 419 { | |
| 420 if (prefixes == null || prefixes.length == 0) | |
| 421 return null; | |
| 422 | |
| 423 String locale = Locale.getDefault().toString().toLowerCase(); | |
| 424 | |
| 425 for (int i = 0; i < prefixes.length; i++) | |
| 426 if (locale.startsWith(prefixes[i].toLowerCase())) | |
| 427 return prefixes[i]; | |
| 428 | |
| 429 return null; | |
| 430 } | |
| 431 | |
| 432 /** | |
| 433 * Starts JS engine. It also initiates subscription refresh if it is enabled | |
| 434 * in user settings. | |
| 435 */ | |
| 436 public void startEngine() | |
| 437 { | |
| 438 if (js == null) | |
| 439 { | |
| 440 Log.e(TAG, "startEngine"); | |
|
Felix Dahlke
2012/10/29 15:06:36
Shouldn't this be Log.d()?
Andrey Novikov
2012/10/30 07:53:22
I use logging not only for error reporting but als
Felix Dahlke
2012/10/30 08:08:42
That's fine for debugging of course, but I think w
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 441 js = new JSThread(this); | |
| 442 js.start(); | |
| 443 | |
| 444 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferen ces(this); | |
| 445 final int refresh = Integer.valueOf(prefs.getString(getString(R.string.pre f_refresh), Integer.toString(getResources().getInteger(R.integer.def_refresh)))) ; | |
|
Felix Dahlke
2012/10/29 15:06:36
We should use a fixed string for storing these pre
Andrey Novikov
2012/10/30 07:53:22
These are not locale dependent. Locale dependent s
Felix Dahlke
2012/10/30 08:08:42
The string "pref_refresh" is "Subscription refresh
Andrey Novikov
2012/10/30 09:24:17
No, the string pref_refresh is "refresh", and what
Felix Dahlke
2012/10/30 09:30:50
True, repeated string literals aren't great. How a
| |
| 446 final boolean wifionly = prefs.getBoolean(getString(R.string.pref_wifirefr esh), getResources().getBoolean(R.bool.def_wifirefresh)); | |
| 447 // Refresh if user selected refresh on each start | |
| 448 if (refresh == 1 && (!wifionly || isWiFiConnected(this))) | |
| 449 { | |
| 450 refreshSubscription(); | |
| 451 } | |
| 452 } | |
| 453 } | |
| 454 | |
| 455 /** | |
| 456 * Stops JS engine. | |
| 457 * | |
| 458 * @param implicitly | |
| 459 * stop even in interactive mode | |
| 460 */ | |
| 461 public void stopEngine(boolean implicitly) | |
| 462 { | |
| 463 if ((implicitly || !interactive) && js != null) | |
| 464 { | |
| 465 js.stopEngine(); | |
| 466 try | |
| 467 { | |
| 468 js.join(); | |
| 469 } | |
| 470 catch (InterruptedException e) | |
| 471 { | |
| 472 e.printStackTrace(); | |
|
Felix Dahlke
2012/10/29 15:06:36
As before, error logging?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 473 } | |
| 474 js = null; | |
| 475 } | |
| 476 } | |
| 477 | |
| 478 /** | |
| 479 * Sets or removes crash handler according to user setting | |
| 480 */ | |
| 481 public void updateCrashReportStatus() | |
| 482 { | |
| 483 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this ); | |
| 484 boolean report = prefs.getBoolean(getString(R.string.pref_crashreport), getR esources().getBoolean(R.bool.def_crashreport)); | |
| 485 if (report != generateCrashReport) | |
| 486 { | |
| 487 if (report) | |
| 488 { | |
| 489 Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(this)); | |
| 490 } | |
| 491 else | |
| 492 { | |
| 493 try | |
| 494 { | |
| 495 CrashHandler handler = (CrashHandler) Thread.getDefaultUncaughtExcepti onHandler(); | |
| 496 Thread.setDefaultUncaughtExceptionHandler(handler.getDefault()); | |
| 497 } | |
| 498 catch (ClassCastException e) | |
| 499 { | |
| 500 // ignore - already default handler | |
| 501 } | |
| 502 } | |
| 503 generateCrashReport = report; | |
| 504 } | |
| 505 } | |
| 506 | |
| 507 /** | |
| 508 * Sets Alarm to call updater after specified number of minutes or after one d ay if | |
| 509 * minutes are set to 0. | |
| 510 * | |
| 511 * @param minutes | |
| 512 * number of minutes to wait | |
| 513 */ | |
| 514 public void scheduleUpdater(int minutes) | |
| 515 { | |
| 516 Calendar updateTime = Calendar.getInstance(); | |
| 517 | |
| 518 if (minutes == 0) | |
| 519 { | |
| 520 // Start update checks at 10:00 GMT... | |
| 521 updateTime.setTimeZone(TimeZone.getTimeZone("GMT")); | |
| 522 updateTime.set(Calendar.HOUR_OF_DAY, 10); | |
| 523 updateTime.set(Calendar.MINUTE, 0); | |
| 524 // ...next day | |
| 525 updateTime.add(Calendar.HOUR_OF_DAY, 24); | |
| 526 // Spread out the “mass downloading” for 6 hours | |
| 527 updateTime.add(Calendar.MINUTE, (int) Math.random() * 60 * 6); | |
| 528 } | |
| 529 else | |
| 530 { | |
| 531 updateTime.add(Calendar.MINUTE, minutes); | |
| 532 } | |
| 533 | |
| 534 Intent updater = new Intent(this, AlarmReceiver.class); | |
| 535 PendingIntent recurringUpdate = PendingIntent.getBroadcast(this, 0, updater, PendingIntent.FLAG_CANCEL_CURRENT); | |
| 536 // Set non-waking alarm | |
| 537 AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE) ; | |
| 538 Log.e(TAG, "Set updater alarm"); | |
|
Felix Dahlke
2012/10/29 15:06:36
I guess Log.d() is more appropriate here?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 539 alarms.set(AlarmManager.RTC, updateTime.getTimeInMillis(), recurringUpdate); | |
| 540 } | |
| 541 | |
| 542 @Override | |
| 543 public void onCreate() | |
| 544 { | |
| 545 super.onCreate(); | |
| 546 myself = this; | |
| 547 | |
| 548 // Check for crash report | |
| 549 try | |
| 550 { | |
| 551 InputStreamReader reportFile = new InputStreamReader(openFileInput(CrashHa ndler.REPORT_FILE)); | |
| 552 final char[] buffer = new char[0x1000]; | |
| 553 StringBuilder out = new StringBuilder(); | |
| 554 int read; | |
| 555 do | |
| 556 { | |
| 557 read = reportFile.read(buffer, 0, buffer.length); | |
| 558 if (read > 0) | |
| 559 out.append(buffer, 0, read); | |
| 560 } | |
| 561 while (read >= 0); | |
| 562 String report = out.toString(); | |
| 563 if (!"".equals(report)) | |
| 564 { | |
| 565 final Intent intent = new Intent(this, CrashReportDialog.class); | |
| 566 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | |
| 567 intent.putExtra("report", report); | |
| 568 startActivity(intent); | |
| 569 } | |
| 570 } | |
| 571 catch (FileNotFoundException e) | |
| 572 { | |
| 573 // ignore | |
| 574 } | |
| 575 catch (IOException e) | |
| 576 { | |
| 577 // TODO Auto-generated catch block | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.e()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 578 e.printStackTrace(); | |
| 579 } | |
| 580 | |
| 581 if (!getResources().getBoolean(R.bool.def_release)) | |
| 582 { | |
| 583 // Set crash handler | |
| 584 updateCrashReportStatus(); | |
| 585 // Initiate update check | |
| 586 scheduleUpdater(0); | |
| 587 } | |
| 588 } | |
| 589 | |
| 590 /** | |
| 591 * Handler for showing toast messages from JS code. | |
| 592 */ | |
| 593 private final Handler messageHandler = new Handler() { | |
| 594 public void handleMessage(Message msg) | |
| 595 { | |
| 596 if (msg.what == MSG_TOAST) | |
| 597 { | |
| 598 Toast.makeText(AdblockPlus.this, msg.getData().getString("message"), Toa st.LENGTH_LONG).show(); | |
| 599 } | |
| 600 } | |
| 601 }; | |
| 602 | |
| 603 /** | |
| 604 * JS execution thread. | |
| 605 */ | |
| 606 private final class JSThread extends Thread | |
| 607 { | |
| 608 private JSEngine jsEngine; | |
| 609 private volatile boolean run = true; | |
| 610 private Context context; | |
| 611 private final LinkedList<Runnable> queue = new LinkedList<Runnable>(); | |
| 612 private long delay = -1; | |
| 613 | |
| 614 JSThread(Context context) | |
| 615 { | |
| 616 this.context = context; | |
| 617 } | |
| 618 | |
| 619 // JS helper | |
| 620 @SuppressWarnings("unused") | |
| 621 public String readJSFile(String name) | |
| 622 { | |
| 623 String result = ""; | |
| 624 AssetManager assetManager = getAssets(); | |
| 625 try | |
| 626 { | |
| 627 InputStreamReader reader = new InputStreamReader(assetManager.open("js" + File.separator + name)); | |
| 628 final char[] buffer = new char[0x10000]; | |
| 629 StringBuilder out = new StringBuilder(); | |
| 630 int read; | |
| 631 do | |
| 632 { | |
| 633 read = reader.read(buffer, 0, buffer.length); | |
| 634 if (read > 0) | |
| 635 out.append(buffer, 0, read); | |
| 636 } | |
| 637 while (read >= 0); | |
| 638 result = out.toString(); | |
| 639 } | |
| 640 catch (IOException e) | |
| 641 { | |
| 642 e.printStackTrace(); | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.e()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 643 } | |
| 644 return result; | |
| 645 } | |
| 646 | |
| 647 // JS helper | |
| 648 public FileInputStream getInputStream(String path) | |
| 649 { | |
| 650 Log.e(TAG, path); | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.d()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 651 File f = new File(path); | |
| 652 try | |
| 653 { | |
| 654 return openFileInput(f.getName()); | |
| 655 } | |
| 656 catch (FileNotFoundException e) | |
| 657 { | |
| 658 e.printStackTrace(); | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.e()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 659 } | |
| 660 return null; | |
| 661 } | |
| 662 | |
| 663 // JS helper | |
| 664 public FileOutputStream getOutputStream(String path) | |
| 665 { | |
| 666 Log.e(TAG, path); | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.d()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 667 File f = new File(path); | |
| 668 try | |
| 669 { | |
| 670 return openFileOutput(f.getName(), MODE_PRIVATE); | |
| 671 } | |
| 672 catch (FileNotFoundException e) | |
| 673 { | |
| 674 e.printStackTrace(); | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.e()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 675 } | |
| 676 return null; | |
| 677 } | |
| 678 | |
| 679 // JS helper | |
| 680 public String getVersion() | |
| 681 { | |
| 682 String versionName = null; | |
| 683 try | |
| 684 { | |
| 685 versionName = getPackageManager().getPackageInfo(getPackageName(), 0).ve rsionName; | |
| 686 } | |
| 687 catch (NameNotFoundException ex) | |
| 688 { | |
| 689 versionName = "n/a"; | |
| 690 } | |
| 691 return versionName; | |
| 692 } | |
| 693 | |
| 694 // JS helper | |
| 695 @SuppressWarnings("unused") | |
| 696 public boolean canAutoupdate() | |
| 697 { | |
| 698 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferen ces(context); | |
| 699 final int refresh = Integer.valueOf(prefs.getString(getString(R.string.pre f_refresh), Integer.toString(context.getResources().getInteger(R.integer.def_ref resh)))); | |
| 700 final boolean wifionly = prefs.getBoolean(getString(R.string.pref_wifirefr esh), getResources().getBoolean(R.bool.def_wifirefresh)); | |
| 701 return refresh == 2 && (!wifionly || isWiFiConnected(context)); | |
| 702 } | |
| 703 | |
| 704 // JS helper | |
| 705 @SuppressWarnings("unused") | |
| 706 public void httpSend(final String method, final String url, final String[][] headers, final boolean async, final long callback) | |
| 707 { | |
| 708 Log.e(TAG, "httpSend('" + method + "', '" + url + "')"); | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.d()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 709 messageHandler.post(new Runnable() { | |
| 710 @Override | |
| 711 public void run() | |
| 712 { | |
| 713 try | |
| 714 { | |
| 715 Task task = new Task(); | |
| 716 task.callback = callback; | |
| 717 task.connection = (HttpURLConnection) new URL(url).openConnection(); | |
| 718 task.connection.setRequestMethod(method); | |
| 719 for (int i = 0; i < headers.length; i++) | |
| 720 { | |
| 721 task.connection.setRequestProperty(headers[i][0], headers[i][1]); | |
| 722 } | |
| 723 DownloadTask downloadTask = new DownloadTask(context); | |
| 724 downloadTask.execute(task); | |
| 725 if (!async) | |
| 726 { | |
| 727 downloadTask.get(); | |
| 728 } | |
| 729 } | |
| 730 catch (Exception e) | |
| 731 { | |
| 732 e.printStackTrace(); | |
| 733 js.callback(callback, null); | |
| 734 } | |
| 735 } | |
| 736 }); | |
| 737 } | |
| 738 | |
| 739 // JS helper | |
| 740 @SuppressWarnings("unused") | |
|
Felix Dahlke
2012/10/29 15:06:36
I don't think this is necessary
Andrey Novikov
2012/10/30 07:53:22
This is necessary because Eclipse highlights all w
Felix Dahlke
2012/10/30 08:08:42
I know what it's for, but I can't see any unused v
Andrey Novikov
2012/10/30 09:24:17
It's not about variables but about the method itse
Felix Dahlke
2012/10/30 09:30:50
Ah, I see. You only need that code for debugging?
Andrey Novikov
2012/10/30 09:45:28
No, it's called by JS engine from native code, tha
| |
| 741 public void setStatus(String text, long time) | |
| 742 { | |
| 743 sendBroadcast(new Intent(BROADCAST_SUBSCRIPTION_STATUS).putExtra("text", t ext).putExtra("time", time)); | |
| 744 } | |
| 745 | |
| 746 // JS helper | |
| 747 @SuppressWarnings("unused") | |
|
Felix Dahlke
2012/10/29 15:06:36
I don't think this is necessary
| |
| 748 public void showToast(String text) | |
| 749 { | |
| 750 Log.e(TAG, "Toast: " + text); | |
| 751 Message msg = messageHandler.obtainMessage(MSG_TOAST); | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.d()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 752 Bundle data = new Bundle(); | |
| 753 data.putString("message", text); | |
| 754 msg.setData(data); | |
| 755 messageHandler.sendMessage(msg); | |
| 756 } | |
| 757 | |
| 758 // JS helper | |
| 759 @SuppressWarnings("unused") | |
|
Felix Dahlke
2012/10/29 15:06:36
I don't think this is necessary
| |
| 760 public void notify(long delay) | |
| 761 { | |
| 762 if (this.delay < 0 || delay < this.delay) | |
| 763 { | |
| 764 this.delay = delay; | |
| 765 synchronized (queue) | |
| 766 { | |
| 767 queue.notify(); | |
| 768 } | |
| 769 } | |
| 770 } | |
| 771 | |
| 772 public Object evaluate(String script) | |
| 773 { | |
| 774 return jsEngine.evaluate(script); | |
| 775 } | |
| 776 | |
| 777 public void callback(long callback, Object[] params) | |
| 778 { | |
| 779 jsEngine.callback(callback, params); | |
| 780 } | |
| 781 | |
| 782 public final void stopEngine() | |
| 783 { | |
| 784 run = false; | |
| 785 synchronized (queue) | |
| 786 { | |
| 787 queue.notify(); | |
| 788 } | |
| 789 } | |
| 790 | |
| 791 public void execute(Runnable r) | |
| 792 { | |
| 793 synchronized (queue) | |
| 794 { | |
| 795 queue.addLast(r); | |
| 796 queue.notify(); | |
| 797 } | |
| 798 } | |
| 799 | |
| 800 public <T> Future<T> submit(Callable<T> callable) | |
| 801 { | |
| 802 FutureTask<T> ftask = new FutureTask<T>(callable); | |
| 803 execute(ftask); | |
| 804 return ftask; | |
| 805 } | |
| 806 | |
| 807 @Override | |
| 808 public final void run() | |
| 809 { | |
| 810 jsEngine = new JSEngine(this); | |
| 811 | |
| 812 jsEngine.put("_locale", Locale.getDefault().toString()); | |
| 813 jsEngine.put("_datapath", getFilesDir().getAbsolutePath()); | |
| 814 jsEngine.put("_separator", File.separator); | |
| 815 jsEngine.put("_version", getVersion()); | |
| 816 | |
| 817 try | |
| 818 { | |
| 819 jsEngine.evaluate("Android.load(\"start.js\");"); | |
| 820 } | |
| 821 catch (Exception e) | |
| 822 { | |
| 823 e.printStackTrace(); | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.e()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 824 } | |
| 825 | |
| 826 while (run) | |
| 827 { | |
| 828 try | |
| 829 { | |
| 830 Runnable r = null; | |
| 831 synchronized (queue) | |
| 832 { | |
| 833 r = queue.poll(); | |
| 834 } | |
| 835 if (r != null) | |
| 836 { | |
| 837 r.run(); | |
| 838 } | |
| 839 else if (delay > 0) | |
| 840 { | |
| 841 long t = SystemClock.uptimeMillis(); | |
| 842 synchronized (queue) | |
| 843 { | |
| 844 try | |
| 845 { | |
| 846 queue.wait(delay); | |
| 847 } | |
| 848 catch (InterruptedException e) | |
| 849 { | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.e()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 850 } | |
| 851 } | |
| 852 delay -= SystemClock.uptimeMillis() - t; | |
| 853 } | |
| 854 else if (delay <= 0) | |
| 855 { | |
| 856 delay = jsEngine.runCallbacks(); | |
| 857 } | |
| 858 else | |
| 859 { | |
| 860 synchronized (queue) | |
| 861 { | |
| 862 try | |
| 863 { | |
| 864 queue.wait(); | |
| 865 } | |
| 866 catch (InterruptedException e) | |
| 867 { | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.e()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 868 } | |
| 869 } | |
| 870 } | |
| 871 } | |
| 872 catch (Exception e) | |
| 873 { | |
| 874 e.printStackTrace(); | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.e()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 875 } | |
| 876 } | |
| 877 | |
| 878 jsEngine.release(); | |
| 879 } | |
| 880 } | |
| 881 | |
| 882 /** | |
| 883 * Helper class for XMLHttpRequest implementation. | |
| 884 */ | |
| 885 private class Task | |
| 886 { | |
| 887 HttpURLConnection connection; | |
| 888 long callback; | |
| 889 } | |
| 890 | |
| 891 /** | |
| 892 * Helper class for XMLHttpRequest implementation. | |
| 893 */ | |
| 894 private class Result | |
| 895 { | |
| 896 long callback; | |
| 897 int code; | |
| 898 String message; | |
| 899 String data; | |
| 900 Map<String, List<String>> headers; | |
| 901 } | |
| 902 | |
| 903 /** | |
| 904 * Helper class for XMLHttpRequest implementation. | |
| 905 */ | |
| 906 private class DownloadTask extends AsyncTask<Task, Integer, Result> | |
| 907 { | |
| 908 public DownloadTask(Context context) | |
| 909 { | |
| 910 } | |
| 911 | |
| 912 @Override | |
| 913 protected void onPreExecute() | |
| 914 { | |
| 915 } | |
| 916 | |
| 917 @Override | |
| 918 protected void onPostExecute(Result result) | |
| 919 { | |
| 920 if (result != null) | |
| 921 { | |
| 922 final long callback = result.callback; | |
| 923 final Object[] params = new Object[4]; | |
| 924 | |
| 925 String[][] headers = null; | |
| 926 if (result.headers != null) | |
| 927 { | |
| 928 headers = new String[result.headers.size()][2]; | |
| 929 int i = 0; | |
| 930 for (String header : result.headers.keySet()) | |
| 931 { | |
| 932 headers[i][0] = header; | |
| 933 headers[i][1] = StringUtils.join(result.headers.get(header).toArray( ), "; "); | |
| 934 i++; | |
| 935 } | |
| 936 } | |
| 937 params[0] = result.code; | |
| 938 params[1] = result.message; | |
| 939 params[2] = headers; | |
| 940 params[3] = result.data; | |
| 941 js.execute(new Runnable() { | |
| 942 @Override | |
| 943 public void run() | |
| 944 { | |
| 945 js.callback(callback, params); | |
| 946 } | |
| 947 | |
| 948 }); | |
| 949 } | |
| 950 } | |
| 951 | |
| 952 @Override | |
| 953 protected void onCancelled() | |
| 954 { | |
| 955 } | |
| 956 | |
| 957 @Override | |
| 958 protected Result doInBackground(Task... tasks) | |
| 959 { | |
| 960 Task task = tasks[0]; | |
| 961 Result result = new Result(); | |
| 962 result.callback = task.callback; | |
| 963 try | |
| 964 { | |
| 965 HttpURLConnection connection = task.connection; | |
| 966 connection.connect(); | |
| 967 int lenghtOfFile = connection.getContentLength(); | |
| 968 Log.w("D", "S: " + lenghtOfFile); | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.d()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 969 | |
| 970 result.code = connection.getResponseCode(); | |
| 971 result.message = connection.getResponseMessage(); | |
| 972 result.headers = connection.getHeaderFields(); | |
| 973 | |
| 974 // download the file | |
| 975 String encoding = connection.getContentEncoding(); | |
| 976 if (encoding == null) | |
| 977 encoding = "utf-8"; | |
| 978 BufferedReader in = new BufferedReader(new InputStreamReader(connection. getInputStream(), encoding)); | |
| 979 | |
| 980 final char[] buffer = new char[0x10000]; | |
|
Felix Dahlke
2012/10/29 15:06:36
Reminds me of the code up there, maybe have a sing
Andrey Novikov
2012/10/30 07:53:22
No, I can't. At least do not know how. Because her
| |
| 981 StringBuilder out = new StringBuilder(); | |
| 982 long total = 0; | |
| 983 int read; | |
| 984 do | |
| 985 { | |
| 986 read = in.read(buffer, 0, buffer.length); | |
| 987 if (read > 0) | |
| 988 { | |
| 989 out.append(buffer, 0, read); | |
| 990 total += read; | |
| 991 publishProgress((int) (total * 100. / lenghtOfFile)); | |
| 992 } | |
| 993 } | |
| 994 while (!isCancelled() && read >= 0); | |
| 995 result.data = out.toString(); | |
| 996 in.close(); | |
| 997 } | |
| 998 catch (Exception e) | |
| 999 { | |
| 1000 e.printStackTrace(); | |
|
Felix Dahlke
2012/10/29 15:06:36
Log.e()?
Andrey Novikov
2012/10/30 09:24:17
Done.
| |
| 1001 result.data = ""; | |
| 1002 result.code = HttpURLConnection.HTTP_INTERNAL_ERROR; | |
| 1003 result.message = e.toString(); | |
| 1004 } | |
| 1005 return result; | |
| 1006 } | |
| 1007 | |
| 1008 protected void onProgressUpdate(Integer... progress) | |
| 1009 { | |
| 1010 Log.i("HTTP", "Progress: " + progress[0].intValue()); | |
| 1011 } | |
| 1012 } | |
| 1013 } | |
| OLD | NEW |