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