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

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

Issue 8478117: ABP/Android core (Closed)
Patch Set: Created Oct. 5, 2012, 9:29 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld