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

Side by Side Diff: adblockplussbrowser/src/main/java/org/adblockplus/sbrowser/contentblocker/engine/Engine.java

Issue 29603697: Issue 5931 - Create tests for util package (Closed)
Patch Set: Created Nov. 10, 2017, 2:23 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-present eyeo GmbH
4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package org.adblockplus.sbrowser.contentblocker.engine;
19
20 import java.io.BufferedReader;
21 import java.io.BufferedWriter;
22 import java.io.File;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.io.OutputStreamWriter;
28 import java.io.Writer;
29 import java.net.URI;
30 import java.net.URISyntaxException;
31 import java.net.URL;
32 import java.net.URLEncoder;
33 import java.nio.charset.StandardCharsets;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.TreeSet;
41 import java.util.concurrent.LinkedBlockingQueue;
42 import java.util.concurrent.TimeUnit;
43 import java.util.concurrent.locks.ReentrantLock;
44
45 import org.adblockplus.adblockplussbrowser.R;
46 import org.adblockplus.sbrowser.contentblocker.util.SharedPrefsUtils;
47 import org.adblockplus.sbrowser.contentblocker.util.SubscriptionUtils;
48
49 import android.content.Context;
50 import android.content.Intent;
51 import android.content.pm.PackageInfo;
52 import android.content.pm.PackageManager;
53 import android.content.pm.ResolveInfo;
54 import android.net.ConnectivityManager;
55 import android.net.NetworkInfo;
56 import android.net.Uri;
57 import android.os.Handler;
58 import android.os.Looper;
59 import android.support.annotation.VisibleForTesting;
60 import android.text.TextUtils;
61 import android.text.format.DateUtils;
62 import android.util.Log;
63
64 public final class Engine
65 {
66 private static final String TAG = Engine.class.getSimpleName();
67
68 public static final String USER_FILTERS_TITLE = "__filters";
69 public static final String USER_EXCEPTIONS_TITLE = "__exceptions";
70
71 public static final String SBROWSER_APP_ID = "com.sec.android.app.sbrowser";
72 public static final String EASYLIST_URL = "https://easylist-downloads.adblockp lus.org/easylist.txt";
73 private static final String ACTION_OPEN_SETTINGS = "com.samsung.android.sbrows er.contentBlocker.ACTION_SETTING";
74 private static final String ACTION_UPDATE = "com.samsung.android.sbrowser.cont entBlocker.ACTION_UPDATE";
75
76 public static final String SUBSCRIPTIONS_EXCEPTIONSURL = "subscriptions_except ionsurl";
77
78 // The value below specifies an interval of [x, 2*x[, where x =
79 // INITIAL_UPDATE_CHECK_DELAY
80 private static final long INITIAL_UPDATE_CHECK_DELAY = 5 * DateUtils.SECOND_IN _MILLIS;
81 private static final long UPDATE_CHECK_INTERVAL = 30 * DateUtils.MINUTE_IN_MIL LIS;
82 private static final long BROADCAST_COMBINATION_DELAY = 2500;
83
84 private static final int NO_FLAG = 0;
85 private static final int OLDEST_SAMSUNG_INTERNET_5_VERSIONCODE = 500000000;
86
87 private final ReentrantLock accessLock = new ReentrantLock();
88 private DefaultSubscriptions defaultSubscriptions;
89 private Subscriptions subscriptions;
90 private JSONPrefs jsonPrefs;
91 private AppInfo appInfo;
92 private final LinkedBlockingQueue<EngineEvent> engineEvents = new LinkedBlocki ngQueue<>();
93 private Thread handlerThread;
94 private Downloader downloader;
95 private SubscriptionUpdateCallback subscriptionUpdateCallback;
96 private final Context serviceContext;
97 private boolean wasFirstRun = false;
98 private long nextUpdateBroadcast = Long.MAX_VALUE;
99
100 private Engine(final Context context)
101 {
102 this.serviceContext = context;
103 }
104
105 public String getPrefsDefault(final String key)
106 {
107 return this.jsonPrefs.getDefaults(key);
108 }
109
110 DefaultSubscriptionInfo getDefaultSubscriptionInfo(final Subscription sub)
111 {
112 return this.defaultSubscriptions.getForUrl(sub.getURL());
113 }
114
115 void lock()
116 {
117 this.accessLock.lock();
118 }
119
120 void unlock()
121 {
122 this.accessLock.unlock();
123 }
124
125 public static boolean openSBrowserSettings(final Context activityContext)
126 {
127 final Intent intent = new Intent(ACTION_OPEN_SETTINGS);
128 final List<ResolveInfo> list = activityContext.getPackageManager()
129 .queryIntentActivities(intent, 0);
130 if (list.size() > 0)
131 {
132 activityContext.startActivity(intent);
133 }
134 return list.size() > 0;
135 }
136
137 public static boolean hasCompatibleSBrowserInstalled(final Context activityCon text)
138 {
139 try
140 {
141 return activityContext.getPackageManager()
142 .queryIntentActivities(new Intent(ACTION_OPEN_SETTINGS), 0).size() > 0 ;
143 }
144 catch (final Throwable t)
145 {
146 return false;
147 }
148 }
149
150 /**
151 * Starting with Samsung Internet 5.0, the way to enable ad blocking has chang ed. As a result, we
152 * need to check for the version of Samsung Internet and apply text changes to the first run slide.
153 *
154 * @param activityContext
155 * @return a boolean that indicates, if the user has Samsung Internet version 5.x
156 */
157 public static boolean hasSamsungInternetVersion5OrNewer(final Context activity Context)
158 {
159 try
160 {
161 PackageInfo packageInfo = activityContext.getPackageManager().getPackageIn fo(SBROWSER_APP_ID, NO_FLAG);
162 return packageInfo.versionCode >= OLDEST_SAMSUNG_INTERNET_5_VERSIONCODE;
163 }
164 catch (PackageManager.NameNotFoundException e)
165 {
166 // Should never happen, as checkAAStatusAndProceed() should not be called if the user
167 // has no compatible SBrowser installed. Nevertheless we have to handle th e Exception.
168 Log.d(TAG, "No compatible Samsung Browser found.", e);
169 return false;
170 }
171 }
172
173 public void setSubscriptionUpdateCallback(final SubscriptionUpdateCallback sub scriptionUpdateCallback)
174 {
175 this.subscriptionUpdateCallback = subscriptionUpdateCallback;
176 }
177
178 public void requestUpdateBroadcast()
179 {
180 this.nextUpdateBroadcast = System.currentTimeMillis() + BROADCAST_COMBINATIO N_DELAY;
181 }
182
183 private void writeFileAndSendUpdateBroadcast()
184 {
185 createAndWriteFile();
186
187 runOnUiThread(new Runnable()
188 {
189 @Override
190 public void run()
191 {
192 final Intent intent = new Intent();
193 intent.setAction(ACTION_UPDATE);
194 intent.setData(Uri.parse("package:" + Engine.this.serviceContext.getPack ageName()));
195 Engine.this.serviceContext.sendBroadcast(intent);
196 }
197 });
198 }
199
200 boolean canUseInternet()
201 {
202 final ConnectivityManager connManager = (ConnectivityManager) this.serviceCo ntext
203 .getSystemService(Context.CONNECTIVITY_SERVICE);
204 final NetworkInfo current = connManager.getActiveNetworkInfo();
205 if (current == null)
206 {
207 return false;
208 }
209
210 if (wasFirstRun())
211 {
212 return true;
213 }
214
215 final boolean wifiOnly = "1".equals(SharedPrefsUtils.getString(
216 this.serviceContext, R.string.key_automatic_updates , "1"));
217
218 if (wifiOnly)
219 {
220 if (current.isConnected() && !current.isRoaming())
221 {
222 switch (current.getType())
223 {
224 case ConnectivityManager.TYPE_BLUETOOTH:
225 case ConnectivityManager.TYPE_ETHERNET:
226 case ConnectivityManager.TYPE_WIFI:
227 case ConnectivityManager.TYPE_WIMAX:
228 return true;
229 default:
230 return false;
231 }
232 }
233 return false;
234 }
235 return current.isConnected();
236 }
237
238 public List<SubscriptionInfo> getListedSubscriptions()
239 {
240 return this.subscriptions.getSubscriptions(this);
241 }
242
243 public void changeSubscriptionState(final String id, final boolean enabled)
244 {
245 if (this.subscriptionUpdateCallback != null)
246 {
247 subscriptionUpdateCallback.subscriptionUpdateRequested(enabled);
248 }
249 this.engineEvents.add(new ChangeEnabledStateEvent(id, enabled));
250 }
251
252 public void subscriptionStateChanged()
253 {
254 if (this.subscriptionUpdateCallback != null)
255 {
256 subscriptionUpdateCallback.subscriptionUpdatedApplied();
257 }
258 }
259
260 public void createAndAddSubscriptionFromUrl(final String url,
261 final SubscriptionAddedCallback callback) throws IOException
262 {
263 final Subscription sub = Subscription.create(url);
264 sub.putMeta(Subscription.KEY_TITLE, url);
265 sub.setEnabled(true);
266 subscriptions.add(sub);
267 subscriptions.persistSubscription(sub);
268 callback.subscriptionAdded();
269 }
270
271 public void removeSubscriptionById(final String subscriptionId)
272 {
273 subscriptions.remove(subscriptionId);
274 }
275
276 void downloadFinished(final String id, final int responseCode, final String re sponse,
277 final Map<String, String> headers)
278 {
279 this.engineEvents.add(new DownloadFinishedEvent(id, responseCode, response, headers));
280 }
281
282 private void createAndWriteFile()
283 {
284 this.lock();
285 try
286 {
287 Log.d(TAG, "Writing filters...");
288 final File filterFile = this.subscriptions.createAndWriteFile();
289 writeWhitelistedWebsites(this.serviceContext, filterFile);
290
291 SharedPrefsUtils.putString(
292 this.serviceContext, R.string.key_cached_filter_path, filterFile.getAb solutePath());
293
294 Log.d(TAG, "Cleaning up cache...");
295 final File dummyFile = getDummyFilterFile(this.serviceContext);
296 final File[] cacheDirFiles = getFilterCacheDir(this.serviceContext).listFi les();
297 if (cacheDirFiles != null)
298 {
299 for (final File file : cacheDirFiles)
300 {
301 if (!file.equals(dummyFile) && !file.equals(filterFile))
302 {
303 Log.d(TAG, "Deleting file:" + file);
304 file.delete();
305 }
306 }
307 }
308 }
309 catch (IOException e)
310 {
311 Log.e(TAG, "Failed to write filters", e);
312 }
313 finally
314 {
315 this.unlock();
316 }
317 }
318
319 public static void runOnUiThread(final Runnable runnable)
320 {
321 new Handler(Looper.getMainLooper()).post(runnable);
322 }
323
324 public boolean isAcceptableAdsEnabled()
325 {
326 this.lock();
327 try
328 {
329 return this.subscriptions.isSubscriptionEnabled("url:"
330 + this.getPrefsDefault(SUBSCRIPTIONS_EXCEPTIONSURL));
331 }
332 finally
333 {
334 this.unlock();
335 }
336 }
337
338 public DefaultSubscriptionInfo getDefaultSubscriptionInfoForUrl(final String u rl)
339 {
340 return this.defaultSubscriptions.getForUrl(url);
341 }
342
343 /**
344 * If the user starts the app for the first time, we force to update the subsc ription which was
345 * selected as the default, no matter if he has a WIFI connection or not. From the second start
346 * we only update when the user has a WIFI connection.
347 *
348 * @return a boolean that indicated if this is the first start of the app
349 */
350 @VisibleForTesting
351 private boolean wasFirstRun()
352 {
353 if (wasFirstRun)
354 {
355 this.wasFirstRun = false;
356 return true;
357 }
358 else
359 {
360 return false;
361 }
362 }
363
364 private void migrateFromPreviousVersion(final Context context)
365 {
366 try
367 {
368 final int versionCode = context.getPackageManager().getPackageInfo(context .getPackageName(),
369 0).versionCode;
370
371 final int previousVersionCode = SharedPrefsUtils.getInt(
372 context, R.string.key_previous_version_code, 0);
373
374 if (versionCode > previousVersionCode)
375 {
376 if (previousVersionCode > 0)
377 {
378 // We can do possible migration stuff here
379 // Currently we only persist the new version code
380 }
381 SharedPrefsUtils.putInt(context, R.string.key_previous_version_code, ver sionCode);
382 }
383 }
384 catch (final Throwable t)
385 {
386 Log.e(TAG, "Failed on migration, please clear all application data", t);
387 }
388 }
389
390 static Engine create(final Context context) throws IOException
391 {
392 final Engine engine = new Engine(context);
393
394 // Migration data from previous version (if needed)
395 engine.migrateFromPreviousVersion(context);
396 Log.d(TAG, "Migration done");
397
398 engine.appInfo = AppInfo.create(context);
399
400 Log.d(TAG, "Creating engine, appInfo=" + engine.appInfo.toString());
401
402 try (final InputStream subscriptionsXml = context.getResources()
403 .openRawResource(R.raw.subscriptions))
404 {
405 engine.defaultSubscriptions = DefaultSubscriptions.fromStream(subscription sXml);
406 }
407
408 Log.d(TAG, "Finished reading 'subscriptions.xml'");
409 engine.subscriptions = Subscriptions.initialize(engine, getSubscriptionsDir( context),
410 getFilterCacheDir(context));
411
412 try (final InputStream prefsJson = context.getResources().openRawResource(R. raw.prefs))
413 {
414 engine.jsonPrefs = JSONPrefs.create(prefsJson);
415 }
416
417 Log.d(TAG, "Finished reading JSON preferences");
418
419 // Check if this is a fresh start, if so: initialize bundled easylist.
420 engine.wasFirstRun = engine.subscriptions.wasUnitialized();
421 if (engine.subscriptions.wasUnitialized())
422 {
423 Log.d(TAG, "Subscription storage was uninitialized, initializing...");
424
425 try (final InputStream easylistTxt = context.getResources().openRawResourc e(R.raw.easylist))
426 {
427 final Subscription easylist = engine.subscriptions.add(Subscription
428 // Use bundled EasyList as default and update it with locale specifi c list later
429 // see: https://issues.adblockplus.org/ticket/5237
430 .create(SubscriptionUtils.chooseDefaultSubscriptionUrl(
431 engine.defaultSubscriptions.getAdsSubscriptions()))
432 .parseLines(readLines(easylistTxt)));
433 easylist.putMeta(Subscription.KEY_UPDATE_TIMESTAMP, "0");
434 easylist.setEnabled(true);
435 }
436 Log.d(TAG, "Added and enabled bundled easylist");
437
438 try (final InputStream exceptionsTxt = context.getResources()
439 .openRawResource(R.raw.exceptionrules))
440 {
441 final Subscription exceptions = engine.subscriptions.add(Subscription
442 .create(engine.getPrefsDefault(SUBSCRIPTIONS_EXCEPTIONSURL))
443 .parseLines(readLines(exceptionsTxt)));
444 exceptions.putMeta(Subscription.KEY_UPDATE_TIMESTAMP, "0");
445 exceptions.setEnabled(true);
446 }
447 Log.d(TAG, "Added and enabled bundled exceptionslist");
448
449 int additional = 0;
450 for (final Subscription sub : engine.defaultSubscriptions.createSubscripti ons())
451 {
452 if (!engine.subscriptions.hasSubscription(sub.getId()))
453 {
454 additional++;
455 engine.subscriptions.add(sub);
456 }
457 }
458
459 Log.d(TAG, "Added " + additional + " additional default/built-in subscript ions");
460 engine.subscriptions.persistSubscriptions();
461 }
462
463 engine.handlerThread = new Thread(new EventHandler(engine));
464 engine.handlerThread.setDaemon(true);
465 engine.handlerThread.start();
466
467 engine.downloader = Downloader.create(engine);
468
469 final File cachedFilterFile = getCachedFilterFile(context);
470 if (cachedFilterFile == null || !cachedFilterFile.exists())
471 {
472 engine.writeFileAndSendUpdateBroadcast();
473 }
474
475 return engine;
476 }
477
478 public static String readFileAsString(InputStream instream) throws IOException
479 {
480 final StringBuilder sb = new StringBuilder();
481 try (final BufferedReader r = new BufferedReader(new InputStreamReader(
482 instream, StandardCharsets.UTF_8)))
483 {
484 for (int ch = r.read(); ch != -1; ch = r.read())
485 {
486 sb.append((char) ch);
487 }
488 }
489 return sb.toString();
490 }
491
492 public static List<String> readLines(InputStream instream) throws IOException
493 {
494 final ArrayList<String> list = new ArrayList<>();
495 try (final BufferedReader r = new BufferedReader(new InputStreamReader(
496 instream, StandardCharsets.UTF_8)))
497 {
498 for (String line = r.readLine(); line != null; line = r.readLine())
499 {
500 list.add(line);
501 }
502 }
503 return list;
504 }
505
506 public static File getOrCreateCachedFilterFile(Context context) throws IOExcep tion
507 {
508 final File cachedFilterFile = getCachedFilterFile(context);
509 if (cachedFilterFile != null && cachedFilterFile.exists())
510 {
511 Log.d(TAG, "Cached filter file found: " + cachedFilterFile);
512 return cachedFilterFile;
513 }
514
515 Log.d(TAG, "Cached filter file not found. Using dummy filter file");
516 final File dummyFilterFile = getDummyFilterFile(context);
517 if (!dummyFilterFile.exists())
518 {
519 Log.d(TAG, "Creating dummy filter file...");
520 dummyFilterFile.getParentFile().mkdirs();
521 try (final BufferedWriter writer = new BufferedWriter(new OutputStreamWrit er(
522 new FileOutputStream(dummyFilterFile), StandardCharsets.UTF_8)))
523 {
524 writeFilterHeaders(writer);
525 }
526 }
527 return dummyFilterFile;
528 }
529
530 public static void writeFilterHeaders(Writer writer) throws IOException
531 {
532 writer.write("[Adblock Plus 2.0]\n");
533 writer.write("! This file was automatically created.\n");
534 }
535
536 private static void writeWhitelistedWebsites(Context context, File filterFile) throws IOException
537 {
538 Log.d(TAG, "Writing whitelisted websites...");
539 final Set<String> whitelistedWebsites = new TreeSet<>();
540 whitelistedWebsites.addAll(SharedPrefsUtils.getStringSet(
541 context, R.string.key_whitelisted_websites, Collections.<String>emptySet ()));
542
543 try (final BufferedWriter w = new BufferedWriter( new OutputStreamWriter(
544 new FileOutputStream(filterFile, true), StandardCharsets.UTF_8)))
545 {
546 for (final String url : whitelistedWebsites)
547 {
548 try
549 {
550 final URI uri = new URI(url);
551 final String host = uri.getHost() != null ? uri.getHost() : uri.getPat h();
552 w.write("@@||" + host + "^$document");
553 w.write('\n');
554 }
555 catch (URISyntaxException e)
556 {
557 Log.w(TAG, "Failed to parse whitelisted website: " + url);
558 }
559 }
560 }
561 }
562
563 private static File getCachedFilterFile(Context context)
564 {
565 final String cachedFilterPath = SharedPrefsUtils.getString(
566 context, R.string.key_cached_filter_path, null);
567
568 if (cachedFilterPath != null)
569 {
570 return new File(cachedFilterPath);
571 }
572
573 return null;
574 }
575
576 private static File getDummyFilterFile(Context context)
577 {
578 return new File(getFilterCacheDir(context), "dummy.txt");
579 }
580
581 private static File getFilterCacheDir(Context context)
582 {
583 return new File(context.getCacheDir(), "subscriptions");
584 }
585
586 private static File getSubscriptionsDir(Context context)
587 {
588 return new File(context.getFilesDir(), "subscriptions");
589 }
590
591 URL createDownloadURL(final Subscription sub) throws IOException
592 {
593 final StringBuilder sb = new StringBuilder();
594
595 sb.append(sub.getURL());
596 if (sub.getURL().getQuery() != null)
597 {
598 sb.append('&');
599 }
600 else
601 {
602 sb.append('?');
603 }
604
605 sb.append("addonName=");
606 sb.append(URLEncoder.encode(this.appInfo.addonName, StandardCharsets.UTF_8.n ame()));
607 sb.append("&addonVersion=");
608 sb.append(URLEncoder.encode(this.appInfo.addonVersion, StandardCharsets.UTF_ 8.name()));
609 sb.append("&application=");
610 sb.append(URLEncoder.encode(this.appInfo.application, StandardCharsets.UTF_8 .name()));
611 sb.append("&applicationVersion=");
612 sb.append(URLEncoder.encode(this.appInfo.applicationVersion, StandardCharset s.UTF_8.name()));
613 sb.append("&platform=");
614 sb.append(URLEncoder.encode(this.appInfo.platform, StandardCharsets.UTF_8.na me()));
615 sb.append("&platformVersion=");
616 sb.append(URLEncoder.encode(this.appInfo.platformVersion, StandardCharsets.U TF_8.name()));
617 sb.append("&lastVersion=");
618 sb.append(sub.getVersion());
619 sb.append("&downloadCount=");
620 final long downloadCount = sub.getDownloadCount();
621 if (downloadCount < 5)
622 {
623 sb.append(downloadCount);
624 }
625 else
626 {
627 sb.append("4%2B"); // "4+" URL encoded
628 }
629
630 return new URL(sb.toString());
631 }
632
633 public boolean isAcceptableAdsUrl(final SubscriptionInfo subscriptionInfo)
634 {
635 return getPrefsDefault(SUBSCRIPTIONS_EXCEPTIONSURL).equals(subscriptionInfo. getUrl());
636 }
637
638 private static class EventHandler implements Runnable
639 {
640 private static final String TAG = EventHandler.class.getSimpleName();
641 private final Engine engine;
642
643 public EventHandler(final Engine engine)
644 {
645 this.engine = engine;
646 }
647
648 @Override
649 public void run()
650 {
651 Log.d(TAG, "Handler thread started");
652 boolean interrupted = false;
653 long nextUpdateCheck = System.currentTimeMillis()
654 + (long) ((1 + Math.random()) * INITIAL_UPDATE_CHECK_DELAY);
655 while (!interrupted)
656 {
657 try
658 {
659 final EngineEvent event = this.engine.engineEvents.poll(100, TimeUnit. MILLISECONDS);
660 engine.lock();
661 try
662 {
663 if (event != null)
664 {
665 switch (event.getType())
666 {
667 case CHANGE_ENABLED_STATE:
668 {
669 final ChangeEnabledStateEvent cese = (ChangeEnabledStateEvent) event;
670 Log.d(TAG, "Changing " + cese.id + " to enabled: " + cese.enab led);
671 engine.subscriptions.changeSubscriptionState(cese.id, cese.ena bled);
672 break;
673 }
674 case DOWNLOAD_FINISHED:
675 {
676 final DownloadFinishedEvent dfe = (DownloadFinishedEvent) even t;
677 Log.d(TAG, "Download finished for '" + dfe.id + "' with respon se code "
678 + dfe.responseCode);
679 this.engine.subscriptions.updateSubscription(dfe.id, dfe.respo nseCode,
680 dfe.response, dfe.headers);
681 break;
682 }
683 default:
684 Log.d(TAG, "Unhandled type: " + event.getType());
685 break;
686 }
687 }
688
689 final long currentTime = System.currentTimeMillis();
690 if (currentTime > nextUpdateCheck)
691 {
692 nextUpdateCheck = currentTime + UPDATE_CHECK_INTERVAL;
693
694 this.engine.subscriptions.checkForUpdates();
695 }
696
697 if (currentTime > this.engine.nextUpdateBroadcast)
698 {
699 this.engine.nextUpdateBroadcast = Long.MAX_VALUE;
700 Log.d(TAG, "Sending update broadcast");
701 this.engine.writeFileAndSendUpdateBroadcast();
702 }
703 }
704 finally
705 {
706 engine.unlock();
707 }
708 }
709 catch (final InterruptedException e)
710 {
711 Log.d(TAG, "Handler interrupted", e);
712 interrupted = true;
713 }
714 catch (final Throwable t)
715 {
716 Log.e(TAG, "Event processing failed: " + t.getMessage(), t);
717 }
718 }
719 Log.d(TAG, "Handler thread finished");
720 }
721 }
722
723 private static class EngineEvent
724 {
725 public enum EngineEventType
726 {
727 CHANGE_ENABLED_STATE,
728 FORCE_DOWNLOAD,
729 DOWNLOAD_FINISHED
730 }
731
732 private final EngineEventType type;
733
734 EngineEvent(final EngineEventType type)
735 {
736 this.type = type;
737 }
738
739 public EngineEventType getType()
740 {
741 return this.type;
742 }
743 }
744
745 private static class ChangeEnabledStateEvent extends EngineEvent
746 {
747 private final String id;
748 private final boolean enabled;
749
750 public ChangeEnabledStateEvent(final String id, final boolean enabled)
751 {
752 super(EngineEvent.EngineEventType.CHANGE_ENABLED_STATE);
753 this.id = id;
754 this.enabled = enabled;
755 }
756 }
757
758 private static class DownloadFinishedEvent extends EngineEvent
759 {
760 private final String id;
761 private final int responseCode;
762 private final String response;
763 private final HashMap<String, String> headers = new HashMap<>();
764
765 public DownloadFinishedEvent(final String id,
766 final int responseCode,
767 final String response,
768 final Map<String, String> headers)
769 {
770 super(EngineEvent.EngineEventType.DOWNLOAD_FINISHED);
771 this.id = id;
772 this.responseCode = responseCode;
773 this.response = response;
774 if (headers != null)
775 {
776 this.headers.putAll(headers);
777 }
778 }
779 }
780
781 public void enqueueDownload(final Subscription sub, final boolean forced) thro ws IOException
782 {
783 if (sub.getURL() != null && sub.shouldUpdate(forced))
784 {
785 final HashMap<String, String> headers = new HashMap<>();
786 if (sub.isMetaDataValid() && sub.isFiltersValid())
787 {
788 final String lastModified = sub.getMeta(Subscription.KEY_HTTP_LAST_MODIF IED);
789 if (!TextUtils.isEmpty(lastModified))
790 {
791 headers.put("If-Modified-Since", lastModified);
792 }
793 final String etag = sub.getMeta(Subscription.KEY_HTTP_ETAG);
794 if (!TextUtils.isEmpty(etag))
795 {
796 headers.put("If-None-Match", etag);
797 }
798 }
799 Log.d(TAG, headers.toString());
800 this.downloader.enqueueDownload(this.createDownloadURL(sub), sub.getId(), headers);
801 }
802 }
803
804 public void connectivityChanged()
805 {
806 this.downloader.connectivityChanged();
807 }
808
809 public interface SubscriptionUpdateCallback
810 {
811 void subscriptionUpdateRequested(boolean enabled);
812 void subscriptionUpdatedApplied();
813 }
814
815 public interface SubscriptionAddedCallback
816 {
817 void subscriptionAdded();
818 }
819 }
OLDNEW

Powered by Google App Engine
This is Rietveld