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 |