diff --git a/.gitignore b/.gitignore index 7f036ba0f..39bb026dc 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ out # Source: # https://raw.githubusercontent.com/github/gitignore/master/Android.gitignore # https://gitlab.com/fdroid/fdroidclient/raw/master/.gitignore + +*.iml diff --git a/.travis.yml b/.travis.yml index b226663c6..175e809bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,9 @@ language: android sudo: false android: components: + - tools + - build-tools-23.0.3 - build-tools-22.0.1 - - build-tools-23.0.1 - android-23 - android-22 - extra-android-support @@ -17,3 +18,4 @@ before_install: install: - ./gradlew script: + - ./gradlew assembleDebug --stacktrace diff --git a/README.md b/README.md index 5c76c020e..411e2cc30 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,11 @@ ####Download * [Download APK from here](https://github.com/anthonycr/Lightning-Browser/releases) -* [Download from Google Play](https://play.google.com/store/apps/details?id=acr.browser.barebones) +* [Download from F-Droid](https://f-droid.org/repository/browse/?fdfilter=lightning&fdid=acr.browser.lightning) + +* [Download Free from Google Play](https://play.google.com/store/apps/details?id=acr.browser.barebones) + +* [Download Paid from Google Play](https://play.google.com/store/apps/details?id=acr.browser.lightning) ####Master Branch * [![Build Status](https://travis-ci.org/anthonycr/Lightning-Browser.svg?branch=master)](https://travis-ci.org/anthonycr/Lightning-Browser) @@ -46,12 +50,14 @@ * Please add translations/translation fixes as you see need ####Contributing +* [The Trello Board](https://trello.com/b/Gwjx8MC3/lightning-browser) * Contributions are always welcome * If you want a feature and can code, feel free to fork and add the change yourself and make a pull request * PLEASE use the ````dev```` branch when contributing as the ````master```` branch is supposed to be for stable builds. I will not reject your pull request if you make it on master, but it will annoy me and make my life harder. * Code Style - * Standard Java camel case - * Member variables are preceded with an 'm' + * Hungarian Notation + * Prefix member variables with 'm' + * Prefix static member variables with 's' * Use 4 spaces instead of a tab (\t) ####Setting Up the Project diff --git a/app/app.iml b/app/app.iml deleted file mode 100644 index d6f1ddbc0..000000000 --- a/app/app.iml +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index b735e6c88..0784786e3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,18 +1,27 @@ apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' +apply plugin: 'com.getkeepsafe.dexcount' android { compileSdkVersion 23 - buildToolsVersion "23.0.1" + buildToolsVersion "23.0.3" + defaultConfig { minSdkVersion 14 targetSdkVersion 23 - versionName "4.2.3a" + versionName "4.3.3" + generatedDensities = [] + } + + aaptOptions { + additionalParameters "--no-version-vectors" } + sourceSets { lightningPlus.setRoot('src/LightningPlus') lightningLite.setRoot('src/LightningLite') } + buildTypes { debug { minifyEnabled false @@ -26,44 +35,73 @@ android { proguardFiles 'proguard-project.txt' } } + productFlavors { lightningPlus { buildConfigField "boolean", "FULL_VERSION", "true" applicationId "acr.browser.lightning" - versionCode 84 + versionCode 88 } + lightningLite { buildConfigField "boolean", "FULL_VERSION", "false" applicationId "acr.browser.barebones" - versionCode 85 + versionCode 90 } } + lintOptions { - abortOnError false + abortOnError true + } + + packagingOptions { + exclude '.readme' } } +dexcount { + includeClasses = false + includeFieldCount = false + printAsTree = true + orderByMethodCount = true + verbose = false +} + dependencies { - compile 'com.android.support:palette-v7:23.0.1' - compile 'com.android.support:appcompat-v7:23.0.1' - compile 'com.android.support:design:23.0.1' - compile 'com.android.support:recyclerview-v7:23.0.1' - compile 'org.jsoup:jsoup:1.8.3' + + // support libraries + compile 'com.android.support:palette-v7:23.3.0' + compile 'com.android.support:appcompat-v7:23.3.0' + compile 'com.android.support:design:23.3.0' + compile 'com.android.support:recyclerview-v7:23.3.0' + compile 'com.android.support:support-v4:23.3.0' + + // html parsing fo reading mode + compile 'org.jsoup:jsoup:1.9.1' + + // event bus compile 'com.squareup:otto:1.3.8' - compile 'com.google.dagger:dagger:2.0.1' - apt 'com.google.dagger:dagger-compiler:2.0.1' + + // dependency injection + compile 'com.google.dagger:dagger:2.0.2' + apt 'com.google.dagger:dagger-compiler:2.0.2' + + // view binding compile 'com.jakewharton:butterknife:7.0.1' - // Only Lightning Plus needs the proxy libraries - compile 'net.i2p.android:client:0.7' + // permissions + compile 'com.anthonycr.grant:permissions:1.1.2' + + // proxy support + compile 'net.i2p.android:client:0.8' // Use the following code to update the libnetcipher submodule // git submodule foreach git reset --hard // git submodule update --remote - compile(project(':libnetcipher')) - - debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' - releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' + compile project(':libnetcipher') + // memory leak analysis + debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2' + releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2' provided 'javax.annotation:jsr250-api:1.0' } \ No newline at end of file diff --git a/app/src/LightningPlus/assets/hosts.txt b/app/src/LightningPlus/assets/hosts.txt index f3571e24a..12168698c 100644 --- a/app/src/LightningPlus/assets/hosts.txt +++ b/app/src/LightningPlus/assets/hosts.txt @@ -15,13 +15,18 @@ 127.0.0.1 007.go2cloud.org 127.0.0.1 0075-7112-e7eb-f9b9.reporo.net 127.0.0.1 008.free-counter.co.uk +127.0.0.1 011i5.voluumtrk.com 127.0.0.1 0124498474f7c13ac9a2-6b191446002b31342189d56cabcf5227.r11.cf2.rackcdn.com +127.0.0.1 02gzx.voluumtrk.com 127.0.0.1 0427d7.se 127.0.0.1 04fd-74b3-b2f6-c645.reporo.net 127.0.0.1 050003.voodoo.com 127.0.0.1 050005.voodoo.com +127.0.0.1 05ni7.voluumtrk.com +127.0.0.1 0602v.voluumtrk.com 127.0.0.1 060810131024.c.mystat-in.net 127.0.0.1 06c5-dbbd-eb79-4cd4.reporo.net +127.0.0.1 06rph.voluumtrk.com 127.0.0.1 08.185.87.0.liveadvert.com 127.0.0.1 08.185.87.00.liveadvert.com 127.0.0.1 08.185.87.01.liveadvert.com @@ -196,14 +201,32 @@ 127.0.0.1 08.185.87.99.liveadvert.com 127.0.0.1 0896-c1b9-ed40-acad.reporo.net 127.0.0.1 0aac4e6a54c170b0.se +127.0.0.1 0alha.voluumtrk.com +127.0.0.1 0b1xr.voluumtrk.com +127.0.0.1 0bfn6.voluumtrk.com 127.0.0.1 0br.realsecuredredirect.com 127.0.0.1 0cfe-e814-fd2a-7ffc.reporo.net +127.0.0.1 0cwjs.voluumtrk.com +127.0.0.1 0dedx.voluumtrk.com +127.0.0.1 0ftgo.voluumtrk.com +127.0.0.1 0gitx.voluumtrk.com 127.0.0.1 0hna.com +127.0.0.1 0iiml.voluumtrk.com 127.0.0.1 0latfee.ero-advertising.com 127.0.0.1 0llii0g6.com +127.0.0.1 0md5e.voluumtrk.com +127.0.0.1 0mt66.voluumtrk.com +127.0.0.1 0muvp.voluumtrk.com +127.0.0.1 0ny3m.voluumtrk.com +127.0.0.1 0qhuv.voluumtrk.com +127.0.0.1 0rlbs.voluumtrk.com +127.0.0.1 0wptk.voluumtrk.com +127.0.0.1 0yn3h.voluumtrk.com 127.0.0.1 1.che-ka.com +127.0.0.1 1.chipde.damoh.schneevonmorgen.com 127.0.0.1 1.gmxnet.damoh.schneevonmorgen.com 127.0.0.1 1.im.cz +127.0.0.1 1.psfree.pay.clickbank.net 127.0.0.1 1.ptp22.com 127.0.0.1 1.quicktrkr.com 127.0.0.1 10.6.87.194.dynamic.dol.ru @@ -249,7 +272,9 @@ 127.0.0.1 11163929-3482.c2.adprotect.net 127.0.0.1 11163943.c.adprotect.net 127.0.0.1 11164000-690.c.adprotect.net +127.0.0.1 11167236.adscreendirect.com 127.0.0.1 112.6.87.194.dynamic.dol.ru +127.0.0.1 11262.engine.mobileapptracking.com 127.0.0.1 113.6.87.194.dynamic.dol.ru 127.0.0.1 114.6.87.194.dynamic.dol.ru 127.0.0.1 115.6.87.194.dynamic.dol.ru @@ -262,6 +287,7 @@ 127.0.0.1 12.ptp22.com 127.0.0.1 120.6.87.194.dynamic.dol.ru 127.0.0.1 120.duba.net +127.0.0.1 120mk.voluumtrk.com 127.0.0.1 121.2cnt.net 127.0.0.1 121.6.87.194.dynamic.dol.ru 127.0.0.1 1212.bitterstrawberry.com @@ -270,6 +296,7 @@ 127.0.0.1 1223.xg4ken.com 127.0.0.1 122807155757.c.mystat-in.net 127.0.0.1 123.6.87.194.dynamic.dol.ru +127.0.0.1 123.dominoad.com 127.0.0.1 123.duba.net 127.0.0.1 1239.9014.302br.net 127.0.0.1 123ads.nl @@ -307,6 +334,7 @@ 127.0.0.1 136.6.87.194.dynamic.dol.ru 127.0.0.1 13618.9005.302br.net 127.0.0.1 137.6.87.194.dynamic.dol.ru +127.0.0.1 137852403.log.optimizely.com 127.0.0.1 138.6.87.194.dynamic.dol.ru 127.0.0.1 139.6.87.194.dynamic.dol.ru 127.0.0.1 14.6.87.194.dynamic.dol.ru @@ -316,6 +344,7 @@ 127.0.0.1 140proof.com 127.0.0.1 141.6.87.194.dynamic.dol.ru 127.0.0.1 1412173.fls.doubleclick.net +127.0.0.1 141aa.voluumtrk.com 127.0.0.1 142.6.87.194.dynamic.dol.ru 127.0.0.1 143.6.87.194.dynamic.dol.ru 127.0.0.1 1435575.fls.doubleclick.net @@ -340,6 +369,7 @@ 127.0.0.1 157.6.87.194.dynamic.dol.ru 127.0.0.1 158.6.87.194.dynamic.dol.ru 127.0.0.1 159.6.87.194.dynamic.dol.ru +127.0.0.1 15qhn.voluumtrk.com 127.0.0.1 15view.atdmt.com.319.6000.302br.net 127.0.0.1 16.6.87.194.dynamic.dol.ru 127.0.0.1 16.ptp22.com @@ -365,6 +395,8 @@ 127.0.0.1 169.6.87.194.dynamic.dol.ru 127.0.0.1 1695693.r.msn.com 127.0.0.1 1695842.r.msn.com +127.0.0.1 1696908298.rsc.cdn77.org +127.0.0.1 16qul.voluumtrk.com 127.0.0.1 17.6.87.194.dynamic.dol.ru 127.0.0.1 17.ptp22.com 127.0.0.1 170.6.87.194.dynamic.dol.ru @@ -381,12 +413,14 @@ 127.0.0.1 178.6.87.194.dynamic.dol.ru 127.0.0.1 179.6.87.194.dynamic.dol.ru 127.0.0.1 17b4-7817-18f7-3abd.reporo.net +127.0.0.1 17oez.voluumtrk.com 127.0.0.1 18.6.87.194.dynamic.dol.ru 127.0.0.1 18.ptp22.com 127.0.0.1 180.6.87.194.dynamic.dol.ru 127.0.0.1 1800inkfarm.go2cloud.org 127.0.0.1 181.6.87.194.dynamic.dol.ru 127.0.0.1 182.6.87.194.dynamic.dol.ru +127.0.0.1 18205.voluumtrk.com 127.0.0.1 1821.bitterstrawberry.com 127.0.0.1 1822333.r.msn.com 127.0.0.1 1829.bitterstrawberry.com @@ -405,6 +439,12 @@ 127.0.0.1 190.6.87.194.dynamic.dol.ru 127.0.0.1 191.6.87.194.dynamic.dol.ru 127.0.0.1 192.168.112.2o7.net +127.0.0.1 192.168.ads.trafficjunky.net +127.0.0.1 192.168.ads2.contentabc.com +127.0.0.1 192.168.cdn-a2.contentabc.com +127.0.0.1 192.168.cdn11.contentabc.com +127.0.0.1 192.168.media.trafficjunky.net +127.0.0.1 192.168.ss.xxxmyself.com 127.0.0.1 192.6.87.194.dynamic.dol.ru 127.0.0.1 192.com 127.0.0.1 192com.112.2o7.net @@ -413,9 +453,12 @@ 127.0.0.1 1940.bitterstrawberry.com 127.0.0.1 195.6.87.194.dynamic.dol.ru 127.0.0.1 196.6.87.194.dynamic.dol.ru +127.0.0.1 196179102.log.optimizely.com 127.0.0.1 197.6.87.194.dynamic.dol.ru 127.0.0.1 198.6.87.194.dynamic.dol.ru +127.0.0.1 1986a44b12e.com 127.0.0.1 199.6.87.194.dynamic.dol.ru +127.0.0.1 199ea.voluumtrk.com 127.0.0.1 1acpa.go2cloud.org 127.0.0.1 1ad.de 127.0.0.1 1app.blob.core.windows.net @@ -426,19 +469,31 @@ 127.0.0.1 1cash.net 127.0.0.1 1cgi.hitbox.com 127.0.0.1 1clickscpa.go2cloud.org +127.0.0.1 1dbxc.voluumtrk.com 127.0.0.1 1de.cqcounter.com +127.0.0.1 1de5g.voluumtrk.com +127.0.0.1 1do16.voluumtrk.com 127.0.0.1 1es.cqcounter.com 127.0.0.1 1f62-ac07-3838-8236.reporo.net 127.0.0.1 1f64-9327-ddd3-a591.reporo.net +127.0.0.1 1fm8o.voluumtrk.com 127.0.0.1 1fr.cqcounter.com 127.0.0.1 1in.cqcounter.com 127.0.0.1 1jo.cqcounter.com +127.0.0.1 1k7uk.voluumtrk.com 127.0.0.1 1m1mage.tr553.com +127.0.0.1 1mp.mobi +127.0.0.1 1mpo6.voluumtrk.com +127.0.0.1 1msro.voluumtrk.com 127.0.0.1 1myrdrcts.com +127.0.0.1 1naf9.voluumtrk.com 127.0.0.1 1nl.cqcounter.com 127.0.0.1 1no.cqcounter.com +127.0.0.1 1nvrm.voluumtrk.com +127.0.0.1 1pgna.voluumtrk.com 127.0.0.1 1phads.com 127.0.0.1 1pt.cqcounter.com +127.0.0.1 1rcu4.voluumtrk.com 127.0.0.1 1se.cqcounter.com 127.0.0.1 1src.us.intellitxt.com 127.0.0.1 1streamline.go2cloud.org @@ -446,8 +501,13 @@ 127.0.0.1 1ul8dvwz0a.s.ad6media.fr 127.0.0.1 1und1.ivwbox.de 127.0.0.1 1us.cqcounter.com +127.0.0.1 1w0xj.voluumtrk.com +127.0.0.1 1wu2u.voluumtrk.com 127.0.0.1 1xxx.cqcounter.com +127.0.0.1 1zl86.voluumtrk.com +127.0.0.1 1zlni.voluumtrk.com 127.0.0.1 2.che-ka.com +127.0.0.1 2.chipde.damoh.schneevonmorgen.com 127.0.0.1 2.livejasmin.com 127.0.0.1 2.ptp22.com 127.0.0.1 20.ptp22.com @@ -771,6 +831,7 @@ 127.0.0.1 2297.bitterstrawberry.com 127.0.0.1 230.6.87.194.dynamic.dol.ru 127.0.0.1 231.6.87.194.dynamic.dol.ru +127.0.0.1 231c-356f-e194-8857.reporo.net 127.0.0.1 232.6.87.194.dynamic.dol.ru 127.0.0.1 232.bitterstrawberry.com 127.0.0.1 233.6.87.194.dynamic.dol.ru @@ -788,8 +849,10 @@ 127.0.0.1 239.6.87.194.dynamic.dol.ru 127.0.0.1 2398.bitterstrawberry.com 127.0.0.1 2400.bitterstrawberry.com +127.0.0.1 2426010203.log.optimizely.com 127.0.0.1 24290.9170.302br.net 127.0.0.1 24293.9170.302br.net +127.0.0.1 2449650414.log.optimizely.com 127.0.0.1 245.bitterstrawberry.com 127.0.0.1 2463678.fls.doubleclick.net 127.0.0.1 247media.com @@ -809,6 +872,7 @@ 127.0.0.1 2551-ebc7-ebcf-4aff.reporo.net 127.0.0.1 256.bitterstrawberry.com 127.0.0.1 2565.bitterstrawberry.com +127.0.0.1 2570540166.log.optimizely.com 127.0.0.1 2573.bitterstrawberry.com 127.0.0.1 2674011.r.msn.com 127.0.0.1 27.6.87.194.dynamic.dol.ru @@ -821,16 +885,22 @@ 127.0.0.1 2895566.fls.doubleclick.net 127.0.0.1 2896678.fls.doubleclick.net 127.0.0.1 29.6.87.194.dynamic.dol.ru +127.0.0.1 2912a.v.fwmrm.net 127.0.0.1 29193.9215.302br.net +127.0.0.1 2926210385.log.optimizely.com +127.0.0.1 2975c.v.fwmrm.net 127.0.0.1 29bca6cb72a665c8.se 127.0.0.1 29rhino.go2cloud.org 127.0.0.1 2a.com.112.207.net +127.0.0.1 2a86.v.fwmrm.net 127.0.0.1 2baners.ero-advertising.com +127.0.0.1 2bibi.voluumtrk.com 127.0.0.1 2c60-1723-23d3-2b28.reporo.net 127.0.0.1 2cgi.hitbox.com 127.0.0.1 2df.ivwbox.de 127.0.0.1 2dprofessor.cadprofessor.netdna-cdn.com 127.0.0.1 2ds.ero-advertising.com +127.0.0.1 2e1ck.voluumtrk.com 127.0.0.1 2fbanners.ero-advertising.com 127.0.0.1 2fflatfee.ero-advertising.com 127.0.0.1 2finteryield.jmp9.com @@ -839,10 +909,30 @@ 127.0.0.1 2fview.atdmt.com.60413.9342.302br.net 127.0.0.1 2fview.atdmt.com.60416.9342.302br.net 127.0.0.1 2gtstrk.com +127.0.0.1 2ijmu.voluumtrk.com +127.0.0.1 2ik1o.voluumtrk.com +127.0.0.1 2inp7.voluumtrk.com +127.0.0.1 2jy2d.voluumtrk.com +127.0.0.1 2kd7g.voluumtrk.com +127.0.0.1 2kygq.voluumtrk.com +127.0.0.1 2lafc.voluumtrk.com 127.0.0.1 2o7.net 127.0.0.1 2options.go2cloud.org +127.0.0.1 2qtn9.voluumtrk.com +127.0.0.1 2sbd3.voluumtrk.com +127.0.0.1 2sksk.voluumtrk.com +127.0.0.1 2snla.voluumtrk.com 127.0.0.1 2spyware.us.intellitxt.com +127.0.0.1 2ttb4.voluumtrk.com +127.0.0.1 2usdj.voluumtrk.com +127.0.0.1 2vci0.voluumtrk.com 127.0.0.1 2vie.catdmt.omc.324.6000.302br.net +127.0.0.1 2wiz6.voluumtrk.com +127.0.0.1 2xkft.voluumtrk.com +127.0.0.1 2xxis.voluumtrk.com +127.0.0.1 2xz5e.voluumtrk.com +127.0.0.1 2y54x.voluumtrk.com +127.0.0.1 2yo1y.voluumtrk.com 127.0.0.1 3.2cnt.net 127.0.0.1 3.ptp22.com 127.0.0.1 30.6.87.194.dynamic.dol.ru @@ -851,9 +941,12 @@ 127.0.0.1 304br.net 127.0.0.1 306.bitterstrawberry.com 127.0.0.1 309.bitterstrawberry.com +127.0.0.1 30klc.voluumtrk.com 127.0.0.1 31.6.87.194.dynamic.dol.ru 127.0.0.1 3130320.fls.doubleclick.net 127.0.0.1 32.6.87.194.dynamic.dol.ru +127.0.0.1 3217776.r.msn.com +127.0.0.1 3252817.r.msn.com 127.0.0.1 3276817.fls.doubleclick.net 127.0.0.1 32d1d3b9c.se 127.0.0.1 33.6.87.194.dynamic.dol.ru @@ -862,6 +955,7 @@ 127.0.0.1 34.6.87.194.dynamic.dol.ru 127.0.0.1 34245.hittail.com 127.0.0.1 343.bitterstrawberry.com +127.0.0.1 3480025.log.optimizely.com 127.0.0.1 35.6.87.194.dynamic.dol.ru 127.0.0.1 3501113.fls.doubleclick.net 127.0.0.1 35082.9255.302br.net @@ -870,39 +964,61 @@ 127.0.0.1 36.6.87.194.dynamic.dol.ru 127.0.0.1 360.yahoo.com 127.0.0.1 360ads.go2cloud.org +127.0.0.1 360popunder.com +127.0.0.1 360redirect.com +127.0.0.1 360switch.net 127.0.0.1 360training.go2cloud.org 127.0.0.1 360unitedmedia.go2cloud.org 127.0.0.1 36222.seu.cleverreach.com 127.0.0.1 3689-7dbc-ddf7-a45f.reporo.net +127.0.0.1 36igq.voluumtrk.com 127.0.0.1 37.6.87.194.dynamic.dol.ru 127.0.0.1 3738527.fls.doubleclick.net 127.0.0.1 3773523.fls.doubleclick.net +127.0.0.1 37dyq.voluumtrk.com 127.0.0.1 38.6.87.194.dynamic.dol.ru +127.0.0.1 38179760.log.optimizely.com 127.0.0.1 3879194.fls.doubleclick.net 127.0.0.1 39.6.87.194.dynamic.dol.ru 127.0.0.1 390.bitterstrawberry.com 127.0.0.1 3941123.fls.doubleclick.net 127.0.0.1 3973258.fls.doubleclick.net 127.0.0.1 3982178.fls.doubleclick.net +127.0.0.1 3axp9.voluumtrk.com 127.0.0.1 3b9cc85dcf732d5.se 127.0.0.1 3c41ddc0.se 127.0.0.1 3c45d848d99.se 127.0.0.1 3cinteractive.com 127.0.0.1 3cnce854.com +127.0.0.1 3d05q.voluumtrk.com 127.0.0.1 3dc265e90c6d9fa3cc0c-3f982316dc17e6e99fe1b47483239d63.r95.cf2.rackcdn.com 127.0.0.1 3dots.co.il 127.0.0.1 3ds.ero-advertising.com 127.0.0.1 3dstats.com 127.0.0.1 3ece-5608-732a-5bbd.reporo.net +127.0.0.1 3flv2.voluumtrk.com +127.0.0.1 3grs6.voluumtrk.com +127.0.0.1 3hfag.voluumtrk.com +127.0.0.1 3m56h.voluumtrk.com 127.0.0.1 3rdeye.go2cloud.org +127.0.0.1 3rih9.voluumtrk.com 127.0.0.1 3sat.ivwbox.de +127.0.0.1 3ukg5.voluumtrk.com +127.0.0.1 3uuak.voluumtrk.com +127.0.0.1 3uzzo.voluumtrk.com +127.0.0.1 3wykr.voluumtrk.com +127.0.0.1 3xvh4.voluumtrk.com +127.0.0.1 3yp7i.voluumtrk.com +127.0.0.1 3zkgw.voluumtrk.com 127.0.0.1 4.6.87.194.dynamic.dol.ru 127.0.0.1 4.ads.dpreview.com 127.0.0.1 4.ptp22.com 127.0.0.1 40.6.87.194.dynamic.dol.ru 127.0.0.1 4002dbde88aebefdb1f7-8f93653c470e43727b1b565964867247.r51.cf2.rackcdn.com +127.0.0.1 401591473.log.optimizely.com 127.0.0.1 401ads.go2cloud.org 127.0.0.1 4055.xg4ken.com +127.0.0.1 40ohl.voluumtrk.com 127.0.0.1 40xbfzk8.com 127.0.0.1 41.6.87.194.dynamic.dol.ru 127.0.0.1 411affiliates.ca @@ -931,10 +1047,14 @@ 127.0.0.1 47.6.87.194.dynamic.dol.ru 127.0.0.1 47.xg4ken.com 127.0.0.1 47550.9340.302br.net +127.0.0.1 47nbp.voluumtrk.com 127.0.0.1 48.6.87.194.dynamic.dol.ru +127.0.0.1 48ed4acf13b.se +127.0.0.1 48qyx.voluumtrk.com 127.0.0.1 49.6.87.194.dynamic.dol.ru 127.0.0.1 49479.9362.302br.net 127.0.0.1 49535.com +127.0.0.1 4a6xw.voluumtrk.com 127.0.0.1 4affiliate.net 127.0.0.1 4anners.ero-advertising.com 127.0.0.1 4bca-d658-a4da-12c8.reporo.net @@ -942,17 +1062,28 @@ 127.0.0.1 4d80-dd41-9ed3-33b1.reporo.net 127.0.0.1 4dogmedia.go2cloud.org 127.0.0.1 4e93-6203-2401-d118.reporo.net +127.0.0.1 4feaw.voluumtrk.com 127.0.0.1 4gezz.win.adsservingtwig.xyz +127.0.0.1 4hdt7.voluumtrk.com +127.0.0.1 4hplo.voluumtrk.com 127.0.0.1 4info.com 127.0.0.1 4k6k0zj3.com +127.0.0.1 4kehf.voluumtrk.com +127.0.0.1 4ksir.voluumtrk.com 127.0.0.1 4mads.com +127.0.0.1 4n7i8.voluumtrk.com +127.0.0.1 4necp.voluumtrk.com 127.0.0.1 4players.ivwbox.de 127.0.0.1 4q.iperceptions.com 127.0.0.1 4qinvite.4q.iperceptions.com 127.0.0.1 4qsurvey.com +127.0.0.1 4rxgu.voluumtrk.com +127.0.0.1 4rzfl.voluumtrk.com 127.0.0.1 4see.mobi 127.0.0.1 4seeresults.com +127.0.0.1 4spp2.voluumtrk.com 127.0.0.1 4uxl.go2cloud.org +127.0.0.1 4va3x.voluumtrk.com 127.0.0.1 4web.cz 127.0.0.1 4x3zy4ql-l8bu4n1j.netdna-ssl.com 127.0.0.1 4x4review.us.intellitxt.com @@ -969,6 +1100,7 @@ 127.0.0.1 53.6.87.194.dynamic.dol.ru 127.0.0.1 535225459.keywordblocks.com 127.0.0.1 54.6.87.194.dynamic.dol.ru +127.0.0.1 5482fc58904b613aee952d23cac4db91.adsk2.co 127.0.0.1 55.6.87.194.dynamic.dol.ru 127.0.0.1 554924358.log.optimizely.com 127.0.0.1 5555c0e19278c10ce23e-e43b9b9293b141a8c68c3bbff03519a0.r36.cf2.rackcdn.com @@ -976,21 +1108,44 @@ 127.0.0.1 55624-3.popunder.loading-delivery1.com 127.0.0.1 55788.9391.302br.net 127.0.0.1 56.6.87.194.dynamic.dol.ru +127.0.0.1 560183740bc38303d0000248.maestroad.checkm8.com 127.0.0.1 5623.web-stats.org 127.0.0.1 56853.9349.302br.net +127.0.0.1 56mfv.voluumtrk.com 127.0.0.1 57.6.87.194.dynamic.dol.ru 127.0.0.1 57222.9406.302br.net 127.0.0.1 57243.9406.302br.net +127.0.0.1 5726.bapi.adsafeprotected.com 127.0.0.1 58.6.87.194.dynamic.dol.ru 127.0.0.1 59.6.87.194.dynamic.dol.ru 127.0.0.1 592.bitterstrawberry.com 127.0.0.1 594.bitterstrawberry.com +127.0.0.1 5ab3o.voluumtrk.com +127.0.0.1 5avxd.voluumtrk.com +127.0.0.1 5c2fk.voluumtrk.com +127.0.0.1 5cuav.voluumtrk.com +127.0.0.1 5czyx.voluumtrk.com 127.0.0.1 5e5ff159.linkbucks.com 127.0.0.1 5f8174fcf50c8f3fcaa2-1d2bf932855ebd52407efbb6cb4b64e5.r49.cf2.rackcdn.com +127.0.0.1 5iesg.voluumtrk.com +127.0.0.1 5jkjr.voluumtrk.com 127.0.0.1 5k9v3bc1-enehfzfv.netdna-ssl.com +127.0.0.1 5ktep.voluumtrk.com +127.0.0.1 5l11k.voluumtrk.com +127.0.0.1 5nv2z.voluumtrk.com +127.0.0.1 5pxno.voluumtrk.com +127.0.0.1 5r2vl.voluumtrk.com 127.0.0.1 5staraffiliateprograms.com 127.0.0.1 5starsupport.us.intellitxt.com +127.0.0.1 5t3is.voluumtrk.com 127.0.0.1 5thfinger.com +127.0.0.1 5toay.voluumtrk.com +127.0.0.1 5tujb.voluumtrk.com +127.0.0.1 5wtbg.voluumtrk.com +127.0.0.1 5ymz8.voluumtrk.com +127.0.0.1 5yrwz.voluumtrk.com +127.0.0.1 5ywhn.voluumtrk.com +127.0.0.1 5zegq.voluumtrk.com 127.0.0.1 6.6.87.194.dynamic.dol.ru 127.0.0.1 6.ptp22.com 127.0.0.1 60.6.87.194.dynamic.dol.ru @@ -998,8 +1153,10 @@ 127.0.0.1 6053-4c08-e99b-8d7a.reporo.net 127.0.0.1 6058.bapi.adsafeprotected.com 127.0.0.1 6067.bapi.adsafeprotected.com +127.0.0.1 60djx.voluumtrk.com 127.0.0.1 61.6.87.194.dynamic.dol.ru 127.0.0.1 6153-8fbc-408b-b36b.reporo.net +127.0.0.1 61qwg.voluumtrk.com 127.0.0.1 62.6.87.194.dynamic.dol.ru 127.0.0.1 623.bapi.adsafeprotected.com 127.0.0.1 62919.9458.302br.net @@ -1007,6 +1164,7 @@ 127.0.0.1 63.9view.atdmth.ya.869.9002.302br.net 127.0.0.1 6322.web-stats.org 127.0.0.1 63ads.go2cloud.org +127.0.0.1 63rbl.voluumtrk.com 127.0.0.1 64.208view.atdmt.com.1818.9020.302br.net 127.0.0.1 64.6.87.194.dynamic.dol.ru 127.0.0.1 65.163view.atdmt.com.801.8000.302br.net @@ -1015,6 +1173,7 @@ 127.0.0.1 6553-fe9a-fd29-78e9.reporo.net 127.0.0.1 66.151.63view.atdmt.com.801.8000.302br.net 127.0.0.1 66.6.87.194.dynamic.dol.ru +127.0.0.1 66mhb.voluumtrk.com 127.0.0.1 67.132.view.atdmt.com.324.6000.302br.net 127.0.0.1 67.13view.atdmt.com.324.6000.302br.net 127.0.0.1 67.6.87.194.dynamic.dol.ru @@ -1032,13 +1191,26 @@ 127.0.0.1 6a036421edec9693c962-4d1f758fa5668c904b9cd6e76bdc0d97.r71.cf2.rackcdn.com 127.0.0.1 6a802238f18629454f48-5fd47577f4847dded97d514126394433.r3.cf2.rackcdn.com 127.0.0.1 6ae6-f579-2f31-2cce.reporo.net +127.0.0.1 6aemj.voluumtrk.com +127.0.0.1 6aixd.voluumtrk.com 127.0.0.1 6bf28imiuaptt949z56frbrn0z.hop.clickbank.net +127.0.0.1 6c0jn.voluumtrk.com +127.0.0.1 6ceig.voluumtrk.com +127.0.0.1 6cl2q.voluumtrk.com +127.0.0.1 6ctvp.voluumtrk.com 127.0.0.1 6dollar.threadpitinc.netdna-cdn.com 127.0.0.1 6e2bd3848d2b73.se +127.0.0.1 6e2ey.voluumtrk.com 127.0.0.1 6e32870d409e7dd29e74-1f888a5500a4bf77de3933bbc73268d9.r21.cf2.rackcdn.com +127.0.0.1 6ecdz.voluumtrk.com 127.0.0.1 6f307e35.linkbucks.com 127.0.0.1 6f80-9f9d-e2a7-67cb.reporo.net +127.0.0.1 6fikt.voluumtrk.com +127.0.0.1 6nkks.voluumtrk.com +127.0.0.1 6p2bh.voluumtrk.com +127.0.0.1 6qtbr.voluumtrk.com 127.0.0.1 6r2uvllu.com +127.0.0.1 6voop.voluumtrk.com 127.0.0.1 7.6.87.194.dynamic.dol.ru 127.0.0.1 7.ptp22.com 127.0.0.1 7.rotator.wigetmedia.com @@ -1063,37 +1235,66 @@ 127.0.0.1 7303a09a9435e14d2141-577d252383f9c1423860b10142058ad7.r27.cf2.rackcdn.com 127.0.0.1 7336.engine.mobileapptracking.com 127.0.0.1 736516903.keywordblocks.com +127.0.0.1 73ec0.voluumtrk.com 127.0.0.1 74.6.87.194.dynamic.dol.ru +127.0.0.1 74647825.log.optimizely.com 127.0.0.1 75.6.87.194.dynamic.dol.ru 127.0.0.1 75334.9458.302br.net 127.0.0.1 757.justclick.ru 127.0.0.1 76.6.87.194.dynamic.dol.ru +127.0.0.1 76er2.voluumtrk.com +127.0.0.1 76f42.voluumtrk.com +127.0.0.1 76wik.voluumtrk.com +127.0.0.1 76wy2.voluumtrk.com 127.0.0.1 77.6.87.194.dynamic.dol.ru 127.0.0.1 777seo.com +127.0.0.1 77nid.voluumtrk.com 127.0.0.1 77tzz.win.adsservingtwig.xyz 127.0.0.1 78.6.87.194.dynamic.dol.ru 127.0.0.1 7828rbrmedia.go2cloud.org 127.0.0.1 7882.bapi.adsafeprotected.com 127.0.0.1 7899-7fc3-3379-0ddd.reporo.net 127.0.0.1 79.6.87.194.dynamic.dol.ru +127.0.0.1 79423.analytics.edgekey.net 127.0.0.1 7adpower.com 127.0.0.1 7b493gkdlhlgwr0brceox8158d.hop.clickbank.net +127.0.0.1 7bndh.voluumtrk.com 127.0.0.1 7cxcrejm.com 127.0.0.1 7dz98ad91l.s.ad6media.fr 127.0.0.1 7ec4-d4b0-f6eb-5588.reporo.net 127.0.0.1 7faa-e93a-19c5-c96d.reporo.net 127.0.0.1 7fdf-f670-24dd-3bd8.reporo.net 127.0.0.1 7fed-2832-993b-cb28.reporo.net +127.0.0.1 7fgba.voluumtrk.com +127.0.0.1 7fpdm.voluumtrk.com +127.0.0.1 7gfqw.voluumtrk.com +127.0.0.1 7hfmu.voluumtrk.com +127.0.0.1 7hpb1.voluumtrk.com +127.0.0.1 7hrpx.voluumtrk.com +127.0.0.1 7iytk.voluumtrk.com +127.0.0.1 7jftl.voluumtrk.com +127.0.0.1 7jjrd.voluumtrk.com +127.0.0.1 7lfu6.voluumtrk.com +127.0.0.1 7ntrp.voluumtrk.com +127.0.0.1 7qkuf.voluumtrk.com 127.0.0.1 7search.com 127.0.0.1 7sultans.com +127.0.0.1 7vhyc.voluumtrk.com +127.0.0.1 7yt26.voluumtrk.com 127.0.0.1 8.6.87.194.dynamic.dol.ru 127.0.0.1 8.dramafeverw2.appspot.com 127.0.0.1 8.ptp22.com 127.0.0.1 80.6.87.194.dynamic.dol.ru +127.0.0.1 8097-c493-8fbf-0dc2.reporo.net +127.0.0.1 80f99d.torrentinstaller.com 127.0.0.1 81.6.87.194.dynamic.dol.ru +127.0.0.1 811b-7d4e-7abb-a29e.reporo.net 127.0.0.1 81d9-e62d-0d23-6259.reporo.net +127.0.0.1 81ynv.voluumtrk.com 127.0.0.1 82.6.87.194.dynamic.dol.ru +127.0.0.1 824le.voluumtrk.com 127.0.0.1 82d914.se +127.0.0.1 82sn9.voluumtrk.com 127.0.0.1 83.6.87.194.dynamic.dol.ru 127.0.0.1 8328.bapi.adsafeprotected.com 127.0.0.1 833b446bf809d05d8cbe-22d497cab0248fe8bf9979b2e6155da2.r90.cf2.rackcdn.com @@ -1109,12 +1310,31 @@ 127.0.0.1 881.engine.mobileapptracking.com 127.0.0.1 8889-38b3-9b87-03a8.reporo.net 127.0.0.1 89.6.87.194.dynamic.dol.ru +127.0.0.1 89jw3.voluumtrk.com +127.0.0.1 8bdnl.voluumtrk.com +127.0.0.1 8cynz.voluumtrk.com +127.0.0.1 8ehdc.voluumtrk.com +127.0.0.1 8jkad.voluumtrk.com +127.0.0.1 8kvks.voluumtrk.com +127.0.0.1 8l1dc.voluumtrk.com +127.0.0.1 8lacf.voluumtrk.com +127.0.0.1 8lt4u.voluumtrk.com +127.0.0.1 8ma9b.voluumtrk.com +127.0.0.1 8mrbg.voluumtrk.com +127.0.0.1 8mzyb.voluumtrk.com +127.0.0.1 8ngcx.voluumtrk.com +127.0.0.1 8pzcp.voluumtrk.com +127.0.0.1 8rhf0.voluumtrk.com +127.0.0.1 8rvlh.voluumtrk.com +127.0.0.1 8wtua.voluumtrk.com +127.0.0.1 8x8ef.voluumtrk.com 127.0.0.1 9.6.87.194.dynamic.dol.ru 127.0.0.1 9.ptp22.com 127.0.0.1 90.6.87.194.dynamic.dol.ru 127.0.0.1 900quickcash.com 127.0.0.1 9014.302br.net 127.0.0.1 9030.302br.net +127.0.0.1 90cc4.voluumtrk.com 127.0.0.1 91.10view.atdmt.com.706.9000.302br.net 127.0.0.1 91.6.87.194.dynamic.dol.ru 127.0.0.1 911promotion.com @@ -1124,6 +1344,7 @@ 127.0.0.1 9215.302br.net 127.0.0.1 9243.302br.net 127.0.0.1 9250.302br.net +127.0.0.1 92b6j.voluumtrk.com 127.0.0.1 92x.tumblr.com 127.0.0.1 93.6.87.194.dynamic.dol.ru 127.0.0.1 9322.302br.net @@ -1132,6 +1353,7 @@ 127.0.0.1 9340.302br.net 127.0.0.1 9349.302br.net 127.0.0.1 9362.302br.net +127.0.0.1 938host.xyz 127.0.0.1 94.6.87.194.dynamic.dol.ru 127.0.0.1 9406.302br.net 127.0.0.1 9414.302br.net @@ -1153,35 +1375,64 @@ 127.0.0.1 968.bitterstrawberry.com 127.0.0.1 97.6.87.194.dynamic.dol.ru 127.0.0.1 9785216.log.optimizely.com +127.0.0.1 97a7e.voluumtrk.com 127.0.0.1 98.6.87.194.dynamic.dol.ru 127.0.0.1 980media.com 127.0.0.1 99.6.87.194.dynamic.dol.ru +127.0.0.1 991.com 127.0.0.1 99binary.go2cloud.org 127.0.0.1 99x.com 127.0.0.1 9ad06fe7lklknzbhi2pbzhoz9w.hop.clickbank.net +127.0.0.1 9ak04.voluumtrk.com 127.0.0.1 9am.2cnt.net 127.0.0.1 9ata.ero-advertising.com +127.0.0.1 9awdf.voluumtrk.com 127.0.0.1 9b80-7ce0-5a47-2323.reporo.net +127.0.0.1 9brpz.voluumtrk.com +127.0.0.1 9bwls.voluumtrk.com +127.0.0.1 9bx7v.voluumtrk.com 127.0.0.1 9c1f917.se 127.0.0.1 9c4e-159e-38f6-08ea.reporo.net 127.0.0.1 9cc85d.se 127.0.0.1 9cfa-356e-ec76-7adf.reporo.net +127.0.0.1 9ctyz.voluumtrk.com 127.0.0.1 9d1a9742.qqc.co 127.0.0.1 9down.us.intellitxt.com +127.0.0.1 9ezja.voluumtrk.com +127.0.0.1 9ezon.voluumtrk.com +127.0.0.1 9kfsz.voluumtrk.com +127.0.0.1 9kzzh.voluumtrk.com +127.0.0.1 9lbry.voluumtrk.com +127.0.0.1 9m1cn.voluumtrk.com +127.0.0.1 9m2hg.voluumtrk.com +127.0.0.1 9mwoq.voluumtrk.com +127.0.0.1 9mybb.voluumtrk.com +127.0.0.1 9nemj.voluumtrk.com +127.0.0.1 9pdq6.voluumtrk.com +127.0.0.1 9rzzg.voluumtrk.com 127.0.0.1 9thelm.go2cloud.org +127.0.0.1 9v798.voluumtrk.com +127.0.0.1 9wjg4.voluumtrk.com +127.0.0.1 9zaju.voluumtrk.com +127.0.0.1 9zfh1.voluumtrk.com 127.0.0.1 a-ads.com 127.0.0.1 a-counter.com.ua 127.0.0.1 a-counter.kiev.ua 127.0.0.1 a.1nimo.com 127.0.0.1 a.ad-sys.com 127.0.0.1 a.admob.com +127.0.0.1 a.adnium.com 127.0.0.1 a.adorika.net 127.0.0.1 a.adroll.com 127.0.0.1 a.ads.t-online.de +127.0.0.1 a.ads1.msn.com +127.0.0.1 a.ads2.msads.net +127.0.0.1 a.ads2.msn.com 127.0.0.1 a.adtech.de 127.0.0.1 a.adtwirl.com 127.0.0.1 a.affil.io 127.0.0.1 a.alimama.cn +127.0.0.1 a.analytics.yahoo.com 127.0.0.1 a.applovin.com 127.0.0.1 a.cam4free.com 127.0.0.1 a.chartboost.com @@ -1374,17 +1625,25 @@ 127.0.0.1 a.intentmedia.net 127.0.0.1 a.intgr.net 127.0.0.1 a.iogous.com +127.0.0.1 a.kat.cr 127.0.0.1 a.kat.ph +127.0.0.1 a.kickass.to +127.0.0.1 a.ktxtr.com 127.0.0.1 a.lazyswipe.com 127.0.0.1 a.livefreefunwith.me 127.0.0.1 a.mibbit.com 127.0.0.1 a.mologiq.net +127.0.0.1 a.msclickrdr.com 127.0.0.1 a.myapp.com 127.0.0.1 a.networkworld.com +127.0.0.1 a.o333o.com 127.0.0.1 a.oix.net +127.0.0.1 a.optinmonster.com +127.0.0.1 a.optnmnstr.com 127.0.0.1 a.plapre.no 127.0.0.1 a.postrelease.com 127.0.0.1 a.raasnet.com +127.0.0.1 a.rad.msn.com 127.0.0.1 a.rdb.so 127.0.0.1 a.remarketstats.com 127.0.0.1 a.rfihub.com @@ -1395,7 +1654,6 @@ 127.0.0.1 a.solarmovie.so 127.0.0.1 a.stanzapub.com 127.0.0.1 a.sucksex.com -127.0.0.1 a.suggestedconcept.com 127.0.0.1 a.t.webtracker.jp 127.0.0.1 a.targetingadvertiser.com 127.0.0.1 a.tellapart.com @@ -1407,8 +1665,10 @@ 127.0.0.1 a.tribalfusion.com 127.0.0.1 a.triggit.com 127.0.0.1 a.ttinline.com +127.0.0.1 a.turbodsp.com 127.0.0.1 a.twiago.com 127.0.0.1 a.unanimis.co.uk +127.0.0.1 a.varanymatrix.link 127.0.0.1 a.vartoken.com 127.0.0.1 a.visadd.com 127.0.0.1 a.visualrevenue.com @@ -1428,6 +1688,7 @@ 127.0.0.1 a03.korrelate.net 127.0.0.1 a04.korrelate.net 127.0.0.1 a05.korrelate.net +127.0.0.1 a0irt.voluumtrk.com 127.0.0.1 a1-m1.doublepimp.com 127.0.0.1 a1.admaster.net 127.0.0.1 a1.cdnpark.com @@ -1436,6 +1697,7 @@ 127.0.0.1 a1.interclick.com 127.0.0.1 a1.suntimes.com 127.0.0.1 a10shd.realshieldlinked.com +127.0.0.1 a11om.voluumtrk.com 127.0.0.1 a11shd.realshieldlinked.com 127.0.0.1 a12a30zg8rw248c8bl5o72qweh.hop.clickbank.net 127.0.0.1 a1388.m.akastream.net @@ -1449,11 +1711,14 @@ 127.0.0.1 a2.suntimes.com 127.0.0.1 a207.p.f.qz3.net 127.0.0.1 a2p.go2cloud.org +127.0.0.1 a2pub.com 127.0.0.1 a2shd.realshieldlinked.com +127.0.0.1 a3.mediagra.com 127.0.0.1 a3.suntimes.com 127.0.0.1 a3339.actonsoftware.com 127.0.0.1 a344.m.akastream.net 127.0.0.1 a39.ac-images.myspacecdn.com +127.0.0.1 a3hb0.voluumtrk.com 127.0.0.1 a3shd.realshieldlinked.com 127.0.0.1 a4.mediagra.com 127.0.0.1 a4.suntimes.com @@ -1462,6 +1727,10 @@ 127.0.0.1 a433.com 127.0.0.1 a4dtracker.com 127.0.0.1 a4dtrk.com +127.0.0.1 a4mwn.voluumtrk.com +127.0.0.1 a4nwz.voluumtrk.com +127.0.0.1 a4ocm.voluumtrk.com +127.0.0.1 a4rlj.voluumtrk.com 127.0.0.1 a4shd.realshieldlinked.com 127.0.0.1 a5.mediagra.com 127.0.0.1 a5.suntimes.com @@ -1479,6 +1748,7 @@ 127.0.0.1 a6ba.com 127.0.0.1 a6shd.realshieldlinked.com 127.0.0.1 a7.suntimes.com +127.0.0.1 a71tb.voluumtrk.com 127.0.0.1 a722-a332-7273-9e88.reporo.net 127.0.0.1 a76.p.f.qz3.net 127.0.0.1 a7shd.realshieldlinked.com @@ -1487,6 +1757,8 @@ 127.0.0.1 a9.com 127.0.0.1 a98dc034c7781a941eba-bac02262202668bbe918ea9fb5289cd2.r58.cf2.rackcdn.com 127.0.0.1 a9shd.realshieldlinked.com +127.0.0.1 aa-download.waw.pl +127.0.0.1 aa-gb.marketgid.com 127.0.0.1 aa.adfarm1.adition.com 127.0.0.1 aa.connextra.com 127.0.0.1 aa.newsblock.dt00.net @@ -1497,6 +1769,8 @@ 127.0.0.1 aad73c550c.se 127.0.0.1 aads.go2cloud.org 127.0.0.1 aads.us +127.0.0.1 aafq0.voluumtrk.com +127.0.0.1 aajtm.voluumtrk.com 127.0.0.1 aaotr.com 127.0.0.1 aap.bitterstrawberry.org 127.0.0.1 aarchives.ifriends.net @@ -1510,6 +1784,7 @@ 127.0.0.1 aax-us-east.amazon-adsystem.com 127.0.0.1 aax-us-pdx.amazon-adsystem.com 127.0.0.1 aax-us-west.amazon-adsystem.com +127.0.0.1 ab.airpush.com 127.0.0.1 ab.goodsblock.dt00.net 127.0.0.1 ab3a-f9be-43ed-76ca.reporo.net 127.0.0.1 abacus.go2cloud.org @@ -1518,9 +1793,12 @@ 127.0.0.1 abbie-surber.us 127.0.0.1 abbott.vo.llnwd.net 127.0.0.1 abby-clowers.us +127.0.0.1 abc.apportium.com 127.0.0.1 abc.bnex.com +127.0.0.1 abc.doublegear.com 127.0.0.1 abc.hearst.co.uk 127.0.0.1 abc.limaction.com +127.0.0.1 abc.mobile-10.com 127.0.0.1 abccampaignaudit.co.uk 127.0.0.1 abcjmp.com 127.0.0.1 abe.com.au @@ -1531,11 +1809,14 @@ 127.0.0.1 abo.libertysurf.fr 127.0.0.1 about.elicitapp.com 127.0.0.1 about.netster.com +127.0.0.1 aboutads.info 127.0.0.1 aboutads.quantcast.com 127.0.0.1 above-gw.liveadvert.com 127.0.0.1 abovetrack.com 127.0.0.1 abovetracking.com 127.0.0.1 abp.smartadcheck.de +127.0.0.1 abr4m.voluumtrk.com +127.0.0.1 abradora.com 127.0.0.1 abscbn.spinbox.net 127.0.0.1 absolstats.co.za 127.0.0.1 abz.com @@ -1555,6 +1836,8 @@ 127.0.0.1 accelerator-media.com 127.0.0.1 access.amberathome.com 127.0.0.1 access.njherald.com +127.0.0.1 accomplir.science +127.0.0.1 accorder.science 127.0.0.1 accounting.livejasmin.com 127.0.0.1 accountonline.bridgetrack.com 127.0.0.1 accuprod.amobee.com @@ -1564,6 +1847,7 @@ 127.0.0.1 acento.com 127.0.0.1 acestats.com 127.0.0.1 acheven2.go2cloud.org +127.0.0.1 achever.science 127.0.0.1 achmedia.com 127.0.0.1 acim.com 127.0.0.1 acme.bfast.com @@ -1579,6 +1863,7 @@ 127.0.0.1 acquiromedia.go2cloud.org 127.0.0.1 acrossperformance.go2cloud.org 127.0.0.1 acspaces.ero-advertising.com +127.0.0.1 act.webmasterplan.com 127.0.0.1 actforvictory.112.2o7.net 127.0.0.1 action.media6degrees.com 127.0.0.1 action.metaffiliation.com @@ -1588,8 +1873,10 @@ 127.0.0.1 activeagent.at 127.0.0.1 activeapps.go2cloud.org 127.0.0.1 activeboard.com +127.0.0.1 activepop.net 127.0.0.1 activetracker.activehotels.com 127.0.0.1 activewin.us.intellitxt.com +127.0.0.1 activities.alibaba.com 127.0.0.1 activities.niagara.comedycentral.com 127.0.0.1 activity.frequency.com 127.0.0.1 activity.homescape.com @@ -1608,9 +1895,11 @@ 127.0.0.1 ad-balancer.net 127.0.0.1 ad-blaster.com 127.0.0.1 ad-board.com +127.0.0.1 ad-brix.com 127.0.0.1 ad-cdn.technoratimedia.com 127.0.0.1 ad-clicks.com 127.0.0.1 ad-clix.com +127.0.0.1 ad-creatives-public.commondatastorage.googleapis.com 127.0.0.1 ad-dev.adotsolution.com 127.0.0.1 ad-emea.doubleclick.net 127.0.0.1 ad-emea.doubleclick.net.1000.9007.302br.net @@ -2325,6 +2614,7 @@ 127.0.0.1 ad-sponsor.com 127.0.0.1 ad-srv.net 127.0.0.1 ad-staff.com +127.0.0.1 ad-stir.com 127.0.0.1 ad-style.com 127.0.0.1 ad-tag.inner-active.mobi 127.0.0.1 ad-text.com @@ -2491,6 +2781,7 @@ 127.0.0.1 ad.coupons.com 127.0.0.1 ad.cpvfeed.com 127.0.0.1 ad.crosswalk.com +127.0.0.1 ad.crwdcntrl.net 127.0.0.1 ad.cuasar.info 127.0.0.1 ad.cz.doubleclick.net 127.0.0.1 ad.damnpervert.com @@ -14115,6 +14406,7 @@ 127.0.0.1 ad.ibest.com.br 127.0.0.1 ad.icasthq.com 127.0.0.1 ad.iconadserver.com +127.0.0.1 ad.idgtn.net 127.0.0.1 ad.ie.doubleclick.net 127.0.0.1 ad.iii.co.uk 127.0.0.1 ad.iinfo.cz @@ -14147,6 +14439,7 @@ 127.0.0.1 ad.kat.ph 127.0.0.1 ad.kataweb.it 127.0.0.1 ad.keenspace.com +127.0.0.1 ad.kixer.com 127.0.0.1 ad.kr.doubleclick.net 127.0.0.1 ad.kw.doubleclick.net 127.0.0.1 ad.layer-ad.org @@ -14188,6 +14481,7 @@ 127.0.0.1 ad.mlnadvertising.com 127.0.0.1 ad.mo.doubleclick.net 127.0.0.1 ad.moboqo.com +127.0.0.1 ad.monetizus.com 127.0.0.1 ad.msn.co.il 127.0.0.1 ad.musicmatch.com 127.0.0.1 ad.mwork.vn @@ -14210,6 +14504,7 @@ 127.0.0.1 ad.nozonedata.com 127.0.0.1 ad.nttnavi.co.jp 127.0.0.1 ad.nz.doubleclick.net +127.0.0.1 ad.ohmyad.co 127.0.0.1 ad.ohmynews.com 127.0.0.1 ad.onlineadserv.com 127.0.0.1 ad.onyx7.com @@ -14238,6 +14533,7 @@ 127.0.0.1 ad.pl.doubleclick.net 127.0.0.1 ad.playground.ru 127.0.0.1 ad.preferances.com +127.0.0.1 ad.prismamediadigital.com 127.0.0.1 ad.propellerads.com 127.0.0.1 ad.proxy.sh 127.0.0.1 ad.prv.pl @@ -14290,6 +14586,7 @@ 127.0.0.1 ad.slygreetings.com 127.0.0.1 ad.sma.punto.net 127.0.0.1 ad.sma.punto.org +127.0.0.1 ad.smaad.jp 127.0.0.1 ad.smaclick.com 127.0.0.1 ad.smartgroups.com 127.0.0.1 ad.smartorrent.com @@ -14352,6 +14649,7 @@ 127.0.0.1 ad.vikadsk.com 127.0.0.1 ad.virtualave.com 127.0.0.1 ad.vonna.com +127.0.0.1 ad.vrvm.com 127.0.0.1 ad.vuiads.org 127.0.0.1 ad.walla.co.il 127.0.0.1 ad.wap4dollars.in @@ -15090,6 +15388,7 @@ 127.0.0.1 ad2click.go2cloud.org 127.0.0.1 ad2games.com 127.0.0.1 ad2m.adk2.co +127.0.0.1 ad2play.ftv-publicite.fr 127.0.0.1 ad2up.com 127.0.0.1 ad3.adfarm1.adition.com 127.0.0.1 ad3.adspaces.ero-advertising.com @@ -15386,8 +15685,11 @@ 127.0.0.1 ad499m.adk2.co 127.0.0.1 ad49m.adk2.co 127.0.0.1 ad4game.com +127.0.0.1 ad4games.com 127.0.0.1 ad4m.adk2.co 127.0.0.1 ad4mat.co.uk +127.0.0.1 ad4mat.de +127.0.0.1 ad4me.net 127.0.0.1 ad4partners.com 127.0.0.1 ad5.adfarm1.adition.com 127.0.0.1 ad5.adrevolver.com @@ -15559,6 +15861,7 @@ 127.0.0.1 ad89.paycount.com 127.0.0.1 ad89m.adk2.co 127.0.0.1 ad8m.adk2.co +127.0.0.1 ad9.adfarm1.adition.com 127.0.0.1 ad9.bannerbank.ru 127.0.0.1 ad9.bb.ru 127.0.0.1 ad9.hpg.com.br @@ -15634,6 +15937,7 @@ 127.0.0.1 adbrite.com 127.0.0.1 adbucks.brandreachsys.com 127.0.0.1 adbucks.com +127.0.0.1 adbuddiz.com 127.0.0.1 adbull.com 127.0.0.1 adbureau.com 127.0.0.1 adbureau.net @@ -15648,6 +15952,7 @@ 127.0.0.1 adc.tripple.at 127.0.0.1 adcache.aftenposten.no 127.0.0.1 adcade.com +127.0.0.1 adcamp.ru 127.0.0.1 adcash.com 127.0.0.1 adcastplus.net 127.0.0.1 adcdnx.com @@ -15674,6 +15979,7 @@ 127.0.0.1 adclickafrica.com 127.0.0.1 adclickint.go2cloud.org 127.0.0.1 adclickmedia.com +127.0.0.1 adclickxpress.com 127.0.0.1 adclient-uol.lp.uol.com.br 127.0.0.1 adclient.163.com 127.0.0.1 adclient.detelefoongids.nl @@ -15699,8 +16005,11 @@ 127.0.0.1 adcycle.com 127.0.0.1 adcycle.everyone.net 127.0.0.1 adcycle.standard.net +127.0.0.1 addapptr.com 127.0.0.1 addash.co 127.0.0.1 addbtest.timesink.com +127.0.0.1 addebris.link +127.0.0.1 addeppo.com 127.0.0.1 addesktop.com 127.0.0.1 addfreestats.com 127.0.0.1 addiply.com @@ -15742,7 +16051,6 @@ 127.0.0.1 adext.inkclub.com 127.0.0.1 adextent.com 127.0.0.1 adf.ero-advertising.com -127.0.0.1 adf.ly 127.0.0.1 adf.send.microad.jp 127.0.0.1 adfacten.go2cloud.org 127.0.0.1 adfactory88.com @@ -15863,6 +16171,8 @@ 127.0.0.1 adforce.com 127.0.0.1 adforce.ru 127.0.0.1 adforgames.com +127.0.0.1 adfornepal.com +127.0.0.1 adfox.ru 127.0.0.1 adfpaces.ero-advertising.com 127.0.0.1 adframesrc.com 127.0.0.1 adfrontiers.com @@ -15870,6 +16180,7 @@ 127.0.0.1 adfuel.com 127.0.0.1 adfusion.com 127.0.0.1 adfux.com +127.0.0.1 adg.bzgint.com 127.0.0.1 adgaem.go2cloud.org 127.0.0.1 adgalax.com 127.0.0.1 adgardener.com @@ -15940,6 +16251,7 @@ 127.0.0.1 adisfy.com 127.0.0.1 adisn.com 127.0.0.1 adistas.go2cloud.org +127.0.0.1 aditic.net 127.0.0.1 adition.com 127.0.0.1 adition.net 127.0.0.1 aditor.go2cloud.org @@ -15948,6 +16260,7 @@ 127.0.0.1 adjaws.go2cloud.org 127.0.0.1 adjector.com 127.0.0.1 adjewel.go2cloud.org +127.0.0.1 adjourne.com 127.0.0.1 adjug.com 127.0.0.1 adjuggler.com 127.0.0.1 adjuggler.yourdictionary.com @@ -15961,6 +16274,7 @@ 127.0.0.1 adk2cdn.cpmrocket.com 127.0.0.1 adk2trk.cpmrocket.com 127.0.0.1 adkaramba.go2cloud.org +127.0.0.1 adkernel.com 127.0.0.1 adklik.com.tr 127.0.0.1 adklip.com 127.0.0.1 adklo.com @@ -15977,6 +16291,7 @@ 127.0.0.1 adlermode.com 127.0.0.1 adlet.voice2page.com 127.0.0.1 adlev.neodatagroup.com +127.0.0.1 adlibr.com 127.0.0.1 adlink.de 127.0.0.1 adlink.net 127.0.0.1 adlink.shopsafe.co.nz @@ -15993,6 +16308,7 @@ 127.0.0.1 adm.shinobi.jp 127.0.0.1 admagnet.go2cloud.org 127.0.0.1 admagnet.net +127.0.0.1 admail.am 127.0.0.1 admailtiser.com 127.0.0.1 adman.gr 127.0.0.1 admanage.com @@ -16010,6 +16326,8 @@ 127.0.0.1 admaster.com.cn 127.0.0.1 admaster.de 127.0.0.1 admaster.heyos.com +127.0.0.1 admaster.union.ucweb.com +127.0.0.1 admaven.adk2x.com 127.0.0.1 admax.go2cloud.org 127.0.0.1 admax.nexage.com 127.0.0.1 admaya.in @@ -16060,6 +16378,7 @@ 127.0.0.1 admission.net 127.0.0.1 admitad.com 127.0.0.1 admix.go2cloud.org +127.0.0.1 admixer.co.kr 127.0.0.1 admixer.net 127.0.0.1 admized.com 127.0.0.1 admn.go2cloud.org @@ -16077,7 +16396,9 @@ 127.0.0.1 admotion.com.ar 127.0.0.1 admp.sanoma.fi 127.0.0.1 admperformance.go2cloud.org +127.0.0.1 admtpmp127.adsk2.co 127.0.0.1 admulti.com +127.0.0.1 admyapp.net 127.0.0.1 adn.blinkx.com 127.0.0.1 adn.fusionads.net 127.0.0.1 adn.meinsol.de @@ -16101,17 +16422,21 @@ 127.0.0.1 adnetwork.net.vn 127.0.0.1 adnetwork.nextgen.net 127.0.0.1 adnetworkme.com +127.0.0.1 adnetworkperformance.com 127.0.0.1 adnetxchange.com 127.0.0.1 adnext.fr 127.0.0.1 adnimation.com 127.0.0.1 adnoble.com +127.0.0.1 adnotbad.com 127.0.0.1 adnotch.com 127.0.0.1 adnxs1.com 127.0.0.1 ado.internet.cz +127.0.0.1 ado.pro-market.net 127.0.0.1 adobemcspb.b.sc.omtrdc.net 127.0.0.1 adoc.fr 127.0.0.1 adocean.pl 127.0.0.1 adolent.go2cloud.org +127.0.0.1 adonads.com 127.0.0.1 adonion.com 127.0.0.1 adonis.go2cloud.org 127.0.0.1 adonnetwork.com @@ -16120,6 +16445,7 @@ 127.0.0.1 adoptim.com 127.0.0.1 adorigin.com 127.0.0.1 adorika.net +127.0.0.1 adosia.com 127.0.0.1 adotic.com 127.0.0.1 adotmobanalytics.go2cloud.org 127.0.0.1 adotomy.com @@ -16127,6 +16453,7 @@ 127.0.0.1 adotube.com 127.0.0.1 adp.cplaza.ne.jp 127.0.0.1 adp.go2cloud.org +127.0.0.1 adpanel.net 127.0.0.1 adparad.net 127.0.0.1 adparlor.com 127.0.0.1 adpath.mobi @@ -16142,6 +16469,7 @@ 127.0.0.1 adplace.adsame.com 127.0.0.1 adplace.i.adsame.com 127.0.0.1 adplans.info +127.0.0.1 adplans.net 127.0.0.1 adplus.co.id 127.0.0.1 adplus.goo.mx 127.0.0.1 adpop.theglobe.net @@ -16165,6 +16493,7 @@ 127.0.0.1 adqa.timesink.com 127.0.0.1 adqadb.timesink.com 127.0.0.1 adqadl.timesink.com +127.0.0.1 adquota.com 127.0.0.1 adr-g3-1.vindicosuite.com 127.0.0.1 adrd.taxuan.net 127.0.0.1 adreactor.com @@ -16205,6 +16534,7 @@ 127.0.0.1 ads-links.com 127.0.0.1 ads-littlestarmedia.co.uk 127.0.0.1 ads-static.saymedia.com +127.0.0.1 ads-trk.vidible.tv 127.0.0.1 ads-us.pictela.net 127.0.0.1 ads-useast1n.yldbt.com 127.0.0.1 ads-ussj1.focalink.com @@ -16231,6 +16561,7 @@ 127.0.0.1 ads.ad-center.com 127.0.0.1 ads.ad-maven.com 127.0.0.1 ads.ad-vo.net +127.0.0.1 ads.ad2games.com 127.0.0.1 ads.ad4game.com 127.0.0.1 ads.adamoads.com 127.0.0.1 ads.adaptv.advertising.com @@ -16440,6 +16771,7 @@ 127.0.0.1 ads.cnn.com 127.0.0.1 ads.cobrad.com 127.0.0.1 ads.columbian.com +127.0.0.1 ads.comeadvertisewithus.com 127.0.0.1 ads.comicbookresources.com 127.0.0.1 ads.commission365.com 127.0.0.1 ads.communitech.net @@ -16449,6 +16781,7 @@ 127.0.0.1 ads.contactmusic.com 127.0.0.1 ads.contentabc.com 127.0.0.1 ads.contentabc.net +127.0.0.1 ads.contextweb.com 127.0.0.1 ads.coolads.de 127.0.0.1 ads.coolmobilegroup.com 127.0.0.1 ads.corky.net @@ -16487,11 +16820,11 @@ 127.0.0.1 ads.dfiles.eu 127.0.0.1 ads.dfiles.ru 127.0.0.1 ads.dgonn.com +127.0.0.1 ads.dhakatribune.com 127.0.0.1 ads.digital-digest.com 127.0.0.1 ads.digitalhealthcare.com 127.0.0.1 ads.digitalmedianet.com 127.0.0.1 ads.digitalpoint.com -127.0.0.1 ads.digitalpressconsortium.com 127.0.0.1 ads.dinclinx.com 127.0.0.1 ads.dir.bg 127.0.0.1 ads.directionsmag.com @@ -16531,6 +16864,7 @@ 127.0.0.1 ads.eniro.com 127.0.0.1 ads.enliven.net 127.0.0.1 ads.epatra.com +127.0.0.1 ads.epom.com 127.0.0.1 ads.ero-advertising.com 127.0.0.1 ads.eromarkt.de 127.0.0.1 ads.erotism.net @@ -16581,6 +16915,7 @@ 127.0.0.1 ads.fortunecity.com 127.0.0.1 ads.fortunecity.de 127.0.0.1 ads.fortunecity.org +127.0.0.1 ads.fotoable.com 127.0.0.1 ads.foxkidseurope.net 127.0.0.1 ads.fredericksburg.com 127.0.0.1 ads.free-banners.com @@ -16609,6 +16944,7 @@ 127.0.0.1 ads.genericlink.com 127.0.0.1 ads.geoads.net 127.0.0.1 ads.geverads.com +127.0.0.1 ads.gfy.com 127.0.0.1 ads.glispa.com 127.0.0.1 ads.globalmedianet.netdna-cdn.com 127.0.0.1 ads.globaltraffico.com @@ -16789,9 +17125,11 @@ 127.0.0.1 ads.mobgold.com 127.0.0.1 ads.mobidea.com 127.0.0.1 ads.mobilecore.com +127.0.0.1 ads.mobilefuse.net 127.0.0.1 ads.mobilepad.co 127.0.0.1 ads.mobilityware.com 127.0.0.1 ads.mobiteasy.com +127.0.0.1 ads.mobvertising.net 127.0.0.1 ads.mocean.mobi 127.0.0.1 ads.models.com 127.0.0.1 ads.mofos.com @@ -16884,6 +17222,7 @@ 127.0.0.1 ads.orange142.com 127.0.0.1 ads.p-club.net 127.0.0.1 ads.p161.net +127.0.0.1 ads.panoramtech.net 127.0.0.1 ads.parkedskins.com 127.0.0.1 ads.parrysound.com 127.0.0.1 ads.partner.lottoland.com @@ -17089,8 +17428,11 @@ 127.0.0.1 ads.poptarts.me 127.0.0.1 ads.pornad.com 127.0.0.1 ads.pornerbros.com +127.0.0.1 ads.pornful.com +127.0.0.1 ads.pornfuror.com 127.0.0.1 ads.pornmarathon.com 127.0.0.1 ads.pornoitalicum.com +127.0.0.1 ads.pornolaan.com 127.0.0.1 ads.pornoreich.com 127.0.0.1 ads.pornotouze.com 127.0.0.1 ads.powweb.com @@ -17101,6 +17443,7 @@ 127.0.0.1 ads.pressdemo.com 127.0.0.1 ads.prestigecasino.com 127.0.0.1 ads.prisacom.com +127.0.0.1 ads.prnt.sc 127.0.0.1 ads.prntscr.com 127.0.0.1 ads.pro-market.net 127.0.0.1 ads.proboards.com @@ -17134,6 +17477,7 @@ 127.0.0.1 ads.realitytraffic.com.s2.gvirabi.com 127.0.0.1 ads.realitytraffic.com.s3.gvirabi.com 127.0.0.1 ads.realmedia.de +127.0.0.1 ads.realposition.ru 127.0.0.1 ads.realslutparty.com 127.0.0.1 ads.recoletos.es 127.0.0.1 ads.redegg.co @@ -17142,9 +17486,11 @@ 127.0.0.1 ads.reed-elsevier.com 127.0.0.1 ads.reelhd.com 127.0.0.1 ads.reelvidz.com +127.0.0.1 ads.reflexcash.com 127.0.0.1 ads.regiedepub.com 127.0.0.1 ads.revmob.com 127.0.0.1 ads.revsci.net +127.0.0.1 ads.reward.rakuten.jp 127.0.0.1 ads.rim.co.uk 127.0.0.1 ads.rkads.com 127.0.0.1 ads.roanoke.com @@ -17171,7 +17517,9 @@ 127.0.0.1 ads.seattletimes.com 127.0.0.1 ads.securebanners.biz 127.0.0.1 ads.seniorfriendfinder.com +127.0.0.1 ads.servebom.com 127.0.0.1 ads.servesharp.net +127.0.0.1 ads.sex.com 127.0.0.1 ads.sexier.com 127.0.0.1 ads.sexinyourcity.com 127.0.0.1 ads.sexspaces.com @@ -17179,6 +17527,7 @@ 127.0.0.1 ads.sexspaces.nl 127.0.0.1 ads.sextip.de 127.0.0.1 ads.sf.net +127.0.0.1 ads.shorte.st 127.0.0.1 ads.showbizspy.com 127.0.0.1 ads.showmeflix.com 127.0.0.1 ads.showsplash.com @@ -17236,6 +17585,7 @@ 127.0.0.1 ads.tagword.com 127.0.0.1 ads.taiphanmem.org 127.0.0.1 ads.tapit.com +127.0.0.1 ads.taptapnetworks.com 127.0.0.1 ads.techbang.com 127.0.0.1 ads.techtv.com 127.0.0.1 ads.teixido.co @@ -17249,6 +17599,7 @@ 127.0.0.1 ads.theglobeandmail.com 127.0.0.1 ads.thehiveworks.com 127.0.0.1 ads.theindependent.com +127.0.0.1 ads.themoneytizer.com 127.0.0.1 ads.theolympian.com 127.0.0.1 ads.theporndb.com 127.0.0.1 ads.thestar.com @@ -17315,6 +17666,7 @@ 127.0.0.1 ads.veelporno.com 127.0.0.1 ads.vegas.com 127.0.0.1 ads.veloxia.com +127.0.0.1 ads.ventivmedia.com 127.0.0.1 ads.videoaxs.com 127.0.0.1 ads.videomega.tv 127.0.0.1 ads.videoslots.com @@ -17370,6 +17722,7 @@ 127.0.0.1 ads.xhamster.com 127.0.0.1 ads.xl.co.id 127.0.0.1 ads.xl.pt +127.0.0.1 ads.xlxtra.com 127.0.0.1 ads.xpg.com.br 127.0.0.1 ads.xpics.com 127.0.0.1 ads.xtac.com @@ -17426,9 +17779,11 @@ 127.0.0.1 ads1.hpg.com.br 127.0.0.1 ads1.intelliads.com 127.0.0.1 ads1.iweb.cortica.com +127.0.0.1 ads1.mb.datingadzone.com 127.0.0.1 ads1.mediacapital.pt 127.0.0.1 ads1.mocean.mobi 127.0.0.1 ads1.mojiva.com +127.0.0.1 ads1.msads.net 127.0.0.1 ads1.msn.com 127.0.0.1 ads1.qadabra.com 127.0.0.1 ads1.searchmiracle.com @@ -17853,6 +18208,7 @@ 127.0.0.1 adsdlmaster.timesink.com 127.0.0.1 adsdot.ph 127.0.0.1 adsearch.adkontekst.pl +127.0.0.1 adsee.jp 127.0.0.1 adseller.hop.ru 127.0.0.1 adsender.us 127.0.0.1 adsenger.com @@ -17895,6 +18251,7 @@ 127.0.0.1 adserv01.ru 127.0.0.1 adserv2.bravenet.com 127.0.0.1 adserv3.bravenet.com +127.0.0.1 adserve-nj.1rx.io 127.0.0.1 adserve.adtoll.com 127.0.0.1 adserve.advertising.com 127.0.0.1 adserve.bizrate.com @@ -17989,6 +18346,7 @@ 127.0.0.1 adserver.gameparty.net 127.0.0.1 adserver.gamigo.de 127.0.0.1 adserver.garden.nl +127.0.0.1 adserver.goforandroid.com 127.0.0.1 adserver.gorillanation.com 127.0.0.1 adserver.gtxcash.com 127.0.0.1 adserver.hardwareanalysis.com @@ -18016,6 +18374,8 @@ 127.0.0.1 adserver.manutd.com 127.0.0.1 adserver.matchcraft.com 127.0.0.1 adserver.mindshare.de +127.0.0.1 adserver.mobillex.com +127.0.0.1 adserver.motdgd.com 127.0.0.1 adserver.mundo-service.de 127.0.0.1 adserver.mylinea.com 127.0.0.1 adserver.myownemail.com @@ -18089,6 +18449,7 @@ 127.0.0.1 adservice.everyone.net 127.0.0.1 adservices02.picadmedia.com 127.0.0.1 adserving.ads.ec +127.0.0.1 adserving.adswisher.com 127.0.0.1 adserving.autotrader.com 127.0.0.1 adserving.bizcrank.com 127.0.0.1 adserving.budsinc.com @@ -18154,6 +18515,7 @@ 127.0.0.1 adsmeans.com 127.0.0.1 adsmile.biz 127.0.0.1 adsmile.go2cloud.org +127.0.0.1 adsmo.ru 127.0.0.1 adsmobi.com 127.0.0.1 adsmobil.go2cloud.org 127.0.0.1 adsmoon.com @@ -18212,9 +18574,12 @@ 127.0.0.1 adsrv2.theledger.com 127.0.0.1 adsrv2.wilmingtonstar.com 127.0.0.1 adsrvmedia.adk2.co +127.0.0.1 adsrvmedia.adk2x.com +127.0.0.1 adsrvmedia.adsk2.co 127.0.0.1 adsrvmedia.com 127.0.0.1 adsrvr.org 127.0.0.1 adss.ad-maven.com +127.0.0.1 adss.comeadvertisewithus.com 127.0.0.1 adss.yahoo.com 127.0.0.1 adsserving.net 127.0.0.1 adssl01.adtech.fr @@ -18256,6 +18621,7 @@ 127.0.0.1 adsxgm.com 127.0.0.1 adsxxxtraff.com 127.0.0.1 adsymptotic.com +127.0.0.1 adsyndication.msn.com 127.0.0.1 adsynergy.com 127.0.0.1 adsys.townnews.com 127.0.0.1 adsyst.biz @@ -18305,6 +18671,7 @@ 127.0.0.1 adtrack1.pl 127.0.0.1 adtracker.inmobi.com 127.0.0.1 adtracking.vinden.nl +127.0.0.1 adtrackone.eu 127.0.0.1 adtrade.net 127.0.0.1 adtrading.de 127.0.0.1 adtrafic.go2cloud.org @@ -18328,6 +18695,8 @@ 127.0.0.1 adultimate.tubewolf.com 127.0.0.1 adultmoda.com 127.0.0.1 adulttubetraffic.com +127.0.0.1 adunblock.com +127.0.0.1 adunblock.fr 127.0.0.1 aduncle.go2cloud.org 127.0.0.1 adv-adserver.com 127.0.0.1 adv-banner.iol.it @@ -18502,6 +18871,7 @@ 127.0.0.1 advertiser.adknowledge.com 127.0.0.1 advertiser.ads.ec 127.0.0.1 advertiser.cyberghostvpn.com +127.0.0.1 advertiser.fyber.com 127.0.0.1 advertiser.lifestyleadnetwork.com 127.0.0.1 advertiser.microad.jp 127.0.0.1 advertiser.net @@ -18550,6 +18920,7 @@ 127.0.0.1 advertmedia.de 127.0.0.1 advertpay.net 127.0.0.1 advertpro.sitepoint.com +127.0.0.1 adverts.adam4adam.com 127.0.0.1 adverts.archant.co.uk 127.0.0.1 adverts.creativemark.co.uk 127.0.0.1 adverts.digitalspy.co.uk @@ -18558,6 +18929,7 @@ 127.0.0.1 advertserve.com 127.0.0.1 advertstats.com 127.0.0.1 advertstream.com +127.0.0.1 advertzer.com 127.0.0.1 advgoogle.com 127.0.0.1 advice-ads.s3.amazonaws.com 127.0.0.1 advidi.optimuum.com @@ -18572,6 +18944,7 @@ 127.0.0.1 advmania.com 127.0.0.1 advmedia.go2cloud.org 127.0.0.1 advmedialtd.com +127.0.0.1 advnstrdm541231c0esprod.122.2o7.net 127.0.0.1 advnt01.com 127.0.0.1 advnt03.com 127.0.0.1 advolution.de @@ -18613,25 +18986,34 @@ 127.0.0.1 adxpose.com 127.0.0.1 adxpower.com 127.0.0.1 adxregie.com +127.0.0.1 adxserve.net 127.0.0.1 adyoulike.com +127.0.0.1 adz.mobi 127.0.0.1 adz.zwee.ly 127.0.0.1 adzerk-www.s3.amazonaws.com 127.0.0.1 adzerk.net 127.0.0.1 adzerver.com 127.0.0.1 adzly.com +127.0.0.1 adzmedia.com 127.0.0.1 adzmob.com +127.0.0.1 adzmobi.com 127.0.0.1 adzone.stltoday.com 127.0.0.1 adzones.com 127.0.0.1 adzonesocial.go2cloud.org 127.0.0.1 adzonk.com 127.0.0.1 adzouk.com +127.0.0.1 adzworld.in 127.0.0.1 ae.siteapps.com 127.0.0.1 ae28-1e4a-ec3b-e9e6.reporo.net +127.0.0.1 aeekj.voluumtrk.com 127.0.0.1 aehistory.112.2o7.net +127.0.0.1 aeht9.voluumtrk.com 127.0.0.1 aen.belugaboost.com 127.0.0.1 aeps.112.2o7.net 127.0.0.1 aeropostale.resultspage.com 127.0.0.1 aetv.112.2o7.net +127.0.0.1 aexp.demdex.net +127.0.0.1 af-gb.mgid.com 127.0.0.1 afdtkr.com 127.0.0.1 aff-jp.dxlive.com 127.0.0.1 aff-jp.exshot.com @@ -18744,6 +19126,7 @@ 127.0.0.1 affiliate.thedatingnetwork.com 127.0.0.1 affiliate.travelnow.com 127.0.0.1 affiliate.trk4.com +127.0.0.1 affiliate.trust.zone 127.0.0.1 affiliate.tvmtracker.com 127.0.0.1 affiliate.unimall.de 127.0.0.1 affiliate.viator.com @@ -18792,6 +19175,7 @@ 127.0.0.1 affiliates.cupidplc.com 127.0.0.1 affiliates.de 127.0.0.1 affiliates.deluxepass.com +127.0.0.1 affiliates.eblastengine.com 127.0.0.1 affiliates.esellerate.net 127.0.0.1 affiliates.galapartners.co.uk 127.0.0.1 affiliates.globat.com @@ -18819,6 +19203,7 @@ 127.0.0.1 affiliates.net 127.0.0.1 affiliates.nl 127.0.0.1 affiliates.old-and-young-lesbians.com +127.0.0.1 affiliates.onetravel.com 127.0.0.1 affiliates.org 127.0.0.1 affiliates.picaboocorp.com 127.0.0.1 affiliates.playboy.com @@ -18863,6 +19248,7 @@ 127.0.0.1 affimob.go2cloud.org 127.0.0.1 affina.com 127.0.0.1 affinitytracking.go2cloud.org +127.0.0.1 affirmer.science 127.0.0.1 affl.sucuri.net 127.0.0.1 affscout.go2cloud.org 127.0.0.1 afftrack.box.tmpds286.trkag1.com @@ -18870,6 +19256,7 @@ 127.0.0.1 afftracker.info 127.0.0.1 afftrackinglinks.com 127.0.0.1 afftrackr.com +127.0.0.1 affyield.com 127.0.0.1 afiliati.ro 127.0.0.1 afimg.liveperson.com 127.0.0.1 afin.doublepimp.com @@ -18885,26 +19272,36 @@ 127.0.0.1 afterview.ru 127.0.0.1 aftp.linksynergy.com 127.0.0.1 aftrk.com +127.0.0.1 afwl8.voluumtrk.com 127.0.0.1 ag.travelocity.com.edgesuite.net 127.0.0.1 agata-lillard.us 127.0.0.1 agata-wadlington.us 127.0.0.1 agenda.complex.com 127.0.0.1 agf-test.2cnt.net 127.0.0.1 aggregateknowledge.com +127.0.0.1 agi5y.voluumtrk.com +127.0.0.1 agiter.science +127.0.0.1 agjdv.voluumtrk.com 127.0.0.1 agmedia.go2cloud.org 127.0.0.1 agmtrk.com 127.0.0.1 agnstaging.com 127.0.0.1 agnvbmaus.112.2o7.net 127.0.0.1 agofm-test-off.2cnt.net +127.0.0.1 agoramedia.com 127.0.0.1 agpaffiliates.go2cloud.org 127.0.0.1 ags.beta.advertising.com 127.0.0.1 ah-ha.com 127.0.0.1 ah.pricegrabber.com +127.0.0.1 ahhgu.voluumtrk.com 127.0.0.1 ahomedia.zxfactory.nl +127.0.0.1 ahqcr.voluumtrk.com 127.0.0.1 ai.hitbox.com 127.0.0.1 ai.net 127.0.0.1 ai.pricegrabber.com 127.0.0.1 ai.yimg.jp +127.0.0.1 aidps.atdmt.com +127.0.0.1 aidqi.voluumtrk.com +127.0.0.1 aiguille.ovh 127.0.0.1 aim-ads.com 127.0.0.1 aim-charts.pf.aol.com 127.0.0.1 aim1.adsoftware.com @@ -18917,9 +19314,11 @@ 127.0.0.1 aio.lp.mydas.mobi 127.0.0.1 airbrake.io 127.0.0.1 airpush.com +127.0.0.1 airpushmarketing.s3.amazonaws.com 127.0.0.1 ais.abacast.com 127.0.0.1 aj.600z.com 127.0.0.1 aj.adjungle.com +127.0.0.1 aj3zt.voluumtrk.com 127.0.0.1 ajcclassifieds.com 127.0.0.1 ajnad.aljazeera.net 127.0.0.1 ak-ns.sascdn.com @@ -18928,31 +19327,41 @@ 127.0.0.1 ak.imgfarm.com 127.0.0.1 ak.linkstorm.net 127.0.0.1 ak.pipoffers.apnpartners.com +127.0.0.1 ak.sail-horizon.com 127.0.0.1 ak.tfag.de 127.0.0.1 ak.toolbar.mywebsearch.com +127.0.0.1 ak.webfetti.com 127.0.0.1 ak1.abmr.net 127.0.0.1 ak1.imgaft.com +127.0.0.1 ak1s.abmr.net 127.0.0.1 aka-cdn-ns.adtech.de 127.0.0.1 aka-cdn-ns.adtechus.com +127.0.0.1 aka-cdn.adtechus.com 127.0.0.1 aka.p80.net 127.0.0.1 akaads-abc.starwave.com 127.0.0.1 akaads-espn.starwave.com 127.0.0.1 akamai.bizrate.com 127.0.0.1 akamai.smartadserver.com 127.0.0.1 akmnetwork.com +127.0.0.1 aknoj.voluumtrk.com 127.0.0.1 aksb-a.akamaihd.net 127.0.0.1 aktrack.pubmatic.com +127.0.0.1 akvoi.voluumtrk.com 127.0.0.1 akway.com +127.0.0.1 akz.imgfarm.com 127.0.0.1 al.myrice.com 127.0.0.1 alaina-hain.us 127.0.0.1 alana-yancy.us 127.0.0.1 albanytimesunion.122.2o7.net 127.0.0.1 albinoblacksheep.us.intellitxt.com +127.0.0.1 albopa.work 127.0.0.1 alchemynteryield.jmp9.com 127.0.0.1 alea.adam.ad.daum.net 127.0.0.1 alea.adam.daum.net 127.0.0.1 aleah-wilfong.us 127.0.0.1 alenty.2cnt.net +127.0.0.1 alert.comnotification.com +127.0.0.1 alerts.zapping.tv 127.0.0.1 alexa-harrod.us 127.0.0.1 alexandra-magruder.us 127.0.0.1 aliaz.go2cloud.org @@ -18970,6 +19379,10 @@ 127.0.0.1 alladream.go2cloud.org 127.0.0.1 allatv.justclick.ru 127.0.0.1 allbanners.ru +127.0.0.1 alldcs-distinct.outbrain.com +127.0.0.1 alldcs.outbrain.com +127.0.0.1 alldcs.sphere.com +127.0.0.1 alleyezonme-collection.buzzfeed.com 127.0.0.1 allhiphop.us.intellitxt.com 127.0.0.1 allie-cunha.us 127.0.0.1 allie-hobdy.us @@ -18981,7 +19394,7 @@ 127.0.0.1 allstats4u.com 127.0.0.1 alltereg0.ru 127.0.0.1 alltraff.ru -127.0.0.1 alluremedia.com.au +127.0.0.1 allumer.science 127.0.0.1 allyes.com 127.0.0.1 alog.umeng.co 127.0.0.1 alog.umeng.com @@ -19026,6 +19439,7 @@ 127.0.0.1 altfarm.mediaplex.com.77799.9664.302br.net 127.0.0.1 altfarm.mediaplex.com.77801.9664.302br.net 127.0.0.1 altiris.112.2o7.net +127.0.0.1 alwaysplanetss.com 127.0.0.1 alyse-bolster.us 127.0.0.1 alysia-lenoir.us 127.0.0.1 alyson-canaday.us @@ -19040,13 +19454,18 @@ 127.0.0.1 amclicks.com 127.0.0.1 amica.ivwbox.de 127.0.0.1 amie-koepke.us +127.0.0.1 amkhn.voluumtrk.com +127.0.0.1 amoad.com 127.0.0.1 amobee.com 127.0.0.1 amoffers.go2cloud.org 127.0.0.1 amoffers.hasoffers.com 127.0.0.1 amono.justclick.ru 127.0.0.1 amp.122.2o7.net +127.0.0.1 ampoule.ovh +127.0.0.1 ampxchange.com 127.0.0.1 ampya.ivwbox.de 127.0.0.1 ampya01.wt-eu02.net +127.0.0.1 amres.voluumtrk.com 127.0.0.1 ams.addflow.ru 127.0.0.1 ams1.ib.adnxs.com 127.0.0.1 ams1.mobile.adnxs.com @@ -19076,7 +19495,10 @@ 127.0.0.1 amscdn.btrll.com 127.0.0.1 amtracking.go2cloud.org 127.0.0.1 amtracking01.com +127.0.0.1 amuco.voluumtrk.com +127.0.0.1 amuser.science 127.0.0.1 amusive.com +127.0.0.1 amwqo.voluumtrk.com 127.0.0.1 amy-stahr.us 127.0.0.1 amz.steamprices.com 127.0.0.1 amzn.trendhure.com @@ -19140,14 +19562,20 @@ 127.0.0.1 analytics.localytics.com 127.0.0.1 analytics.loop-cloud.de 127.0.0.1 analytics.matchbin.com +127.0.0.1 analytics.mefeedia.com 127.0.0.1 analytics.mindjolt.com +127.0.0.1 analytics.mobile.yandex.net 127.0.0.1 analytics.moneycontrol.com 127.0.0.1 analytics.moz.com +127.0.0.1 analytics.mozzi.com 127.0.0.1 analytics.mpn.mokonocdn.com 127.0.0.1 analytics.nascar.com +127.0.0.1 analytics.newsinc.com +127.0.0.1 analytics.openload.co 127.0.0.1 analytics.optilead.co.uk 127.0.0.1 analytics.poferries.com 127.0.0.1 analytics.popsci.com +127.0.0.1 analytics.query.yahoo.com 127.0.0.1 analytics.r17.com 127.0.0.1 analytics.rechtslupe.org 127.0.0.1 analytics.recruitics.com @@ -19166,16 +19594,20 @@ 127.0.0.1 analytics.traidnt.net 127.0.0.1 analytics.traviangames.com 127.0.0.1 analytics.tribeca.vidavee.com +127.0.0.1 analytics.tube8.phncdn.com 127.0.0.1 analytics.twitter.com 127.0.0.1 analytics.unister-gmbh.de 127.0.0.1 analytics.upworthy.com 127.0.0.1 analytics.us.archive.org 127.0.0.1 analytics.wildtangent.com +127.0.0.1 analytics.wittyfeed.com 127.0.0.1 analytics.worldnow.com 127.0.0.1 analytics.xl.pt +127.0.0.1 analytics.yahoo.com 127.0.0.1 analytics.yam.com 127.0.0.1 analytics.yola.net 127.0.0.1 analytics.yolacdn.net +127.0.0.1 analytics.youporn.phncdn.com 127.0.0.1 analytics.zhihu.com 127.0.0.1 analyticsengine.s3.amazonaws.com 127.0.0.1 analyze.full-marke.com @@ -19204,15 +19636,19 @@ 127.0.0.1 android.reporo.net 127.0.0.1 android.revmob.com 127.0.0.1 androidads20.adcolony.com +127.0.0.1 androidads21.adcolony.com +127.0.0.1 androidquery.appspot.com 127.0.0.1 androidsdk.ads.mp.mydas.mobi 127.0.0.1 androidsk.ads.mp.mydas.mobi 127.0.0.1 andropit.ivwbox.de +127.0.0.1 anfyx.voluumtrk.com 127.0.0.1 angebot0.ivwbox.de 127.0.0.1 angelia-malave.us 127.0.0.1 angelina-mckernan.us 127.0.0.1 angelina-roberts.us 127.0.0.1 angelina-stacey.us 127.0.0.1 angiba.112.2o7.net +127.0.0.1 anglais.science 127.0.0.1 angmar.112.2o7.net 127.0.0.1 angpar.112.2o7.net 127.0.0.1 angtr.112.2o7.net @@ -19235,6 +19671,7 @@ 127.0.0.1 annuncio.com.do 127.0.0.1 anon.doubleclick.speedera.net 127.0.0.1 anonymousads.com +127.0.0.1 anpqt.voluumtrk.com 127.0.0.1 ans1.adsoftware.com 127.0.0.1 ans2.adsoftware.com 127.0.0.1 ans3.adsoftware.com @@ -19242,6 +19679,7 @@ 127.0.0.1 answers.us.intellitxt.com 127.0.0.1 antenne.ivwbox.de 127.0.0.1 antezeta.com +127.0.0.1 anticyclone.science 127.0.0.1 antoinette-cowles.us 127.0.0.1 antoinette-grabowski.us 127.0.0.1 antoinette-hanlon.us @@ -19255,6 +19693,9 @@ 127.0.0.1 anz.ads.link4ads.com 127.0.0.1 anz.ms.link4ads.com 127.0.0.1 anzeigen.bei-uns.de +127.0.0.1 aobwt.voluumtrk.com +127.0.0.1 aocular.com +127.0.0.1 aojws.voluumtrk.com 127.0.0.1 aok.122.207.net 127.0.0.1 aol.ivwbox.de 127.0.0.1 aol.tt.omtrdc.net @@ -19267,6 +19708,7 @@ 127.0.0.1 aolwpmqnoban.112.2o7.net 127.0.0.1 aolwpnscom.112.2o7.net 127.0.0.1 aolwpnswhatsnew.112.2o7.net +127.0.0.1 aonx6.voluumtrk.com 127.0.0.1 aos.wall.youmi.net 127.0.0.1 ap.ads.link4ads.com 127.0.0.1 ap.lijit.com @@ -19278,6 +19720,7 @@ 127.0.0.1 ap2.alphagirlz.mobi 127.0.0.1 apac.analytics.yahoo.com 127.0.0.1 apachestat.cimedia.net +127.0.0.1 apapi.dc121677.com 127.0.0.1 apapi.rapsio.com 127.0.0.1 aparat.ads.saba-e.com 127.0.0.1 apdigitalorg.112.2o7.net @@ -19289,10 +19732,13 @@ 127.0.0.1 api.ad-patrick.com 127.0.0.1 api.ad.ly 127.0.0.1 api.adip.ly +127.0.0.1 api.adlure.net +127.0.0.1 api.adrta.com 127.0.0.1 api.adsymptotic.com 127.0.0.1 api.adyoulike.com 127.0.0.1 api.airpush.com 127.0.0.1 api.analytics.omgpop.com +127.0.0.1 api.appfireworks.com 127.0.0.1 api.applifier.com 127.0.0.1 api.appoxee.com 127.0.0.1 api.appsflyer.com @@ -19304,16 +19750,19 @@ 127.0.0.1 api.bitp.it 127.0.0.1 api.bizographics.com 127.0.0.1 api.burt.io +127.0.0.1 api.celtra.com 127.0.0.1 api.chartboost.com 127.0.0.1 api.choicestream.com 127.0.0.1 api.citygridmedia.com 127.0.0.1 api.conduit.com +127.0.0.1 api.contentclick.co.uk 127.0.0.1 api.contextly.com 127.0.0.1 api.crittercism.com 127.0.0.1 api.demandbase.com 127.0.0.1 api.domob.cn 127.0.0.1 api.elasticemail.com 127.0.0.1 api.ero-advertising.com +127.0.0.1 api.flicktweets.com 127.0.0.1 api.flurry.com 127.0.0.1 api.flyertown.ca 127.0.0.1 api.fortumo.com @@ -19338,8 +19787,11 @@ 127.0.0.1 api.mixpanel.com 127.0.0.1 api.mobileapptracking.com 127.0.0.1 api.mobpartner.mobi +127.0.0.1 api.nanigans.com +127.0.0.1 api.native.ai 127.0.0.1 api.naturaltracking.com 127.0.0.1 api.nrelate.com +127.0.0.1 api.omniata.com 127.0.0.1 api.opencandy.com 127.0.0.1 api.optimizely.com 127.0.0.1 api.otherlevels.com @@ -19358,11 +19810,13 @@ 127.0.0.1 api.statdns.com 127.0.0.1 api.superfish.com 127.0.0.1 api.tapfortap.com +127.0.0.1 api.taps.io 127.0.0.1 api.target.smi2.net 127.0.0.1 api.thetrafficstat.net 127.0.0.1 api.trak.io 127.0.0.1 api.tweetmeme.com 127.0.0.1 api.umbel.com +127.0.0.1 api.umeng.com 127.0.0.1 api.unthem.com 127.0.0.1 api.usersnap.com 127.0.0.1 api.v2.sslsecure1.com @@ -19377,6 +19831,7 @@ 127.0.0.1 api.v2.sslsecure9.com 127.0.0.1 api.value-bit.com 127.0.0.1 api.viglink.com +127.0.0.1 api.virool.com 127.0.0.1 api.vungle.com 127.0.0.1 api.w3i.com 127.0.0.1 api.widgetbucks.com @@ -19385,6 +19840,7 @@ 127.0.0.1 api.yeahmobi.com 127.0.0.1 api.yieldkit.com 127.0.0.1 api.yp.com +127.0.0.1 api.zanox.com 127.0.0.1 api.zanox.ws 127.0.0.1 api.zedo.com 127.0.0.1 api1.intellicontact.com @@ -19403,7 +19859,9 @@ 127.0.0.1 api8.thesearchagency.net 127.0.0.1 api9.thesearchagency.net 127.0.0.1 apiae.hopscore.com +127.0.0.1 apis.sharethrough.com 127.0.0.1 apiservices.krxd.net +127.0.0.1 apiskywebbercom-a.akamaihd.net 127.0.0.1 apkquery.ksmobile.net 127.0.0.1 apnx-match.dotomi.com 127.0.0.1 apo.ero-advertising.com @@ -19416,6 +19874,7 @@ 127.0.0.1 app-abm.marketo.com 127.0.0.1 app-abq.marketo.com 127.0.0.1 app-authority.com +127.0.0.1 app-creatives.cdn.admobile.me 127.0.0.1 app-promo.com 127.0.0.1 app-sj01.marketo.com 127.0.0.1 app-sj09.marketo.com @@ -19423,6 +19882,7 @@ 127.0.0.1 app-sjn.marketo.com 127.0.0.1 app-sjo.marketo.com 127.0.0.1 app-storage.elicitapp.com +127.0.0.1 app.adjust.com 127.0.0.1 app.adjust.io 127.0.0.1 app.androidphone.mobi 127.0.0.1 app.app-authority.com @@ -19445,6 +19905,8 @@ 127.0.0.1 app.medyanetads.com 127.0.0.1 app.mlcampaignru.com 127.0.0.1 app.mlnewsletterru.com +127.0.0.1 app.mstrck03d.com +127.0.0.1 app.mysafeurl.com 127.0.0.1 app.qualaroo.com 127.0.0.1 app.salecycle.com 127.0.0.1 app.sgiz.mobi @@ -19457,13 +19919,21 @@ 127.0.0.1 app4us.info 127.0.0.1 appads.com 127.0.0.1 appapi.inspsearchapi.com +127.0.0.1 appartement.science 127.0.0.1 appboy.com +127.0.0.1 appclick.co +127.0.0.1 appclick.net 127.0.0.1 appdog.com +127.0.0.1 appel.science 127.0.0.1 appflood.com 127.0.0.1 appflood.go2cloud.org +127.0.0.1 appfly.mobi 127.0.0.1 appia.com +127.0.0.1 appintop.com 127.0.0.1 applab-sdk.amazon.com +127.0.0.1 applebarq.com 127.0.0.1 appleglobal.112.2o7.net +127.0.0.1 appleitl.rewards-bonanza-2.com 127.0.0.1 applestoreus.112.2o7.net 127.0.0.1 application.bfast.com 127.0.0.1 applicationgrabb.net @@ -19474,6 +19944,8 @@ 127.0.0.1 applift.com 127.0.0.1 appload.ingest.crittercism.com 127.0.0.1 applovin.com +127.0.0.1 appnexus.download.akamai.com +127.0.0.1 appodeal.com 127.0.0.1 appolicious.com 127.0.0.1 apprebates.com 127.0.0.1 appredeem.com @@ -19492,6 +19964,8 @@ 127.0.0.1 apps.social.omniture.com 127.0.0.1 apps2.outfit7.com 127.0.0.1 appscion.go2cloud.org +127.0.0.1 appserver-ap.com +127.0.0.1 appserver-cp.com 127.0.0.1 appsfire.com 127.0.0.1 appsfire.net 127.0.0.1 appsflyer.com @@ -19508,7 +19982,9 @@ 127.0.0.1 april-wilcoxon.us 127.0.0.1 aproegroup.go2cloud.org 127.0.0.1 apromoweb.com +127.0.0.1 aps.hearstnp.com 127.0.0.1 apsalar.com +127.0.0.1 aptrk.com 127.0.0.1 apture.com 127.0.0.1 apu04c0.audientia.net 127.0.0.1 apu0640.audientia.net @@ -19516,11 +19992,14 @@ 127.0.0.1 apx.internal-redirect.avazutracking.net 127.0.0.1 apx.irck.avazutracking.net 127.0.0.1 apx.matk.avazutracking.net +127.0.0.1 apx.moatads.com +127.0.0.1 apx.motads.com 127.0.0.1 apx.trck.avazutracking.net 127.0.0.1 aqua.7eer.net 127.0.0.1 aquantive.com 127.0.0.1 aquasoft.us.intellitxt.com 127.0.0.1 aqueduct.com +127.0.0.1 aqueux.science 127.0.0.1 ar.2.cqcounter.com 127.0.0.1 ar.hao123.com 127.0.0.1 ar.tns-counter.ru @@ -19535,6 +20014,7 @@ 127.0.0.1 ar8.atwola.com 127.0.0.1 ar9.atwola.com 127.0.0.1 arabadzhi.justclick.ru +127.0.0.1 arabmistress.s3.amazonaws.com 127.0.0.1 arabyads.go2cloud.org 127.0.0.1 arachne.cz 127.0.0.1 arads.0fees.us @@ -19560,6 +20040,8 @@ 127.0.0.1 ard.xxxblackbook.com 127.0.0.1 ard.yahoo.co.jp 127.0.0.1 ardboers.ivwbox.de +127.0.0.1 ardrone.swoop.com +127.0.0.1 areasins.com 127.0.0.1 areasnap.com 127.0.0.1 arena-quantum.co.uk 127.0.0.1 arena.altitude-arena.com @@ -19572,6 +20054,9 @@ 127.0.0.1 arlequin0.levillage.org 127.0.0.1 arlequin01.levillage.org 127.0.0.1 arlequin02.levillage.org +127.0.0.1 arnmp.voluumtrk.com +127.0.0.1 arome.science +127.0.0.1 arriere.science 127.0.0.1 arrowtec.go2cloud.org 127.0.0.1 arroyomedia.go2cloud.org 127.0.0.1 ars.couphole.com @@ -19602,6 +20087,7 @@ 127.0.0.1 as.wkcr.cz 127.0.0.1 as5000.com 127.0.0.1 as5000.wunderground.com +127.0.0.1 asa.brwavz.com 127.0.0.1 ashely-felts.us 127.0.0.1 ashleigh-shellenbarger.us 127.0.0.1 ashly-legette.us @@ -19613,11 +20099,14 @@ 127.0.0.1 askjolene.ero-advertising.com 127.0.0.1 askmen.us.intellitxt.com 127.0.0.1 askmen2.us.intellitxt.com +127.0.0.1 asmedia.adsupplyssl.com 127.0.0.1 asmi-mobile.nuggad.net 127.0.0.1 asn-trk.advolution.de 127.0.0.1 asn.advolution.de +127.0.0.1 asooda.com 127.0.0.1 asotrack1.fluentmobile.com 127.0.0.1 asplayer-ovp.piksel.com +127.0.0.1 asqcr.voluumtrk.com 127.0.0.1 asrv-a.akamaihd.net 127.0.0.1 ass4all.com 127.0.0.1 asset-0.tenderapp.com @@ -19632,12 +20121,12 @@ 127.0.0.1 assets.cntdy.mobi 127.0.0.1 assets.devx.com 127.0.0.1 assets.infinity-tracking.net +127.0.0.1 assets.kampyle.com 127.0.0.1 assets.kuhlmann-software.at 127.0.0.1 assets.matchbin.com 127.0.0.1 assets.olark.com 127.0.0.1 assets.omniture.com 127.0.0.1 assets.oomz.de -127.0.0.1 assets.pinterest.com 127.0.0.1 assets.scan.me 127.0.0.1 assets.servedby-buysellads.com 127.0.0.1 assets.sharethrough.com @@ -19652,9 +20141,11 @@ 127.0.0.1 astrocash.org 127.0.0.1 astroportal.de.intellitxt.com 127.0.0.1 astrowo.ivwbox.de +127.0.0.1 astrsk.net 127.0.0.1 asuum.com 127.0.0.1 asv.nuggad.net 127.0.0.1 asx13.com +127.0.0.1 at.amgdgt.com 127.0.0.1 at.atwola.com 127.0.0.1 at.netster.com 127.0.0.1 ata.ero-advertising.com @@ -19690,15 +20181,19 @@ 127.0.0.1 atemda.com 127.0.0.1 athena-dirksen.us 127.0.0.1 atinternet.com +127.0.0.1 atkeg.voluumtrk.com 127.0.0.1 atkins.vo.llnwd.net 127.0.0.1 atlas.astrology.com 127.0.0.1 atlasdmt.com +127.0.0.1 atmek.voluumtrk.com 127.0.0.1 atomicleads.go2cloud.org 127.0.0.1 atrack.allposters.com 127.0.0.1 atrack.art.com 127.0.0.1 atsfi.de 127.0.0.1 attadworks.turn.com 127.0.0.1 atti.velti.com +127.0.0.1 atticwicket.com +127.0.0.1 attirer.science 127.0.0.1 attrack.ientrynetwork.net 127.0.0.1 attributiontrackingga.googlecode.com 127.0.0.1 attservicesinc.tt.omtrdc.net @@ -19720,9 +20215,11 @@ 127.0.0.1 aubrey-kutz.us 127.0.0.1 aubrey-oyola.us 127.0.0.1 auctionads.com +127.0.0.1 aucun.science 127.0.0.1 aud.pubmatic.com 127.0.0.1 audi-club.ru 127.0.0.1 audiag.112.2o7.net +127.0.0.1 audience.powerlinks.com 127.0.0.1 audio.digidip.net 127.0.0.1 audio.iad.cimedia.net 127.0.0.1 audiode.ivwbox.de @@ -19741,6 +20238,7 @@ 127.0.0.1 audra-parsons.us 127.0.0.1 audra-talbott.us 127.0.0.1 augsallg.ivwbox.de +127.0.0.1 auihv.voluumtrk.com 127.0.0.1 aumospo.ivwbox.de 127.0.0.1 aureate.com 127.0.0.1 aurora-musso.us @@ -19748,6 +20246,7 @@ 127.0.0.1 aurora.starpartner.com 127.0.0.1 australiapost.122.2o7.net 127.0.0.1 austria1.adverserve.net +127.0.0.1 auteur.science 127.0.0.1 auth.livejasmin.com 127.0.0.1 auth20120430.getjar.com 127.0.0.1 auto-ping.com @@ -19764,16 +20263,23 @@ 127.0.0.1 autotrader.tt.omtrdc.net 127.0.0.1 autoweb.112.2o7.net 127.0.0.1 auvito.ivwbox.de +127.0.0.1 av.ageverify.co 127.0.0.1 av.beap.bc.yahoo.com +127.0.0.1 av7df.voluumtrk.com 127.0.0.1 ava-meader.us 127.0.0.1 avads.co.uk +127.0.0.1 avant.science 127.0.0.1 avatar.truongton.net 127.0.0.1 avatarcash.com 127.0.0.1 avatarresources.com 127.0.0.1 avatraffic.com 127.0.0.1 avazudsp.net +127.0.0.1 avazutracking.net +127.0.0.1 aventure.science 127.0.0.1 avenuea.com 127.0.0.1 avgtechnologies.112.2o7.net +127.0.0.1 avoir.science +127.0.0.1 avouer.science 127.0.0.1 avp.innity.com 127.0.0.1 avres.net 127.0.0.1 aw.masterstats.com @@ -19806,6 +20312,7 @@ 127.0.0.1 awjul7.radar11ab.co.uk 127.0.0.1 awjul8.radar11ab.co.uk 127.0.0.1 awjul9.radar11ab.co.uk +127.0.0.1 awos8.voluumtrk.com 127.0.0.1 aws-ap-southeast-2a.bench.cedexis.com 127.0.0.1 aws.tracker.squidanalytics.com 127.0.0.1 ax10.a.cocolog-nifty.com @@ -19814,10 +20321,17 @@ 127.0.0.1 axf8.net 127.0.0.1 axis7.go2cloud.org 127.0.0.1 axislogger.appspot.com +127.0.0.1 axldy.voluumtrk.com +127.0.0.1 axonix.com 127.0.0.1 axp.2-01-29be-0007.cdx.cedexis.net 127.0.0.1 axp.zedo.com +127.0.0.1 aydne.voluumtrk.com +127.0.0.1 ayuzu.voluumtrk.com 127.0.0.1 az.trackword.net 127.0.0.1 az413505.vo.msecnd.net +127.0.0.1 az416426.vo.msecnd.net +127.0.0.1 az598575.vo.msecnd.net +127.0.0.1 az708531.vo.msecnd.net 127.0.0.1 az7t8.com 127.0.0.1 azads.net 127.0.0.1 azalead.com @@ -19827,6 +20341,8 @@ 127.0.0.1 b-online.de 127.0.0.1 b.admedia.com 127.0.0.1 b.ads1.msn.com +127.0.0.1 b.ads2.msads.net +127.0.0.1 b.aklamio.com 127.0.0.1 b.aol.com 127.0.0.1 b.aol.de 127.0.0.1 b.baidu.com @@ -19834,20 +20350,25 @@ 127.0.0.1 b.big7.com 127.0.0.1 b.bm324.com 127.0.0.1 b.c8.net.ua +127.0.0.1 b.camplace.com 127.0.0.1 b.casalemedia.com 127.0.0.1 b.chartboost.com +127.0.0.1 b.collective-media.net 127.0.0.1 b.dlsite.net 127.0.0.1 b.esecure-transaction.com 127.0.0.1 b.grvcdn.com 127.0.0.1 b.hidemyass.com 127.0.0.1 b.huffingtonpost.com 127.0.0.1 b.huffingtonpost.de +127.0.0.1 b.ifmnwi.club 127.0.0.1 b.inbox.lv 127.0.0.1 b.localpages.com +127.0.0.1 b.netscape.com 127.0.0.1 b.paipai.com 127.0.0.1 b.photobucket.com 127.0.0.1 b.portalnet.cl 127.0.0.1 b.positive-technology.co.uk +127.0.0.1 b.rad.msn.com 127.0.0.1 b.retailmenot.com 127.0.0.1 b.rmgserving.com 127.0.0.1 b.sc.omtrdc.net @@ -19865,6 +20386,7 @@ 127.0.0.1 b.zedo.com 127.0.0.1 b.zeroredirect.com 127.0.0.1 b.zeroredirect1.com +127.0.0.1 b0bsi.voluumtrk.com 127.0.0.1 b1.2cnt.net 127.0.0.1 b1.boards2go.com 127.0.0.1 b117f8da23446a91387efea0e428392a.pl @@ -19872,6 +20394,7 @@ 127.0.0.1 b12.sitemeter.com 127.0.0.1 b17261b2b5010f3c6c93-d77e110c9a6908e75cd02fbd7eb24572.r86.cf2.rackcdn.com 127.0.0.1 b1d.org +127.0.0.1 b1d6z.voluumtrk.com 127.0.0.1 b2.boards2go.com 127.0.0.1 b2.sitemeter.com 127.0.0.1 b22b15b8b99fac60bd7cb6566b2e92034ebef6ee.sdk.testflightapp.com @@ -19879,6 +20402,8 @@ 127.0.0.1 b243-4eb2-8667-4a30.reporo.net 127.0.0.1 b2blinkshare.com 127.0.0.1 b2c-mlm.marketo.com +127.0.0.1 b2fkj.voluumtrk.com +127.0.0.1 b2wtd.voluumtrk.com 127.0.0.1 b3-uk.mookie1.com 127.0.0.1 b3-uk.mookie1.com.63430.9488.302br.net 127.0.0.1 b3.mookie1.com @@ -19886,15 +20411,19 @@ 127.0.0.1 b3.toparcadehits.com 127.0.0.1 b344-7507-e4c2-b742.reporo.net 127.0.0.1 b365.2cnt.net +127.0.0.1 b36df47b3d.site.internapcdn.net 127.0.0.1 b4.rivalgaming.com 127.0.0.1 b4.toparcadehits.com 127.0.0.1 b4.yahoo.co.jp 127.0.0.1 b4psads.com +127.0.0.1 b5wpo.voluumtrk.com +127.0.0.1 b7.sitemeter.com 127.0.0.1 b99217.r.axf8.net 127.0.0.1 ba.ccm2.net 127.0.0.1 ba.kioskea.net 127.0.0.1 ba2b687.se 127.0.0.1 ba42-c08c-2e10-c6e3.reporo.net +127.0.0.1 ba965.voluumtrk.com 127.0.0.1 babes.go2cloud.org 127.0.0.1 babes.picrush.com 127.0.0.1 babm.texthelp.com @@ -19914,6 +20443,8 @@ 127.0.0.1 badges.instagram.com 127.0.0.1 badoink.com 127.0.0.1 badzeit.ivwbox.de +127.0.0.1 baiducdn.biz +127.0.0.1 baisser.science 127.0.0.1 bakler.net 127.0.0.1 bal.ad.dotandad.com 127.0.0.1 baleron.com @@ -19924,6 +20455,7 @@ 127.0.0.1 bam-5.nr-data.net 127.0.0.1 bam.nr-data.net 127.0.0.1 ban.krooncasino.com +127.0.0.1 ban.promotools.biz 127.0.0.1 ban3ers.ero-advertising.com 127.0.0.1 ban4ers.ero-advertising.com 127.0.0.1 bandai.hs.llnwd.net @@ -20431,6 +20963,7 @@ 127.0.0.1 banners.anunciweb.pt 127.0.0.1 banners.aris.ge 127.0.0.1 banners.asiafriendfinder.com +127.0.0.1 banners.askmecca.com 127.0.0.1 banners.babetimes.com 127.0.0.1 banners.babylon-x.com 127.0.0.1 banners.babylonbucks.com @@ -20883,6 +21416,7 @@ 127.0.0.1 bardzomedia.com 127.0.0.1 bargainpda.us.intellitxt.com 127.0.0.1 barium.cheezdev.com +127.0.0.1 barometre.science 127.0.0.1 barry-hoskinson.us 127.0.0.1 bart.ixn.local.vmsn.de 127.0.0.1 baryof.com @@ -20903,10 +21437,12 @@ 127.0.0.1 bbcios.2cnt.net 127.0.0.1 bbciostest.2cnt.net 127.0.0.1 bbcnewscouk.112.2o7.net +127.0.0.1 bbf7n.voluumtrk.com 127.0.0.1 bbn.img.com.ua 127.0.0.1 bbp.brazzers.com 127.0.0.1 bbpntg3.homestead.com 127.0.0.1 bbs.duba.net +127.0.0.1 bbvj6.voluumtrk.com 127.0.0.1 bc84-88b8-96b7-6515.reporo.net 127.0.0.1 bcanalytics.bigcommerce.com 127.0.0.1 bcfads.com @@ -20915,6 +21451,7 @@ 127.0.0.1 bcp.crwdcntrl.net 127.0.0.1 bd.moatads.com 127.0.0.1 bdbaffiliates.go2cloud.org +127.0.0.1 bddff.voluumtrk.com 127.0.0.1 bdv.bidvertiser.com 127.0.0.1 be.mobsweet.com 127.0.0.1 be.nedstat.net @@ -20936,6 +21473,7 @@ 127.0.0.1 beacon.indieclick.com 127.0.0.1 beacon.indieclicktv.com 127.0.0.1 beacon.kmi-us.com +127.0.0.1 beacon.krxd.net 127.0.0.1 beacon.lycos.com 127.0.0.1 beacon.mtgx.tv 127.0.0.1 beacon.net-updater.com @@ -20948,11 +21486,15 @@ 127.0.0.1 beacon.sina.com.cn 127.0.0.1 beacon.sinauda.com 127.0.0.1 beacon.stage.walmart.com +127.0.0.1 beacon.wikia-services.com 127.0.0.1 beacon.www.theguardian.com 127.0.0.1 beacon2.indieclick.com 127.0.0.1 beacon2.indieclicktv.com 127.0.0.1 beacons.brandads.net +127.0.0.1 beam.hlserve.com +127.0.0.1 beap-bc.yahoo.com 127.0.0.1 beastasuum.a.ssl.fastly.net +127.0.0.1 bebsz.voluumtrk.com 127.0.0.1 becausebabes.go2cloud.org 127.0.0.1 becontext.com 127.0.0.1 bee-ads.com @@ -20966,6 +21508,7 @@ 127.0.0.1 bellca.demdex.net 127.0.0.1 bellevue.ivwbox.de 127.0.0.1 bellserviceeng.112.2o7.net +127.0.0.1 belole.ru 127.0.0.1 belstat.be 127.0.0.1 benchbrands.com 127.0.0.1 berater.adition.com @@ -20977,8 +21520,10 @@ 127.0.0.1 bernadette-stefanski.us 127.0.0.1 beseen.com 127.0.0.1 best-ads.com +127.0.0.1 best-deals-products.com 127.0.0.1 bestadbid.com 127.0.0.1 bestaffiliates.go2cloud.org +127.0.0.1 bestfwdservice.com 127.0.0.1 bestmusic.2cnt.net 127.0.0.1 bestwebnutfunblack.biz 127.0.0.1 beta-old.superstats.com @@ -21009,16 +21554,21 @@ 127.0.0.1 beyourownaffiliate.com 127.0.0.1 bfd.secureintl.com 127.0.0.1 bfe9-0d31-18b3-7493.reporo.net +127.0.0.1 bgn0f.voluumtrk.com +127.0.0.1 bgngq.voluumtrk.com 127.0.0.1 bgsurveys.go2cloud.org 127.0.0.1 bh.ams.contextweb.com 127.0.0.1 bh.contextweb.com +127.0.0.1 bh2y3.voluumtrk.com 127.0.0.1 bhconsulting.go2cloud.org 127.0.0.1 bhgmarketing.112.2o7.net 127.0.0.1 bi-three.wooga.com 127.0.0.1 bi.installcore.com 127.0.0.1 bi.medscape.com 127.0.0.1 bi.softservers.net +127.0.0.1 bi827.voluumtrk.com 127.0.0.1 bianca-rhoton.us +127.0.0.1 bibqh.voluumtrk.com 127.0.0.1 bid.pubmatic.com 127.0.0.1 bid.socdm.com 127.0.0.1 bidclix.com @@ -21027,6 +21577,7 @@ 127.0.0.1 bidhere.go2cloud.org 127.0.0.1 bidsystem.adknowledge.com 127.0.0.1 bidvertiser.com +127.0.0.1 big.sddan.com 127.0.0.1 bigads.guj.de 127.0.0.1 bigasia.com 127.0.0.1 bigbangads.go2cloud.org @@ -21038,6 +21589,7 @@ 127.0.0.1 bigsoccer.us.intellitxt.com 127.0.0.1 bigstats.net 127.0.0.1 bigtopleads.cn +127.0.0.1 biipe.voluumtrk.com 127.0.0.1 bilbob.com 127.0.0.1 bild.de.intellitxt.com 127.0.0.1 bild.ivwbox.de @@ -21046,6 +21598,7 @@ 127.0.0.1 bill-info.com 127.0.0.1 billboard.cz 127.0.0.1 billiger.ivwbox.de +127.0.0.1 billmscurlrev.com 127.0.0.1 bimlocal.com 127.0.0.1 binarybliss.com 127.0.0.1 binaryoffers.go2cloud.org @@ -21065,6 +21618,7 @@ 127.0.0.1 bizban.net 127.0.0.1 bizsolutions.strands.com 127.0.0.1 bizzclick.com +127.0.0.1 bjvim.voluumtrk.com 127.0.0.1 blabbermouth.us.intellitxt.com 127.0.0.1 blackchek.popunder.ru 127.0.0.1 blacklightimages.com @@ -21075,12 +21629,15 @@ 127.0.0.1 blankrefer.com 127.0.0.1 blast4traffic.com 127.0.0.1 blatfee.ero-advertising.com +127.0.0.1 blazwuatr.com 127.0.0.1 bleepingcomputer.us.intellitxt.com 127.0.0.1 blige2.tk 127.0.0.1 blindferretmedia.go2cloud.org +127.0.0.1 blkget6.com 127.0.0.1 blm.bz 127.0.0.1 blockbuster.112.2o7.net 127.0.0.1 blockbustercom.112.2o7.net +127.0.0.1 blockedf9ojq.voluumtrk.com 127.0.0.1 blocks.ginotrack.com 127.0.0.1 blog-hits.com 127.0.0.1 blog.adspaces.ero-advertising.com @@ -21104,6 +21661,7 @@ 127.0.0.1 blueadvertise.com 127.0.0.1 bluecava.com 127.0.0.1 bluechillies.us.intellitxt.com +127.0.0.1 blueconic.net 127.0.0.1 blueflameelite.go2cloud.org 127.0.0.1 bluehavenmedia.com 127.0.0.1 blueheart.org @@ -21117,14 +21675,20 @@ 127.0.0.1 blutrumpet.com 127.0.0.1 bm.annonce.cz 127.0.0.1 bm.hbtronix.de +127.0.0.1 bm7dc.voluumtrk.com +127.0.0.1 bmcp5.voluumtrk.com 127.0.0.1 bmetrack.com +127.0.0.1 bmg8u.voluumtrk.com 127.0.0.1 bmi.go2cloud.org 127.0.0.1 bmmetrix.com +127.0.0.1 bmobl.voluumtrk.com 127.0.0.1 bmp.rentboy.com 127.0.0.1 bms.zeptolab.com 127.0.0.1 bn.gewinn24.de 127.0.0.1 bn.xp1.ru4.com 127.0.0.1 bnbaz.eb2a.com +127.0.0.1 bncrf.voluumtrk.com +127.0.0.1 bng3w.voluumtrk.com 127.0.0.1 bnkr8dev.112.2o7.net 127.0.0.1 bnr.sys.lv 127.0.0.1 bnw.xp1.ru4.com @@ -21156,7 +21720,10 @@ 127.0.0.1 bobgw02.con.local.vmsn.de 127.0.0.1 bobgwtmp.vmsn.de 127.0.0.1 bobtv.ivwbox.de +127.0.0.1 bodybuilding-com.112.2o7.net 127.0.0.1 boerseon.ivwbox.de +127.0.0.1 bofa.demdex.net +127.0.0.1 boh00.voluumtrk.com 127.0.0.1 boingdragon.com 127.0.0.1 bolch02.webtrekk.net 127.0.0.1 boldcenter.com @@ -21164,11 +21731,16 @@ 127.0.0.1 bolde02.webtrekk.net 127.0.0.1 bolt.us.intellitxt.com 127.0.0.1 bomek.com +127.0.0.1 bon7g.voluumtrk.com +127.0.0.1 bongacams.com 127.0.0.1 bongacash.com 127.0.0.1 bonnie-schrecengost.us 127.0.0.1 book-mark.net +127.0.0.1 bookcorps.com 127.0.0.1 boom.ro +127.0.0.1 boom.udimg.com 127.0.0.1 boomerang.com.au +127.0.0.1 boostads.net 127.0.0.1 bootstraps.timesink.com 127.0.0.1 bos-tapreq01.jumptap.com 127.0.0.1 bos-tapreq02.jumptap.com @@ -21221,7 +21793,9 @@ 127.0.0.1 boxesists.com 127.0.0.1 boxofficeprophets.us.intellitxt.com 127.0.0.1 bp.ads.link4ads.com +127.0.0.1 bp5xs.voluumtrk.com 127.0.0.1 bpath.com +127.0.0.1 bpflx.voluumtrk.com 127.0.0.1 bpmsza.com 127.0.0.1 bptracking.com 127.0.0.1 br.blackfling.com @@ -21231,7 +21805,9 @@ 127.0.0.1 br.vghd.com 127.0.0.1 brain.cubi.me 127.0.0.1 braincash.com +127.0.0.1 brainient.com 127.0.0.1 brainteasers.freestats.com +127.0.0.1 branche.science 127.0.0.1 brandedoffersaff.go2cloud.org 127.0.0.1 brandi-haskins.us 127.0.0.1 brandmovers.net @@ -22547,14 +23123,19 @@ 127.0.0.1 bs.serving-sys.com.78503.9655.302br.net 127.0.0.1 bs.serving-sys.com.78505.9655.302br.net 127.0.0.1 bs.yandex.ru +127.0.0.1 bsaig.voluumtrk.com 127.0.0.1 bsch.serving-sys.com +127.0.0.1 bshha.voluumtrk.com 127.0.0.1 bsitm3.com 127.0.0.1 bsrv.adohana.com 127.0.0.1 bstracker.blogspirit.net 127.0.0.1 bt.rcs.it 127.0.0.1 bt.tt.omtrdc.net 127.0.0.1 btdirectnav.com +127.0.0.1 bte5z.voluumtrk.com 127.0.0.1 btg.mtvnservices.com +127.0.0.1 btnativedirect.com +127.0.0.1 btnativenav.com 127.0.0.1 btprmnav.com 127.0.0.1 btr.domywife.com 127.0.0.1 btr.sharethis.com @@ -22563,20 +23144,27 @@ 127.0.0.1 btsportdotcom.2cnt.net 127.0.0.1 btsportios.2cnt.net 127.0.0.1 btstats.devtribu.fr +127.0.0.1 bttrack.com 127.0.0.1 btttag.com 127.0.0.1 btw.ero-advertising.com +127.0.0.1 btwwu.voluumtrk.com +127.0.0.1 btz9j.voluumtrk.com 127.0.0.1 bucataras.2cnt.net 127.0.0.1 buchch02.webtrekk.net 127.0.0.1 buchde02.webtrekk.net 127.0.0.1 bucketsofbanners.com 127.0.0.1 bucksense.go2cloud.org +127.0.0.1 budgetedbauer.com 127.0.0.1 budsinc.com +127.0.0.1 bugsense.appspot.com 127.0.0.1 bugsense.com 127.0.0.1 bugstest.adition.com 127.0.0.1 builder.com 127.0.0.1 builder.extensionfactory.com 127.0.0.1 buildtraffic.com 127.0.0.1 buildtrafficx.com +127.0.0.1 builfico.de +127.0.0.1 bujrc.voluumtrk.com 127.0.0.1 buket7.justclick.ru 127.0.0.1 buldog-stats.com 127.0.0.1 bullseye-media.net @@ -22601,6 +23189,7 @@ 127.0.0.1 button.clickability.com 127.0.0.1 buxflow.com 127.0.0.1 buy.com +127.0.0.1 buy.thetrackr.com 127.0.0.1 buy2.go2cloud.org 127.0.0.1 buyhitscheap.com 127.0.0.1 buysellad.eu @@ -22608,17 +23197,23 @@ 127.0.0.1 buysub.com 127.0.0.1 buytraf.ru 127.0.0.1 buzz.igg.com +127.0.0.1 buzzbox.buzzfeed.com 127.0.0.1 buzzfeed.d1.sc.omtrdc.net 127.0.0.1 buzzreferrals.go2cloud.org 127.0.0.1 bvalit.justclick.ru +127.0.0.1 bvoau.voluumtrk.com 127.0.0.1 bvsrv.adk2x.com +127.0.0.1 bwhcj.voluumtrk.com 127.0.0.1 bwin90.com 127.0.0.1 bwp.download.com 127.0.0.1 bwp.theinsider.com.com 127.0.0.1 bx.clickmedia.ro 127.0.0.1 bxsql.clickmedia.ro +127.0.0.1 bxtop.voluumtrk.com 127.0.0.1 by.advertising.com 127.0.0.1 bytecenter.com +127.0.0.1 byxng.voluumtrk.com +127.0.0.1 bzpkv.voluumtrk.com 127.0.0.1 c-col.com 127.0.0.1 c-evt.moatads.com 127.0.0.1 c-on-text.com @@ -22633,6 +23228,7 @@ 127.0.0.1 c.adroll.com 127.0.0.1 c.ads.com 127.0.0.1 c.afftrx.com +127.0.0.1 c.algovid.com 127.0.0.1 c.allegrostatic.pl 127.0.0.1 c.am11.ru 127.0.0.1 c.am15.net @@ -22648,9 +23244,11 @@ 127.0.0.1 c.casalemedia.com 127.0.0.1 c.chango.com 127.0.0.1 c.chartboost.com +127.0.0.1 c.cldlr.com 127.0.0.1 c.cnzz.com 127.0.0.1 c.cocacola.co.jp 127.0.0.1 c.commtimize.com +127.0.0.1 c.compete.com 127.0.0.1 c.computerbild.de 127.0.0.1 c.coneco.net 127.0.0.1 c.coolshader.com @@ -22671,11 +23269,15 @@ 127.0.0.1 c.live.com 127.0.0.1 c.medialytics.com 127.0.0.1 c.microsoft.com +127.0.0.1 c.mobhubrdrms.com 127.0.0.1 c.mobpartner.mobi 127.0.0.1 c.moreover.com 127.0.0.1 c.mscimg.com 127.0.0.1 c.msn.com 127.0.0.1 c.msnbc.com +127.0.0.1 c.msparktrk.com +127.0.0.1 c.munmedtrflow.com +127.0.0.1 c.netu.tv 127.0.0.1 c.newsinc.com 127.0.0.1 c.ns.inbox.lv 127.0.0.1 c.orange.fr @@ -22690,10 +23292,13 @@ 127.0.0.1 c.sgbfjs.info 127.0.0.1 c.smartclick.net 127.0.0.1 c.spiegel.de +127.0.0.1 c.statcounter.com 127.0.0.1 c.t4ft.de 127.0.0.1 c.teromil.com 127.0.0.1 c.thanksearch.com 127.0.0.1 c.tracking.gamigoads.com +127.0.0.1 c.trffrcmrd.com +127.0.0.1 c.viewsecure.net 127.0.0.1 c.vindicosuite.com 127.0.0.1 c.vrvm.com 127.0.0.1 c.vserv.mobi @@ -22702,10 +23307,12 @@ 127.0.0.1 c.woopic.com 127.0.0.1 c.wrating.com 127.0.0.1 c.x.oanda.com +127.0.0.1 c.xbox.com 127.0.0.1 c.zeroredirect1.com 127.0.0.1 c01.brickoffers.com 127.0.0.1 c02.smaato.net 127.0.0.1 c02qds.2cnt.net +127.0.0.1 c0u8w.voluumtrk.com 127.0.0.1 c1.adform.net 127.0.0.1 c1.amazingcounters.com 127.0.0.1 c1.gostats.com @@ -22717,6 +23324,7 @@ 127.0.0.1 c1.gostats.vn 127.0.0.1 c1.microsoft.com 127.0.0.1 c1.nowlinux.com +127.0.0.1 c1.onedmp.com 127.0.0.1 c1.outster.com 127.0.0.1 c1.popads.net 127.0.0.1 c1.rfihub.net @@ -22736,6 +23344,7 @@ 127.0.0.1 c16.statcounter.com 127.0.0.1 c1605.ic-live.com 127.0.0.1 c17.statcounter.com +127.0.0.1 c18.statcounter.com 127.0.0.1 c2.amazingcounters.com 127.0.0.1 c2.campartner.com 127.0.0.1 c2.clickprotects.com @@ -22758,6 +23367,7 @@ 127.0.0.1 c2mtrack01.track.c2m00b.net 127.0.0.1 c2path.com 127.0.0.1 c2s-openrtb.liverail.com +127.0.0.1 c2x4s.voluumtrk.com 127.0.0.1 c3.amazingcounters.com 127.0.0.1 c3.gostats.com 127.0.0.1 c3.gostats.de @@ -22770,6 +23380,7 @@ 127.0.0.1 c3.xxxcounter.com 127.0.0.1 c3.zedo.com 127.0.0.1 c32.statcounter.com +127.0.0.1 c3ad7.voluumtrk.com 127.0.0.1 c3onlinemarketing.com 127.0.0.1 c4.amazingcounters.com 127.0.0.1 c4.gostats.com @@ -22786,8 +23397,10 @@ 127.0.0.1 c479842.r42.cf2.rackcdn.com 127.0.0.1 c4android.2cnt.net 127.0.0.1 c4androidtest.2cnt.net +127.0.0.1 c4dk1.voluumtrk.com 127.0.0.1 c4dotcom.2cnt.net 127.0.0.1 c4dotcomtest.2cnt.net +127.0.0.1 c4fbb.voluumtrk.com 127.0.0.1 c4ios.2cnt.net 127.0.0.1 c4iostest.2cnt.net 127.0.0.1 c4tracking01.com @@ -22809,6 +23422,7 @@ 127.0.0.1 c5dotcomtest.2cnt.net 127.0.0.1 c5ios.2cnt.net 127.0.0.1 c5iostest.2cnt.net +127.0.0.1 c5vup.voluumtrk.com 127.0.0.1 c6.amazingcounters.com 127.0.0.1 c6.statcounter.com 127.0.0.1 c6.zedo.com @@ -22816,14 +23430,17 @@ 127.0.0.1 c7.amazingcounters.com 127.0.0.1 c7.statcounter.com 127.0.0.1 c7.zedo.com +127.0.0.1 c74vr.voluumtrk.com 127.0.0.1 c753738.r38.cf2.rackcdn.com 127.0.0.1 c8.amazingcounters.com 127.0.0.1 c8.net.ua 127.0.0.1 c8.statcounter.com 127.0.0.1 c8.zedo.com 127.0.0.1 c829aeaf4090c1289783-9ad4110c8011547ec25e241b917b5aab.r35.cf2.rackcdn.com +127.0.0.1 c8j4u.voluumtrk.com 127.0.0.1 c9.amazingcounters.com 127.0.0.1 c97a-b910-eb5b-b0b8.reporo.net +127.0.0.1 c9k4p.voluumtrk.com 127.0.0.1 ca.2.cqcounter.com 127.0.0.1 ca.clickinc.com 127.0.0.1 ca.cqcounter.com @@ -22872,9 +23489,12 @@ 127.0.0.1 calendar.kissmetrics.com 127.0.0.1 calendar.performancerevenues.com 127.0.0.1 calendar.zedo.com +127.0.0.1 calendrier.science 127.0.0.1 call-tracking.co.uk 127.0.0.1 callfire.com 127.0.0.1 callloop.com +127.0.0.1 callmd5map.com +127.0.0.1 calme.science 127.0.0.1 cameraboys.com 127.0.0.1 cameron-wolter.us 127.0.0.1 camille-drennen.us @@ -22887,6 +23507,7 @@ 127.0.0.1 campaigns.rioseo.com 127.0.0.1 campaigns.services.mozilla.com 127.0.0.1 campaigntracking01.com +127.0.0.1 campf.voluumtrk.com 127.0.0.1 camsiteonline.wiredcircular.com 127.0.0.1 camz.tintel.nl 127.0.0.1 can.capacitygrid.com @@ -22896,11 +23517,13 @@ 127.0.0.1 canvas-advert.ru 127.0.0.1 canvas-ping.conduit-data.com 127.0.0.1 canvas-usage-v2.conduit-data.com +127.0.0.1 canvaspl-a.akamaihd.net 127.0.0.1 canwest.112.207.net 127.0.0.1 canwest.112.2o7.net 127.0.0.1 canwestdose.112.2o7.net 127.0.0.1 cap1ss.xp1.ru4.com 127.0.0.1 caprewards.go2cloud.org +127.0.0.1 capture.trackjs.com 127.0.0.1 carbonads.com 127.0.0.1 careers.112.2o7.net 127.0.0.1 caren-jerry.us @@ -22917,6 +23540,7 @@ 127.0.0.1 caroline-beckman.us 127.0.0.1 carolyn-deckard.us 127.0.0.1 cars.ign.us.intellitxt.com +127.0.0.1 carte.science 127.0.0.1 cartonetwork.com 127.0.0.1 cartoonnrtwork.com 127.0.0.1 cas.criteo.com @@ -22950,6 +23574,7 @@ 127.0.0.1 cashmylinks.com 127.0.0.1 cashpartner.com 127.0.0.1 cashpartner.net +127.0.0.1 cashprom.ru 127.0.0.1 cashspace.com 127.0.0.1 cashtrafic.com 127.0.0.1 cashuniversity.go2cloud.org @@ -22959,12 +23584,14 @@ 127.0.0.1 casinorewards.com 127.0.0.1 casinotraffic.com 127.0.0.1 casinotreasure.com +127.0.0.1 casof.voluumtrk.com 127.0.0.1 cassandra-klima.us 127.0.0.1 cast.ra.icast.cn 127.0.0.1 cat.clx.ru 127.0.0.1 cat.fr.eu.criteo.com 127.0.0.1 cata.ero-advertising.com 127.0.0.1 catalina-lafond.us +127.0.0.1 catalyse.science 127.0.0.1 catharon.com 127.0.0.1 cathleen-rooker.us 127.0.0.1 cathy-adams.us @@ -22987,6 +23614,7 @@ 127.0.0.1 cbmsn.112.2o7.net 127.0.0.1 cbn.tbn.ru 127.0.0.1 cbs.wondershare.com +127.0.0.1 cbsi.demdex.net 127.0.0.1 cbsmarket.com 127.0.0.1 cbsncaasports.112.2o7.net 127.0.0.1 cbsnfl.112.2o7.net @@ -22997,10 +23625,14 @@ 127.0.0.1 cc.amazingcounters.com 127.0.0.1 cc.connextra.com 127.0.0.1 cc.zeit.de +127.0.0.1 cc03-41cf-c2ea-3a84.reporo.net 127.0.0.1 cc54-ed21-fc7f-e519.reporo.net 127.0.0.1 ccamholdings.go2cloud.org 127.0.0.1 ccas.clearchannel.com 127.0.0.1 ccc01.opinionlab.com +127.0.0.1 cclfx.voluumtrk.com +127.0.0.1 cclrn.voluumtrk.com +127.0.0.1 ccn2n.voluumtrk.com 127.0.0.1 ccran.com 127.0.0.1 ccs.infospace.com 127.0.0.1 ccstatic.highwebmedia.com @@ -23008,19 +23640,22 @@ 127.0.0.1 cd-ladsp-com.s3.amazonaws.com 127.0.0.1 cdate.122.2o7.net 127.0.0.1 cdmagurus.us.intellitxt.com +127.0.0.1 cdn-0.pics.dvdcdn.com +127.0.0.1 cdn-akamai.mookie1.com 127.0.0.1 cdn-ci34.actonsoftware.com 127.0.0.1 cdn-i.dmdentertainment.com 127.0.0.1 cdn-static.liverail.com 127.0.0.1 cdn-tags.mmondi.com 127.0.0.1 cdn-www.madblast.com +127.0.0.1 cdn-wx.rainbowtgx.com 127.0.0.1 cdn.2app.lk 127.0.0.1 cdn.2xbpub.com 127.0.0.1 cdn.ad-maven.com 127.0.0.1 cdn.ad.maist.jp 127.0.0.1 cdn.adbrau.com 127.0.0.1 cdn.addelive.com +127.0.0.1 cdn.addnow.com 127.0.0.1 cdn.adengine.org -127.0.0.1 cdn.adf.ly 127.0.0.1 cdn.adfoc.us 127.0.0.1 cdn.adikteev.com 127.0.0.1 cdn.adk2.com @@ -23028,6 +23663,7 @@ 127.0.0.1 cdn.adnotch.com 127.0.0.1 cdn.adnxs.com 127.0.0.1 cdn.adnxs.com.44946.9340.302br.net +127.0.0.1 cdn.adonads.com 127.0.0.1 cdn.adorikacontentportal.com 127.0.0.1 cdn.adplxmd.com 127.0.0.1 cdn.adrolays.de @@ -23037,14 +23673,17 @@ 127.0.0.1 cdn.adspirit.de 127.0.0.1 cdn.adsrv247.com 127.0.0.1 cdn.adsrvmedia.net +127.0.0.1 cdn.adultforce.com 127.0.0.1 cdn.advanseads.com 127.0.0.1 cdn.advertig.com 127.0.0.1 cdn.adworkmedia.com 127.0.0.1 cdn.ajillionmax.com 127.0.0.1 cdn.allegedmedia.com 127.0.0.1 cdn.ap.bittorrent.com +127.0.0.1 cdn.applebarq.com 127.0.0.1 cdn.applifier.com 127.0.0.1 cdn.apptv.com +127.0.0.1 cdn.asooda.com 127.0.0.1 cdn.assets.craveonline.com 127.0.0.1 cdn.assets.gorillanation.com 127.0.0.1 cdn.at.atwola.com @@ -23057,32 +23696,46 @@ 127.0.0.1 cdn.boomtrain.com 127.0.0.1 cdn.brcdn.com 127.0.0.1 cdn.broadstreetads.com +127.0.0.1 cdn.bttrack.com 127.0.0.1 cdn.buysellads.com 127.0.0.1 cdn.c.try9.com 127.0.0.1 cdn.callrail.com +127.0.0.1 cdn.capturly.com 127.0.0.1 cdn.castplatform.com 127.0.0.1 cdn.cdnco.us 127.0.0.1 cdn.cdncomputer.com +127.0.0.1 cdn.cdntraffic.com 127.0.0.1 cdn.celtra.com +127.0.0.1 cdn.chapewa.net 127.0.0.1 cdn.chargeplatform.com 127.0.0.1 cdn.chuknu.sokrati.com 127.0.0.1 cdn.clickfuse.com 127.0.0.1 cdn.clicktale.net +127.0.0.1 cdn.cms.neatcontent.com 127.0.0.1 cdn.complexmedianetwork.com +127.0.0.1 cdn.connatix.com 127.0.0.1 cdn.contentadserver.com +127.0.0.1 cdn.cpiera.com 127.0.0.1 cdn.cpmstar.com 127.0.0.1 cdn.cpnscdn.com +127.0.0.1 cdn.cpsoft86.com 127.0.0.1 cdn.creative.medialytics.com +127.0.0.1 cdn.cxense.com +127.0.0.1 cdn.derlatas.com +127.0.0.1 cdn.devolapgt.com 127.0.0.1 cdn.digitalcity.com 127.0.0.1 cdn.directrev.com +127.0.0.1 cdn.directtrk.com 127.0.0.1 cdn.dirjournal.com 127.0.0.1 cdn.doubleverify.com 127.0.0.1 cdn.downloaddabs.com 127.0.0.1 cdn.dsultra.com 127.0.0.1 cdn.dynamicyield.com +127.0.0.1 cdn.ebocornac.com 127.0.0.1 cdn.effectivemeasure.net 127.0.0.1 cdn.elasticad.net 127.0.0.1 cdn.engine.4dsply.com +127.0.0.1 cdn.engine.adsupply.com 127.0.0.1 cdn.engine.avid.doublepimp.com 127.0.0.1 cdn.engine.bang.doublepimp.com 127.0.0.1 cdn.engine.doublepimp.com @@ -23094,9 +23747,13 @@ 127.0.0.1 cdn.engine.trklnks.com 127.0.0.1 cdn.epom.com 127.0.0.1 cdn.erendri.com +127.0.0.1 cdn.errantolo.com 127.0.0.1 cdn.escalatenetwork.com +127.0.0.1 cdn.eveitoki.com 127.0.0.1 cdn.exactag.com 127.0.0.1 cdn.exogripper.com +127.0.0.1 cdn.exoticads.com +127.0.0.1 cdn.exovueplatform.com 127.0.0.1 cdn.extensions.buzznet.com 127.0.0.1 cdn.eyewonder.com 127.0.0.1 cdn.eyewonder.com.12812.9047.302br.net @@ -23130,45 +23787,61 @@ 127.0.0.1 cdn.eyewonder.com.70431.9538.302br.net 127.0.0.1 cdn.eyewonder.com.70432.9538.302br.net 127.0.0.1 cdn.eyewonder.com.70433.9538.302br.net +127.0.0.1 cdn.fancybar.net 127.0.0.1 cdn.fastclick.net 127.0.0.1 cdn.fastclick.net.956.9006.302br.net 127.0.0.1 cdn.fastclick.net.958.9006.302br.net 127.0.0.1 cdn.feeds.videosz.com +127.0.0.1 cdn.firstimpression.io 127.0.0.1 cdn.flashtalking.com +127.0.0.1 cdn.flurry.com +127.0.0.1 cdn.frestacero.com 127.0.0.1 cdn.ftdownloader.net 127.0.0.1 cdn.funnel.naturaltracking.com 127.0.0.1 cdn.gameadvert.com 127.0.0.1 cdn.hadj7.adjuggler.net +127.0.0.1 cdn.hanaprop.com 127.0.0.1 cdn.herezera.com +127.0.0.1 cdn.hgdat.com 127.0.0.1 cdn.hiido.cn 127.0.0.1 cdn.hyperpromote.com 127.0.0.1 cdn.iasrv.com 127.0.0.1 cdn.imrworldwide.com +127.0.0.1 cdn.inentasky.com 127.0.0.1 cdn.innity.com 127.0.0.1 cdn.insights.gravity.com 127.0.0.1 cdn.inskinmedia.com 127.0.0.1 cdn.inspectlet.com 127.0.0.1 cdn.interactivemedia.net +127.0.0.1 cdn.intergi.com 127.0.0.1 cdn.justonecookbook.com 127.0.0.1 cdn.kikuzip.com +127.0.0.1 cdn.kixer.com 127.0.0.1 cdn.krxd.net 127.0.0.1 cdn.lfstmedia.com +127.0.0.1 cdn.listrakbi.com 127.0.0.1 cdn.livechatinc.com 127.0.0.1 cdn.liverail.com +127.0.0.1 cdn.mb.datingadzone.com 127.0.0.1 cdn.mbp91.com +127.0.0.1 cdn.mcextjmp.com 127.0.0.1 cdn.mdotm.com 127.0.0.1 cdn.media.net 127.0.0.1 cdn.millennialmedia.com 127.0.0.1 cdn.mobicow.com 127.0.0.1 cdn.mobidea.com 127.0.0.1 cdn.mouseflow.com +127.0.0.1 cdn.mplxtms.com +127.0.0.1 cdn.mxpnl.com 127.0.0.1 cdn.nativendo.de 127.0.0.1 cdn.nearbyad.com +127.0.0.1 cdn.neblotech.com 127.0.0.1 cdn.niche.videosz.com +127.0.0.1 cdn.o333o.com 127.0.0.1 cdn.onescreen.net 127.0.0.1 cdn.onlinecountdowns.com 127.0.0.1 cdn.optimatic.com -127.0.0.1 cdn.optimizely.com +127.0.0.1 cdn.orarala.com 127.0.0.1 cdn.pardot.com 127.0.0.1 cdn.petametrics.com 127.0.0.1 cdn.popcash.net @@ -23176,31 +23849,50 @@ 127.0.0.1 cdn.psdvault.com 127.0.0.1 cdn.puatraffic.com 127.0.0.1 cdn.pubexchange.com +127.0.0.1 cdn.putono5.com 127.0.0.1 cdn.qservz.com +127.0.0.1 cdn.ramctrlgate.com +127.0.0.1 cdn.reporo.net +127.0.0.1 cdn.revcontent.com +127.0.0.1 cdn.ripbwing.com 127.0.0.1 cdn.rooktemplate.com 127.0.0.1 cdn.royale.spongecell.com.18367.9113.302br.net 127.0.0.1 cdn.royale.spongecell.com.18473.9113.302br.net 127.0.0.1 cdn.sailthru.com 127.0.0.1 cdn.satgreera.com +127.0.0.1 cdn.scootloor.com 127.0.0.1 cdn.search.aol.com +127.0.0.1 cdn.segment.com +127.0.0.1 cdn.segment.io 127.0.0.1 cdn.shorte.st 127.0.0.1 cdn.siftscience.com +127.0.0.1 cdn.slendastic.com +127.0.0.1 cdn.slopeaota.com 127.0.0.1 cdn.snapsitemap.com +127.0.0.1 cdn.socialtwist.com 127.0.0.1 cdn.spotxchange.com +127.0.0.1 cdn.spoutable.com 127.0.0.1 cdn.startpagea.com +127.0.0.1 cdn.static.zdbb.net 127.0.0.1 cdn.staticstor.com +127.0.0.1 cdn.stdmbl.com 127.0.0.1 cdn.stroeerdigitalmedia.de 127.0.0.1 cdn.superloofy.com +127.0.0.1 cdn.suthome.com 127.0.0.1 cdn.taboola.com 127.0.0.1 cdn.taboolasyndication.com 127.0.0.1 cdn.tanx.com +127.0.0.1 cdn.teads.tv 127.0.0.1 cdn.technoratimedia.com 127.0.0.1 cdn.thetorntv.com 127.0.0.1 cdn.theturboforums.com 127.0.0.1 cdn.tinypass.com +127.0.0.1 cdn.tomekas.com 127.0.0.1 cdn.topsy.com 127.0.0.1 cdn.trafficexchangelist.com 127.0.0.1 cdn.trafficforce.com +127.0.0.1 cdn.trafficposse.com +127.0.0.1 cdn.trafficstars.com 127.0.0.1 cdn.triggertag.gorillanation.com 127.0.0.1 cdn.trkclk.net 127.0.0.1 cdn.turn.com @@ -23209,6 +23901,7 @@ 127.0.0.1 cdn.undertone.com 127.0.0.1 cdn.urbanmusichq.se 127.0.0.1 cdn.us.goldspotmedia.com +127.0.0.1 cdn.utokapa.com 127.0.0.1 cdn.vaguntrader.com 127.0.0.1 cdn.valueclick.net 127.0.0.1 cdn.vdopia.com @@ -23221,16 +23914,24 @@ 127.0.0.1 cdn.voscast.com 127.0.0.1 cdn.w55c.net 127.0.0.1 cdn.widgets.webengage.com +127.0.0.1 cdn.wigetmedia.com 127.0.0.1 cdn.wonderfulengineering.com +127.0.0.1 cdn.www1.oratosaeron.com 127.0.0.1 cdn.xdirectx.com 127.0.0.1 cdn.xeontopa.com 127.0.0.1 cdn.yb0t.com +127.0.0.1 cdn.yldbt.com +127.0.0.1 cdn.yourtopoffers.info +127.0.0.1 cdn.ywxi.net 127.0.0.1 cdn.zerezas.com 127.0.0.1 cdn.zeusclicks.com +127.0.0.1 cdn.ziffstatic.com +127.0.0.1 cdn.zipropyl.com 127.0.0.1 cdn.zopim.com 127.0.0.1 cdn0.mobmore.com 127.0.0.1 cdn0.puata.info 127.0.0.1 cdn1-ref.landing.comcontent.net +127.0.0.1 cdn1.ad-center.com 127.0.0.1 cdn1.ads.contentabc.com 127.0.0.1 cdn1.appia.com 127.0.0.1 cdn1.bad-credit-cdn.com @@ -23249,19 +23950,24 @@ 127.0.0.1 cdn1.rhtag.com 127.0.0.1 cdn1.skinected.com 127.0.0.1 cdn1.smartadserver.com +127.0.0.1 cdn1.srv.revdepo.com +127.0.0.1 cdn1.tags1.revdepo.com 127.0.0.1 cdn1.traffichaus.com 127.0.0.1 cdn1.tribalfusion.com 127.0.0.1 cdn10-ref.landing.comcontent.net 127.0.0.1 cdn11-ref.landing.comcontent.net +127.0.0.1 cdn11.contentabc.com 127.0.0.1 cdn12-ref.landing.comcontent.net 127.0.0.1 cdn13-ref.landing.comcontent.net 127.0.0.1 cdn14-ref.landing.comcontent.net 127.0.0.1 cdn15-ref.landing.comcontent.net +127.0.0.1 cdn15.acloudimages.com 127.0.0.1 cdn16-ref.landing.comcontent.net 127.0.0.1 cdn17-ref.landing.comcontent.net 127.0.0.1 cdn18-ref.landing.comcontent.net 127.0.0.1 cdn19-ref.landing.comcontent.net 127.0.0.1 cdn1b.adspace.porntube.com +127.0.0.1 cdn1ht.traffichaus.com 127.0.0.1 cdn2-ref.landing.comcontent.net 127.0.0.1 cdn2.ad-center.com 127.0.0.1 cdn2.adbrau.com @@ -23271,7 +23977,9 @@ 127.0.0.1 cdn2.cpmstar.com 127.0.0.1 cdn2.crispadvertising.com 127.0.0.1 cdn2.deals-discounts-codes.com +127.0.0.1 cdn2.lockerdome.com 127.0.0.1 cdn2.maxiget.com +127.0.0.1 cdn2.mobile.contentdef.com 127.0.0.1 cdn2.optimizely.com 127.0.0.1 cdn2.reporo.net 127.0.0.1 cdn2.urbanmusichq.se @@ -23304,14 +24012,18 @@ 127.0.0.1 cdn8-ref.landing.comcontent.net 127.0.0.1 cdn9-ref.landing.comcontent.net 127.0.0.1 cdna.tremormedia.com +127.0.0.1 cdnads.cam4.com 127.0.0.1 cdncache2-a.akamaihd.net 127.0.0.1 cdneu.alcoholsoftcdn.com 127.0.0.1 cdneu.filefactscdn.com 127.0.0.1 cdnk.interclick.com +127.0.0.1 cdnke.voluumtrk.com 127.0.0.1 cdns.brsrvr.com +127.0.0.1 cdnstats-a.akamaihd.net 127.0.0.1 cdnus.alcoholsoftcdn.com 127.0.0.1 cdnus.filefactscdn.com 127.0.0.1 cdnx.tribalfusion.com +127.0.0.1 cdr0b.voluumtrk.com 127.0.0.1 cds.g8j8b9g6.hwcdn.net 127.0.0.1 cds2.freenet.de 127.0.0.1 cdtpiwik.us @@ -23319,6 +24031,8 @@ 127.0.0.1 ce.lijit.com 127.0.0.1 ce2-dev-trk.s3.amazonaws.com 127.0.0.1 ce2-dev.s3.amazonaws.com +127.0.0.1 ce5iz.voluumtrk.com +127.0.0.1 cebj8.voluumtrk.com 127.0.0.1 cecash.com 127.0.0.1 cecilia-boatwright.us 127.0.0.1 ced.sascdn.com @@ -23329,9 +24043,11 @@ 127.0.0.1 cedexiscom.btttag.com 127.0.0.1 cedexispub.cdnetworks.net 127.0.0.1 celeb-ads.com +127.0.0.1 celebrities.searchwho.com 127.0.0.1 celebrity-image.com 127.0.0.1 celebrityhack.us.intellitxt.com 127.0.0.1 celebscentral.us.intellitxt.com +127.0.0.1 celerite.science 127.0.0.1 celeste-rehkop.us 127.0.0.1 celia-chance.us 127.0.0.1 celia-shrader.us @@ -23353,8 +24069,15 @@ 127.0.0.1 cf.ads.kontextua.com 127.0.0.1 cf.cdn.inmobi.com 127.0.0.1 cf.ipav.xyz +127.0.0.1 cf.shareasimage.com +127.0.0.1 cf33b.voluumtrk.com 127.0.0.1 cfcdn.fhserve.com +127.0.0.1 cfpbg.voluumtrk.com 127.0.0.1 cfrfa.112.2o7.net +127.0.0.1 cfs.u-ad.info +127.0.0.1 cfyav.voluumtrk.com +127.0.0.1 cfzp1.voluumtrk.com +127.0.0.1 cg27e.voluumtrk.com 127.0.0.1 cgi.hotstat.nl 127.0.0.1 cgi.netscape.com 127.0.0.1 cgi.zdnet.com @@ -23362,30 +24085,39 @@ 127.0.0.1 cgicounter.oneandone.co.uk 127.0.0.1 cgicounter.onlinehome.de 127.0.0.1 cgicounter.puretec.de +127.0.0.1 cgovj.voluumtrk.com 127.0.0.1 ch.questionmarket.com +127.0.0.1 ch2lb.checkm8.com +127.0.0.1 chaine.science 127.0.0.1 chanalytics.merchantadvantage.com +127.0.0.1 changement.science 127.0.0.1 chango.com 127.0.0.1 channel4.2cnt.net 127.0.0.1 channel4.com.112.2o7.net 127.0.0.1 channel5.2cnt.net +127.0.0.1 channeltraffic.net 127.0.0.1 chantel-duquette.us 127.0.0.1 chantel-jauregui.us 127.0.0.1 chantelle-hedden.us 127.0.0.1 chapdyn.bidsystem.com +127.0.0.1 chapewa.net 127.0.0.1 chart.superstats.com 127.0.0.1 chartaca.com.s3.amazonaws.com 127.0.0.1 chartbeat.download.akamai.com 127.0.0.1 chartboost.com 127.0.0.1 chasity-oceguera.us +127.0.0.1 chasser.science 127.0.0.1 chastity-stoltenberg.us 127.0.0.1 chat.adspaces.ero-advertising.com 127.0.0.1 chat.ero-advertising.com 127.0.0.1 chat.zedo.com 127.0.0.1 chathu.apkmania.co 127.0.0.1 chatserver.comm100.cn +127.0.0.1 chaud.science 127.0.0.1 cheaperia.digidip.net 127.0.0.1 cheatcc.us.intellitxt.com 127.0.0.1 cheatingdome.us.intellitxt.com +127.0.0.1 check.tamobi.net 127.0.0.1 checker.admitad.com 127.0.0.1 checker.cityads.com 127.0.0.1 checker.cityads.com.br @@ -23409,6 +24141,9 @@ 127.0.0.1 cherryaffiliates.omarsys.com 127.0.0.1 cheryl-edelstein.us 127.0.0.1 chestionar.ro +127.0.0.1 chgqm.voluumtrk.com +127.0.0.1 chi-only.outbrain.com +127.0.0.1 chi2only.outbrain.com 127.0.0.1 chillout.real.com 127.0.0.1 china.inmobi.com 127.0.0.1 chinaads.net @@ -23419,12 +24154,14 @@ 127.0.0.1 chip.met.vgwort.de 127.0.0.1 chip.pl.intellitxt.com 127.0.0.1 choice.demdex.net +127.0.0.1 choice.microsoft.com 127.0.0.1 choices.truste.com 127.0.0.1 christie-hanlon.us 127.0.0.1 christie-pearlman.us 127.0.0.1 christin-alejandro.us 127.0.0.1 christine-coffin.us 127.0.0.1 christine-winfrey.us +127.0.0.1 chromatographie.science 127.0.0.1 chronicads.com 127.0.0.1 chtatic.appspot.com 127.0.0.1 ci.categoria.hpg.com.br @@ -23446,12 +24183,14 @@ 127.0.0.1 ciao.ivwbox.de 127.0.0.1 ciaoshopde.122.2o7.net 127.0.0.1 cidemo.actonsoftware.com +127.0.0.1 ciktv.voluumtrk.com 127.0.0.1 cimedia.com 127.0.0.1 cinema.ivwbox.de 127.0.0.1 cinestar.ivwbox.de 127.0.0.1 cinthya-hanks.us 127.0.0.1 cinthya-sipe.us 127.0.0.1 cipg.actonsoftware.com +127.0.0.1 circonstance.science 127.0.0.1 ciscosystemsinc.tt.omtrdc.net 127.0.0.1 citi.bridgetrack.com 127.0.0.1 citi.bridgetrack.com.21936.9128.302br.net @@ -23515,6 +24254,8 @@ 127.0.0.1 ck.jp.ap.valuecommerce.com 127.0.0.1 ck.juicyads.com 127.0.0.1 ck.solocpm.com +127.0.0.1 cke1i.voluumtrk.com +127.0.0.1 cklu4.voluumtrk.com 127.0.0.1 ckm-m.xp1.ru4.com 127.0.0.1 cksocial.hitpointstudios.com 127.0.0.1 ckstatic.com @@ -23549,6 +24290,7 @@ 127.0.0.1 clearspring.com 127.0.0.1 clearwebstats.com 127.0.0.1 cleco.jag.vmsn.de +127.0.0.1 clepsydre.science 127.0.0.1 cli.linksynergy.com 127.0.0.1 clic-exchange.com 127.0.0.1 clic.reussissonsensemble.fr @@ -23599,7 +24341,9 @@ 127.0.0.1 click.livejasmin.com 127.0.0.1 click.loudgames.com 127.0.0.1 click.mmosite.com +127.0.0.1 click.mobtimizer.com 127.0.0.1 click.monstracking.com +127.0.0.1 click.mvc19.com 127.0.0.1 click.mz.simba.taobao.com 127.0.0.1 click.netpondcash.com 127.0.0.1 click.news2.v3.email-publisher.com @@ -23620,11 +24364,14 @@ 127.0.0.1 click.topica.com 127.0.0.1 click.tv.repubblica.it 127.0.0.1 click.tz.simba.taobao.com +127.0.0.1 click.union.ucweb.com 127.0.0.1 click.vgnett.no +127.0.0.1 click.whittlesbiz.info 127.0.0.1 click.wrating.com 127.0.0.1 click168.com 127.0.0.1 click2.cafepress.com 127.0.0.1 click2.danarimedia.com +127.0.0.1 click2.scour.com 127.0.0.1 click202.com 127.0.0.1 click2boost.com 127.0.0.1 click2net.nl @@ -23640,6 +24387,7 @@ 127.0.0.1 clickable.com 127.0.0.1 clickad.com.pl 127.0.0.1 clickad.pl +127.0.0.1 clickadscounter.com 127.0.0.1 clickadu.com 127.0.0.1 clickagents.com 127.0.0.1 clickagy.com @@ -23675,6 +24423,7 @@ 127.0.0.1 clickfuse.com 127.0.0.1 clickheat.elitedaily.com 127.0.0.1 clickheretofind.com +127.0.0.1 clickhoofind.com 127.0.0.1 clickhouse.com 127.0.0.1 clickice.com 127.0.0.1 clicking.com.tw @@ -23723,6 +24472,7 @@ 127.0.0.1 clicksor.com 127.0.0.1 clicksor.net 127.0.0.1 clicksrvr.co +127.0.0.1 clickstotrack.com 127.0.0.1 clickstream.co.za 127.0.0.1 clickstream.loomia.com 127.0.0.1 clickstrip.6wav.es @@ -23742,6 +24492,7 @@ 127.0.0.1 clicktracks.com 127.0.0.1 clicktrade.linkexchange.net 127.0.0.1 clicktrade.net +127.0.0.1 clicktraffix.com 127.0.0.1 clicktraq.mtree.com 127.0.0.1 clicktripz.com 127.0.0.1 clicktv.com @@ -23774,6 +24525,8 @@ 127.0.0.1 clixsense.com 127.0.0.1 clixtrac.com 127.0.0.1 clixzen1.go2cloud.org +127.0.0.1 clk.77978.xn--q9jyb4c +127.0.0.1 clk.8mm.shiksha 127.0.0.1 clk.about.com 127.0.0.1 clk.adgatemedia.com 127.0.0.1 clk.atdmt.com @@ -23803,10 +24556,12 @@ 127.0.0.1 clkmr.com 127.0.0.1 clkrev.com 127.0.0.1 clkstat.qihoo.com +127.0.0.1 clktag.com 127.0.0.1 clkuk.tradedoubler.com 127.0.0.1 clmbtrk.com 127.0.0.1 clnk.me 127.0.0.1 clog.go.com +127.0.0.1 clou.ovh 127.0.0.1 cloud-q.duba.net 127.0.0.1 cloud.beyondmobtrax.com 127.0.0.1 cloud.cashtrafic.info @@ -23815,9 +24570,11 @@ 127.0.0.1 cloudfront-dsa-eu.cedexis.com 127.0.0.1 cloudfront-labs.amazonaws.com 127.0.0.1 cloudfront.cedexis.com +127.0.0.1 cloudhostbanner.com 127.0.0.1 cloudhot.net 127.0.0.1 cloudixconnection.com 127.0.0.1 cloveads.go2cloud.org +127.0.0.1 clpremdo.com 127.0.0.1 cls.assoc-amazon.de 127.0.0.1 cls.vrvm.com 127.0.0.1 cltomedia.info @@ -23843,13 +24600,17 @@ 127.0.0.1 cm.adgrx.com 127.0.0.1 cm.adkmob.com 127.0.0.1 cm.allyes.com +127.0.0.1 cm.dpclk.com 127.0.0.1 cm.emarbox.com 127.0.0.1 cm.g.doubleclick.net 127.0.0.1 cm.gcm.ksmobile.com 127.0.0.1 cm.l.qq.com +127.0.0.1 cm.mobiletimes.info 127.0.0.1 cm.npc-lee.overture.com 127.0.0.1 cm.npc-mcclatchy.overture.com +127.0.0.1 cm8og.voluumtrk.com 127.0.0.1 cma.zdnet.com +127.0.0.1 cma60.voluumtrk.com 127.0.0.1 cmail1.com 127.0.0.1 cmail2.com 127.0.0.1 cmail5.com @@ -23867,6 +24628,7 @@ 127.0.0.1 cmpglobalvista.112.2o7.net 127.0.0.1 cmpsmarter-downloader.maynemyltf.netdna-cdn.com 127.0.0.1 cms-pixel.crowdreport.com +127.0.0.1 cms-skin.com 127.0.0.1 cms.abmr.net 127.0.0.1 cms.analytics.yahoo.com 127.0.0.1 cms.grandcloud.cn @@ -23879,12 +24641,14 @@ 127.0.0.1 cn.api.vpon.com 127.0.0.1 cn.clickable.net 127.0.0.1 cn01.dwstat.cn +127.0.0.1 cnb.cnews.ru 127.0.0.1 cncdn.9966.org 127.0.0.1 cnetnews.112.2o7.net 127.0.0.1 cnettech.112.2o7.net 127.0.0.1 cnetwidget.creativemark.co.uk 127.0.0.1 cnetzdnet.112.2o7.net 127.0.0.1 cnheagletribune.112.2o7.net +127.0.0.1 cnhsq.voluumtrk.com 127.0.0.1 cnn.122.2o7.net 127.0.0.1 cnn.dyn.cnn.com 127.0.0.1 cnn.entertainment.printthis.clickability.com @@ -23919,11 +24683,14 @@ 127.0.0.1 cnt2.xhamster.com 127.0.0.1 cntdy.mobi 127.0.0.1 cnteryield.jmp9.com +127.0.0.1 cntrck.com 127.0.0.1 cntv.wrating.com +127.0.0.1 cntwr.voluumtrk.com 127.0.0.1 cnzz.com 127.0.0.1 cnzz.mmstat.com 127.0.0.1 coachingminceur.go2cloud.org 127.0.0.1 coadvertise.com +127.0.0.1 coaft.voluumtrk.com 127.0.0.1 cobrands.mailermailer.com 127.0.0.1 coconuts.boy.jp 127.0.0.1 code.37cs.com @@ -23936,7 +24703,9 @@ 127.0.0.1 code.snapengage.com 127.0.0.1 code.superstats.com 127.0.0.1 code.taggify.net +127.0.0.1 code.theads.me 127.0.0.1 code.xidx.org +127.0.0.1 codead.dajusepe.com 127.0.0.1 codecpackriver.eu 127.0.0.1 codes.wai.it 127.0.0.1 codice.shinystat.com @@ -23950,6 +24719,7 @@ 127.0.0.1 coke.nuggad.net 127.0.0.1 coldfusion.affiliateshop.com 127.0.0.1 colette-connell.us +127.0.0.1 colle.ovh 127.0.0.1 collect.finanzen.net 127.0.0.1 collect.igodigital.com 127.0.0.1 collect.m-pathy.com @@ -23963,11 +24733,13 @@ 127.0.0.1 collector.ksax.com 127.0.0.1 collector.kstptv5.com 127.0.0.1 collector.nextguide.tv +127.0.0.1 collector.shorte.st 127.0.0.1 collector.statowl.com 127.0.0.1 collector.stats.webs.com 127.0.0.1 collector.tescocompare.com 127.0.0.1 collector.viki.io 127.0.0.1 colleen-kelso.us +127.0.0.1 colline.science 127.0.0.1 colonize.com 127.0.0.1 colorfoto.digidip.net 127.0.0.1 com-magazin.de.adadapter.netzathleten-media.de @@ -23984,18 +24756,22 @@ 127.0.0.1 com.djinnworks.sdm.s3.amazonaws.com 127.0.0.1 com.econa.com 127.0.0.1 comagic.ru +127.0.0.1 combine.urbanairship.com 127.0.0.1 comcast.112.2o7.net 127.0.0.1 comcast.demdex.net 127.0.0.1 comclick.com 127.0.0.1 comclick.net 127.0.0.1 comet.ibsrv.net 127.0.0.1 comics.ign.us.intellitxt.com +127.0.0.1 comme.science 127.0.0.1 commerce.cimedia.com 127.0.0.1 commerce.cimedia.net 127.0.0.1 commissionmonster.com 127.0.0.1 commonname.com 127.0.0.1 commonssearch.com 127.0.0.1 commonwealth.riddler.com +127.0.0.1 compagnie.science +127.0.0.1 compagnon.science 127.0.0.1 compareyourclicks.com 127.0.0.1 compart.ivwbox.de 127.0.0.1 compatdb.us.intellitxt.com @@ -24003,6 +24779,7 @@ 127.0.0.1 compete.com 127.0.0.1 compete.evergage.com 127.0.0.1 competeinc.com +127.0.0.1 complet.science 127.0.0.1 completecarrd.com 127.0.0.1 compnet.us.intellitxt.com 127.0.0.1 components.adsender.us @@ -24022,6 +24799,7 @@ 127.0.0.1 concours.fr 127.0.0.1 concours.hit-parade.com 127.0.0.1 cond01.etbxml.com +127.0.0.1 condenast.demdex.net 127.0.0.1 condenast.tt.omtrdc.net 127.0.0.1 conductedresearch.com 127.0.0.1 conduit-banners.com @@ -24029,6 +24807,7 @@ 127.0.0.1 config-ltvp.inmobi.com 127.0.0.1 config.broadcastpc.tv 127.0.0.1 config.ioam.de +127.0.0.1 config.mobile.wxbug.com 127.0.0.1 configusa.veinteractive.com 127.0.0.1 confirm-referer.glrsales.com 127.0.0.1 confluence.kissmetrics.com @@ -24044,6 +24823,8 @@ 127.0.0.1 connect.ok.ru 127.0.0.1 connect.quoka.de 127.0.0.1 connect.tapjoy.com +127.0.0.1 connect001.com +127.0.0.1 connectbot.org 127.0.0.1 connectedhome.digidip.net 127.0.0.1 connectionads.com 127.0.0.1 connectlinking1.com @@ -24062,9 +24843,11 @@ 127.0.0.1 connextra.com 127.0.0.1 connie-barto.us 127.0.0.1 connxtionads.go2cloud.org +127.0.0.1 consent-st.truste.com 127.0.0.1 consent.truste.com 127.0.0.1 consent.webmasterplan.com 127.0.0.1 console.revmob.com +127.0.0.1 consolefiles.info 127.0.0.1 consors.ivwbox.de 127.0.0.1 constance-polen.us 127.0.0.1 consumerinfo.com @@ -24093,15 +24876,18 @@ 127.0.0.1 content.liveuniverse.com 127.0.0.1 content.pop6.com 127.0.0.1 content.quantcount.com +127.0.0.1 content.rbc.medialand.ru 127.0.0.1 content.surpax.net 127.0.0.1 content.tucows.com 127.0.0.1 content.uclick.com 127.0.0.1 content.untd.com +127.0.0.1 content.videoclick.ru 127.0.0.1 content.wt.net 127.0.0.1 content.yieldmanager.com 127.0.0.1 content.yieldmanager.edgesuite.net 127.0.0.1 content.zemanta.com 127.0.0.1 contentcache-a.akamaihd.net +127.0.0.1 contentclick.akamaized.net 127.0.0.1 contentd.fundsforngos.org 127.0.0.1 contentlockingnetworks.com 127.0.0.1 contentserv.brandaffinity.net @@ -24111,12 +24897,14 @@ 127.0.0.1 contextweb.pixel.invitemedia.com 127.0.0.1 control.123banners.com 127.0.0.1 control.cityofcairns.com +127.0.0.1 control.kochava.com 127.0.0.1 controlcenter.superstats.com 127.0.0.1 controller.4seeresults.com 127.0.0.1 controller2.foreseeresults.com 127.0.0.1 controlroom.netmining.com 127.0.0.1 contxmedia.go2cloud.org 127.0.0.1 conv.adengage.com +127.0.0.1 convenir.science 127.0.0.1 conversion-tracker.com 127.0.0.1 conversion.7search.com 127.0.0.1 conversion.buddymedia.com @@ -24152,20 +24940,24 @@ 127.0.0.1 core.naturalmotion.com 127.0.0.1 core.yorkvillemarketing.com 127.0.0.1 coreads.net +127.0.0.1 coreclickhoo.com 127.0.0.1 coreg-feed.fr 127.0.0.1 coretalk.co 127.0.0.1 coretarget.co.uk 127.0.0.1 cori-borne.us 127.0.0.1 cori-stogner.us 127.0.0.1 corinne-gump.us +127.0.0.1 corpext.msitadfs.glbdns2.microsoft.com 127.0.0.1 corrie-deloney.us 127.0.0.1 corrie-mchale.us 127.0.0.1 corrine-scroggins.us +127.0.0.1 corrosif.science 127.0.0.1 cortezz.justclick.ru 127.0.0.1 cortoonnetwork.com 127.0.0.1 cosmos01ssl.webtrekk.net 127.0.0.1 cossette.com 127.0.0.1 cotidianul.2cnt.net +127.0.0.1 coudre.ovh 127.0.0.1 coull.com 127.0.0.1 coull.go2cloud.org 127.0.0.1 count.51yes.com @@ -25275,6 +26067,7 @@ 127.0.0.1 counter.theconversation.edu.au 127.0.0.1 counter.times.lv 127.0.0.1 counter.top.dkd.it +127.0.0.1 counter.top.ge 127.0.0.1 counter.top.kg 127.0.0.1 counter.topping.com.ua 127.0.0.1 counter.tripod.com @@ -25386,6 +26179,7 @@ 127.0.0.1 counters4u.com 127.0.0.1 counterservis.com 127.0.0.1 counterstrike.server.us +127.0.0.1 countess.twitch.tv 127.0.0.1 counting4free.com 127.0.0.1 countit.ch 127.0.0.1 countmaster.com @@ -25409,6 +26203,7 @@ 127.0.0.1 coupons4humanity.go2cloud.org 127.0.0.1 courtney-jacobi.us 127.0.0.1 courtney-werner.us +127.0.0.1 couter.science 127.0.0.1 covusaffiliates.go2cloud.org 127.0.0.1 coxds.com 127.0.0.1 coxnetmasterglobal.112.2o7.net @@ -25418,6 +26213,7 @@ 127.0.0.1 cp.intl.match.com 127.0.0.1 cp.pushwoosh.com 127.0.0.1 cp.surf-town.net +127.0.0.1 cp1dk.voluumtrk.com 127.0.0.1 cpa.ly 127.0.0.1 cpaaltima.go2cloud.org 127.0.0.1 cpabrasil.go2cloud.org @@ -25428,6 +26224,7 @@ 127.0.0.1 cpainfinity.com 127.0.0.1 cpajizz.go2cloud.org 127.0.0.1 cpalead.com +127.0.0.1 cpanel.nativeads.com 127.0.0.1 cpaprohits.com 127.0.0.1 cparevenue.go2cloud.org 127.0.0.1 cparussia.go2cloud.org @@ -25467,6 +26264,7 @@ 127.0.0.1 cpxl.golem.de 127.0.0.1 cpxmobile.go2cloud.org 127.0.0.1 cqcounter.com +127.0.0.1 cqyoj.voluumtrk.com 127.0.0.1 cr.loszona.com 127.0.0.1 cr.tractionize.com 127.0.0.1 crackberry.us.intellitxt.com @@ -25501,6 +26299,7 @@ 127.0.0.1 creative.ad135m.com 127.0.0.1 creative.ad136m.com 127.0.0.1 creative.ad7m.com +127.0.0.1 creative.admtpmp127.com 127.0.0.1 creative.adonion.com 127.0.0.1 creative.ak.fbcdn.net 127.0.0.1 creative.apn.co.nz @@ -25512,6 +26311,7 @@ 127.0.0.1 creative.metalyzer.com 127.0.0.1 creative.rev2pub.com 127.0.0.1 creative.speednetwork2.com +127.0.0.1 creative.speednetwork6.com 127.0.0.1 creative.whi.co.nz 127.0.0.1 creative.wwwpromoter.com 127.0.0.1 creative.xtendmedia.com @@ -25535,6 +26335,7 @@ 127.0.0.1 credit-cards.org 127.0.0.1 creoads.com 127.0.0.1 crispads.com +127.0.0.1 crispadvertising.com 127.0.0.1 crispmedia.com 127.0.0.1 crispwireless.com 127.0.0.1 crista-lentz.us @@ -25542,7 +26343,10 @@ 127.0.0.1 cristin-shank.us 127.0.0.1 cristy-baty.us 127.0.0.1 crittercism.com +127.0.0.1 crkld.voluumtrk.com 127.0.0.1 crm.3dots.co.il +127.0.0.1 crochet.ovh +127.0.0.1 croix.science 127.0.0.1 crosspromotion.ubi.com 127.0.0.1 crowdcontrol.lotame.com 127.0.0.1 crowdscience.com @@ -25553,12 +26357,17 @@ 127.0.0.1 crunkedtracking.com 127.0.0.1 crushads.com 127.0.0.1 crwdcntrl.net +127.0.0.1 crx92.voluumtrk.com +127.0.0.1 crxbh.voluumtrk.com 127.0.0.1 crystal-eisenberg.us 127.0.0.1 cs.adingo.jp 127.0.0.1 cs.adxpansion.com +127.0.0.1 cs.atdmt.com 127.0.0.1 cs.celebbusters.com 127.0.0.1 cs.exposedontape.com 127.0.0.1 cs.go.affec.tv +127.0.0.1 cs.meltdsp.com +127.0.0.1 cs.ns1p.net 127.0.0.1 cs.sexcounter.com 127.0.0.1 cs.traffichold.com 127.0.0.1 cs1.livetex.ru @@ -25586,6 +26395,7 @@ 127.0.0.1 cshoppingbox.partner.leguide.com 127.0.0.1 csi.gstatic.com 127.0.0.1 csnation.us.intellitxt.com +127.0.0.1 cso6i.voluumtrk.com 127.0.0.1 css.aliyun.com 127.0.0.1 css.uvidi.com 127.0.0.1 csstatic.com @@ -25596,6 +26406,7 @@ 127.0.0.1 ct.eid.co.nz 127.0.0.1 ct.itbusinessedge.com 127.0.0.1 ct.needlive.com +127.0.0.1 ct.sddan.com 127.0.0.1 ct.thegear-box.com 127.0.0.1 ct1.addthis.com 127.0.0.1 ct1.xrea.com @@ -25614,6 +26425,7 @@ 127.0.0.1 cts.adssend.net 127.0.0.1 cts.businesswire.com 127.0.0.1 cts.channelintelligence.com +127.0.0.1 cts.servesharp.net 127.0.0.1 cts.snmmd.nl 127.0.0.1 cts.tradepub.com 127.0.0.1 cts.vresp.com @@ -25623,6 +26435,7 @@ 127.0.0.1 cube.ign.us.intellitxt.com 127.0.0.1 cubegroup.go2cloud.org 127.0.0.1 cucdn.genesismedia.com +127.0.0.1 cujaz.voluumtrk.com 127.0.0.1 cunda.122.2o7.net 127.0.0.1 cupidplc.go2cloud.org 127.0.0.1 cur.lv @@ -25641,13 +26454,20 @@ 127.0.0.1 cutestuf.com 127.0.0.1 cutterbuck.com 127.0.0.1 cuty.doublepimp.com +127.0.0.1 cv26u.voluumtrk.com 127.0.0.1 cvt.mydas.mobi +127.0.0.1 cw1k9.voluumtrk.com +127.0.0.1 cw5jp.voluumtrk.com 127.0.0.1 cwd.86clicks.com +127.0.0.1 cwdvm.voluumtrk.com +127.0.0.1 cwdwb.voluumtrk.com +127.0.0.1 cws.psccint.com 127.0.0.1 cwsitetrack.com 127.0.0.1 cx.atdmt.com 127.0.0.1 cxad.cxense.com 127.0.0.1 cxdigitalmedia.com 127.0.0.1 cxoios.chip.de +127.0.0.1 cxw73.voluumtrk.com 127.0.0.1 cya2.net 127.0.0.1 cyberbounty.com 127.0.0.1 cyberclick.net @@ -25656,21 +26476,27 @@ 127.0.0.1 cyclops.prod.untd.com 127.0.0.1 cyclops.untd.com 127.0.0.1 cyhners.ero-advertising.com +127.0.0.1 cyk0d.voluumtrk.com 127.0.0.1 cynthia-tomas.us 127.0.0.1 cyonix.to 127.0.0.1 cyprusads.info 127.0.0.1 cz8.clickzzs.nl 127.0.0.1 czproxy8.cz +127.0.0.1 czrqs.voluumtrk.com 127.0.0.1 d.adcash.com 127.0.0.1 d.addelive.com 127.0.0.1 d.adroll.com 127.0.0.1 d.adx.io 127.0.0.1 d.adxcore.com +127.0.0.1 d.afftrx.com 127.0.0.1 d.annarbor.com 127.0.0.1 d.applovin.com 127.0.0.1 d.appsdt.com +127.0.0.1 d.audienceiq.com +127.0.0.1 d.billyaffcontent.com 127.0.0.1 d.btttag.com 127.0.0.1 d.chango.com +127.0.0.1 d.cpsoft86.com 127.0.0.1 d.delivery45.com 127.0.0.1 d.delivery47.com 127.0.0.1 d.delivery49.com @@ -25726,6 +26552,8 @@ 127.0.0.1 d1cerpgff739r9.cloudfront.net 127.0.0.1 d1cl1sqtf3o420.cloudfront.net 127.0.0.1 d1clfvuu2240eh.cloudfront.net +127.0.0.1 d1clufhfw8sswh.cloudfront.net +127.0.0.1 d1d43ayl08oaq2.cloudfront.net 127.0.0.1 d1dnmhdhg9vg1d.cloudfront.net 127.0.0.1 d1eoo1tco6rr5e.cloudfront.net 127.0.0.1 d1ey3fksimezm4.cloudfront.net @@ -25734,6 +26562,8 @@ 127.0.0.1 d1gojtoka5qi10.cloudfront.net 127.0.0.1 d1gp8joe0evc8s.cloudfront.net 127.0.0.1 d1ivexoxmp59q7.cloudfront.net +127.0.0.1 d1izbwxtc3f9ue.cloudfront.net +127.0.0.1 d1ksyxj9xozc2j.cloudfront.net 127.0.0.1 d1l6p2sc9645hc.cloudfront.net 127.0.0.1 d1lm7kd3bd3yo9.cloudfront.net 127.0.0.1 d1nkcqm1nusqof.cloudfront.net @@ -25750,6 +26580,7 @@ 127.0.0.1 d1wscoizcbxzhp.cloudfront.net 127.0.0.1 d1yu5hbtu8mng9.cloudfront.net 127.0.0.1 d1z2jf7jlzjs58.cloudfront.net +127.0.0.1 d1zgderxoe1a.cloudfront.net 127.0.0.1 d2.2-01-29be-0006.cdx.cedexis.net 127.0.0.1 d2.zedo.com 127.0.0.1 d21aw2xov4zz0i.cloudfront.net @@ -25764,11 +26595,13 @@ 127.0.0.1 d26wy0pxd3qqpv.cloudfront.net 127.0.0.1 d27jt7xr4fq3e8.cloudfront.net 127.0.0.1 d28ethi6slcjbm.cloudfront.net +127.0.0.1 d29gqcij.com 127.0.0.1 d29p64779x43zo.cloudfront.net 127.0.0.1 d29r6igjpnoykg.cloudfront.net 127.0.0.1 d2b2x1ywompm1b.cloudfront.net 127.0.0.1 d2b65ihpmocv7w.cloudfront.net 127.0.0.1 d2bgg7rjywcwsy.cloudfront.net +127.0.0.1 d2bj2texxib1qg.cloudfront.net 127.0.0.1 d2bw638ufki166.cloudfront.net 127.0.0.1 d2d2lbvq8xirbs.cloudfront.net 127.0.0.1 d2dq2ahtl5zl1z.cloudfront.net @@ -25776,17 +26609,20 @@ 127.0.0.1 d2gfi8ctn6kki7.cloudfront.net 127.0.0.1 d2gi7ultltnc2u.cloudfront.net 127.0.0.1 d2gpgaupalra1d.cloudfront.net +127.0.0.1 d2gt9oovykfp1z.cloudfront.net 127.0.0.1 d2gtlljtkeiyzd.cloudfront.net 127.0.0.1 d2gz6iop9uxobu.cloudfront.net 127.0.0.1 d2hap2bsh1k9lw.cloudfront.net 127.0.0.1 d2mic0r0bo3i6z.cloudfront.net 127.0.0.1 d2mq0uzafv8ytp.cloudfront.net 127.0.0.1 d2nlytvx51ywh9.cloudfront.net +127.0.0.1 d2nq0f8d9ofdwv.cloudfront.net 127.0.0.1 d2o307dm5mqftz.cloudfront.net 127.0.0.1 d2oallm7wrqvmi.cloudfront.net 127.0.0.1 d2oh4tlt9mrke9.cloudfront.net 127.0.0.1 d2pgy8h4i30on1.cloudfront.net 127.0.0.1 d2plxos94peuwp.cloudfront.net +127.0.0.1 d2pxb4n3f9klsc.cloudfront.net 127.0.0.1 d2r359adnh3sfn.cloudfront.net 127.0.0.1 d2ry9vue95px0b.cloudfront.net 127.0.0.1 d2so4705rl485y.cloudfront.net @@ -25794,6 +26630,7 @@ 127.0.0.1 d2tnimpzlb191i.cloudfront.net 127.0.0.1 d2ubicnllnnszy.cloudfront.net 127.0.0.1 d2uevgmgh16uk4.cloudfront.net +127.0.0.1 d2uzdrx7k4koxz.cloudfront.net 127.0.0.1 d2v9ajh2eysdau.cloudfront.net 127.0.0.1 d2vig74li2resi.cloudfront.net 127.0.0.1 d2vt6q0n0iy66w.cloudfront.net @@ -25801,6 +26638,7 @@ 127.0.0.1 d2xkqxdy6ewr93.cloudfront.net 127.0.0.1 d2yhukq7vldf1u.cloudfront.net 127.0.0.1 d2z1smm3i01tnr.cloudfront.net +127.0.0.1 d2zah9y47r7bi2.cloudfront.net 127.0.0.1 d3.2-01-29be-0006.cdx.cedexis.net 127.0.0.1 d3.sc.omtrdc.net 127.0.0.1 d3.sina.com.cn @@ -25839,6 +26677,7 @@ 127.0.0.1 d3lzezfa753mqu.cloudfront.net 127.0.0.1 d3m41swuqq4sv5.cloudfront.net 127.0.0.1 d3m83gvgzupli.cloudfront.net +127.0.0.1 d3mj0pkbugdjcn.cloudfront.net 127.0.0.1 d3mvnvhjmkxpjz.cloudfront.net 127.0.0.1 d3nb4kk262p6sf.cloudfront.net 127.0.0.1 d3nslu0hdya83q.cloudfront.net @@ -25854,6 +26693,7 @@ 127.0.0.1 d3rmnwi2tssrfx.cloudfront.net 127.0.0.1 d3s7ggfq1s6jlj.cloudfront.net 127.0.0.1 d3tdefw8pwfkbk.cloudfront.net +127.0.0.1 d3ujids68p6xmq.cloudfront.net 127.0.0.1 d3v1lb83psg9di.cloudfront.net 127.0.0.1 d3v27wwd40f0xu.cloudfront.net 127.0.0.1 d3vc1nm9xbncz5.cloudfront.net @@ -25862,6 +26702,7 @@ 127.0.0.1 d47xnnr8b1rki.cloudfront.net 127.0.0.1 d5.zedo.com 127.0.0.1 d5e1ef2qzo-8108dl9wk1l8m59.hop.clickbank.net +127.0.0.1 d5grz.voluumtrk.com 127.0.0.1 d5nxst8fruw4z.cloudfront.net 127.0.0.1 d5pvnbpawsaav.cloudfront.net 127.0.0.1 d6.zedo.com @@ -25873,17 +26714,24 @@ 127.0.0.1 d7.zedo.com 127.0.0.1 d8.zedo.com 127.0.0.1 d81mfvml8p5ml.cloudfront.net +127.0.0.1 d844g.voluumtrk.com 127.0.0.1 d8qy7md4cj3gz.cloudfront.net 127.0.0.1 d8rk54i4mohrb.cloudfront.net 127.0.0.1 d9.zedo.com 127.0.0.1 d9ae99824.se 127.0.0.1 d9b05-pgfb01xj76ljh8o4sg3f.hop.clickbank.net +127.0.0.1 d9iwc.voluumtrk.com 127.0.0.1 d9lq0o81skkdj.cloudfront.net +127.0.0.1 d9shvcd294i4o.cloudfront.net +127.0.0.1 d9uqd.voluumtrk.com 127.0.0.1 da.feedsportal.com 127.0.0.1 da.virginmedia.com +127.0.0.1 da5c8.voluumtrk.com 127.0.0.1 daaa.ero-advertising.com +127.0.0.1 dacgb.voluumtrk.com 127.0.0.1 dadegid.ru 127.0.0.1 daea.ero-advertising.com +127.0.0.1 dagek.voluumtrk.com 127.0.0.1 daggr.adsxgm.com 127.0.0.1 daia.ero-advertising.com 127.0.0.1 daidalos.twyn.com @@ -25908,12 +26756,14 @@ 127.0.0.1 danielle-guo.us 127.0.0.1 danieltan.evplayer.com 127.0.0.1 daniweb.us.intellitxt.com +127.0.0.1 danser.science 127.0.0.1 danskebank.122.207.net 127.0.0.1 daoblocks.com 127.0.0.1 daoxml.com 127.0.0.1 daphne-alsup.us 127.0.0.1 daphne-jared.us 127.0.0.1 dara-tolliver.us +127.0.0.1 darangi.ru 127.0.0.1 darla-rumfelt.us 127.0.0.1 dart.l.doubleclick.net 127.0.0.1 das5ku9q.com @@ -25941,14 +26791,24 @@ 127.0.0.1 data.gametree.tw 127.0.0.1 data.gosquared.com 127.0.0.1 data.imakenews.com +127.0.0.1 data.initialcontroledge.info 127.0.0.1 data.kamcord.com +127.0.0.1 data.lockscalecompare.com +127.0.0.1 data.logentries.com 127.0.0.1 data.marketgid.com +127.0.0.1 data.mb8e17f12.website 127.0.0.1 data.mobclix.com 127.0.0.1 data.neuroxmedia.com +127.0.0.1 data.ninemsn.com.au +127.0.0.1 data.oileddaintiessunset.info 127.0.0.1 data.perion.com +127.0.0.1 data.permittingnorthlandseamen.info 127.0.0.1 data.ppn-ad-cdn.populis.com 127.0.0.1 data.publishflow.com +127.0.0.1 data.quithappenbetting.com 127.0.0.1 data.redhelper.ru +127.0.0.1 data.replacingobservedlose.info +127.0.0.1 data.retainguaninefluorite.info 127.0.0.1 data2.ero-advertising.com 127.0.0.1 data2.gosquared.com 127.0.0.1 data3.perf.overture.com @@ -25960,6 +26820,7 @@ 127.0.0.1 datais.com 127.0.0.1 datalabusa.com 127.0.0.1 datamaster.com.cn +127.0.0.1 datametrical.com 127.0.0.1 datashreddergold.com 127.0.0.1 dataxu.com 127.0.0.1 date.ero-advertising.com @@ -25968,14 +26829,20 @@ 127.0.0.1 dating.atraf.co.il 127.0.0.1 dating.ezstatic.com 127.0.0.1 datingadnetwork.com +127.0.0.1 datingnow.mobi 127.0.0.1 datingoffersmedia.go2cloud.org +127.0.0.1 datingsinglesfree.net 127.0.0.1 dats.ero-advertising.com 127.0.0.1 datt.ero-advertising.com 127.0.0.1 daua.ero-advertising.com +127.0.0.1 daudc.voluumtrk.com 127.0.0.1 dava.ero-advertising.com +127.0.0.1 davantage.science 127.0.0.1 daversion4.digitalbrandsinc.netdna-cdn.com +127.0.0.1 daw76z57propz.cloudfront.net 127.0.0.1 dawa.ero-advertising.com 127.0.0.1 daylogs.com +127.0.0.1 dayms.voluumtrk.com 127.0.0.1 dayna-cadet.us 127.0.0.1 daz.com 127.0.0.1 db.bnex.com @@ -26009,14 +26876,17 @@ 127.0.0.1 db5.sitestats.com 127.0.0.1 db6.net-filter.com 127.0.0.1 db6.sitestats.com +127.0.0.1 db6fz.voluumtrk.com 127.0.0.1 db7.net-filter.com 127.0.0.1 db7.sitestats.com 127.0.0.1 db8.net-filter.com 127.0.0.1 db8.sitestats.com 127.0.0.1 db9.sitestats.com 127.0.0.1 dbam.dashbida.com +127.0.0.1 dban4-549565586.eu-west-1.elb.amazonaws.com 127.0.0.1 dbase01.jag.vmsn.de 127.0.0.1 dbase03.jag.vmsn.de +127.0.0.1 dbbru.voluumtrk.com 127.0.0.1 dbbsrv.com 127.0.0.1 dbi1.surf-town.net 127.0.0.1 dbltb.vmsn.de @@ -26035,17 +26905,24 @@ 127.0.0.1 dc4d4996bc86498d8959-7dc0216bc6cc2f4ed239035dfc17235b.r83.cf3.rackcdn.com 127.0.0.1 dc84.s290.meetrics.net 127.0.0.1 dc8na2hxrj29i.cloudfront.net +127.0.0.1 dc8xl0ndzn2cb.cloudfront.net 127.0.0.1 dcad.watersoul.com 127.0.0.1 dcdd29eaa743c493e732-7dc0216bc6cc2f4ed239035dfc17235b.ssl.cf3.rackcdn.com +127.0.0.1 dcisw.voluumtrk.com 127.0.0.1 dclk.net 127.0.0.1 dcs.netbiscuits.net 127.0.0.1 dcs.plussizetech.com 127.0.0.1 dcs.wtlive.com 127.0.0.1 dd.connextra.com 127.0.0.1 dd.myapp.com +127.0.0.1 dd.servinator.pw +127.0.0.1 ddfnmo6ev4fd.cloudfront.net +127.0.0.1 ddgmb.voluumtrk.com 127.0.0.1 ddi2.com +127.0.0.1 ddnk.advertur.ru 127.0.0.1 ddos.bob.local.vmsn.de 127.0.0.1 ddos.con.local.vmsn.de +127.0.0.1 ddqhn.voluumtrk.com 127.0.0.1 ddwht76d9jvfl.cloudfront.net 127.0.0.1 de-ipd.cdn.videoplaza.tv 127.0.0.1 de.2.cqcounter.com @@ -26054,8 +26931,10 @@ 127.0.0.1 de.adserver.yahoo.com 127.0.0.1 de.cb.b0.a1.top.mail.ru 127.0.0.1 de.intellitxt.com +127.0.0.1 de.ioam.de 127.0.0.1 de.nedstat.net 127.0.0.1 de.sensic.net +127.0.0.1 de.sitestat.com 127.0.0.1 de.static.planet49.com 127.0.0.1 de.tynt.com 127.0.0.1 de.web.planet49.com @@ -26082,11 +26961,16 @@ 127.0.0.1 debug.adition.com 127.0.0.1 debug.ivwbox.de 127.0.0.1 debugger.zedo.com +127.0.0.1 decantation.science +127.0.0.1 decide.mixpanel.com +127.0.0.1 declaredthoughtfulness.co +127.0.0.1 declip.crakmedia.snaxxx.tv 127.0.0.1 dedicatedads.go2cloud.org 127.0.0.1 dedicatedmedia.com 127.0.0.1 deepcom.com 127.0.0.1 definitelyfind.com 127.0.0.1 defshop01.webtrekk.net +127.0.0.1 dehaj.voluumtrk.com 127.0.0.1 deirdre-dahl.us 127.0.0.1 deirdre-houchens.us 127.0.0.1 deirdre-lupien.us @@ -26137,11 +27021,13 @@ 127.0.0.1 delivery7.trafficjunky.net 127.0.0.1 delivery8.trafficjunky.net 127.0.0.1 delivery9.trafficjunky.net +127.0.0.1 deliverymailsysytem.co.uk 127.0.0.1 deloo.de 127.0.0.1 delta223.homestead.com 127.0.0.1 deltaairlines.tt.omtrdc.net 127.0.0.1 deltarad.ivwbox.de 127.0.0.1 demandbase.com +127.0.0.1 demande.science 127.0.0.1 demandmedia.s3.amazonaws.com 127.0.0.1 demarketing.go2cloud.org 127.0.0.1 demetria-tickle.us @@ -26156,16 +27042,21 @@ 127.0.0.1 demos.zedo.com 127.0.0.1 denisse-sequeira.us 127.0.0.1 denofgeek.uk.intellitxt.com +127.0.0.1 densite.science 127.0.0.1 denver.cbslocal.us.intellitxt.com 127.0.0.1 denverpost.112.2o7.net 127.0.0.1 depesche01.webtrekk.net 127.0.0.1 depici.2cnt.net +127.0.0.1 deposer.science 127.0.0.1 depositphotos.go2cloud.org 127.0.0.1 derigina.justclick.ru +127.0.0.1 deriversal.com 127.0.0.1 derlatas.com 127.0.0.1 dermadoctoraffiliates.com 127.0.0.1 derstand.oewabox.at 127.0.0.1 derstandard.nuggad.net +127.0.0.1 des.smartclip.net +127.0.0.1 descapita.com 127.0.0.1 designweekly.co.cc 127.0.0.1 desiree-dane.us 127.0.0.1 desiree-novotny.us @@ -26200,16 +27091,22 @@ 127.0.0.1 devel.superfish.com 127.0.0.1 devel01.jag.vmsn.de 127.0.0.1 developer.anscamobile.com +127.0.0.1 device-metrics-us-2.amazon.com 127.0.0.1 device.4seeresults.com 127.0.0.1 device.maxmind.com 127.0.0.1 devil.go2cloud.org +127.0.0.1 devolapgt.com 127.0.0.1 devsalliemaecom.112.2o7.net 127.0.0.1 devshed.us.intellitxt.com 127.0.0.1 dewezet.ivwbox.de +127.0.0.1 dezaz.voluumtrk.com +127.0.0.1 df.telemetry.microsoft.com 127.0.0.1 dfd8-789c-33b8-f419.reporo.net 127.0.0.1 dfdbz2tdq3k01.cloudfront.net +127.0.0.1 dff7tx5c2qbxc.cloudfront.net 127.0.0.1 dfp.doubleclick.net 127.0.0.1 dfpreports.doubleclick.net +127.0.0.1 dgird.voluumtrk.com 127.0.0.1 dgqxhburlt16t.cloudfront.net 127.0.0.1 dhd.ivwbox.de 127.0.0.1 dhmonitor.dinaserver.com @@ -26222,10 +27119,13 @@ 127.0.0.1 dianna-gannon.us 127.0.0.1 dianomioffers.co.uk 127.0.0.1 dibmarketing.go2cloud.org +127.0.0.1 dickssgmobile.btttag.com +127.0.0.1 dicl9.voluumtrk.com 127.0.0.1 didit.com 127.0.0.1 didtal.com 127.0.0.1 didtheyreadit.com 127.0.0.1 diet.rodale.com +127.0.0.1 dieting.searchwho.com 127.0.0.1 diff.smartadserver.com 127.0.0.1 diff2.smartadserver.com 127.0.0.1 diff3.smartadserver.com @@ -26243,7 +27143,9 @@ 127.0.0.1 digits.com 127.0.0.1 digiwebmarketing.go2cloud.org 127.0.0.1 digiwebtracking.go2cloud.org +127.0.0.1 dihgq.voluumtrk.com 127.0.0.1 dihitt.com +127.0.0.1 dilhd.voluumtrk.com 127.0.0.1 dimpact.co.il 127.0.0.1 dina-rish.us 127.0.0.1 dinclinx.com @@ -26266,9 +27168,11 @@ 127.0.0.1 directory.snapshot.toolbar.conduit-services.com 127.0.0.1 directoryreferences.com 127.0.0.1 directrev.blob.core.windows.net +127.0.0.1 directrev.cloudapp.net 127.0.0.1 directstuff.com 127.0.0.1 directtrack.com 127.0.0.1 directvalue.nl +127.0.0.1 dirli.voluumtrk.com 127.0.0.1 dirt.dennis.co.uk 127.0.0.1 dirtcheapadvertising.com 127.0.0.1 dis.criteo.com @@ -26291,6 +27195,7 @@ 127.0.0.1 displayads.liveuniversenetwork.com 127.0.0.1 displayadsmedia.com 127.0.0.1 displayincloud.adk2.co +127.0.0.1 disqusads.com 127.0.0.1 dist.belnk.com 127.0.0.1 distillery.wistia.com 127.0.0.1 distrelec-02.webtrekk.net @@ -26299,9 +27204,12 @@ 127.0.0.1 diversifymarketing.go2cloud.org 127.0.0.1 divicash.com 127.0.0.1 dj.renren.com +127.0.0.1 dj1.baidu.com 127.0.0.1 djlf5xdlz7m8m.cloudfront.net 127.0.0.1 djoy-dev.91.com 127.0.0.1 dk.adserver.yahoo.com +127.0.0.1 dk49j.voluumtrk.com +127.0.0.1 dk8u3.voluumtrk.com 127.0.0.1 dkd69bwkvrht1.cloudfront.net 127.0.0.1 dkdwv3lcby5zi.cloudfront.net 127.0.0.1 dkj2m377b0yzw.cloudfront.net @@ -26313,7 +27221,10 @@ 127.0.0.1 dl.webprodcdn.com 127.0.0.1 dl1d2m8ri9v3j.cloudfront.net 127.0.0.1 dl392qndlveq0.cloudfront.net +127.0.0.1 dlapf.voluumtrk.com +127.0.0.1 dlixv.voluumtrk.com 127.0.0.1 dlmagnetdl.maynemyltf.netdna-cdn.com +127.0.0.1 dltags.com 127.0.0.1 dltrk.com 127.0.0.1 dlupv9uqtjlie.cloudfront.net 127.0.0.1 dlv.ad-serving.co @@ -26332,11 +27243,13 @@ 127.0.0.1 dmg.digitaltarget.ru 127.0.0.1 dmgt.grapeshot.co.uk 127.0.0.1 dmm.tt.omtrdc.net +127.0.0.1 dmp.springserve.com 127.0.0.1 dmp.theadex.com 127.0.0.1 dmpnetwork.go2cloud.org 127.0.0.1 dmros.ysm.yahoo.com 127.0.0.1 dmtracker.com 127.0.0.1 dmtracking01.com +127.0.0.1 dmtracking2.alibaba.com 127.0.0.1 dn.adzerver.com 127.0.0.1 dn3y71tq7jf07.cloudfront.net 127.0.0.1 dna1.mookie1.com @@ -26355,6 +27268,7 @@ 127.0.0.1 dnssec-vd.gexperiments2.com 127.0.0.1 dnsstat.com 127.0.0.1 dntx.com +127.0.0.1 dnxek.voluumtrk.com 127.0.0.1 do.you.uh.yahoo.at.bnex.com 127.0.0.1 docs.adition.com 127.0.0.1 docs.applifier.com @@ -26374,16 +27288,20 @@ 127.0.0.1 dolores-vanallen.us 127.0.0.1 dolphinsfootball.com 127.0.0.1 domain.gabia.com +127.0.0.1 domainoptions.engine.adglare.net 127.0.0.1 domains.googlesyndication.com 127.0.0.1 domainsponsor.com 127.0.0.1 domainsteam.de 127.0.0.1 domdex.com +127.0.0.1 dominoad.com 127.0.0.1 dompark.dadapro.com 127.0.0.1 domseo.com.edgesuite.net +127.0.0.1 done.witchcraftcash.com 127.0.0.1 donkeymails.com 127.0.0.1 donna-borst.us 127.0.0.1 dontblockme.modaco.com 127.0.0.1 donval.112.2o7.net +127.0.0.1 doqby.voluumtrk.com 127.0.0.1 dorothy-carrero.us 127.0.0.1 dot.eporner.com 127.0.0.1 dot.idealzip.com @@ -26391,7 +27309,6 @@ 127.0.0.1 dot.wp.pl 127.0.0.1 dot2.eporner.com 127.0.0.1 dotcom-monitor.com -127.0.0.1 dotcomemail.com 127.0.0.1 dotcommedia.de 127.0.0.1 dotlight.2cnt.net 127.0.0.1 doubleclic.com @@ -26449,8 +27366,10 @@ 127.0.0.1 doubleclicks.me 127.0.0.1 doug1izaerwt3.cloudfront.net 127.0.0.1 dowlatow.justclick.ru +127.0.0.1 download-performance.com 127.0.0.1 download.akamaitools.com.edgesuite.net 127.0.0.1 download.hitbox.com +127.0.0.1 download.installnow.mobi 127.0.0.1 download.pchubs.com 127.0.0.1 download.scorecardresearch.com 127.0.0.1 download.timesink.com @@ -26462,29 +27381,40 @@ 127.0.0.1 downloads.superfish.com 127.0.0.1 dp.g.doubleclick.net 127.0.0.1 dp51h10v6ggpa.cloudfront.net +127.0.0.1 dpdds.voluumtrk.com +127.0.0.1 dpfyq.voluumtrk.com +127.0.0.1 dpgoo.voluumtrk.com +127.0.0.1 dpjxu.voluumtrk.com 127.0.0.1 dpm.bluray-disc.de 127.0.0.1 dpm.demdex.net +127.0.0.1 dpm.zebestof.com +127.0.0.1 dpmno.voluumtrk.com 127.0.0.1 dpp750yjcl65g.cloudfront.net +127.0.0.1 dprtb.com 127.0.0.1 dps.bing.com 127.0.0.1 dq2tgxnc2knif.cloudfront.net 127.0.0.1 dqs001.adtech.fr +127.0.0.1 dr8pk6ovub897.cloudfront.net 127.0.0.1 dragon1.visits.lt 127.0.0.1 dragon145.visits.lt 127.0.0.1 dragonballzhomeland.com 127.0.0.1 dramafever.go2cloud.org 127.0.0.1 drawbrid.ge +127.0.0.1 drb34.voluumtrk.com 127.0.0.1 drd.hauchi.com.tw 127.0.0.1 dreammates.112.2o7.net 127.0.0.1 dreamsearch.or.kr 127.0.0.1 drinkmy.com 127.0.0.1 drippler.helpshift.com 127.0.0.1 drive.richbanner.ru +127.0.0.1 drive.videoclick.ru 127.0.0.1 drivelinemedia.com 127.0.0.1 driver.skyhookwireless.com 127.0.0.1 driving4dollars.com 127.0.0.1 drivotracker.com 127.0.0.1 drops.ads.visionweb.no 127.0.0.1 drp60gfj3y9kn.cloudfront.net +127.0.0.1 drrey.voluumtrk.com 127.0.0.1 drtserver.com 127.0.0.1 drugscom.us.intellitxt.com 127.0.0.1 drumcash.com @@ -26494,7 +27424,10 @@ 127.0.0.1 ds.eyeblaster.com 127.0.0.1 ds.ign.us.intellitxt.com 127.0.0.1 ds.serving-sys.com +127.0.0.1 ds1.nl +127.0.0.1 dsct1.com 127.0.0.1 dsgretaillimited.tt.omtrdc.net +127.0.0.1 dsjuu.voluumtrk.com 127.0.0.1 dsm.doubleclick.net 127.0.0.1 dsmmadvantage.com 127.0.0.1 dsmreports.doubleclick.net @@ -26502,12 +27435,14 @@ 127.0.0.1 dsnr.net 127.0.0.1 dsnrmg.com 127.0.0.1 dsp.adfarm1.adition.com +127.0.0.1 dsp.adkernel.com 127.0.0.1 dsp.bnmla.com 127.0.0.1 dspcluster.adfarm1.adition.com 127.0.0.1 dsply.com 127.0.0.1 dssja7qsifeak.cloudfront.net 127.0.0.1 dsum.casalemedia.com 127.0.0.1 dsv2.rotator.hadj1.adjuggler.net +127.0.0.1 dsyxv.voluumtrk.com 127.0.0.1 dt.302br.net 127.0.0.1 dt.adsafeprotected.com 127.0.0.1 dt.doubleclick.net.12016.9038.302br.net @@ -26516,27 +27451,34 @@ 127.0.0.1 dt.sellpoint.net 127.0.0.1 dt.videohub2.tv 127.0.0.1 dt001.net +127.0.0.1 dtf.goyavelab.com 127.0.0.1 dtkm4pd19nw6z.cloudfront.net 127.0.0.1 dtlilztwypawv.cloudfront.net 127.0.0.1 dtm.advertising.com 127.0.0.1 dtta.ero-advertising.com 127.0.0.1 dtym7iokkjlif.cloudfront.net 127.0.0.1 du8783wkf05yr.cloudfront.net +127.0.0.1 dualstack.cloudinary.com 127.0.0.1 duba.net 127.0.0.1 dubich91119.justclick.ru 127.0.0.1 dufue2m4sondk.cloudfront.net 127.0.0.1 dumedia.ru +127.0.0.1 dupue.voluumtrk.com 127.0.0.1 durre.freestats.com 127.0.0.1 dust.ipfingerprint.com +127.0.0.1 duv3c.voluumtrk.com 127.0.0.1 dv.bitterstrawberry.com 127.0.0.1 dv1970.freestats.com +127.0.0.1 dv1ih.voluumtrk.com 127.0.0.1 dv4uxy777adjt.cloudfront.net 127.0.0.1 dvdbeats.com +127.0.0.1 dvnzx.voluumtrk.com 127.0.0.1 dw-eu.com.com 127.0.0.1 dw.cbsi.com 127.0.0.1 dw.cbsi.com.cn 127.0.0.1 dw.cnet.com 127.0.0.1 dw.com.com +127.0.0.1 dwb54.voluumtrk.com 127.0.0.1 dwin2.com 127.0.0.1 dwn.pushtraffic.net 127.0.0.1 dwxmyiyf7jg6.cloudfront.net @@ -26544,13 +27486,17 @@ 127.0.0.1 dx5qvhwg92mjd.cloudfront.net 127.0.0.1 dxq6c0tx3v6mm.cloudfront.net 127.0.0.1 dxqd86uz345mg.cloudfront.net +127.0.0.1 dy21q.voluumtrk.com +127.0.0.1 dy48bnzanqw0v.cloudfront.net 127.0.0.1 dycpc40hvg4ki.cloudfront.net +127.0.0.1 dyhju.voluumtrk.com 127.0.0.1 dyl3p6so5yozo.cloudfront.net -127.0.0.1 dyn.keepa.com 127.0.0.1 dynamic.aol.com 127.0.0.1 dynamic.fmpub.net 127.0.0.1 dynamic.woolik.com 127.0.0.1 dyntraq.mtree.com +127.0.0.1 dypbo.voluumtrk.com +127.0.0.1 dzccl.voluumtrk.com 127.0.0.1 e-2dj6wfk4ggdzkbo.stats.esomniture.com 127.0.0.1 e-2dj6wfkikjd5glq.stats.esomniture.com 127.0.0.1 e-2dj6wfkiokc5odp.stats.esomniture.com @@ -26593,6 +27539,7 @@ 127.0.0.1 e.nexac.com 127.0.0.1 e.ofuda.cc 127.0.0.1 e.performancerevenues.com +127.0.0.1 e.qq.com 127.0.0.1 e.rmgserving.com 127.0.0.1 e.tdmagroup.com 127.0.0.1 e.thanksearch.com @@ -26611,9 +27558,13 @@ 127.0.0.1 e2.extreme-dm.com 127.0.0.1 e2.static.hoptopboy.com 127.0.0.1 e250a.track4.com +127.0.0.1 e2yth.tv 127.0.0.1 e32e0c3c972d179cd1d0-1847ac4c91d55b307d162b6d5ad07fe3.r71.cf2.rackcdn.com 127.0.0.1 e46fa8d94b17745ac277-ae524ab82d83e9108c081b44b53c4ff2.r94.cf2.rackcdn.com +127.0.0.1 e4sa0.voluumtrk.com +127.0.0.1 e64cf.voluumtrk.com 127.0.0.1 e89.friendfinder.com +127.0.0.1 e8obj.voluumtrk.com 127.0.0.1 e97527f0.se 127.0.0.1 e9mlrvy1.com 127.0.0.1 ea-traffic-redirection.com @@ -26628,6 +27579,7 @@ 127.0.0.1 eads.com 127.0.0.1 eads.nl 127.0.0.1 eads.org +127.0.0.1 eafl3.voluumtrk.com 127.0.0.1 earntosurf.com 127.0.0.1 earthlinkcom.122.2o7.net 127.0.0.1 earthlnkcom.122.2o7.net @@ -26642,6 +27594,7 @@ 127.0.0.1 easypolls.superstats.com 127.0.0.1 easyscopes.net 127.0.0.1 easyspace.com +127.0.0.1 eb0ej.voluumtrk.com 127.0.0.1 ebao.duba.net 127.0.0.1 ebay.ivwbox.de 127.0.0.1 ebay.northernhost.com @@ -26650,22 +27603,29 @@ 127.0.0.1 ebayobjects.com.au 127.0.0.1 ebayrelevancead.webmasterplan.com 127.0.0.1 ebe3-ad9d-992b-3990.reporo.net +127.0.0.1 ebocornac.com 127.0.0.1 ebonyhead.com 127.0.0.1 ebtmarketing.com +127.0.0.1 ebvjw.voluumtrk.com +127.0.0.1 ec-ns.sascdn.com 127.0.0.1 ec.atdmt.com 127.0.0.1 ec.tynt.com 127.0.0.1 ec.yimg.com +127.0.0.1 ec01c392919812c4f818-79afe539d963810002081e6e2a51e67e.ssl.cf2.rackcdn.com 127.0.0.1 ec1.hitbox.com 127.0.0.1 ec2-54-171-97-32.eu-west-1.compute.amazonaws.com 127.0.0.1 ec2-54-225-149-4.compute-1.amazonaws.com 127.0.0.1 ec2-54-235-183-132.compute-1.amazonaws.com 127.0.0.1 ec90-e3eb-7fb8-2a1c.reporo.net 127.0.0.1 ecb.onejumpwebinc.netdna-cdn.com +127.0.0.1 ecdn.firstimpression.io 127.0.0.1 echo.teasernet.com 127.0.0.1 echo4.bluehornet.com 127.0.0.1 echofonads.appspot.com 127.0.0.1 echonl.ivwbox.de 127.0.0.1 eclick.baidu.com +127.0.0.1 eclkmpbn.com +127.0.0.1 eclkmpsa.com 127.0.0.1 ecomcon.go2cloud.org 127.0.0.1 ecommerce.go2cloud.org 127.0.0.1 ecommercetimes.us.intellitxt.com @@ -26676,10 +27636,12 @@ 127.0.0.1 ecoupons.com 127.0.0.1 ecoverage.go2cloud.org 127.0.0.1 ecpm.adbooth.com +127.0.0.1 ecpmrocks.com 127.0.0.1 ectestlampsplus1.112.2o7.net 127.0.0.1 ed.koeln.de 127.0.0.1 eddie-theriot.us 127.0.0.1 edgar.ivwbox.de +127.0.0.1 edge.ayboll.com 127.0.0.1 edge.bredg.com 127.0.0.1 edge.jeetyetmedia.com 127.0.0.1 edge.mb.gammae.com @@ -26688,6 +27650,8 @@ 127.0.0.1 edge.sharethis.com 127.0.0.1 edgy.demo.sproutinc.com 127.0.0.1 edidomus01.webtrekk.net +127.0.0.1 editorial.outbrain.com +127.0.0.1 edk5y.voluumtrk.com 127.0.0.1 edmundscom.112.2o7.net 127.0.0.1 edna-read.us 127.0.0.1 edomz.com @@ -26705,12 +27669,15 @@ 127.0.0.1 ee02gws.advertising.com 127.0.0.1 ee02hmq.advertising.com 127.0.0.1 ee12gp4.advertising.com +127.0.0.1 ee74ff81b44.se 127.0.0.1 ee8f928b71ed0dc6033231fa0943d9f5.adsk2.co 127.0.0.1 eeb06878.qqc.co 127.0.0.1 eedclicks.ero-advertising.com +127.0.0.1 eezy.plxserve.com 127.0.0.1 efanguide.us.intellitxt.com 127.0.0.1 efoods.go2cloud.org 127.0.0.1 efront.com +127.0.0.1 egnfa.voluumtrk.com 127.0.0.1 ehc-e-healthlinks.122.2o7.net 127.0.0.1 ehc-justepilepsy.122.2o7.net 127.0.0.1 ehealthcaresolutions.com @@ -26849,13 +27816,20 @@ 127.0.0.1 ehg-yellowpages.hitbox.com 127.0.0.1 ehg-zentropypartners.hitbox.com 127.0.0.1 ehg.hitbox.com +127.0.0.1 ehh7w.voluumtrk.com 127.0.0.1 ei.cnzz.com 127.0.0.1 eightfoldlogic.com 127.0.0.1 eileen-devore.us 127.0.0.1 eileen-welk.us 127.0.0.1 einets.com +127.0.0.1 einsfest.ivwbox.de +127.0.0.1 einsfestivalliveaccount01.wt-eu02.net +127.0.0.1 ejbgr.voluumtrk.com 127.0.0.1 ejs.hitbox.com 127.0.0.1 ejs.moatads.com +127.0.0.1 eju10.voluumtrk.com +127.0.0.1 ejyav.voluumtrk.com +127.0.0.1 ek4de.voluumtrk.com 127.0.0.1 ekmpinpoint.co.uk 127.0.0.1 el.woolik.com 127.0.0.1 elaine-hamdan.us @@ -26881,6 +27855,7 @@ 127.0.0.1 eltern.ivwbox.de 127.0.0.1 eltiempocitytv.112.2o7.net 127.0.0.1 eltiempocom.112.2o7.net +127.0.0.1 eltrafiko.com 127.0.0.1 elyse-meserve.us 127.0.0.1 em.offerx.co.uk 127.0.0.1 email-newsletters.com @@ -26923,8 +27898,10 @@ 127.0.0.1 employment.112.2o7.net 127.0.0.1 emsdirect.go2cloud.org 127.0.0.1 emsvr.com +127.0.0.1 emwsz.voluumtrk.com 127.0.0.1 en.adition.com 127.0.0.1 en.adtech.info +127.0.0.1 en.mywebzines.com 127.0.0.1 en.oxosurf.eu 127.0.0.1 encrypted.reporo.net 127.0.0.1 endow.com @@ -26935,6 +27912,7 @@ 127.0.0.1 enfintrouver.com 127.0.0.1 engagebdr.com 127.0.0.1 engine.4dsply.com +127.0.0.1 engine.a.redditmedia.com 127.0.0.1 engine.adbooth.com 127.0.0.1 engine.adsupply.com 127.0.0.1 engine.adverserve.net @@ -26956,6 +27934,7 @@ 127.0.0.1 engine.doublepimp.com 127.0.0.1 engine.edocbuilder.com 127.0.0.1 engine.espace.netavenir.com +127.0.0.1 engine.fl-ads.com 127.0.0.1 engine.fling.doublepimp.com 127.0.0.1 engine.gamerati.net 127.0.0.1 engine.manwin.doublepimp.com @@ -26966,6 +27945,7 @@ 127.0.0.1 engine.netanday.it 127.0.0.1 engine.partygaming.doublepimp.com 127.0.0.1 engine.pgmediaserve.com +127.0.0.1 engine.phn.doublepimp.com 127.0.0.1 engine.phptrader.com 127.0.0.1 engine.pianomedia.sk 127.0.0.1 engine.rk.doublepimp.com @@ -26984,8 +27964,10 @@ 127.0.0.1 enlightenment.secureshoppingbasket.com 127.0.0.1 enliven.org 127.0.0.1 enlnks.com +127.0.0.1 enpdx.voluumtrk.com 127.0.0.1 enrichment.moportals.com 127.0.0.1 enscl.com +127.0.0.1 ensyp.voluumtrk.com 127.0.0.1 entercasino.com 127.0.0.1 enterprise.hitbox.com 127.0.0.1 enterprisemediagroup.112.2o7.net @@ -26994,15 +27976,22 @@ 127.0.0.1 entrepreneur.com 127.0.0.1 entrepreneur.us.intellitxt.com 127.0.0.1 entrepreneurpoc.122.2o7.net +127.0.0.1 environment.searchwho.com 127.0.0.1 envy.2cnt.net 127.0.0.1 enzjptkr.com +127.0.0.1 eo0c1.voluumtrk.com +127.0.0.1 eoleb.voluumtrk.com 127.0.0.1 eosesd.org +127.0.0.1 ep73e.voluumtrk.com 127.0.0.1 epicgameads.com 127.0.0.1 epidm.edgesuite.net 127.0.0.1 eplayer.clipsyndicate.com +127.0.0.1 epomads2.4shared.com 127.0.0.1 epowernetworktrackerimages.s3.amazonaws.com 127.0.0.1 eprofile.uimserv.net +127.0.0.1 epxkb8zz4ssdv7b.global.ssl.fastly.net 127.0.0.1 eqads.com +127.0.0.1 eqgdx.voluumtrk.com 127.0.0.1 equantum.com 127.0.0.1 equitymarketingsolutions.com 127.0.0.1 eqx.smartadserver.com @@ -27011,6 +28000,7 @@ 127.0.0.1 ericka-riffle.us 127.0.0.1 erika-winkle.us 127.0.0.1 erin-hartwell.us +127.0.0.1 erjbe.voluumtrk.com 127.0.0.1 ero-advertising.biz 127.0.0.1 ero-advertising.com 127.0.0.1 ero-advertising.eu @@ -27025,13 +28015,16 @@ 127.0.0.1 eroadvertising.net 127.0.0.1 eroadvertising.nl 127.0.0.1 eroadvertising.org +127.0.0.1 eroanalysis.com 127.0.0.1 erostracker.com 127.0.0.1 erostracker.net 127.0.0.1 erotic.masterstats.com +127.0.0.1 errantolo.com 127.0.0.1 error.2cnt.net 127.0.0.1 error.mobpartner.mobi 127.0.0.1 error.reporo.com 127.0.0.1 ers.ero-advertising.com +127.0.0.1 eruno.voluumtrk.com 127.0.0.1 eryield.jmp9.com 127.0.0.1 es.adserver.yahoo.com 127.0.0.1 es.cj.com @@ -27043,6 +28036,7 @@ 127.0.0.1 escati.hypermart.net 127.0.0.1 escati.linkopp.net 127.0.0.1 esg.hitbox.com +127.0.0.1 esg5l.voluumtrk.com 127.0.0.1 eskimokay.bravejournal.com 127.0.0.1 esomniture.com 127.0.0.1 espolupracecz.go2cloud.org @@ -27072,6 +28066,7 @@ 127.0.0.1 estat.com 127.0.0.1 esther-munsey.us 127.0.0.1 esther-strauss.us +127.0.0.1 esylx.voluumtrk.com 127.0.0.1 et.grabnetworks.com 127.0.0.1 et.nytimes.com 127.0.0.1 et.twyn-group.com @@ -27079,6 +28074,7 @@ 127.0.0.1 etahub.com 127.0.0.1 ethicalads.net 127.0.0.1 ethn.io +127.0.0.1 etlpa.voluumtrk.com 127.0.0.1 etonovosti.biz 127.0.0.1 etonovosti.net 127.0.0.1 etoys.bfast.com @@ -27087,7 +28083,9 @@ 127.0.0.1 etracker.de 127.0.0.1 etrader.kalahari.com 127.0.0.1 etrader.kalahari.net +127.0.0.1 etre.science 127.0.0.1 etui.fs.ml.com +127.0.0.1 etvsg.voluumtrk.com 127.0.0.1 eu-adcenter.net 127.0.0.1 eu-gmtdmp.gd1.mookie1.com 127.0.0.1 eu-pn1.adserver.yahoo.com @@ -27109,11 +28107,13 @@ 127.0.0.1 europe.adserver.yahoo.com 127.0.0.1 euros4click.de 127.0.0.1 eurosponsor.de +127.0.0.1 euus7.voluumtrk.com 127.0.0.1 euwidget.imshopping.com 127.0.0.1 ev.ib-ibi.com 127.0.0.1 eva-ryerson.us 127.0.0.1 evbeacon.godaddy.com 127.0.0.1 eve-hy.us +127.0.0.1 eveitoki.com 127.0.0.1 event-dev.adotsolution.com 127.0.0.1 event.adotsolution.com 127.0.0.1 event.adxpose.com @@ -27129,28 +28129,37 @@ 127.0.0.1 events.foreseeresults.com 127.0.0.1 events.jotform.com 127.0.0.1 events.olark.com +127.0.0.1 events.readrboard.com 127.0.0.1 events.realgravity.com +127.0.0.1 events.redditmedia.com 127.0.0.1 events.tremorhub.com 127.0.0.1 events.walla.co.il 127.0.0.1 events30.adcolony.com +127.0.0.1 everydayhealth.demdex.net 127.0.0.1 everyfreegift.com +127.0.0.1 everyscape.com +127.0.0.1 everythingbts.com 127.0.0.1 evidencecleanergold.com 127.0.0.1 evisit.exeter.ac.uk 127.0.0.1 evisitcs2.com 127.0.0.1 evita.ivwbox.de +127.0.0.1 evklj.voluumtrk.com 127.0.0.1 evo5-com-netmining.netmining.com 127.0.0.1 evs-hosted-14facd241e1c08.s3.amazonaws.com 127.0.0.1 evt.moatads.com +127.0.0.1 evtsl.voluumtrk.com 127.0.0.1 evwr.hitbox.com 127.0.0.1 ewordofmouth.com 127.0.0.1 ex.joyjasp.com 127.0.0.1 ex.mobmore.com 127.0.0.1 ex.puata.info 127.0.0.1 ex.umengcloud.com +127.0.0.1 exactly0r.com 127.0.0.1 exactseek.com 127.0.0.1 exacttarget.com 127.0.0.1 exad.mmo1vn.com 127.0.0.1 exadwese.us +127.0.0.1 exakj.voluumtrk.com 127.0.0.1 examnotes.us.intellitxt.com 127.0.0.1 excelpractic.justclick.ru 127.0.0.1 exch-e.atdmt.com @@ -27177,6 +28186,7 @@ 127.0.0.1 exitmoney.com 127.0.0.1 exitstitial.gopher.com 127.0.0.1 exmapro.go2cloud.org +127.0.0.1 exp.platform.glispa.com 127.0.0.1 expandsearchanswers.com 127.0.0.1 expbl2ro.xbox.com 127.0.0.1 expedia.ca.112.2o7.net @@ -27195,6 +28205,16 @@ 127.0.0.1 ext1.engageya.com 127.0.0.1 ext4.price.ru 127.0.0.1 extclickmedia-maynemyltf.netdna-ssl.com +127.0.0.1 external-lhr0-1.xx.fbcdn.net +127.0.0.1 external-lhr1-1.xx.fbcdn.net +127.0.0.1 external-lhr10-1.xx.fbcdn.net +127.0.0.1 external-lhr2-1.xx.fbcdn.net +127.0.0.1 external-lhr4-1.xx.fbcdn.net +127.0.0.1 external-lhr5-1.xx.fbcdn.net +127.0.0.1 external-lhr6-1.xx.fbcdn.net +127.0.0.1 external-lhr7-1.xx.fbcdn.net +127.0.0.1 external-lhr8-1.xx.fbcdn.net +127.0.0.1 external-lhr9-1.xx.fbcdn.net 127.0.0.1 externaldb.switchadhub.com 127.0.0.1 extntechnologies.us.intellitxt.com 127.0.0.1 extra.wavecdn.net @@ -27203,18 +28223,25 @@ 127.0.0.1 extreme-dm.com 127.0.0.1 extremetracking.com 127.0.0.1 extremetracking.net +127.0.0.1 ey4uh.voluumtrk.com +127.0.0.1 eyaxf.voluumtrk.com 127.0.0.1 eyeblaster.com 127.0.0.1 eyewond.hs.llnwd.net 127.0.0.1 ez-poll.superstats.com 127.0.0.1 ez-polls.superstats.com +127.0.0.1 ezbng.voluumtrk.com 127.0.0.1 ezcybersearch.com 127.0.0.1 ezec.co.uk 127.0.0.1 ezpoll.superstats.com 127.0.0.1 ezpolls.superstats.com +127.0.0.1 ezrsd.voluumtrk.com 127.0.0.1 eztexting.com +127.0.0.1 ezw0z.voluumtrk.com 127.0.0.1 f-js1.spotsniper.ru 127.0.0.1 f.about.com 127.0.0.1 f.aduwant.com +127.0.0.1 f.blogads.com +127.0.0.1 f.ds1.nl 127.0.0.1 f.mol.im 127.0.0.1 f.pinid.com 127.0.0.1 f.rmgserving.com @@ -27224,16 +28251,22 @@ 127.0.0.1 f.tracking.goodgamestudios.com 127.0.0.1 f.zeroredirect.com 127.0.0.1 f.zeroredirect1.com +127.0.0.1 f0nuq.voluumtrk.com 127.0.0.1 f1.ilivlite.com +127.0.0.1 f1bwg.voluumtrk.com 127.0.0.1 f2f2-e0e3-6b11-5f39.reporo.net 127.0.0.1 f2ncracker.112.2o7.net 127.0.0.1 f2nsmh.112.2o7.net 127.0.0.1 f2ntheage.112.2o7.net 127.0.0.1 f54d6bf2b1.se 127.0.0.1 f5da-aa36-cfa9-346f.reporo.net +127.0.0.1 f6xzw.voluumtrk.com 127.0.0.1 f72a-d3a4-0314-7b97.reporo.net +127.0.0.1 f74zc.voluumtrk.com 127.0.0.1 f8350e7c1.se 127.0.0.1 f935-2e8e-c7df-392d.reporo.net +127.0.0.1 f9ojq.voluumtrk.com +127.0.0.1 fabriquer.ovh 127.0.0.1 faceculture.justclick.ru 127.0.0.1 faceoff.112.2o7.net 127.0.0.1 fad-1104.nyc1.targetnet.com @@ -27244,12 +28277,15 @@ 127.0.0.1 fad-408.mtl4.targetnet.com 127.0.0.1 fad-411.mtl4.targetnet.com 127.0.0.1 fad-413.mtl4.targetnet.com +127.0.0.1 faeuz.voluumtrk.com 127.0.0.1 fairfax.cxsearch.cxense.com 127.0.0.1 faith-weissman.us 127.0.0.1 falk.ivwbox.de 127.0.0.1 falk.speedera.net 127.0.0.1 fallon-navarro.us 127.0.0.1 fan.twitch.tv +127.0.0.1 fandango.gcrd.co +127.0.0.1 fandangostore.wgiftcard.com 127.0.0.1 fanners.ero-advertising.com 127.0.0.1 fanpop.tags.crwdcntrl.net 127.0.0.1 fansign.streamray.com @@ -27294,6 +28330,7 @@ 127.0.0.1 faz.ivwbox.de 127.0.0.1 faz.met.vgwort.de 127.0.0.1 fb-nym.adnxs.com +127.0.0.1 fb.umeng.com 127.0.0.1 fbcdb01.jag.vmsn.de 127.0.0.1 fbcdn-creative-a.akamaihd.net 127.0.0.1 fbgdc.com @@ -27303,16 +28340,21 @@ 127.0.0.1 fbtrk.com 127.0.0.1 fc.webmasterpro.de 127.0.0.1 fc4c-b275-b59b-7a32.reporo.net +127.0.0.1 fccl5.voluumtrk.com 127.0.0.1 fcd3-8570-6529-d8b7.reporo.net 127.0.0.1 fcds.affiliatetracking.net 127.0.0.1 fcgadgets.blogspot.com +127.0.0.1 fcged.voluumtrk.com 127.0.0.1 fcstats.altervista.org +127.0.0.1 fctdi.voluumtrk.com 127.0.0.1 fdimages.fairfax.com.au +127.0.0.1 fdp5y.voluumtrk.com 127.0.0.1 fe-au.imrworldwide.com 127.0.0.1 fe.lea.lycos.es 127.0.0.1 fe1-au.imrworldwide.com 127.0.0.1 fe2-au.imrworldwide.com 127.0.0.1 fe3-au.imrworldwide.com +127.0.0.1 feclo.voluumtrk.com 127.0.0.1 fedex.demdex.net 127.0.0.1 fedex.tt.omtrdc.net 127.0.0.1 feed.bizzclick.com @@ -27327,22 +28369,33 @@ 127.0.0.1 feeds.webtrekk.com 127.0.0.1 feedshare.flipora.com 127.0.0.1 felitb.rightinthebox.com +127.0.0.1 femi9.voluumtrk.com 127.0.0.1 ferdy.org 127.0.0.1 fermakontenta.justclick.ru 127.0.0.1 ferrago.uk.intellitxt.com +127.0.0.1 ferrari1.info +127.0.0.1 fessr.voluumtrk.com 127.0.0.1 ff.connextra.com +127.0.0.1 ffbqk.voluumtrk.com 127.0.0.1 fff.dailymail.co.uk 127.0.0.1 ffhvtest.2cnt.net 127.0.0.1 fftf-ips.heroku.com 127.0.0.1 ffxcam.fairfax.com.au +127.0.0.1 fg5aa.voluumtrk.com +127.0.0.1 fghmt.voluumtrk.com +127.0.0.1 fh1tz.voluumtrk.com +127.0.0.1 fh2ex.voluumtrk.com 127.0.0.1 fhatfee.ero-advertising.com +127.0.0.1 fhb9n.trackvoluum.com 127.0.0.1 fhg.iknowthatgirl.com 127.0.0.1 fhg.mofos.com 127.0.0.1 fhg.pervsonpatrol.com 127.0.0.1 fhg.realslutparty.com +127.0.0.1 fhlyo.voluumtrk.com 127.0.0.1 fhserve.com 127.0.0.1 fiacardservicesgroup.tt.omtrdc.net 127.0.0.1 fiafii.org +127.0.0.1 ficelle.ovh 127.0.0.1 fiesta-game.com 127.0.0.1 fifa.d2.sc.omtrdc.net 127.0.0.1 fiksu.com @@ -27351,10 +28404,13 @@ 127.0.0.1 file00.dus.local.vmsn.de 127.0.0.1 filecm.net 127.0.0.1 fileloadr.com +127.0.0.1 files.adspdbl.com 127.0.0.1 files.chartboost.com 127.0.0.1 files.download-sponsor.de 127.0.0.1 fileserver.glam.com +127.0.0.1 filestorage52.xyz 127.0.0.1 filestore.adition.com +127.0.0.1 filetarget.net 127.0.0.1 filitrac.com 127.0.0.1 filmboxlive.go2cloud.org 127.0.0.1 filmforcedvd.ign.us.intellitxt.com @@ -27384,8 +28440,10 @@ 127.0.0.1 finduses.com 127.0.0.1 findwebnow.com 127.0.0.1 fineclicks.com +127.0.0.1 finen.voluumtrk.com 127.0.0.1 fingerhut.track4.com 127.0.0.1 finn.demdex.net +127.0.0.1 fips.uimserv.net 127.0.0.1 firecash.org 127.0.0.1 firecpa.com 127.0.0.1 firehunt.com @@ -27403,14 +28461,22 @@ 127.0.0.1 fishsnifferee.thefishsniffer.netdna-cdn.com 127.0.0.1 fistart.ivwbox.de 127.0.0.1 fitforfu.ivwbox.de +127.0.0.1 fitness.searchwho.com 127.0.0.1 fitwoman.justclick.ru 127.0.0.1 fizzix.com +127.0.0.1 fjhtp.voluumtrk.com +127.0.0.1 fjlhf.voluumtrk.com +127.0.0.1 fjm0v.voluumtrk.com +127.0.0.1 fjz3c.voluumtrk.com +127.0.0.1 fjzwn.voluumtrk.com 127.0.0.1 fkm.go2cloud.org 127.0.0.1 fkref.com +127.0.0.1 fkyno.voluumtrk.com 127.0.0.1 fl.milesplit.com 127.0.0.1 fl01.ct2.comclick.com 127.0.0.1 flaconi02.webtrekk.net 127.0.0.1 flagship.asp-host.co.uk +127.0.0.1 flappyrat.net 127.0.0.1 flash-counter.com 127.0.0.1 flash.doubleclick-analytics.com 127.0.0.1 flash.quantserve.com @@ -27425,13 +28491,16 @@ 127.0.0.1 flatfea.ero-advertising.com 127.0.0.1 flatfee.ero-advertising.com 127.0.0.1 fldownloadnet.maynemyltf.netdna-cdn.com +127.0.0.1 fledn.voluumtrk.com 127.0.0.1 flex.atdmt.com +127.0.0.1 flex.msn.com 127.0.0.1 flexbanner.com 127.0.0.1 flexbeta.us.intellitxt.com 127.0.0.1 flickr.ivwbox.de 127.0.0.1 fliegen-sparen.de.intellitxt.com 127.0.0.1 flippermedia.go2cloud.org 127.0.0.1 flippermedia.hasoffers.com +127.0.0.1 flirt.youjizz.com 127.0.0.1 flixfacts.co.uk 127.0.0.1 flom.net 127.0.0.1 floppybank.com @@ -27442,25 +28511,33 @@ 127.0.0.1 fls-eu.amazon.com 127.0.0.1 fls-eu.amazon.de 127.0.0.1 fls-fe.amazon-adsystem.com +127.0.0.1 fls-fe.amazon.co.jp 127.0.0.1 fls-na.amazon-adsystem.com 127.0.0.1 fls.doubleclick.net 127.0.0.1 flu.secureintl.com +127.0.0.1 fluentmobile.com 127.0.0.1 flurry.com 127.0.0.1 flv.hs3dmr.com 127.0.0.1 flvmoviesdownloader.com 127.0.0.1 flvxplayer.maynemyltf.netdna-cdn.com +127.0.0.1 flvyh.voluumtrk.com 127.0.0.1 flw.ero-advertising.com +127.0.0.1 flygo.ru 127.0.0.1 flyinads.com 127.0.0.1 fmaster.webtrekk.net 127.0.0.1 fmdwbsfxf0.com +127.0.0.1 fmmlk.voluumtrk.com 127.0.0.1 fmpub.net 127.0.0.1 fmtv.go2cloud.org +127.0.0.1 fmydk.voluumtrk.com 127.0.0.1 fn.hgin.com 127.0.0.1 fnatfee.ero-advertising.com 127.0.0.1 focalink.com +127.0.0.1 focas.jp 127.0.0.1 focus.ivwbox.de 127.0.0.1 focusin.com 127.0.0.1 focusworks.com +127.0.0.1 foi8q.voluumtrk.com 127.0.0.1 followup.adlandpro.com 127.0.0.1 folloyu.com 127.0.0.1 foo.freelogs.com @@ -27478,7 +28555,9 @@ 127.0.0.1 forexac.justclick.ru 127.0.0.1 forexforecast.co.cc 127.0.0.1 form-cdn.pardot.com +127.0.0.1 forms.ontraport.com 127.0.0.1 forskning.tns-cs.net +127.0.0.1 forum-fok.digidip.net 127.0.0.1 forum.adspaces.ero-advertising.com 127.0.0.1 forum.data.ero-advertising.com 127.0.0.1 forum.ero-advertising.com @@ -27500,12 +28579,18 @@ 127.0.0.1 foxsimpsons.112.2o7.net 127.0.0.1 foxy.cj.com 127.0.0.1 foxy.track4.com +127.0.0.1 foy4a.voluumtrk.com +127.0.0.1 fozdw.voluumtrk.com 127.0.0.1 fp.buy.com +127.0.0.1 fp.fraudmetrix.cn 127.0.0.1 fp.gad-network.com 127.0.0.1 fp108.digitaloptout.com 127.0.0.1 fpa.ero-advertising.com 127.0.0.1 fpctraffic.com 127.0.0.1 fpctraffic2.com +127.0.0.1 fpfts.voluumtrk.com +127.0.0.1 fpxrl.voluumtrk.com +127.0.0.1 fpzee.voluumtrk.com 127.0.0.1 fr-himedia.cdn.videoplaza.tv 127.0.0.1 fr.2.cqcounter.com 127.0.0.1 fr.adserver.yahoo.com @@ -28018,6 +29103,7 @@ 127.0.0.1 francesca-fleetwood.us 127.0.0.1 francesca-studer.us 127.0.0.1 frantic.com +127.0.0.1 frantro.de 127.0.0.1 frapost.ivwbox.de 127.0.0.1 freddyman.com 127.0.0.1 free-banners.com @@ -28039,6 +29125,7 @@ 127.0.0.1 freebieclub.com 127.0.0.1 freebiesfrenzy.go2cloud.org 127.0.0.1 freecameranow.com +127.0.0.1 freecelebvideo.net 127.0.0.1 freeclickstats.com 127.0.0.1 freecodecs.us.intellitxt.com 127.0.0.1 freecountersnow.com @@ -28074,11 +29161,19 @@ 127.0.0.1 frogsthemes.go2cloud.org 127.0.0.1 from.rovion.com 127.0.0.1 front.facetz.net +127.0.0.1 frtya.com +127.0.0.1 frtyb.com +127.0.0.1 frtye.com 127.0.0.1 fs.moatads.com 127.0.0.1 fsecfsecdev2.122.2o7.net 127.0.0.1 fsfsfsfsfsf.112.207.net 127.0.0.1 fsimedia.go2cloud.org +127.0.0.1 fspsm.voluumtrk.com +127.0.0.1 fsvbh.voluumtrk.com +127.0.0.1 fsvzj.voluumtrk.com +127.0.0.1 fswvj.voluumtrk.com 127.0.0.1 ft-snappy-02-debate.herokuapp.com +127.0.0.1 ftebd.voluumtrk.com 127.0.0.1 ftp.123banners.com 127.0.0.1 ftp.admex.com 127.0.0.1 ftp.carmunity.de @@ -28100,9 +29195,14 @@ 127.0.0.1 ftp01.dus.vmsn.de 127.0.0.1 ftpcontent.worldnow.com 127.0.0.1 ftr2.external.xerox.com +127.0.0.1 fu7fb.voluumtrk.com +127.0.0.1 fuauq.voluumtrk.com 127.0.0.1 fucktubenetwork.com 127.0.0.1 fugger.ipage.com +127.0.0.1 fullstory.com 127.0.0.1 fun-town.com +127.0.0.1 fun.searchwho.com +127.0.0.1 fundownloads102.com 127.0.0.1 fungamesmobile.com 127.0.0.1 funklicks.com 127.0.0.1 funkyd.2cnt.net @@ -28112,6 +29212,7 @@ 127.0.0.1 funstun.com 127.0.0.1 furthermobi.go2cloud.org 127.0.0.1 fus.walla.co.il +127.0.0.1 fusdc.voluumtrk.com 127.0.0.1 fuse.go2cloud.org 127.0.0.1 fusion.adtoma.com 127.0.0.1 fusion.nettavisen.no @@ -28119,23 +29220,35 @@ 127.0.0.1 futurebazaar.go2cloud.org 127.0.0.1 futurenet.com 127.0.0.1 futurepay.globway.eu +127.0.0.1 fvglt.voluumtrk.com 127.0.0.1 fw.adsafeprotected.com 127.0.0.1 fw.moatads.com +127.0.0.1 fw2zy.voluumtrk.com 127.0.0.1 fwarnr.us 127.0.0.1 fwdservice.com +127.0.0.1 fwocy.voluumtrk.com 127.0.0.1 fwpgw1.surf-town.net 127.0.0.1 fx.gtop.ro 127.0.0.1 fxj.realsecuredredirect.com +127.0.0.1 fxox3.voluumtrk.com 127.0.0.1 fxstyle.net 127.0.0.1 fxt.go2cloud.org +127.0.0.1 fyber.com +127.0.0.1 fyhjs.voluumtrk.com 127.0.0.1 fyvzz.survey7.adsservingtwig.xyz +127.0.0.1 fywwy.voluumtrk.com +127.0.0.1 fz6wh.voluumtrk.com +127.0.0.1 fzab1.voluumtrk.com +127.0.0.1 fzgmq.voluumtrk.com 127.0.0.1 g-i2.com 127.0.0.1 g-pixel.invitemedia.com +127.0.0.1 g.3gl.net 127.0.0.1 g.adnxs.com 127.0.0.1 g.adspeed.net 127.0.0.1 g.atdmt.com 127.0.0.1 g.brothersoft.com 127.0.0.1 g.delivery.net +127.0.0.1 g.live.com 127.0.0.1 g.msn.com 127.0.0.1 g.searchgist.com 127.0.0.1 g.seoparts.net @@ -28145,9 +29258,14 @@ 127.0.0.1 g.zeroredirect.com 127.0.0.1 g.zeroredirect1.com 127.0.0.1 g.zeroredirect2.com +127.0.0.1 g0ffs.voluumtrk.com +127.0.0.1 g11em.voluumtrk.com +127.0.0.1 g1vzn.voluumtrk.com 127.0.0.1 g2.gumgum.com 127.0.0.1 g3.us.intellitxt.com +127.0.0.1 g39yz.voluumtrk.com 127.0.0.1 g5.us.intellitxt.com +127.0.0.1 g9xzz.voluumtrk.com 127.0.0.1 ga.canoe.ca 127.0.0.1 ga.webdigi.co.uk 127.0.0.1 gabia.com @@ -28174,6 +29292,7 @@ 127.0.0.1 gamehouse.com 127.0.0.1 gameleads.ru 127.0.0.1 gamersblast.com +127.0.0.1 gamescpc.com 127.0.0.1 gamingblast.com 127.0.0.1 gamingclub.com 127.0.0.1 gamingrealms.go2cloud.org @@ -28198,20 +29317,28 @@ 127.0.0.1 gay.xxxcounter.com 127.0.0.1 gayadnetwork.com 127.0.0.1 gazzz.survey7.adsservingtwig.xyz +127.0.0.1 gb0qw.voluumtrk.com 127.0.0.1 gbotvisit.com +127.0.0.1 gbpeq.voluumtrk.com +127.0.0.1 gbtqw.voluumtrk.com 127.0.0.1 gc128.infusionsoft.com +127.0.0.1 gcamb.voluumtrk.com 127.0.0.1 gcld.linksynergy.com 127.0.0.1 gcounter.hosting4u.net +127.0.0.1 gcsbr.voluumtrk.com 127.0.0.1 gd.geobytes.com 127.0.0.1 gdc.indeed.com 127.0.0.1 ge1.ca 127.0.0.1 gear.ign.us.intellitxt.com 127.0.0.1 geekstogo.us.intellitxt.com 127.0.0.1 geigercount.net +127.0.0.1 gelzg.voluumtrk.com +127.0.0.1 gemgx.voluumtrk.com 127.0.0.1 geneme.go2cloud.org 127.0.0.1 generator.zdnet.com 127.0.0.1 genhit.com 127.0.0.1 genorithm.com +127.0.0.1 geo.api.qualaroo.com 127.0.0.1 geo.cliphunter.com 127.0.0.1 geo.crtracklink.com 127.0.0.1 geo.deepmetrix.com @@ -28222,9 +29349,12 @@ 127.0.0.1 geo.mtvnn.com 127.0.0.1 geo.offermatica.com 127.0.0.1 geo.q5media.net +127.0.0.1 geo.query.yahoo.com 127.0.0.1 geo.safelinktracker.com 127.0.0.1 geo.smaato.net 127.0.0.1 geo.yahoo.com +127.0.0.1 geo.ziffdavis.com +127.0.0.1 geo2.adobe.com 127.0.0.1 geobanner.adultfriendfinder.com 127.0.0.1 geobanner.alt.com 127.0.0.1 geobanner.blacksexmatch.com @@ -28348,6 +29478,7 @@ 127.0.0.1 georgewbushcom.112.2o7.net 127.0.0.1 georgia-mumford.us 127.0.0.1 georgiana-millwood.us +127.0.0.1 geoservice.curse.com 127.0.0.1 geoshopping.nzherald.co.nz 127.0.0.1 geotarget.info 127.0.0.1 geoup.com @@ -28357,9 +29488,13 @@ 127.0.0.1 get-answers-fast.com 127.0.0.1 get-information.com 127.0.0.1 get.hitbox.com +127.0.0.1 get.mb8e17f12.website 127.0.0.1 get.mirando.de 127.0.0.1 get.netscape.com 127.0.0.1 get.qualaroo.com +127.0.0.1 get.quithappenbetting.com +127.0.0.1 get.replacingobservedlose.info +127.0.0.1 get.retainguaninefluorite.info 127.0.0.1 get.rubyroyal.com 127.0.0.1 get.sad9876.info 127.0.0.1 get.slotocash.com @@ -28376,16 +29511,20 @@ 127.0.0.1 getpaidtosurfthenetmoney.homestead.com 127.0.0.1 getrank.ciki.me 127.0.0.1 gets.faz.net +127.0.0.1 gets.lockscalecompare.com 127.0.0.1 getsidecar.s3.amazonaws.com 127.0.0.1 geverons.justclick.ru +127.0.0.1 gevfh.voluumtrk2.com 127.0.0.1 gfaf-banners.s3.amazonaws.com 127.0.0.1 gfk-de.sensic.net +127.0.0.1 gfrsz.voluumtrk.com 127.0.0.1 gft2.de 127.0.0.1 gfx.infomine.com 127.0.0.1 gfx3.avn.com 127.0.0.1 gfxa.sheetmusicplus.com 127.0.0.1 gg.google.com 127.0.0.1 gg.mob.vendimob.pl +127.0.0.1 ggean.voluumtrk.com 127.0.0.1 ggi.go2cloud.org 127.0.0.1 ggo.directrev.com 127.0.0.1 gh.ffshrine.org @@ -28404,6 +29543,7 @@ 127.0.0.1 gillian-boissonneault.us 127.0.0.1 gillian-leavens.us 127.0.0.1 girafa.com +127.0.0.1 girlscoutsoftheusa.sc.omtrdc.net 127.0.0.1 girlsofvs.com 127.0.0.1 giselle-finlay.us 127.0.0.1 git01.dus.local.vmsn.de @@ -28412,7 +29552,10 @@ 127.0.0.1 giveaboost.go2cloud.org 127.0.0.1 givemefreestuff.com 127.0.0.1 gj.mmstat.com +127.0.0.1 gj7py.voluumtrk.com +127.0.0.1 gjana.voluumtrk.com 127.0.0.1 gjerrigknark.no.intellitxt.com +127.0.0.1 gjr7r.voluumtrk.com 127.0.0.1 gk.rts.doublepimp.com 127.0.0.1 gk.streamate.doublepimp.com 127.0.0.1 gladys-lapierre.us @@ -28421,6 +29564,7 @@ 127.0.0.1 glam.ivwbox.de 127.0.0.1 glbad.adtech.de 127.0.0.1 glean.pop6.com +127.0.0.1 glieo.voluumtrk.com 127.0.0.1 glitternetwork.go2cloud.org 127.0.0.1 global-adsrv.com 127.0.0.1 global.adserver.yahoo.com @@ -28438,16 +29582,23 @@ 127.0.0.1 globirank.com 127.0.0.1 globo.demdex.net 127.0.0.1 glopro.com +127.0.0.1 gluma.voluumtrk.com 127.0.0.1 gm.mmstat.com +127.0.0.1 gm7ru.voluumtrk.com 127.0.0.1 gmads.mookie1.com 127.0.0.1 gmads.net 127.0.0.1 gmagictrk.com 127.0.0.1 gmonitor.aliimg.com +127.0.0.1 gmoxn.voluumtrk.com 127.0.0.1 gmtdmp.mookie1.com 127.0.0.1 gmtracker.com 127.0.0.1 gmx-ssl.wemfbox.ch 127.0.0.1 gmx.ivwbox.de 127.0.0.1 gmx.oewabox.at +127.0.0.1 gnaea.voluumtrk.com +127.0.0.1 gnaur.voluumtrk.com +127.0.0.1 gnftr.voluumtrk.com +127.0.0.1 gniho.voluumtrk.com 127.0.0.1 gnnzz.gaming.bannernumber.eu 127.0.0.1 go-clicks.de 127.0.0.1 go.activengage.com @@ -28460,19 +29611,25 @@ 127.0.0.1 go.by 127.0.0.1 go.coxds.com 127.0.0.1 go.cpmadvisors.com +127.0.0.1 go.delnapb.com 127.0.0.1 go.feedxfeed.com +127.0.0.1 go.goroost.com +127.0.0.1 go.hd-3.com +127.0.0.1 go.herdailyvideos.com 127.0.0.1 go.hunkwebcam.com 127.0.0.1 go.inmobi.com 127.0.0.1 go.jetswap.hs5.ru 127.0.0.1 go.kissmetrics.com 127.0.0.1 go.ll.net.co 127.0.0.1 go.madmimi.com +127.0.0.1 go.mobilix.eu 127.0.0.1 go.mobilix.mobi 127.0.0.1 go.mobisla.com 127.0.0.1 go.mobitraxk.com 127.0.0.1 go.mobpartner.mobi 127.0.0.1 go.mobtrks.com 127.0.0.1 go.mobytrks.com +127.0.0.1 go.myimgt.com 127.0.0.1 go.oclaserver.com 127.0.0.1 go.oclasrv.com 127.0.0.1 go.onclasrv.com @@ -28497,7 +29654,9 @@ 127.0.0.1 go.underclick.ru 127.0.0.1 go.voice2page.com 127.0.0.1 go.vrvm.com +127.0.0.1 go.wastedpushdevelop.info 127.0.0.1 go.wbsadsdel.com +127.0.0.1 go.wellhello.com 127.0.0.1 go.wsockd.com 127.0.0.1 go.youlamedia.com 127.0.0.1 go2.trekaklik.com @@ -28525,6 +29684,8 @@ 127.0.0.1 gomobbi.go2cloud.org 127.0.0.1 gomovies.track.clicksure.com 127.0.0.1 gooal.herokuapp.com +127.0.0.1 good.9downloadall.com +127.0.0.1 goodbookbook.com 127.0.0.1 gooddata.com 127.0.0.1 goodiemob.com 127.0.0.1 goods.marketgid.com @@ -28537,9 +29698,14 @@ 127.0.0.1 googlesyndication.com 127.0.0.1 googletagmanager.com 127.0.0.1 gooool.com +127.0.0.1 gopacket.goforandroid.com +127.0.0.1 gopath2.xyz 127.0.0.1 goreal.at 127.0.0.1 gorilla.go2cloud.org 127.0.0.1 goshoppingonline.bfast.com +127.0.0.1 gosms.3g.cn +127.0.0.1 gosmsdev.3g.cn +127.0.0.1 gosmstheme.3g.cn 127.0.0.1 gospycash.com 127.0.0.1 gostats.com 127.0.0.1 gostats.de @@ -28554,6 +29720,7 @@ 127.0.0.1 goviralnetwork.net 127.0.0.1 goweb.de 127.0.0.1 goyellow.ivwbox.de +127.0.0.1 goywt.voluumtrk.com 127.0.0.1 gozilla.com 127.0.0.1 gp.dejanews.com 127.0.0.1 gpaper104.112.2o7.net @@ -28654,13 +29821,20 @@ 127.0.0.1 gpaper231.112.2o7.net 127.0.0.1 gpaper246.112.2o7.net 127.0.0.1 gpapercareer.112.2o7.net +127.0.0.1 gqe0d.voluumtrk.com +127.0.0.1 gqgng.voluumtrk.com +127.0.0.1 gqkzu.voluumtrk.com +127.0.0.1 gqqv4.voluumtrk.com 127.0.0.1 gr1innovations.com +127.0.0.1 gr2hk6ze.com 127.0.0.1 gr8.com +127.0.0.1 gr8nk.voluumtrk.com 127.0.0.1 grace-hansley.us 127.0.0.1 graffix.adition.com 127.0.0.1 grafix.xxxcounter.com 127.0.0.1 grammarly.go2cloud.org 127.0.0.1 granite.bnex.com +127.0.0.1 graphics.cams.com 127.0.0.1 graphics.friendfinder.com 127.0.0.1 graphics1.sextracker.com 127.0.0.1 graphics2.sextracker.com @@ -28685,6 +29859,7 @@ 127.0.0.1 groupe.go2cloud.org 127.0.0.1 groupware.adition.com 127.0.0.1 grow.kissmetrics.com +127.0.0.1 grsyd.voluumtrk.com 127.0.0.1 grupoblidoo.go2cloud.org 127.0.0.1 gruppoespresso01.webtrekk.net 127.0.0.1 gs.spylog.ru @@ -28692,13 +29867,17 @@ 127.0.0.1 gs1.surf-town.net 127.0.0.1 gsanet.com 127.0.0.1 gscounters.eu1.gigya.com +127.0.0.1 gscounters.us1.gigya.com 127.0.0.1 gserv.zdnet.co.uk 127.0.0.1 gsfn.us +127.0.0.1 gsfr1.voluumtrk.com 127.0.0.1 gsicrsk.112.207.net 127.0.0.1 gslbeacon.lijit.com 127.0.0.1 gsmtop.net +127.0.0.1 gsujb.voluumtrk.com 127.0.0.1 gtag.yieldoptimizer.com 127.0.0.1 gtcc1.acecounter.com +127.0.0.1 gtiuy.voluumtrk.com 127.0.0.1 gtop.ro 127.0.0.1 gtrforums.us.intellitxt.com 127.0.0.1 gts-ads.twistbox.com @@ -28707,13 +29886,17 @@ 127.0.0.1 gu-pix.appspot.com 127.0.0.1 gui.secure.mobile.contentabc.com 127.0.0.1 guid.org +127.0.0.1 gum.criteo.com 127.0.0.1 gumball-tracker.co.uk +127.0.0.1 gumlz.voluumtrk.com 127.0.0.1 guppymedia.com 127.0.0.1 gurgle.zdbb.net 127.0.0.1 guru3d.us.intellitxt.com 127.0.0.1 gutefrage.de.intellitxt.com 127.0.0.1 guterrat.ivwbox.de 127.0.0.1 guyism.us.intellitxt.com +127.0.0.1 gv90x.voluumtrk.com +127.0.0.1 gvp3l.voluumtrk.com 127.0.0.1 gw.adotsolution.com 127.0.0.1 gw1.surf-town.net 127.0.0.1 gw2.surf-town.net @@ -28722,6 +29905,8 @@ 127.0.0.1 gwa.lphbs.com 127.0.0.1 gwa.reedbusiness.net 127.0.0.1 gymboree.hs.llnwd.net +127.0.0.1 gzadt.voluumtrk.com +127.0.0.1 gzwku.voluumtrk.com 127.0.0.1 h.atdmt.com 127.0.0.1 h.cliphunter.com 127.0.0.1 h.imedia.cz @@ -28737,6 +29922,8 @@ 127.0.0.1 h.zeroredirect2.com 127.0.0.1 h1.flashvortex.com 127.0.0.1 h2.msn.com +127.0.0.1 h8lku.voluumtrk.com +127.0.0.1 h9diz.voluumtrk.com 127.0.0.1 hab3n.trackvoluum.com 127.0.0.1 hadj1-a.akamaihd.net 127.0.0.1 hairg.biz @@ -28771,6 +29958,7 @@ 127.0.0.1 hastrk3.com 127.0.0.1 hastrk4.com 127.0.0.1 hata.ero-advertising.com +127.0.0.1 hatbo.voluumtrk.com 127.0.0.1 hatmedia.go2cloud.org 127.0.0.1 hauptstadtmuttide.digidip.net 127.0.0.1 haus.ivwbox.de @@ -28781,6 +29969,7 @@ 127.0.0.1 haz.moarclicks.com 127.0.0.1 hb.justclick.ru 127.0.0.1 hbb.concentra.be +127.0.0.1 hbho0.voluumtrk.com 127.0.0.1 hbpub.vo.llnwd.net 127.0.0.1 hbs.bitterstrawberry.org 127.0.0.1 hbytracker.com @@ -28788,7 +29977,10 @@ 127.0.0.1 hc1.humanclick.com 127.0.0.1 hc2.humanclick.com 127.0.0.1 hchrmain.112.2o7.net +127.0.0.1 hclrm.voluumtrk.com +127.0.0.1 hcu1u.voluumtrk.com 127.0.0.1 hd.jmp9.com +127.0.0.1 hdc3z.voluumtrk.com 127.0.0.1 hdmoviesinc.com 127.0.0.1 hdplugindownload.com 127.0.0.1 heals.msgfocus.com @@ -28803,6 +29995,7 @@ 127.0.0.1 heise.met.vgwort.de 127.0.0.1 heise02.webtrekk.net 127.0.0.1 heisebanner.geizhals.at +127.0.0.1 hekng.voluumtrk.com 127.0.0.1 held.jmp9.com 127.0.0.1 helen-plumley.us 127.0.0.1 helen-ponte.us @@ -28812,20 +30005,28 @@ 127.0.0.1 hellen-behr.us 127.0.0.1 hello.myfonts.net 127.0.0.1 hello.staticstuff.net +127.0.0.1 helloyoungmanqq.com 127.0.0.1 help.adtech.fr 127.0.0.1 help.chartboost.com 127.0.0.1 help.doubleclick.net 127.0.0.1 help.qualaroo.com 127.0.0.1 hernando.com +127.0.0.1 hexacash.com 127.0.0.1 heywire.com +127.0.0.1 heyzap.com 127.0.0.1 hfp.gdmdigital.com +127.0.0.1 hfph5.voluumtrk.com +127.0.0.1 hfrov.voluumtrk.com 127.0.0.1 hfx.go2cloud.org 127.0.0.1 hg-ios.hitbox.com 127.0.0.1 hg.travelocity.com.edgesuite.net 127.0.0.1 hg1.hitbox.com +127.0.0.1 hg7nu.voluumtrk.com 127.0.0.1 hgads.com +127.0.0.1 hgcjh.voluumtrk.com 127.0.0.1 hhbekxxw5d9e.pflexads.com 127.0.0.1 hhdevel.com +127.0.0.1 hhioj.voluumtrk.com 127.0.0.1 hi.hellobar.com 127.0.0.1 hide.io 127.0.0.1 hideaway.com @@ -28839,6 +30040,7 @@ 127.0.0.1 hilary-yarber.us 127.0.0.1 hillary-buhr.us 127.0.0.1 hillary-rhodes.us +127.0.0.1 hilltopads.net 127.0.0.1 hindsight.com 127.0.0.1 hints.netflame.cc 127.0.0.1 hipcrime.home.dhs.org @@ -28870,6 +30072,7 @@ 127.0.0.1 hit2.hotlog.ru 127.0.0.1 hit20.hotlog.ru 127.0.0.1 hit3.hotlog.ru +127.0.0.1 hit30.hotlog.ru 127.0.0.1 hit4.hotlog.ru 127.0.0.1 hit5.hotlog.ru 127.0.0.1 hit6.hotlog.ru @@ -28913,16 +30116,26 @@ 127.0.0.1 hitx.net 127.0.0.1 hitzr.com 127.0.0.1 hiwire.com +127.0.0.1 hjapa.voluumtrk.com 127.0.0.1 hjchung.myweb.hinet.net +127.0.0.1 hjhaw.voluumtrk.com 127.0.0.1 hjlas.com +127.0.0.1 hjtjz.voluumtrk.com 127.0.0.1 hk.adserver.yahoo.com +127.0.0.1 hk9dl.voluumtrk.com 127.0.0.1 hkik.ru 127.0.0.1 hld.jmp9.com 127.0.0.1 hlgsolutions.go2cloud.org +127.0.0.1 hlrke.voluumtrk.com +127.0.0.1 hlt6o.voluumtrk.com 127.0.0.1 hm-analytics.com 127.0.0.1 hm.baidu.com +127.0.0.1 hmhrw.voluumtrk.com +127.0.0.1 hmrhz.voluumtrk.com 127.0.0.1 hmultiplex.2cnt.net +127.0.0.1 hmz8h.voluumtrk.com 127.0.0.1 hnbutton.appspot.com +127.0.0.1 hni14.voluumtrk.com 127.0.0.1 ho-openx.ha.rs 127.0.0.1 hobot.ru 127.0.0.1 holidayc.ivwbox.de @@ -28930,6 +30143,9 @@ 127.0.0.1 hollie-ohagan.us 127.0.0.1 holly-mix.us 127.0.0.1 holly-whitlow.us +127.0.0.1 hollywoodlife-us.intellitxt.com +127.0.0.1 hollywoodlife.us.intellitxt.com +127.0.0.1 homad-global-configs-eu-fra.schneevonmorgen.com.s3.amazonaws.com 127.0.0.1 homad-global-configs.schneevonmorgen.com 127.0.0.1 home-remodeling.com 127.0.0.1 home.compete.com.edgesuite.net @@ -28959,12 +30175,17 @@ 127.0.0.1 hometown-art.aol.com 127.0.0.1 homevisions.com 127.0.0.1 hondahookup.us.intellitxt.com +127.0.0.1 honeybadger.io 127.0.0.1 hook.yieldbuild.com 127.0.0.1 hoopsvibe.us.intellitxt.com 127.0.0.1 hooqy.com 127.0.0.1 hope-weidner.us 127.0.0.1 hoptopboy.com 127.0.0.1 hopurl.org +127.0.0.1 horizon.attn.com +127.0.0.1 horizon.forbes.com +127.0.0.1 horizon.playboy.com +127.0.0.1 horizon.sailthru.com 127.0.0.1 horizont.ivwbox.de 127.0.0.1 horse-racing-affiliate-program.co.uk 127.0.0.1 host-tracker.com @@ -28983,8 +30204,10 @@ 127.0.0.1 hosting2.rts.lj.doublepimp.com 127.0.0.1 hosting4.rts.fling.doublepimp.com 127.0.0.1 hostlnks.com +127.0.0.1 hostpost4.xyz 127.0.0.1 hot50.net 127.0.0.1 hotdatinglist.com +127.0.0.1 hotex.voluumtrk.com 127.0.0.1 hotjar.com 127.0.0.1 hotlinking.dosmil.imap.cc 127.0.0.1 hotlog.ru @@ -28994,24 +30217,39 @@ 127.0.0.1 hotwords.com.br 127.0.0.1 hotwords.com.mx 127.0.0.1 house27.ch +127.0.0.1 housead.umeng.com 127.0.0.1 howaboutwe.go2cloud.org 127.0.0.1 howardchui.us.intellitxt.com +127.0.0.1 hoz01.voluumtrk.com 127.0.0.1 hpglobal.112.2o7.net 127.0.0.1 hphqglobal.112.2o7.net 127.0.0.1 hpr.outbrain.com +127.0.0.1 hpygd.voluumtrk.com 127.0.0.1 hq.kissmetrics.com 127.0.0.1 hqfootyad4.blogspot.com 127.0.0.1 hqs7.cnzz.com 127.0.0.1 hr.ivwbox.de +127.0.0.1 hrdya.voluumtrk.com 127.0.0.1 hs-sites.com 127.0.0.1 hs.interpolls.com +127.0.0.1 hs4rs.voluumtrk.com +127.0.0.1 hsaki.voluumtrk.com 127.0.0.1 hsbc.tt.omtrdc.net 127.0.0.1 hsn01.wcom-ma.us.conversion-marketing.com 127.0.0.1 hstde.tradedoubler.com 127.0.0.1 hstest.surf-town.net 127.0.0.1 hstpnetwork.com +127.0.0.1 hsxfa.voluumtrk.com +127.0.0.1 hsxmo.voluumtrk.com 127.0.0.1 ht-tracking01.com +127.0.0.1 ht.keezmovies.com +127.0.0.1 ht.pornhub.com +127.0.0.1 ht.redtube.com +127.0.0.1 ht.spankwire.com 127.0.0.1 ht.tidaltv.com.10089.9022.302br.net +127.0.0.1 ht.tube8.com +127.0.0.1 ht.xtube.com +127.0.0.1 ht.youporn.com 127.0.0.1 htinua.justclick.ru 127.0.0.1 html-images.realnetworks.com 127.0.0.1 html.overture.com @@ -29031,21 +30269,36 @@ 127.0.0.1 http.tidaltv.com.42109.9319.302br.net 127.0.0.1 http100.content.ru4.com 127.0.0.1 http300.content.ru4.com +127.0.0.1 hu0pd.voluumtrk.com 127.0.0.1 hubspot.my.salesforce.com 127.0.0.1 hubtraffic.com 127.0.0.1 hubxt.pornhub.com 127.0.0.1 hubxt.redtube.com 127.0.0.1 hubxt.tube8.com +127.0.0.1 hubxt.youporn.com 127.0.0.1 hulu.112.2o7.net 127.0.0.1 humandemand.com 127.0.0.1 humaniq.com 127.0.0.1 hunt-leads.com +127.0.0.1 huntmad.com 127.0.0.1 hurricanedigitalmedia.com 127.0.0.1 hv3.webstat.com +127.0.0.1 hv3jo.voluumtrk.com +127.0.0.1 hvqtw.voluumtrk.com +127.0.0.1 hvxca.voluumtrk.com +127.0.0.1 hw1vb.voluumtrk.com 127.0.0.1 hwcdn.reporo.net +127.0.0.1 hwuhk.voluumtrk.com 127.0.0.1 hwupgrade.it.intellitxt.com +127.0.0.1 hxhsu.voluumtrk.com +127.0.0.1 hxmtq.voluumtrk.com +127.0.0.1 hxnfk.voluumtrk.com +127.0.0.1 hxpwl.voluumtrk.com 127.0.0.1 hybl9bazbc35.pflexads.com +127.0.0.1 hybtq.voluumtrk.com 127.0.0.1 hyield.jmp9.com +127.0.0.1 hym.albinass.com +127.0.0.1 hymdo.voluumtrk.com 127.0.0.1 hyperbanner.net 127.0.0.1 hyperion.adtech.fr 127.0.0.1 hyperlinksecure.com @@ -29054,10 +30307,13 @@ 127.0.0.1 hyperspace-network.com 127.0.0.1 hyperspacenetwork.com 127.0.0.1 hypertracker.com +127.0.0.1 hyv6u.voluumtrk.com +127.0.0.1 hywae.voluumtrk.com 127.0.0.1 hz7.de 127.0.0.1 i-cdn.servedbyopenx.com 127.0.0.1 i-lookup.com 127.0.0.1 i-mobile.co.jp +127.0.0.1 i-vengo.com 127.0.0.1 i-xzone.com 127.0.0.1 i.1cat.com 127.0.0.1 i.4see.mobi @@ -29073,10 +30329,12 @@ 127.0.0.1 i.exclusivegiftcards.com 127.0.0.1 i.flowgo.com 127.0.0.1 i.idownloadgalore.com +127.0.0.1 i.isohunt.to 127.0.0.1 i.jumptap.com 127.0.0.1 i.kissmetrics.com 127.0.0.1 i.ligatus.com 127.0.0.1 i.linkurysearch.me +127.0.0.1 i.mol.im 127.0.0.1 i.moreover.com 127.0.0.1 i.mxplayer.j2inter.com 127.0.0.1 i.n.jwpltx.com @@ -29107,6 +30365,7 @@ 127.0.0.1 i.zeroredirect2.com 127.0.0.1 i1-j1-18-7-1-13335-2395654547-i.init.cedexis-radar.net 127.0.0.1 i1-j1-18-7-1-13335-423715794-i.init.cedexis-radar.net +127.0.0.1 i1-j4-19-0-1-13960-2465639427-i.init.cedexis-radar.net 127.0.0.1 i1-js-14-3-01-13960-152038880-i.init.cedexis-radar.net 127.0.0.1 i1-js-14-3-01-13960-28308226-i.init.cedexis-radar.net 127.0.0.1 i1-js-14-3-01-13960-519948169-i.init.cedexis-radar.net @@ -29124,6 +30383,8 @@ 127.0.0.1 i1.likes-media.com 127.0.0.1 i1.putags.com 127.0.0.1 i18n.adition.com +127.0.0.1 i1k2c.voluumtrk.com +127.0.0.1 i1slv.voluumtrk.com 127.0.0.1 i2.cmail1.com 127.0.0.1 i2.cmail2.com 127.0.0.1 i2.createsend1.com @@ -29141,15 +30402,19 @@ 127.0.0.1 i3.createsend4.com 127.0.0.1 i3.createsend5.com 127.0.0.1 i3.putags.com +127.0.0.1 i3pw2.voluumtrk.com 127.0.0.1 i4.cmail1.com 127.0.0.1 i4.createsend4.com 127.0.0.1 i4.putags.com +127.0.0.1 i46rz.voluumtrk.com +127.0.0.1 i4927.voluumtrk.com 127.0.0.1 i4c.go2cloud.org 127.0.0.1 i5.cmail2.com 127.0.0.1 i5.cmail5.com 127.0.0.1 i5.createsend1.com 127.0.0.1 i5.createsend3.com 127.0.0.1 i5.createsend5.com +127.0.0.1 i5g9d.voluumtrk.com 127.0.0.1 i6.cmail1.com 127.0.0.1 i6.createsend3.com 127.0.0.1 i6.createsend5.com @@ -29157,12 +30422,19 @@ 127.0.0.1 i7.cmail2.com 127.0.0.1 i7.cmail5.com 127.0.0.1 i7.createsend5.com +127.0.0.1 i8vnk.voluumtrk.com +127.0.0.1 i9mhy.voluumtrk.com 127.0.0.1 ia.spinbox.net 127.0.0.1 ia1.7search.com 127.0.0.1 ia1.sftcdn.net 127.0.0.1 iacpromotion.s3.amazonaws.com +127.0.0.1 iad-login.dotomi.com 127.0.0.1 iad.anm.co.uk +127.0.0.1 iadc.qwapi.com 127.0.0.1 iadctest.qwapi.com +127.0.0.1 iadfeed.qwapi.com +127.0.0.1 iadfeedtest.qwapi.com +127.0.0.1 iads.com.np 127.0.0.1 iadsdk.apple.com 127.0.0.1 iadtools.cimedia.com 127.0.0.1 ialchemnteryield.jmp9.com @@ -29177,6 +30449,8 @@ 127.0.0.1 iar.worthathousandwords.com 127.0.0.1 ias.hitbox.com 127.0.0.1 iaspromotes.com +127.0.0.1 iatzk.voluumtrk.com +127.0.0.1 ib.3lift.com 127.0.0.1 ib.adnxs.com 127.0.0.1 ib.bounceads.net 127.0.0.1 ib.mookie1.com @@ -29185,6 +30459,7 @@ 127.0.0.1 ibeat.indiatimes.com 127.0.0.1 ibeu2.mookie1.com 127.0.0.1 ibg.hitbox.com +127.0.0.1 ibjk3.voluumtrk.com 127.0.0.1 ibope.2cnt.net 127.0.0.1 ibs.indiatimes.com 127.0.0.1 ibsys.com @@ -29197,11 +30472,13 @@ 127.0.0.1 icon.cnzz.com 127.0.0.1 iconator.com 127.0.0.1 icontact.com +127.0.0.1 icontent.us 127.0.0.1 icount.com 127.0.0.1 icoupon.go2cloud.org 127.0.0.1 icover.realmedia.com 127.0.0.1 icptrack.com 127.0.0.1 ics.hitbox.com +127.0.0.1 icthl.voluumtrk.com 127.0.0.1 icubes.go2cloud.org 127.0.0.1 icvdm.vo.llnwd.net 127.0.0.1 id.allegisgroup.com @@ -29215,12 +30492,20 @@ 127.0.0.1 idsync.rlcdn.com 127.0.0.1 idvisitor.socialreader.com 127.0.0.1 ie.justclick.ru +127.0.0.1 ieah8.voluumtrk.com +127.0.0.1 iectl.voluumtrk.com +127.0.0.1 iefqr.voluumtrk.com 127.0.0.1 ield.jmp9.com +127.0.0.1 ientc.voluumtrk.com 127.0.0.1 ientry.com 127.0.0.1 ientrymail.com 127.0.0.1 ientrynetwork.net 127.0.0.1 iesnare.com +127.0.0.1 iezgb.voluumtrk.com +127.0.0.1 ifa.camads.net 127.0.0.1 ifc.inmobi.com +127.0.0.1 ifcvc.voluumtrk.com +127.0.0.1 ifdds.voluumtrk.com 127.0.0.1 iffet.de 127.0.0.1 iforex.go2cloud.org 127.0.0.1 iframe.adultfriendfinder.com @@ -29232,6 +30517,7 @@ 127.0.0.1 iframesrccdn1.adexprt.com 127.0.0.1 iframesrccdn2.adexprt.com 127.0.0.1 ifriends.net +127.0.0.1 ifxvq.voluumtrk.com 127.0.0.1 igmarauders.isotx.com 127.0.0.1 ign.us.intellitxt.com 127.0.0.1 igninetwork.go2cloud.org @@ -29243,14 +30529,25 @@ 127.0.0.1 ih4.gamecopyworld.com 127.0.0.1 ihatepops.com 127.0.0.1 ihcgpro.go2cloud.org +127.0.0.1 ihfpu.voluumtrk.com 127.0.0.1 ihm01.ct2.comclick.com 127.0.0.1 iicdn.com +127.0.0.1 iiiyr.voluumtrk.com +127.0.0.1 ijdkc.voluumtrk.com +127.0.0.1 ijrgl.voluumtrk.com +127.0.0.1 ijsl1.voluumtrk.com +127.0.0.1 ijyrq.voluumtrk.com +127.0.0.1 ikay6.voluumtrk.com +127.0.0.1 iknka.voluumtrk.com 127.0.0.1 ikwyd.analytics.sapo.pt 127.0.0.1 ilapi.ebay.com +127.0.0.1 ilbfk.voluumtrk.com 127.0.0.1 ilead.itrack.it 127.0.0.1 ilife.com 127.0.0.1 iloopmobile.com +127.0.0.1 ilu.troughtheend.com 127.0.0.1 iluv.clickbooth.com +127.0.0.1 ilzzf.voluumtrk.com 127.0.0.1 im.405d5700.1ff6a9b.ads.a4cdn.contentabc.com 127.0.0.1 im.405d5700.8d9937e.ads.a4cdn.contentabc.com 127.0.0.1 im.51111900.b1aad3f.ads.a4cdn.contentabc.com @@ -29264,6 +30561,7 @@ 127.0.0.1 im2.smartadserver.com 127.0.0.1 ima3vpaid.appspot.com 127.0.0.1 imads.ero-advertising.com +127.0.0.1 imads.integral-marketing.com 127.0.0.1 imads.rediff.com 127.0.0.1 imadworks.rediff.com 127.0.0.1 image-map.com @@ -29311,12 +30609,14 @@ 127.0.0.1 images-aud.freshmeat.net 127.0.0.1 images-aud.slashdot.org 127.0.0.1 images-aud.sourceforge.net +127.0.0.1 images-geo.outbrain.com 127.0.0.1 images-pw.secureserver.net 127.0.0.1 images.about.com 127.0.0.1 images.adkmob.com 127.0.0.1 images.adprofile.net 127.0.0.1 images.ads.fairfax.com.au 127.0.0.1 images.ads.whaleads.com +127.0.0.1 images.adsyndication.msn.com 127.0.0.1 images.adultplex.com 127.0.0.1 images.banners.com.br 127.0.0.1 images.bizrate.com @@ -29328,6 +30628,7 @@ 127.0.0.1 images.chitika.net 127.0.0.1 images.clickability.com 127.0.0.1 images.clicktrade.com +127.0.0.1 images.cloudassets.mobi 127.0.0.1 images.cybereps.com 127.0.0.1 images.dentalplans.com 127.0.0.1 images.directtrack.com @@ -29348,7 +30649,9 @@ 127.0.0.1 images.ientrymail.com 127.0.0.1 images.ifriends.net 127.0.0.1 images.indiads.com +127.0.0.1 images.intellitxt.com 127.0.0.1 images.livejasmin.com +127.0.0.1 images.lookwhatworks.com 127.0.0.1 images.m0.net 127.0.0.1 images.marchex.com 127.0.0.1 images.millennialmedia.com @@ -29374,6 +30677,7 @@ 127.0.0.1 images.specialnetoffer.com 127.0.0.1 images.specificclick.net 127.0.0.1 images.speedbit.com +127.0.0.1 images.startappservice.com 127.0.0.1 images.streamray.com 127.0.0.1 images.taboola.com 127.0.0.1 images.tigerdirect.com.edgesuite.net @@ -29392,7 +30696,6 @@ 127.0.0.1 imagine-inc.com 127.0.0.1 imaginemedia.net 127.0.0.1 imap.linkshare.com -127.0.0.1 imasdk.googleapis.com 127.0.0.1 imatmobile.com 127.0.0.1 imb.mobitkr.com 127.0.0.1 imedia.co.il @@ -29456,6 +30759,7 @@ 127.0.0.1 img.porn365.com 127.0.0.1 img.propellerads.com 127.0.0.1 img.qualtrics.com +127.0.0.1 img.revcontent.com 127.0.0.1 img.revmob.com 127.0.0.1 img.sedoparking.com 127.0.0.1 img.servint.net @@ -29502,6 +30806,9 @@ 127.0.0.1 imgcache.classesusa.com 127.0.0.1 imgcache.vmsn.de 127.0.0.1 imgclck.com +127.0.0.1 imgfarm.com +127.0.0.1 imgg.marketgid.com +127.0.0.1 imgg.steepto.com 127.0.0.1 imgn.dt07.net 127.0.0.1 imgs.xinhuanet.com 127.0.0.1 imgserv.adbutler.com @@ -29509,9 +30816,12 @@ 127.0.0.1 imgtrack.domainmarket.com 127.0.0.1 imiclk.com 127.0.0.1 imimobile.com +127.0.0.1 imln5.voluumtrk.com 127.0.0.1 immassets.s3.amazonaws.com +127.0.0.1 immediate.mob-alerts15.com 127.0.0.1 imonitor.dhgate.com 127.0.0.1 imonitor.nethost.cz +127.0.0.1 imp.adsmogo.com 127.0.0.1 imp.bid.ace.advertising.com 127.0.0.1 imp.clickability.com 127.0.0.1 imp.constantcontact.com @@ -29519,6 +30829,7 @@ 127.0.0.1 imp.optaim.com 127.0.0.1 imp.tradedoubler.com 127.0.0.1 impact.applifier.com +127.0.0.1 impact.staging.applifier.com 127.0.0.1 impactmobile.com 127.0.0.1 impbe.tradedoubler.com 127.0.0.1 impcount.sensenetworks.com @@ -29548,14 +30859,18 @@ 127.0.0.1 imserv006.adtech.fr 127.0.0.1 imserv00x.adtech.fr 127.0.0.1 imssl01.adtech.fr +127.0.0.1 imstore.bet365affiliates.com 127.0.0.1 imtrk.trktoo.com 127.0.0.1 imu.com.cn +127.0.0.1 imupdate.3g.cn 127.0.0.1 imuweb.com 127.0.0.1 imx.comedycentral.com 127.0.0.1 in.adserver.yahoo.com 127.0.0.1 in.cybererotica.com 127.0.0.1 in.getclicky.com 127.0.0.1 in.mainentrypoint.com +127.0.0.1 in.ml314.com +127.0.0.1 in.mstrckinam.com 127.0.0.1 in.mydirtyhobby.com 127.0.0.1 in.paycounter.com 127.0.0.1 in.riskymail4free.com @@ -29576,6 +30891,7 @@ 127.0.0.1 industryclick.com 127.0.0.1 indystar.com 127.0.0.1 inedo.com +127.0.0.1 inentasky.com 127.0.0.1 ineryield.jmp9.com 127.0.0.1 inet-traffic.com 127.0.0.1 inetlog.com @@ -29615,6 +30931,7 @@ 127.0.0.1 inline.playbryte.com 127.0.0.1 inm.go2cloud.org 127.0.0.1 inmobi.com +127.0.0.1 inmobi.net 127.0.0.1 inmobisdk-a.akamaihd.net 127.0.0.1 inner-active.com 127.0.0.1 inner-active.mobi @@ -29631,11 +30948,14 @@ 127.0.0.1 insightexpressai.com 127.0.0.1 insightfirst.com 127.0.0.1 insights.gravity.com +127.0.0.1 insightsprod.zelfy.com 127.0.0.1 insitepromotion.com 127.0.0.1 insites.be +127.0.0.1 inskin.hs.llnwd.net 127.0.0.1 inskin.vo.llnwd.net 127.0.0.1 inspectlet.com 127.0.0.1 inspectorclick.com +127.0.0.1 instinctiveads.com 127.0.0.1 instore-audience.pb.adition.com 127.0.0.1 int.sitestat.com 127.0.0.1 intares.net @@ -29716,10 +31036,13 @@ 127.0.0.1 invites.info-emailer.com 127.0.0.1 invites.infoaxe.com 127.0.0.1 invites.infoaxe.net +127.0.0.1 inze5.voluumtrk.com 127.0.0.1 ioads.ero-advertising.com +127.0.0.1 ione.adspirit.de 127.0.0.1 ione.netmng.com 127.0.0.1 iopodx.de 127.0.0.1 ios.revmob.com +127.0.0.1 ip-api.com 127.0.0.1 ip.casalemedia.com 127.0.0.1 ip134.timesink.com 127.0.0.1 ip2.casalemedia.com @@ -29747,19 +31070,25 @@ 127.0.0.1 iq.mobilix.mobi 127.0.0.1 iq001.adtech.fr 127.0.0.1 iqads.2cnt.net +127.0.0.1 iqolu.voluumtrk.com 127.0.0.1 ir-de.amazon-adsystem.com 127.0.0.1 ir-jp.amazon-adsystem.com 127.0.0.1 ir-na.amazon-adsystem.com 127.0.0.1 ir.doubleclick.net +127.0.0.1 ir0.mobify.com +127.0.0.1 ir1p5.voluumtrk.com 127.0.0.1 ir2.beap.gemini.yahoo.com +127.0.0.1 ir752.voluumtrk.com 127.0.0.1 iranpopup.ir 127.0.0.1 ireklama.cz 127.0.0.1 irene-campanelli.us 127.0.0.1 irepdeliver.com 127.0.0.1 iris-derryberry.us +127.0.0.1 irozb.voluumtrk.com 127.0.0.1 irpopup.ir 127.0.0.1 irs01.net 127.0.0.1 is.vinsight.de +127.0.0.1 is9ip.voluumtrk.com 127.0.0.1 isearch.shopathome.com 127.0.0.1 isg01.casalemedia.com 127.0.0.1 ishare.metric.rediff.com @@ -29780,6 +31109,7 @@ 127.0.0.1 itrack19.com 127.0.0.1 itrckr.com 127.0.0.1 itreviews.uk.intellitxt.com +127.0.0.1 itroq.voluumtrk.com 127.0.0.1 itsptp.com 127.0.0.1 itunes.mobpartner.mobi 127.0.0.1 itv.2cnt.net @@ -29791,10 +31121,14 @@ 127.0.0.1 itxt.vibrantmedia.com 127.0.0.1 itxt2.us.intellitxt.com 127.0.0.1 itxt3.us.intellitxt.com +127.0.0.1 iuhts.voluumtrk.com +127.0.0.1 iut4u.voluumtrk.com 127.0.0.1 iv.doubleclick.net 127.0.0.1 ivi.traflab.ru 127.0.0.1 ivillageglobal.112.2o7.net +127.0.0.1 ivjca.voluumtrk.com 127.0.0.1 ivory.vnunet.com +127.0.0.1 ivqva.voluumtrk.com 127.0.0.1 ivw.discover-outdoor.de 127.0.0.1 ivw.dumontreise.de 127.0.0.1 ivw.fem.com @@ -29802,25 +31136,40 @@ 127.0.0.1 ivwextern.prosieben.de 127.0.0.1 ivwextern.sat1.de 127.0.0.1 ivypixel.com +127.0.0.1 ivypx.voluumtrk.com 127.0.0.1 iweb.go2cloud.org 127.0.0.1 iwebimg.net +127.0.0.1 iwqzh.voluumtrk.com 127.0.0.1 ixn001.ixn.local.vmsn.de 127.0.0.1 ixn002.ixn.local.vmsn.de 127.0.0.1 ixn003.ixn.local.vmsn.de +127.0.0.1 ixsjz.voluumtrk.com +127.0.0.1 iy3me.voluumtrk.com +127.0.0.1 iyczm.voluumtrk.com +127.0.0.1 iymaw.voluumtrk.com 127.0.0.1 izzonet.go2cloud.org 127.0.0.1 i_sbitinbsjs_info.tlscdn.com 127.0.0.1 j-ad.dominiosfree.net 127.0.0.1 j.2004cms.com 127.0.0.1 j.adlooxtracking.com 127.0.0.1 j.clickdensity.com -127.0.0.1 j.gs 127.0.0.1 j.kissinsights.com +127.0.0.1 j.mp 127.0.0.1 j.ophan.co.uk +127.0.0.1 j.rvttrack.com 127.0.0.1 j.sahcdn.com 127.0.0.1 j.zeroredirect.com 127.0.0.1 j.zeroredirect1.com 127.0.0.1 j.zeroredirect2.com +127.0.0.1 j0q3w.voluumtrk.com +127.0.0.1 j1alc.voluumtrk.com +127.0.0.1 j3vcj.voluumtrk.com 127.0.0.1 j4.signclck.com +127.0.0.1 j4sgq.voluumtrk.com +127.0.0.1 j5g2b.voluumtrk.com +127.0.0.1 j5sjr.voluumtrk.com +127.0.0.1 j8r2b.voluumtrk.com +127.0.0.1 jaapt.voluumtrk.com 127.0.0.1 jackie-dibella.us 127.0.0.1 jackpotgambling.net 127.0.0.1 jade-aultman.us @@ -29830,6 +31179,7 @@ 127.0.0.1 jag01.jag.vmsn.de 127.0.0.1 jag06.jag.vmsn.de 127.0.0.1 jaimie-adames.us +127.0.0.1 jajzr.voluumtrk.com 127.0.0.1 jalbum.net 127.0.0.1 james.adbutler.de 127.0.0.1 jamii.2cnt.net @@ -29844,6 +31194,7 @@ 127.0.0.1 janna-ransome.us 127.0.0.1 japan.inmobi.com 127.0.0.1 jaqueline-bostwick.us +127.0.0.1 jarvinzo.com 127.0.0.1 jasmin-mcgehee.us 127.0.0.1 jasmin-thaxton.us 127.0.0.1 jasmin.com @@ -29856,16 +31207,21 @@ 127.0.0.1 jayde.com 127.0.0.1 jayme-cray.us 127.0.0.1 jayohmedia.go2cloud.org +127.0.0.1 jbmqa.voluumtrk.com 127.0.0.1 jbonlinemedia-electronics.t.domdex.com +127.0.0.1 jbrotrk.com 127.0.0.1 jbugk.com 127.0.0.1 jcarter.spinbox.net +127.0.0.1 jclqf.voluumtrk.com 127.0.0.1 jcmailer.justcode.biz 127.0.0.1 jcount.com 127.0.0.1 jcr0757kum.s.ad6media.fr 127.0.0.1 jd-mail.co.uk 127.0.0.1 jd.revolvermaps.com +127.0.0.1 jd2yj.voluumtrk.com 127.0.0.1 jdn.monster.com 127.0.0.1 je.revolvermaps.com +127.0.0.1 je60z.voluumtrk.com 127.0.0.1 jeana-pedigo.us 127.0.0.1 jeanette-cropper.us 127.0.0.1 jeanette-hipps.us @@ -29877,6 +31233,7 @@ 127.0.0.1 jeanne-hutcherson.us 127.0.0.1 jeannette-nauman.us 127.0.0.1 jeannie-mccord.us +127.0.0.1 jeiup.voluumtrk.com 127.0.0.1 jen-lobdell.us 127.0.0.1 jena-murrill.us 127.0.0.1 jena-porras.us @@ -29887,6 +31244,7 @@ 127.0.0.1 jennie-underhill.us 127.0.0.1 jennifer-krieger.us 127.0.0.1 jennifer-nease.us +127.0.0.1 jeolk.voluumtrk.com 127.0.0.1 jerrcotch.com 127.0.0.1 jerrica-abraham.us 127.0.0.1 jerrica-godinez.us @@ -29902,6 +31260,11 @@ 127.0.0.1 jetmultimedia1.audientia.net 127.0.0.1 jetzt.digidip.net 127.0.0.1 jewelryincandlescomplg-wxhost.netdna-ssl.com +127.0.0.1 jfhjh.voluumtrk.com +127.0.0.1 jggxm.voluumtrk.com +127.0.0.1 jh4ne.voluumtrk.com +127.0.0.1 jhojr.voluumtrk.com +127.0.0.1 jidbn.voluumtrk.com 127.0.0.1 jigoshop.go2cloud.org 127.0.0.1 jijsonline.112.2o7.net 127.0.0.1 jill-mortimer.us @@ -29910,8 +31273,12 @@ 127.0.0.1 jira.adition.com 127.0.0.1 jira.omniture.com 127.0.0.1 jiwire.com +127.0.0.1 jixfo.voluumtrk.com 127.0.0.1 jk.chapm.com 127.0.0.1 jkearns.freestats.com +127.0.0.1 jkeyp.voluumtrk.com +127.0.0.1 jkgah.voluumtrk.com +127.0.0.1 jlh9b.voluumtrk.com 127.0.0.1 jlinks.industrybrains.com 127.0.0.1 jlyse.net 127.0.0.1 jm.moatads.com @@ -29919,8 +31286,10 @@ 127.0.0.1 jmp.clickbooth.com 127.0.0.1 jmp.realtraq.net 127.0.0.1 jmp9.com +127.0.0.1 jmpdirect01.com 127.0.0.1 jmpgo.com 127.0.0.1 jmplink.com +127.0.0.1 jmsri.voluumtrk.com 127.0.0.1 joan-moronta.us 127.0.0.1 joanna-cohen.us 127.0.0.1 joanna-ruckman.us @@ -29937,10 +31306,12 @@ 127.0.0.1 jody-leal.us 127.0.0.1 joetec.net 127.0.0.1 johnlewis.112.2o7.net +127.0.0.1 joigd.voluumtrk.com 127.0.0.1 join.whitegfs.com 127.0.0.1 join4free.com 127.0.0.1 jokedollars.com 127.0.0.1 jokep.com +127.0.0.1 jokes.searchwho.com 127.0.0.1 jolene-mclin.us 127.0.0.1 jonathanleger.com 127.0.0.1 jorel.radfellas.com @@ -29952,10 +31323,21 @@ 127.0.0.1 joyourself.com 127.0.0.1 jp-u.openx.net 127.0.0.1 jp1.sb01.com +127.0.0.1 jp8yh.voluumtrk.com 127.0.0.1 jpmedia.go2cloud.org +127.0.0.1 jppcj.voluumtrk.com +127.0.0.1 jpvoj.voluumtrk.com +127.0.0.1 jpxal.voluumtrk.com +127.0.0.1 jqreb.voluumtrk.com +127.0.0.1 jqs7d.voluumtrk.com +127.0.0.1 jqueryapi.ru +127.0.0.1 jqyss.voluumtrk.com 127.0.0.1 jrcdelcotimescom.122.2o7.net +127.0.0.1 jrjdn.voluumtrk.com 127.0.0.1 jrtux.com 127.0.0.1 js-agent.newrelic.com +127.0.0.1 js-sec.indexww.com +127.0.0.1 js-webt-lol.9cache.com 127.0.0.1 js.ad-stir.com 127.0.0.1 js.adforgames.com 127.0.0.1 js.adm.cnzz.net @@ -29963,8 +31345,11 @@ 127.0.0.1 js.adscale.de 127.0.0.1 js.adsonar.com 127.0.0.1 js.aiya.com.cn +127.0.0.1 js.avstats.com 127.0.0.1 js.bigdoor.com +127.0.0.1 js.bitlordservdownload.com 127.0.0.1 js.bizographics.com +127.0.0.1 js.bqcpu.com 127.0.0.1 js.buzzcity.net 127.0.0.1 js.callbutton.net 127.0.0.1 js.cdn.ac @@ -29974,12 +31359,16 @@ 127.0.0.1 js.embedarticle.com 127.0.0.1 js.ero-advertising.com 127.0.0.1 js.francite.com +127.0.0.1 js.honeybadger.io 127.0.0.1 js.hs-analytics.net 127.0.0.1 js.hubspot.com 127.0.0.1 js.indexww.com +127.0.0.1 js.juicyads.com 127.0.0.1 js.kau.li 127.0.0.1 js.libertysurf.fr 127.0.0.1 js.livehelper.com +127.0.0.1 js.matheranalytics.com +127.0.0.1 js.maxmind.com 127.0.0.1 js.moatads.com 127.0.0.1 js.myinfotopia.com 127.0.0.1 js.nrcdn.com @@ -29988,6 +31377,7 @@ 127.0.0.1 js.revsci.net 127.0.0.1 js.rmbn.ru 127.0.0.1 js.ru.redtram.com +127.0.0.1 js.sddan.com 127.0.0.1 js.smartredirect.de 127.0.0.1 js.socialsay.me 127.0.0.1 js.stormcontainertag.com @@ -30003,6 +31393,7 @@ 127.0.0.1 jsc.dinclinx.com 127.0.0.1 jsc.dt07.net 127.0.0.1 jsc.madisonlogic.com +127.0.0.1 jsc.marketgid.com 127.0.0.1 jsc.mgid.com 127.0.0.1 jscount.com 127.0.0.1 jserr.cnzz.com @@ -30020,7 +31411,13 @@ 127.0.0.1 jsonp.moatads.com 127.0.0.1 jspy.ru 127.0.0.1 jsu.dt07.net +127.0.0.1 jsu.mgid.com +127.0.0.1 jsv2.ptengine.com 127.0.0.1 jswrite.com +127.0.0.1 jsy59.voluumtrk.com +127.0.0.1 jttnk.voluumtrk.com +127.0.0.1 jtzvl.voluumtrk.com +127.0.0.1 ju6jb.voluumtrk.com 127.0.0.1 juana-gribble.us 127.0.0.1 judith-whitbeck.us 127.0.0.1 juiceadv.com @@ -30029,7 +31426,9 @@ 127.0.0.1 julianna-ferrara.us 127.0.0.1 julianna-hassell.us 127.0.0.1 jump.aragontrack.com +127.0.0.1 jump.jspg.cc 127.0.0.1 jump.omnitarget.com +127.0.0.1 jump.youmobistein.com 127.0.0.1 jumptap.com 127.0.0.1 junbi-tracker.com 127.0.0.1 jupiter.appads.com @@ -30038,19 +31437,37 @@ 127.0.0.1 jupiter1.appads.com 127.0.0.1 jupiter2.appads.com 127.0.0.1 jupiter3.appads.com +127.0.0.1 justad.mobi 127.0.0.1 justcounter.com +127.0.0.1 justgetitfaster.com 127.0.0.1 justsaywow.com 127.0.0.1 justwebads.com 127.0.0.1 justwtchaffiliatecom.ipage.com +127.0.0.1 jvesd.voluumtrk.com +127.0.0.1 jvhuv.voluumtrk.com +127.0.0.1 jvpmb.voluumtrk.com +127.0.0.1 jvrub.voluumtrk.com 127.0.0.1 jvz5.com 127.0.0.1 jvz7.com 127.0.0.1 jvz8.com 127.0.0.1 jvz9.com 127.0.0.1 jvzoo.com +127.0.0.1 jwner.voluumtrk.com +127.0.0.1 jwpltx.com +127.0.0.1 jxzmo.voluumtrk.com +127.0.0.1 jzgng.voluumtrk.com 127.0.0.1 k.collective-media.net 127.0.0.1 k.zeroredirect.com 127.0.0.1 k.zeroredirect1.com 127.0.0.1 k.zeroredirect2.com +127.0.0.1 k30mn.voluumtrk.com +127.0.0.1 k3sbo.voluumtrk.com +127.0.0.1 k658t.voluumtrk.com +127.0.0.1 k66fd.voluumtrk.com +127.0.0.1 k6afu.voluumtrk.com +127.0.0.1 k6pwy.voluumtrk.com +127.0.0.1 k7j6a.voluumtrk.com +127.0.0.1 k9a8e.voluumtrk.com 127.0.0.1 kaartenhuis.nl.site-id.nl 127.0.0.1 kabel1.ivwbox.de 127.0.0.1 kacey-sheilds.us @@ -30060,6 +31477,7 @@ 127.0.0.1 kaitlin-greenhalgh.us 127.0.0.1 kala-mazzariello.us 127.0.0.1 kalanternaya.justclick.ru +127.0.0.1 kaldi.voluumtrk.com 127.0.0.1 kaleidoaffiliates.go2cloud.org 127.0.0.1 kalender.adition.com 127.0.0.1 kalpit.go2cloud.org @@ -30070,6 +31488,7 @@ 127.0.0.1 kanoodle.com 127.0.0.1 karen-lobato.us 127.0.0.1 karen-warden.us +127.0.0.1 kargo.com 127.0.0.1 karina-bolds.us 127.0.0.1 karla-law.us 127.0.0.1 karoline-fortin.us @@ -30088,16 +31507,27 @@ 127.0.0.1 katlyn-oday.us 127.0.0.1 katrina-rumbaugh.us 127.0.0.1 katrina-urias.us +127.0.0.1 kavanga.ru 127.0.0.1 kayla-massie.us 127.0.0.1 kaylee-donley.us 127.0.0.1 kayleigh-cota.us 127.0.0.1 kayleigh-kilian.us 127.0.0.1 kb.2cnt.net 127.0.0.1 kb1vtest.2cnt.net +127.0.0.1 kb9kd.voluumtrk.com +127.0.0.1 kcaf9.voluumtrk.com +127.0.0.1 kd1fv.voluumtrk.com +127.0.0.1 kdhhl.voluumtrk.com +127.0.0.1 kdovs.voluumtrk.com 127.0.0.1 kds.ero-advertising.com +127.0.0.1 kdzwk.voluumtrk.com +127.0.0.1 kea98.voluumtrk.com +127.0.0.1 kechekch.info 127.0.0.1 keen.io 127.0.0.1 kefu.duba.net 127.0.0.1 keisu.eproof.com +127.0.0.1 kek2s.voluumtrk.com +127.0.0.1 kel6x.voluumtrk.com 127.0.0.1 kelley-eggert.us 127.0.0.1 kelley-madewell.us 127.0.0.1 kelly-duby.us @@ -30119,6 +31549,8 @@ 127.0.0.1 keyword.netscape.com 127.0.0.1 keywordblocks.com 127.0.0.1 keywordmax.com +127.0.0.1 keywords.fmpub.net +127.0.0.1 kfmm5.voluumtrk.com 127.0.0.1 kfw-05.webtrekk.net 127.0.0.1 kfw-06.webtrekk.net 127.0.0.1 kfw-07.webtrekk.net @@ -30126,17 +31558,24 @@ 127.0.0.1 kfw02.webtrekk.net 127.0.0.1 kfw03.webtrekk.net 127.0.0.1 kfw04.webtrekk.net +127.0.0.1 kgj9s.voluumtrk.com +127.0.0.1 kgn3j.voluumtrk.com +127.0.0.1 khe5q.voluumtrk.com +127.0.0.1 khkg2.voluumtrk.com 127.0.0.1 khvx.secoptim.com +127.0.0.1 ki6fu.voluumtrk.com 127.0.0.1 kicker.ivwbox.de 127.0.0.1 kiks.yandex.ru 127.0.0.1 kikuzip.com 127.0.0.1 kiley-beeman.us +127.0.0.1 kilomonj.net 127.0.0.1 kilopog.com 127.0.0.1 kim-whittemore.us 127.0.0.1 kimberlee-llanos.us 127.0.0.1 kimberley-chin.us 127.0.0.1 kimberley-mackinnon.us 127.0.0.1 kimberly-worsham.us +127.0.0.1 kimia.monetizerlink.com 127.0.0.1 kinkycards.com 127.0.0.1 kino.ivwbox.de 127.0.0.1 kinocash.com @@ -30154,10 +31593,14 @@ 127.0.0.1 kissinsights.com 127.0.0.1 kissmetrics.com 127.0.0.1 kissmyads.com +127.0.0.1 kitcg.voluumtrk.com 127.0.0.1 kizash.com +127.0.0.1 kjolj.voluumtrk.com 127.0.0.1 kjos.vo.llnwd.net +127.0.0.1 kjswp.voluumtrk.com 127.0.0.1 kka.agitos.de 127.0.0.1 kka.idg.de +127.0.0.1 kkastatic.s3.amazonaws.com 127.0.0.1 klarmobi.ivwbox.de 127.0.0.1 kleiderkreisel.digidip.net 127.0.0.1 klick.affiliwelt.net @@ -30170,9 +31613,14 @@ 127.0.0.1 klipmart.forbes.com 127.0.0.1 kliptracker.com 127.0.0.1 klix.cz +127.0.0.1 klu02.voluumtrk.com +127.0.0.1 klxik.voluumtrk.com 127.0.0.1 kmechte.justclick.ru +127.0.0.1 kmfrk.voluumtrk.com 127.0.0.1 kmylvwo5.com +127.0.0.1 knalo.voluumtrk.com 127.0.0.1 knigge.webtrekk.net +127.0.0.1 kochava.com 127.0.0.1 koeln.ivwbox.de 127.0.0.1 koelnrsc.ivwbox.de 127.0.0.1 kohls.tt.omtrdc.net @@ -30181,10 +31629,16 @@ 127.0.0.1 kondratev.popunder.ru 127.0.0.1 kontaktanzeigen-fotos.de 127.0.0.1 kontera.com +127.0.0.1 koocash.com +127.0.0.1 kople.voluumtrk.com 127.0.0.1 kori-stimson.us 127.0.0.1 kosoft-ads.com +127.0.0.1 kostenloseanzeigen.momentblick.de 127.0.0.1 kourtney-fewell.us +127.0.0.1 kpcrw.voluumtrk.com +127.0.0.1 kpe75.voluumtrk.com 127.0.0.1 kpicentral.com +127.0.0.1 kqram.voluumtrk.com 127.0.0.1 kr-adimage.lycos.co.kr 127.0.0.1 krafteurope.112.2o7.net 127.0.0.1 kraftfoodseurope.d3.sc.omtrdc.net @@ -30197,27 +31651,42 @@ 127.0.0.1 krj9b.redirectvoluum.com 127.0.0.1 krone.oewabox.at 127.0.0.1 kropka.tro.pl +127.0.0.1 krwxs.voluumtrk.com 127.0.0.1 kryptobanners.com 127.0.0.1 krysta-quinlan.us 127.0.0.1 ksta.ivwbox.de +127.0.0.1 kszmp.voluumtrk.com +127.0.0.1 ktmoh.voluumtrk.com 127.0.0.1 ktu.sv2.biz 127.0.0.1 kuad.kusogi.com 127.0.0.1 kudika.2cnt.net +127.0.0.1 kuhuz.voluumtrk.com +127.0.0.1 kuivt.voluumtrk.com 127.0.0.1 kulinar.justclick.ru +127.0.0.1 kulxc.voluumtrk.com 127.0.0.1 kumaweg.ivwbox.de 127.0.0.1 kunal3291.ibprofits.cpa.clicksure.com 127.0.0.1 kundenserver.de +127.0.0.1 kvjwu.voluumtrk.com 127.0.0.1 kvm001.bob.local.vmsn.de 127.0.0.1 kvm001.con.local.vmsn.de 127.0.0.1 kvm002.bob.local.vmsn.de 127.0.0.1 kvm002.con.local.vmsn.de 127.0.0.1 kvm003.bob.local.vmsn.de 127.0.0.1 kvm003.con.local.vmsn.de +127.0.0.1 kvsge.voluumtrk.com 127.0.0.1 kvvadrat.net +127.0.0.1 ky6oi.voluumtrk.com +127.0.0.1 ky9ab.voluumtrk.com 127.0.0.1 kyg.go2cloud.org +127.0.0.1 kyuhd.voluumtrk.com +127.0.0.1 kzhav.voluumtrk.com +127.0.0.1 kzyrg.voluumtrk.com +127.0.0.1 l.5min.com 127.0.0.1 l.addthiscdn.com 127.0.0.1 l.advertstream.com 127.0.0.1 l.adxcore.com +127.0.0.1 l.betrad.com 127.0.0.1 l.collective-media.net 127.0.0.1 l.deals.ebay.com 127.0.0.1 l.deindeal.ch @@ -30240,7 +31709,13 @@ 127.0.0.1 l.zeroredirect2.com 127.0.0.1 l1.zedo.com 127.0.0.1 l2.zedo.com +127.0.0.1 l3egp.voluumtrk.com +127.0.0.1 l4vt1.voluumtrk.com 127.0.0.1 l5.zedo.com +127.0.0.1 l69or.voluumtrk.com +127.0.0.1 l6kjl.voluumtrk.com +127.0.0.1 l9wne.voluumtrk.com +127.0.0.1 la-chi.outbrain.com 127.0.0.1 la1w1.salesforceliveagent.com 127.0.0.1 lab.atdmt.com 127.0.0.1 labelfind01.webtrekk.net @@ -30260,15 +31735,20 @@ 127.0.0.1 landing.seek2.com 127.0.0.1 landing.trafficz.com 127.0.0.1 landingpagegenius.com +127.0.0.1 landscaping.srv123.com 127.0.0.1 lapescuit.2cnt.net 127.0.0.1 lapi.ebay.com 127.0.0.1 lapoo.net 127.0.0.1 lara-goodwill.us 127.0.0.1 latency-pixel.ic-live.com +127.0.0.1 latest-369538.ahtamsu.ru 127.0.0.1 latinoreview.us.intellitxt.com +127.0.0.1 lau1.slot.union.ucweb.com 127.0.0.1 launch1.co 127.0.0.1 launchbit.com +127.0.0.1 launchermsg.3g.cn 127.0.0.1 laura-garver.us +127.0.0.1 laura.dntrax.com 127.0.0.1 laurel.macrovision.com 127.0.0.1 lauren-kinsman.us 127.0.0.1 laurie-boyles.us @@ -30295,10 +31775,13 @@ 127.0.0.1 lb.usemaxserver.de 127.0.0.1 lb.webovernet.com 127.0.0.1 lbhf.logo-net.co.uk +127.0.0.1 lbkkq.voluumtrk.com 127.0.0.1 lbn.ru 127.0.0.1 lbs.justclick.ru +127.0.0.1 lbtel.voluumtrk.com 127.0.0.1 lc-pay.com 127.0.0.1 lc.iadvize.com +127.0.0.1 lcc64.voluumtrk.com 127.0.0.1 lcs.livedoor.net 127.0.0.1 lcs.naver.com 127.0.0.1 ld.you2mail.com @@ -30340,6 +31823,7 @@ 127.0.0.1 learn.doubleclick.net 127.0.0.1 leave.exacttarget.com 127.0.0.1 legacylabs.go2cloud.org +127.0.0.1 legisland.net 127.0.0.1 legitfreecounters.com 127.0.0.1 legkokstroinosti.justclick.ru 127.0.0.1 lego.112.207.net @@ -30349,9 +31833,15 @@ 127.0.0.1 lerelais.com 127.0.0.1 letssearch.com 127.0.0.1 lettre.research-int.fr +127.0.0.1 level.turboads.de 127.0.0.1 level3com.d2.sc.omtrdc.net +127.0.0.1 lever.co 127.0.0.1 lexa3maus.justclick.ru +127.0.0.1 lfbot.voluumtrk.com 127.0.0.1 lfstmedia.com +127.0.0.1 lg.brandreachsys.com +127.0.0.1 lhcep.voluumtrk.com +127.0.0.1 lhgqc.voluumtrk.com 127.0.0.1 lia-bartell.us 127.0.0.1 lia-trollinger.us 127.0.0.1 liana-carls.us @@ -30362,17 +31852,20 @@ 127.0.0.1 libstat.com 127.0.0.1 license20121011.getjar.com 127.0.0.1 liczniki.org +127.0.0.1 lidl.adspirit.de 127.0.0.1 life-in-travels.justclick.ru 127.0.0.1 life.imagepix.org 127.0.0.1 lifeserv.com 127.0.0.1 lifetoyou.justclick.ru 127.0.0.1 lift.openx.com 127.0.0.1 liftopia.go2cloud.org +127.0.0.1 lightboxanalytics-53675.onmodulus.net 127.0.0.1 lightedpages.com 127.0.0.1 lijit.com 127.0.0.1 likebtn.com 127.0.0.1 liliana-prokop.us 127.0.0.1 lily-mcnamara.us +127.0.0.1 limelightovp3.creatorcloud.netdna-cdn.com 127.0.0.1 limitlessoffers.go2cloud.org 127.0.0.1 linda-bretz.us 127.0.0.1 linda-jose.us @@ -30467,6 +31960,7 @@ 127.0.0.1 link.pcspeedup.com 127.0.0.1 link.profitreload.com 127.0.0.1 link.que-du-top.com +127.0.0.1 link.safecart.com 127.0.0.1 link.saisietachance.fr 127.0.0.1 link.super-courriel.com 127.0.0.1 link.sysmail.fr @@ -30544,15 +32038,18 @@ 127.0.0.1 list.ru 127.0.0.1 list2.bravenet.com 127.0.0.1 listbot.com +127.0.0.1 lisyn.voluumtrk.com 127.0.0.1 litiumo.com 127.0.0.1 live-cams-1.livejasmin.com 127.0.0.1 live.chartboost.com 127.0.0.1 live.ec2.cxo.name 127.0.0.1 live.ivwbox.de 127.0.0.1 live.logaholic.com +127.0.0.1 live.rads.msn.com 127.0.0.1 live.sekindo.com 127.0.0.1 live1.logaholic.com 127.0.0.1 live2.logaholic.com +127.0.0.1 liveadexchanger.com 127.0.0.1 liveadvert.com 127.0.0.1 livecams.nl 127.0.0.1 livechat.s3.amazonaws.com @@ -30561,7 +32058,6 @@ 127.0.0.1 liveinternet.ru 127.0.0.1 livejasmin.com 127.0.0.1 livepass.conviva.com -127.0.0.1 livepassdl.conviva.com 127.0.0.1 liveprivates.com 127.0.0.1 liverail.com 127.0.0.1 livere.co.kr @@ -30583,8 +32079,10 @@ 127.0.0.1 liz-vassar.us 127.0.0.1 liza-kyler.us 127.0.0.1 lizette-stickler.us +127.0.0.1 lizk.co 127.0.0.1 ljostrade.go2cloud.org 127.0.0.1 ljteas.com +127.0.0.1 ljujm.voluumtrk.com 127.0.0.1 lkb.berlinermedienvertrieb.de 127.0.0.1 lli.org 127.0.0.1 llivedotcom.2cnt.net @@ -30597,12 +32095,16 @@ 127.0.0.1 lmcd.us.intellitxt.com 127.0.0.1 lmdemo.netmining.com 127.0.0.1 lmebxwbsno.com +127.0.0.1 lmjgo.voluumtrk.com 127.0.0.1 lmlttrack.com 127.0.0.1 ln1-g669.intellitxt.com 127.0.0.1 lnkgo.com 127.0.0.1 lnkgt.com +127.0.0.1 lnwns.voluumtrk.com 127.0.0.1 lnx.lu 127.0.0.1 load.exelator.com +127.0.0.1 load.instinctiveads.com +127.0.0.1 load.sumome.com 127.0.0.1 loader.engage.gsfn.us 127.0.0.1 loadeu.exelator.com 127.0.0.1 loading1.widdit.com @@ -30620,6 +32122,7 @@ 127.0.0.1 locktrk.com 127.0.0.1 log-3.previewnetworks.com 127.0.0.1 log.adap.tv +127.0.0.1 log.adefficiency.net 127.0.0.1 log.advista.no 127.0.0.1 log.amitshah.net 127.0.0.1 log.applifier.com @@ -30702,6 +32205,7 @@ 127.0.0.1 logc246.xiti.com 127.0.0.1 logc269.xiti.com 127.0.0.1 logc278.xiti.com +127.0.0.1 logc281.xiti.com 127.0.0.1 logc286.xiti.com 127.0.0.1 logc35.xiti.com 127.0.0.1 logc4.xiti.com @@ -30775,6 +32279,7 @@ 127.0.0.1 logs1204.xiti.com 127.0.0.1 logs1241.xiti.com 127.0.0.1 logs1242.xiti.com +127.0.0.1 logs1281.xiti.com 127.0.0.1 logs13.xiti.com 127.0.0.1 logs1400.xiti.com 127.0.0.1 logs1403.xiti.com @@ -30809,6 +32314,7 @@ 127.0.0.1 logv7.xiti.com 127.0.0.1 logv8.xiti.com 127.0.0.1 logv9.xiti.com +127.0.0.1 logyd.voluumtrk.com 127.0.0.1 lokhlp.com 127.0.0.1 lolfun.com 127.0.0.1 lon.telefonica.bench.cedexis.com @@ -30821,8 +32327,10 @@ 127.0.0.1 looksa.com 127.0.0.1 looksmart.com 127.0.0.1 looksmartcollect.247realmedia.com +127.0.0.1 lookup-dir.xyz 127.0.0.1 looky.hyves.org 127.0.0.1 loopme.biz +127.0.0.1 loopme.me 127.0.0.1 lootcrate.go2cloud.org 127.0.0.1 lora-keeler.us 127.0.0.1 loran-naccarato.us @@ -30830,6 +32338,9 @@ 127.0.0.1 lorena-margolis.us 127.0.0.1 lori-ridenour.us 127.0.0.1 los.go2cloud.org +127.0.0.1 losbs.voluumtrk.com +127.0.0.1 losii.voluumtrk.com +127.0.0.1 lostwebtracker.com 127.0.0.1 lotame.com 127.0.0.1 lotame.nexac.com 127.0.0.1 loudgames.com @@ -30844,6 +32355,7 @@ 127.0.0.1 lp.bigfarm.goodgamestudios.com 127.0.0.1 lp.callandput.com 127.0.0.1 lp.empire.goodgamestudios.com +127.0.0.1 lp.funtapps.com 127.0.0.1 lp.koramgame.com 127.0.0.1 lp.lol55.com 127.0.0.1 lp.longtailvideo.com @@ -30857,33 +32369,53 @@ 127.0.0.1 lp.webgame.web.id 127.0.0.1 lpm-20minutes.nuggad.net 127.0.0.1 lptag.liveperson.net +127.0.0.1 lpvzq.voluumtrk.com +127.0.0.1 lqckn.voluumtrk.com +127.0.0.1 lqhoy.voluumtrk.com +127.0.0.1 lqipx.voluumtrk.com +127.0.0.1 lrpush.apxlv.com +127.0.0.1 lrpush.cogocast.net +127.0.0.1 lrzxk.voluumtrk.com 127.0.0.1 ls.ezakus.net 127.0.0.1 ls.nexon.net 127.0.0.1 lsawards.com +127.0.0.1 lsegt.voluumtrk.com 127.0.0.1 lslmetrics.djlmgdigital.com +127.0.0.1 lsut8.voluumtrk.com 127.0.0.1 lt.andomedia.com 127.0.0.1 lt.retargeter.com 127.0.0.1 ltassrv.com.s3.amazonaws.com +127.0.0.1 ltljm.voluumtrk.com 127.0.0.1 ltur.112.2o7.net 127.0.0.1 ltur.ivwbox.de 127.0.0.1 lucky.netmng.com 127.0.0.1 luckyorange.com +127.0.0.1 lugiy.ru 127.0.0.1 luisa-hayden.us 127.0.0.1 luisa-rester.us +127.0.0.1 lumdf.voluumtrk.com 127.0.0.1 luminate.com 127.0.0.1 lunafetch.about.com 127.0.0.1 lunametrics.wpengine.netdna-cdn.com 127.0.0.1 lunarads.com 127.0.0.1 lunho.com +127.0.0.1 lup5z.voluumtrk.com 127.0.0.1 luu.lightquartrate.com 127.0.0.1 luvianetwork.hasoffers.com +127.0.0.1 lvler.voluumtrk.com +127.0.0.1 lvtxg.voluumtrk.com 127.0.0.1 lw1.cdmediaworld.com +127.0.0.1 lwfmc.voluumtrk.com 127.0.0.1 lwken.com +127.0.0.1 lxa6d.voluumtrk.com +127.0.0.1 lxpwv.voluumtrk.com +127.0.0.1 lyaxz.voluumtrk.com 127.0.0.1 lycos-eu.imrworldwide.com 127.0.0.1 lycos.247realmedia.com 127.0.0.1 lycoscollect.247realmedia.com 127.0.0.1 lycoscollect.realmedia.com 127.0.0.1 lydia-birnbaum.us +127.0.0.1 lyikl.voluumtrk.com 127.0.0.1 lynda-malchow.us 127.0.0.1 lyndsay-lambeth.us 127.0.0.1 lyndsey-hansley.us @@ -30894,7 +32426,10 @@ 127.0.0.1 lynsey-mabon.us 127.0.0.1 lynxtrack.com 127.0.0.1 lysoft.go2cloud.org +127.0.0.1 lyyy6.voluumtrk.com 127.0.0.1 lzonline.ivwbox.de +127.0.0.1 lzukp.voluumtrk.com +127.0.0.1 lzxrz.net 127.0.0.1 m.2mdn.net 127.0.0.1 m.ad6media.fr 127.0.0.1 m.addthis.com @@ -30903,6 +32438,7 @@ 127.0.0.1 m.adsymptotic.com 127.0.0.1 m.adtc.daum.net 127.0.0.1 m.astrokolik.com +127.0.0.1 m.awesometracker.com 127.0.0.1 m.banner.linksynergy.com 127.0.0.1 m.banner.t-online.de 127.0.0.1 m.benaughty.com @@ -30912,6 +32448,7 @@ 127.0.0.1 m.doubleclick.net 127.0.0.1 m.duba.net 127.0.0.1 m.exactag.com +127.0.0.1 m.exovueplatform.com 127.0.0.1 m.friendlyduck.com 127.0.0.1 m.leadbolt.com 127.0.0.1 m.livejasmin.com @@ -30938,6 +32475,8 @@ 127.0.0.1 m.zeroredirect.com 127.0.0.1 m.zeroredirect1.com 127.0.0.1 m.zeroredirect2.com +127.0.0.1 m08hh.voluumtrk.com +127.0.0.1 m0zl8.voluumtrk.com 127.0.0.1 m1.2mdn.net 127.0.0.1 m1.webstats4u.com 127.0.0.1 m12n.servebom.com @@ -30951,21 +32490,31 @@ 127.0.0.1 m2m1.inner-active.mobi 127.0.0.1 m3.usnews.com 127.0.0.1 m3.webstats.motigo.com +127.0.0.1 m3jok.voluumtrk.com 127.0.0.1 m4.webstats.motigo.com 127.0.0.1 m4.zedo.com +127.0.0.1 m43fw.voluumtrk.com 127.0.0.1 m4n.nl 127.0.0.1 m5.webstats.motigo.com 127.0.0.1 m6.webstats.motigo.com 127.0.0.1 m7.webstats.motigo.com 127.0.0.1 m8.webstats.motigo.com 127.0.0.1 m9.webstats.motigo.com +127.0.0.1 m9qi1.voluumtrk.com 127.0.0.1 m9zzz.survey7.adsservingtwig.xyz 127.0.0.1 ma.inmobi.com 127.0.0.1 ma.tinyflashlight.com 127.0.0.1 ma156-r.analytics.edgesuite.net +127.0.0.1 ma180-r.analytics.edgekey.net +127.0.0.1 ma180-r.analytics.edgesuite.net 127.0.0.1 ma277-r.analytics.edgesuite.net +127.0.0.1 ma728-r.analytics.edgekey.net +127.0.0.1 ma9jh.voluumtrk.com +127.0.0.1 mabping.chartbeat.net +127.0.0.1 mac-system-alert.com 127.0.0.1 mackenzie-brand.us 127.0.0.1 macwelt.ivwbox.de +127.0.0.1 mad2.brandreachsys.com 127.0.0.1 madadsmedia.com 127.0.0.1 madas-ads.securest.org 127.0.0.1 madbidcom.go2cloud.org @@ -30977,6 +32526,7 @@ 127.0.0.1 madmimi.com 127.0.0.1 madnet.ru 127.0.0.1 mads.at.atwola.com +127.0.0.1 mads.bz 127.0.0.1 mads.cnet.com 127.0.0.1 mads.com.com 127.0.0.1 mads.gamespot.com @@ -30985,6 +32535,7 @@ 127.0.0.1 madsone.com 127.0.0.1 madsuk.aol.com 127.0.0.1 madsuk.at.atwola.com +127.0.0.1 maeas.voluumtrk.com 127.0.0.1 maedge.com 127.0.0.1 magdalena-pollak.us 127.0.0.1 magentanews.com @@ -31013,6 +32564,7 @@ 127.0.0.1 mailb1.surf-town.net 127.0.0.1 mailb2.surf-town.net 127.0.0.1 mailb3.surf-town.net +127.0.0.1 mailderef.web.de 127.0.0.1 mailer.livejasmin.com 127.0.0.1 mailer.p1ne.com 127.0.0.1 mailinb1.surf-town.net @@ -31048,6 +32600,7 @@ 127.0.0.1 map.media6degrees.com 127.0.0.1 map.pop6.com 127.0.0.1 map.popunderz.com +127.0.0.1 map.sddan.com 127.0.0.1 map1.adsniper.ru 127.0.0.1 map2.adsniper.ru 127.0.0.1 mapi.tapreason.com @@ -31116,7 +32669,9 @@ 127.0.0.1 marlene-baley.us 127.0.0.1 marlene-hunsinger.us 127.0.0.1 marsha-hoppe.us +127.0.0.1 marsoffset.goforandroid.com 127.0.0.1 marta-kenny.us +127.0.0.1 marteau.ovh 127.0.0.1 mary-marcellus.us 127.0.0.1 mary-omeara.us 127.0.0.1 mash.network.coull.com @@ -31166,12 +32721,16 @@ 127.0.0.1 maxspeedcdn.com 127.0.0.1 maxtrust.ru 127.0.0.1 maxvr.112.2o7.net +127.0.0.1 mazetin.ru +127.0.0.1 mb.datingadzone.com 127.0.0.1 mb.gammae.com 127.0.0.1 mb.hockeybuzz.com 127.0.0.1 mb.marathonbet.com 127.0.0.1 mb.zam.com 127.0.0.1 mb01.com 127.0.0.1 mb104.com +127.0.0.1 mb871.voluumtrk.com +127.0.0.1 mbjqc.voluumtrk.com 127.0.0.1 mbotvisit.com 127.0.0.1 mbox12.offermatica.com 127.0.0.1 mbox12e.offermatica.com @@ -31192,12 +32751,15 @@ 127.0.0.1 mclient2.ivwbox.de 127.0.0.1 mcmads.mediacapital.pt 127.0.0.1 mcmoby.go2cloud.org +127.0.0.1 mcp8f.voluumtrk.com 127.0.0.1 mcpatrack.tv +127.0.0.1 mcpaz.voluumtrk.com 127.0.0.1 mcs.delvenetworks.com 127.0.0.1 mcs.mlmwebtracker.com 127.0.0.1 mcssl.com 127.0.0.1 mctracking.go2cloud.org 127.0.0.1 mdamarillo.112.2o7.net +127.0.0.1 mdataroom.com 127.0.0.1 mdjacksonville.112.2o7.net 127.0.0.1 mdlinx.app4.hubspot.com 127.0.0.1 mdmserver.mobidia.com @@ -31205,6 +32767,7 @@ 127.0.0.1 mdn2.phluantmobile.net 127.0.0.1 mdn3.phluantmobile.net 127.0.0.1 mdn3origin.phluantmobile.net +127.0.0.1 mdqc2.voluumtrk.com 127.0.0.1 mdwjuneau.122.2o7.net 127.0.0.1 me-cdn.effectivemeasure.net 127.0.0.1 meagan-josephs.us @@ -31238,6 +32801,7 @@ 127.0.0.1 media.admob.com 127.0.0.1 media.adrevolver.com 127.0.0.1 media.alphaload.com +127.0.0.1 media.brandreachsys.com 127.0.0.1 media.campartner.com 127.0.0.1 media.carpediem.fr 127.0.0.1 media.contextweb.com @@ -31258,8 +32822,10 @@ 127.0.0.1 media.go2speed.org 127.0.0.1 media.gsimedia.net 127.0.0.1 media.hasoffers.com +127.0.0.1 media.justad.mobi 127.0.0.1 media.l3.cdn.adbucks.com 127.0.0.1 media.likes.com +127.0.0.1 media.livepromotools.com 127.0.0.1 media.markethealth.com 127.0.0.1 media.match.com 127.0.0.1 media.mgnetwork.com @@ -31287,6 +32853,7 @@ 127.0.0.1 media.superstats.com 127.0.0.1 media.syracuse.com 127.0.0.1 media.tipjunkie.com +127.0.0.1 media.trafficfactory.biz 127.0.0.1 media.trafficjunky.net 127.0.0.1 media.ym0.net 127.0.0.1 media1.adsoftware.com @@ -31341,6 +32908,7 @@ 127.0.0.1 megan-farrier.us 127.0.0.1 megan-schank.us 127.0.0.1 megan-stagner.us +127.0.0.1 megapopads.com 127.0.0.1 megastore.bnex.com 127.0.0.1 melisa-espino.us 127.0.0.1 melisa-traynor.us @@ -31350,6 +32918,7 @@ 127.0.0.1 melissa-sakamoto.us 127.0.0.1 mellissa-lack.us 127.0.0.1 mellissa-sandford.us +127.0.0.1 mellowads.com 127.0.0.1 meltingpoint.com 127.0.0.1 meltwater.com 127.0.0.1 meltwaternews.com @@ -31363,17 +32932,23 @@ 127.0.0.1 memorix.sdv.fr 127.0.0.1 menato.ru 127.0.0.1 meraad2.blogspot.com +127.0.0.1 meraxes-cdn.polarmobile.com 127.0.0.1 merchant.com 127.0.0.1 merchant.linksynergy.com 127.0.0.1 merchant.myofferpal.com +127.0.0.1 merchant.wgiftcard.com 127.0.0.1 meshbean.com 127.0.0.1 mesia.com 127.0.0.1 messagia.adcentric.proximi-t.com +127.0.0.1 mesurer.ovh 127.0.0.1 met.adwhirl.com 127.0.0.1 meta.7search.com +127.0.0.1 meta.streamcloud.eu 127.0.0.1 metacount.com +127.0.0.1 metal.ovh 127.0.0.1 metartmoney.met-art.com 127.0.0.1 meter.2cnt.net +127.0.0.1 metre.ovh 127.0.0.1 metric.federalnewsradio.com 127.0.0.1 metric.ind.rediff.com 127.0.0.1 metric.inetcore.com @@ -31385,11 +32960,13 @@ 127.0.0.1 metrica.yandex.kz 127.0.0.1 metrica.yandex.ru 127.0.0.1 metrica.yandex.ua +127.0.0.1 metricology.com 127.0.0.1 metrics.active.com 127.0.0.1 metrics.ambient-platform.com 127.0.0.1 metrics.apple.com 127.0.0.1 metrics.att.com 127.0.0.1 metrics.brightcove.com +127.0.0.1 metrics.cnn.com 127.0.0.1 metrics.codahale.com 127.0.0.1 metrics.comcast.com 127.0.0.1 metrics.directv.com @@ -31409,6 +32986,7 @@ 127.0.0.1 metrics.ryanair.com 127.0.0.1 metrics.scribblelive.com 127.0.0.1 metrics.seenon.com +127.0.0.1 metrics.skype.com 127.0.0.1 metrics.sonymusicd2c.com 127.0.0.1 metrics.sourceforge.net 127.0.0.1 metrics.ted.com @@ -31431,14 +33009,19 @@ 127.0.0.1 mg.dt00.net 127.0.0.1 mg.dt07.net 127.0.0.1 mg.mgid.com +127.0.0.1 mgage.com +127.0.0.1 mgbhr.voluumtrk.com 127.0.0.1 mgcash.com 127.0.0.1 mgcashgate.com 127.0.0.1 mgid.com 127.0.0.1 mgjournalnow.112.2o7.net 127.0.0.1 mgtbo.112.2o7.net 127.0.0.1 mgtimesdispatch.112.2o7.net +127.0.0.1 mgutu.voluumtrk.com 127.0.0.1 mgwnct.112.2o7.net 127.0.0.1 mgwsls.112.2o7.net +127.0.0.1 mgxqs.voluumtrk.com +127.0.0.1 mha4c.voluumtrk.com 127.0.0.1 mi-web10.prod.millennialmedia.com 127.0.0.1 mi-web11.prod.millennialmedia.com 127.0.0.1 mi-web12.prod.millennialmedia.com @@ -31473,10 +33056,12 @@ 127.0.0.1 mi-web42.prod.millennialmedia.com 127.0.0.1 mi-web43.prod.millennialmedia.com 127.0.0.1 mi.adinterax.com +127.0.0.1 mi.gdt.qq.com 127.0.0.1 mia-fricks.us 127.0.0.1 miami.ero-advertising.com 127.0.0.1 michelle-laird.us 127.0.0.1 micro.marketo.com +127.0.0.1 microad.net 127.0.0.1 microreporting.metro.se 127.0.0.1 microreporting.metrofrance.com 127.0.0.1 microreporting.metronews.ru @@ -31498,6 +33083,7 @@ 127.0.0.1 mildred-patrick.us 127.0.0.1 milesaway.oracle.cpa.clicksure.com 127.0.0.1 milira.justclick.ru +127.0.0.1 millennialmedia.com 127.0.0.1 millennyadv.go2cloud.org 127.0.0.1 million.francite.com 127.0.0.1 millionaire.go2cloud.org @@ -31507,6 +33093,8 @@ 127.0.0.1 mindy-sawyer.us 127.0.0.1 mint.boingboing.net 127.0.0.1 mint.good.is +127.0.0.1 mintake.com +127.0.0.1 miov5.voluumtrk.com 127.0.0.1 mirgadaniy.justclick.ru 127.0.0.1 mirror1.surf-town.net 127.0.0.1 mirror3.filefacts.com @@ -31516,9 +33104,16 @@ 127.0.0.1 misti-smythe.us 127.0.0.1 misti-weigel.us 127.0.0.1 mitel.marketbright.com +127.0.0.1 mixadvert.com 127.0.0.1 mixpanel.com 127.0.0.1 mixrank.go2cloud.org +127.0.0.1 mjjjr.voluumtrk.com +127.0.0.1 mjov7.voluumtrk.com +127.0.0.1 mjvns.voluumtrk.com 127.0.0.1 mjx.ads.nwsource.com +127.0.0.1 mkgcp.voluumtrk.com +127.0.0.1 mkgod.voluumtrk.com +127.0.0.1 mkin4.voluumtrk.com 127.0.0.1 mkpri3fasq4cn5.ru 127.0.0.1 mktg.actonsoftware.com 127.0.0.1 mkto-c0059.com @@ -31529,15 +33124,18 @@ 127.0.0.1 mlinktracker.com 127.0.0.1 mlogin.linksynergy.com 127.0.0.1 mlook.mcdstorage.com +127.0.0.1 mlvrp.voluumtrk.com 127.0.0.1 mlweb.dmlab.hu 127.0.0.1 mm.777-partner.com 127.0.0.1 mm.777-partners.net 127.0.0.1 mm.chitika.net 127.0.0.1 mmftpf.com 127.0.0.1 mmi.bemobile.ua +127.0.0.1 mmnetwork.mobi 127.0.0.1 mmotraffic.com 127.0.0.1 mmpstats.mirror-image.com 127.0.0.1 mmtracking.com +127.0.0.1 mmtrkdb.com 127.0.0.1 mmtrkmc.com 127.0.0.1 mnetads.net 127.0.0.1 mngiangibabest.112.2o7.net @@ -31548,11 +33146,13 @@ 127.0.0.1 mno2.hairg.biz 127.0.0.1 mno3.hairg.biz 127.0.0.1 mno4.hairg.biz -127.0.0.1 mnstat.com 127.0.0.1 mntr.babcdn.com 127.0.0.1 moadnet.com 127.0.0.1 mob.adwhirl.com 127.0.0.1 mob.tictacti.com +127.0.0.1 moba8.net +127.0.0.1 mobad.ijinshan.com +127.0.0.1 mobads.baidu.com 127.0.0.1 mobaloo.go2cloud.org 127.0.0.1 mobbridge.mobpartner.mobi 127.0.0.1 mobcdn.com @@ -31560,11 +33160,13 @@ 127.0.0.1 mobfox.com 127.0.0.1 mobgold.com 127.0.0.1 mobhero.com +127.0.0.1 mobi-mobi.info 127.0.0.1 mobi.mobijo.tv 127.0.0.1 mobi.pornhubpremium.com 127.0.0.1 mobi.yanosik.pl 127.0.0.1 mobiads.ru 127.0.0.1 mobiaffiliatenetwork.go2cloud.org +127.0.0.1 mobiappsclub.com 127.0.0.1 mobidia.com 127.0.0.1 mobilcom.ivwbox.de 127.0.0.1 mobilda.com @@ -31572,6 +33174,7 @@ 127.0.0.1 mobile-ads.wifog.com 127.0.0.1 mobile-collector.newrelic.com 127.0.0.1 mobile-ent.biz +127.0.0.1 mobile.ad-tracker.ch 127.0.0.1 mobile.adfarm1.adition.com 127.0.0.1 mobile.adnxs.com 127.0.0.1 mobile.adperium.com @@ -31583,6 +33186,7 @@ 127.0.0.1 mobile.plugrush.com 127.0.0.1 mobile.traffic-tracker.net 127.0.0.1 mobile18.in.com +127.0.0.1 mobile333.com 127.0.0.1 mobileactive.com 127.0.0.1 mobileads.ero-advertising.com 127.0.0.1 mobileads.mobilebanner.net @@ -31604,6 +33208,7 @@ 127.0.0.1 mobiletest.2cnt.net 127.0.0.1 mobiletest2.2cnt.net 127.0.0.1 mobiletheory.com +127.0.0.1 mobileweb.api.airpush.com 127.0.0.1 mobilewithsms.net 127.0.0.1 mobilitysite.us.intellitxt.com 127.0.0.1 mobisystems.com @@ -31616,6 +33221,7 @@ 127.0.0.1 mobooka.hasoffers.com 127.0.0.1 mobpartner.com 127.0.0.1 mobpartner.mobi +127.0.0.1 mobred.net 127.0.0.1 mobscout-stage.dus.vmsn.de 127.0.0.1 mobscout.dus.vmsn.de 127.0.0.1 mobstac.com @@ -31624,18 +33230,25 @@ 127.0.0.1 mobwall.mobpartner.com 127.0.0.1 mobyt.com 127.0.0.1 moceanmobile.com +127.0.0.1 modbm.voluumtrk.com 127.0.0.1 modelatos.com 127.0.0.1 modemspeedbooster.com 127.0.0.1 moderninstalls.go2cloud.org 127.0.0.1 moe.jlist.com +127.0.0.1 mofox.com 127.0.0.1 mogreet.com 127.0.0.1 mojiva.com +127.0.0.1 mojiva2-13fc.kxcdn.com 127.0.0.1 mojofarm.mediaplex.com 127.0.0.1 mojoworks.mediaplex.com 127.0.0.1 mojoworks.snv.mediaplex.com 127.0.0.1 mokono.com 127.0.0.1 mollie-patino.us 127.0.0.1 mom.freelogs.com +127.0.0.1 mon.0f8.cdnfarm18.com +127.0.0.1 mon.2os.cdnfarm18.com +127.0.0.1 mon.2yf.cdnfarm18.com +127.0.0.1 mon.cdnfarm18.com 127.0.0.1 mondrian.twyn.com 127.0.0.1 monespec.ivwbox.de 127.0.0.1 moneta.trove.com @@ -31671,6 +33284,7 @@ 127.0.0.1 monsterpops.com 127.0.0.1 moo.go2cloud.org 127.0.0.1 mopo.ivwbox.de +127.0.0.1 mopub.web107-east.manage.com 127.0.0.1 more-games.creative-mobile.com 127.0.0.1 morehitserver.com 127.0.0.1 moreover.com @@ -31694,6 +33308,7 @@ 127.0.0.1 moviepulp.eu 127.0.0.1 moz.ivwbox.de 127.0.0.1 mozo-widgets.f2.com.au +127.0.0.1 mozoo.com 127.0.0.1 mp-success.com 127.0.0.1 mp.apmebf.com 127.0.0.1 mp.clicksor.net @@ -31706,14 +33321,21 @@ 127.0.0.1 mproxy.banner.linksynergy.com 127.0.0.1 mprptrk.com 127.0.0.1 mpsnare.iesnare.com +127.0.0.1 mq98e.voluumtrk.com +127.0.0.1 mqo7a.voluumtrk.com +127.0.0.1 mqot2.voluumtrk.com 127.0.0.1 mqs.ioam.de 127.0.0.1 mraidjs.adtilt.com +127.0.0.1 mrapq.voluumtrk.com 127.0.0.1 mrchewy.go2cloud.org 127.0.0.1 mreport.linksynergy.com 127.0.0.1 mrktrecord3.com +127.0.0.1 mrskin.netmng.com 127.0.0.1 mrss.ads.moguldom.com 127.0.0.1 mrtg.hitbox.com +127.0.0.1 mrwjy.voluumtrk.com 127.0.0.1 ms-links.com +127.0.0.1 ms49r.voluumtrk.com 127.0.0.1 msbmopod.vo.llnwd.net 127.0.0.1 msbrandent.vo.llnwd.net 127.0.0.1 msdbb1.surf-town.net @@ -31725,6 +33347,7 @@ 127.0.0.1 msgui.go2cloud.org 127.0.0.1 mshakers.rotator.hadj7.adjuggler.net 127.0.0.1 msimg.com +127.0.0.1 msjil.voluumtrk.com 127.0.0.1 msn.careerbuilder.de 127.0.0.1 msn.rce.veeseo.com 127.0.0.1 msn4adults.com @@ -31748,8 +33371,11 @@ 127.0.0.1 mssqladmin.surf-town.net 127.0.0.1 mstadt.ivwbox.de 127.0.0.1 mswgaprod.112.2o7.net +127.0.0.1 mswtg.voluumtrk.com 127.0.0.1 msxml.webcrawler.com 127.0.0.1 mt.sellingrealestatemalta.com +127.0.0.1 mtburn.com +127.0.0.1 mtburn.jp 127.0.0.1 mtcount.channeladvisor.com 127.0.0.1 mtkzz.survey7.adsservingtwig.xyz 127.0.0.1 mto.mediatakeout.com @@ -31757,9 +33383,14 @@ 127.0.0.1 mtree.com 127.0.0.1 mtrx.go.sonobi.com 127.0.0.1 mts.mansion.com +127.0.0.1 mtsd9.voluumtrk.com +127.0.0.1 mttwtrack.com 127.0.0.1 mtv.ivwbox.de +127.0.0.1 mtvn.demdex.net 127.0.0.1 mtvnetworks.tt.omtrdc.net +127.0.0.1 muas4.voluumtrk.com 127.0.0.1 muchmarketing.go2cloud.org +127.0.0.1 muesq.voluumtrk.com 127.0.0.1 multi.xnxx.com 127.0.0.1 multicounter.de 127.0.0.1 multimania.com @@ -31770,30 +33401,40 @@ 127.0.0.1 musculahq.appspot.com 127.0.0.1 musecdn.businesscatalyst.com 127.0.0.1 musiccounter.ru +127.0.0.1 mv6we.voluumtrk.com +127.0.0.1 mvexg.voluumtrk.com +127.0.0.1 mvldn.voluumtrk.com +127.0.0.1 mvpxv.voluumtrk.com 127.0.0.1 mvtracker.com 127.0.0.1 mwc.velti.com 127.0.0.1 mx.adserver.yahoo.com 127.0.0.1 mx.ero-advertising.com 127.0.0.1 mx.yandex.ru 127.0.0.1 mx1.revsci.net +127.0.0.1 mx3ih.voluumtrk.com 127.0.0.1 mxmacromedia.112.2o7.net 127.0.0.1 my-etracker.com +127.0.0.1 my-linker.com 127.0.0.1 my-tds.net 127.0.0.1 my.365adsolutions.com 127.0.0.1 my.aim4media.com 127.0.0.1 my.applifier.com 127.0.0.1 my.blueadvertise.com +127.0.0.1 my.hellobar.com 127.0.0.1 my.leadpages.net 127.0.0.1 my.media-servers.net 127.0.0.1 my.mobfox.com 127.0.0.1 my.omniture.com +127.0.0.1 my.plexapp.com 127.0.0.1 my.skyhookwireless.com +127.0.0.1 my.xadsales.com 127.0.0.1 my.yieldmanager.com 127.0.0.1 myad.no 127.0.0.1 myadmarket.com 127.0.0.1 myadpromoter.com 127.0.0.1 myads.newads.com 127.0.0.1 myadserve.s3-website-us-east-1.amazonaws.com +127.0.0.1 myadvertisingpays.com 127.0.0.1 myaffiliateads.com 127.0.0.1 myaffiliateprogram.com 127.0.0.1 mybb.ero-advertising.com @@ -31825,6 +33466,7 @@ 127.0.0.1 myimagetracking.com 127.0.0.1 mylistenetwork.go2cloud.org 127.0.0.1 mylottoadserv.com +127.0.0.1 mymediadownloadsthirtytwo.com 127.0.0.1 myns-v1.websys.aol.com 127.0.0.1 mypoints.com 127.0.0.1 mypopup.ir @@ -31871,17 +33513,24 @@ 127.0.0.1 mytds.pr.vc 127.0.0.1 mytiwi.com 127.0.0.1 mytoday.ivwbox.de +127.0.0.1 mytogolinks.com 127.0.0.1 mytrafficbuilder.info 127.0.0.1 mytrannycams.com 127.0.0.1 myvacationguide.com 127.0.0.1 mywactrack.com 127.0.0.1 mywebstats.com.au 127.0.0.1 mz28ismn.com +127.0.0.1 mzqtm.voluumtrk.com +127.0.0.1 mzrvi.voluumtrk.com 127.0.0.1 mzweb.ivwbox.de +127.0.0.1 m_graph.vidpark.com 127.0.0.1 n-tv.de.intellitxt.com 127.0.0.1 n-tv.met.vgwort.de +127.0.0.1 n.bodybuilding.com +127.0.0.1 n.gemini.yahoo.com 127.0.0.1 n.ladycash.ru 127.0.0.1 n.pay-click.ru +127.0.0.1 n.targetdtracker.com 127.0.0.1 n.uimserv.net 127.0.0.1 n.zeroredirect.com 127.0.0.1 n.zeroredirect1.com @@ -31891,9 +33540,14 @@ 127.0.0.1 n1.nedstatbasic.net 127.0.0.1 n10.adshostnet.com 127.0.0.1 n2.adshostnet.com +127.0.0.1 n21o1.voluumtrk.com 127.0.0.1 n24mobse.nuggad.net +127.0.0.1 n2m1z.voluumtrk.com 127.0.0.1 n3.adshostnet.com +127.0.0.1 n32o1.voluumtrk.com 127.0.0.1 n388hkxg.com +127.0.0.1 n3hps.voluumtrk.com +127.0.0.1 n3qcp.voluumtrk.com 127.0.0.1 n4.adshostnet.com 127.0.0.1 n4052ad.doubleclick.net 127.0.0.1 n4403ad.doubleclick.net @@ -31905,6 +33559,7 @@ 127.0.0.1 n69.com 127.0.0.1 n7.adshostnet.com 127.0.0.1 n72.adshostnet.com +127.0.0.1 n7yk4.voluumtrk.com 127.0.0.1 n8.adshostnet.com 127.0.0.1 n9.adshostnet.com 127.0.0.1 n94.adshostnet.com @@ -31912,11 +33567,14 @@ 127.0.0.1 na-sjn.marketo.com 127.0.0.1 nagios.adition.com 127.0.0.1 naj.sk +127.0.0.1 nam11.voluumtrk.com 127.0.0.1 namestation.tenderapp.com 127.0.0.1 nancy-mosca.us 127.0.0.1 nandp.go2cloud.org +127.0.0.1 nanigans.com 127.0.0.1 nanostats.nanopress.it 127.0.0.1 naomi-thorn.us +127.0.0.1 napster.searchwho.com 127.0.0.1 nastydollars.com 127.0.0.1 nata.ero-advertising.com 127.0.0.1 natalia-dimick.us @@ -31927,7 +33585,9 @@ 127.0.0.1 natgeoedit.112.2o7.net 127.0.0.1 natgeoeditcom.112.2o7.net 127.0.0.1 nationalpost.112.2o7.net +127.0.0.1 native.ai 127.0.0.1 native.sharethrough.com +127.0.0.1 nativeads.com 127.0.0.1 nativitystones.go2cloud.org 127.0.0.1 nats.hushmoney.com 127.0.0.1 naturalsearchtoolresults.com @@ -31942,9 +33602,21 @@ 127.0.0.1 nbasic.sitestat.com 127.0.0.1 nbimg.dt00.net 127.0.0.1 nbjmp.com +127.0.0.1 nbnkg.voluumtrk.com +127.0.0.1 nbsmr.voluumtrk.com +127.0.0.1 nc0vx.voluumtrk.com +127.0.0.1 ncfae.voluumtrk.com +127.0.0.1 ncmzm.voluumtrk.com +127.0.0.1 nczgs.voluumtrk.com +127.0.0.1 ndirect.ppro.de +127.0.0.1 ndjln.voluumtrk.com +127.0.0.1 ndk38.voluumtrk.com 127.0.0.1 ndoverdrive.net 127.0.0.1 ndparking.com +127.0.0.1 ndwck.voluumtrk.com +127.0.0.1 nearbyad.com 127.0.0.1 neber.112.2o7.net +127.0.0.1 neblotech.com 127.0.0.1 ned.itv.com 127.0.0.1 nedstat.co.uk 127.0.0.1 nedstat.com @@ -31962,6 +33634,7 @@ 127.0.0.1 neptune1.appads.com 127.0.0.1 neptune2.appads.com 127.0.0.1 neptune3.appads.com +127.0.0.1 nesql.voluumtrk.com 127.0.0.1 net-france.com 127.0.0.1 net.29193.9215.302br.net 127.0.0.1 net.29674.9239.302br.net @@ -32032,6 +33705,7 @@ 127.0.0.1 network22.com 127.0.0.1 networkadvertising.org 127.0.0.1 networkcommerce.com +127.0.0.1 networkpb.com 127.0.0.1 networksolutionsaffiliates.com 127.0.0.1 netzathleten-media.de 127.0.0.1 netzathleten.net @@ -32071,6 +33745,7 @@ 127.0.0.1 nexage.com 127.0.0.1 nextlevel.com 127.0.0.1 nexus.adspirit.de +127.0.0.1 nexus.ensighten.com 127.0.0.1 nexusads.adspirit.de 127.0.0.1 nexzenpro.go2cloud.org 127.0.0.1 nfbal.trackvoluum.com @@ -32082,18 +33757,31 @@ 127.0.0.1 ngbn.net 127.0.0.1 ngohq.us.intellitxt.com 127.0.0.1 ngs.impress.co.jp +127.0.0.1 ngxsx.voluumtrk.com +127.0.0.1 nhhr6.voluumtrk.com 127.0.0.1 nht-2.extreme-dm.com 127.0.0.1 ni3.imlive.com +127.0.0.1 ni8bd.voluumtrk.com 127.0.0.1 nicolette-brier.us 127.0.0.1 nicolette-salas.us 127.0.0.1 nid.timesink.com 127.0.0.1 nidinternal.timesink.com 127.0.0.1 nidtest.timesink.com +127.0.0.1 nifvd.voluumtrk.com 127.0.0.1 nikole-villalba.us +127.0.0.1 ninavyg.ru 127.0.0.1 ninemsn.imrworldwide.com 127.0.0.1 nip.net 127.0.0.1 nitroclicks.com 127.0.0.1 njashka.ru +127.0.0.1 njfy6.voluumtrk.com +127.0.0.1 njump.youmobistein.com +127.0.0.1 njwol.voluumtrk.com +127.0.0.1 nk.brandreachsys.com +127.0.0.1 nkcache.brandreachsys.com +127.0.0.1 nkf3i.voluumtrk.com +127.0.0.1 nkqgy.voluumtrk.com +127.0.0.1 nkyrh.voluumtrk.com 127.0.0.1 nl.adserver.yahoo.com 127.0.0.1 nl.count.worldstats.com 127.0.0.1 nl.cqcounter.com @@ -32101,22 +33789,31 @@ 127.0.0.1 nl.nedstatpro.net 127.0.0.1 nl.sitestat.com 127.0.0.1 nl.topstat.com +127.0.0.1 nl1.ero-advertising.com 127.0.0.1 nl2.ero-advertising.com 127.0.0.1 nlbanner.nl +127.0.0.1 nlhra.voluumtrk.com 127.0.0.1 nm.netmng.com +127.0.0.1 nmaio.voluumtrk.com 127.0.0.1 nmanchorage.112.2o7.net 127.0.0.1 nmcommancomedia.112.2o7.net 127.0.0.1 nmeprod.122.2o7.net 127.0.0.1 nmhiltonhead.112.2o7.net 127.0.0.1 nmkawartha.112.2o7.net 127.0.0.1 nmminneapolis.112.2o7.net +127.0.0.1 nmmua.voluumtrk.com 127.0.0.1 nmnandomedia.112.2o7.net +127.0.0.1 nmqtp.voluumtrk.com 127.0.0.1 nmraleigh.112.2o7.net 127.0.0.1 nmsacramento.112.2o7.net +127.0.0.1 nmt5m.voluumtrk.com 127.0.0.1 nmtracking.netflix.com 127.0.0.1 nmyork.112.2o7.net +127.0.0.1 nn7la.voluumtrk.com 127.0.0.1 nners.ero-advertising.com +127.0.0.1 nnfku.voluumtrk.com 127.0.0.1 nnlb.go2cloud.org +127.0.0.1 nno9c.voluumtrk.com 127.0.0.1 no-stress.justclick.ru 127.0.0.1 no.2.cqcounter.com 127.0.0.1 no.adserver.yahoo.com @@ -32133,6 +33830,7 @@ 127.0.0.1 nodes.double6affiliation.com 127.0.0.1 nodes.r66t.com 127.0.0.1 noelle-traxler.us +127.0.0.1 nokby.voluumtrk.com 127.0.0.1 nomen-est-omen.de 127.0.0.1 nonames.tk 127.0.0.1 noowho.com @@ -32143,13 +33841,18 @@ 127.0.0.1 nova.dice.net 127.0.0.1 novascotianetworks.go2cloud.org 127.0.0.1 novellcom.112.2o7.net +127.0.0.1 novostimira.biz 127.0.0.1 now.eloqua.com 127.0.0.1 nowlucky.go2cloud.org 127.0.0.1 np.lexity.com 127.0.0.1 npdor.go2cloud.org +127.0.0.1 npoyv.voluumtrk.com +127.0.0.1 npvnf.voluumtrk.com 127.0.0.1 npvos.com +127.0.0.1 nqi5j.voluumtrk.com 127.0.0.1 nrgdigital.checkm8.com 127.0.0.1 nrj.ivwbox.de +127.0.0.1 nry5r.voluumtrk.com 127.0.0.1 ns.2cnt.net 127.0.0.1 ns.ivwbox.de 127.0.0.1 ns.mediaplex.com @@ -32193,6 +33896,7 @@ 127.0.0.1 ns2.skyhookwireless.com 127.0.0.1 ns2.surf-town.net 127.0.0.1 ns2.w3open.com +127.0.0.1 ns24i.voluumtrk.com 127.0.0.1 ns3.adition.com 127.0.0.1 ns3.adsender.us 127.0.0.1 ns3.datapipe.net @@ -32214,19 +33918,24 @@ 127.0.0.1 nsa.lphbs.com 127.0.0.1 nsclick.baidu.com 127.0.0.1 nsl.mapticket.net +127.0.0.1 nsmyk.voluumtrk.com 127.0.0.1 nstat.tudou.com 127.0.0.1 nstracking.nonstopdelivery.com 127.0.0.1 ntcompatible.us.intellitxt.com 127.0.0.1 nteryield.jmp9.com 127.0.0.1 ntv.ivwbox.de +127.0.0.1 nuadn.voluumtrk.com 127.0.0.1 nub9r.maisonx.com 127.0.0.1 nudge.qualaroo.com 127.0.0.1 nuera.go2cloud.org 127.0.0.1 nuggad.net 127.0.0.1 numb.hotshare.biz 127.0.0.1 nv.ad.naver.com +127.0.0.1 nv4n8.voluumtrk.com 127.0.0.1 nvk.realsecuredredirect.com +127.0.0.1 nvmuh.voluumtrk.com 127.0.0.1 nvtrak.com +127.0.0.1 nvy.sharesix.com 127.0.0.1 nw.ads.doko.jp 127.0.0.1 nwidget.networkedblogs.com 127.0.0.1 nws.naltis.com @@ -32242,6 +33951,14 @@ 127.0.0.1 nx-adv0009.247realmedia.com 127.0.0.1 nx-adv0010.247realmedia.com 127.0.0.1 nxa-ls.s3.amazonaws.com +127.0.0.1 nxeso.voluumtrk.com +127.0.0.1 nxmav.voluumtrk.com +127.0.0.1 nxref.voluumtrk.com +127.0.0.1 ny-only.outbrain.com +127.0.0.1 ny1dm.voluumtrk.com +127.0.0.1 nychi2.outbrain.com +127.0.0.1 nydwc.voluumtrk.com +127.0.0.1 nyhyz.voluumtrk.com 127.0.0.1 nym1.ib.adnxs.com 127.0.0.1 nym1.mobile.adnxs.com 127.0.0.1 nym2.ib.adnxs.com @@ -32261,20 +33978,33 @@ 127.0.0.1 nytrwilmington.112.2o7.net 127.0.0.1 nyttechnology.112.2o7.net 127.0.0.1 nztv.prod.untd.com +127.0.0.1 nzujq.voluumtrk.com 127.0.0.1 o.addthis.com 127.0.0.1 o.leadbolt.com 127.0.0.1 o.sa.aol.com 127.0.0.1 o.swisscom.ch +127.0.0.1 o.xbox.com 127.0.0.1 o.yieldsquare.com 127.0.0.1 o.zedo.com 127.0.0.1 o.zeroredirect.com 127.0.0.1 o.zeroredirect1.com 127.0.0.1 o.zeroredirect2.com 127.0.0.1 o0.winfuture.de +127.0.0.1 o11cr.voluumtrk.com +127.0.0.1 o1bxv.voluumtrk.com +127.0.0.1 o1ghd.voluumtrk.com +127.0.0.1 o1hak.voluumtrk.com 127.0.0.1 o2onbusiness.de +127.0.0.1 o3hll.voluumtrk.com +127.0.0.1 o3ozt.voluumtrk.com +127.0.0.1 o64x.voluumtrk.com +127.0.0.1 o8yrg.voluumtrk.com +127.0.0.1 oa4xu.voluumtrk.com 127.0.0.1 oad.realmedia.com 127.0.0.1 oamtrk.com +127.0.0.1 oaqkv.voluumtrk.com 127.0.0.1 oas-central.realmedia.com +127.0.0.1 oas.alltforforaldrar.se 127.0.0.1 oas.benchmark.fr 127.0.0.1 oas.foxnews.com 127.0.0.1 oas.hitbox.com @@ -32311,6 +34041,7 @@ 127.0.0.1 oascentral.abclocal.go.com 127.0.0.1 oascentral.adage.com 127.0.0.1 oascentral.adageglobal.com +127.0.0.1 oascentral.advanstar.com 127.0.0.1 oascentral.appssavvy.com.23051.9155.302br.net 127.0.0.1 oascentral.appssavvy.com.23053.9155.302br.net 127.0.0.1 oascentral.appssavvy.com.23054.9155.302br.net @@ -32374,6 +34105,7 @@ 127.0.0.1 oazzz.survey8.0236.info 127.0.0.1 oazzz.survey9.0236.info 127.0.0.1 ob1trk.com +127.0.0.1 obc99.voluumtrk.com 127.0.0.1 oberpfal.ivwbox.de 127.0.0.1 obesw.com 127.0.0.1 obgyn.us.intellitxt.com @@ -32387,15 +34119,29 @@ 127.0.0.1 oc-track.autonomycloud.com 127.0.0.1 oc.umeng.co 127.0.0.1 oc.umeng.com +127.0.0.1 oc9e5.voluumtrk.com +127.0.0.1 oca.telemetry.microsoft.com +127.0.0.1 oca.telemetry.microsoft.com.nsatc.net +127.0.0.1 ocdn.adsterra.com 127.0.0.1 oceango.net +127.0.0.1 ocio.leadzu.com 127.0.0.1 oclaserver.com 127.0.0.1 oclasrv.com +127.0.0.1 ocoym.voluumtrk.com +127.0.0.1 ocpmb.voluumtrk.com 127.0.0.1 ocs.websponsors.com +127.0.0.1 odb-chidc2.outbrain.com 127.0.0.1 odb.outbrain.com 127.0.0.1 oddcast.com 127.0.0.1 odin.goo.mx +127.0.0.1 odmdr.voluumtrk.com +127.0.0.1 odzb5nkp.com +127.0.0.1 oehui.voluumtrk.com 127.0.0.1 oekotest.ivwbox.de +127.0.0.1 oekwa.voluumtrk.com +127.0.0.1 oew7r.voluumtrk.com 127.0.0.1 oewa.oewabox.at +127.0.0.1 of3ga.voluumtrk.com 127.0.0.1 ofdtracker.com 127.0.0.1 offended.feenode.net 127.0.0.1 offer.17bullets.com @@ -32419,14 +34165,22 @@ 127.0.0.1 office.partnerearning.com 127.0.0.1 offline.adsoftware.com 127.0.0.1 offline.sanomaservices.nl +127.0.0.1 og1rc.voluumtrk.com +127.0.0.1 oggva.voluumtrk.com 127.0.0.1 ogilvy.ngadcenter.net +127.0.0.1 ogxtu.voluumtrk.com 127.0.0.1 ohlone.vizu.com +127.0.0.1 oi2cz.voluumtrk.com 127.0.0.1 oimg.m.cnbc.com 127.0.0.1 oimg.mobile.cnbc.com 127.0.0.1 oin.valuead.com 127.0.0.1 ojolink.com 127.0.0.1 ojrq.net +127.0.0.1 okohp.voluumtrk.com +127.0.0.1 okpgn.voluumtrk.com +127.0.0.1 okrg0.voluumtrk.com 127.0.0.1 oktfest.ivwbox.de +127.0.0.1 ol14x.voluumtrk.com 127.0.0.1 olddata.radarurl.com 127.0.0.1 oleg71d.justclick.ru 127.0.0.1 olegnekrom.justclick.ru @@ -32446,17 +34200,22 @@ 127.0.0.1 omg247.us4.list-manage2.com 127.0.0.1 omg247.us5.list-manage1.com 127.0.0.1 omg247.us5.list-manage2.com +127.0.0.1 omkjd.voluumtrk.com 127.0.0.1 omnaling.com 127.0.0.1 omni.canadiantire.ca 127.0.0.1 omni.finanzen100.de 127.0.0.1 omni.holidaycheck.com 127.0.0.1 omni.holidaycheck.de +127.0.0.1 omniata.com 127.0.0.1 omnistats.jetblue.com 127.0.0.1 omniture.112.207.net 127.0.0.1 omniture.112.2o7.net +127.0.0.1 omniture.chip.de 127.0.0.1 omniture.com 127.0.0.1 omniture.eaeurope.eu +127.0.0.1 omniture.mirror.co.uk 127.0.0.1 omniture.scotiabank.com +127.0.0.1 omns.americanexpress.com 127.0.0.1 oms.digitaledienste.web.de 127.0.0.1 omtrdc.net 127.0.0.1 on-js.herokuapp.com @@ -32491,15 +34250,20 @@ 127.0.0.1 onlineboss.go2cloud.org 127.0.0.1 onlineemailmarketing.com 127.0.0.1 onlineindigoca.112.2o7.net +127.0.0.1 onlinerewards.xyz 127.0.0.1 onlinetraffic.info +127.0.0.1 onlinewebfind.com 127.0.0.1 onlinsport.2cnt.net 127.0.0.1 onlysix.co.uk 127.0.0.1 onmobile.com +127.0.0.1 onshj.voluumtrk.com 127.0.0.1 onvis.ivwbox.de 127.0.0.1 onyarysh.ru 127.0.0.1 onyx.bnex.com +127.0.0.1 oobqt.voluumtrk.com 127.0.0.1 op4g.go2cloud.org 127.0.0.1 opads.ero-advertising.com +127.0.0.1 opame.voluumtrk.com 127.0.0.1 opap.co.kr 127.0.0.1 open.ad.yieldmanager.net 127.0.0.1 open.mkt1397.com @@ -32555,7 +34319,6 @@ 127.0.0.1 opera1-servedby.advertising.com 127.0.0.1 operatest.webtrekk.net 127.0.0.1 operationfabulous.com -127.0.0.1 opf.ooyala.com 127.0.0.1 ophan.guardian.co.uk 127.0.0.1 ophan.theguardian.com 127.0.0.1 opi.yahoo.com @@ -32563,6 +34326,7 @@ 127.0.0.1 opinionlabcc.122.2o7.net 127.0.0.1 opinions.research-interactive.com 127.0.0.1 opportunity-tracking.com +127.0.0.1 opqyo.voluumtrk.com 127.0.0.1 opt-media.com 127.0.0.1 opt-n.net 127.0.0.1 opt.leadbolt.com @@ -32591,11 +34355,14 @@ 127.0.0.1 optout.ad8.adfarm1.adition.com 127.0.0.1 optout.adfarm1.adition.com 127.0.0.1 optout.ivwbox.de +127.0.0.1 oqw9b.voluumtrk.com +127.0.0.1 or3v3.voluumtrk.com 127.0.0.1 oradestiri.2cnt.net 127.0.0.1 orange-fr.adinfuse.com 127.0.0.1 orangesoda.com 127.0.0.1 orangesoda.d2.sc.omtrdc.net 127.0.0.1 orangeuk-mc.adinfuse.com +127.0.0.1 orarala.com 127.0.0.1 orbitscripts.com 127.0.0.1 orbitz.tt.omtrdc.net 127.0.0.1 orders.webpower.com @@ -32604,7 +34371,10 @@ 127.0.0.1 origin.privoy.doublepimp.com 127.0.0.1 origin.zedo.com 127.0.0.1 orion.bnex.com +127.0.0.1 orlbh.voluumtrk.com +127.0.0.1 ornacorn.com 127.0.0.1 orts.wixawin.com +127.0.0.1 orw2k.voluumtrk.com 127.0.0.1 os.bororeb.com 127.0.0.1 os.filefactscdn.com 127.0.0.1 os.scmpacdn.com @@ -32613,11 +34383,14 @@ 127.0.0.1 ostfztg.ivwbox.de 127.0.0.1 ostseezt.ivwbox.de 127.0.0.1 ot.ca-mpr.jp +127.0.0.1 ot7od.voluumtrk.com +127.0.0.1 otf.msn.com 127.0.0.1 other.xxxcounter.com 127.0.0.1 otherprofit.com 127.0.0.1 otmsrv.com 127.0.0.1 otnnetwork.net 127.0.0.1 otracking.com +127.0.0.1 ots.optimize.webtrends.com 127.0.0.1 otter.topsy.com 127.0.0.1 otto.ixn.local.vmsn.de 127.0.0.1 ouah.com @@ -32644,6 +34417,7 @@ 127.0.0.1 ovhomes.com 127.0.0.1 owha.vancouverco.com 127.0.0.1 owlonl.ivwbox.de +127.0.0.1 owzdu.voluumtrk.com 127.0.0.1 ox-d.aa.com 127.0.0.1 ox-d.adobe.com 127.0.0.1 ox-d.ads.alliancehealth.com @@ -32656,26 +34430,34 @@ 127.0.0.1 ox-d.bstk.servedbyopenx.com 127.0.0.1 ox-d.buyer.servedbyopenx.com 127.0.0.1 ox-d.catholic.org +127.0.0.1 ox-d.cbs.servedbyopenx.com 127.0.0.1 ox-d.cesarsway.com 127.0.0.1 ox-d.covers.com +127.0.0.1 ox-d.curse.servedbyopenx.com 127.0.0.1 ox-d.datacenterdynamics.com 127.0.0.1 ox-d.doucettemedia.com 127.0.0.1 ox-d.enveromedia.com 127.0.0.1 ox-d.fark.servedbyopenx.com +127.0.0.1 ox-d.forbesbidder.servedbyopenx.com 127.0.0.1 ox-d.fwmedia.com 127.0.0.1 ox-d.gamer-network.net 127.0.0.1 ox-d.gloh.co.uk +127.0.0.1 ox-d.ibt.servedbyopenx.com 127.0.0.1 ox-d.imgur.servedbyopenx.com 127.0.0.1 ox-d.intermarkets.net 127.0.0.1 ox-d.interweave.com 127.0.0.1 ox-d.journatic.com 127.0.0.1 ox-d.jrn.com 127.0.0.1 ox-d.lolspotsarticles.com +127.0.0.1 ox-d.medianet.servedbyopenx.com +127.0.0.1 ox-d.merriamwebster.servedbyopenx.com 127.0.0.1 ox-d.mm1x.nl 127.0.0.1 ox-d.mmaadnet.com 127.0.0.1 ox-d.mods.pch.com +127.0.0.1 ox-d.monetizemore.servedbyopenx.com 127.0.0.1 ox-d.monetizingpartners.com 127.0.0.1 ox-d.openx.jp +127.0.0.1 ox-d.playboy.servedbyopenx.com 127.0.0.1 ox-d.pubgears.com 127.0.0.1 ox-d.rocketadserver.com 127.0.0.1 ox-d.sbnation.com @@ -32683,6 +34465,7 @@ 127.0.0.1 ox-d.southwest.com 127.0.0.1 ox-d.tmn.servedbyopenx.com 127.0.0.1 ox-d.traffichub.ch +127.0.0.1 ox-d.viumbe.servedbyopenx.com 127.0.0.1 ox-d.w55c.net 127.0.0.1 ox-d.wetransfer.com 127.0.0.1 ox-d.yp.com @@ -32709,9 +34492,14 @@ 127.0.0.1 ox.videobuster.de 127.0.0.1 oxado.com 127.0.0.1 oxcash.com +127.0.0.1 oxeey.voluumtrk.com +127.0.0.1 oxen8.voluumtrk.com 127.0.0.1 oxoads.com 127.0.0.1 oxosurf.eu 127.0.0.1 oxs1.selsin.net +127.0.0.1 oylkc.voluumtrk.com +127.0.0.1 oyvir.voluumtrk.com +127.0.0.1 ozzhf.voluumtrk.com 127.0.0.1 o_thus.ero-advertising.com 127.0.0.1 p-log.ykimg.com 127.0.0.1 p.37lai.com @@ -32720,15 +34508,18 @@ 127.0.0.1 p.admob.com 127.0.0.1 p.adpdx.com 127.0.0.1 p.adsymptotic.com +127.0.0.1 p.algovid.com 127.0.0.1 p.ato.mx 127.0.0.1 p.blkz2x.com 127.0.0.1 p.chango.com 127.0.0.1 p.chartboost.com +127.0.0.1 p.cpx.to 127.0.0.1 p.cpxinteractive.com 127.0.0.1 p.ctasnet.com 127.0.0.1 p.delivery.net 127.0.0.1 p.emaradx.com 127.0.0.1 p.errorception.com +127.0.0.1 p.imgur.com 127.0.0.1 p.l.qq.com 127.0.0.1 p.l.youku.com 127.0.0.1 p.medialytics.com @@ -32763,14 +34554,18 @@ 127.0.0.1 p2-n3zurhre4jjvk-can5rb2f2a4urcxh-if-v6exp3-v4.metric.gstatic.com 127.0.0.1 p2.adhitzads.com 127.0.0.1 p236.atemda.com +127.0.0.1 p2in2.voluumtrk.com 127.0.0.1 p2tre.emv3.com 127.0.0.1 p30download.ads.saba-e.com +127.0.0.1 p30gp.voluumtrk.com +127.0.0.1 p36jk.voluumtrk.com 127.0.0.1 p3marketing.com 127.0.0.1 p3tre.emv3.com 127.0.0.1 p4-ajvwyt5lpjazy-us7r2dzqcjsqh7pt-164149-i1-v6exp3-v4.metric.gstatic.com 127.0.0.1 p4-ajvwyt5lpjazy-us7r2dzqcjsqh7pt-164149-i2-v6exp3-ds.metric.gstatic.com 127.0.0.1 p4-ajvwyt5lpjazy-us7r2dzqcjsqh7pt-164149-s1-v6exp3-v4.metric.gstatic.com 127.0.0.1 p4-ajvwyt5lpjazy-us7r2dzqcjsqh7pt-if-v6exp3-v4.metric.gstatic.com +127.0.0.1 p4iov.voluumtrk.com 127.0.0.1 p4tre.emv3.com 127.0.0.1 p5-lj5aujgj7jl7w-r2pmxqvndsgx2im2-931517-i1-v6exp3-v4.metric.gstatic.com 127.0.0.1 p5-lj5aujgj7jl7w-r2pmxqvndsgx2im2-931517-i2-v6exp3-ds.metric.gstatic.com @@ -32778,6 +34573,7 @@ 127.0.0.1 p5tre.emv3.com 127.0.0.1 p6tre.emv3.com 127.0.0.1 p7tre.emv3.com +127.0.0.1 p8gqd.voluumtrk.com 127.0.0.1 p8tre.emv3.com 127.0.0.1 p96582.adskape.ru 127.0.0.1 p9tre.emv3.com @@ -32800,10 +34596,12 @@ 127.0.0.1 pages.um-per.com 127.0.0.1 pages2.marketo.com 127.0.0.1 pagevisit.net +127.0.0.1 pagvl.voluumtrk.com 127.0.0.1 paid-work-at-home.com 127.0.0.1 paid.outbrain.com 127.0.0.1 paid4clicks.de 127.0.0.1 paige-weigand.us +127.0.0.1 pajpv.voluumtrk.com 127.0.0.1 palimashop.com 127.0.0.1 pamedia.com.au 127.0.0.1 pamela-bax.us @@ -32823,6 +34621,7 @@ 127.0.0.1 papayamobile.com 127.0.0.1 papi.slideme.org 127.0.0.1 paramount.go2cloud.org +127.0.0.1 parenting.searchwho.com 127.0.0.1 paris.typepad.com 127.0.0.1 paritycube.go2cloud.org 127.0.0.1 park.above.com @@ -32851,6 +34650,7 @@ 127.0.0.1 partner.googleadservices.com 127.0.0.1 partner.leguide.com 127.0.0.1 partner.maxxim.de +127.0.0.1 partner.monetizus.com 127.0.0.1 partner.oboom.com 127.0.0.1 partner.premiumdomains.com 127.0.0.1 partner.privatbank.ua @@ -32878,6 +34678,7 @@ 127.0.0.1 partners.cmptch.com 127.0.0.1 partners.cotterweb.net 127.0.0.1 partners.eatsmarter.de +127.0.0.1 partners.keezmovies.com 127.0.0.1 partners.optiontide.com 127.0.0.1 partners.pornerbros.com 127.0.0.1 partners.rochen.com @@ -32886,6 +34687,7 @@ 127.0.0.1 partners.trafficz.com 127.0.0.1 partners.tremorhub.com 127.0.0.1 partners.vouchedfor.co.uk +127.0.0.1 partners.webmasterplan.com 127.0.0.1 partners.webtrends.com 127.0.0.1 partners.xpertmarket.com 127.0.0.1 partners.yobt.com @@ -32921,12 +34723,15 @@ 127.0.0.1 pb.i.sogou.com 127.0.0.1 pb.sogou.com 127.0.0.1 pba39.inethoster.org +127.0.0.1 pbc7m.voluumtrk.com 127.0.0.1 pbid.pro-market.net +127.0.0.1 pbm7t.voluumtrk.com 127.0.0.1 pbnet.ru 127.0.0.1 pbtool.adition.com 127.0.0.1 pc1.dntrax.com 127.0.0.1 pcash.imlive.com 127.0.0.1 pcastuces.fr.intellitxt.com +127.0.0.1 pcdwh.voluumtrk.com 127.0.0.1 pcgameha.ivwbox.de 127.0.0.1 pcgames.de.intellitxt.com 127.0.0.1 pclick.europe.yahoo.com @@ -32936,12 +34741,16 @@ 127.0.0.1 pcmagzin.ivwbox.de 127.0.0.1 pcmega.hasoffers.com 127.0.0.1 pcookie.cnzz.com +127.0.0.1 pcppu.voluumtrk.com +127.0.0.1 pcsvy.voluumtrk.com +127.0.0.1 pcsx9.voluumtrk.com 127.0.0.1 pctipp.ch.intellitxt.com 127.0.0.1 pctools.tt.omtrdc.net 127.0.0.1 pctweu.vancouverco.com 127.0.0.1 pcwelt.ivwbox.de 127.0.0.1 pd.justclick.ru 127.0.0.1 pda.mv.bidsystem.com +127.0.0.1 pde.lp4.io 127.0.0.1 pdf.forbes.com 127.0.0.1 pdn.applovin.com 127.0.0.1 pds.directrev.com @@ -32950,11 +34759,18 @@ 127.0.0.1 peakcounter.dk 127.0.0.1 pebble.bnex.com 127.0.0.1 peerform.go2cloud.org +127.0.0.1 peffk.voluumtrk.com 127.0.0.1 pegasoweb.com +127.0.0.1 peils.voluumtrk.com +127.0.0.1 peinture.ovh 127.0.0.1 pencomputing.us.intellitxt.com 127.0.0.1 penny-brannon.us 127.0.0.1 penton.us.intellitxt.com +127.0.0.1 pentos-cdn.polarmobile.com 127.0.0.1 people-group.su +127.0.0.1 people.searchwho.com +127.0.0.1 percer.ovh +127.0.0.1 perf.mmstat.com 127.0.0.1 perf.overture.com 127.0.0.1 perfect-privacy.postaffiliatepro.com 127.0.0.1 performance.affiliaxe.com @@ -32962,6 +34778,7 @@ 127.0.0.1 performances.bestofmedia.com 127.0.0.1 performercenter.livejasmin.com 127.0.0.1 permissionmedia.com +127.0.0.1 perpro18-ue1b.ml314.com 127.0.0.1 persevered.com 127.0.0.1 persiangig.ads.saba-e.com 127.0.0.1 persianstat.com @@ -32977,8 +34794,12 @@ 127.0.0.1 pfead2.netway.at 127.0.0.1 pfead3.netway.at 127.0.0.1 pfead4.netway.at +127.0.0.1 pflexads.com 127.0.0.1 pfpc.citygridmedia.com +127.0.0.1 pfuv5.voluumtrk.com +127.0.0.1 pfx9u.voluumtrk.com 127.0.0.1 pg.buzzfeed.com +127.0.0.1 pgcxe.voluumtrk.com 127.0.0.1 pgdb1.surf-town.net 127.0.0.1 pgdbb1.surf-town.net 127.0.0.1 pglb.buzzfed.com @@ -33024,6 +34845,7 @@ 127.0.0.1 phpmyadmin.ero-advertising.com 127.0.0.1 phpmyadmin.js.ero-advertising.com 127.0.0.1 phx.co.in +127.0.0.1 phy.cropspromotional.com 127.0.0.1 physorg.us.intellitxt.com 127.0.0.1 pi.feedsportal.com 127.0.0.1 pi.pardot.com @@ -33035,9 +34857,11 @@ 127.0.0.1 picreel.com 127.0.0.1 pics.firstload.de 127.0.0.1 pics3.inxhost.com +127.0.0.1 picupload.tnaflix.com 127.0.0.1 pigment-adv.co.il 127.0.0.1 pimprig.us.intellitxt.com 127.0.0.1 pimproll.com +127.0.0.1 pinceau.ovh 127.0.0.1 ping-fast.com 127.0.0.1 ping.aclst.com 127.0.0.1 ping.answerbook.com @@ -33053,6 +34877,7 @@ 127.0.0.1 pingjs.qq.com 127.0.0.1 pings.conviva.com 127.0.0.1 pinktrkn.com +127.0.0.1 pipedream.wistia.com 127.0.0.1 piranho.com 127.0.0.1 piranho.de 127.0.0.1 piratinviaggio.digidip.net @@ -33075,6 +34900,7 @@ 127.0.0.1 pix521.adtech.fr 127.0.0.1 pix522.adtech.fr 127.0.0.1 pixazza.com +127.0.0.1 pixel-dimestore.dmi.sensic.net 127.0.0.1 pixel.1und1.de 127.0.0.1 pixel.33across.com 127.0.0.1 pixel.ad.mlnadvertising.com @@ -33082,12 +34908,14 @@ 127.0.0.1 pixel.adcrowd.com 127.0.0.1 pixel.adsafeprotected.com 127.0.0.1 pixel.adsniper.ru +127.0.0.1 pixel.advertising.com 127.0.0.1 pixel.alephd.com 127.0.0.1 pixel.alexametrics.com 127.0.0.1 pixel.autoaffiliatenetwork.com 127.0.0.1 pixel.bild.de 127.0.0.1 pixel.colorupmedia.com 127.0.0.1 pixel.despegar.com +127.0.0.1 pixel.eversttech.net 127.0.0.1 pixel.facebook.com 127.0.0.1 pixel.fanbridge.com 127.0.0.1 pixel.fetchback.com @@ -33105,7 +34933,6 @@ 127.0.0.1 pixel.nbsp.de 127.0.0.1 pixel.netster.com 127.0.0.1 pixel.newsdata.com.au -127.0.0.1 pixel.nymag.com 127.0.0.1 pixel.pcworld.com 127.0.0.1 pixel.prfct.co 127.0.0.1 pixel.prosieben.de @@ -33120,9 +34947,11 @@ 127.0.0.1 pixel.staging.tree.com 127.0.0.1 pixel.staticworld.net 127.0.0.1 pixel.tapad.com +127.0.0.1 pixel.tcog.cp1.news.com.au 127.0.0.1 pixel.trafficmp.com 127.0.0.1 pixel.tree.com 127.0.0.1 pixel.vmm-satellite2.com +127.0.0.1 pixel.wp.com 127.0.0.1 pixel.wp.tv 127.0.0.1 pixel.xmladfeed.com 127.0.0.1 pixel.yabidos.com @@ -33136,13 +34965,30 @@ 127.0.0.1 pixeleze.com 127.0.0.1 pixels.youknowbest.com 127.0.0.1 pixetrk.com +127.0.0.1 pixiedust.buzzfeed.com +127.0.0.1 pk5qr.voluumtrk.com 127.0.0.1 pkmg.go2cloud.org 127.0.0.1 pl.intag.co 127.0.0.1 pl.marketgid.com 127.0.0.1 pl105423.putags.com 127.0.0.1 pl105715.ooecyaauiz.com 127.0.0.1 pl105716.nquchhfyex.com +127.0.0.1 pl106332.puhtml.com +127.0.0.1 pl108274.puhtml.com +127.0.0.1 pl109299.puhtml.com +127.0.0.1 pl111316.puhtml.com +127.0.0.1 pl111439.puhtml.com +127.0.0.1 pl112153.puhtml.com +127.0.0.1 pl116608.gortags.com +127.0.0.1 pl116670.puhtml.com +127.0.0.1 pl117290.puhtml.com +127.0.0.1 pl130464.puhtml.com +127.0.0.1 pl133504.puhtml.com +127.0.0.1 pl144731.puhtml.com +127.0.0.1 pl147032.puhtml.com 127.0.0.1 pl1567.bntags.com +127.0.0.1 pl3700.puhtml.com +127.0.0.1 pl3792.puhtml.com 127.0.0.1 pl4277.putags.com 127.0.0.1 pl4759.aoqneyvmaz.com 127.0.0.1 pl4851.putags.com @@ -33152,13 +34998,18 @@ 127.0.0.1 pl5191.putags.com 127.0.0.1 pl5225.bntags.com 127.0.0.1 pl5411.bntags.com +127.0.0.1 pl61x.voluumtrk.com +127.0.0.1 planche.ovh 127.0.0.1 planet-source-code.us.intellitxt.com 127.0.0.1 planetactive.com 127.0.0.1 planetgamecube.us.intellitxt.com 127.0.0.1 planetluck.com 127.0.0.1 planituk.go2cloud.org 127.0.0.1 platform.communicatorcorp.com +127.0.0.1 platform.linkedin.com 127.0.0.1 platform.revenuestreet.com +127.0.0.1 platform.stumbleupon.com +127.0.0.1 platform.tumblr.com 127.0.0.1 platincasino.postaffiliatepro.com 127.0.0.1 play.affmob.org 127.0.0.1 play.leadzu.com @@ -33172,12 +35023,17 @@ 127.0.0.1 player.piksel.com 127.0.0.1 playminigolf.com 127.0.0.1 playtomic.com +127.0.0.1 plb27.voluumtrk.com 127.0.0.1 plemedia.com 127.0.0.1 plemx.com 127.0.0.1 plenews.net 127.0.0.1 pleshkov.justclick.ru 127.0.0.1 plethoramobile.com +127.0.0.1 plex.r.worldssl.net +127.0.0.1 plex2.com +127.0.0.1 pll28.voluumtrk.com 127.0.0.1 plocia.com +127.0.0.1 ploko.voluumtrk.com 127.0.0.1 pls.webtype.com 127.0.0.1 plsdrct2.me 127.0.0.1 plug.plugerr.com @@ -33204,11 +35060,15 @@ 127.0.0.1 pm.sofortficken.com 127.0.0.1 pm.swingerdatenbank.com 127.0.0.1 pm.tittendating.com +127.0.0.1 pm.w55c.net 127.0.0.1 pm.web.com 127.0.0.1 pmediatrack.com +127.0.0.1 pmloe.voluumtrk.com +127.0.0.1 pmlt1.voluumtrk.com 127.0.0.1 pmm.people.com.cn 127.0.0.1 pn2.adserver.yahoo.com 127.0.0.1 pnp.ivwbox.de +127.0.0.1 pnx6n.voluumtrk.com 127.0.0.1 pnytimes.chartbeat.net 127.0.0.1 po.st 127.0.0.1 pocketmedia.go2cloud.org @@ -33218,13 +35078,13 @@ 127.0.0.1 pod.xxxconnect.com 127.0.0.1 podcasts.screenrant.com 127.0.0.1 podtrac.advertserve.com -127.0.0.1 pof.com 127.0.0.1 pointific.go2cloud.org 127.0.0.1 pointix.com 127.0.0.1 pointroll.com 127.0.0.1 pointshop.dk 127.0.0.1 pokasvobodna.justclick.ru 127.0.0.1 politads.com +127.0.0.1 politics.searchwho.com 127.0.0.1 poll.truehits.net 127.0.0.1 polluxnetwork.com 127.0.0.1 polly-hammack.us @@ -33237,6 +35097,7 @@ 127.0.0.1 pool02.2cnt.net 127.0.0.1 poopoo.freestats.com 127.0.0.1 pop.adcocktail.com +127.0.0.1 pop.adconjure.com 127.0.0.1 pop.billionuploads.com 127.0.0.1 pop.kissmetrics.com 127.0.0.1 pop.mobpartner.mobi @@ -33249,12 +35110,14 @@ 127.0.0.1 popads.net 127.0.0.1 popadscdn.net 127.0.0.1 popit.mediumpimpin.com +127.0.0.1 popload.net 127.0.0.1 popme.163.com 127.0.0.1 poponclick.com 127.0.0.1 pops.ero-advertising.com 127.0.0.1 pops.freeze.com 127.0.0.1 popstats.com.br 127.0.0.1 popstrap.com +127.0.0.1 poptm.com 127.0.0.1 popunder.adtrgt.com 127.0.0.1 popunder.jacquieetmichel.net 127.0.0.1 popunder.loading-delivery1.com @@ -33266,6 +35129,7 @@ 127.0.0.1 popup.msn.com 127.0.0.1 popup.smusic.ir 127.0.0.1 popup.tak3az.ir +127.0.0.1 popup.wisepops.com 127.0.0.1 popupad.net 127.0.0.1 popupads.ir 127.0.0.1 popupmoney.com @@ -33273,6 +35137,7 @@ 127.0.0.1 popuptraffic.com 127.0.0.1 popzila.com 127.0.0.1 pornads.biz +127.0.0.1 pornaz.net 127.0.0.1 porner.at 127.0.0.1 pornhub.tardangro.com 127.0.0.1 pornhub.yoshatia.com @@ -33303,12 +35168,15 @@ 127.0.0.1 postmasterbannernet.com 127.0.0.1 postpixel.vindicosuite.com 127.0.0.1 potd.onlytease.com +127.0.0.1 pouvoir.science 127.0.0.1 power-affliate.blogspot.com 127.0.0.1 poweradvertising.co.uk 127.0.0.1 powercount.jswelt.de 127.0.0.1 powersearch.us.com 127.0.0.1 powertube.bitterstrawberry.com 127.0.0.1 pp-serve.newsinc.com +127.0.0.1 pp0ru.voluumtrk.com +127.0.0.1 ppcchicago.com 127.0.0.1 ppcindo.com 127.0.0.1 ppctracking.net 127.0.0.1 ppctrck.com @@ -33316,6 +35184,7 @@ 127.0.0.1 pppads.com 127.0.0.1 pptrk.com 127.0.0.1 pq-direct.revsci.net +127.0.0.1 pr-bh.ybp.yahoo.com 127.0.0.1 pr-static.empflix.com 127.0.0.1 pr.atwola.com 127.0.0.1 pr.blogflux.com @@ -33324,6 +35193,7 @@ 127.0.0.1 praiseads.go2cloud.org 127.0.0.1 pranking12.ziyu.net 127.0.0.1 prchecker.info +127.0.0.1 pre.footprintpredict.com 127.0.0.1 preciselylocate.com 127.0.0.1 precisionleads.go2cloud.org 127.0.0.1 predictad.com @@ -33364,7 +35234,10 @@ 127.0.0.1 prismaprofits.go2cloud.org 127.0.0.1 privoy.doublepimp.com 127.0.0.1 prizee.com +127.0.0.1 prkhg.voluumtrk.com +127.0.0.1 prm.europacash.com 127.0.0.1 prmfactory.go2cloud.org +127.0.0.1 prmobiles.com 127.0.0.1 pro-advertising.com 127.0.0.1 pro.hit.gemius.pl 127.0.0.1 pro7.ivwbox.de @@ -33374,6 +35247,7 @@ 127.0.0.1 probe.yieldlab.net 127.0.0.1 probes.cedexis.com 127.0.0.1 probux.com +127.0.0.1 prochina.work 127.0.0.1 procinema.2cnt.net 127.0.0.1 prod.ipav.xyz 127.0.0.1 prod01.jag.vmsn.de @@ -33382,6 +35256,7 @@ 127.0.0.1 prodege.go2cloud.org 127.0.0.1 production-eqbc.lvp.llnw.net 127.0.0.1 production-mcs.lvp.llnw.net +127.0.0.1 production-ps.lvp.llnw.net 127.0.0.1 production-stats-ezpublishing.netdna-ssl.com 127.0.0.1 production.mcs.delve.cust.lldns.net 127.0.0.1 productpartnersllc.tt.omtrdc.net @@ -33407,6 +35282,7 @@ 127.0.0.1 promo.badoink.com 127.0.0.1 promo.blackcrush.com 127.0.0.1 promo.bluesq.com +127.0.0.1 promo.bongacash.com 127.0.0.1 promo.easy-dating.org 127.0.0.1 promo.ezstatic.com 127.0.0.1 promo.fileforum.com @@ -33415,6 +35291,7 @@ 127.0.0.1 promo.indecentes-voisines.com 127.0.0.1 promo.pegcweb.com 127.0.0.1 promo.selbstaendig20.12227.digistore24.com +127.0.0.1 promo.thepornsurvey.com 127.0.0.1 promo.ulust.com 127.0.0.1 promo.yahoo.com 127.0.0.1 promo1.c-rewards.com @@ -33436,6 +35313,7 @@ 127.0.0.1 promptleads.go2cloud.org 127.0.0.1 propellerpops.com 127.0.0.1 prophet.heise.de +127.0.0.1 prosperent.com 127.0.0.1 protexting.com 127.0.0.1 protraffic.com 127.0.0.1 providence.voxmedia.com @@ -33443,10 +35321,28 @@ 127.0.0.1 proxy.ladot.com 127.0.0.1 proxy.mobilecore.com 127.0.0.1 proxypage.msn.com +127.0.0.1 prpops.com +127.0.0.1 prscripts.com +127.0.0.1 prstatics.com +127.0.0.1 prv3h.voluumtrk.com +127.0.0.1 prwidgets.com 127.0.0.1 prx.freecj.com 127.0.0.1 ps-eu.amazon-adsystem.com +127.0.0.1 ps.ns-cdn.com +127.0.0.1 ps1.ultraflawlesspop.com 127.0.0.1 ps2.interpolls.com +127.0.0.1 ps2.ultraflawlesspop.com 127.0.0.1 ps3.ign.us.intellitxt.com +127.0.0.1 ps3.ultraflawlesspop.com +127.0.0.1 ps4.ultraflawlesspop.com +127.0.0.1 ps5.ultraflawlesspop.com +127.0.0.1 ps6.ultraflawlesspop.com +127.0.0.1 ps7.ultraflawlesspop.com +127.0.0.1 ps8.ultraflawlesspop.com +127.0.0.1 ps9.ultraflawlesspop.com +127.0.0.1 psfjg.voluumtrk.com +127.0.0.1 psl19.voluumtrk.com +127.0.0.1 psma02.com 127.0.0.1 psp.ign.us.intellitxt.com 127.0.0.1 psstatic.podshow.com 127.0.0.1 psstt.com @@ -33459,13 +35355,18 @@ 127.0.0.1 pt.trafficjunky.net 127.0.0.1 pt000127.unica.com 127.0.0.1 pt200204.unica.com +127.0.0.1 pt7ma.voluumtrk.com 127.0.0.1 ptest.webtrekk.net +127.0.0.1 ptk6n.voluumtrk.com 127.0.0.1 ptp.lolco.net +127.0.0.1 ptp4ever.fr +127.0.0.1 ptqss.voluumtrk.com 127.0.0.1 ptracker.be2.com 127.0.0.1 ptrking.com 127.0.0.1 ptsc.shoplocal.com 127.0.0.1 pu-dsp.adsniper.ru 127.0.0.1 pu.plugrush.com +127.0.0.1 pu.trafficshop.com 127.0.0.1 puat.go2cloud.org 127.0.0.1 pub.ad-sys.com 127.0.0.1 pub.adswam.com @@ -33486,6 +35387,7 @@ 127.0.0.1 pub.sv2.biz 127.0.0.1 pub.telecharger.com 127.0.0.1 pub1.bravenet.com +127.0.0.1 pub1.co 127.0.0.1 pub10.bravenet.com 127.0.0.1 pub11.bravenet.com 127.0.0.1 pub12.bravenet.com @@ -33607,9 +35509,11 @@ 127.0.0.1 publisherportal.doubleclick.net 127.0.0.1 publishers.clickbooth.com 127.0.0.1 publishers.gourmetads.com +127.0.0.1 pubnative.net 127.0.0.1 pubs.lemonde.fr 127.0.0.1 pubted.com 127.0.0.1 pud.cpulse.com +127.0.0.1 puhtml.com 127.0.0.1 pulharktheherald.112.2o7.net 127.0.0.1 pulpantagraph.112.2o7.net 127.0.0.1 pulsetv.com @@ -33617,6 +35521,7 @@ 127.0.0.1 puma.vizu.com.51276.9332.302br.net 127.0.0.1 puma.vizu.com.51277.9332.302br.net 127.0.0.1 puma.vizu.com.51278.9332.302br.net +127.0.0.1 puokp.voluumtrk.com 127.0.0.1 puppet.vmsn.de 127.0.0.1 puppet02.bob.local.vmsn.de 127.0.0.1 puppet02.con.local.vmsn.de @@ -33628,13 +35533,18 @@ 127.0.0.1 push.cdnads.com 127.0.0.1 putbid.net 127.0.0.1 pv.sogou.com +127.0.0.1 pvnrr.voluumtrk.com +127.0.0.1 pvrgf.voluumtrk.com 127.0.0.1 pwk.etrxc.com +127.0.0.1 pwu0e.voluumtrk.com 127.0.0.1 px.adhigh.net 127.0.0.1 px.at.atwola.com 127.0.0.1 px.cdn.creative.medialytics.com 127.0.0.1 px.demdex.net 127.0.0.1 px.excitedigitalmedia.com +127.0.0.1 px.owneriq.net 127.0.0.1 px.ozonemedia.com +127.0.0.1 px.srvcs.tumblr.com 127.0.0.1 px.topspin.net 127.0.0.1 px.wa.ui-portal.de 127.0.0.1 px1.vtrtl.de @@ -33644,6 +35554,14 @@ 127.0.0.1 pxlctl.as.com 127.0.0.1 pxlctl.elpais.com 127.0.0.1 pxses.smowtion.com +127.0.0.1 py2so.voluumtrk.com +127.0.0.1 pyhel.voluumtrk.com +127.0.0.1 pyj6n.voluumtrk.com +127.0.0.1 pywatur.biz +127.0.0.1 pyzct.voluumtrk.com +127.0.0.1 pyzou.voluumtrk.com +127.0.0.1 pztqt.voluumtrk.com +127.0.0.1 pzxld.voluumtrk.com 127.0.0.1 p_en.popunder.net 127.0.0.1 q.addthis.com 127.0.0.1 q.azcentral.com @@ -33655,33 +35573,67 @@ 127.0.0.1 q.zeroredirect.com 127.0.0.1 q.zeroredirect1.com 127.0.0.1 q.zeroredirect2.com +127.0.0.1 q1blk.voluumtrk.com +127.0.0.1 q2fqo.voluumtrk.com 127.0.0.1 q3-2.webtrekk.net 127.0.0.1 q3.webtrekk.net +127.0.0.1 q3aut.voluumtrk.com +127.0.0.1 q3iko.voluumtrk.com +127.0.0.1 q3mz4.voluumtrk.com +127.0.0.1 q47bd.voluumtrk.com +127.0.0.1 q4trt.voluumtrk.com +127.0.0.1 q9dga.voluumtrk.com 127.0.0.1 qa.rlcdn.com +127.0.0.1 qa3wf.voluumtrk.com 127.0.0.1 qadabra.com 127.0.0.1 qads1.de 127.0.0.1 qainteryield.jmp9.com +127.0.0.1 qannd.voluumtrk.com 127.0.0.1 qantas.demdex.net 127.0.0.1 qarmonija.justclick.ru +127.0.0.1 qb2h0.voluumtrk.com +127.0.0.1 qb6q5.voluumtrk.com 127.0.0.1 qbeeeld.jmp9.com 127.0.0.1 qbeeld.jmp9.com 127.0.0.1 qbeeseld.jmp9.com 127.0.0.1 qbeld.jmp9.com +127.0.0.1 qbsg9.voluumtrk.com +127.0.0.1 qbt0g.voluumtrk.com +127.0.0.1 qbuxm.voluumtrk.com 127.0.0.1 qckjmp.com +127.0.0.1 qclyu.voluumtrk.com 127.0.0.1 qd.jmp9.com 127.0.0.1 qdigital.co.il 127.0.0.1 qdmil.com +127.0.0.1 qdzlt.voluumtrk.com 127.0.0.1 qeld.jmp9.com +127.0.0.1 qf2e4.voluumtrk.com +127.0.0.1 qftqw.voluumtrk.com +127.0.0.1 qi2kt.voluumtrk.com +127.0.0.1 qibly.voluumtrk.com +127.0.0.1 qimuj.voluumtrk.com +127.0.0.1 qitr0.voluumtrk.com +127.0.0.1 qixrb.voluumtrk.com 127.0.0.1 qj.us.intellitxt.com 127.0.0.1 qksrv.growhope.com 127.0.0.1 qld.jmp9.com 127.0.0.1 qlipsodigital.checkm8.com 127.0.0.1 qlog.adap.tv +127.0.0.1 qm41q.voluumtrk.com +127.0.0.1 qmkkm.voluumtrk.com +127.0.0.1 qmtjz.voluumtrk.com +127.0.0.1 qmuuc.voluumtrk.com +127.0.0.1 qnlcq.voluumtrk.com +127.0.0.1 qnzdd.voluumtrk.com +127.0.0.1 qof7o.voluumtrk.com 127.0.0.1 qoolaid.mygeek.com +127.0.0.1 qos.bandwidth-check.de5.bobodise.com 127.0.0.1 qos.report.qq.com 127.0.0.1 qos.video.yimg.com +127.0.0.1 qplir.voluumtrk.com 127.0.0.1 qq.m.cn.miaozhen.com 127.0.0.1 qqc.co +127.0.0.1 qqvj6.voluumtrk.com 127.0.0.1 qrdeom.com 127.0.0.1 qs.ioam.de 127.0.0.1 qs.ivwbox.de @@ -33691,28 +35643,39 @@ 127.0.0.1 qualigo.de 127.0.0.1 qualigo.net 127.0.0.1 qualimed.ivwbox.de +127.0.0.1 qualityclickcontrol.com 127.0.0.1 qualitylegion.com 127.0.0.1 quantcount.com 127.0.0.1 quantserve.com 127.0.0.1 quartz.bnex.com 127.0.0.1 qubitanalytics.appspot.com 127.0.0.1 qubitproducts.app13.hubspot.com +127.0.0.1 qudll.voluumtrk.com +127.0.0.1 queen.albinass.com 127.0.0.1 questaffiliates.net 127.0.0.1 questnet.de 127.0.0.1 questseek.com +127.0.0.1 qui.science +127.0.0.1 quickdomainfwd.com 127.0.0.1 quicken.demdex.net 127.0.0.1 quickinfo247.com 127.0.0.1 quickinspirations.com 127.0.0.1 quickpay.carmunity.de 127.0.0.1 quickstartstore.go2cloud.org 127.0.0.1 quinst.com +127.0.0.1 qvb2w.voluumtrk.com +127.0.0.1 qvciu.voluumtrk.com 127.0.0.1 qwest.bfast.com 127.0.0.1 qwiklnk.com +127.0.0.1 qwwqb.voluumtrk.com +127.0.0.1 qxj3b.voluumtrk.com 127.0.0.1 qyrix.go2cloud.org 127.0.0.1 r.254a.comjs.moatads.com 127.0.0.1 r.369dl.com 127.0.0.1 r.ads.zynga.com +127.0.0.1 r.adserver01.de 127.0.0.1 r.aol.com +127.0.0.1 r.apina.biz 127.0.0.1 r.bbci.co.uk 127.0.0.1 r.casalemedia.com 127.0.0.1 r.chip.de @@ -33721,9 +35684,11 @@ 127.0.0.1 r.edge.inmobicdn.net 127.0.0.1 r.kissinsights.com 127.0.0.1 r.leadzu.com +127.0.0.1 r.leadzuaf.com 127.0.0.1 r.letsw.com 127.0.0.1 r.mail.ru 127.0.0.1 r.migch.com +127.0.0.1 r.mobhubrdrms.com 127.0.0.1 r.mobpartner.mobi 127.0.0.1 r.movad.de 127.0.0.1 r.msn.com @@ -33731,6 +35696,7 @@ 127.0.0.1 r.pixel.trafficmp.com 127.0.0.1 r.radikal.ru 127.0.0.1 r.skimresources.com +127.0.0.1 r.trffrcmrd.com 127.0.0.1 r.turn.com 127.0.0.1 r.twimg.com 127.0.0.1 r.uimserv.net @@ -33740,6 +35706,7 @@ 127.0.0.1 r.zeroredirect.com 127.0.0.1 r.zeroredirect1.com 127.0.0.1 r.zeroredirect2.com +127.0.0.1 r0d2x.voluumtrk.com 127.0.0.1 r1.ace.advertising.com 127.0.0.1 r1.beta.ace.advertising.com 127.0.0.1 r1.computerbild.de @@ -33755,17 +35722,23 @@ 127.0.0.1 r2.linksynergy.com 127.0.0.1 r2.plugrush.com 127.0.0.1 r20.rs6.net +127.0.0.1 r2fjs.voluumtrk.com 127.0.0.1 r2jmarketing.go2cloud.org +127.0.0.1 r2v3n.voluumtrk.com 127.0.0.1 r3.cooleremail.com 127.0.0.1 r3.plugrush.com 127.0.0.1 r4.cooleremail.com 127.0.0.1 r4.plugrush.com +127.0.0.1 r4zih.voluumtrk.com 127.0.0.1 r5.cooleremail.com 127.0.0.1 r5.plugrush.com 127.0.0.1 r6.cooleremail.com +127.0.0.1 r6qz8.voluumtrk.com 127.0.0.1 r7.cooleremail.com 127.0.0.1 r8.cooleremail.com 127.0.0.1 r9.cooleremail.com +127.0.0.1 r9pyq.voluumtrk.com +127.0.0.1 ra.revolvermaps.com 127.0.0.1 rachael-mcquay.us 127.0.0.1 rachel-beer.us 127.0.0.1 rachel-billips.us @@ -33774,11 +35747,14 @@ 127.0.0.1 rack-iad.bench.cedexis.com 127.0.0.1 rack.bauermedia.co.uk 127.0.0.1 rad.adriver.ru +127.0.0.1 rad.live.com 127.0.0.1 rad.microsoft.com +127.0.0.1 rad.msn.com 127.0.0.1 rad.reporo.net 127.0.0.1 radar.cedexis.com 127.0.0.1 radar.cedexis.swiftserve.com 127.0.0.1 radar11ab.co.uk +127.0.0.1 radartumblr.cedexis.com 127.0.0.1 radarurl.com 127.0.0.1 radiate.com 127.0.0.1 radibonn.ivwbox.de @@ -33791,6 +35767,8 @@ 127.0.0.1 rainbow-de.mythings.com 127.0.0.1 rainbow.mythings.com 127.0.0.1 rainmaker.digitalbrandsinc.netdna-cdn.com +127.0.0.1 raiss.voluumtrk.com +127.0.0.1 ramctrlgate.com 127.0.0.1 rampidads.com 127.0.0.1 randall-mccabe.us 127.0.0.1 ranking-charts.de @@ -33803,13 +35781,16 @@ 127.0.0.1 rankyou.com 127.0.0.1 rapdirt.us.intellitxt.com 127.0.0.1 rapleaf.com +127.0.0.1 raplz.voluumtrk.com 127.0.0.1 raquel-freda.us 127.0.0.1 rareru.ru 127.0.0.1 ras.doubleclick.net +127.0.0.1 rasml.voluumtrk.com 127.0.0.1 rata.ero-advertising.com 127.0.0.1 rates.insureship.com 127.0.0.1 rating.openstat.com 127.0.0.1 rating.openstat.ru +127.0.0.1 ratx9.voluumtrk.com 127.0.0.1 ravenstonedigital.go2cloud.org 127.0.0.1 rawdinner.justclick.ru 127.0.0.1 rawstory.us.intellitxt.com @@ -33833,7 +35814,9 @@ 127.0.0.1 rcm-images.amazon.com 127.0.0.1 rcm.amazon.com 127.0.0.1 rcm.go2cloud.org +127.0.0.1 rcpmf.voluumtrk.com 127.0.0.1 rcsmetrics.it +127.0.0.1 rd-eu.a9.com 127.0.0.1 rd.ads.juno.com 127.0.0.1 rd.advertising.com 127.0.0.1 rd.alice.it @@ -33847,15 +35830,19 @@ 127.0.0.1 rd.yahoo.com 127.0.0.1 rd1.hitbox.com 127.0.0.1 rd1.surfernetwork.com +127.0.0.1 rddywd.com 127.0.0.1 rdir.smartshoppingads.co.uk +127.0.0.1 rds4k.voluumtrk.com 127.0.0.1 rdstat.tanx.com 127.0.0.1 re.adroll.com +127.0.0.1 re.directrev.com 127.0.0.1 re.try9.com 127.0.0.1 readserver.cachefly.net 127.0.0.1 realads.realmedia.com 127.0.0.1 realclick.co.kr 127.0.0.1 realclix.com 127.0.0.1 realcounter.eu +127.0.0.1 realestate.searchwho.com 127.0.0.1 realist.gen.tr 127.0.0.1 realitatea.2cnt.net 127.0.0.1 reallybig.spinbox.net @@ -33870,7 +35857,6 @@ 127.0.0.1 realnetworks.com 127.0.0.1 realonlineshop.ugc.bazaarvoice.com 127.0.0.1 realperson70.net -127.0.0.1 realresultspublish.com 127.0.0.1 realtechnetwork.com 127.0.0.1 realtime.services.disqus.com 127.0.0.1 realtracker.com @@ -33882,6 +35868,7 @@ 127.0.0.1 recommendation.24.com 127.0.0.1 recommendations.shareaholic.com 127.0.0.1 recommendedpreview.com +127.0.0.1 record.commissionlounge.com 127.0.0.1 recorded-email.com 127.0.0.1 recreationalequipmen.tt.omtrdc.net 127.0.0.1 recreativ.ru @@ -33895,10 +35882,13 @@ 127.0.0.1 redir-traffic.elephant-traffic.com 127.0.0.1 redir.adap.tv 127.0.0.1 redir.aitrkng.com +127.0.0.1 redir.metaservices.microsoft.com 127.0.0.1 redir.overture.com 127.0.0.1 redir.speedbit.com 127.0.0.1 redir.widdit.com +127.0.0.1 redirect-for-more.info 127.0.0.1 redirect.advertising.se +127.0.0.1 redirect.com 127.0.0.1 redirect.dealabs.com 127.0.0.1 redirect.ero-advertising.com 127.0.0.1 redirect.fairfax.com.au @@ -33914,6 +35904,8 @@ 127.0.0.1 redirect1.vip.store.yahoo.com 127.0.0.1 redirect2719.ws 127.0.0.1 redirecting.ws +127.0.0.1 redirector.themobilehub.net +127.0.0.1 redirects.ero-advertising.com 127.0.0.1 redirects.timesink.com 127.0.0.1 reditions.net 127.0.0.1 redmas.com @@ -33937,6 +35929,7 @@ 127.0.0.1 referral.totost.com 127.0.0.1 referral.totosuper.com 127.0.0.1 referralware.com +127.0.0.1 referrer.disqus.com 127.0.0.1 refinedads.com 127.0.0.1 reformal.ru 127.0.0.1 reg20.ubermedia.com @@ -33952,6 +35945,7 @@ 127.0.0.1 reina-petty.us 127.0.0.1 reisprei.ivwbox.de 127.0.0.1 rejectclick.com +127.0.0.1 rek.mobi 127.0.0.1 reklam.rfsl.se 127.0.0.1 reklama.internet.cz 127.0.0.1 reklama.reflektor.cz @@ -33976,6 +35970,7 @@ 127.0.0.1 replay.foreseeresults.com 127.0.0.1 replaycontroller.4seeresults.com 127.0.0.1 repo01.ixn.local.vmsn.de +127.0.0.1 report-zt.1rx.io 127.0.0.1 report.adsender.us 127.0.0.1 report.adview.cn 127.0.0.1 report.ivwbox.de @@ -34008,11 +36003,14 @@ 127.0.0.1 reports.extreme-dm.com 127.0.0.1 reports.pagesuite-professional.co.uk 127.0.0.1 reports.superfish.com +127.0.0.1 reports.wes.df.telemetry.microsoft.com 127.0.0.1 reports.zedo.com 127.0.0.1 reprox01.dus.local.vmsn.de 127.0.0.1 republika.onet.pl +127.0.0.1 req.adsmogo.com 127.0.0.1 req.appads.com 127.0.0.1 req.mojiva.com +127.0.0.1 req.startappservice.com 127.0.0.1 req1.appads.com 127.0.0.1 req2.appads.com 127.0.0.1 req3.appads.com @@ -34040,14 +36038,18 @@ 127.0.0.1 resultonline.com 127.0.0.1 retail-affiliates.com 127.0.0.1 retaildirect.realmedia.com +127.0.0.1 retainguaninefluorite.info 127.0.0.1 retarget.ssl-services.com 127.0.0.1 retargetpro.net +127.0.0.1 retentionscience.com 127.0.0.1 retrostats.com 127.0.0.1 return.bs.domainnamesales.com 127.0.0.1 return.to 127.0.0.1 return.uk.domainnamesales.com 127.0.0.1 rev.adip.ly 127.0.0.1 rev.fapdu.com +127.0.0.1 rev.pornxs.com +127.0.0.1 rev2pub.adk2x.com 127.0.0.1 revadsnetwork.com 127.0.0.1 revadstrk.com 127.0.0.1 revealads.appspot.com @@ -34058,6 +36060,7 @@ 127.0.0.1 revenues.performancerevenues.com 127.0.0.1 revenueservice.com 127.0.0.1 revlabs.go2cloud.org +127.0.0.1 revmob.com 127.0.0.1 revmobmobileadnetwork.com 127.0.0.1 revolution.ign.us.intellitxt.com 127.0.0.1 revolutiongolf.go2cloud.org @@ -34077,28 +36080,41 @@ 127.0.0.1 rg6.com 127.0.0.1 rg7.com 127.0.0.1 rg9.com +127.0.0.1 rgady.voluumtrk.com 127.0.0.1 rgmarket.adspirit.net 127.0.0.1 rgrmarketing.go2cloud.org +127.0.0.1 rgryk.voluumtrk.com +127.0.0.1 rgxzf.voluumtrk.com 127.0.0.1 rheinzei.ivwbox.de 127.0.0.1 rhtag.com +127.0.0.1 rhvtt.voluumtrk.com 127.0.0.1 ribbon.india.com 127.0.0.1 rich-agent.s3.amazonaws.com 127.0.0.1 richelle-crandell.us 127.0.0.1 richfind.com +127.0.0.1 richpays.com 127.0.0.1 richwebmaster.com 127.0.0.1 ricor.ru 127.0.0.1 rightmedia.com 127.0.0.1 rightonmediagroup.go2cloud.org 127.0.0.1 rightpundits.us.intellitxt.com 127.0.0.1 rightstats.com +127.0.0.1 rink.hockeyapp.net +127.0.0.1 ripbwing.com 127.0.0.1 rivasearch.com +127.0.0.1 rjfjx.voluumtrk.com +127.0.0.1 rjnc0.voluumtrk.com 127.0.0.1 rkaffiliates.go2cloud.org +127.0.0.1 rkq0r.voluumtrk.com 127.0.0.1 rl.auto.ru 127.0.0.1 rl.heise.de 127.0.0.1 rland.searchlabel.com 127.0.0.1 rlcdn.com +127.0.0.1 rlcxi.voluumtrk.com 127.0.0.1 rleeden.myby.co.uk +127.0.0.1 rll9v.voluumtrk.com 127.0.0.1 rlog.9gag.com +127.0.0.1 rlyg7.voluumtrk.com 127.0.0.1 rm.yieldmanager.com 127.0.0.1 rm2.adtilt.com 127.0.0.1 rma-api.gravity.com @@ -34109,10 +36125,12 @@ 127.0.0.1 rmbn.ru 127.0.0.1 rmd.atdmt.com 127.0.0.1 rmedia.boston.com +127.0.0.1 rmj9i.voluumtrk.com 127.0.0.1 rmpanelzone.research-int.com 127.0.0.1 rmsurveys.research-int.com 127.0.0.1 rmv.so 127.0.0.1 rnd.yxo.ru +127.0.0.1 rnyp9.voluumtrk.com 127.0.0.1 ro.adocean.pl 127.0.0.1 roadcatalogs.us.intellitxt.com 127.0.0.1 roar.com @@ -34137,11 +36155,15 @@ 127.0.0.1 rose.ixbt.com 127.0.0.1 rosettastone.tt.omtrdc.net 127.0.0.1 rotabanner.kulichki.net +127.0.0.1 rotate.fncnet1.com 127.0.0.1 rotatingbannerads.com 127.0.0.1 rotator.adjuggler.com 127.0.0.1 rotator.nbjmp.com 127.0.0.1 rotator.offpageads.com 127.0.0.1 rotator.tradetracker.nl +127.0.0.1 rotator.trafficstars.com +127.0.0.1 rotrk.com +127.0.0.1 router.adlure.net 127.0.0.1 router.revmob.com 127.0.0.1 router.tlvmedia.com 127.0.0.1 router01.ixn.local.vmsn.de @@ -34166,9 +36188,11 @@ 127.0.0.1 rpm-images.newrelic.com 127.0.0.1 rponl.ivwbox.de 127.0.0.1 rpt.cedexis.com +127.0.0.1 rqc3w.voluumtrk.com 127.0.0.1 rr1.outster.com 127.0.0.1 rr1.xxxcounter.com 127.0.0.1 rrmprod.amobee.com +127.0.0.1 rrwnt.voluumtrk.com 127.0.0.1 rs-staticart.ybcdn.net 127.0.0.1 rs.epoq.de 127.0.0.1 rs.ero-advertising.com @@ -34180,6 +36204,9 @@ 127.0.0.1 rsh.ivwbox.de 127.0.0.1 rsmrttracking.com 127.0.0.1 rss.dtiserv.com +127.0.0.1 rsvfh.voluumtrk.com +127.0.0.1 rsvjs.voluumtrk.com +127.0.0.1 rt.analytics.anvato.net 127.0.0.1 rt.globo.com 127.0.0.1 rt.liftdna.com 127.0.0.1 rt.prnewswire.com @@ -34190,10 +36217,12 @@ 127.0.0.1 rtax.criteo.com 127.0.0.1 rtb-pclick.secure.yahoo.com 127.0.0.1 rtb-pixel-ams1.everesttech.net +127.0.0.1 rtb.nexage.com 127.0.0.1 rtb.vmsn.de 127.0.0.1 rtb.watchmedia.biz 127.0.0.1 rtbpop.com 127.0.0.1 rtd.tubemogul.com +127.0.0.1 rtg.ml314.com 127.0.0.1 rtl.2cnt.net 127.0.0.1 rtl.ivwbox.de 127.0.0.1 rtl2.ivwbox.de @@ -34214,9 +36243,11 @@ 127.0.0.1 ru03-hits.spylog.com 127.0.0.1 ru09-hits.spylog.com 127.0.0.1 ru4.com +127.0.0.1 ruaft.voluumtrk.com 127.0.0.1 rubanners.com 127.0.0.1 rubicon.admailtiser.com 127.0.0.1 rubulat.justclick.ru +127.0.0.1 rucir.voluumtrk.com 127.0.0.1 ruclicks.com 127.0.0.1 rum-static.pingdom.net 127.0.0.1 rum.monitis.com @@ -34230,14 +36261,23 @@ 127.0.0.1 russia2.2cnt.net 127.0.0.1 ruth-bromley.us 127.0.0.1 ruth-westman.us +127.0.0.1 rutht.voluumtrk.com +127.0.0.1 rvcjn.voluumtrk.com +127.0.0.1 rvkai.voluumtrk.com +127.0.0.1 rvy2a.voluumtrk.com 127.0.0.1 rvzr-a.akamaihd.net 127.0.0.1 rvzr2-a.akamaihd.net +127.0.0.1 rwbd0.voluumtrk.com +127.0.0.1 rwnkj.voluumtrk.com +127.0.0.1 rxdja.voluumtrk.com 127.0.0.1 rxf.answcdn.com 127.0.0.1 ry7c5.directadsopt.com 127.0.0.1 ryan-lietz.us 127.0.0.1 rydium.us.intellitxt.com 127.0.0.1 ryerose.net +127.0.0.1 rygpq.voluumtrk.com 127.0.0.1 ryield.jmp9.com +127.0.0.1 rytj3.voluumtrk.com 127.0.0.1 rzr.tractionize.com 127.0.0.1 s-adserver.cxad.cxense.com 127.0.0.1 s-adserver.sandbox.cxad.cxense.com @@ -34245,6 +36285,7 @@ 127.0.0.1 s-jsonp.moatads.com 127.0.0.1 s-yoolk-banner-assets.yoolk.com 127.0.0.1 s-yoolk-billboard-assets.yoolk.com +127.0.0.1 s.206ads.com 127.0.0.1 s.2app.lk 127.0.0.1 s.ad120m.com 127.0.0.1 s.ad121m.com @@ -34268,6 +36309,7 @@ 127.0.0.1 s.agava.ru 127.0.0.1 s.amazon-adsystem.com 127.0.0.1 s.amazon-cornerstone.com +127.0.0.1 s.arlime.com 127.0.0.1 s.as997.de 127.0.0.1 s.ato.mx 127.0.0.1 s.btstatic.com @@ -34275,27 +36317,38 @@ 127.0.0.1 s.click.alimama.com 127.0.0.1 s.click.taobao.com 127.0.0.1 s.clicktale.net +127.0.0.1 s.cpx.to 127.0.0.1 s.effectivemeasure.net 127.0.0.1 s.ero-advertising.com 127.0.0.1 s.fgimg.com 127.0.0.1 s.free-porn-vidz.com 127.0.0.1 s.fxtimg.com +127.0.0.1 s.getsmartcontent.com 127.0.0.1 s.gstat.orange.fr +127.0.0.1 s.innovid.com 127.0.0.1 s.ixiaa.com 127.0.0.1 s.jwpltx.com +127.0.0.1 s.ktxtr.com +127.0.0.1 s.luxadv.com 127.0.0.1 s.m2pub.com +127.0.0.1 s.moatads.com 127.0.0.1 s.mobclix.com +127.0.0.1 s.motads.com 127.0.0.1 s.mousetrace.com 127.0.0.1 s.n.jwpltx.com 127.0.0.1 s.ndemiccreations.com +127.0.0.1 s.ntv.io 127.0.0.1 s.o-clk.com +127.0.0.1 s.po.st 127.0.0.1 s.rankdirectory.org 127.0.0.1 s.renren.com +127.0.0.1 s.rev2pub.com 127.0.0.1 s.sharethis.com 127.0.0.1 s.shopify.com 127.0.0.1 s.skimresources.com 127.0.0.1 s.speednetwork2.com 127.0.0.1 s.ssacdn.com +127.0.0.1 s.tagsrvcs.com 127.0.0.1 s.tcimg.com 127.0.0.1 s.tctm.co 127.0.0.1 s.tfile.ru @@ -34306,6 +36359,7 @@ 127.0.0.1 s.update.rubiconproject.com 127.0.0.1 s.userzoom.com 127.0.0.1 s.vgsgaming-ads.com +127.0.0.1 s.wnd.com 127.0.0.1 s.xp1.ru4.com 127.0.0.1 s.yjtag.jp 127.0.0.1 s.zeroredirect.com @@ -34325,10 +36379,12 @@ 127.0.0.1 s06i.categoria.hpg.com.br 127.0.0.1 s07.flagcounter.com 127.0.0.1 s0kdl.castup.net +127.0.0.1 s0s5e.voluumtrk.com 127.0.0.1 s1.2mdn.net 127.0.0.1 s1.freehostedscripts.net 127.0.0.1 s1.img.awempire.com 127.0.0.1 s1.ipicture.ru +127.0.0.1 s1.listrakbi.com 127.0.0.1 s1.mobile-tracking.gfsrv.net 127.0.0.1 s1.rotaban.ru 127.0.0.1 s1.shinystat.com @@ -34389,6 +36445,7 @@ 127.0.0.1 s290.meetrics.net 127.0.0.1 s290.mxcdn.net 127.0.0.1 s2hd-plugincom-maynemyltf.netdna-ssl.com +127.0.0.1 s2wrw.voluumtrk.com 127.0.0.1 s3-tracking.synthasite.net.s3.amazonaws.com 127.0.0.1 s3.buysellads.com 127.0.0.1 s3.ongsono.com @@ -34397,20 +36454,26 @@ 127.0.0.1 s32.research.de.com 127.0.0.1 s361.meetrics.net 127.0.0.1 s383.meetrics.net +127.0.0.1 s3bvd.voluumtrk.com 127.0.0.1 s3fdl.castup.net 127.0.0.1 s3kdl.castup.net +127.0.0.1 s3mzd.voluumtrk.com +127.0.0.1 s3nit.voluumtrk.com 127.0.0.1 s3rdl.castup.net 127.0.0.1 s4.histats.com 127.0.0.1 s4.shinystat.com 127.0.0.1 s4.trafficmaxx.de +127.0.0.1 s48bp.voluumtrk.com 127.0.0.1 s4candroid.2cnt.net 127.0.0.1 s4candroidtest.2cnt.net 127.0.0.1 s4cdotcom.2cnt.net 127.0.0.1 s4cdotcomtest.2cnt.net 127.0.0.1 s4cios.2cnt.net 127.0.0.1 s4ciostest.2cnt.net +127.0.0.1 s4orw.voluumtrk.com 127.0.0.1 s4smedia.go2cloud.org 127.0.0.1 s4snetwork.go2cloud.org +127.0.0.1 s4tac.voluumtrk.com 127.0.0.1 s5.ads.tradeads.eu 127.0.0.1 s5.com 127.0.0.1 s5.histats.com @@ -34418,16 +36481,21 @@ 127.0.0.1 s6.netlogstatic.com 127.0.0.1 s6.shinystat.com 127.0.0.1 s62.research.de.com +127.0.0.1 s6b3m.voluumtrk.com +127.0.0.1 s6hbo.voluumtrk.com +127.0.0.1 s6x7y.voluumtrk.com 127.0.0.1 s7.adaces.ero-advertising.com 127.0.0.1 s7.addthis.com 127.0.0.1 s7.cnzz.com 127.0.0.1 s7.shinystat.com 127.0.0.1 s73.cnzz.com +127.0.0.1 s8.algovid.com 127.0.0.1 s8.shinystat.com 127.0.0.1 s9.addthis.com 127.0.0.1 s9.shinystat.com 127.0.0.1 s90.cnzz.com 127.0.0.1 s95.research.de.com +127.0.0.1 s9kpd.voluumtrk.com 127.0.0.1 sa.bbc.co.uk 127.0.0.1 sa.bbc.com 127.0.0.1 sa.seotoaster.com @@ -34457,11 +36525,14 @@ 127.0.0.1 sana.newsinc.com.s3.amazonaws.com 127.0.0.1 sand-01.adnxs.com 127.0.0.1 sandbox-elb.beintoo.com +127.0.0.1 sandbox.outbrain.com 127.0.0.1 sandra-tester.us 127.0.0.1 sapremium.go2cloud.org 127.0.0.1 sara-machen.us 127.0.0.1 sarah-maurer.us +127.0.0.1 sarah.dntrax.com 127.0.0.1 sasha-castleman.us +127.0.0.1 sat.sanoma.fi 127.0.0.1 sat1.ivwbox.de 127.0.0.1 sat101.webtrekk.net 127.0.0.1 satgreera.com @@ -34480,14 +36551,19 @@ 127.0.0.1 sb.sellpoint.net 127.0.0.1 sb.vevo.com 127.0.0.1 sb1.surf-town.net +127.0.0.1 sb44a.voluumtrk.com 127.0.0.1 sba.about.co.kr 127.0.0.1 sbdhealth.go2cloud.org 127.0.0.1 sbs.2cnt.net 127.0.0.1 sbx.pagesjaunes.fr 127.0.0.1 sc-a-lbs.focalink.com 127.0.0.1 sc.ca.us.cddb.com +127.0.0.1 sc.rvtlife.com 127.0.0.1 sc.tradetracker.net 127.0.0.1 sc.tynt.com +127.0.0.1 sc2bg.voluumtrk.com +127.0.0.1 sc5pr.voluumtrk.com +127.0.0.1 sca1.listrakbi.com 127.0.0.1 scand.adlink.de 127.0.0.1 scarlett-esser.us 127.0.0.1 scd.secureintl.com @@ -34497,9 +36573,10 @@ 127.0.0.1 schuelvz.ivwbox.de 127.0.0.1 schumacher.adtech.fr 127.0.0.1 sciamglobal.112.2o7.net -127.0.0.1 scientiamobile.com +127.0.0.1 scie.ovh 127.0.0.1 scimmiablu.altervista.org 127.0.0.1 sclick.baidu.com +127.0.0.1 scootloor.com 127.0.0.1 scorecard.wspisp.net 127.0.0.1 scores-brataudit.2cnt.net 127.0.0.1 scores01.2cnt.net @@ -34508,12 +36585,16 @@ 127.0.0.1 scout.lexisnexis.com 127.0.0.1 scout.rollcall.com 127.0.0.1 scp.adip.ly +127.0.0.1 scptn.voluumtrk.com 127.0.0.1 screencapturewidget.aebn.net 127.0.0.1 scribol.com 127.0.0.1 scrippsfoodnet.112.2o7.net 127.0.0.1 script.blamads.com +127.0.0.1 script.crazyegg.com 127.0.0.1 script.footprintlive.com 127.0.0.1 script.ioam.de +127.0.0.1 script.leadboxer.com +127.0.0.1 script.opentracker.net 127.0.0.1 script.starpass.fr 127.0.0.1 script.tailsweep.com 127.0.0.1 scripts.adrcdn.com @@ -34527,6 +36608,7 @@ 127.0.0.1 scripts.gmtracker.com 127.0.0.1 scripts.hellobar.com 127.0.0.1 scripts.insite.com.br +127.0.0.1 scripts.kiosked.com 127.0.0.1 scripts.kissmetrics.com 127.0.0.1 scripts.lycos.com 127.0.0.1 scripts.powermarketing.com @@ -34561,6 +36643,7 @@ 127.0.0.1 sdk-b.apptornado.com 127.0.0.1 sdk.appbrain.com 127.0.0.1 sdk.hockeyapp.net +127.0.0.1 sdk.justad.mobi 127.0.0.1 sdk.testflightapp.com 127.0.0.1 sdkm.w.inmobi.com 127.0.0.1 sdr.totango.com @@ -34575,6 +36658,7 @@ 127.0.0.1 seal.verisign.com 127.0.0.1 search-images.com 127.0.0.1 search-results.com +127.0.0.1 search-results.mobi 127.0.0.1 search-tracker.com 127.0.0.1 search.clicksor.com 127.0.0.1 search.dealply.com @@ -34593,7 +36677,11 @@ 127.0.0.1 search.url.com 127.0.0.1 search.us.com 127.0.0.1 searchacts.com +127.0.0.1 searchesinteractive.com +127.0.0.1 searchfusion.com 127.0.0.1 searching-mails.org +127.0.0.1 searchingresult.com +127.0.0.1 searchinquire.com 127.0.0.1 searchlinear.com 127.0.0.1 searchlocate.com 127.0.0.1 searchmaybe.com @@ -34608,6 +36696,7 @@ 127.0.0.1 searsholdings.tt.omtrdc.net 127.0.0.1 seawood.org 127.0.0.1 sebeadmin.justclick.ru +127.0.0.1 sec.hit.gemius.pl 127.0.0.1 sec.yimg.com 127.0.0.1 secimage.adtech.fr 127.0.0.1 secoptim.com @@ -34634,16 +36723,21 @@ 127.0.0.1 secure.advertising.com 127.0.0.1 secure.adzerk.net 127.0.0.1 secure.applifier.com +127.0.0.1 secure.asiansluttease.com 127.0.0.1 secure.avangate.com 127.0.0.1 secure.bidvertiser.com +127.0.0.1 secure.bigfatjuicylesbians.com 127.0.0.1 secure.cpaempire.com 127.0.0.1 secure.download-sponsor.de 127.0.0.1 secure.eloqua.com +127.0.0.1 secure.exoclick.com +127.0.0.1 secure.flashtalking.com 127.0.0.1 secure.footprint.net 127.0.0.1 secure.fortuneaffiliates.com 127.0.0.1 secure.gaug.es 127.0.0.1 secure.gtop.ro 127.0.0.1 secure.ifbyphone.com +127.0.0.1 secure.img-cdn.mediaplex.com 127.0.0.1 secure.leadback.advertising.com 127.0.0.1 secure.livejasmin.com 127.0.0.1 secure.medleyads.com @@ -34656,6 +36750,9 @@ 127.0.0.1 secure.xsrving.com 127.0.0.1 secure128.go2cloud.org 127.0.0.1 secure3.go2cloud.org +127.0.0.1 secureadcenter.com +127.0.0.1 securejoinsite.com +127.0.0.1 securejump.net 127.0.0.1 securemetrics.apple.com 127.0.0.1 securepaths.com 127.0.0.1 securepubads.g.doubleclick.net @@ -34668,6 +36765,8 @@ 127.0.0.1 seevolution.com 127.0.0.1 seg.sharethis.com 127.0.0.1 sega.go2cloud.org +127.0.0.1 segapi.quantserve.com +127.0.0.1 segment-data.zqtk.net 127.0.0.1 segment-pixel.invitemedia.com 127.0.0.1 segment.com 127.0.0.1 segment.io @@ -34692,7 +36791,6 @@ 127.0.0.1 selina-scarlett.us 127.0.0.1 selina-wimmer.us 127.0.0.1 sellads.eu -127.0.0.1 sellfy.com 127.0.0.1 sem.shopexplorer.com 127.0.0.1 semrush.com 127.0.0.1 send.microad.jp @@ -34722,6 +36820,7 @@ 127.0.0.1 serve.williamhill.com 127.0.0.1 serve.williamhill.it 127.0.0.1 serve.williamhillcasino.com.27688.9216.302br.net +127.0.0.1 servecontent.net 127.0.0.1 servedby.adsdumpo.com 127.0.0.1 servedby.adsfactor.net 127.0.0.1 servedby.adsplats.com @@ -34801,6 +36900,7 @@ 127.0.0.1 servedby.openxmarket.jp 127.0.0.1 servedby.orbisvideo.com 127.0.0.1 servedby.revcontent.com +127.0.0.1 servedby.reviveservers.com 127.0.0.1 servedby1.advertising.com 127.0.0.1 servedby2.advertising.com 127.0.0.1 servedby3.advertising.com @@ -34841,10 +36941,12 @@ 127.0.0.1 server3.web-stat.com 127.0.0.1 serverfarm.continentads.com 127.0.0.1 serverwatch.de.intellitxt.com +127.0.0.1 servesharp.net 127.0.0.1 servethis.com 127.0.0.1 service-stat.tbn.ru 127.0.0.1 service.adtech.fr 127.0.0.1 service.clicksvenue.com +127.0.0.1 service.maxymiser.net 127.0.0.1 service.sponsorpay.com 127.0.0.1 service.urchin.com 127.0.0.1 service001.adtech.fr @@ -34860,19 +36962,25 @@ 127.0.0.1 services.qualaroo.com 127.0.0.1 services.serving-sys.com 127.0.0.1 services.webestools.com +127.0.0.1 services.wes.df.telemetry.microsoft.com 127.0.0.1 services.x-traceur.com 127.0.0.1 services1.adtech.fr 127.0.0.1 servicetick.com 127.0.0.1 serving-sys.com.36659.9244.302br.net 127.0.0.1 serving.adsrevenue.clicksor.net 127.0.0.1 serving.asdrevenue.clicksor.net +127.0.0.1 serving.plexop.net +127.0.0.1 servlet.adten.eu 127.0.0.1 serw.clicksor.com 127.0.0.1 serw.myroitracking.com 127.0.0.1 session-tracker.badcreditloans.com +127.0.0.1 sessionm.com 127.0.0.1 seth.avazutracking.net 127.0.0.1 setihome.com 127.0.0.1 seto.createsend.com 127.0.0.1 sett.i12.de +127.0.0.1 settings-sandbox.data.microsoft.com +127.0.0.1 settings-win.data.microsoft.com 127.0.0.1 settings.crashlytics.com 127.0.0.1 setup01.ixn.local.vmsn.de 127.0.0.1 seu.cleverreach.com @@ -34885,6 +36993,8 @@ 127.0.0.1 sextube2.bitterstrawberry.com 127.0.0.1 sexy.fling.com 127.0.0.1 sf.vserv.mobi +127.0.0.1 sfl.downloadfilesnow.info +127.0.0.1 sfl.erw.downloadfilesnow.info 127.0.0.1 sftp.barb.2cnt.net 127.0.0.1 sftp.clickability.com 127.0.0.1 sftrack.searchforce.net @@ -34905,9 +37015,11 @@ 127.0.0.1 shantel-poulin.us 127.0.0.1 shape.ivwbox.de 127.0.0.1 shara-brimer.us +127.0.0.1 share.9cdn.net 127.0.0.1 share.loginradius.com 127.0.0.1 share.pluso.ru 127.0.0.1 shareasale.com +127.0.0.1 shared.9msn.com.au 127.0.0.1 sharemyclick.go2cloud.org 127.0.0.1 shari-baskerville.us 127.0.0.1 shari-leggett.us @@ -34923,6 +37035,8 @@ 127.0.0.1 sherrie-wherry.us 127.0.0.1 sherry-klingbeil.us 127.0.0.1 shfr.hernando.com +127.0.0.1 shield.sitelock.com +127.0.0.1 shift.nuggad.net 127.0.0.1 shinystat.com 127.0.0.1 shinystat.it 127.0.0.1 shinystat.lvlar.com @@ -34968,6 +37082,7 @@ 127.0.0.1 signpost.go2cloud.org 127.0.0.1 signup.clicksor.com 127.0.0.1 siliconaction.com +127.0.0.1 silverpush.com 127.0.0.1 silvia-maffei.us 127.0.0.1 sim.thetrafficstat.net 127.0.0.1 simg.sinajs.cn @@ -34980,6 +37095,7 @@ 127.0.0.1 simpleonlinemedia.go2cloud.org 127.0.0.1 simyo.adclear.net 127.0.0.1 sin.ero-advertising.com +127.0.0.1 sinatra.addemam.eu 127.0.0.1 sired.com 127.0.0.1 site-stats.com 127.0.0.1 site-stats.i8.com @@ -35032,6 +37148,8 @@ 127.0.0.1 skyrocketingmedia.go2cloud.org 127.0.0.1 sl3tsw6.com 127.0.0.1 slashphone.us.intellitxt.com +127.0.0.1 slendastic.com +127.0.0.1 slfpu.com 127.0.0.1 slider.plugrush.com 127.0.0.1 slimtrade.com 127.0.0.1 slinse.com @@ -35048,6 +37166,7 @@ 127.0.0.1 sm8.sitemeter.com 127.0.0.1 sm9.sitemeter.com 127.0.0.1 sma.punto.net +127.0.0.1 smaato.net 127.0.0.1 smart-ip.net 127.0.0.1 smart.allocine.fr 127.0.0.1 smart.montiera.com @@ -35091,6 +37210,7 @@ 127.0.0.1 sms.otair.com 127.0.0.1 sms02.ad.netzquadrat.de 127.0.0.1 sms18.in.com +127.0.0.1 smssts.3g.cn 127.0.0.1 smtp.ero-advertising.com 127.0.0.1 smtp.kissmetrics.com 127.0.0.1 smtp.mobpartner.mobi @@ -35116,12 +37236,14 @@ 127.0.0.1 soaperformance.adition.com 127.0.0.1 soastage.adition.com 127.0.0.1 soav1.adition.com +127.0.0.1 soccer.searchwho.com 127.0.0.1 social.msgplus.net 127.0.0.1 socialize-it.com 127.0.0.1 socialskive.com 127.0.0.1 sociomantic.com 127.0.0.1 socitm.govmetric.com 127.0.0.1 soclock.com +127.0.0.1 sofa.bankofamerica.com 127.0.0.1 softcash.biz 127.0.0.1 softclick.com.br 127.0.0.1 softcore.xxxcounter.com @@ -35156,10 +37278,12 @@ 127.0.0.1 soundadgroup.go2cloud.org 127.0.0.1 soundbeat.go2cloud.org 127.0.0.1 soundslam.us.intellitxt.com +127.0.0.1 soundwave.mobile.yahoo.com 127.0.0.1 southwestairlines.tt.omtrdc.net 127.0.0.1 sovereign.sitetracker.com 127.0.0.1 sovetnik.go2cloud.org 127.0.0.1 sp.analytics.yahoo.com +127.0.0.1 sp.udimg.com 127.0.0.1 spacash.com 127.0.0.1 spaces.ero-advertising.com 127.0.0.1 spaces.slimspots.com @@ -35189,6 +37313,7 @@ 127.0.0.1 speedclicks.ero-advertising.com 127.0.0.1 speedclics.ero-advertising.com 127.0.0.1 speedlicks.ero-advertising.com +127.0.0.1 speednetwork6.adk2x.com 127.0.0.1 speedtrap.shopdirect.com 127.0.0.1 speeuclicks.ero-advertising.com 127.0.0.1 sperse.com @@ -35222,6 +37347,7 @@ 127.0.0.1 sportsapp-com.maynemyltf.netdna-cdn.com 127.0.0.1 spotexchange.com 127.0.0.1 spotlight.accuweather.com +127.0.0.1 spots.ah-me.com 127.0.0.1 spotsniper.ru 127.0.0.1 sprfetch.about.com 127.0.0.1 springboardplatform.com @@ -35244,6 +37370,9 @@ 127.0.0.1 spywarenuker.com 127.0.0.1 sq2trk2.com 127.0.0.1 sql.doublepimp.com +127.0.0.1 sqm.df.telemetry.microsoft.com +127.0.0.1 sqm.telemetry.microsoft.com +127.0.0.1 sqm.telemetry.microsoft.com.nsatc.net 127.0.0.1 squarespace.evyy.net 127.0.0.1 sr.adrevolver.com 127.0.0.1 src.kitcode.net @@ -35272,6 +37401,8 @@ 127.0.0.1 srv3.bannercommunity.de 127.0.0.1 srv3.wa.marketingsolutions.yahoo.com 127.0.0.1 srv6.mobpedia.com +127.0.0.1 srvabc.com +127.0.0.1 srver110.xyz 127.0.0.1 srvjs.media.net 127.0.0.1 srvpub.com 127.0.0.1 srx.at.ebayrtm.com @@ -35295,6 +37426,7 @@ 127.0.0.1 ss.crowdprocess.com 127.0.0.1 ss.linksynergy.com 127.0.0.1 ss.tiscali.it +127.0.0.1 ss.xxxnavy.com 127.0.0.1 ss2.zedo.com 127.0.0.1 ssangyong.co.il 127.0.0.1 sscdn.banners.advidi.com @@ -35318,6 +37450,7 @@ 127.0.0.1 ssl-vznetzme.ivwbox.de 127.0.0.1 ssl-vznetzst.ivwbox.de 127.0.0.1 ssl-youtube.2cnt.net +127.0.0.1 ssl.clickbank.net 127.0.0.1 ssl.fra.vmsn.de 127.0.0.1 ssl.google-analytics.com 127.0.0.1 ssl.linksynergy.com @@ -35326,7 +37459,9 @@ 127.0.0.1 ssl.xplosion.de 127.0.0.1 ssl1.fra.vmsn.de 127.0.0.1 sslad.adcash.com +127.0.0.1 ssljscdn.airbrake.io 127.0.0.1 sso.conduit.com +127.0.0.1 ssp.virool.com 127.0.0.1 sstatic.etracker.com 127.0.0.1 sstatic1.histats.com 127.0.0.1 sstats.adobe.com @@ -35335,7 +37470,9 @@ 127.0.0.1 sszomsa.imdrv.net 127.0.0.1 st-ops.stage.adition.com 127.0.0.1 st.a-link.co.kr +127.0.0.1 st.bebi.com 127.0.0.1 st.blogads.com +127.0.0.1 st.dynamicyield.com 127.0.0.1 st.ladycash.ru 127.0.0.1 st.listrak.com 127.0.0.1 st.madisonlogic.com @@ -35414,6 +37551,7 @@ 127.0.0.1 start.xxxcounter.com 127.0.0.1 startapp.com 127.0.0.1 startapp.go2cloud.org +127.0.0.1 startappexchange.com 127.0.0.1 starterdir.ru 127.0.0.1 starwoodhotelsandres.tt.omtrdc.net 127.0.0.1 stat.4u.pl @@ -35473,23 +37611,29 @@ 127.0.0.1 stat4.edev.at 127.0.0.1 statcounter.com 127.0.0.1 statdb.pressflex.com +127.0.0.1 stateresolver.link 127.0.0.1 stathat.com +127.0.0.1 static-analytics.nativeads.com 127.0.0.1 static-cdn.anetwork.ir 127.0.0.1 static-goaheadtours.netdna-ssl.com +127.0.0.1 static-rtb.adkernel.com 127.0.0.1 static-ssl.exoclick.com 127.0.0.1 static-trackers.adtarget.me +127.0.0.1 static-www.ec.popcap.com +127.0.0.1 static.2mdn.net 127.0.0.1 static.360buyimg.com 127.0.0.1 static.acs86.com 127.0.0.1 static.addinto.com 127.0.0.1 static.addtoany.com 127.0.0.1 static.addynamo.net -127.0.0.1 static.adf.ly 127.0.0.1 static.adfarm1.adition.com 127.0.0.1 static.ads.crakmedia.com 127.0.0.1 static.adsafeprotected.com 127.0.0.1 static.adsender.us +127.0.0.1 static.adsnative.com 127.0.0.1 static.adzerk.net 127.0.0.1 static.adziff.com +127.0.0.1 static.affilae.com 127.0.0.1 static.alimama.com 127.0.0.1 static.ap.bittorrent.com 127.0.0.1 static.app.widdit.com @@ -35502,10 +37646,10 @@ 127.0.0.1 static.atdmt.com.68423.9546.302br.net 127.0.0.1 static.atdmt.com.75273.9615.302br.net 127.0.0.1 static.atomload.at +127.0.0.1 static.audienceinsights.net 127.0.0.1 static.awempire.com 127.0.0.1 static.bannerbank.ru 127.0.0.1 static.beintoo.com -127.0.0.1 static.ccm2.net 127.0.0.1 static.cdn.gtsmobi.com 127.0.0.1 static.cdn.puatraffic.com 127.0.0.1 static.chartbeat.com @@ -35518,6 +37662,8 @@ 127.0.0.1 static.criteo.net 127.0.0.1 static.crowdscience.com 127.0.0.1 static.csbew.com +127.0.0.1 static.dcoengine.com +127.0.0.1 static.digidip.net 127.0.0.1 static.directrev.com 127.0.0.1 static.doubleclick.com 127.0.0.1 static.doubleclick.net @@ -35534,8 +37680,10 @@ 127.0.0.1 static.doubleclick.net.51253.9318.302br.net 127.0.0.1 static.doubleclick.net.67594.9515.302br.net 127.0.0.1 static.doubleclick.net.76539.9544.302br.net +127.0.0.1 static.elded.eu 127.0.0.1 static.eplayer.performgroup.com 127.0.0.1 static.estebull.com +127.0.0.1 static.etracker.com 127.0.0.1 static.everyone.net 127.0.0.1 static.everyplay.com 127.0.0.1 static.exoclick.com @@ -35549,26 +37697,31 @@ 127.0.0.1 static.groupon.co.uk 127.0.0.1 static.hoptopboy.com 127.0.0.1 static.hotjar.com +127.0.0.1 static.htmlhubing.xyz 127.0.0.1 static.hubspot.com 127.0.0.1 static.hupso.com 127.0.0.1 static.icmwebserv.com +127.0.0.1 static.ifa.camads.net 127.0.0.1 static.it.groupon-content.net 127.0.0.1 static.itrack.it 127.0.0.1 static.kameleoon.com 127.0.0.1 static.kinghost.com 127.0.0.1 static.kinley.com +127.0.0.1 static.l3.cdn.adbucks.com 127.0.0.1 static.livejasmin.com 127.0.0.1 static.mailchimp.com 127.0.0.1 static.manwin.doublepimp.com 127.0.0.1 static.matchnet.com 127.0.0.1 static.mediav.com 127.0.0.1 static.mlt01.com +127.0.0.1 static.mmotraffic.com 127.0.0.1 static.mobilecore.com 127.0.0.1 static.ndoverdrive.net 127.0.0.1 static.ngbn.net 127.0.0.1 static.nirror.com 127.0.0.1 static.nrelate.com 127.0.0.1 static.parse.ly +127.0.0.1 static.parsely.com 127.0.0.1 static.pixazza.com 127.0.0.1 static.plista.com 127.0.0.1 static.plugrush.com @@ -35579,16 +37732,19 @@ 127.0.0.1 static.rts.phn.doublepimp.com 127.0.0.1 static.salesresourcepartners.com 127.0.0.1 static.serving-sys.com.47157.9349.302br.net -127.0.0.1 static.shorte.st 127.0.0.1 static.slfpu.com +127.0.0.1 static.smowtion.com 127.0.0.1 static.smsac.net.edgesuite.net 127.0.0.1 static.streamate.doublepimp.com 127.0.0.1 static.styria-digital.com 127.0.0.1 static.superfish.com +127.0.0.1 static.t4btv.com +127.0.0.1 static.teasermedia.net 127.0.0.1 static.tellaparts.com 127.0.0.1 static.theadex.com 127.0.0.1 static.thumbr.com 127.0.0.1 static.tradetracker.net +127.0.0.1 static.traffe.eu 127.0.0.1 static.trafficjunky.net 127.0.0.1 static.uolcontent.com 127.0.0.1 static.verticalscope.com @@ -35596,9 +37752,11 @@ 127.0.0.1 static.vpptechnologies.com 127.0.0.1 static.weborama.fr 127.0.0.1 static.xpct.de +127.0.0.1 static.yieldmo.com 127.0.0.1 static.zangocash.com 127.0.0.1 static.zanox.com 127.0.0.1 static.zedo.com +127.0.0.1 static.ziffdavis.com 127.0.0.1 static0.awempire.com 127.0.0.1 static1.awempire.com 127.0.0.1 static1.baifendian.com @@ -35724,7 +37882,9 @@ 127.0.0.1 stats.irishmirror.ie 127.0.0.1 stats.itsol.it 127.0.0.1 stats.iwebtrack.com +127.0.0.1 stats.justad.mobi 127.0.0.1 stats.justin.tv +127.0.0.1 stats.kaltura.com 127.0.0.1 stats.lifenews.ru 127.0.0.1 stats.lnol.com.ar 127.0.0.1 stats.macmillanusa.com @@ -35803,6 +37963,8 @@ 127.0.0.1 stats.wetpaint.com 127.0.0.1 stats.wew173.com 127.0.0.1 stats.wieistmeineip.de +127.0.0.1 stats.wired.com +127.0.0.1 stats.wittyfeed.com 127.0.0.1 stats.wordpress.com 127.0.0.1 stats.wp.com 127.0.0.1 stats.wwd.com @@ -35864,6 +38026,7 @@ 127.0.0.1 storage.creative-mobile.com 127.0.0.1 storage.ivwbox.de 127.0.0.1 storage.outbrain.com +127.0.0.1 storage.psstgccint.com 127.0.0.1 storage.superfish.com 127.0.0.1 store.greatdealnews.com 127.0.0.1 store.lavasoft.com @@ -35882,9 +38045,11 @@ 127.0.0.1 strikeadcdn.s3.amazonaws.com 127.0.0.1 strk.enlnks.com 127.0.0.1 strtrk.go2cloud.org +127.0.0.1 struct.nt-com.co 127.0.0.1 studio.doubleclick.com 127.0.0.1 studio.doubleclick.net 127.0.0.1 studivz.adfarm1.adition.com +127.0.0.1 stuntoffer.com 127.0.0.1 stuttest.ivwbox.de 127.0.0.1 stutzeit.ivwbox.de 127.0.0.1 stvdotcom.2cnt.net @@ -35915,6 +38080,7 @@ 127.0.0.1 sub.hablum.info 127.0.0.1 sub.uniust.info 127.0.0.1 submitexpress.co.uk +127.0.0.1 subs.flixhangar.com 127.0.0.1 success.act-on.com 127.0.0.1 success123.com 127.0.0.1 successalert.com @@ -35960,9 +38126,11 @@ 127.0.0.1 surfers.ro 127.0.0.1 surfingprizes.com 127.0.0.1 survey.112.2o7.net +127.0.0.1 survey.122.2o7.net 127.0.0.1 survey.burstmedia.com 127.0.0.1 survey.com-annualsurvey.info 127.0.0.1 survey.foreseeresults.com +127.0.0.1 survey.g.doubleclick.net 127.0.0.1 survey.interquest.com 127.0.0.1 survey.ivwbox.de 127.0.0.1 survey.questionmarket.com @@ -35978,6 +38146,7 @@ 127.0.0.1 susanna-shane.us 127.0.0.1 susanna-warriner.us 127.0.0.1 susi.adtech.fr +127.0.0.1 suthome.com 127.0.0.1 suvinleo.justclick.ru 127.0.0.1 suzanne-healy.us 127.0.0.1 suzi-champ.us @@ -35985,6 +38154,7 @@ 127.0.0.1 suzi-vogelsang.us 127.0.0.1 svast.moatads.com 127.0.0.1 svastx.moatads.com +127.0.0.1 svc-analytics.adkernel.com 127.0.0.1 svn01.dus.local.vmsn.de 127.0.0.1 svn01.dus.vmsn.de 127.0.0.1 svn01.vmsn.de @@ -36012,13 +38182,16 @@ 127.0.0.1 switch6.castup.net 127.0.0.1 swww.sc.pages02.net 127.0.0.1 swx.domob.cn +127.0.0.1 sx.thrnt.com 127.0.0.1 sydney-steil.us 127.0.0.1 symantec.tt.omtrdc.net 127.0.0.1 sympmsnsports.112.2o7.net 127.0.0.1 synad.nuffnang.com.my 127.0.0.1 synad2.nuffnang.com.my 127.0.0.1 synad2.nuffnang.com.ph +127.0.0.1 sync.1rx.io 127.0.0.1 sync.adap.tv +127.0.0.1 sync.adaptv.advertising.com 127.0.0.1 sync.adjuggler.zenoviaexchange.com 127.0.0.1 sync.apnx.zenoviaexchange.com 127.0.0.1 sync.go.sonobi.com @@ -36037,6 +38210,7 @@ 127.0.0.1 syndication.jobthread.com 127.0.0.1 syndication.loganmueller.info 127.0.0.1 syndication.openapi.naver.com +127.0.0.1 syndication.streamads.yahoo.com 127.0.0.1 syndication.teleborsa.it 127.0.0.1 syndication.theguardian.com 127.0.0.1 syndication.traffichaus.com @@ -36046,6 +38220,7 @@ 127.0.0.1 syndication1.traffichaus.com 127.0.0.1 synergetic.de 127.0.0.1 synthes.vo.llnwd.net +127.0.0.1 sys.itop.cz 127.0.0.1 sys.osttirol-online.at 127.0.0.1 syslog01.bob.local.vmsn.de 127.0.0.1 syslog01.bob.vmsn.de @@ -36069,8 +38244,11 @@ 127.0.0.1 t.9gag.com 127.0.0.1 t.acxiom-online.com 127.0.0.1 t.adbuyer.com +127.0.0.1 t.adonly.com +127.0.0.1 t.appsflyer.com 127.0.0.1 t.atdmt.com 127.0.0.1 t.bimvid.com +127.0.0.1 t.booksuper.info 127.0.0.1 t.channeladvisor.com 127.0.0.1 t.cpa37.com 127.0.0.1 t.cpmadvisors.com @@ -36079,23 +38257,32 @@ 127.0.0.1 t.dailymail.co.uk 127.0.0.1 t.devnet.com 127.0.0.1 t.dgm-au.com +127.0.0.1 t.dtscout.com 127.0.0.1 t.eharmony.com 127.0.0.1 t.extreme-dm.com 127.0.0.1 t.flux.com 127.0.0.1 t.formalyzer.com 127.0.0.1 t.hypers.com.cn +127.0.0.1 t.insigit.com 127.0.0.1 t.ivwbox.de 127.0.0.1 t.jobthread.com +127.0.0.1 t.ktxtr.com 127.0.0.1 t.leadlander.com 127.0.0.1 t.leady.com 127.0.0.1 t.locpub.com 127.0.0.1 t.mdn2015x1.com +127.0.0.1 t.mdn2015x2.com 127.0.0.1 t.medialytics.com 127.0.0.1 t.menepe.com 127.0.0.1 t.mobilediva.com +127.0.0.1 t.mobitrk.com 127.0.0.1 t.mookie1.com +127.0.0.1 t.mpire.nxus.mobi 127.0.0.1 t.mplxtms.com +127.0.0.1 t.mtagmonetizationa.com +127.0.0.1 t.mtagmonetizationb.com 127.0.0.1 t.myfuncards.com +127.0.0.1 t.myvisualiq.net 127.0.0.1 t.nxus.mobi 127.0.0.1 t.offerx.co.uk 127.0.0.1 t.p.mybuys.com @@ -36106,12 +38293,15 @@ 127.0.0.1 t.qservz.com 127.0.0.1 t.searchelper.com 127.0.0.1 t.searchhelper.com +127.0.0.1 t.sf14g.com 127.0.0.1 t.skimresources.com +127.0.0.1 t.tellapart.com 127.0.0.1 t.thetrafficstat.net 127.0.0.1 t.trackalyzer.com 127.0.0.1 t.trackedlink.net 127.0.0.1 t.unbounce.com 127.0.0.1 t.update.moatads.com +127.0.0.1 t.varioususa.info 127.0.0.1 t.vimeo.com 127.0.0.1 t.webtracker.jp 127.0.0.1 t.wowanalytics.co.uk @@ -36183,6 +38373,7 @@ 127.0.0.1 tag.120ask.com 127.0.0.1 tag.163.com 127.0.0.1 tag.adip.ly +127.0.0.1 tag.bounceexchange.com 127.0.0.1 tag.contextweb.com 127.0.0.1 tag.crsspxl.com 127.0.0.1 tag.didit.com @@ -36208,6 +38399,7 @@ 127.0.0.1 tagcontainer.arvato-e.com 127.0.0.1 tagessch.ivwbox.de 127.0.0.1 tagjunction.com +127.0.0.1 tags-cdn.deployads.com 127.0.0.1 tags-eu.tiqcdn.com 127.0.0.1 tags.1001spiele.de 127.0.0.1 tags.api.umbel.com @@ -36218,12 +38410,14 @@ 127.0.0.1 tags.crwdcntrl.net 127.0.0.1 tags.expo9.exponential.com 127.0.0.1 tags.grajteraz.pl +127.0.0.1 tags.h12-media.com 127.0.0.1 tags.jiwire.com 127.0.0.1 tags.liveperson.net 127.0.0.1 tags.master-perf-tools.com 127.0.0.1 tags.mathtag.com 127.0.0.1 tags.mediaforge.com 127.0.0.1 tags.msnbc.com +127.0.0.1 tags.news.com.au 127.0.0.1 tags.pixnet.net 127.0.0.1 tags.pubgears.com 127.0.0.1 tags.qservz.com @@ -36246,7 +38440,9 @@ 127.0.0.1 tap.rubiconproject.com 127.0.0.1 tap2-cdn.rubiconproject.com 127.0.0.1 tapad.com +127.0.0.1 taper.ovh 127.0.0.1 tapestry.tapad.com +127.0.0.1 tapfiliate.com 127.0.0.1 tapfortap.com 127.0.0.1 tapgen.com 127.0.0.1 tapit.com @@ -36254,11 +38450,13 @@ 127.0.0.1 tapjoy.com 127.0.0.1 tapjoy.go2cloud.org 127.0.0.1 tapjoyads.com +127.0.0.1 tapone.jp 127.0.0.1 tapsense.com 127.0.0.1 tara-kyser.us 127.0.0.1 tarah-kohn.us 127.0.0.1 tarakc1.net 127.0.0.1 targad.com +127.0.0.1 target-talent.com 127.0.0.1 target.122.2o7.net 127.0.0.1 target.untd.com 127.0.0.1 target.vivid.com @@ -36268,6 +38466,7 @@ 127.0.0.1 targetpoint.com 127.0.0.1 targetspot.com 127.0.0.1 tarifliste.com +127.0.0.1 tarkita.ru 127.0.0.1 tarrinc.go2cloud.org 127.0.0.1 tasha-tatro.us 127.0.0.1 tasks.kissmetrics.com @@ -36351,6 +38550,7 @@ 127.0.0.1 techexpress.home.mindspring.com 127.0.0.1 techmarkaffi.go2cloud.org 127.0.0.1 technewsworld.us.intellitxt.com +127.0.0.1 technology.searchwho.com 127.0.0.1 techpatterns.com 127.0.0.1 techreview.112.2o7.net 127.0.0.1 techspot.us.intellitxt.com @@ -36363,22 +38563,31 @@ 127.0.0.1 teenrevenue.com 127.0.0.1 teknosurf3.com 127.0.0.1 teleboer.ivwbox.de +127.0.0.1 telecommand.telemetry.microsoft.com +127.0.0.1 telecommand.telemetry.microsoft.com.nsatc.net 127.0.0.1 telekbas.ivwbox.de 127.0.0.1 telekom01.webtrekk.net 127.0.0.1 telekom02.webtrekk.net +127.0.0.1 telemetry.appex.bing.net +127.0.0.1 telemetry.microsoft.com +127.0.0.1 telemetry.urs.microsoft.com 127.0.0.1 telenor.112.2o7.net 127.0.0.1 telenorstartsiden.112.2o7.net 127.0.0.1 teletari.ivwbox.de +127.0.0.1 television.searchwho.com 127.0.0.1 telstra.imrworldwide.com 127.0.0.1 temp.adzerver.com +127.0.0.1 tempest.services.disqus.com 127.0.0.1 template.aebn.net 127.0.0.1 tennae880interyield.jmp9.com 127.0.0.1 tennessean.us.intellitxt.com 127.0.0.1 tenzero.com +127.0.0.1 tenzing.fmpub.net 127.0.0.1 teresa-noriega.us 127.0.0.1 teresa-taveras.us 127.0.0.1 terms.reporo.net 127.0.0.1 terra.demdex.net +127.0.0.1 terraclicks.com 127.0.0.1 terrazzo.bnex.com 127.0.0.1 terren.cntv.cn 127.0.0.1 terri-camarena.us @@ -36407,6 +38616,7 @@ 127.0.0.1 test.superstats.com 127.0.0.1 test.yesadvertising.com 127.0.0.1 test.zedo.com +127.0.0.1 test1.lmsoft.cz 127.0.0.1 test1.sem-tracking-analytics.com 127.0.0.1 testadmin.cimedia.com 127.0.0.1 testandtarget.omniture.com @@ -36459,6 +38669,7 @@ 127.0.0.1 thehitsusa.com 127.0.0.1 theoads.com 127.0.0.1 theoads.com. +127.0.0.1 theodosium.com 127.0.0.1 thepoint.go2cloud.org 127.0.0.1 theresa-buchman.us 127.0.0.1 theresa-ellett.us @@ -36468,6 +38679,7 @@ 127.0.0.1 thetradedesk-tags.s3.amazonaws.com 127.0.0.1 thevanda.tk 127.0.0.1 thevictorynetwork.com +127.0.0.1 theylike.org 127.0.0.1 thindivide.com 127.0.0.1 thing1.infolink.com 127.0.0.1 thing2.infolink.com @@ -36493,6 +38705,7 @@ 127.0.0.1 thisbanner.com 127.0.0.1 thisteddagblad.dk 127.0.0.1 thor-cpa.com +127.0.0.1 threivetrack.com 127.0.0.1 thruport.com 127.0.0.1 thumb.ero-advertising.com 127.0.0.1 thumbads.ero-advertising.com @@ -36515,6 +38728,8 @@ 127.0.0.1 tigerloads.com 127.0.0.1 tigertext.com 127.0.0.1 til.go2cloud.org +127.0.0.1 tiles.services.mozilla.com +127.0.0.1 tiller.co 127.0.0.1 tim-eckhoff.us 127.0.0.1 timebus2.112.2o7.net 127.0.0.1 timecom.112.2o7.net @@ -36541,13 +38756,18 @@ 127.0.0.1 titanads3.com 127.0.0.1 titanads4.com 127.0.0.1 titanads5.com +127.0.0.1 titsbro.net +127.0.0.1 titsbro.org 127.0.0.1 tiumbs.ero-advertising.com 127.0.0.1 tizer8.net +127.0.0.1 tizerclik.com 127.0.0.1 tizermy.net 127.0.0.1 tizernaya-reklama.ru +127.0.0.1 tizru.com 127.0.0.1 tj.phpwind.net 127.0.0.1 tjs.maxiget.com 127.0.0.1 tk3.sbc60.com +127.0.0.1 tll.tl 127.0.0.1 tlvmedia.adk2.co 127.0.0.1 tlvmedia.com 127.0.0.1 tm.tradetracker.net @@ -36575,6 +38795,7 @@ 127.0.0.1 tom.vgwort.de 127.0.0.1 tom01.ixn.local.vmsn.de 127.0.0.1 tom2.int.vmsn.de +127.0.0.1 tomekas.com 127.0.0.1 toms.us.intellitxt.com 127.0.0.1 tomshard.ivwbox.de 127.0.0.1 tonefuse.go2cloud.org @@ -36582,6 +38803,7 @@ 127.0.0.1 toolbar.utorrent.com 127.0.0.1 toolbarmodal.medialeopard.com 127.0.0.1 toolbox.contentspread.net +127.0.0.1 tools.bongacams.com 127.0.0.1 tools.bongacash.com 127.0.0.1 tools.gfcash.com 127.0.0.1 tools.hitbox.com @@ -36594,6 +38816,7 @@ 127.0.0.1 toolsa.hitbox.com 127.0.0.1 top-fwz1.mail.ru 127.0.0.1 top-ro.ro +127.0.0.1 top-viral-videos.com 127.0.0.1 top.addfreestats.com 127.0.0.1 top.chebra.lt 127.0.0.1 top.dkd.lt @@ -36620,7 +38843,6 @@ 127.0.0.1 toppagefinder.com 127.0.0.1 toprebates.com 127.0.0.1 topsafelist.net -127.0.0.1 topsearcher.com 127.0.0.1 topsky.go2cloud.org 127.0.0.1 topspot.go2cloud.org 127.0.0.1 topstat.com @@ -36631,7 +38853,10 @@ 127.0.0.1 toroadvertisingmedia.com 127.0.0.1 torque.bittorrent.com 127.0.0.1 torr-d.torrpedoads.net +127.0.0.1 torrentinstaller.com +127.0.0.1 torrentinstallname.com 127.0.0.1 torrentprivacy.postaffiliatepro.com +127.0.0.1 torrentstorage.download 127.0.0.1 torssebu.com 127.0.0.1 torstarcollect.247realmedia.com 127.0.0.1 toruk.tanx.com @@ -36641,6 +38866,7 @@ 127.0.0.1 totalsystemservices.112.2o7.net 127.0.0.1 totemcash.com 127.0.0.1 touchclarity.com +127.0.0.1 tournevis.ovh 127.0.0.1 tours.imlive.com 127.0.0.1 towerrecords.track4.com 127.0.0.1 toyota.112.2o7.net @@ -36650,6 +38876,7 @@ 127.0.0.1 tpcnc.googlesyndication.com 127.0.0.1 tpl1.realtracker.com 127.0.0.1 tpl2.realtracker.com +127.0.0.1 tppintext.com 127.0.0.1 tpu.2cnt.net 127.0.0.1 tr-metrics.loomia.com 127.0.0.1 tr.202trackingsoft.com @@ -36697,6 +38924,7 @@ 127.0.0.1 traceworks.com 127.0.0.1 traci-sirianni.us 127.0.0.1 traci-towery.us +127.0.0.1 track-east.mobilepassback.com 127.0.0.1 track.007cpa.com 127.0.0.1 track.12trackway.com 127.0.0.1 track.99acres.com @@ -36717,6 +38945,7 @@ 127.0.0.1 track.adxmi.com 127.0.0.1 track.affiliate-b.com 127.0.0.1 track.affktch.com +127.0.0.1 track.ahury.com 127.0.0.1 track.appsflyer.com 127.0.0.1 track.ashley-george.com 127.0.0.1 track.atgstores.com @@ -36810,6 +39039,7 @@ 127.0.0.1 track.scorpiointeractive.com 127.0.0.1 track.searchignite.com 127.0.0.1 track.sexchangegirl.com +127.0.0.1 track.shop2market.com 127.0.0.1 track.sigfig.com 127.0.0.1 track.slideshare.net 127.0.0.1 track.smcnetwork.net @@ -36822,6 +39052,8 @@ 127.0.0.1 track.tracking85.com 127.0.0.1 track.trademail.fr 127.0.0.1 track.trafficholder.com +127.0.0.1 track.turboyourpc.com +127.0.0.1 track.umeng.com 127.0.0.1 track.untd.com 127.0.0.1 track.v2.sslsecure1.com 127.0.0.1 track.v2.sslsecure10.com @@ -36836,6 +39068,7 @@ 127.0.0.1 track.webgains.com 127.0.0.1 track.websiteceo.com 127.0.0.1 track.webtrekk.net +127.0.0.1 track.webwindows.co.uk 127.0.0.1 track.wildblue.com 127.0.0.1 track.xtrasize.co.uk 127.0.0.1 track.xtrasize.com @@ -36909,6 +39142,7 @@ 127.0.0.1 tracker.roitesting.com 127.0.0.1 tracker.scao.it 127.0.0.1 tracker.seoboost.net +127.0.0.1 tracker.smartcounter.de 127.0.0.1 tracker.snake-leads.fr 127.0.0.1 tracker.swfstats.com 127.0.0.1 tracker.timesgroup.com @@ -36923,6 +39157,7 @@ 127.0.0.1 trackertracker.com 127.0.0.1 trackflv.net 127.0.0.1 trackicollect.ibase.fr +127.0.0.1 tracking.1moretoy.com 127.0.0.1 tracking.247search.com 127.0.0.1 tracking.actionads.ru 127.0.0.1 tracking.adjug.com @@ -36941,6 +39176,7 @@ 127.0.0.1 tracking.base.de 127.0.0.1 tracking.battleon.com 127.0.0.1 tracking.bbinary.com +127.0.0.1 tracking.binarypromos.com 127.0.0.1 tracking.bloomberg.com 127.0.0.1 tracking.bol.com 127.0.0.1 tracking.bucksense.com @@ -36968,6 +39204,7 @@ 127.0.0.1 tracking.euroads.no 127.0.0.1 tracking.euroads.se 127.0.0.1 tracking.eurosport.com +127.0.0.1 tracking.everydayhealth.com 127.0.0.1 tracking.fathomseo.com 127.0.0.1 tracking.feedperfect.com 127.0.0.1 tracking.fits.me @@ -36975,7 +39212,9 @@ 127.0.0.1 tracking.gamigoads.com 127.0.0.1 tracking.gammae.com 127.0.0.1 tracking.gfycat.com +127.0.0.1 tracking.godatafeed.com 127.0.0.1 tracking.goodgamestudios.com +127.0.0.1 tracking.groupon.com 127.0.0.1 tracking.gruppo.mps.it 127.0.0.1 tracking.ha.rueducommerce.fr 127.0.0.1 tracking.hannoversche.de @@ -36988,6 +39227,7 @@ 127.0.0.1 tracking.ientry.net 127.0.0.1 tracking.immobilienscout24.de 127.0.0.1 tracking.infoninjas.net +127.0.0.1 tracking.instantcheckmate.com 127.0.0.1 tracking.interweave.com 127.0.0.1 tracking.inuvo.com 127.0.0.1 tracking.jithasoffers.com @@ -37058,13 +39298,16 @@ 127.0.0.1 tracking.trackor.net 127.0.0.1 tracking.tradeking.com 127.0.0.1 tracking.traviangames.com +127.0.0.1 tracking.truthfinder.com 127.0.0.1 tracking.ukwm.co.uk 127.0.0.1 tracking.vcommission.com 127.0.0.1 tracking.veoxa.com 127.0.0.1 tracking.voltagesearch.com +127.0.0.1 tracking.waterfrontmedia.com 127.0.0.1 tracking.we7.com 127.0.0.1 tracking.websitealive.com 127.0.0.1 tracking.wfnetwork.com +127.0.0.1 tracking.wisepops.com 127.0.0.1 tracking.worldmedia.net 127.0.0.1 tracking01.bob.local.vmsn.de 127.0.0.1 tracking01.con.local.vmsn.de @@ -37082,6 +39325,8 @@ 127.0.0.1 trackingoffer.info 127.0.0.1 trackingoffer.net 127.0.0.1 trackingstatalytics.com +127.0.0.1 trackinguk2.com +127.0.0.1 trackit.skyhighctr.com 127.0.0.1 tracklead.net 127.0.0.1 tracknewsletter.com 127.0.0.1 trackpm.shop2market.com @@ -37094,6 +39339,7 @@ 127.0.0.1 trackyourstats.com 127.0.0.1 tractionize.com 127.0.0.1 trade-in-value.com +127.0.0.1 tradeadexchange.com 127.0.0.1 tradearabia.advertserve.com 127.0.0.1 tradedoubler.com 127.0.0.1 tradeexpert.net @@ -37114,13 +39360,17 @@ 127.0.0.1 traffic-supremacy.com 127.0.0.1 traffic.acwebconnecting.com 127.0.0.1 traffic.alexa.com +127.0.0.1 traffic.bannerator.com 127.0.0.1 traffic.bokecc.com 127.0.0.1 traffic.buyservices.com 127.0.0.1 traffic.cimedia.com 127.0.0.1 traffic.firstmobilelink.com 127.0.0.1 traffic.index.hu +127.0.0.1 traffic.moportals.com 127.0.0.1 traffic.ru 127.0.0.1 traffic.sexpillguru.com +127.0.0.1 traffic.trafficcompany.com +127.0.0.1 traffic.trafficposse.com 127.0.0.1 traffic.velvet.hu 127.0.0.1 traffic4you.de 127.0.0.1 trafficads.s3-website-us-west-1.amazonaws.com @@ -37166,10 +39416,13 @@ 127.0.0.1 traficdom.com 127.0.0.1 traficdublu.ro 127.0.0.1 traficmax.fr +127.0.0.1 trafmag.com 127.0.0.1 trakksocial.googlecode.com +127.0.0.1 traktum.com 127.0.0.1 trans20120430.getjar.com 127.0.0.1 transact-can.cedexis.com 127.0.0.1 transact.justclick.ru +127.0.0.1 transfermarkt01.wt-eu02.net 127.0.0.1 travel.netster.com 127.0.0.1 travel.travelocity.com 127.0.0.1 traveladvertising.com @@ -37179,6 +39432,7 @@ 127.0.0.1 travelscream.com 127.0.0.1 travidia.com 127.0.0.1 travsoft.bfast.com +127.0.0.1 trax-ad.jp 127.0.0.1 trax.gamespot.com 127.0.0.1 tray-e-ex.tekblue.net 127.0.0.1 tray-w-ex.tekblue.net @@ -37194,9 +39448,11 @@ 127.0.0.1 treasuretrooper.com 127.0.0.1 tredirect.com 127.0.0.1 tree-pixel-log.s3.amazonaws.com +127.0.0.1 trefogli8.adulttargettrack.pw 127.0.0.1 trekblue.com 127.0.0.1 trekdata.com 127.0.0.1 tremorvideo.com +127.0.0.1 trends.revcontent.com 127.0.0.1 tresalacarta.2cnt.net 127.0.0.1 trhzc.com 127.0.0.1 triadmedianetwork.com @@ -37232,7 +39488,9 @@ 127.0.0.1 trk.pswec.com 127.0.0.1 trk.rrcpm.com 127.0.0.1 trk.sele.co +127.0.0.1 trk.trafficposse.com 127.0.0.1 trk.vaultrk.com +127.0.0.1 trk.vidible.tv 127.0.0.1 trk.vindicosuite.com 127.0.0.1 trk02.gtrk.info 127.0.0.1 trk2it4.com @@ -37244,7 +39502,9 @@ 127.0.0.1 trklnks.com 127.0.0.1 trks.us 127.0.0.1 trksst.com +127.0.0.1 trkuoy.com 127.0.0.1 trkur.com +127.0.0.1 trkur1.com 127.0.0.1 trkur5.com 127.0.0.1 trkzen.com 127.0.0.1 trollzeweb.com @@ -37286,6 +39546,7 @@ 127.0.0.1 ts2.hitbox.com 127.0.0.1 ts2zz.diet.bannernumber.eu 127.0.0.1 ts777.com +127.0.0.1 tsog8.redirectvoluum.com 127.0.0.1 tt.omtrdc.net 127.0.0.1 ttlmodels.com 127.0.0.1 ttwbs.channelintelligence.com @@ -37303,6 +39564,7 @@ 127.0.0.1 tutvp.com 127.0.0.1 tv.livejasmin.com 127.0.0.1 tv.sohu.com +127.0.0.1 tv4chan.com 127.0.0.1 tvinfo.ivwbox.de 127.0.0.1 tvlistings3.zap2it.com 127.0.0.1 tvlistings4.zap2it.com @@ -37322,7 +39584,9 @@ 127.0.0.1 twitchtv.ivwbox.de 127.0.0.1 twitter-badges.s3.amazonaws.com 127.0.0.1 twneteng.ivwbox.de +127.0.0.1 twsg21x6-22dcca05c0915aa0dad0a07273e7a3c5015ddc6d-am1.d.aa.online-metrix.net 127.0.0.1 txel.snmmd.nl +127.0.0.1 txn.ingest.crittercism.com 127.0.0.1 txtimpact.com 127.0.0.1 tynt.com 127.0.0.1 typiol.com @@ -37593,9 +39857,11 @@ 127.0.0.1 u3417.93.spylog.com 127.0.0.1 u342.39.spylog.com 127.0.0.1 u3436.19.spylog.com +127.0.0.1 u35233.nilopo.ru 127.0.0.1 u3571.56.spylog.com 127.0.0.1 u3585.14.spylog.com 127.0.0.1 u3608.20.spylog.com +127.0.0.1 u38005.nilopo.ru 127.0.0.1 u385.24.spylog.com 127.0.0.1 u4056.56.spylog.com 127.0.0.1 u4122.70.spylog.com @@ -37634,18 +39900,22 @@ 127.0.0.1 uac.advertising.com 127.0.0.1 uadx.com 127.0.0.1 ubermedia.com +127.0.0.1 ucaluco.com 127.0.0.1 uclick.com 127.0.0.1 ucounter.ucoz.net 127.0.0.1 ucoz.spotsniper.ru 127.0.0.1 udac.se +127.0.0.1 udarem.com 127.0.0.1 udc.msn.com 127.0.0.1 udelivery.trademob.com 127.0.0.1 udf.staging.adition.com 127.0.0.1 udilova.justclick.ru 127.0.0.1 udm.scorecardresearch.com +127.0.0.1 udmserve.net 127.0.0.1 udns1.newdotnet.net 127.0.0.1 uenetwork.go2cloud.org 127.0.0.1 uestat.video.qiyi.com +127.0.0.1 ueuerea.com 127.0.0.1 ugaral.com 127.0.0.1 ugo.tractionize.com 127.0.0.1 ui.avid.doublepimp.com @@ -37685,11 +39955,13 @@ 127.0.0.1 ulock.it 127.0.0.1 ultimacash.go2cloud.org 127.0.0.1 ultimate-guitar.us.intellitxt.com +127.0.0.1 ultimategracelessness.info 127.0.0.1 ultimatelyreveal.com 127.0.0.1 ultsearch.com 127.0.0.1 um-per.com 127.0.0.1 um.eqads.com 127.0.0.1 um.simpli.fi +127.0.0.1 umbel.com 127.0.0.1 umbs.ero-advertising.com 127.0.0.1 ums.adtech.de 127.0.0.1 unadvertising.info @@ -37702,6 +39974,7 @@ 127.0.0.1 unica.com 127.0.0.1 unicast.com 127.0.0.1 unicast.ign.com +127.0.0.1 unicume.com 127.0.0.1 unicume.go2cloud.org 127.0.0.1 union2.50bang.org 127.0.0.1 uniqueleads.com @@ -37709,10 +39982,12 @@ 127.0.0.1 unisonsas.bravejournal.com 127.0.0.1 universal-traffic.com 127.0.0.1 unlck.net +127.0.0.1 unlimitedtextads.com 127.0.0.1 uno.wbrotator.biz 127.0.0.1 unrulymedia.com 127.0.0.1 unstat.baidu.com 127.0.0.1 unsubscribe.rmv.so +127.0.0.1 uobsoe.com 127.0.0.1 up.cm.ksmobile.com 127.0.0.1 up.nytimes.com 127.0.0.1 upads.info @@ -37726,9 +40001,11 @@ 127.0.0.1 upload.zedo.com 127.0.0.1 uproar.com 127.0.0.1 upsellit.com +127.0.0.1 upsight.com 127.0.0.1 upsnap.com 127.0.0.1 upstream.where.com 127.0.0.1 uptpro.homestead.com +127.0.0.1 uptrck.com 127.0.0.1 upv4.moatads.com 127.0.0.1 urbanityadnetwork.com 127.0.0.1 urbo.2cnt.net @@ -37739,12 +40016,18 @@ 127.0.0.1 urlcash.net 127.0.0.1 urlcheck.hulu.com 127.0.0.1 urlstats.com +127.0.0.1 urx.io 127.0.0.1 us-ads.openx.net +127.0.0.1 us-east-1.profile-api.ads.linkedin.com +127.0.0.1 us-gmtdmp.mookie1.com +127.0.0.1 us-nj-e10.traffictradinghub.com +127.0.0.1 us-nj-e2.traffictradinghub.com 127.0.0.1 us-u.openx.net 127.0.0.1 us.2.cqcounter.com 127.0.0.1 us.a1.yimg.com 127.0.0.1 us.a2.yimg.com 127.0.0.1 us.adserver.yahoo.com +127.0.0.1 us.cam4ads.com 127.0.0.1 us.cqcounter.com 127.0.0.1 us.i1.yimg.com 127.0.0.1 us.intellitxt.com @@ -37771,6 +40054,7 @@ 127.0.0.1 usabilla.com 127.0.0.1 usachoice.net 127.0.0.1 usads.futurenet.nl +127.0.0.1 usage.trackjs.com 127.0.0.1 usarevenue.com 127.0.0.1 usarmy.vo.llnwd.net 127.0.0.1 usbanners.com @@ -37781,10 +40065,12 @@ 127.0.0.1 usenet.raidrush.org 127.0.0.1 user-disp.tidaltv.com 127.0.0.1 userlog.synapseip.tv +127.0.0.1 username1.link 127.0.0.1 users.ero-advertising.com 127.0.0.1 users.marketleverage.com 127.0.0.1 usimg.adbureau.net 127.0.0.1 uslbtest.cedexis.com +127.0.0.1 usr.dropkickmedia.com 127.0.0.1 usr.mgid.com 127.0.0.1 usun.112.2o7.net 127.0.0.1 utarget.co.uk @@ -37799,8 +40085,11 @@ 127.0.0.1 utm.webfetti.com 127.0.0.1 utm2.smileycentral.com 127.0.0.1 utmtrk2.smileycentral.com +127.0.0.1 utokapa.com 127.0.0.1 utopiad.com +127.0.0.1 utorrentinstall.com 127.0.0.1 utrade.com +127.0.0.1 uts-api.at.atwola.com 127.0.0.1 uur.at 127.0.0.1 uv.terra.com.br 127.0.0.1 uzhegovandrey.justclick.ru @@ -37809,6 +40098,7 @@ 127.0.0.1 v.effectivemeasure.net 127.0.0.1 v.emedia.cn 127.0.0.1 v.extreme-dm.com +127.0.0.1 v.gfhdkse.com 127.0.0.1 v.movad.de 127.0.0.1 v.visitweb.com 127.0.0.1 v.wqsph.net @@ -37819,6 +40109,7 @@ 127.0.0.1 v0.extreme-dm.com 127.0.0.1 v1.extreme-dm.com 127.0.0.1 v1.nedstatbasic.net +127.0.0.1 v2.adsbookie.com 127.0.0.1 v2.ipopup.ir 127.0.0.1 v2.popupsky.ir 127.0.0.1 v2.shareaholic.com @@ -37872,6 +40163,7 @@ 127.0.0.1 velti.com 127.0.0.1 vemna.go2cloud.org 127.0.0.1 vendor1.fitschigogerl.com +127.0.0.1 venturead.com 127.0.0.1 verdict.abc.go.com 127.0.0.1 verio.122.2o7.net 127.0.0.1 verisign.bfast.com @@ -37912,10 +40204,13 @@ 127.0.0.1 viaden.com 127.0.0.1 viadeplayer.112.2o7.net 127.0.0.1 vialeads.com +127.0.0.1 viamichelin.ads.trafficjunky.net +127.0.0.1 viamichelin.ss.xxxmyself.com 127.0.0.1 viamtvcom.112.2o7.net 127.0.0.1 viamtvde.112.2o7.net 127.0.0.1 viavivade.112.2o7.net 127.0.0.1 vibrantmedia.com +127.0.0.1 vice-ads.s3.amazonaws.com 127.0.0.1 vicecom.ivwbox.de 127.0.0.1 vidego.multicastmedia.com 127.0.0.1 video-loader.com @@ -41444,7 +43739,7 @@ 127.0.0.1 viewablemedia.net 127.0.0.1 viewbix.com 127.0.0.1 viewhttp.atdmt.com.70777.9596.302br.net -127.0.0.1 viewpoint.com +127.0.0.1 viewsecure.net 127.0.0.1 viewstat.hotstat.nl 127.0.0.1 viewstffdater.com 127.0.0.1 vig.tynt.com @@ -41485,6 +43780,7 @@ 127.0.0.1 visitweb.com 127.0.0.1 visualdna-widgets.mirror.co.uk 127.0.0.1 visualnetworks.com +127.0.0.1 visualrevenue.com 127.0.0.1 vitacost.tt.omtrdc.net 127.0.0.1 vitax.abc-sms.de 127.0.0.1 vitrade02.webtrekk.net @@ -41505,11 +43801,15 @@ 127.0.0.1 vod.mobvid.mobi 127.0.0.1 vodafone.inq.com 127.0.0.1 vodafone.ivwbox.de +127.0.0.1 vogo-vogo.ru 127.0.0.1 voicenetplus.com 127.0.0.1 voodooparking.com 127.0.0.1 vop.sundaysky.com 127.0.0.1 vorcu.go2cloud.org 127.0.0.1 vorlon.ferretsoft.com +127.0.0.1 vortex-sandbox.data.microsoft.com +127.0.0.1 vortex-win.data.microsoft.com +127.0.0.1 vortex.data.microsoft.com 127.0.0.1 vortextraffic.com 127.0.0.1 vox-static.liverail.com 127.0.0.1 vox.ivwbox.de @@ -41524,8 +43824,14 @@ 127.0.0.1 vpn01.ixn.vmsn.de 127.0.0.1 vpos.bnex.com 127.0.0.1 vprmnwbskk.com +127.0.0.1 vps.activedevelopment.co.nz 127.0.0.1 vps.zeptolab.com +127.0.0.1 vq91811.com +127.0.0.1 vra.outbrain.com 127.0.0.1 vroll.net +127.0.0.1 vrp.outbrain.com +127.0.0.1 vrt-chidc2-only.outbrain.com +127.0.0.1 vrt.outbrain.com 127.0.0.1 vrts.doublepimp.com 127.0.0.1 vs.dmtracker.com 127.0.0.1 vs.target.com @@ -41533,6 +43839,7 @@ 127.0.0.1 vsc.send.microad.jp 127.0.0.1 vseprogroshi.justclick.ru 127.0.0.1 vserv.bc.cdn.bitgravity.com +127.0.0.1 vserv.mobi 127.0.0.1 vsevjednom.cz 127.0.0.1 vsii.spinbox.net 127.0.0.1 vstats.net @@ -41564,6 +43871,7 @@ 127.0.0.1 w.sharethis.com 127.0.0.1 w.tr553.com 127.0.0.1 w.uimserv.net +127.0.0.1 w.uptolike.com 127.0.0.1 w.usabilla.com 127.0.0.1 w.zeroredirect.com 127.0.0.1 w.zeroredirect1.com @@ -41574,6 +43882,7 @@ 127.0.0.1 w1.extreme-dm.com 127.0.0.1 w1.hitbox.com 127.0.0.1 w1.luckyorange.com +127.0.0.1 w1.mozzi.com 127.0.0.1 w1.plugrush.com 127.0.0.1 w10.hitbox.com 127.0.0.1 w100.hitbox.com @@ -41666,19 +43975,23 @@ 127.0.0.1 w6.hitbox.com 127.0.0.1 w7.hitbox.com 127.0.0.1 w8.hitbox.com +127.0.0.1 w88.go.com 127.0.0.1 w88.m.espn.go.com 127.0.0.1 w9.hitbox.com +127.0.0.1 wa.and.co.uk 127.0.0.1 wa.metro.co.uk 127.0.0.1 wa.ui-portal.de 127.0.0.1 wac.658e.edgecastcdn.net 127.0.0.1 wac.a164.edgecastcdn.net 127.0.0.1 wahoha.com +127.0.0.1 waiads.com 127.0.0.1 waiwidgets-1246822334.eu-west-1.elb.amazonaws.com 127.0.0.1 wallston.ivwbox.de 127.0.0.1 wallstreet.2cnt.net 127.0.0.1 wallstreetprep.go2cloud.org 127.0.0.1 walmartcom.112.2o7.net 127.0.0.1 wan.duba.net +127.0.0.1 wap-click.com 127.0.0.1 wap.adlink.de 127.0.0.1 wap.advertising.com 127.0.0.1 wap.bob.vmsn.de @@ -41714,6 +44027,7 @@ 127.0.0.1 wapp2de.mobimaniac.com 127.0.0.1 wapp4de.brickoffers.com 127.0.0.1 wapplanet.org +127.0.0.1 wapstart.ru 127.0.0.1 warcry.us.intellitxt.com 127.0.0.1 warentest01.webtrekk.net 127.0.0.1 warnerbros.112.207.net @@ -41724,13 +44038,19 @@ 127.0.0.1 warp2search.us.intellitxt.com 127.0.0.1 warsport.timesink.com 127.0.0.1 watch-free.stream-online.net +127.0.0.1 watch.stream4know.com 127.0.0.1 watch.teroti.com 127.0.0.1 watchever.bild.de 127.0.0.1 watchfree.flv.in 127.0.0.1 watson.live.com +127.0.0.1 watson.microsoft.com +127.0.0.1 watson.ppe.telemetry.microsoft.com +127.0.0.1 watson.telemetry.microsoft.com +127.0.0.1 watson.telemetry.microsoft.com.nsatc.net 127.0.0.1 wayads.com 127.0.0.1 wbf.go2cloud.org 127.0.0.1 wblasti.go2cloud.org +127.0.0.1 wbn.su 127.0.0.1 wcbsimg.dayport.com 127.0.0.1 wcf.m.shopathome.com 127.0.0.1 wctrack.go2cloud.org @@ -41747,6 +44067,7 @@ 127.0.0.1 web-counter.5u.com 127.0.0.1 web-jp.ad-v.jp 127.0.0.1 web-stat.com +127.0.0.1 web-t.9gag.com 127.0.0.1 web-track.telekom-dienste.de 127.0.0.1 web.adblade.com 127.0.0.1 web.adknowledge.com @@ -41756,6 +44077,7 @@ 127.0.0.1 web.mxradon.com 127.0.0.1 web.secmedia.de 127.0.0.1 web.shotad.com +127.0.0.1 web.softonic-analytics.net 127.0.0.1 web0.carmunity.de 127.0.0.1 web01.bob.local.vmsn.de 127.0.0.1 web01.bob.vmsn.de @@ -41850,6 +44172,8 @@ 127.0.0.1 webhits.de 127.0.0.1 webhitsdirect.com 127.0.0.1 webindicator.siteheart.com +127.0.0.1 webinline-usage.streamesh.net +127.0.0.1 webinline.look4like.com 127.0.0.1 webiq-cdn.appspot.com 127.0.0.1 webiq-warp.appspot.com 127.0.0.1 weblemon.pl @@ -41869,6 +44193,7 @@ 127.0.0.1 webmaster.extabit.com 127.0.0.1 webmasterkai.sitetracker.com 127.0.0.1 webmasters.hugetraffic.com +127.0.0.1 webmasters.nastydollars.com 127.0.0.1 webmasters.tubealliance.com 127.0.0.1 webmedia.co.il 127.0.0.1 webmedia.pl @@ -41886,9 +44211,12 @@ 127.0.0.1 websatpub.fth.net 127.0.0.1 websense-media.co.il 127.0.0.1 webservices.aptwords.net +127.0.0.1 webservices.ecn5.com 127.0.0.1 webservices.sub2tech.com 127.0.0.1 webservices.websitepros.com 127.0.0.1 webservis.gen.tr +127.0.0.1 webservops.com +127.0.0.1 websexy.mobi 127.0.0.1 website-designs.com 127.0.0.1 websitefinancing.com 127.0.0.1 websitehome.co.uk @@ -41897,6 +44225,7 @@ 127.0.0.1 websitesponsor.de 127.0.0.1 websitetrafficreport.com 127.0.0.1 webspace.webhoster.de +127.0.0.1 webspectator.com 127.0.0.1 websponsors.com 127.0.0.1 websponsors.net 127.0.0.1 webstars2000.com @@ -41917,7 +44246,6 @@ 127.0.0.1 webtrace.exapaq.com 127.0.0.1 webtrack-brickstreetconnect-bau-prd.hsbc.com.hk 127.0.0.1 webtrack-brickstreetconnect-mkt-prd.hsbc.com.hk -127.0.0.1 webtrack.dhlglobalmail.com 127.0.0.1 webtrack.jwgrant.co.uk 127.0.0.1 webtracker.educationconnection.com 127.0.0.1 webtracker.tnt.com @@ -41938,6 +44266,7 @@ 127.0.0.1 webts.adac.de 127.0.0.1 webuytraffic.com 127.0.0.1 webxu.go2cloud.org +127.0.0.1 weddings.searchwho.com 127.0.0.1 wee.co.il 127.0.0.1 weecia.7173.clicksurecpa.com 127.0.0.1 weedoit.fr @@ -41964,6 +44293,7 @@ 127.0.0.1 werbung.mediaplex.com 127.0.0.1 werbung.xfind.de 127.0.0.1 werehacked.com +127.0.0.1 wes.df.telemetry.microsoft.com 127.0.0.1 wesell.co.il 127.0.0.1 west.adlink.de 127.0.0.1 westeins.2cnt.net @@ -41983,6 +44313,7 @@ 127.0.0.1 whenu.com 127.0.0.1 whereapps.com 127.0.0.1 whinteryield.jmp9.com +127.0.0.1 whisk.com 127.0.0.1 whispa.com 127.0.0.1 whistleout.s3.amazonaws.com 127.0.0.1 whitney-swindell.us @@ -41997,6 +44328,7 @@ 127.0.0.1 wibiya-actions.conduit-data.com 127.0.0.1 wibiya-june-new-log.conduit-data.com 127.0.0.1 wicked-media.net +127.0.0.1 widget-analytics.virool.com 127.0.0.1 widget.apptap.com 127.0.0.1 widget.breakingburner.com 127.0.0.1 widget.cheki.com.ng @@ -42010,9 +44342,11 @@ 127.0.0.1 widget.kaufda.com 127.0.0.1 widget.kelkoo.com 127.0.0.1 widget.marktjagd.de +127.0.0.1 widget.perfectmarket.com 127.0.0.1 widget.scoutpa.com 127.0.0.1 widget.sharecash.org 127.0.0.1 widget.siteheart.com +127.0.0.1 widget.stagram.com 127.0.0.1 widget.supercounters.com 127.0.0.1 widget.uservoice.com 127.0.0.1 widget.weibo.com @@ -42084,6 +44418,9 @@ 127.0.0.1 wms-eu.amazon-adsystem.com 127.0.0.1 wms-fe.amazon-adsystem.com 127.0.0.1 wms.assoc-amazon.com +127.0.0.1 wms.assoc-amazon.de +127.0.0.1 wmtrackinglink.com +127.0.0.1 wmtrafficentry.com 127.0.0.1 wn.pos.baidu.com 127.0.0.1 wnpcdn.com 127.0.0.1 womansanga.justclick.ru @@ -42107,6 +44444,7 @@ 127.0.0.1 wprp.zemanta.com 127.0.0.1 wptag.net 127.0.0.1 wrb.pornme.com +127.0.0.1 wrestling.searchwho.com 127.0.0.1 ws-eu.amazon-adsystem.com 127.0.0.1 ws-na.amazon-adsystem.com 127.0.0.1 ws.amazon.com @@ -42153,12 +44491,15 @@ 127.0.0.1 wsc.ehost-services.com 127.0.0.1 wsc1.surf-town.net 127.0.0.1 wsc1.webspectator.com +127.0.0.1 wsc4.webspectator.com 127.0.0.1 wsclick.infospace.com 127.0.0.1 wsi1.surf-town.net 127.0.0.1 wsod.com 127.0.0.1 wsp1.surf-town.net +127.0.0.1 wsq.umeng.com 127.0.0.1 wsspeed.com 127.0.0.1 wsstatic.govmetric.com +127.0.0.1 wstat.wibiya.com 127.0.0.1 wsw.ero-advertising.com 127.0.0.1 wsw1.surf-town.net 127.0.0.1 wsw2.surf-town.net @@ -42169,6 +44510,7 @@ 127.0.0.1 wswb1.surf-town.net 127.0.0.1 wswb3.surf-town.net 127.0.0.1 wt.neton-line.com +127.0.0.1 wt.o.nytimes.com 127.0.0.1 wt.socialsex.biz 127.0.0.1 wt01.webtrekk.net 127.0.0.1 wta.ero-advertising.com @@ -42176,6 +44518,7 @@ 127.0.0.1 wtlive.com 127.0.0.1 wtpn.twenga.co.uk 127.0.0.1 wtpn.twenga.de +127.0.0.1 wtraff.com 127.0.0.1 wundercounter.com 127.0.0.1 wusstrack.wunderground.com 127.0.0.1 wutlar.fortumo.com @@ -42184,6 +44527,7 @@ 127.0.0.1 wvwr1.hitbox.com 127.0.0.1 ww.thefile.me 127.0.0.1 ww.tr553.com +127.0.0.1 ww1.app4install.com 127.0.0.1 ww1.hitbox.com 127.0.0.1 ww1.hstpnetwork.com 127.0.0.1 ww1.searchhelper.com @@ -42201,6 +44545,7 @@ 127.0.0.1 ww251.bz-berlin.de 127.0.0.1 ww251.smartadserver.com 127.0.0.1 ww264.smartadserver.com +127.0.0.1 ww276.smartadserver.com 127.0.0.1 ww3.hitbox.com 127.0.0.1 ww3.hstpnetwork.com 127.0.0.1 ww302.smartadserver.com @@ -42217,11 +44562,14 @@ 127.0.0.1 ww651.smartadserver.com 127.0.0.1 ww684.smartadserver.com 127.0.0.1 ww690.smartadserver.com +127.0.0.1 ww7.3redirect.com 127.0.0.1 ww84.smartadserver.com 127.0.0.1 ww856.smartadserver.com 127.0.0.1 ww881.smartadserver.com +127.0.0.1 wwa.dateformore.de 127.0.0.1 wwa.ero-advertising.com 127.0.0.1 wwa.hitbox.com +127.0.0.1 wwa.only-dates.de 127.0.0.1 wwbanners2.ero-advertising.com 127.0.0.1 wwc.hitbox.com 127.0.0.1 wwd.hitbox.com @@ -42266,6 +44614,7 @@ 127.0.0.1 www.2gtstrk.com 127.0.0.1 www.2o7.net 127.0.0.1 www.302br.net +127.0.0.1 www.360adshost.net 127.0.0.1 www.3dots.co.il 127.0.0.1 www.49535.com 127.0.0.1 www.4qsurvey.com @@ -42274,6 +44623,7 @@ 127.0.0.1 www.718unlimited.com 127.0.0.1 www.83nsdjqqo1cau183xz.com 127.0.0.1 www.863c4c0c521.se +127.0.0.1 www.938host.xyz 127.0.0.1 www.a-ads.com 127.0.0.1 www.a.ero-advertising.com 127.0.0.1 www.a4dtracker.com @@ -42318,12 +44668,15 @@ 127.0.0.1 www.ad-style.com 127.0.0.1 www.ad-text.com 127.0.0.1 www.ad-visor.com +127.0.0.1 www.ad-x.co.uk 127.0.0.1 www.ad.doubleclick.net.60532.9279.302br.net 127.0.0.1 www.ad.doubleclick.net.68390.9544.302br.net 127.0.0.1 www.ad.doubleclick.net.73285.9423.302br.net 127.0.0.1 www.ad.doubleclick.net.76530.9544.302br.net 127.0.0.1 www.ad.infoseek.com 127.0.0.1 www.ad.preferences.net +127.0.0.1 www.ad2games.com +127.0.0.1 www.ad4games.com 127.0.0.1 www.adadvisor.net 127.0.0.1 www.adaos-ads.net 127.0.0.1 www.adap.tv @@ -42339,6 +44692,7 @@ 127.0.0.1 www.adcenter.net 127.0.0.1 www.adcentriconline.com 127.0.0.1 www.adchoices.com.vn +127.0.0.1 www.adclickxpress.com 127.0.0.1 www.adclix.com 127.0.0.1 www.adclub.net 127.0.0.1 www.adcron.com @@ -42348,12 +44702,12 @@ 127.0.0.1 www.adelina-ashman.us 127.0.0.1 www.adexcite.com 127.0.0.1 www.adexm.com -127.0.0.1 www.adf.ly 127.0.0.1 www.adfest.com 127.0.0.1 www.adfluence.net 127.0.0.1 www.adfoc.us 127.0.0.1 www.adforce.ru 127.0.0.1 www.adforgames.com +127.0.0.1 www.adfornepal.com 127.0.0.1 www.adfusion.com 127.0.0.1 www.adgalax.com 127.0.0.1 www.adgatemedia.com @@ -42382,6 +44736,8 @@ 127.0.0.1 www.admeld.com 127.0.0.1 www.admestate.ru 127.0.0.1 www.admitad.com +127.0.0.1 www.admyapp.net +127.0.0.1 www.adnetworkperformance.com 127.0.0.1 www.adnxs1.com 127.0.0.1 www.adocean.pl 127.0.0.1 www.adonnetwork.com @@ -42417,10 +44773,12 @@ 127.0.0.1 www.adserver.brandilitynetwork.de 127.0.0.1 www.adserver.cz.cc 127.0.0.1 www.adserverplus.com +127.0.0.1 www.adserverxxl.de 127.0.0.1 www.adshiftmedia.com 127.0.0.1 www.adshooter.com 127.0.0.1 www.adshost1.com 127.0.0.1 www.adshost2.com +127.0.0.1 www.adshot.de 127.0.0.1 www.adshuh.com 127.0.0.1 www.adsmarket.co.il 127.0.0.1 www.adsmarketgroup.com @@ -42440,6 +44798,7 @@ 127.0.0.1 www.adsview.com 127.0.0.1 www.adtech.com 127.0.0.1 www.adtegrity.com +127.0.0.1 www.adternal.com 127.0.0.1 www.adtilt.com 127.0.0.1 www.adtoll.com 127.0.0.1 www.adtrack1.pl @@ -42450,6 +44809,7 @@ 127.0.0.1 www.adult-adv.com 127.0.0.1 www.adult-banner-ads.com 127.0.0.1 www.adultbannerexchange.de +127.0.0.1 www.adultblogtoplist.com 127.0.0.1 www.adv-adserver.com 127.0.0.1 www.adv.co.il 127.0.0.1 www.adv679854.ru @@ -42470,6 +44830,7 @@ 127.0.0.1 www.advertising.com 127.0.0.1 www.advertserve.com 127.0.0.1 www.advertstream.com +127.0.0.1 www.advertzer.com 127.0.0.1 www.advido.com 127.0.0.1 www.adview.cn 127.0.0.1 www.adviews-sponsor.de @@ -42505,6 +44866,7 @@ 127.0.0.1 www.afftracker.info 127.0.0.1 www.afftrackinglinks.com 127.0.0.1 www.afftrackr.com +127.0.0.1 www.affyield.com 127.0.0.1 www.afiliati.ro 127.0.0.1 www.afterdownload.com 127.0.0.1 www.afterview.ru @@ -42554,6 +44916,7 @@ 127.0.0.1 www.app-authority.com 127.0.0.1 www.app4us.info 127.0.0.1 www.appflood.com +127.0.0.1 www.appfoxes.com 127.0.0.1 www.appia.com 127.0.0.1 www.appliedsemantics.com 127.0.0.1 www.applifier.com @@ -42566,11 +44929,13 @@ 127.0.0.1 www.april-tindell.us 127.0.0.1 www.april-wilcoxon.us 127.0.0.1 www.apture.com +127.0.0.1 www.apxadtracking.net 127.0.0.1 www.areasnap.com 127.0.0.1 www.arielle-younkin.us 127.0.0.1 www.armstrongsystems.bizland.com 127.0.0.1 www.ashly-truman.us 127.0.0.1 www.ass4all.com +127.0.0.1 www.assoc-amazon.com 127.0.0.1 www.assoc-amazon.de 127.0.0.1 www.associeta.com 127.0.0.1 www.associmg.com @@ -42579,6 +44944,7 @@ 127.0.0.1 www.athena-dirksen.us 127.0.0.1 www.aubrey-kutz.us 127.0.0.1 www.auctionads.com +127.0.0.1 www.audienceinsights.net 127.0.0.1 www.audiopal.com 127.0.0.1 www.audra-talbott.us 127.0.0.1 www.aurora-musso.us @@ -42603,8 +44969,10 @@ 127.0.0.1 www.bango.com 127.0.0.1 www.bango.net 127.0.0.1 www.bangtuoc.vn +127.0.0.1 www.banner-rotation.com 127.0.0.1 www.bannerbank.ru 127.0.0.1 www.bannercenter.net +127.0.0.1 www.bannerchange24.de 127.0.0.1 www.bannerforge.com 127.0.0.1 www.bannerpro.tk 127.0.0.1 www.banners.ero-advertising.com @@ -42625,7 +44993,10 @@ 127.0.0.1 www.benchbrands.com 127.0.0.1 www.bernadette-depalma.us 127.0.0.1 www.bernadette-stefanski.us +127.0.0.1 www.bestdealconnector.com +127.0.0.1 www.bestfwdservice.com 127.0.0.1 www.bestwebnutfunblack.biz +127.0.0.1 www.besuchercounter.de 127.0.0.1 www.bettermail.ca 127.0.0.1 www.bettingmarket.com 127.0.0.1 www.betty-cendejas.us @@ -42645,6 +45016,7 @@ 127.0.0.1 www.blamads.com 127.0.0.1 www.blanca-livingstone.us 127.0.0.1 www.blankrefer.com +127.0.0.1 www.blkget6.com 127.0.0.1 www.blog-hits.com 127.0.0.1 www.blogads.com 127.0.0.1 www.blueheart.org @@ -42655,12 +45027,14 @@ 127.0.0.1 www.bmetrack.com 127.0.0.1 www.bnbaz.eb2a.com 127.0.0.1 www.bnex.com +127.0.0.1 www.bnhtml.com 127.0.0.1 www.boards2go.com 127.0.0.1 www.bob.vmsn.de 127.0.0.1 www.boldcenter.com 127.0.0.1 www.boldchat.com 127.0.0.1 www.bongacash.com 127.0.0.1 www.bonnie-schrecengost.us +127.0.0.1 www.bookcorps.com 127.0.0.1 www.bounceexchange.com 127.0.0.1 www.bpath.com 127.0.0.1 www.bpmsza.com @@ -42668,6 +45042,7 @@ 127.0.0.1 www.bravenetmedianetwork.com 127.0.0.1 www.bree-wile.us 127.0.0.1 www.brianne-crofoot.us +127.0.0.1 www.brightadnetwork.com 127.0.0.1 www.brightshare.com 127.0.0.1 www.brigitte-burtch.us 127.0.0.1 www.brittni-olson.us @@ -42678,6 +45053,8 @@ 127.0.0.1 www.browser-statistik.de 127.0.0.1 www.bsitm3.com 127.0.0.1 www.btdirectnav.com +127.0.0.1 www.btnativedirect.com +127.0.0.1 www.btnativenav.com 127.0.0.1 www.btprmnav.com 127.0.0.1 www.bullseye-network.com 127.0.0.1 www.bullseye-network.net @@ -42710,6 +45087,7 @@ 127.0.0.1 www.cartonetwork.com 127.0.0.1 www.cartoonnrtwork.com 127.0.0.1 www.casalemedia.com +127.0.0.1 www.cash-duck.com 127.0.0.1 www.cash4files.com 127.0.0.1 www.cash4webmaster.de 127.0.0.1 www.cashassociate.com @@ -42738,6 +45116,7 @@ 127.0.0.1 www.certified-email.com 127.0.0.1 www.cetrk.com 127.0.0.1 www.chango.com +127.0.0.1 www.channeltraffic.net 127.0.0.1 www.chartboost.com 127.0.0.1 www.chastity-stoltenberg.us 127.0.0.1 www.checkm8.com @@ -42754,6 +45133,7 @@ 127.0.0.1 www.cityadspix.com 127.0.0.1 www.cityredirect.com 127.0.0.1 www.cityviplink.com +127.0.0.1 www.civicscience.com 127.0.0.1 www.cjsab.com 127.0.0.1 www.cktagalongs.com 127.0.0.1 www.cktrk.net @@ -42779,8 +45159,11 @@ 127.0.0.1 www.clicksor.com 127.0.0.1 www.clicksor.net 127.0.0.1 www.clicksrvr.co +127.0.0.1 www.clickstotrack.com 127.0.0.1 www.clicksurecpa.com +127.0.0.1 www.clickterra.net 127.0.0.1 www.clicktracksolutions.com +127.0.0.1 www.clicktraffix.com 127.0.0.1 www.clickv.com 127.0.0.1 www.clickwinks.com 127.0.0.1 www.clickxchange.com @@ -42794,6 +45177,7 @@ 127.0.0.1 www.clkmon.com 127.0.0.1 www.clkmr.com 127.0.0.1 www.clkrev.com +127.0.0.1 www.clktag.com 127.0.0.1 www.cloudhot.net 127.0.0.1 www.cloudixconnection.com 127.0.0.1 www.cml1234.com @@ -42828,6 +45212,7 @@ 127.0.0.1 www.connectlinking9.com 127.0.0.1 www.connexionsecure.com 127.0.0.1 www.connextra.com +127.0.0.1 www.consolefiles.info 127.0.0.1 www.contador-de-visitas.com 127.0.0.1 www.content.applift.com 127.0.0.1 www.contentlockingnetworks.com @@ -42889,6 +45274,7 @@ 127.0.0.1 www.cpd8.net 127.0.0.1 www.cpm-plus.com 127.0.0.1 www.cpm.biz +127.0.0.1 www.cpmaffiliation.com 127.0.0.1 www.cpmfun.com 127.0.0.1 www.cpmleader.com 127.0.0.1 www.cpv2tracking.com @@ -42913,6 +45299,7 @@ 127.0.0.1 www.cutterbuck.com 127.0.0.1 www.cwahi.net 127.0.0.1 www.cyberwavemedia.com +127.0.0.1 www.cycleuniverseupdate.com 127.0.0.1 www.cynthia-tomas.us 127.0.0.1 www.cyonix.to 127.0.0.1 www.cyprusads.info @@ -42966,7 +45353,10 @@ 127.0.0.1 www.directoryreferences.com 127.0.0.1 www.directtrack.com 127.0.0.1 www.dirtcheapadvertising.com +127.0.0.1 www.dirtnaprecs.com 127.0.0.1 www.discoverexactly.com +127.0.0.1 www.dltags.com +127.0.0.1 www.dmle1chique.space 127.0.0.1 www.dmtracker.com 127.0.0.1 www.dmtracking01.com 127.0.0.1 www.dnps.com @@ -42983,10 +45373,13 @@ 127.0.0.1 www.doubleclick.ne.jp 127.0.0.1 www.doubleclick.net 127.0.0.1 www.doubleclick.net.my +127.0.0.1 www.download-performance.com 127.0.0.1 www.downloadcounter.de +127.0.0.1 www.dprtb.com 127.0.0.1 www.dragonballzhomeland.com 127.0.0.1 www.drivotracker.com 127.0.0.1 www.drtserver.com +127.0.0.1 www.dsct1.com 127.0.0.1 www.dsmmadvantage.com 127.0.0.1 www.dsnr.net 127.0.0.1 www.dsnrmg.com @@ -43008,6 +45401,9 @@ 127.0.0.1 www.easyhitcounters.com 127.0.0.1 www.easyhits4u.com 127.0.0.1 www.ebtmarketing.com +127.0.0.1 www.eclkmpbn.com +127.0.0.1 www.eclkmpsa.com +127.0.0.1 www.ecpmrocks.com 127.0.0.1 www.eddie-theriot.us 127.0.0.1 www.edge.quantserve.com 127.0.0.1 www.edna-read.us @@ -43015,6 +45411,7 @@ 127.0.0.1 www.edward-hulsey.us 127.0.0.1 www.eightfoldlogic.com 127.0.0.1 www.elaine-hamdan.us +127.0.0.1 www.elded.eu 127.0.0.1 www.eleadstracker.com 127.0.0.1 www.elise-throop.us 127.0.0.1 www.elitemarketing.net @@ -43046,6 +45443,7 @@ 127.0.0.1 www.eroadvertising.com 127.0.0.1 www.eroadvertising.info 127.0.0.1 www.eroadvertising.nl +127.0.0.1 www.eroanalysis.com 127.0.0.1 www.erostracker.com 127.0.0.1 www.erostracker.net 127.0.0.1 www.escalatenetwork.com @@ -43057,10 +45455,13 @@ 127.0.0.1 www.etracker.com 127.0.0.1 www.etracker.de 127.0.0.1 www.euroclick.com +127.0.0.1 www.eurocounter.com 127.0.0.1 www.euros4click.de 127.0.0.1 www.eva-parkin.us 127.0.0.1 www.eva-ryerson.us 127.0.0.1 www.eventi.co.il +127.0.0.1 www.everestjs.net +127.0.0.1 www.everyscape.com 127.0.0.1 www.evisitcs2.com 127.0.0.1 www.ewordofmouth.com 127.0.0.1 www.exactadvertising.com @@ -43100,6 +45501,7 @@ 127.0.0.1 www.fhserve.com 127.0.0.1 www.fiesta-game.com 127.0.0.1 www.filecm.net +127.0.0.1 www.filestorage52.xyz 127.0.0.1 www.filitrac.com 127.0.0.1 www.find-fast-answers.com 127.0.0.1 www.findaccurate.com @@ -43132,18 +45534,22 @@ 127.0.0.1 www.flycast.com 127.0.0.1 www.flyinads.com 127.0.0.1 www.fmpub.net +127.0.0.1 www.fncnet1.com 127.0.0.1 www.forelook.com 127.0.0.1 www.foreseeresults.com 127.0.0.1 www.foxcounter.com +127.0.0.1 www.fpcplugs.com 127.0.0.1 www.fpctraffic.com 127.0.0.1 www.francesca-studer.us 127.0.0.1 www.freddyman.com 127.0.0.1 www.free-banners.com 127.0.0.1 www.freeadposting.com +127.0.0.1 www.freecamdollars.com 127.0.0.1 www.freecountersnow.com 127.0.0.1 www.freedatarecharge.com 127.0.0.1 www.freehomepages.com 127.0.0.1 www.freelogs.com +127.0.0.1 www.freeonescams.com 127.0.0.1 www.freeresultsguide.com 127.0.0.1 www.freerotator.com 127.0.0.1 www.freestats.com @@ -43151,6 +45557,7 @@ 127.0.0.1 www.freewha.com 127.0.0.1 www.friendlyduck.com 127.0.0.1 www.fucktubenetwork.com +127.0.0.1 www.fullqurandownload.com 127.0.0.1 www.fun-hits.de 127.0.0.1 www.fun-town.com 127.0.0.1 www.funklicks.com @@ -43163,10 +45570,13 @@ 127.0.0.1 www.fwdservice.com 127.0.0.1 www.fxstyle.net 127.0.0.1 www.gabia.com +127.0.0.1 www.game-advertising-online.com 127.0.0.1 www.gameadexchange.com 127.0.0.1 www.gamecity.net 127.0.0.1 www.gameleads.ru 127.0.0.1 www.gamersblast.com +127.0.0.1 www.gamescpc.com +127.0.0.1 www.gamevui24.com 127.0.0.1 www.gamingblast.com 127.0.0.1 www.gbotvisit.com 127.0.0.1 www.geoplugin.net @@ -43202,6 +45612,7 @@ 127.0.0.1 www.googletagmanager.com 127.0.0.1 www.googletagservices.com 127.0.0.1 www.gooool.com +127.0.0.1 www.gopath2.xyz 127.0.0.1 www.goreal.at 127.0.0.1 www.gospycash.com 127.0.0.1 www.gostats.com @@ -43238,6 +45649,7 @@ 127.0.0.1 www.hayley-mund.us 127.0.0.1 www.hbytracker.com 127.0.0.1 www.hdplugindownload.com +127.0.0.1 www.health.searchwho.com 127.0.0.1 www.heather-brennen.us 127.0.0.1 www.heidi-arroyo.us 127.0.0.1 www.helen-ponte.us @@ -43288,12 +45700,14 @@ 127.0.0.1 www.hopurl.org 127.0.0.1 www.host-tracker.com 127.0.0.1 www.hostlnks.com +127.0.0.1 www.hostpost4.xyz 127.0.0.1 www.hotjar.com 127.0.0.1 www.hotlog.ru 127.0.0.1 www.ht-tracking01.com 127.0.0.1 www.html-email-marketing.com 127.0.0.1 www.html5zombo.com 127.0.0.1 www.htmlcounter.com +127.0.0.1 www.hubtraffic.com 127.0.0.1 www.humanclick.com 127.0.0.1 www.hurricanedigitalmedia.com 127.0.0.1 www.hyperbanner.net @@ -43301,6 +45715,7 @@ 127.0.0.1 www.hyperspace-network.com 127.0.0.1 www.hyperspacenetwork.com 127.0.0.1 www.i-xzone.com +127.0.0.1 www.iads.com.np 127.0.0.1 www.iaspromotes.com 127.0.0.1 www.ib.adnxs.com 127.0.0.1 www.iballs.nl @@ -43322,6 +45737,7 @@ 127.0.0.1 www.ignitad.com 127.0.0.1 www.igogoshare.com 127.0.0.1 www.iicdn.com +127.0.0.1 www.ilgm-deals.com 127.0.0.1 www.imads.ero-advertising.com 127.0.0.1 www.image-map.com 127.0.0.1 www.image.ft.com @@ -43341,6 +45757,8 @@ 127.0.0.1 www.inmobi.com 127.0.0.1 www.inspectlet.com 127.0.0.1 www.inspectorclick.com +127.0.0.1 www.instantdownloaderpro.com +127.0.0.1 www.instinctiveads.com 127.0.0.1 www.integrate.com 127.0.0.1 www.intelatrack.biz 127.0.0.1 www.intelatracking.net @@ -43378,7 +45796,6 @@ 127.0.0.1 www.itrckr.com 127.0.0.1 www.ivwbox.de 127.0.0.1 www.iwebimg.net -127.0.0.1 www.j.gs 127.0.0.1 www.jade-aultman.us 127.0.0.1 www.jadina-counter.de 127.0.0.1 www.jag.vmsn.de @@ -43424,6 +45841,7 @@ 127.0.0.1 www.julianna-ferrara.us 127.0.0.1 www.jumptap.com 127.0.0.1 www.junbi-tracker.com +127.0.0.1 www.just4single.com 127.0.0.1 www.justclick.ru 127.0.0.1 www.justcounter.com 127.0.0.1 www.justsaywow.com @@ -43477,6 +45895,7 @@ 127.0.0.1 www.kizash.com 127.0.0.1 www.kliktrek.com 127.0.0.1 www.kontera.com +127.0.0.1 www.koocash.com 127.0.0.1 www.kosoft-ads.com 127.0.0.1 www.kpicentral.com 127.0.0.1 www.kryptoads.com @@ -43505,6 +45924,7 @@ 127.0.0.1 www.liana-millican.us 127.0.0.1 www.liczniki.org 127.0.0.1 www.lightedpages.com +127.0.0.1 www.lijit.com 127.0.0.1 www.likebtn.com 127.0.0.1 www.liliana-prokop.us 127.0.0.1 www.lily-mcnamara.us @@ -43541,11 +45961,13 @@ 127.0.0.1 www.lisa-giannini.us 127.0.0.1 www.listbot.com 127.0.0.1 www.litiumo.com +127.0.0.1 www.liveadexchanger.com 127.0.0.1 www.livecounter.dk 127.0.0.1 www.liveinternet.ru 127.0.0.1 www.livejasmin.com 127.0.0.1 www.livesearchnow.com 127.0.0.1 www.livestat.com +127.0.0.1 www.lizk.co 127.0.0.1 www.ljteas.com 127.0.0.1 www.llnwd.net 127.0.0.1 www.lloogg.com @@ -43564,9 +45986,11 @@ 127.0.0.1 www.lora-keeler.us 127.0.0.1 www.loren-ruybal.us 127.0.0.1 www.lorena-margolis.us +127.0.0.1 www.lostwebtracker.com 127.0.0.1 www.lotame.com 127.0.0.1 www.louise-poulsen.us 127.0.0.1 www.loxtk.com +127.0.0.1 www.lugiy.ru 127.0.0.1 www.luisa-hayden.us 127.0.0.1 www.luminate.com 127.0.0.1 www.lydia-birnbaum.us @@ -43574,12 +45998,14 @@ 127.0.0.1 www.lynette-raber.us 127.0.0.1 www.lynn-buzbee.us 127.0.0.1 www.lynsey-mabon.us +127.0.0.1 www.lzxrz.net 127.0.0.1 www.macaddictads.snv.futurenet.nl 127.0.0.1 www.madblast.com 127.0.0.1 www.madisonlogic.com 127.0.0.1 www.madmax53.stellar.cpa.clicksure.com 127.0.0.1 www.madmimi.com 127.0.0.1 www.madnet.ru +127.0.0.1 www.magames4.com 127.0.0.1 www.magentanews.com 127.0.0.1 www.magicmail.co.nz 127.0.0.1 www.magictubevideo.com @@ -43641,6 +46067,7 @@ 127.0.0.1 www.maxcdn.com 127.0.0.1 www.maxtrust.ru 127.0.0.1 www.mb01.com +127.0.0.1 www.mb103.com 127.0.0.1 www.mb104.com 127.0.0.1 www.mbotvisit.com 127.0.0.1 www.mcpatrack.tv @@ -43661,15 +46088,19 @@ 127.0.0.1 www.megacounter.de 127.0.0.1 www.megan-farrier.us 127.0.0.1 www.megan-stagner.us +127.0.0.1 www.megapopads.com 127.0.0.1 www.melissa-kirksey.us 127.0.0.1 www.melissa-ruggles.us 127.0.0.1 www.mellissa-lack.us +127.0.0.1 www.mellowads.com 127.0.0.1 www.meltwater.com 127.0.0.1 www.meltwaternews.com 127.0.0.1 www.memopumpkin.com 127.0.0.1 www.menato.ru 127.0.0.1 www.meshbean.com +127.0.0.1 www.metabesttours.com 127.0.0.1 www.metacount.com +127.0.0.1 www.metricology.com 127.0.0.1 www.mfsuite.com 127.0.0.1 www.mgcash.com 127.0.0.1 www.mgcashgate.com @@ -43688,15 +46119,18 @@ 127.0.0.1 www.mlinktracker.com 127.0.0.1 www.mmftpf.com 127.0.0.1 www.mmnetwork.mobi +127.0.0.1 www.mmo123.co 127.0.0.1 www.mmotraffic.com 127.0.0.1 www.mmtracking.com 127.0.0.1 www.mmtrkmc.com 127.0.0.1 www.mnetads.net 127.0.0.1 www.moatads.com 127.0.0.1 www.mobfox.com +127.0.0.1 www.mobi-mobi.info 127.0.0.1 www.mobile-abc.com 127.0.0.1 www.mobile.adnxs.com 127.0.0.1 www.mobileandinternetadvertising.com +127.0.0.1 www.mobilelandings.com 127.0.0.1 www.mobisystems.com 127.0.0.1 www.mobitracker.info 127.0.0.1 www.mobpartner.com @@ -43732,11 +46166,13 @@ 127.0.0.1 www.multicounter.de 127.0.0.1 www.my-dirty-hobby.com 127.0.0.1 www.my-etracker.com +127.0.0.1 www.my-linker.com 127.0.0.1 www.my-tds.net 127.0.0.1 www.myad.no 127.0.0.1 www.myadmarket.com 127.0.0.1 www.myadpromoter.com 127.0.0.1 www.myadtrack.com +127.0.0.1 www.myadvertisingpays.com 127.0.0.1 www.myaffiliateads.com 127.0.0.1 www.myaffiliateprogram.com 127.0.0.1 www.myapp.com @@ -43744,11 +46180,13 @@ 127.0.0.1 www.myforum365.com 127.0.0.1 www.mygosecure.com 127.0.0.1 www.myiframe.com +127.0.0.1 www.mymobcn.com 127.0.0.1 www.myseostats.com 127.0.0.1 www.mysuperpharm.com 127.0.0.1 www.mysweetdeals.org 127.0.0.1 www.mytds.pr.vc 127.0.0.1 www.mytiwi.com +127.0.0.1 www.mytogolinks.com 127.0.0.1 www.mytrafficbuilder.info 127.0.0.1 www.myvpn.pro 127.0.0.1 www.mywactrack.com @@ -43801,15 +46239,18 @@ 127.0.0.1 www.olizyr.com 127.0.0.1 www.omarsys.com 127.0.0.1 www.omnaling.com +127.0.0.1 www.omniata.com 127.0.0.1 www.omniture.com 127.0.0.1 www.omtrdc.net 127.0.0.1 www.onclickads.net +127.0.0.1 www.onclickpredictiv.com 127.0.0.1 www.oneandonlynetwork.com 127.0.0.1 www.onelouder.com 127.0.0.1 www.onestat.com 127.0.0.1 www.onestatfree.com 127.0.0.1 www.onlineemailmarketing.com 127.0.0.1 www.onlinetraffic.info +127.0.0.1 www.only-dates.de 127.0.0.1 www.onyarysh.ru 127.0.0.1 www.openadserving.com 127.0.0.1 www.opentracker.net @@ -43836,6 +46277,7 @@ 127.0.0.1 www.padv.co.il 127.0.0.1 www.paid-work-at-home.com 127.0.0.1 www.paidclick.ro +127.0.0.1 www.paidsurveysfree.com 127.0.0.1 www.paige-weigand.us 127.0.0.1 www.palimashop.com 127.0.0.1 www.pamela-bax.us @@ -43855,6 +46297,7 @@ 127.0.0.1 www.peakclick.com 127.0.0.1 www.peakcounter.dk 127.0.0.1 www.people-group.su +127.0.0.1 www.performanceadexchange.com 127.0.0.1 www.performancerevenues.com 127.0.0.1 www.persevered.com 127.0.0.1 www.persianstat.com @@ -43888,9 +46331,11 @@ 127.0.0.1 www.popadscdn.net 127.0.0.1 www.poponclick.com 127.0.0.1 www.popstrap.com +127.0.0.1 www.poptm.com 127.0.0.1 www.popunder.net 127.0.0.1 www.popunder.ru 127.0.0.1 www.popzila.com +127.0.0.1 www.pornland.mobi 127.0.0.1 www.pornlist.mobi 127.0.0.1 www.pornolist.org 127.0.0.1 www.pornsponsors.com @@ -43920,12 +46365,16 @@ 127.0.0.1 www.promomasters.at 127.0.0.1 www.promotions.yahoo.com 127.0.0.1 www.propellerpops.com +127.0.0.1 www.prosperent.com 127.0.0.1 www.prtracker.com 127.0.0.1 www.psychics4free.com 127.0.0.1 www.ptp22.com +127.0.0.1 www.ptp4ever.fr 127.0.0.1 www.ptrking.com 127.0.0.1 www.pubdirecte.com 127.0.0.1 www.pulse360.com +127.0.0.1 www.pureadexchange.com +127.0.0.1 www.pywatur.biz 127.0.0.1 www.qadabra.com 127.0.0.1 www.qdigital.co.il 127.0.0.1 www.qqc.co @@ -43933,12 +46382,14 @@ 127.0.0.1 www.qualaroo.com 127.0.0.1 www.qualigo.de 127.0.0.1 www.qualigo.net +127.0.0.1 www.qualityclickcontrol.com 127.0.0.1 www.qualitylegion.com 127.0.0.1 www.quantcount.com 127.0.0.1 www.quantserve.com 127.0.0.1 www.qubitproducts.com 127.0.0.1 www.questionmarket.com 127.0.0.1 www.quick-torrent.com +127.0.0.1 www.quickshareconecpt.com 127.0.0.1 www.quizilla.com 127.0.0.1 www.quotestream.com 127.0.0.1 www.qwiklnk.com @@ -43950,6 +46401,8 @@ 127.0.0.1 www.rankseller.de 127.0.0.1 www.ranksider.com 127.0.0.1 www.rapleaf.com +127.0.0.1 www.rbmut.science +127.0.0.1 www.rddywd.com 127.0.0.1 www.real-marketing.com 127.0.0.1 www.realcounter.eu 127.0.0.1 www.realresultspublish.com @@ -43960,6 +46413,7 @@ 127.0.0.1 www.red422.com 127.0.0.1 www.redcounter.net 127.0.0.1 www.redir04mail.com +127.0.0.1 www.redirect.com 127.0.0.1 www.reduxmediia.com 127.0.0.1 www.reformal.ru 127.0.0.1 www.rejectclick.com @@ -43983,6 +46437,7 @@ 127.0.0.1 www.rgadvert.com 127.0.0.1 www.rhtag.com 127.0.0.1 www.richelle-crandell.us +127.0.0.1 www.rivcash.com 127.0.0.1 www.rlcdn.com 127.0.0.1 www.rmbn.ru 127.0.0.1 www.rmv.so @@ -43990,6 +46445,7 @@ 127.0.0.1 www.robotreplay.com 127.0.0.1 www.roiadtracker.com 127.0.0.1 www.roitracker.com +127.0.0.1 www.rotrk.com 127.0.0.1 www.roxyaffiliates.com 127.0.0.1 www.rsmrttracking.com 127.0.0.1 www.rt-ns.ru @@ -43997,6 +46453,7 @@ 127.0.0.1 www.rtrk.com 127.0.0.1 www.rtsg.com 127.0.0.1 www.ruclicks.com +127.0.0.1 www.runnergamesch.com 127.0.0.1 www.runnergameshk.com 127.0.0.1 www.ruralworkforcecenter.com 127.0.0.1 www.safelinking.net @@ -44012,10 +46469,15 @@ 127.0.0.1 www.scriptshead.com 127.0.0.1 www.search-images.com 127.0.0.1 www.search-results.com +127.0.0.1 www.search-results.mobi 127.0.0.1 www.search-tracker.com 127.0.0.1 www.search.us.com 127.0.0.1 www.searchacts.com +127.0.0.1 www.searchesinteractive.com +127.0.0.1 www.searchfusion.com 127.0.0.1 www.searching-mails.org +127.0.0.1 www.searchingresult.com +127.0.0.1 www.searchinquire.com 127.0.0.1 www.searchlinear.com 127.0.0.1 www.searchmaybe.com 127.0.0.1 www.searchpeack.com @@ -44035,6 +46497,8 @@ 127.0.0.1 www.secretgrowth-97676.us 127.0.0.1 www.secrets40.com 127.0.0.1 www.sectracking.com +127.0.0.1 www.secureadcenter.com +127.0.0.1 www.securejump.net 127.0.0.1 www.securepaths.com 127.0.0.1 www.securestudies.com 127.0.0.1 www.securetracking2.com @@ -44054,8 +46518,8 @@ 127.0.0.1 www.selfdestructingmessage.com 127.0.0.1 www.selfnetwork.com 127.0.0.1 www.selina-krouse.us +127.0.0.1 www.selipuquoe.com 127.0.0.1 www.sellads.eu -127.0.0.1 www.sellfy.com 127.0.0.1 www.semrush.com 127.0.0.1 www.send4fun.com 127.0.0.1 www.senseiproducts.com @@ -44088,6 +46552,7 @@ 127.0.0.1 www.showm3.com 127.0.0.1 www.showroomvip.com 127.0.0.1 www.sichtbarkeitsindex.de +127.0.0.1 www.silverpush.com 127.0.0.1 www.silvia-maffei.us 127.0.0.1 www.simona-stanford.us 127.0.0.1 www.simonsearch.com @@ -44096,6 +46561,7 @@ 127.0.0.1 www.sitestat.com 127.0.0.1 www.sitetracker.com 127.0.0.1 www.skimlinks.com +127.0.0.1 www.skyenetmedia.com 127.0.0.1 www.skyhookwireless.com 127.0.0.1 www.slundi.com 127.0.0.1 www.smart-ip.net @@ -44122,6 +46588,7 @@ 127.0.0.1 www.sonia-pantoja.us 127.0.0.1 www.sonja-rascon.us 127.0.0.1 www.sonya-howland.us +127.0.0.1 www.soundstation.dk 127.0.0.1 www.sparkimg.com 127.0.0.1 www.specificmedia.com 127.0.0.1 www.spectato.com @@ -44138,6 +46605,7 @@ 127.0.0.1 www.spylog.ru 127.0.0.1 www.sq2trk2.com 127.0.0.1 www.srv2trking.com +127.0.0.1 www.srver110.xyz 127.0.0.1 www.srvpub.com 127.0.0.1 www.ssangyong.co.il 127.0.0.1 www.stacia-jose.us @@ -44148,6 +46616,7 @@ 127.0.0.1 www.starterdir.ru 127.0.0.1 www.statcount.com 127.0.0.1 www.statcounter.com +127.0.0.1 www.stateresolver.link 127.0.0.1 www.statistik.biz 127.0.0.1 www.stats4all.com 127.0.0.1 www.stats4you.com @@ -44179,6 +46648,7 @@ 127.0.0.1 www.suzi-vogelsang.us 127.0.0.1 www.swiftad.com 127.0.0.1 www.swimsuitnetwork.com +127.0.0.1 www.tag4arm.com 127.0.0.1 www.taggstar.com 127.0.0.1 www.tagjunction.com 127.0.0.1 www.talia-mettler.us @@ -44188,11 +46658,14 @@ 127.0.0.1 www.tarakc1.net 127.0.0.1 www.targad.com 127.0.0.1 www.targetmarketingusa.com +127.0.0.1 www.tarkita.ru 127.0.0.1 www.td553.com 127.0.0.1 www.teasermedia.net 127.0.0.1 www.teasernet.com 127.0.0.1 www.tenzero.com 127.0.0.1 www.teresa-taveras.us +127.0.0.1 www.terraads.net +127.0.0.1 www.terraclicks.com 127.0.0.1 www.terri-camarena.us 127.0.0.1 www.terri-macomber.us 127.0.0.1 www.terri-thrailkill.us @@ -44222,6 +46695,7 @@ 127.0.0.1 www.tiffanie-machin.us 127.0.0.1 www.tiffany-santee.us 127.0.0.1 www.tigerloads.com +127.0.0.1 www.tiller.co 127.0.0.1 www.tina-skiles.us 127.0.0.1 www.tinbuadserv.com 127.0.0.1 www.tisha-stickney.us @@ -44231,6 +46705,7 @@ 127.0.0.1 www.titanads4.com 127.0.0.1 www.titanads5.com 127.0.0.1 www.tizernaya-reklama.ru +127.0.0.1 www.tll.tl 127.0.0.1 www.tlvmedia.com 127.0.0.1 www.tns-counter.ru 127.0.0.1 www.tojoh.info @@ -44239,7 +46714,6 @@ 127.0.0.1 www.topadvancedsearch.com 127.0.0.1 www.topixc.net 127.0.0.1 www.toppagefinder.com -127.0.0.1 www.topsearcher.com 127.0.0.1 www.topstat.ru 127.0.0.1 www.toptrafficsource.com 127.0.0.1 www.tori-rakes.us @@ -44262,12 +46736,15 @@ 127.0.0.1 www.trackingbin.com 127.0.0.1 www.trackingclick.net 127.0.0.1 www.trackingstatalytics.com +127.0.0.1 www.trackinguk2.com 127.0.0.1 www.tracklead.net 127.0.0.1 www.tracknewsletter.com 127.0.0.1 www.trackvoluum.com 127.0.0.1 www.trackyourstats.com +127.0.0.1 www.tradeadexchange.com 127.0.0.1 www.tradetraffic.com 127.0.0.1 www.trading.dianomi.com +127.0.0.1 www.traffe.eu 127.0.0.1 www.traffic-exchanger.net 127.0.0.1 www.traffic4you.de 127.0.0.1 www.trafficads.com @@ -44288,6 +46765,8 @@ 127.0.0.1 www.traffictraders.com 127.0.0.1 www.traffspot.com 127.0.0.1 www.trafic.ro +127.0.0.1 www.traficmax.fr +127.0.0.1 www.traktum.com 127.0.0.1 www.trhzc.com 127.0.0.1 www.tribalfusion.com 127.0.0.1 www.trisha-meikle.us @@ -44296,21 +46775,27 @@ 127.0.0.1 www.trkag1.com 127.0.0.1 www.trkcpamedia.com 127.0.0.1 www.trksst.com +127.0.0.1 www.trkuoy.com 127.0.0.1 www.trkur.com +127.0.0.1 www.trkur1.com 127.0.0.1 www.trkur5.com 127.0.0.1 www.trkzen.com 127.0.0.1 www.trollzeweb.com +127.0.0.1 www.truetraffic4u.com 127.0.0.1 www.truongton.net 127.0.0.1 www.trw12.com 127.0.0.1 www.tryry.info 127.0.0.1 www.tudoparablogs2.hpg.com.br 127.0.0.1 www.tumri.net +127.0.0.1 www.tv4chan.com 127.0.0.1 www.tvsquad.com 127.0.0.1 www.tynt.com 127.0.0.1 www.typepad.com 127.0.0.1 www.typiol.com 127.0.0.1 www.tyxo.bg 127.0.0.1 www.uclick.com +127.0.0.1 www.udmserve.net +127.0.0.1 www.ugdturner.com 127.0.0.1 www.ukbanners.com 127.0.0.1 www.ulock.it 127.0.0.1 www.ultimatelyreveal.com @@ -44322,6 +46807,7 @@ 127.0.0.1 www.upv4.moatads.com 127.0.0.1 www.urlstats.com 127.0.0.1 www.urmediazone.com +127.0.0.1 www.urx.io 127.0.0.1 www.usabilla.com 127.0.0.1 www.usarevenue.com 127.0.0.1 www.usearchmedia.com @@ -44333,12 +46819,14 @@ 127.0.0.1 www.valuesponsor.com 127.0.0.1 www.vanessa-cohn.us 127.0.0.1 www.vayavicio.com +127.0.0.1 www.vdownloadall.com 127.0.0.1 www.vectormarketing.com 127.0.0.1 www.veruta.com 127.0.0.1 www.vibrantmedia.com 127.0.0.1 www.video-loader.com 127.0.0.1 www.view.atdmt.com.35354.9267.302br.net 127.0.0.1 www.viewbix.com +127.0.0.1 www.viewsecure.net 127.0.0.1 www.viglink.com 127.0.0.1 www.vignette.com 127.0.0.1 www.vikingtrck.com @@ -44350,9 +46838,11 @@ 127.0.0.1 www.visitorpath.com 127.0.0.1 www.visitorprofiler.com 127.0.0.1 www.visitweb.com +127.0.0.1 www.visualrevenue.com 127.0.0.1 www.vml-127.com 127.0.0.1 www.vmsn.de 127.0.0.1 www.voodooparking.com +127.0.0.1 www.vq91811.com 127.0.0.1 www.vroll.net 127.0.0.1 www.vsevjednom.cz 127.0.0.1 www.vstats.net @@ -44400,6 +46890,7 @@ 127.0.0.1 www.wendi-bell.us 127.0.0.1 www.werehacked.com 127.0.0.1 www.wesell.co.il +127.0.0.1 www.wgiftcard.com 127.0.0.1 www.whatseek.com 127.0.0.1 www.whosread.com 127.0.0.1 www.whydowork.com @@ -44410,6 +46901,7 @@ 127.0.0.1 www.wildrhino.com 127.0.0.1 www.win7affiliates.com 127.0.0.1 www.witchlab.com +127.0.0.1 www.wopnersoft.com 127.0.0.1 www.workflowboard.com 127.0.0.1 www.workfromhome.org 127.0.0.1 www.workingonline.com @@ -44428,6 +46920,7 @@ 127.0.0.1 www.x19network.com 127.0.0.1 www.xeanon.com 127.0.0.1 www.xiti.com +127.0.0.1 www.xl415.com 127.0.0.1 www.xratedtracking.com 127.0.0.1 www.xrevtrk.com 127.0.0.1 www.xstat.pl @@ -44447,7 +46940,6 @@ 127.0.0.1 www.yesupweb.com 127.0.0.1 www.yieldmanager.com 127.0.0.1 www.yllix.com -127.0.0.1 www.yoast.com 127.0.0.1 www.you2mail.com 127.0.0.1 www.youradexchange.com 127.0.0.1 www.youradslab.com @@ -44456,6 +46948,7 @@ 127.0.0.1 www.youyield.com 127.0.0.1 www.ytubevideoqualitymanager.com 127.0.0.1 www.z5x.net +127.0.0.1 www.za-ads.de 127.0.0.1 www.zanox-affiliate.de 127.0.0.1 www.zedo.com 127.0.0.1 www.zedoadnetwork.com @@ -44477,6 +46970,7 @@ 127.0.0.1 www1.kliks.nl 127.0.0.1 www1.mpnrs.com 127.0.0.1 www1.mystats.nl +127.0.0.1 www1.oratosaeron.com 127.0.0.1 www1.perf.overture.com 127.0.0.1 www1.runnergameshk.com 127.0.0.1 www1.track4.com @@ -44554,6 +47048,7 @@ 127.0.0.1 www3.webhostingtalk.com 127.0.0.1 www3.yesadvertising.com 127.0.0.1 www35.glam.com +127.0.0.1 www3secure.com 127.0.0.1 www4.addfreestats.com 127.0.0.1 www4.burstnet.com 127.0.0.1 www4.click-fr.com @@ -44602,6 +47097,7 @@ 127.0.0.1 www61.overture.com 127.0.0.1 www7.addfreestats.com 127.0.0.1 www7.bannerspace.com +127.0.0.1 www7.cbox.ws 127.0.0.1 www7.counter.bloke.com 127.0.0.1 www7.effectivemeasure.net 127.0.0.1 www7.fwarnr.us @@ -44703,7 +47199,9 @@ 127.0.0.1 xads.zedo.com 127.0.0.1 xapi.juicyads.com 127.0.0.1 xaxis.2cnt.net +127.0.0.1 xb11776.com 127.0.0.1 xban.walla.co.il +127.0.0.1 xbonus.net 127.0.0.1 xbox.ign.us.intellitxt.com 127.0.0.1 xbox.visits.lt 127.0.0.1 xbox360.ign.us.intellitxt.com @@ -44752,6 +47250,7 @@ 127.0.0.1 xyz.freelogs.com 127.0.0.1 xz.duba.net 127.0.0.1 xz6h6zj3.com +127.0.0.1 y.analytics.yahoo.com 127.0.0.1 y.extreme-dm.com 127.0.0.1 y.ibsys.com 127.0.0.1 y.jmp9.com @@ -44774,11 +47273,13 @@ 127.0.0.1 yahoo.serving-sys.com 127.0.0.1 yakezienetwork.go2cloud.org 127.0.0.1 yam.ivwbox.de +127.0.0.1 yandexadexchange.net 127.0.0.1 yang.hitbox.com 127.0.0.1 yapi.awe.sm 127.0.0.1 yast.rutube.ru 127.0.0.1 yb.torchbrowser.com 127.0.0.1 ybotvisit.com +127.0.0.1 yc-ads.s3.amazonaws.com 127.0.0.1 ycctrk.co.uk 127.0.0.1 ycv.netadsloc.com 127.0.0.1 ycv.secoptim.com @@ -44789,6 +47290,7 @@ 127.0.0.1 yellgroup.122.2o7.net 127.0.0.1 yello-de01.webtrekk.net 127.0.0.1 yellostrom.webtrekk.net +127.0.0.1 yen.appsfire.net 127.0.0.1 yepdigital.adk2x.com 127.0.0.1 yepdigital.go2cloud.org 127.0.0.1 yeppyepp.co.il @@ -44807,11 +47309,13 @@ 127.0.0.1 yj.moatads.com 127.0.0.1 yllix.com 127.0.0.1 ylu7k4i2o7.s.ad6media.fr -127.0.0.1 yoast.com 127.0.0.1 yoc-performance.com 127.0.0.1 yoclub.waphall.com 127.0.0.1 yorick.adjuggler.net +127.0.0.1 yotefiles.com +127.0.0.1 yottos.com 127.0.0.1 you2mail.com +127.0.0.1 youmi.net 127.0.0.1 your.365advert.com 127.0.0.1 youradexchange.com 127.0.0.1 youradslab.com @@ -44835,16 +47339,20 @@ 127.0.0.1 yrkeve.112.2o7.net 127.0.0.1 ysm.hauchi.com.tw 127.0.0.1 ytubevideoqualitymanager.com -127.0.0.1 yui.yahooapis.com 127.0.0.1 yuilop.com 127.0.0.1 yume.com 127.0.0.1 yuppy.2cnt.net +127.0.0.1 yvap.query.yahoo.com +127.0.0.1 yx-ads6.com +127.0.0.1 yxo.warmportrait.com 127.0.0.1 yz0fi.voluumtrk.com 127.0.0.1 yzus09by.com 127.0.0.1 z-na.amazon-adsystem.com +127.0.0.1 z.adcpm.com 127.0.0.1 z.blogads.com 127.0.0.1 z.extreme-dm.com 127.0.0.1 z.moatads.com +127.0.0.1 z.on.aol.com 127.0.0.1 z.times.lv 127.0.0.1 z.zedo.com 127.0.0.1 z.zeroredirect.com @@ -44915,6 +47423,7 @@ 127.0.0.1 zeitonl.ivwbox.de 127.0.0.1 zenent.go2cloud.org 127.0.0.1 zenithperformance.go2cloud.org +127.0.0.1 zenkreka.com 127.0.0.1 zerezas.com 127.0.0.1 zeroredirect.com 127.0.0.1 zeroredirect1.com @@ -44932,8 +47441,10 @@ 127.0.0.1 ziflx.trackvoluum.com 127.0.0.1 ziinga.go2cloud.org 127.0.0.1 zillow.go2cloud.org +127.0.0.1 zinturax.com 127.0.0.1 zinzimo.info 127.0.0.1 zipcity.com +127.0.0.1 zipropyl.com 127.0.0.1 zipzap.go2cloud.org 127.0.0.1 zj.dcys.ksmobile.com 127.0.0.1 zj.zeroredirect1.com @@ -44952,9 +47463,13 @@ 127.0.0.1 zm.zeroredirect1.com 127.0.0.1 zmedia.com 127.0.0.1 zn.zeroredirect1.com +127.0.0.1 zn0cjgsju6439lcsd-microsoft.siteintercept.qualtrics.com +127.0.0.1 zn87b2gchqlbyp0jn-viacom.siteintercept.qualtrics.com 127.0.0.1 zn_0xssfnnsxmogd01-cbs.siteintercept.qualtrics.com +127.0.0.1 zn_4slwdofs4ykbmhc-espn.siteintercept.qualtrics.com 127.0.0.1 zn_9nquvazst1xipkt-cbs.siteintercept.qualtrics.com 127.0.0.1 zn_b4q1adq70amqdth-citrix.siteintercept.qualtrics.com +127.0.0.1 zn_brp7urzdaj4xmxn-edhealth.siteintercept.qualtrics.com 127.0.0.1 zo.zeroredirect1.com 127.0.0.1 zo6.adservlite.com 127.0.0.1 zombo.com @@ -44972,6 +47487,7 @@ 127.0.0.1 zs.ffshrine.org 127.0.0.1 zs.zeroredirect1.com 127.0.0.1 zs0s5xfe.com +127.0.0.1 zt.1rx.io 127.0.0.1 zt.zeroredirect1.com 127.0.0.1 ztrack.net 127.0.0.1 zu.zeroredirect1.com @@ -44983,7 +47499,9 @@ 127.0.0.1 zx.zeroredirect1.com 127.0.0.1 zxypenguin.people-group.su 127.0.0.1 zy.zeroredirect1.com +127.0.0.1 zyngawithfriends.com 127.0.0.1 zz.cqcounter.com 127.0.0.1 zz.zeroredirect1.com +127.0.0.1 zzz.clickbank.net 127.0.0.1 _thums.ero-advertising.com -# Hosts: 44978 \ No newline at end of file +# Hosts: 47496 \ No newline at end of file diff --git a/app/src/LightningPlus/java/acr/browser/lightning/utils/ProxyUtils.java b/app/src/LightningPlus/java/acr/browser/lightning/utils/ProxyUtils.java deleted file mode 100644 index d49f9f2b5..000000000 --- a/app/src/LightningPlus/java/acr/browser/lightning/utils/ProxyUtils.java +++ /dev/null @@ -1,214 +0,0 @@ -package acr.browser.lightning.utils; - -import android.app.Activity; -import android.content.Context; -import android.content.DialogInterface; -import android.support.v7.app.AlertDialog; -import android.util.Log; - -import net.i2p.android.ui.I2PAndroidHelper; - -import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; -import acr.browser.lightning.constant.Constants; -import acr.browser.lightning.preference.PreferenceManager; -import info.guardianproject.netcipher.proxy.OrbotHelper; -import info.guardianproject.netcipher.web.WebkitProxy; - -/** - * 6/4/2015 Anthony Restaino - */ -public class ProxyUtils { - // Helper - private final I2PAndroidHelper mI2PHelper; - private static boolean mI2PHelperBound; - private static boolean mI2PProxyInitialized; - private final PreferenceManager mPreferences; - private static ProxyUtils mInstance; - - private ProxyUtils(Context context) { - mPreferences = PreferenceManager.getInstance(); - mI2PHelper = new I2PAndroidHelper(context.getApplicationContext()); - } - - public static ProxyUtils getInstance() { - if (mInstance == null) { - mInstance = new ProxyUtils(BrowserApp.getAppContext()); - } - return mInstance; - } - - /* - * If Orbot/Tor or I2P is installed, prompt the user if they want to enable - * proxying for this session - */ - public void checkForProxy(final Activity activity) { - boolean useProxy = mPreferences.getUseProxy(); - - final boolean orbotInstalled = OrbotHelper.isOrbotInstalled(activity); - boolean orbotChecked = mPreferences.getCheckedForTor(); - boolean orbot = orbotInstalled && !orbotChecked; - - boolean i2pInstalled = mI2PHelper.isI2PAndroidInstalled(); - boolean i2pChecked = mPreferences.getCheckedForI2P(); - boolean i2p = i2pInstalled && !i2pChecked; - - // TODO Is the idea to show this per-session, or only once? - if (!useProxy && (orbot || i2p)) { - if (orbot) mPreferences.setCheckedForTor(true); - if (i2p) mPreferences.setCheckedForI2P(true); - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - - if (orbotInstalled && i2pInstalled) { - String[] proxyChoices = activity.getResources().getStringArray(R.array.proxy_choices_array); - builder.setTitle(activity.getResources().getString(R.string.http_proxy)) - .setSingleChoiceItems(proxyChoices, mPreferences.getProxyChoice(), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - mPreferences.setProxyChoice(which); - } - }) - .setNeutralButton(activity.getResources().getString(R.string.action_ok), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (mPreferences.getUseProxy()) - initializeProxy(activity); - } - }); - } else { - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - mPreferences.setProxyChoice(orbotInstalled ? - Constants.PROXY_ORBOT : Constants.PROXY_I2P); - initializeProxy(activity); - break; - case DialogInterface.BUTTON_NEGATIVE: - mPreferences.setProxyChoice(Constants.NO_PROXY); - break; - } - } - }; - - builder.setMessage(orbotInstalled ? R.string.use_tor_prompt : R.string.use_i2p_prompt) - .setPositiveButton(R.string.yes, dialogClickListener) - .setNegativeButton(R.string.no, dialogClickListener); - } - builder.show(); - } - } - - /* - * Initialize WebKit Proxying - */ - private void initializeProxy(Activity activity) { - String host; - int port; - - switch (mPreferences.getProxyChoice()) { - case Constants.NO_PROXY: - // We shouldn't be here - return; - - case Constants.PROXY_ORBOT: - if (!OrbotHelper.isOrbotRunning(activity)) - OrbotHelper.requestStartTor(activity); - host = "localhost"; - port = 8118; - break; - - case Constants.PROXY_I2P: - mI2PProxyInitialized = true; - if (mI2PHelperBound && !mI2PHelper.isI2PAndroidRunning()) { - mI2PHelper.requestI2PAndroidStart(activity); - } - host = "localhost"; - port = 4444; - break; - - default: - host = mPreferences.getProxyHost(); - port = mPreferences.getProxyPort(); - } - - try { - WebkitProxy.setProxy(BrowserApp.class.getName(), activity.getApplicationContext(), null, host, port); - } catch (Exception e) { - Log.d(Constants.TAG, "error enabling web proxying", e); - } - - } - - public boolean isProxyReady(Activity activity) { - if (mPreferences.getProxyChoice() == Constants.PROXY_I2P) { - if (!mI2PHelper.isI2PAndroidRunning()) { - Utils.showSnackbar(activity, R.string.i2p_not_running); - return false; - } else if (!mI2PHelper.areTunnelsActive()) { - Utils.showSnackbar(activity, R.string.i2p_tunnels_not_ready); - return false; - } - } - - return true; - } - - public void updateProxySettings(Activity activity) { - if (mPreferences.getUseProxy()) { - initializeProxy(activity); - } else { - try { - WebkitProxy.resetProxy(BrowserApp.class.getName(), activity.getApplicationContext()); - } catch (Exception e) { - e.printStackTrace(); - } - - mI2PProxyInitialized = false; - } - } - - public void onStop() { - mI2PHelper.unbind(); - mI2PHelperBound = false; - } - - public void onStart(final Activity activity) { - if (mPreferences.getProxyChoice() == Constants.PROXY_I2P) { - // Try to bind to I2P Android - mI2PHelper.bind(new I2PAndroidHelper.Callback() { - @Override - public void onI2PAndroidBound() { - mI2PHelperBound = true; - if (mI2PProxyInitialized && !mI2PHelper.isI2PAndroidRunning()) - mI2PHelper.requestI2PAndroidStart(activity); - } - }); - } - } - - public static int setProxyChoice(int choice, Activity activity) { - switch (choice) { - case Constants.PROXY_ORBOT: - if (!OrbotHelper.isOrbotInstalled(activity)) { - choice = Constants.NO_PROXY; - Utils.showSnackbar(activity, R.string.install_orbot); - } - break; - - case Constants.PROXY_I2P: - I2PAndroidHelper ih = new I2PAndroidHelper(activity.getApplicationContext()); - if (!ih.isI2PAndroidInstalled()) { - choice = Constants.NO_PROXY; - ih.promptToInstall(activity); - } - break; - case Constants.PROXY_MANUAL: - break; - } - return choice; - } -} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c3d47ec23..a1df8d8d9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,24 +1,26 @@ - - - - - - - - - - + + + + + + + + + + + + + android:required="false"/> + android:required="false"/> + android:required="false"/> + android:theme="@style/Theme.LightTheme" + android:windowSoftInputMode="adjustResize"> - + - - - - + + + + - + - - + + - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - + - - + + - - - - + + + + - + - - + + + + + + + + - - - + - + - + + android:windowSoftInputMode="stateHidden|adjustResize"> - + - + - + - + diff --git a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java index 39a081261..a32a37660 100644 --- a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java @@ -4,10 +4,7 @@ package acr.browser.lightning.activity; -import android.animation.ArgbEvaluator; -import android.animation.LayoutTransition; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.TargetApi; import android.app.Activity; import android.content.ClipData; import android.content.ClipboardManager; @@ -15,17 +12,12 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Configuration; import android.database.sqlite.SQLiteException; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.ColorFilter; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.Paint; import android.graphics.PorterDuff; -import android.graphics.drawable.BitmapDrawable; +import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.media.MediaPlayer; @@ -34,25 +26,25 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.os.StrictMode; import android.provider.MediaStore; +import android.support.annotation.ColorInt; import android.support.annotation.IdRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.StringRes; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; import android.support.v4.view.GravityCompat; -import android.support.v4.view.ViewCompat; import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.DrawerLayout.DrawerListener; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.support.v7.graphics.Palette; -import android.support.v7.graphics.drawable.DrawerArrowDrawable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; @@ -64,10 +56,11 @@ import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; +import android.view.ViewTreeObserver; import android.view.Window; +import android.view.WindowInsets; import android.view.WindowManager; import android.view.animation.Animation; -import android.view.animation.Animation.AnimationListener; import android.view.animation.DecelerateInterpolator; import android.view.animation.Transformation; import android.view.inputmethod.EditorInfo; @@ -76,7 +69,6 @@ import android.webkit.WebChromeClient.CustomViewCallback; import android.webkit.WebIconDatabase; import android.webkit.WebView; -import android.webkit.WebView.HitTestResult; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; @@ -91,34 +83,41 @@ import android.widget.TextView.OnEditorActionListener; import android.widget.VideoView; +import com.anthonycr.grant.PermissionsManager; import com.squareup.otto.Bus; import com.squareup.otto.Subscribe; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import javax.inject.Inject; +import acr.browser.lightning.BuildConfig; import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.browser.BrowserPresenter; +import acr.browser.lightning.browser.BrowserView; +import acr.browser.lightning.browser.TabsView; import acr.browser.lightning.bus.BookmarkEvents; import acr.browser.lightning.bus.BrowserEvents; +import acr.browser.lightning.bus.NavigationEvents; +import acr.browser.lightning.bus.TabEvents; import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.HistoryPage; -import acr.browser.lightning.controller.BrowserController; +import acr.browser.lightning.controller.UIController; import acr.browser.lightning.database.BookmarkManager; import acr.browser.lightning.database.HistoryDatabase; -import acr.browser.lightning.dialog.BookmarksDialogBuilder; -import acr.browser.lightning.object.ClickHandler; -import acr.browser.lightning.object.SearchAdapter; -import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.database.HistoryItem; +import acr.browser.lightning.dialog.LightningDialogBuilder; +import acr.browser.lightning.fragment.BookmarksFragment; +import acr.browser.lightning.fragment.TabsFragment; +import acr.browser.lightning.search.SuggestionsAdapter; +import acr.browser.lightning.react.Observable; +import acr.browser.lightning.react.Schedulers; import acr.browser.lightning.receiver.NetworkReceiver; -import acr.browser.lightning.utils.PermissionsManager; +import acr.browser.lightning.utils.DrawableUtils; +import acr.browser.lightning.utils.KeyboardHelper; import acr.browser.lightning.utils.ProxyUtils; import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.UrlUtils; @@ -129,95 +128,88 @@ import butterknife.Bind; import butterknife.ButterKnife; -public abstract class BrowserActivity extends ThemableBrowserActivity implements BrowserController, OnClickListener, OnLongClickListener { +public abstract class BrowserActivity extends ThemableBrowserActivity implements BrowserView, UIController, OnClickListener, OnLongClickListener { - // Static Layout - @Bind(R.id.drawer_layout) - DrawerLayout mDrawerLayout; - - @Bind(R.id.content_frame) - FrameLayout mBrowserFrame; - - @Bind(R.id.left_drawer) - ViewGroup mDrawerLeft; - - @Bind(R.id.right_drawer) - ViewGroup mDrawerRight; - - @Bind(R.id.ui_layout) - ViewGroup mUiLayout; - - @Bind(R.id.toolbar_layout) - ViewGroup mToolbarLayout; + private static final String TAG = BrowserActivity.class.getSimpleName(); - @Bind(R.id.progress_view) - AnimatedProgressBar mProgressBar; + private static final String INTENT_PANIC_TRIGGER = "info.guardianproject.panic.action.TRIGGER"; - @Bind(R.id.search_bar) - RelativeLayout mSearchBar; + private static final String TAG_BOOKMARK_FRAGMENT = "TAG_BOOKMARK_FRAGMENT"; + private static final String TAG_TABS_FRAGMENT = "TAG_TABS_FRAGMENT"; + // Static Layout + @Bind(Window.ID_ANDROID_CONTENT) View mRoot; + @Bind(R.id.drawer_layout) DrawerLayout mDrawerLayout; + @Bind(R.id.content_frame) FrameLayout mBrowserFrame; + @Bind(R.id.left_drawer) ViewGroup mDrawerLeft; + @Bind(R.id.right_drawer) ViewGroup mDrawerRight; + @Bind(R.id.ui_layout) ViewGroup mUiLayout; + @Bind(R.id.toolbar_layout) ViewGroup mToolbarLayout; + @Bind(R.id.progress_view) AnimatedProgressBar mProgressBar; + @Bind(R.id.search_bar) RelativeLayout mSearchBar; - // Browser Views - private final List mWebViewList = new ArrayList<>(); - private LightningView mCurrentView; - private WebView mWebView; - private RecyclerView mTabListView; // Toolbar Views + private View mSearchBackground; + private Toolbar mToolbar; private AutoCompleteTextView mSearch; private ImageView mArrowImage; + // Current tab view being displayed + @Nullable private View mCurrentView; + // Full Screen Video Views private FrameLayout mFullscreenContainer; private VideoView mVideoView; private View mCustomView; // Adapter - private LightningViewAdapter mTabAdapter; - private SearchAdapter mSearchAdapter; + private SuggestionsAdapter mSuggestionsAdapter; // Callback - private ClickHandler mClickHandler; private CustomViewCallback mCustomViewCallback; private ValueCallback mUploadMessage; private ValueCallback mFilePathCallback; // Primatives - private boolean mFullScreen, mColorMode, mDarkTheme, - mIsNewIntent = false, - mIsFullScreen = false, - mIsImmersive = false, - mShowTabsInDrawer; - private int mOriginalOrientation, mBackgroundColor, mIdGenerator, mIconColor, - mCurrentUiColor = Color.BLACK; - private String mSearchText, mUntitledTitle, mHomepage, mCameraPhotoPath; - - // Storage - private HistoryDatabase mHistoryDatabase; - private final PreferenceManager mPreferences = PreferenceManager.getInstance(); + private boolean mFullScreen; + private boolean mDarkTheme; + private boolean mIsFullScreen = false; + private boolean mIsImmersive = false; + private boolean mShowTabsInDrawer; + private int mOriginalOrientation; + private int mBackgroundColor; + private int mIconColor; + private int mDisabledIconColor; + private int mCurrentUiColor = Color.BLACK; + private String mSearchText; + private String mUntitledTitle; + private String mCameraPhotoPath; + + private final Handler mDrawerHandler = new Handler(); // The singleton BookmarkManager - @Inject - BookmarkManager mBookmarkManager; + @Inject BookmarkManager mBookmarkManager; // Event bus - @Inject - Bus mEventBus; + @Inject Bus mEventBus; + + @Inject LightningDialogBuilder mBookmarksDialogBuilder; - @Inject - BookmarkPage mBookmarkPage; + private TabsManager mTabsManager; - @Inject - BookmarksDialogBuilder mBookmarksDialogBuilder; + @Inject HistoryDatabase mHistoryDatabase; // Image private Bitmap mWebpageBitmap; private final ColorDrawable mBackground = new ColorDrawable(); private Drawable mDeleteIcon, mRefreshIcon, mClearIcon, mIcon; - private DrawerArrowDrawable mArrowDrawable; + + private BrowserPresenter mPresenter; + private TabsView mTabsView; // Proxy - private ProxyUtils mProxyUtils; + @Inject ProxyUtils mProxyUtils; // Constant private static final int API = android.os.Build.VERSION.SDK_INT; @@ -227,85 +219,129 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements private static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - public abstract boolean isIncognito(); - - abstract void initializeTabs(); - - abstract void closeActivity(); + protected abstract boolean isIncognito(); - public abstract void updateHistory(final String title, final String url); + public abstract void closeActivity(); - abstract void updateCookiePreference(); + public abstract void updateHistory(@Nullable final String title, @NonNull final String url); + abstract Observable updateCookiePreference(); @Override protected void onCreate(Bundle savedInstanceState) { + + if (BuildConfig.DEBUG) { + StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() + .detectDiskReads() + .detectDiskWrites() + .detectNetwork() + .penaltyLog() + .build()); + StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() + .detectLeakedClosableObjects() + .detectLeakedSqlLiteObjects() + .penaltyLog() + .build()); + } + super.onCreate(savedInstanceState); BrowserApp.getAppComponent().inject(this); setContentView(R.layout.activity_main); ButterKnife.bind(this); - initialize(); + + mTabsManager = new TabsManager(); + mPresenter = new BrowserPresenter(this, isIncognito()); + + initialize(savedInstanceState); + + KeyboardHelper keyboardHelper = new KeyboardHelper(mRoot); + keyboardHelper.registerKeyboardListener(new KeyboardHelper.KeyboardListener() { + @Override + public void keyboardVisibilityChanged(boolean visible) { + if (visible) { + setTabHeightForKeyboard(); + } else { + setTabHeight(); + } + } + }); } - private synchronized void initialize() { - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + private synchronized void initialize(Bundle savedInstanceState) { + mToolbar = (Toolbar) findViewById(R.id.toolbar); + initializeToolbarHeight(getResources().getConfiguration()); + setSupportActionBar(mToolbar); ActionBar actionBar = getSupportActionBar(); //TODO make sure dark theme flag gets set correctly mDarkTheme = mPreferences.getUseTheme() != 0 || isIncognito(); mIconColor = mDarkTheme ? ThemeUtils.getIconDarkThemeColor(this) : ThemeUtils.getIconLightThemeColor(this); + mDisabledIconColor = mDarkTheme ? ContextCompat.getColor(this, R.color.icon_dark_theme_disabled) : + ContextCompat.getColor(this, R.color.icon_light_theme_disabled); mShowTabsInDrawer = mPreferences.getShowTabsInDrawer(!isTablet()); - mWebViewList.clear(); - - mClickHandler = new ClickHandler(this); // initialize background ColorDrawable - mBackground.setColor(((ColorDrawable) mToolbarLayout.getBackground()).getColor()); + int primaryColor = ThemeUtils.getPrimaryColor(this); + mBackground.setColor(primaryColor); - setupFrameLayoutButton(R.id.new_tab_button, R.id.icon_plus); // Drawer stutters otherwise - mDrawerLeft.setLayerType(View.LAYER_TYPE_HARDWARE, null); - mDrawerRight.setLayerType(View.LAYER_TYPE_HARDWARE, null); - ImageView tabTitleImage = (ImageView) findViewById(R.id.plusIcon); - tabTitleImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); + mDrawerLeft.setLayerType(View.LAYER_TYPE_NONE, null); + mDrawerRight.setLayerType(View.LAYER_TYPE_NONE, null); + + mDrawerLayout.addDrawerListener(new DrawerListener() { + @Override + public void onDrawerSlide(View drawerView, float slideOffset) {} + + @Override + public void onDrawerOpened(View drawerView) {} + + @Override + public void onDrawerClosed(View drawerView) {} + + @Override + public void onDrawerStateChanged(int newState) { + if (newState == DrawerLayout.STATE_DRAGGING) { + mDrawerLeft.setLayerType(View.LAYER_TYPE_HARDWARE, null); + mDrawerRight.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } else if (newState == DrawerLayout.STATE_IDLE) { + mDrawerLeft.setLayerType(View.LAYER_TYPE_NONE, null); + mDrawerRight.setLayerType(View.LAYER_TYPE_NONE, null); + } + } + }); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !mShowTabsInDrawer) { getWindow().setStatusBarColor(Color.BLACK); } setNavigationDrawerWidth(); - mDrawerLayout.setDrawerListener(new DrawerLocker()); + mDrawerLayout.addDrawerListener(new DrawerLocker()); mWebpageBitmap = ThemeUtils.getThemedBitmap(this, R.drawable.ic_webpage, mDarkTheme); - mHomepage = mPreferences.getHomepage(); - - RecyclerView horizontalListView = (RecyclerView) findViewById(R.id.twv_list); - - + final TabsFragment tabsFragment = new TabsFragment(); + mTabsView = tabsFragment; + final int containerId = mShowTabsInDrawer ? R.id.left_drawer : R.id.tabs_toolbar_container; + final Bundle tabsFragmentArguments = new Bundle(); + tabsFragmentArguments.putBoolean(TabsFragment.IS_INCOGNITO, isIncognito()); + tabsFragmentArguments.putBoolean(TabsFragment.VERTICAL_MODE, mShowTabsInDrawer); + tabsFragment.setArguments(tabsFragmentArguments); + + final BookmarksFragment bookmarksFragment = new BookmarksFragment(); + final Bundle bookmarksFragmentArguments = new Bundle(); + bookmarksFragmentArguments.putBoolean(BookmarksFragment.INCOGNITO_MODE, isIncognito()); + bookmarksFragment.setArguments(bookmarksFragmentArguments); + + final FragmentManager fragmentManager = getSupportFragmentManager(); + fragmentManager + .beginTransaction() + .replace(containerId, tabsFragment, TAG_TABS_FRAGMENT) + .replace(R.id.right_drawer, bookmarksFragment, TAG_BOOKMARK_FRAGMENT) + .commit(); if (mShowTabsInDrawer) { - mTabAdapter = new LightningViewAdapter(this, R.layout.tab_list_item, mWebViewList); - mTabListView = (RecyclerView) findViewById(R.id.left_drawer_list); - mTabListView.setOverScrollMode(View.OVER_SCROLL_IF_CONTENT_SCROLLS); - RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); - mTabListView.setLayoutManager(layoutManager); - mTabListView.setHasFixedSize(true); - mToolbarLayout.removeView(horizontalListView); - } else { - mTabAdapter = new LightningViewAdapter(this, R.layout.tab_list_item_horizontal, mWebViewList); - mTabListView = horizontalListView; - mTabListView.setOverScrollMode(View.OVER_SCROLL_NEVER); - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mDrawerLeft); - RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); - mTabListView.setLayoutManager(layoutManager); - mTabListView.setHasFixedSize(true); + mToolbarLayout.removeView(findViewById(R.id.tabs_toolbar_container)); } - mTabListView.setAdapter(mTabAdapter); - - mHistoryDatabase = HistoryDatabase.getInstance(); - if (actionBar == null) return; @@ -324,30 +360,31 @@ private synchronized void initialize() { mArrowImage = (ImageView) customView.findViewById(R.id.arrow); FrameLayout arrowButton = (FrameLayout) customView.findViewById(R.id.arrow_button); if (mShowTabsInDrawer) { - // Use hardware acceleration for the animation - mArrowDrawable = new DrawerArrowDrawable(this); - mArrowImage.setLayerType(View.LAYER_TYPE_HARDWARE, null); - mArrowImage.setImageDrawable(mArrowDrawable); + if (mArrowImage.getWidth() <= 0) { + mArrowImage.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + } + updateTabNumber(0); } else { + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mDrawerLeft); mArrowImage.setImageResource(R.drawable.ic_action_home); mArrowImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); } arrowButton.setOnClickListener(this); - mProxyUtils = ProxyUtils.getInstance(); - - setupFrameLayoutButton(R.id.action_back, R.id.icon_back); - setupFrameLayoutButton(R.id.action_forward, R.id.icon_forward); - setupFrameLayoutButton(R.id.action_toggle_desktop, R.id.icon_desktop); - setupFrameLayoutButton(R.id.action_reading, R.id.icon_reading); - // create the search EditText in the ToolBar mSearch = (AutoCompleteTextView) customView.findViewById(R.id.search); + mSearchBackground = customView.findViewById(R.id.search_container); + + // initialize search background color + mSearchBackground.getBackground().setColorFilter(getSearchBarColor(primaryColor, primaryColor), PorterDuff.Mode.SRC_IN); + mSearch.setHintTextColor(ThemeUtils.getThemedTextHintColor(mDarkTheme)); + mSearch.setTextColor(mDarkTheme ? Color.WHITE : Color.BLACK); + mUntitledTitle = getString(R.string.untitled); - mBackgroundColor = ContextCompat.getColor(this, R.color.primary_color); - mDeleteIcon = ThemeUtils.getLightThemedDrawable(this, R.drawable.ic_action_delete); - mRefreshIcon = ThemeUtils.getLightThemedDrawable(this, R.drawable.ic_action_refresh); - mClearIcon = ThemeUtils.getLightThemedDrawable(this, R.drawable.ic_action_delete); + mBackgroundColor = ThemeUtils.getPrimaryColor(this); + mDeleteIcon = ThemeUtils.getThemedDrawable(this, R.drawable.ic_action_delete, mDarkTheme); + mRefreshIcon = ThemeUtils.getThemedDrawable(this, R.drawable.ic_action_refresh, mDarkTheme); + mClearIcon = ThemeUtils.getThemedDrawable(this, R.drawable.ic_action_delete, mDarkTheme); int iconBounds = Utils.dpToPx(30); mDeleteIcon.setBounds(0, 0, iconBounds, iconBounds); @@ -361,14 +398,7 @@ private synchronized void initialize() { mSearch.setOnEditorActionListener(search); mSearch.setOnTouchListener(search); - new Thread(new Runnable() { - - @Override - public void run() { - initializeSearchSuggestions(mSearch); - } - - }).run(); + initializeSearchSuggestions(mSearch); mDrawerLayout.setDrawerShadow(R.drawable.drawer_right_shadow, GravityCompat.END); mDrawerLayout.setDrawerShadow(R.drawable.drawer_left_shadow, GravityCompat.START); @@ -378,9 +408,33 @@ public void run() { WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath()); } - initializeTabs(); + Intent intent = savedInstanceState == null ? getIntent() : null; + + if (isPanicTrigger(intent)) { + setIntent(null); + panicClean(); + } else { + mPresenter.setupTabs(intent); + setIntent(null); + mProxyUtils.checkForProxy(BrowserActivity.this); + } + } + + static boolean isPanicTrigger(@Nullable Intent intent) { + return intent != null && INTENT_PANIC_TRIGGER.equals(intent.getAction()); + } - mProxyUtils.checkForProxy(this); + void panicClean() { + Log.d(TAG, "Closing browser"); + mTabsManager.newTab(this, "", false); + mTabsManager.switchToTab(0); + mTabsManager.clearSavedState(); + HistoryPage.deleteHistoryPage(getApplication()); + closeBrowser(); + // System exit needed in the case of receiving + // the panic intent since finish() isn't completely + // closing the browser + System.exit(1); } private class SearchListenerClass implements OnKeyListener, OnEditorActionListener, OnFocusChangeListener, OnTouchListener { @@ -393,8 +447,9 @@ public boolean onKey(View arg0, int arg1, KeyEvent arg2) { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0); searchTheWeb(mSearch.getText().toString()); - if (mCurrentView != null) { - mCurrentView.requestFocus(); + final LightningView currentView = mTabsManager.getCurrentTab(); + if (currentView != null) { + currentView.requestFocus(); } return true; default: @@ -415,8 +470,9 @@ public boolean onEditorAction(TextView arg0, int actionId, KeyEvent arg2) { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0); searchTheWeb(mSearch.getText().toString()); - if (mCurrentView != null) { - mCurrentView.requestFocus(); + final LightningView currentView = mTabsManager.getCurrentTab(); + if (currentView != null) { + currentView.requestFocus(); } return true; } @@ -425,71 +481,22 @@ public boolean onEditorAction(TextView arg0, int actionId, KeyEvent arg2) { @Override public void onFocusChange(View v, final boolean hasFocus) { - if (!hasFocus && mCurrentView != null) { - setIsLoading(mCurrentView.getProgress() < 100); - updateUrl(mCurrentView.getUrl(), true); - } else if (hasFocus) { - String url = mCurrentView.getUrl(); - if (url.startsWith(Constants.FILE)) { + final LightningView currentView = mTabsManager.getCurrentTab(); + if (!hasFocus && currentView != null) { + setIsLoading(currentView.getProgress() < 100); + updateUrl(currentView.getUrl(), true); + } else if (hasFocus && currentView != null) { + String url = currentView.getUrl(); + if (UrlUtils.isSpecialUrl(url)) { mSearch.setText(""); } else { mSearch.setText(url); } - ((AutoCompleteTextView) v).selectAll(); // Hack to make sure - // the text gets - // selected + // Hack to make sure the text gets selected + ((AutoCompleteTextView) v).selectAll(); mIcon = mClearIcon; mSearch.setCompoundDrawables(null, null, mClearIcon, null); } - final Animation anim = new Animation() { - - @Override - protected void applyTransformation(float interpolatedTime, Transformation t) { - if (!hasFocus) { - mArrowDrawable.setProgress(1.0f - interpolatedTime); - } else { - mArrowDrawable.setProgress(interpolatedTime); - } - } - - @Override - public boolean willChangeBounds() { - return true; - } - - }; - anim.setDuration(300); - anim.setInterpolator(new DecelerateInterpolator()); - anim.setAnimationListener(new AnimationListener() { - - @Override - public void onAnimationStart(Animation animation) { - } - - @Override - public void onAnimationEnd(Animation animation) { - if (!hasFocus) { - mArrowDrawable.setProgress(0.0f); - } else { - mArrowDrawable.setProgress(1.0f); - } - } - - @Override - public void onAnimationRepeat(Animation animation) { - } - - }); - new Handler().postDelayed(new Runnable() { - - @Override - public void run() { - if (mArrowDrawable != null) { - mArrowImage.startAnimation(anim); - } - } - - }, 100); if (!hasFocus) { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); @@ -538,12 +545,10 @@ public void onDrawerOpened(View v) { } @Override - public void onDrawerSlide(View v, float arg) { - } + public void onDrawerSlide(View v, float arg) {} @Override - public void onDrawerStateChanged(int arg) { - } + public void onDrawerStateChanged(int arg) {} } @@ -580,83 +585,52 @@ private void setNavigationDrawerWidth() { } } - void restoreOrNewTab() { - mIdGenerator = 0; - - String url = null; - if (getIntent() != null) { - url = getIntent().getDataString(); - if (url != null) { - if (url.startsWith(Constants.FILE)) { - Utils.showSnackbar(this, R.string.message_blocked_local); - url = null; - } - } - } - if (mPreferences.getRestoreLostTabsEnabled()) { - String mem = mPreferences.getMemoryUrl(); - mPreferences.setMemoryUrl(""); - String[] array = Utils.getArray(mem); - int count = 0; - for (String urlString : array) { - if (!urlString.isEmpty()) { - if (url != null && url.compareTo(urlString) == 0) { - url = null; - } - newTab(urlString, true); - count++; - } - } - if (url != null) { - newTab(url, true); - } else if (count == 0) { - newTab(null, true); - } - } else { - newTab(url, true); - } - } - private void initializePreferences() { + final LightningView currentView = mTabsManager.getCurrentTab(); mFullScreen = mPreferences.getFullScreenEnabled(); - mColorMode = mPreferences.getColorModeEnabled(); - mColorMode &= !mDarkTheme; - if (!isIncognito() && !mColorMode && !mDarkTheme && mWebpageBitmap != null) { + boolean colorMode = mPreferences.getColorModeEnabled(); + colorMode &= !mDarkTheme; + if (!isIncognito() && !colorMode && !mDarkTheme && mWebpageBitmap != null) { + changeToolbarBackground(mWebpageBitmap, null); + } else if (!isIncognito() && currentView != null && !mDarkTheme) { + changeToolbarBackground(currentView.getFavicon(), null); + } else if (!isIncognito() && !mDarkTheme && mWebpageBitmap != null) { changeToolbarBackground(mWebpageBitmap, null); - mTabAdapter.notifyDataSetChanged(); - } else if (!isIncognito() && mCurrentView != null && !mDarkTheme - && mCurrentView.getFavicon() != null) { - changeToolbarBackground(mCurrentView.getFavicon(), null); - mTabAdapter.notifyDataSetChanged(); } - if (mFullScreen) { - mToolbarLayout.setTranslationY(0); - int height = mToolbarLayout.getHeight(); - if (height == 0) { - mToolbarLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - height = mToolbarLayout.getMeasuredHeight(); - } - if (mWebView != null) - mWebView.setTranslationY(height); - mBrowserFrame.setLayoutTransition(null); - if (mBrowserFrame.findViewById(R.id.toolbar_layout) == null) { - mUiLayout.removeView(mToolbarLayout); - mBrowserFrame.addView(mToolbarLayout); - mToolbarLayout.bringToFront(); - } - } else { - mToolbarLayout.setTranslationY(0); - if (mBrowserFrame.findViewById(R.id.toolbar_layout) != null) { - mBrowserFrame.removeView(mToolbarLayout); - mUiLayout.addView(mToolbarLayout, 0); - } - mBrowserFrame.setLayoutTransition(new LayoutTransition()); - if (mWebView != null) - mWebView.setTranslationY(0); + FragmentManager manager = getSupportFragmentManager(); + Fragment tabsFragment = manager.findFragmentByTag(TAG_TABS_FRAGMENT); + if (tabsFragment instanceof TabsFragment) { + ((TabsFragment) tabsFragment).reinitializePreferences(); } + Fragment bookmarksFragment = manager.findFragmentByTag(TAG_BOOKMARK_FRAGMENT); + if (bookmarksFragment instanceof BookmarksFragment) { + ((BookmarksFragment) bookmarksFragment).reinitializePreferences(); + } + + + // TODO layout transition causing memory leak +// mBrowserFrame.setLayoutTransition(new LayoutTransition()); + + mToolbarLayout.setTranslationY(0); + mBrowserFrame.setTranslationY(0); setFullscreen(mPreferences.getHideStatusBarEnabled(), false); + initializeTabHeight(); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) { + // Sets the tab height correctly if the status bar is hidden + // Also ensures that tab is correct height on rotation + mRoot.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) + @Override + public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { + initializeTabHeight(); + return mRoot.onApplyWindowInsets(insets); + } + }); + } + switch (mPreferences.getSearchChoice()) { case 0: mSearchText = mPreferences.getSearchUrl(); @@ -697,7 +671,7 @@ private void initializePreferences() { break; } - updateCookiePreference(); + updateCookiePreference().subscribeOn(Schedulers.worker()).subscribe(); mProxyUtils.updateProxySettings(this); } @@ -730,6 +704,8 @@ public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { @Override public boolean onOptionsItemSelected(MenuItem item) { + final LightningView currentView = mTabsManager.getCurrentTab(); + final String currentUrl = currentView != null ? currentView.getUrl() : null; // Handle action buttons switch (item.getItemId()) { case android.R.id.home: @@ -738,13 +714,20 @@ public boolean onOptionsItemSelected(MenuItem item) { } return true; case R.id.action_back: - if (mCurrentView != null && mCurrentView.canGoBack()) { - mCurrentView.goBack(); + if (currentView != null && currentView.canGoBack()) { + currentView.goBack(); } return true; case R.id.action_forward: - if (mCurrentView != null && mCurrentView.canGoForward()) { - mCurrentView.goForward(); + if (currentView != null && currentView.canGoForward()) { + currentView.goForward(); + } + return true; + case R.id.action_add_to_homescreen: + if (currentView != null) { + HistoryItem shortcut = new HistoryItem(currentView.getUrl(), currentView.getTitle()); + shortcut.setBitmap(currentView.getFavicon()); + Utils.createShortcut(this, shortcut); } return true; case R.id.action_new_tab: @@ -755,11 +738,11 @@ public boolean onOptionsItemSelected(MenuItem item) { overridePendingTransition(R.anim.slide_up_in, R.anim.fade_out_scale); return true; case R.id.action_share: - if (mCurrentView != null && !mCurrentView.getUrl().startsWith(Constants.FILE)) { + if (currentUrl != null && !UrlUtils.isSpecialUrl(currentUrl)) { Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("text/plain"); - shareIntent.putExtra(Intent.EXTRA_SUBJECT, mCurrentView.getTitle()); - shareIntent.putExtra(Intent.EXTRA_TEXT, mCurrentView.getUrl()); + shareIntent.putExtra(Intent.EXTRA_SUBJECT, currentView.getTitle()); + shareIntent.putExtra(Intent.EXTRA_TEXT, currentUrl); startActivity(Intent.createChooser(shareIntent, getResources().getString(R.string.dialog_title_share))); } return true; @@ -767,9 +750,9 @@ public boolean onOptionsItemSelected(MenuItem item) { openBookmarks(); return true; case R.id.action_copy: - if (mCurrentView != null && !mCurrentView.getUrl().startsWith(Constants.FILE)) { + if (currentUrl != null && !UrlUtils.isSpecialUrl(currentUrl)) { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("label", mCurrentView.getUrl()); + ClipData clip = ClipData.newPlainText("label", currentUrl); clipboard.setPrimaryClip(clip); Utils.showSnackbar(this, R.string.message_link_copied); } @@ -781,24 +764,46 @@ public boolean onOptionsItemSelected(MenuItem item) { openHistory(); return true; case R.id.action_add_bookmark: - if (mCurrentView != null && !mCurrentView.getUrl().startsWith(Constants.FILE)) { - mEventBus.post(new BrowserEvents.AddBookmark(mCurrentView.getTitle(), - mCurrentView.getUrl())); + if (currentUrl != null && !UrlUtils.isSpecialUrl(currentUrl)) { + addBookmark(currentView.getTitle(), currentUrl); } return true; case R.id.action_find: findInPage(); return true; case R.id.action_reading_mode: - Intent read = new Intent(this, ReadingActivity.class); - read.putExtra(Constants.LOAD_READING_URL, mCurrentView.getUrl()); - startActivity(read); + if (currentUrl != null) { + Intent read = new Intent(this, ReadingActivity.class); + read.putExtra(Constants.LOAD_READING_URL, currentUrl); + startActivity(read); + } return true; default: return super.onOptionsItemSelected(item); } } + // By using a manager, adds a bookmark and notifies third parties about that + private void addBookmark(final String title, final String url) { + final HistoryItem item = !mBookmarkManager.isBookmark(url) + ? new HistoryItem(url, title) + : null; + if (item != null && mBookmarkManager.addBookmark(item)) { + mSuggestionsAdapter.refreshBookmarks(); + mEventBus.post(new BrowserEvents.BookmarkAdded(title, url)); + } + } + + private void deleteBookmark(final String title, final String url) { + final HistoryItem item = mBookmarkManager.isBookmark(url) + ? new HistoryItem(url, title) + : null; + if (item != null && mBookmarkManager.deleteBookmark(item)) { + mSuggestionsAdapter.refreshBookmarks(); + mEventBus.post(new BrowserEvents.CurrentPageUrl(url)); + } + } + /** * method that shows a dialog asking what string the user wishes to search * for. It highlights the text entered. @@ -823,8 +828,9 @@ public void onClick(DialogInterface dialog, int which) { } private void showSearchInterfaceBar(String text) { - if (mCurrentView != null) { - mCurrentView.find(text); + final LightningView currentView = mTabsManager.getCurrentTab(); + if (currentView != null) { + currentView.find(text); } mSearchBar.setVisibility(View.VISIBLE); @@ -841,25 +847,35 @@ private void showSearchInterfaceBar(String text) { quit.setOnClickListener(this); } - private void showCloseDialog(final int position) { + @Override + public TabsManager getTabModel() { + return mTabsManager; + } + + @Override + public void showCloseDialog(final int position) { if (position < 0) { return; } AlertDialog.Builder builder = new AlertDialog.Builder(this); ArrayAdapter adapter = new ArrayAdapter<>(this, - android.R.layout.simple_dropdown_item_1line); - adapter.add(this.getString(R.string.close_tab)); + android.R.layout.simple_list_item_1); adapter.add(this.getString(R.string.close_all_tabs)); + adapter.add(this.getString(R.string.close_other_tabs)); + adapter.add(this.getString(R.string.close_tab)); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case 0: - deleteTab(position); + closeBrowser(); break; case 1: - closeBrowser(); + mPresenter.closeAllOtherTabs(); + break; + case 2: + deleteTab(position); break; default: break; @@ -869,145 +885,144 @@ public void onClick(DialogInterface dialog, int which) { builder.show(); } - /** - * The click listener for ListView in the navigation drawer - */ - private class DrawerItemClickListener implements OnClickListener { + @Override + public void notifyTabViewRemoved(int position) { + Log.d(TAG, "Notify Tab Removed: " + position); + mTabsView.tabRemoved(position); + } - @Override - public void onClick(View v) { - int position = mTabListView.getChildAdapterPosition(v); - if (mCurrentView != mWebViewList.get(position)) { - mIsNewIntent = false; - showTab(mWebViewList.get(position)); - } - } + @Override + public void notifyTabViewAdded() { + Log.d(TAG, "Notify Tab Added"); + mTabsView.tabAdded(); } - /** - * long click listener for Navigation Drawer - */ - private class DrawerItemLongClickListener implements OnLongClickListener { + @Override + public void notifyTabViewChanged(int position) { + Log.d(TAG, "Notify Tab Changed: " + position); + mTabsView.tabChanged(position); + } - @Override - public boolean onLongClick(View v) { - int position = mTabListView.getChildAdapterPosition(v); - showCloseDialog(position); - return true; - } + @Override + public void tabChanged(LightningView tab) { + mPresenter.tabChangeOccurred(tab); } - /** - * displays the WebView contained in the LightningView Also handles the - * removal of previous views - * - * @param view the LightningView to show - */ - private synchronized void showTab(LightningView view) { + @Override + public void removeTabView() { + + Log.d(TAG, "Remove the tab view"); + // Set the background color so the color mode color doesn't show through mBrowserFrame.setBackgroundColor(mBackgroundColor); - if (view == null || (view == mCurrentView && !mCurrentView.isShown())) { - return; - } - final float translation = mToolbarLayout.getTranslationY(); + mBrowserFrame.removeAllViews(); - if (mCurrentView != null) { - mCurrentView.setForegroundTab(false); - mCurrentView.onPause(); - } - mCurrentView = view; - mWebView = view.getWebView(); - mCurrentView.setForegroundTab(true); - if (mWebView != null) { - updateUrl(mCurrentView.getUrl(), true); - updateProgress(mCurrentView.getProgress()); - } else { - updateUrl("", true); - updateProgress(0); - } - mBrowserFrame.addView(mWebView, MATCH_PARENT); - mCurrentView.requestFocus(); - mCurrentView.onResume(); + removeViewFromParent(mCurrentView); - if (mFullScreen) { - // mToolbarLayout has already been removed - mBrowserFrame.addView(mToolbarLayout); - mToolbarLayout.bringToFront(); - Log.d(Constants.TAG, "Move view to browser frame"); - int height = mToolbarLayout.getHeight(); - if (height == 0) { - mToolbarLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - height = mToolbarLayout.getMeasuredHeight(); + mCurrentView = null; + + // Use a delayed handler to make the transition smooth + // otherwise it will get caught up with the showTab code + // and cause a janky motion + mDrawerHandler.postDelayed(new Runnable() { + @Override + public void run() { + mDrawerLayout.closeDrawers(); } - mWebView.setTranslationY(translation + height); - mToolbarLayout.setTranslationY(translation); - } else { - mWebView.setTranslationY(0); + }, 200); + + } + + @Override + public void setTabView(@NonNull final View view) { + if (mCurrentView == view) { + return; } + Log.d(TAG, "Setting the tab view"); + + // Set the background color so the color mode color doesn't show through + mBrowserFrame.setBackgroundColor(mBackgroundColor); + + mBrowserFrame.removeAllViews(); + + removeViewFromParent(view); + removeViewFromParent(mCurrentView); + + mBrowserFrame.addView(view, MATCH_PARENT); + + view.requestFocus(); + + mCurrentView = view; + showActionBar(); // Use a delayed handler to make the transition smooth // otherwise it will get caught up with the showTab code // and cause a janky motion - new Handler().postDelayed(new Runnable() { + mDrawerHandler.postDelayed(new Runnable() { @Override public void run() { mDrawerLayout.closeDrawers(); } }, 200); - // Should update the bookmark status in BookmarksFragment - mEventBus.post(new BrowserEvents.CurrentPageUrl(mCurrentView.getUrl())); - -// new Handler().postDelayed(new Runnable() { -// @Override -// public void run() { + // mDrawerHandler.postDelayed(new Runnable() { + // @Override + // public void run() { // Remove browser frame background to reduce overdraw //TODO evaluate performance -// mBrowserFrame.setBackgroundColor(Color.TRANSPARENT); -// } -// }, 300); + // mBrowserFrame.setBackgroundColor(Color.TRANSPARENT); + // } + // }, 300); + } + @Override + public void showBlockedLocalFileDialog(DialogInterface.OnClickListener listener) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setCancelable(true) + .setTitle(R.string.title_warning) + .setMessage(R.string.message_blocked_local) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.action_open, listener) + .show(); } - void handleNewIntent(Intent intent) { + @Override + public void showSnackbar(@StringRes int resource) { + Utils.showSnackbar(this, resource); + } - String url = null; - if (intent != null) { - url = intent.getDataString(); - } - int num = 0; - String source = null; - if (intent != null && intent.getExtras() != null) { - num = intent.getExtras().getInt(getPackageName() + ".Origin"); - source = intent.getExtras().getString("SOURCE"); - } - if (num == 1) { - loadUrlInCurrentView(url); - } else if (url != null) { - if (url.startsWith(Constants.FILE)) { - Utils.showSnackbar(this, R.string.message_blocked_local); - url = null; - } - newTab(url, true); - mIsNewIntent = (source == null); - } + /** + * displays the WebView contained in the LightningView Also handles the + * removal of previous views + * + * @param position the poition of the tab to display + */ + // TODO move to presenter + private synchronized void showTab(final int position) { + mPresenter.tabChanged(position); } - private void loadUrlInCurrentView(final String url) { - if (mCurrentView == null) { + private static void removeViewFromParent(@Nullable View view) { + if (view == null) { return; } + ViewGroup parent = ((ViewGroup) view.getParent()); + if (parent != null) { + parent.removeView(view); + } + } - mCurrentView.loadUrl(url); - mEventBus.post(new BrowserEvents.CurrentPageUrl(url)); + void handleNewIntent(Intent intent) { + mPresenter.onNewIntent(intent); } @Override public void closeEmptyTab() { - if (mWebView != null && mWebView.copyBackForwardList().getSize() == 0) { + final WebView currentWebView = mTabsManager.getCurrentWebView(); + if (currentWebView != null && currentWebView.copyBackForwardList().getSize() == 0) { closeCurrentTab(); } } @@ -1019,125 +1034,43 @@ private void closeCurrentTab() { @Override public void onTrimMemory(int level) { if (level > TRIM_MEMORY_MODERATE && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Log.d(Constants.TAG, "Low Memory, Free Memory"); - for (int n = 0, size = mWebViewList.size(); n < size; n++) { - mWebViewList.get(n).freeMemory(); - } + Log.d(TAG, "Low Memory, Free Memory"); + mTabsManager.freeMemory(); } } - synchronized boolean newTab(String url, boolean show) { - // Limit number of tabs for limited version of app - if (!Constants.FULL_VERSION && mWebViewList.size() >= 10) { - Utils.showSnackbar(this, R.string.max_tabs); - return false; - } - mIsNewIntent = false; - LightningView startingTab = new LightningView(this, url, mDarkTheme, isIncognito(), this); - if (mIdGenerator == 0) { - startingTab.resumeTimers(); - } - mIdGenerator++; - mWebViewList.add(startingTab); - - if (show) { - showTab(startingTab); - } - updateTabs(); - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - mTabListView.smoothScrollToPosition(mWebViewList.size() - 1); - } - }, 300); + // TODO move to presenter + private synchronized boolean newTab(String url, boolean show) { + return mPresenter.newTab(url, show); + } - return true; + @Override + public void newTabClicked() { + mPresenter.newTab(null, true); } + // TODO move this to presenter private synchronized void deleteTab(int position) { - if (position >= mWebViewList.size()) { - return; - } - - int current = mWebViewList.indexOf(mCurrentView); - if (current < 0) { - return; - } - LightningView reference = mWebViewList.get(position); - if (reference == null) { - return; - } - if (!reference.getUrl().startsWith(Constants.FILE) && !isIncognito()) { - mPreferences.setSavedUrl(reference.getUrl()); - } - boolean isShown = reference.isShown(); - if (isShown) { - mBrowserFrame.setBackgroundColor(mBackgroundColor); - } - if (current > position) { - mWebViewList.remove(position); - updateTabs(); - reference.onDestroy(); - } else if (mWebViewList.size() > position + 1) { - if (current == position) { - showTab(mWebViewList.get(position + 1)); - mWebViewList.remove(position); - updateTabs(); - } else { - mWebViewList.remove(position); - } - - reference.onDestroy(); - } else if (mWebViewList.size() > 1) { - if (current == position) { - showTab(mWebViewList.get(position - 1)); - mWebViewList.remove(position); - updateTabs(); - } else { - mWebViewList.remove(position); - } - - reference.onDestroy(); - } else { - if (mCurrentView.getUrl().startsWith(Constants.FILE) || mCurrentView.getUrl().equals(mHomepage)) { - closeActivity(); - } else { - mWebViewList.remove(position); - performExitCleanUp(); - reference.pauseTimers(); - reference.onDestroy(); - mCurrentView = null; - mWebView = null; - mTabAdapter.notifyDataSetChanged(); - finish(); - } - } - mTabAdapter.notifyDataSetChanged(); - - if (mIsNewIntent && isShown) { - mIsNewIntent = false; - closeActivity(); - } - - Log.d(Constants.TAG, "deleted tab"); + mPresenter.deleteTab(position); } - private void performExitCleanUp() { - if (mPreferences.getClearCacheExit() && mCurrentView != null && !isIncognito()) { - WebUtils.clearCache(mCurrentView.getWebView()); - Log.d(Constants.TAG, "Cache Cleared"); + void performExitCleanUp() { + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (mPreferences.getClearCacheExit() && currentTab != null && !isIncognito()) { + WebUtils.clearCache(currentTab.getWebView()); + Log.d(TAG, "Cache Cleared"); } if (mPreferences.getClearHistoryExitEnabled() && !isIncognito()) { - WebUtils.clearHistory(this); - Log.d(Constants.TAG, "History Cleared"); + WebUtils.clearHistory(this, mHistoryDatabase); + Log.d(TAG, "History Cleared"); } if (mPreferences.getClearCookiesExitEnabled() && !isIncognito()) { WebUtils.clearCookies(this); - Log.d(Constants.TAG, "Cookies Cleared"); + Log.d(TAG, "Cookies Cleared"); } if (mPreferences.getClearWebStorageExitEnabled() && !isIncognito()) { WebUtils.clearWebStorage(); - Log.d(Constants.TAG, "WebStorage Cleared"); + Log.d(TAG, "WebStorage Cleared"); } else if (isIncognito()) { WebUtils.clearWebStorage(); // We want to make sure incognito mode is secure } @@ -1145,50 +1078,90 @@ private void performExitCleanUp() { @Override public boolean onKeyLongPress(int keyCode, KeyEvent event) { + final LightningView currentTab = mTabsManager.getCurrentTab(); if (keyCode == KeyEvent.KEYCODE_BACK) { - showCloseDialog(mWebViewList.indexOf(mCurrentView)); + showCloseDialog(mTabsManager.positionOf(currentTab)); } return true; } - private void closeBrowser() { + @Override + public void onConfigurationChanged(final Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + Log.d(TAG, "onConfigurationChanged"); + + if (mFullScreen) { + showActionBar(); + mBrowserFrame.setTranslationY(0); + mToolbarLayout.setTranslationY(0); + } + + initializeTabHeight(); + supportInvalidateOptionsMenu(); + initializeToolbarHeight(newConfig); + } + + private void initializeToolbarHeight(@NonNull final Configuration configuration) { + // TODO externalize the dimensions + doOnLayout(mUiLayout, new Runnable() { + @Override + public void run() { + int toolbarSize; + if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) { + // In portrait toolbar should be 56 dp tall + toolbarSize = Utils.dpToPx(56); + } else { + // In landscape toolbar should be 48 dp tall + toolbarSize = Utils.dpToPx(52); + } + mToolbar.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, toolbarSize)); + mToolbar.setMinimumHeight(toolbarSize); + mToolbar.requestLayout(); + } + }); + } + + public void closeBrowser() { mBrowserFrame.setBackgroundColor(mBackgroundColor); performExitCleanUp(); + mBrowserFrame.removeAllViews(); + int size = mTabsManager.size(); + mTabsManager.shutdown(); mCurrentView = null; - mWebView = null; - for (int n = 0, size = mWebViewList.size(); n < size; n++) { - if (mWebViewList.get(n) != null) { - mWebViewList.get(n).onDestroy(); - } + for (int n = 0; n < size; n++) { + mTabsView.tabRemoved(0); } - mWebViewList.clear(); - mTabAdapter.notifyDataSetChanged(); finish(); } @Override - public void onBackPressed() { + public synchronized void onBackPressed() { + final LightningView currentTab = mTabsManager.getCurrentTab(); if (mDrawerLayout.isDrawerOpen(mDrawerLeft)) { mDrawerLayout.closeDrawer(mDrawerLeft); } else if (mDrawerLayout.isDrawerOpen(mDrawerRight)) { - mEventBus - .post(new BrowserEvents.UserPressedBack()); + mEventBus.post(new BrowserEvents.UserPressedBack()); } else { - if (mCurrentView != null) { - Log.d(Constants.TAG, "onBackPressed"); + if (currentTab != null) { + Log.d(TAG, "onBackPressed"); if (mSearch.hasFocus()) { - mCurrentView.requestFocus(); - } else if (mCurrentView.canGoBack()) { - if (!mCurrentView.isShown()) { + currentTab.requestFocus(); + } else if (currentTab.canGoBack()) { + if (!currentTab.isShown()) { onHideCustomView(); } else { - mCurrentView.goBack(); + currentTab.goBack(); } } else { - deleteTab(mWebViewList.indexOf(mCurrentView)); + if (mCustomView != null || mCustomViewCallback != null) { + onHideCustomView(); + } else { + deleteTab(mTabsManager.positionOf(currentTab)); + } } } else { - Log.e(Constants.TAG, "This shouldn't happen ever"); + Log.e(TAG, "This shouldn't happen ever"); super.onBackPressed(); } } @@ -1197,15 +1170,12 @@ public void onBackPressed() { @Override protected void onPause() { super.onPause(); - Log.d(Constants.TAG, "onPause"); - if (mCurrentView != null) { - mCurrentView.pauseTimers(); - mCurrentView.onPause(); - } + Log.d(TAG, "onPause"); + mTabsManager.pauseAll(); try { - unregisterReceiver(mNetworkReceiver); + BrowserApp.get(this).unregisterReceiver(mNetworkReceiver); } catch (IllegalArgumentException e) { - e.printStackTrace(); + Log.e(TAG, "Receiver was not registered", e); } if (isIncognito() && isFinishing()) { overridePendingTransition(R.anim.fade_in_scale, R.anim.slide_down_out); @@ -1216,13 +1186,7 @@ protected void onPause() { void saveOpenTabs() { if (mPreferences.getRestoreLostTabsEnabled()) { - StringBuilder s = new StringBuilder(mWebViewList.size() * 50); - for (int n = 0, size = mWebViewList.size(); n < size; n++) { - if (!mWebViewList.get(n).getUrl().isEmpty()) { - s.append(mWebViewList.get(n).getUrl()).append("|$|SEPARATOR|$|"); - } - } - mPreferences.setMemoryUrl(s.toString()); + mTabsManager.saveState(); } } @@ -1234,11 +1198,15 @@ protected void onStop() { @Override protected void onDestroy() { - Log.d(Constants.TAG, "onDestroy"); + Log.d(TAG, "onDestroy"); + + mPresenter.shutdown(); + if (mHistoryDatabase != null) { mHistoryDatabase.close(); mHistoryDatabase = null; } + super.onDestroy(); } @@ -1248,33 +1216,29 @@ protected void onStart() { mProxyUtils.onStart(this); } + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + mTabsManager.shutdown(); + } + @Override protected void onResume() { super.onResume(); - Log.d(Constants.TAG, "onResume"); - if (mSearchAdapter != null) { - mSearchAdapter.refreshPreferences(); - mSearchAdapter.refreshBookmarks(); + Log.d(TAG, "onResume"); + if (mSuggestionsAdapter != null) { + mSuggestionsAdapter.refreshPreferences(); + mSuggestionsAdapter.refreshBookmarks(); } - if (mCurrentView != null) { - mCurrentView.resumeTimers(); - mCurrentView.onResume(); - } - mHistoryDatabase = HistoryDatabase.getInstance(); + mTabsManager.resumeAll(); initializePreferences(); - for (int n = 0, size = mWebViewList.size(); n < size; n++) { - if (mWebViewList.get(n) != null) { - mWebViewList.get(n).initializePreferences(null, this); - } else { - mWebViewList.remove(n); - } - } + mTabsManager.resume(this); supportInvalidateOptionsMenu(); IntentFilter filter = new IntentFilter(); filter.addAction(NETWORK_BROADCAST_ACTION); - registerReceiver(mNetworkReceiver, filter); + BrowserApp.get(this).registerReceiver(mNetworkReceiver, filter); mEventBus.register(mBusEventListener); } @@ -1284,173 +1248,18 @@ protected void onResume() { * checks if it is a search, url, etc. */ private void searchTheWeb(@NonNull String query) { + final LightningView currentTab = mTabsManager.getCurrentTab(); if (query.isEmpty()) { return; } String searchUrl = mSearchText + UrlUtils.QUERY_PLACE_HOLDER; query = query.trim(); - mCurrentView.stopLoading(); - if (mCurrentView != null) { - loadUrlInCurrentView(UrlUtils.smartUrlFilter(query, true, searchUrl)); - } - } - - private class LightningViewAdapter extends RecyclerView.Adapter { - - private final Context context; - private final int layoutResourceId; - private List data = null; - private final CloseTabListener mExitListener; - private final Drawable mBackgroundTabDrawable; - private final Drawable mForegroundTabDrawable; - private final Bitmap mForegroundTabBitmap; - private final DrawerItemClickListener mClickListener; - private final DrawerItemLongClickListener mLongClickListener; - private ColorMatrix mColorMatrix; - private Paint mPaint; - private ColorFilter mFilter; - private static final float DESATURATED = 0.5f; - - public LightningViewAdapter(Context context, int layoutResourceId, List data) { - this.layoutResourceId = layoutResourceId; - this.context = context; - this.data = data; - this.mExitListener = new CloseTabListener(); - this.mClickListener = new DrawerItemClickListener(); - this.mLongClickListener = new DrawerItemLongClickListener(); - - if (mShowTabsInDrawer) { - mBackgroundTabDrawable = null; - mForegroundTabBitmap = null; - mForegroundTabDrawable = ThemeUtils.getSelectedBackground(context, mDarkTheme); - } else { - int backgroundColor = Utils.mixTwoColors(ThemeUtils.getPrimaryColor(context), Color.BLACK, 0.75f); - Bitmap backgroundTabBitmap = Bitmap.createBitmap(Utils.dpToPx(175), Utils.dpToPx(30), Bitmap.Config.ARGB_8888); - Utils.drawTrapezoid(new Canvas(backgroundTabBitmap), backgroundColor, true); - mBackgroundTabDrawable = new BitmapDrawable(getResources(), backgroundTabBitmap); - - int foregroundColor = ThemeUtils.getPrimaryColor(context); - mForegroundTabBitmap = Bitmap.createBitmap(Utils.dpToPx(175), Utils.dpToPx(30), Bitmap.Config.ARGB_8888); - Utils.drawTrapezoid(new Canvas(mForegroundTabBitmap), foregroundColor, false); - mForegroundTabDrawable = null; - } - } - - @Override - public LightningViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { - LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); - View view = inflater.inflate(layoutResourceId, viewGroup, false); - return new LightningViewHolder(view); - } - - @Override - public void onBindViewHolder(final LightningViewHolder holder, int position) { - holder.exitButton.setTag(position); - holder.exitButton.setOnClickListener(mExitListener); - holder.layout.setOnClickListener(mClickListener); - holder.layout.setOnLongClickListener(mLongClickListener); - - ViewCompat.jumpDrawablesToCurrentState(holder.exitButton); - - LightningView web = data.get(position); - holder.txtTitle.setText(web.getTitle()); - - final Bitmap favicon = web.getFavicon(); - if (web.isForegroundTab()) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - holder.txtTitle.setTextAppearance(R.style.boldText); - } else { - //noinspection deprecation - holder.txtTitle.setTextAppearance(context, R.style.boldText); - } - Drawable foregroundDrawable; - if (!mShowTabsInDrawer) { - foregroundDrawable = new BitmapDrawable(getResources(), mForegroundTabBitmap); - if (!isIncognito() && mColorMode) { - foregroundDrawable.setColorFilter(mCurrentUiColor, PorterDuff.Mode.SRC_IN); - } - } else { - foregroundDrawable = mForegroundTabDrawable; - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - holder.layout.setBackground(foregroundDrawable); - } else { - //noinspection deprecation - holder.layout.setBackgroundDrawable(foregroundDrawable); - } - if (!isIncognito() && mColorMode) { - changeToolbarBackground(favicon, foregroundDrawable); - } - holder.favicon.setImageBitmap(favicon); - } else { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - holder.txtTitle.setTextAppearance(R.style.normalText); - } else { - //noinspection deprecation - holder.txtTitle.setTextAppearance(context, R.style.normalText); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - holder.layout.setBackground(mBackgroundTabDrawable); - } else { - //noinspection deprecation - holder.layout.setBackgroundDrawable(mBackgroundTabDrawable); - } - - holder.favicon.setImageBitmap(getDesaturatedBitmap(favicon)); - } - } - - @Override - public int getItemCount() { - return (data != null) ? data.size() : 0; - } - - public Bitmap getDesaturatedBitmap(Bitmap favicon) { - Bitmap grayscaleBitmap = Bitmap.createBitmap(favicon.getWidth(), - favicon.getHeight(), Bitmap.Config.ARGB_8888); - - Canvas c = new Canvas(grayscaleBitmap); - if (mColorMatrix == null || mFilter == null || mPaint == null) { - mPaint = new Paint(); - mColorMatrix = new ColorMatrix(); - mColorMatrix.setSaturation(DESATURATED); - mFilter = new ColorMatrixColorFilter(mColorMatrix); - mPaint.setColorFilter(mFilter); - } - - c.drawBitmap(favicon, 0, 0, mPaint); - return grayscaleBitmap; - } - - public class LightningViewHolder extends RecyclerView.ViewHolder { - - public LightningViewHolder(View view) { - super(view); - txtTitle = (TextView) view.findViewById(R.id.textTab); - favicon = (ImageView) view.findViewById(R.id.faviconTab); - exit = (ImageView) view.findViewById(R.id.deleteButton); - layout = (LinearLayout) view.findViewById(R.id.tab_item_background); - exitButton = (FrameLayout) view.findViewById(R.id.deleteAction); - exit.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); - } - - final TextView txtTitle; - final ImageView favicon; - final ImageView exit; - final FrameLayout exitButton; - final LinearLayout layout; + if (currentTab != null) { + currentTab.stopLoading(); + mPresenter.loadUrlInCurrentView(UrlUtils.smartUrlFilter(query, true, searchUrl)); } } - private class CloseTabListener implements OnClickListener { - - @Override - public void onClick(View v) { - deleteTab((int) v.getTag()); - } - - } - /** * Animates the color of the toolbar from one color to another. Optionally animates * the color of the tab background, for use when the tabs are displayed on the top @@ -1459,7 +1268,8 @@ public void onClick(View v) { * @param favicon the Bitmap to extract the color from * @param tabBackground the optional LinearLayout to color */ - private void changeToolbarBackground(@NonNull Bitmap favicon, @Nullable final Drawable tabBackground) { + @Override + public void changeToolbarBackground(@NonNull Bitmap favicon, @Nullable final Drawable tabBackground) { final int defaultColor = ContextCompat.getColor(this, R.color.primary_color); if (mCurrentUiColor == Color.BLACK) { mCurrentUiColor = defaultColor; @@ -1471,7 +1281,7 @@ public void onGenerated(Palette palette) { // OR with opaque black to remove transparency glitches int color = 0xff000000 | palette.getVibrantColor(defaultColor); - int finalColor; // Lighten up the dark color if it is + final int finalColor; // Lighten up the dark color if it is // too dark if (!mShowTabsInDrawer || Utils.isColorTooDark(color)) { finalColor = Utils.mixTwoColors(defaultColor, color, 0.25f); @@ -1479,17 +1289,19 @@ public void onGenerated(Palette palette) { finalColor = color; } - ValueAnimator anim = ValueAnimator.ofInt(mCurrentUiColor, finalColor); - anim.setEvaluator(new ArgbEvaluator()); + final Window window = getWindow(); if (!mShowTabsInDrawer) { window.setBackgroundDrawable(new ColorDrawable(Color.BLACK)); } - anim.addUpdateListener(new AnimatorUpdateListener() { + final int startSearchColor = getSearchBarColor(mCurrentUiColor, defaultColor); + final int finalSearchColor = getSearchBarColor(finalColor, defaultColor); + + Animation animation = new Animation() { @Override - public void onAnimationUpdate(ValueAnimator animation) { - final int color = (Integer) animation.getAnimatedValue(); + protected void applyTransformation(float interpolatedTime, Transformation t) { + final int color = DrawableUtils.mixColor(interpolatedTime, mCurrentUiColor, finalColor); if (mShowTabsInDrawer) { mBackground.setColor(color); window.setBackgroundDrawable(mBackground); @@ -1498,22 +1310,43 @@ public void onAnimationUpdate(ValueAnimator animation) { } mCurrentUiColor = color; mToolbarLayout.setBackgroundColor(color); + mSearchBackground.getBackground().setColorFilter(DrawableUtils.mixColor(interpolatedTime, + startSearchColor, finalSearchColor), PorterDuff.Mode.SRC_IN); } - - }); - anim.setDuration(300); - anim.start(); + }; + animation.setDuration(300); + mToolbarLayout.startAnimation(animation); } }); } + private int getSearchBarColor(int requestedColor, int defaultColor) { + if (requestedColor == defaultColor) { + return mDarkTheme ? DrawableUtils.mixColor(0.25f, defaultColor, Color.WHITE) : Color.WHITE; + } else { + return DrawableUtils.mixColor(0.25f, requestedColor, Color.WHITE); + } + } + + @Override + public boolean getUseDarkTheme() { + return mDarkTheme; + } + + @ColorInt @Override - public void updateUrl(String url, boolean shortUrl) { + public int getUiColor() { + return mCurrentUiColor; + } + + @Override + public void updateUrl(@Nullable String url, boolean shortUrl) { if (url == null || mSearch == null || mSearch.hasFocus()) { return; } + final LightningView currentTab = mTabsManager.getCurrentTab(); mEventBus.post(new BrowserEvents.CurrentPageUrl(url)); - if (shortUrl && !url.startsWith(Constants.FILE)) { + if (shortUrl && !UrlUtils.isSpecialUrl(url)) { switch (mPreferences.getUrlBoxContentChoice()) { case 0: // Default, show only the domain url = url.replaceFirst(Constants.HTTP, ""); @@ -1524,48 +1357,53 @@ public void updateUrl(String url, boolean shortUrl) { mSearch.setText(url); break; case 2: // Title, show the page's title - if (mCurrentView != null && !mCurrentView.getTitle().isEmpty()) { - mSearch.setText(mCurrentView.getTitle()); + if (currentTab != null && !currentTab.getTitle().isEmpty()) { + mSearch.setText(currentTab.getTitle()); } else { mSearch.setText(mUntitledTitle); } break; } } else { - if (url.startsWith(Constants.FILE)) { + if (UrlUtils.isSpecialUrl(url)) { url = ""; } mSearch.setText(url); } } + @Override + public void updateTabNumber(int number) { + if (mArrowImage != null && mShowTabsInDrawer) { + mArrowImage.setImageBitmap(DrawableUtils.getRoundedNumberImage(number, Utils.dpToPx(24), + Utils.dpToPx(24), ThemeUtils.getIconThemeColor(this, mDarkTheme), Utils.dpToPx(2.5f))); + } + } + @Override public void updateProgress(int n) { setIsLoading(n < 100); mProgressBar.setProgress(n); } - void addItemToHistory(final String title, final String url) { - Runnable update = new Runnable() { + void addItemToHistory(@Nullable final String title, @NonNull final String url) { + if (UrlUtils.isSpecialUrl(url)) { + return; + } + BrowserApp.getIOThread().execute(new Runnable() { @Override public void run() { try { - if (mHistoryDatabase == null) { - mHistoryDatabase = HistoryDatabase.getInstance(); - } mHistoryDatabase.visitHistoryItem(url, title); } catch (IllegalStateException e) { - Log.e(Constants.TAG, "IllegalStateException in updateHistory", e); + Log.e(TAG, "IllegalStateException in updateHistory", e); } catch (NullPointerException e) { - Log.e(Constants.TAG, "NullPointerException in updateHistory", e); + Log.e(TAG, "NullPointerException in updateHistory", e); } catch (SQLiteException e) { - Log.e(Constants.TAG, "SQLiteException in updateHistory", e); + Log.e(TAG, "SQLiteException in updateHistory", e); } } - }; - if (url != null && !url.startsWith(Constants.FILE)) { - new Thread(update).start(); - } + }); } /** @@ -1574,59 +1412,50 @@ public void run() { */ private void initializeSearchSuggestions(final AutoCompleteTextView getUrl) { + mSuggestionsAdapter = new SuggestionsAdapter(this, mDarkTheme, isIncognito()); + getUrl.setThreshold(1); getUrl.setDropDownWidth(-1); getUrl.setDropDownAnchor(R.id.toolbar_layout); getUrl.setOnItemClickListener(new OnItemClickListener() { @Override - public void onItemClick(AdapterView arg0, View arg1, int arg2, long arg3) { - try { - String url; - url = ((TextView) arg1.findViewById(R.id.url)).getText().toString(); - if (url.startsWith(BrowserActivity.this.getString(R.string.suggestion))) { - url = ((TextView) arg1.findViewById(R.id.title)).getText().toString(); - } else { - getUrl.setText(url); - } - searchTheWeb(url); - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(getUrl.getWindowToken(), 0); - if (mCurrentView != null) { - mCurrentView.requestFocus(); + public void onItemClick(AdapterView adapterView, View view, int pos, long l) { + String url = null; + CharSequence urlString = ((TextView) view.findViewById(R.id.url)).getText(); + if (urlString != null) { + url = urlString.toString(); + } + if (url == null || url.startsWith(BrowserActivity.this.getString(R.string.suggestion))) { + CharSequence searchString = ((TextView) view.findViewById(R.id.title)).getText(); + if (searchString != null) { + url = searchString.toString(); } - } catch (NullPointerException e) { - Log.e("Browser Error: ", "NullPointerException on item click"); + } + if (url == null) { + return; + } + getUrl.setText(url); + searchTheWeb(url); + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(getUrl.getWindowToken(), 0); + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (currentTab != null) { + currentTab.requestFocus(); } } }); getUrl.setSelectAllOnFocus(true); - mSearchAdapter = new SearchAdapter(this, mDarkTheme, isIncognito()); - getUrl.setAdapter(mSearchAdapter); - } - - @Override - public boolean proxyIsNotReady() { - return !mProxyUtils.isProxyReady(this); + getUrl.setAdapter(mSuggestionsAdapter); } /** * function that opens the HTML history page in the browser */ private void openHistory() { - // use a thread so that history retrieval doesn't block the UI - Thread history = new Thread(new Runnable() { - - @Override - public void run() { - loadUrlInCurrentView(HistoryPage.getHistoryPage(BrowserActivity.this)); - mSearch.setText(""); - } - - }); - history.run(); + new HistoryPage(mTabsManager.getCurrentTab(), getApplication(), mHistoryDatabase).load(); } /** @@ -1639,49 +1468,82 @@ private void openBookmarks() { mDrawerLayout.openDrawer(mDrawerRight); } - void closeDrawers() { + /** + * This method closes any open drawer and executes + * the runnable after the drawers are completely closed. + * + * @param runnable an optional runnable to run after + * the drawers are closed. + */ + void closeDrawers(@Nullable final Runnable runnable) { + if (!mDrawerLayout.isDrawerOpen(mDrawerLeft) && !mDrawerLayout.isDrawerOpen(mDrawerRight)) { + if (runnable != null) { + runnable.run(); + return; + } + } mDrawerLayout.closeDrawers(); + + mDrawerLayout.addDrawerListener(new DrawerListener() { + @Override + public void onDrawerSlide(View drawerView, float slideOffset) {} + + @Override + public void onDrawerOpened(View drawerView) {} + + @Override + public void onDrawerClosed(View drawerView) { + if (runnable != null) { + runnable.run(); + } + mDrawerLayout.removeDrawerListener(this); + } + + @Override + public void onDrawerStateChanged(int newState) {} + }); } @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuItem back = menu.findItem(R.id.action_back); - MenuItem forward = menu.findItem(R.id.action_forward); - if (back != null && back.getIcon() != null) - back.getIcon().setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); - if (forward != null && forward.getIcon() != null) - forward.getIcon().setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); - return super.onCreateOptionsMenu(menu); + public void setForwardButtonEnabled(boolean enabled) { + if (mForwardMenuItem != null && mForwardMenuItem.getIcon() != null) { + int colorFilter; + if (enabled) { + colorFilter = mIconColor; + } else { + colorFilter = mDisabledIconColor; + } + mForwardMenuItem.getIcon().setColorFilter(colorFilter, PorterDuff.Mode.SRC_IN); + mForwardMenuItem.setIcon(mForwardMenuItem.getIcon()); + } } - /** - * open the HTML bookmarks page, parameter view is the WebView that should show the page - */ @Override - public void openBookmarkPage(WebView view) { - if (view == null) - return; - Bitmap folderIcon = ThemeUtils.getThemedBitmap(this, R.drawable.ic_folder, false); - FileOutputStream outputStream = null; - File image = new File(this.getCacheDir(), "folder.png"); - try { - outputStream = new FileOutputStream(image); - folderIcon.compress(Bitmap.CompressFormat.PNG, 100, outputStream); - folderIcon.recycle(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } finally { - Utils.close(outputStream); + public void setBackButtonEnabled(boolean enabled) { + if (mBackMenuItem != null && mBackMenuItem.getIcon() != null) { + int colorFilter; + if (enabled) { + colorFilter = mIconColor; + } else { + colorFilter = mDisabledIconColor; + } + mBackMenuItem.getIcon().setColorFilter(colorFilter, PorterDuff.Mode.SRC_IN); + mBackMenuItem.setIcon(mBackMenuItem.getIcon()); } - File bookmarkWebPage = new File(this.getFilesDir(), Constants.BOOKMARKS_FILENAME); - - mBookmarkPage.buildBookmarkPage(null, mBookmarkManager.getBookmarksFromFolder(null, true)); - view.loadUrl(Constants.FILE + bookmarkWebPage); } + private MenuItem mBackMenuItem; + private MenuItem mForwardMenuItem; + @Override - public void updateTabs() { - mTabAdapter.notifyDataSetChanged(); + public boolean onCreateOptionsMenu(Menu menu) { + mBackMenuItem = menu.findItem(R.id.action_back); + mForwardMenuItem = menu.findItem(R.id.action_forward); + if (mBackMenuItem != null && mBackMenuItem.getIcon() != null) + mBackMenuItem.getIcon().setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); + if (mForwardMenuItem != null && mForwardMenuItem.getIcon() != null) + mForwardMenuItem.getIcon().setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); + return super.onCreateOptionsMenu(menu); } /** @@ -1757,7 +1619,7 @@ public void showFileChooser(ValueCallback filePathCallback) { takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath); } catch (IOException ex) { // Error occurred while creating the File - Log.e(Constants.TAG, "Unable to create Image File", ex); + Log.e(TAG, "Unable to create Image File", ex); } // Continue only if the File was successfully created @@ -1785,92 +1647,111 @@ public void showFileChooser(ValueCallback filePathCallback) { chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser"); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); - this.startActivityForResult(chooserIntent, 1); + startActivityForResult(chooserIntent, 1); } - /** - * handles long presses for the browser, tries to get the - * url of the item that was clicked and sends it (it can be null) - * to the click handler that does cool stuff with it - */ @Override - public void onLongPress() { - if (mClickHandler == null) { - mClickHandler = new ClickHandler(this); - } - Message click = mClickHandler.obtainMessage(); - if (click != null) { - click.setTarget(mClickHandler); - mWebView.requestFocusNodeHref(click); - } + public synchronized void onShowCustomView(View view, CustomViewCallback callback) { + int requestedOrientation = mOriginalOrientation = getRequestedOrientation(); + onShowCustomView(view, callback, requestedOrientation); } @Override - public void onShowCustomView(View view, CustomViewCallback callback) { - if (view == null) { - return; - } - if (mCustomView != null && callback != null) { - callback.onCustomViewHidden(); + public synchronized void onShowCustomView(final View view, CustomViewCallback callback, int requestedOrientation) { + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (view == null || mCustomView != null) { + if (callback != null) { + try { + callback.onCustomViewHidden(); + } catch (Exception e) { + Log.e(TAG, "Error hiding custom view", e); + } + } return; } try { view.setKeepScreenOn(true); } catch (SecurityException e) { - Log.e(Constants.TAG, "WebView is not allowed to keep the screen on"); + Log.e(TAG, "WebView is not allowed to keep the screen on"); } mOriginalOrientation = getRequestedOrientation(); - FrameLayout decor = (FrameLayout) getWindow().getDecorView(); + mCustomViewCallback = callback; + mCustomView = view; + + setRequestedOrientation(requestedOrientation); + final FrameLayout decorView = (FrameLayout) getWindow().getDecorView(); + mFullscreenContainer = new FrameLayout(this); mFullscreenContainer.setBackgroundColor(ContextCompat.getColor(this, android.R.color.black)); - mCustomView = view; - mFullscreenContainer.addView(mCustomView, COVER_SCREEN_PARAMS); - decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS); - setFullscreen(true, true); - mCurrentView.setVisibility(View.GONE); if (view instanceof FrameLayout) { if (((FrameLayout) view).getFocusedChild() instanceof VideoView) { mVideoView = (VideoView) ((FrameLayout) view).getFocusedChild(); mVideoView.setOnErrorListener(new VideoCompletionListener()); mVideoView.setOnCompletionListener(new VideoCompletionListener()); } + } else if (view instanceof VideoView) { + mVideoView = (VideoView) view; + mVideoView.setOnErrorListener(new VideoCompletionListener()); + mVideoView.setOnCompletionListener(new VideoCompletionListener()); + } + decorView.addView(mFullscreenContainer, COVER_SCREEN_PARAMS); + mFullscreenContainer.addView(mCustomView, COVER_SCREEN_PARAMS); + decorView.requestLayout(); + setFullscreen(true, true); + if (currentTab != null) { + currentTab.setVisibility(View.INVISIBLE); } - mCustomViewCallback = callback; } @Override public void onHideCustomView() { - if (mCustomView == null || mCustomViewCallback == null || mCurrentView == null) { + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (mCustomView == null || mCustomViewCallback == null || currentTab == null) { + if (mCustomViewCallback != null) { + try { + mCustomViewCallback.onCustomViewHidden(); + } catch (Exception e) { + Log.e(TAG, "Error hiding custom view", e); + } + mCustomViewCallback = null; + } return; } - Log.d(Constants.TAG, "onHideCustomView"); - mCurrentView.setVisibility(View.VISIBLE); + Log.d(TAG, "onHideCustomView"); + currentTab.setVisibility(View.VISIBLE); try { mCustomView.setKeepScreenOn(false); } catch (SecurityException e) { - Log.e(Constants.TAG, "WebView is not allowed to keep the screen on"); + Log.e(TAG, "WebView is not allowed to keep the screen on"); } setFullscreen(mPreferences.getHideStatusBarEnabled(), false); - FrameLayout decor = (FrameLayout) getWindow().getDecorView(); - if (decor != null) { - decor.removeView(mFullscreenContainer); - } - - if (API < Build.VERSION_CODES.KITKAT) { - try { - mCustomViewCallback.onCustomViewHidden(); - } catch (Throwable ignored) { - + if (mFullscreenContainer != null) { + ViewGroup parent = (ViewGroup) mFullscreenContainer.getParent(); + if (parent != null) { + parent.removeView(mFullscreenContainer); } + mFullscreenContainer.removeAllViews(); } + mFullscreenContainer = null; mCustomView = null; if (mVideoView != null) { + Log.d(TAG, "VideoView is being stopped"); + mVideoView.stopPlayback(); mVideoView.setOnErrorListener(null); mVideoView.setOnCompletionListener(null); mVideoView = null; } + if (mCustomViewCallback != null) { + try { + mCustomViewCallback.onCustomViewHidden(); + } catch (Exception e) { + Log.e(TAG, "Error hiding custom view", e); + } + } + mCustomViewCallback = null; setRequestedOrientation(mOriginalOrientation); + setTabHeight(); } private class VideoCompletionListener implements MediaPlayer.OnCompletionListener, @@ -1891,6 +1772,7 @@ public void onCompletion(MediaPlayer mp) { @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); + Log.d(TAG, "onWindowFocusChanged"); if (hasFocus) { setFullscreen(mIsFullScreen, mIsImmersive); } @@ -1913,8 +1795,6 @@ private void setFullscreen(boolean enabled, boolean immersive) { Window window = getWindow(); View decor = window.getDecorView(); if (enabled) { - window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); if (immersive) { decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION @@ -1922,39 +1802,17 @@ private void setFullscreen(boolean enabled, boolean immersive) { | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } else { + decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); } + window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); } else { window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); } } - /** - * This interface method is used by the LightningView to obtain an - * image that is displayed as a placeholder on a video until the video - * has initialized and can begin loading. - * - * @return a Bitmap that can be used as a place holder for videos. - */ - @Override - public Bitmap getDefaultVideoPoster() { - return BitmapFactory.decodeResource(getResources(), android.R.drawable.spinner_background); - } - - /** - * An interface method so that we can inflate a view to send to - * a LightningView when it needs to display a video and has to - * show a loading dialog. Inflates a progress view and returns it. - * - * @return A view that should be used to display the state - * of a video's loading progress. - */ - @Override - public View getVideoLoadingProgressView() { - LayoutInflater inflater = LayoutInflater.from(this); - return inflater.inflate(R.layout.video_loading_progress, null); - } - /** * This method handles the JavaScript callback to create a new tab. * Basically this handles the event that JavaScript needs to create @@ -1964,14 +1822,20 @@ public View getVideoLoadingProgressView() { * the newly created WebView. */ @Override - public void onCreateWindow(Message resultMsg) { + public synchronized void onCreateWindow(Message resultMsg) { if (resultMsg == null) { return; } if (newTab("", true)) { - WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; - transport.setWebView(mWebView); - resultMsg.sendToTarget(); + LightningView newTab = mTabsManager.getTabAtPosition(mTabsManager.size() - 1); + if (newTab != null) { + final WebView webView = newTab.getWebView(); + if (webView != null) { + WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; + transport.setWebView(webView); + resultMsg.sendToTarget(); + } + } } } @@ -1985,7 +1849,7 @@ public void onCreateWindow(Message resultMsg) { */ @Override public void onCloseWindow(LightningView view) { - deleteTab(mWebViewList.indexOf(view)); + deleteTab(mTabsManager.positionOf(view)); } /** @@ -1996,32 +1860,22 @@ public void onCloseWindow(LightningView view) { @Override public void hideActionBar() { if (mFullScreen) { - if (mBrowserFrame.findViewById(R.id.toolbar_layout) == null) { - mUiLayout.removeView(mToolbarLayout); - mBrowserFrame.addView(mToolbarLayout); - mToolbarLayout.bringToFront(); - Log.d(Constants.TAG, "Move view to browser frame"); - mToolbarLayout.setTranslationY(0); - mWebView.setTranslationY(mToolbarLayout.getHeight()); - } - if (mToolbarLayout == null || mCurrentView == null) + if (mToolbarLayout == null || mBrowserFrame == null) return; final int height = mToolbarLayout.getHeight(); - final WebView view = mWebView; if (mToolbarLayout.getTranslationY() > -0.01f) { Animation show = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { float trans = (1.0f - interpolatedTime) * height; mToolbarLayout.setTranslationY(trans - height); - if (view != null) - view.setTranslationY(trans); + mBrowserFrame.setTranslationY(trans - height); } }; show.setDuration(250); show.setInterpolator(new DecelerateInterpolator()); - mWebView.startAnimation(show); + mBrowserFrame.startAnimation(show); } } } @@ -2034,7 +1888,6 @@ protected void applyTransformation(float interpolatedTime, Transformation t) { @Override public void showActionBar() { if (mFullScreen) { - if (mToolbarLayout == null) return; @@ -2044,18 +1897,10 @@ public void showActionBar() { height = mToolbarLayout.getMeasuredHeight(); } - if (mBrowserFrame.findViewById(R.id.toolbar_layout) == null) { - mUiLayout.removeView(mToolbarLayout); - mBrowserFrame.addView(mToolbarLayout); - mToolbarLayout.bringToFront(); - Log.d(Constants.TAG, "Move view to browser frame"); - mToolbarLayout.setTranslationY(0); - mWebView.setTranslationY(height); - } - if (mCurrentView == null) + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (currentTab == null) return; - final WebView view = mWebView; final int totalHeight = height; if (mToolbarLayout.getTranslationY() < -(height - 0.01f)) { Animation show = new Animation() { @@ -2063,197 +1908,92 @@ public void showActionBar() { protected void applyTransformation(float interpolatedTime, Transformation t) { float trans = interpolatedTime * totalHeight; mToolbarLayout.setTranslationY(trans - totalHeight); - // null pointer here on close - if (view != null) - view.setTranslationY(trans); + mBrowserFrame.setTranslationY(trans - totalHeight); } }; show.setDuration(250); show.setInterpolator(new DecelerateInterpolator()); - mWebView.startAnimation(show); + mBrowserFrame.startAnimation(show); } } } /** - * Handle a long-press on the current web page. The WebView provides - * a URL to this method that it thinks is what the user has long-pressed. - * This may be different than what the user actually long-pressed, e.g. in - * the case of an image link you may want to provide options for both the - * image and the link. This method also handles the event that the WebView - * was not able to get a URL from the event in which case we need to drill - * down using {@link HitTestResult} and obtain the URL that is at the root - * of their press. This method is responsible with delegating the dialog - * creation to one of the several other methods in this class. - * - * @param url the URL that was retrieved from the WebView in - * the long-press event. + * This method initializes the height of the + * view that holds the current WebView. It waits + * for the root layout to be laid out before setting + * the height as it needs the root layout to be measured + * first. */ - @Override - public void longClickPage(final String url) { - HitTestResult result = null; - String currentUrl = null; - if (mWebView != null) { - result = mWebView.getHitTestResult(); - currentUrl = mWebView.getUrl(); - } - if (currentUrl != null && currentUrl.startsWith(Constants.FILE)) { - if (currentUrl.endsWith(HistoryPage.FILENAME)) { - if (url != null) { - longPressHistoryLink(url); - } else if (result != null && result.getExtra() != null) { - final String newUrl = result.getExtra(); - longPressHistoryLink(newUrl); - } - } else if (currentUrl.endsWith(Constants.BOOKMARKS_FILENAME)) { - if (url != null) { - mBookmarksDialogBuilder.showLongPressedDialogForUrl(this, url); - } else if (result != null && result.getExtra() != null) { - final String newUrl = result.getExtra(); - mBookmarksDialogBuilder.showLongPressedDialogForUrl(this, newUrl); - } - } - } else { - if (url != null) { - if (result != null) { - if (result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == HitTestResult.IMAGE_TYPE) { - longPressImage(url); - } else { - longPressLink(url); - } - } else { - longPressLink(url); - } - } else if (result != null && result.getExtra() != null) { - final String newUrl = result.getExtra(); - if (result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == HitTestResult.IMAGE_TYPE) { - longPressImage(newUrl); - } else { - longPressLink(newUrl); - } + private void initializeTabHeight() { + Log.d(TAG, "initializeTabHeight"); + doOnLayout(mUiLayout, new Runnable() { + @Override + public void run() { + setTabHeight(); } - } + }); } /** - * Handle the event that the user has long-pressed an item on the {@link HistoryPage}. - * In this case we wish to present the user with a dialog with a few options: - * open the history item in a new tab, delete the history item, or open the - * item in the current tab. + * Performs an action when the provided view is laid out. * - * @param url the URL of the history item. + * @param view the view to listen to for layouts. + * @param runnable the runnable to run when the view is + * laid out. */ - private void longPressHistoryLink(final String url) { - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + private static void doOnLayout(@NonNull final View view, @NonNull final Runnable runnable) { + view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - newTab(url, false); - mDrawerLayout.closeDrawers(); - break; - - case DialogInterface.BUTTON_NEGATIVE: - mHistoryDatabase.deleteHistoryItem(url); - openHistory(); - break; - - case DialogInterface.BUTTON_NEUTRAL: - if (mCurrentView != null) { - loadUrlInCurrentView(url); - } - break; + public void onGlobalLayout() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + view.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } else { + view.getViewTreeObserver().removeGlobalOnLayoutListener(this); } + runnable.run(); } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.action_history) - .setMessage(R.string.dialog_history_long_press) - .setCancelable(true) - .setPositiveButton(R.string.action_new_tab, dialogClickListener) - .setNegativeButton(R.string.action_delete, dialogClickListener) - .setNeutralButton(R.string.action_open, dialogClickListener) - .show(); + }); } /** - * Handle the event that the user has long-pressed on an image link - * and provide them with various options for things to do with that image. - * Options include opening the image in a new tab, loading the image in the - * current tab, or downloading the image. - * - * @param url the URL of the image that was retrieved from long-press. + * This method sets the height of the browser + * frame view that holds the current WebView. + * It requires the root layout to be properly + * laid out in order to set the correct height. */ - private void longPressImage(final String url) { - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - newTab(url, false); - break; + private void setTabHeight() { + Log.d(TAG, "setTabHeight"); + if (mRoot.getHeight() == 0) { + mRoot.measure(View.MeasureSpec.EXACTLY, View.MeasureSpec.EXACTLY); + } - case DialogInterface.BUTTON_NEGATIVE: - loadUrlInCurrentView(url); - break; + Log.d(TAG, "UI Layout top: " + mUiLayout.getTop()); - case DialogInterface.BUTTON_NEUTRAL: - Utils.downloadFile(BrowserActivity.this, url, - mCurrentView.getUserAgent(), "attachment"); - break; - } - } - }; + if (mFullScreen) { + int height = mRoot.getHeight() - mUiLayout.getTop(); + mBrowserFrame.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, height)); + } else { + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + params.weight = 1; + mBrowserFrame.setLayoutParams(params); + } - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(url.replace(Constants.HTTP, "")) - .setCancelable(true) - .setMessage(R.string.dialog_image) - .setPositiveButton(R.string.action_new_tab, dialogClickListener) - .setNegativeButton(R.string.action_open, dialogClickListener) - .setNeutralButton(R.string.action_download, dialogClickListener) - .show(); + mBrowserFrame.requestLayout(); } - /** - * Handle the event that the user has long-pressed a link on a web page. - * It creates a dialog with a few options on what the user can do with the - * URL that has been retrieved from their long-press, namely, create a - * new tab, load the URL in the current tab, or copy the link. - * - * @param url the URL that has been retrieved in the long-press event - */ - private void longPressLink(final String url) { - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + private void setTabHeightForKeyboard() { + doOnLayout(mUiLayout, new Runnable() { @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - newTab(url, false); - break; - - case DialogInterface.BUTTON_NEGATIVE: - loadUrlInCurrentView(url); - break; + public void run() { + Rect rect = new Rect(); + mRoot.getWindowVisibleDisplayFrame(rect); - case DialogInterface.BUTTON_NEUTRAL: - ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("label", url); - clipboard.setPrimaryClip(clip); - break; - } + int height = rect.bottom - mUiLayout.getTop(); + mBrowserFrame.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, height)); + mBrowserFrame.requestLayout(); } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(this); // dialog - builder.setTitle(url) - .setCancelable(true) - .setMessage(R.string.dialog_link) - .setPositiveButton(R.string.action_new_tab, dialogClickListener) - .setNegativeButton(R.string.action_open, dialogClickListener) - .setNeutralButton(R.string.action_copy, dialogClickListener) - .show(); + }); } /** @@ -2274,11 +2014,12 @@ private void setIsLoading(boolean isLoading) { * See setIsFinishedLoading and setIsLoading for displaying the correct icon */ private void refreshOrStop() { - if (mCurrentView != null) { - if (mCurrentView.getProgress() < 100) { - mCurrentView.stopLoading(); + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (currentTab != null) { + if (currentTab.getProgress() < 100) { + currentTab.stopLoading(); } else { - mCurrentView.reload(); + currentTab.reload(); } } } @@ -2292,54 +2033,39 @@ private void refreshOrStop() { */ @Override public void onClick(View v) { + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (currentTab == null) { + return; + } switch (v.getId()) { - case R.id.action_back: - if (mCurrentView != null) { - if (mCurrentView.canGoBack()) { - mCurrentView.goBack(); - } else { - deleteTab(mWebViewList.indexOf(mCurrentView)); - } - } - break; - case R.id.action_forward: - if (mCurrentView != null) { - if (mCurrentView.canGoForward()) { - mCurrentView.goForward(); - } - } - break; case R.id.arrow_button: if (mSearch != null && mSearch.hasFocus()) { - mCurrentView.requestFocus(); + currentTab.requestFocus(); } else if (mShowTabsInDrawer) { mDrawerLayout.openDrawer(mDrawerLeft); - } else if (mCurrentView != null) { - mCurrentView.loadHomepage(); + } else { + currentTab.loadHomepage(); } break; - case R.id.new_tab_button: - newTab(null, true); - break; case R.id.button_next: - mWebView.findNext(false); + currentTab.findNext(); break; case R.id.button_back: - mWebView.findNext(true); + currentTab.findPrevious(); break; case R.id.button_quit: - mWebView.clearMatches(); + currentTab.clearFindMatches(); mSearchBar.setVisibility(View.GONE); break; case R.id.action_reading: Intent read = new Intent(this, ReadingActivity.class); - read.putExtra(Constants.LOAD_READING_URL, mCurrentView.getUrl()); + read.putExtra(Constants.LOAD_READING_URL, currentTab.getUrl()); startActivity(read); break; case R.id.action_toggle_desktop: - mCurrentView.toggleDesktopUA(this); - mCurrentView.reload(); - closeDrawers(); + currentTab.toggleDesktopUA(this); + currentTab.reload(); + closeDrawers(null); break; } } @@ -2356,19 +2082,11 @@ public void onClick(View v) { */ @Override public boolean onLongClick(View view) { - switch (view.getId()) { - case R.id.new_tab_button: - String url = mPreferences.getSavedUrl(); - if (url != null) { - newTab(url, true); - Utils.showSnackbar(this, R.string.deleted_tab); - } - mPreferences.setSavedUrl(null); - break; - } return true; } + // TODO Check if all the calls are relative to TabsFragement + /** * A utility method that creates a FrameLayout button with the given ID and * sets the image of the button to the given image ID. The OnClick and OnLongClick @@ -2381,10 +2099,10 @@ public boolean onLongClick(View view) { * @param imageId the image to set as the button image */ private void setupFrameLayoutButton(@IdRes int buttonId, @IdRes int imageId) { - View frameButton = findViewById(buttonId); + final View frameButton = findViewById(buttonId); + final ImageView buttonImage = (ImageView) findViewById(imageId); frameButton.setOnClickListener(this); frameButton.setOnLongClickListener(this); - ImageView buttonImage = (ImageView) findViewById(imageId); buttonImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); } @@ -2399,12 +2117,8 @@ private void setupFrameLayoutButton(@IdRes int buttonId, @IdRes int imageId) { public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); boolean isConnected = isConnected(context); - Log.d(Constants.TAG, "Network Connected: " + String.valueOf(isConnected)); - for (int n = 0, size = mWebViewList.size(); n < size; n++) { - WebView view = mWebViewList.get(n).getWebView(); - if (view != null) - view.setNetworkAvailable(isConnected); - } + Log.d(TAG, "Network Connected: " + String.valueOf(isConnected)); + mTabsManager.notifyConnectionStatus(isConnected); } }; @@ -2419,25 +2133,25 @@ public void onReceive(Context context, Intent intent) { */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - PermissionsManager.getInstance().notifyPermissionsChange(permissions); + PermissionsManager.getInstance().notifyPermissionsChange(permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults); } private final Object mBusEventListener = new Object() { /** - * Load the given bookmark in the current tab, used by the the - * {@link acr.browser.lightning.fragment.BookmarksFragment} + * Load the given url in the current tab, used by the the + * {@link acr.browser.lightning.fragment.BookmarksFragment} and by the + * {@link LightningDialogBuilder} * * @param event Bus event indicating that the user has clicked a bookmark */ @Subscribe - public void loadBookmarkInCurrentTab(final BookmarkEvents.Clicked event) { - loadUrlInCurrentView(event.bookmark.getUrl()); + public void loadUrlInCurrentTab(final BrowserEvents.OpenUrlInCurrentTab event) { + mPresenter.loadUrlInCurrentView(event.url); // keep any jank from happening when the drawer is closed after the // URL starts to load - final Handler handler = new Handler(); - handler.postDelayed(new Runnable() { + mDrawerHandler.postDelayed(new Runnable() { @Override public void run() { mDrawerLayout.closeDrawer(mDrawerRight); @@ -2445,42 +2159,46 @@ public void run() { }, 150); } + @Subscribe + public void loadHistory(final BrowserEvents.OpenHistoryInCurrentTab event) { + new HistoryPage(mTabsManager.getCurrentTab(), getApplication(), mHistoryDatabase).load(); + } + /** - * Load the given bookmark in a new tab, used by the the - * {@link acr.browser.lightning.fragment.BookmarksFragment} + * Load the given url in a new tab, used by the the + * {@link acr.browser.lightning.fragment.BookmarksFragment} and by the + * {@link LightningDialogBuilder} * * @param event Bus event indicating that the user wishes * to open a bookmark in a new tab */ @Subscribe - public void loadBookmarkInNewTab(final BookmarkEvents.AsNewTab event) { - newTab(event.bookmark.getUrl(), true); + public void loadUrlInNewTab(final BrowserEvents.OpenUrlInNewTab event) { + BrowserActivity.this.newTab(event.url, true); mDrawerLayout.closeDrawers(); } /** - * When receive a {@link acr.browser.lightning.bus.BookmarkEvents.WantToBookmarkCurrentPage} + * When receive a {@link BookmarkEvents.ToggleBookmarkForCurrentPage} * message this receiver answer firing the - * {@link acr.browser.lightning.bus.BrowserEvents.AddBookmark} message + * {@link BrowserEvents.BookmarkAdded} message * * @param event an event that the user wishes to bookmark the current page */ @Subscribe - public void bookmarkCurrentPage(final BookmarkEvents.WantToBookmarkCurrentPage event) { - if (mCurrentView != null) { - mEventBus.post(new BrowserEvents.AddBookmark(mCurrentView.getTitle(), mCurrentView.getUrl())); + public void bookmarkCurrentPage(final BookmarkEvents.ToggleBookmarkForCurrentPage event) { + final LightningView currentTab = mTabsManager.getCurrentTab(); + final String url = currentTab != null ? currentTab.getUrl() : null; + final String title = currentTab != null ? currentTab.getTitle() : null; + if (url == null) { + return; } - } - /** - * This message is received when a bookmark was added by the - * {@link acr.browser.lightning.fragment.BookmarksFragment} - * - * @param event the event that a bookmark has been added - */ - @Subscribe - public void bookmarkAdded(final BookmarkEvents.Added event) { - mSearchAdapter.refreshBookmarks(); + if (!mBookmarkManager.isBookmark(url)) { + addBookmark(title, url); + } else { + deleteBookmark(title, url); + } } /** @@ -2490,12 +2208,13 @@ public void bookmarkAdded(final BookmarkEvents.Added event) { */ @Subscribe public void bookmarkChanged(final BookmarkEvents.BookmarkChanged event) { - if (mCurrentView != null && mCurrentView.getUrl().startsWith(Constants.FILE) - && mCurrentView.getUrl().endsWith(Constants.BOOKMARKS_FILENAME)) { - openBookmarkPage(mWebView); + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (currentTab != null && currentTab.getUrl().startsWith(Constants.FILE) + && currentTab.getUrl().endsWith(BookmarkPage.FILENAME)) { + currentTab.loadBookmarkpage(); } - if (mCurrentView != null) { - mEventBus.post(new BrowserEvents.CurrentPageUrl(mCurrentView.getUrl())); + if (currentTab != null) { + mEventBus.post(new BrowserEvents.CurrentPageUrl(currentTab.getUrl())); } } @@ -2506,12 +2225,13 @@ public void bookmarkChanged(final BookmarkEvents.BookmarkChanged event) { */ @Subscribe public void bookmarkDeleted(final BookmarkEvents.Deleted event) { - if (mCurrentView != null && mCurrentView.getUrl().startsWith(Constants.FILE) - && mCurrentView.getUrl().endsWith(Constants.BOOKMARKS_FILENAME)) { - openBookmarkPage(mWebView); + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (currentTab != null && currentTab.getUrl().startsWith(Constants.FILE) + && currentTab.getUrl().endsWith(BookmarkPage.FILENAME)) { + currentTab.loadBookmarkpage(); } - if (mCurrentView != null) { - mEventBus.post(new BrowserEvents.CurrentPageUrl(mCurrentView.getUrl())); + if (currentTab != null) { + mEventBus.post(new BrowserEvents.CurrentPageUrl(currentTab.getUrl())); } } @@ -2527,5 +2247,111 @@ public void bookmarkDeleted(final BookmarkEvents.Deleted event) { public void closeBookmarks(final BookmarkEvents.CloseBookmarks event) { mDrawerLayout.closeDrawer(mDrawerRight); } + + /** + * The user wants to close a tab + * + * @param event contains the position inside the tabs adapter + */ + @Subscribe + public void closeTab(final TabEvents.CloseTab event) { + deleteTab(event.position); + } + + /** + * The user clicked on a tab, let's show it + * + * @param event contains the tab position in the tabs adapter + */ + @Subscribe + public void showTab(final TabEvents.ShowTab event) { + BrowserActivity.this.showTab(event.position); + } + + /** + * The user long pressed on a tab, ask him if he want to close the tab + * + * @param event contains the tab position in the tabs adapter + */ + @Subscribe + public void showCloseDialog(final TabEvents.ShowCloseDialog event) { + BrowserActivity.this.showCloseDialog(event.position); + } + + /** + * The user wants to create a new tab + * + * @param event a marker + */ + @Subscribe + public void newTab(final TabEvents.NewTab event) { + BrowserActivity.this.newTab(null, true); + } + + /** + * The user wants to go back on current tab + * + * @param event a marker + */ + @Subscribe + public void goBack(final NavigationEvents.GoBack event) { + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (currentTab != null) { + if (currentTab.canGoBack()) { + currentTab.goBack(); + } else { + deleteTab(mTabsManager.positionOf(currentTab)); + } + } + } + + /** + * The user wants to go forward on current tab + * + * @param event a marker + */ + @Subscribe + public void goForward(final NavigationEvents.GoForward event) { + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (currentTab != null) { + if (currentTab.canGoForward()) { + currentTab.goForward(); + } + } + } + + @Subscribe + public void goHome(final NavigationEvents.GoHome event) { + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (currentTab != null) { + currentTab.loadHomepage(); + closeDrawers(null); + } + } + + /** + * The user long pressed the new tab button + * + * @param event a marker + */ + @Subscribe + public void newTabLongPress(final TabEvents.NewTabLongPress event) { + String url = mPreferences.getSavedUrl(); + if (url != null) { + BrowserActivity.this.newTab(url, true); + + Utils.showSnackbar(BrowserActivity.this, R.string.deleted_tab); + } + mPreferences.setSavedUrl(null); + } + + @Subscribe + public void displayInSnackbar(final BrowserEvents.ShowSnackBarMessage event) { + if (event.message != null) { + Utils.showSnackbar(BrowserActivity.this, event.message); + } else { + Utils.showSnackbar(BrowserActivity.this, event.stringRes); + } + } }; } diff --git a/app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java b/app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java index b7be9d317..fa2460bd7 100644 --- a/app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java @@ -2,28 +2,33 @@ import android.content.Intent; import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.view.Menu; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import acr.browser.lightning.R; -import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.react.Action; +import acr.browser.lightning.react.Observable; +import acr.browser.lightning.react.Subscriber; @SuppressWarnings("deprecation") public class IncognitoActivity extends BrowserActivity { @Override - public void updateCookiePreference() { - CookieManager cookieManager = CookieManager.getInstance(); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - CookieSyncManager.createInstance(this); - } - cookieManager.setAcceptCookie(PreferenceManager.getInstance().getIncognitoCookiesEnabled()); - } - - @Override - public synchronized void initializeTabs() { - newTab(null, true); + public Observable updateCookiePreference() { + return Observable.create(new Action() { + @Override + public void onSubscribe(@NonNull Subscriber subscriber) { + CookieManager cookieManager = CookieManager.getInstance(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + CookieSyncManager.createInstance(IncognitoActivity.this); + } + cookieManager.setAcceptCookie(mPreferences.getIncognitoCookiesEnabled()); + subscriber.onComplete(); + } + }); } @Override @@ -45,7 +50,7 @@ protected void onPause() { } @Override - public void updateHistory(String title, String url) { + public void updateHistory(@Nullable String title, @NonNull String url) { // addItemToHistory(title, url); } @@ -56,7 +61,11 @@ public boolean isIncognito() { @Override public void closeActivity() { - closeDrawers(); - finish(); + closeDrawers(new Runnable() { + @Override + public void run() { + closeBrowser(); + } + }); } } diff --git a/app/src/main/java/acr/browser/lightning/activity/MainActivity.java b/app/src/main/java/acr/browser/lightning/activity/MainActivity.java index 68d602213..32ca6eb69 100644 --- a/app/src/main/java/acr/browser/lightning/activity/MainActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/MainActivity.java @@ -2,29 +2,33 @@ import android.content.Intent; import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.view.Menu; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import acr.browser.lightning.R; -import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.react.Action; +import acr.browser.lightning.react.Observable; +import acr.browser.lightning.react.Subscriber; @SuppressWarnings("deprecation") public class MainActivity extends BrowserActivity { @Override - public void updateCookiePreference() { - CookieManager cookieManager = CookieManager.getInstance(); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - CookieSyncManager.createInstance(this); - } - cookieManager.setAcceptCookie(PreferenceManager.getInstance().getCookiesEnabled()); - } - - @Override - public synchronized void initializeTabs() { - restoreOrNewTab(); - // if incognito mode use newTab(null, true); instead + public Observable updateCookiePreference() { + return Observable.create(new Action() { + @Override + public void onSubscribe(@NonNull Subscriber subscriber) { + CookieManager cookieManager = CookieManager.getInstance(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + CookieSyncManager.createInstance(MainActivity.this); + } + cookieManager.setAcceptCookie(mPreferences.getCookiesEnabled()); + subscriber.onComplete(); + } + }); } @Override @@ -35,8 +39,12 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override protected void onNewIntent(Intent intent) { - handleNewIntent(intent); - super.onNewIntent(intent); + if (isPanicTrigger(intent)) { + panicClean(); + } else { + handleNewIntent(intent); + super.onNewIntent(intent); + } } @Override @@ -46,7 +54,7 @@ protected void onPause() { } @Override - public void updateHistory(String title, String url) { + public void updateHistory(@Nullable String title, @NonNull String url) { addItemToHistory(title, url); } @@ -57,8 +65,13 @@ public boolean isIncognito() { @Override public void closeActivity() { - closeDrawers(); - moveTaskToBack(true); + closeDrawers(new Runnable() { + @Override + public void run() { + performExitCleanUp(); + moveTaskToBack(true); + } + }); } diff --git a/app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java b/app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java index 8474e85b1..088ae2f7d 100644 --- a/app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java @@ -1,18 +1,19 @@ package acr.browser.lightning.activity; import android.animation.ObjectAnimator; -import android.app.Activity; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.graphics.PorterDuff; import android.graphics.drawable.ColorDrawable; -import android.os.AsyncTask; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -21,23 +22,42 @@ import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; +import javax.inject.Inject; + import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.react.Action; +import acr.browser.lightning.react.Observable; +import acr.browser.lightning.react.OnSubscribe; +import acr.browser.lightning.react.Subscriber; +import acr.browser.lightning.react.Schedulers; +import acr.browser.lightning.react.Subscription; import acr.browser.lightning.reading.HtmlFetcher; import acr.browser.lightning.reading.JResult; import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.Utils; +import butterknife.Bind; +import butterknife.ButterKnife; public class ReadingActivity extends AppCompatActivity { - private TextView mTitle; - private TextView mBody; + private static final String TAG = ReadingActivity.class.getSimpleName(); + + @Bind(R.id.textViewTitle) + TextView mTitle; + + @Bind(R.id.textViewBody) + TextView mBody; + + @Inject PreferenceManager mPreferences; + private boolean mInvert; private String mUrl = null; - private PreferenceManager mPreferences; private int mTextSize; private ProgressDialog mProgressDialog; + private Subscription mPageLoaderSubscription; private static final float XXLARGE = 30.0f; private static final float XLARGE = 26.0f; @@ -48,8 +68,9 @@ public class ReadingActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { + BrowserApp.getAppComponent().inject(this); + overridePendingTransition(R.anim.slide_in_from_right, R.anim.fade_out_scale); - mPreferences = PreferenceManager.getInstance(); mInvert = mPreferences.getInvertColors(); final int color; if (mInvert) { @@ -63,6 +84,7 @@ protected void onCreate(Bundle savedInstanceState) { } super.onCreate(savedInstanceState); setContentView(R.layout.reading_view); + ButterKnife.bind(this); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); @@ -70,9 +92,6 @@ protected void onCreate(Bundle savedInstanceState) { if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true); - mTitle = (TextView) findViewById(R.id.textViewTitle); - mBody = (TextView) findViewById(R.id.textViewBody); - mTextSize = mPreferences.getReadingTextSize(); mBody.setTextSize(getTextSize(mTextSize)); mTitle.setText(getString(R.string.untitled)); @@ -129,66 +148,87 @@ private boolean loadPage(Intent intent) { } if (getSupportActionBar() != null) getSupportActionBar().setTitle(Utils.getDomainName(mUrl)); - new PageLoader(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mUrl); + mPageLoaderSubscription = loadPage(mUrl).subscribeOn(Schedulers.worker()) + .observeOn(Schedulers.main()) + .subscribe(new OnSubscribe() { + @Override + public void onStart() { + mProgressDialog = new ProgressDialog(ReadingActivity.this); + mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + mProgressDialog.setCancelable(false); + mProgressDialog.setIndeterminate(true); + mProgressDialog.setMessage(getString(R.string.loading)); + mProgressDialog.show(); + } + + @Override + public void onNext(@Nullable ReaderInfo item) { + if (item == null || item.getTitle().isEmpty() || item.getBody().isEmpty()) { + setText(getString(R.string.untitled), getString(R.string.loading_failed)); + } else { + setText(item.getTitle(), item.getBody()); + } + } + + @Override + public void onError(@NonNull Throwable throwable) { + setText(getString(R.string.untitled), getString(R.string.loading_failed)); + if (mProgressDialog != null && mProgressDialog.isShowing()) { + mProgressDialog.dismiss(); + mProgressDialog = null; + } + } + + @Override + public void onComplete() { + if (mProgressDialog != null && mProgressDialog.isShowing()) { + mProgressDialog.dismiss(); + mProgressDialog = null; + } + } + }); return true; } - private class PageLoader extends AsyncTask { - - private final Activity mActivity; - private String mTitleText; - private String mBodyText; + private static Observable loadPage(@NonNull final String url) { + return Observable.create(new Action() { + @Override + public void onSubscribe(@NonNull Subscriber subscriber) { + HtmlFetcher fetcher = new HtmlFetcher(); + try { + JResult result = fetcher.fetchAndExtract(url, 2500, true); + subscriber.onNext(new ReaderInfo(result.getTitle(), result.getText())); + } catch (Exception e) { + subscriber.onError(new Throwable("Encountered exception")); + Log.e(TAG, "Error parsing page", e); + } catch (OutOfMemoryError e) { + System.gc(); + subscriber.onError(new Throwable("Out of memory")); + Log.e(TAG, "Out of memory", e); + } + subscriber.onComplete(); + } + }); + } - public PageLoader(Activity activity) { - mActivity = activity; - } + private static class ReaderInfo { + @NonNull private final String mTitleText; + @NonNull private final String mBodyText; - @Override - protected void onPreExecute() { - super.onPreExecute(); - mProgressDialog = new ProgressDialog(mActivity); - mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); - mProgressDialog.setCancelable(false); - mProgressDialog.setIndeterminate(true); - mProgressDialog.setMessage(mActivity.getString(R.string.loading)); - mProgressDialog.show(); + public ReaderInfo(@NonNull String title, @NonNull String body) { + mTitleText = title; + mBodyText = body; } - @Override - protected Void doInBackground(String... params) { - - HtmlFetcher fetcher = new HtmlFetcher(); - try { - JResult result = fetcher.fetchAndExtract(params[0], 2500, true); - mTitleText = result.getTitle(); - mBodyText = result.getText(); - } catch (Exception e) { - mTitleText = ""; - mBodyText = ""; - e.printStackTrace(); - } catch (OutOfMemoryError e) { - System.gc(); - mTitleText = ""; - mBodyText = ""; - e.printStackTrace(); - } - return null; + @NonNull + public String getTitle() { + return mTitleText; } - @Override - protected void onPostExecute(Void result) { - if (mProgressDialog != null && mProgressDialog.isShowing()) { - mProgressDialog.dismiss(); - mProgressDialog = null; - } - if (mTitleText.isEmpty() || mBodyText.isEmpty()) { - setText(getString(R.string.untitled), getString(R.string.loading_failed)); - } else { - setText(mTitleText, mBodyText); - } - super.onPostExecute(result); + @NonNull + public String getBody() { + return mBodyText; } - } private void setText(String title, String body) { @@ -219,6 +259,8 @@ private void setText(String title, String body) { @Override protected void onDestroy() { + mPageLoaderSubscription.unsubscribe(); + if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); mProgressDialog = null; diff --git a/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java b/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java index d97c0da03..f9c82ce6d 100644 --- a/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java @@ -3,6 +3,7 @@ */ package acr.browser.lightning.activity; +import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v7.widget.Toolbar; @@ -11,15 +12,16 @@ import android.view.ViewGroup; import android.widget.LinearLayout; +import com.anthonycr.grant.PermissionsManager; + import java.util.ArrayList; import java.util.List; import acr.browser.lightning.R; -import acr.browser.lightning.utils.PermissionsManager; public class SettingsActivity extends ThemableSettingsActivity { - private static final List fragments = new ArrayList<>(); + private static final List mFragments = new ArrayList<>(6); @Override protected void onCreate(Bundle savedInstanceState) { @@ -42,15 +44,19 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onBuildHeaders(List
target) { loadHeadersFromResource(R.xml.preferences_headers, target); - fragments.clear(); + mFragments.clear(); for (Header header : target) { - fragments.add(header.fragment); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + // Workaround for bug in the AppCompat support library + header.iconRes = R.drawable.empty; + } + mFragments.add(header.fragment); } } @Override protected boolean isValidFragment(String fragmentName) { - return fragments.contains(fragmentName); + return mFragments.contains(fragmentName); } @Override @@ -61,7 +67,7 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - PermissionsManager.getInstance().notifyPermissionsChange(permissions); + PermissionsManager.getInstance().notifyPermissionsChange(permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } diff --git a/app/src/main/java/acr/browser/lightning/activity/TabsManager.java b/app/src/main/java/acr/browser/lightning/activity/TabsManager.java new file mode 100644 index 000000000..8efab1d92 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/activity/TabsManager.java @@ -0,0 +1,542 @@ +package acr.browser.lightning.activity; + +import android.app.Activity; +import android.app.Application; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AlertDialog; +import android.util.Log; +import android.webkit.WebView; + +import com.squareup.otto.Bus; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.constant.BookmarkPage; +import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.constant.HistoryPage; +import acr.browser.lightning.constant.StartPage; +import acr.browser.lightning.database.BookmarkManager; +import acr.browser.lightning.database.HistoryDatabase; +import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.react.Action; +import acr.browser.lightning.react.Observable; +import acr.browser.lightning.react.OnSubscribe; +import acr.browser.lightning.react.Schedulers; +import acr.browser.lightning.react.Subscriber; +import acr.browser.lightning.utils.FileUtils; +import acr.browser.lightning.utils.UrlUtils; +import acr.browser.lightning.view.LightningView; + +/** + * A manager singleton that holds all the {@link LightningView} + * and tracks the current tab. It handles creation, deletion, + * restoration, state saving, and switching of tabs. + */ +public class TabsManager { + + private static final String TAG = TabsManager.class.getSimpleName(); + private static final String BUNDLE_KEY = "WEBVIEW_"; + private static final String URL_KEY = "URL_KEY"; + private static final String BUNDLE_STORAGE = "SAVED_TABS.parcel"; + + private final List mTabList = new ArrayList<>(1); + @Nullable private LightningView mCurrentTab; + @Nullable private TabNumberChangedListener mTabNumberListener; + + private boolean mIsInitialized = false; + private final List mPostInitializationWorkList = new ArrayList<>(); + + @Inject PreferenceManager mPreferenceManager; + @Inject BookmarkManager mBookmarkManager; + @Inject HistoryDatabase mHistoryManager; + @Inject Bus mEventBus; + @Inject Application mApp; + + public TabsManager() { + BrowserApp.getAppComponent().inject(this); + } + + // TODO remove and make presenter call new tab methods so it always knows + @Deprecated + public interface TabNumberChangedListener { + void tabNumberChanged(int newNumber); + } + + public void setTabNumberChangedListener(@Nullable TabNumberChangedListener listener) { + mTabNumberListener = listener; + } + + public void cancelPendingWork() { + mPostInitializationWorkList.clear(); + } + + public void doAfterInitialization(@NonNull Runnable runnable) { + if (mIsInitialized) { + runnable.run(); + } else { + mPostInitializationWorkList.add(runnable); + } + } + + private void finishInitialization() { + mIsInitialized = true; + for (Runnable runnable : mPostInitializationWorkList) { + runnable.run(); + } + } + + /** + * Restores old tabs that were open before the browser + * was closed. Handles the intent used to open the browser. + * + * @param activity the activity needed to create tabs. + * @param intent the intent that started the browser activity. + * @param incognito whether or not we are in incognito mode. + */ + public synchronized Observable initializeTabs(@NonNull final Activity activity, + @Nullable final Intent intent, + final boolean incognito) { + return Observable.create(new Action() { + @Override + public void onSubscribe(@NonNull final Subscriber subscriber) { + + // Make sure we start with a clean tab list + shutdown(); + + // If incognito, only create one tab, do not handle intent + // in order to protect user privacy + if (incognito) { + newTab(activity, null, true); + subscriber.onComplete(); + return; + } + + String url = null; + if (intent != null) { + url = intent.getDataString(); + } + Log.d(TAG, "URL from intent: " + url); + mCurrentTab = null; + if (mPreferenceManager.getRestoreLostTabsEnabled()) { + restoreLostTabs(url, activity, subscriber); + } else { + newTab(activity, null, false); + finishInitialization(); + subscriber.onComplete(); + } + + } + }); + + } + + private void restoreLostTabs(@Nullable final String url, @NonNull final Activity activity, + @NonNull final Subscriber subscriber) { + + restoreState().subscribeOn(Schedulers.io()) + .observeOn(Schedulers.main()).subscribe(new OnSubscribe() { + @Override + public void onNext(Bundle item) { + LightningView tab = newTab(activity, "", false); + String url = item.getString(URL_KEY); + if (url != null && tab.getWebView() != null) { + if (UrlUtils.isBookmarkUrl(url)) { + new BookmarkPage(tab, activity, mBookmarkManager).load(); + } else if (UrlUtils.isStartPageUrl(url)) { + new StartPage(tab, mApp).load(); + } else if (UrlUtils.isHistoryUrl(url)) { + new HistoryPage(tab, mApp, mHistoryManager).load(); + } + } else if (tab.getWebView() != null) { + tab.getWebView().restoreState(item); + } + } + + @Override + public void onComplete() { + if (url != null) { + if (url.startsWith(Constants.FILE)) { + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setCancelable(true) + .setTitle(R.string.title_warning) + .setMessage(R.string.message_blocked_local) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.action_open, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + newTab(activity, url, false); + } + }).show(); + } else { + newTab(activity, url, false); + } + } + if (mTabList.size() == 0) { + newTab(activity, null, false); + } + finishInitialization(); + subscriber.onComplete(); + } + }); + } + + /** + * Method used to resume all the tabs in the browser. + * This is necessary because we cannot pause the + * WebView when the app is open currently due to a + * bug in the WebView, where calling onResume doesn't + * consistently resume it. + */ + public void resumeAll() { + LightningView current = getCurrentTab(); + if (current != null) { + current.resumeTimers(); + } + for (LightningView tab : mTabList) { + if (tab != null) { + tab.onResume(); + } + } + } + + /** + * Method used to pause all the tabs in the browser. + * This is necessary because we cannot pause the + * WebView when the app is open currently due to a + * bug in the WebView, where calling onResume doesn't + * consistently resume it. + */ + public void pauseAll() { + LightningView current = getCurrentTab(); + if (current != null) { + current.pauseTimers(); + } + for (LightningView tab : mTabList) { + if (tab != null) { + tab.onPause(); + } + } + } + + /** + * Return the tab at the given position in tabs list, or + * null if position is not in tabs list range. + * + * @param position the index in tabs list + * @return the corespondent {@link LightningView}, + * or null if the index is invalid + */ + @Nullable + public synchronized LightningView getTabAtPosition(final int position) { + if (position < 0 || position >= mTabList.size()) { + return null; + } + + return mTabList.get(position); + } + + /** + * Frees memory for each tab in the + * manager. Note: this will only work + * on API < KITKAT as on KITKAT onward + * the WebViews manage their own + * memory correctly. + */ + public synchronized void freeMemory() { + for (LightningView tab : mTabList) { + //noinspection deprecation + tab.freeMemory(); + } + } + + /** + * Shutdown the manager. This destroys + * all tabs and clears the references + * to those tabs. Current tab is also + * released for garbage collection. + */ + public synchronized void shutdown() { + for (LightningView tab : mTabList) { + tab.onDestroy(); + } + mTabList.clear(); + mIsInitialized = false; + mCurrentTab = null; + } + + /** + * Reinitializes the preferences for + * all the tabs in the list. + * + * @param context the context needed + * to initialize the preferences. + */ + public synchronized void resume(@NonNull final Context context) { + for (LightningView tab : mTabList) { + tab.initializePreferences(context); + } + } + + /** + * Forwards network connection status to the WebViews. + * + * @param isConnected whether there is a network + * connection or not. + */ + public synchronized void notifyConnectionStatus(final boolean isConnected) { + for (LightningView tab : mTabList) { + final WebView webView = tab.getWebView(); + if (webView != null) { + webView.setNetworkAvailable(isConnected); + } + } + } + + /** + * The current number of tabs in the manager. + * + * @return the number of tabs in the list. + */ + public synchronized int size() { + return mTabList.size(); + } + + /** + * The index of the last tab in the manager. + * + * @return the last tab in the list or -1 if there are no tabs. + */ + public synchronized int last() { + return mTabList.size() - 1; + } + + + /** + * The last tab in the tab manager. + * + * @return the last tab, or null if + * there are no tabs. + */ + @Nullable + public synchronized LightningView lastTab() { + if (last() < 0) { + return null; + } + return mTabList.get(last()); + } + + /** + * Create and return a new tab. The tab is + * automatically added to the tabs list. + * + * @param activity the activity needed to create the tab. + * @param url the URL to initialize the tab with. + * @param isIncognito whether the tab is an incognito + * tab or not. + * @return a valid initialized tab. + */ + @NonNull + public synchronized LightningView newTab(@NonNull final Activity activity, + @Nullable final String url, + final boolean isIncognito) { + Log.d(TAG, "New tab"); + final LightningView tab = new LightningView(activity, url, isIncognito); + mTabList.add(tab); + if (mTabNumberListener != null) { + mTabNumberListener.tabNumberChanged(size()); + } + return tab; + } + + /** + * Removes a tab from the list and destroys the tab. + * If the tab removed is the current tab, the reference + * to the current tab will be nullified. + * + * @param position The position of the tab to remove. + */ + private synchronized void removeTab(final int position) { + if (position >= mTabList.size()) { + return; + } + final LightningView tab = mTabList.remove(position); + if (mCurrentTab == tab) { + mCurrentTab = null; + } + tab.onDestroy(); + } + + /** + * Deletes a tab from the manager. If the tab + * being deleted is the current tab, this method + * will switch the current tab to a new valid tab. + * + * @param position the position of the tab to delete. + * @return returns true if the current tab + * was deleted, false otherwise. + */ + public synchronized boolean deleteTab(int position) { + Log.d(TAG, "Delete tab: " + position); + final LightningView currentTab = getCurrentTab(); + int current = positionOf(currentTab); + + if (current == position) { + if (size() == 1) { + mCurrentTab = null; + } else if (current < size() - 1) { + // There is another tab after this one + switchToTab(current + 1); + } else { + switchToTab(current - 1); + } + } + + removeTab(position); + if (mTabNumberListener != null) { + mTabNumberListener.tabNumberChanged(size()); + } + return current == position; + } + + /** + * Return the position of the given tab. + * + * @param tab the tab to look for. + * @return the position of the tab or -1 + * if the tab is not in the list. + */ + public synchronized int positionOf(final LightningView tab) { + return mTabList.indexOf(tab); + } + + /** + * Saves the state of the current WebViews, + * to a bundle which is then stored in persistent + * storage and can be unparceled. + */ + public void saveState() { + Bundle outState = new Bundle(ClassLoader.getSystemClassLoader()); + Log.d(Constants.TAG, "Saving tab state"); + for (int n = 0; n < mTabList.size(); n++) { + LightningView tab = mTabList.get(n); + Bundle state = new Bundle(ClassLoader.getSystemClassLoader()); + if (tab.getWebView() != null && !UrlUtils.isSpecialUrl(tab.getUrl())) { + tab.getWebView().saveState(state); + outState.putBundle(BUNDLE_KEY + n, state); + } else if (tab.getWebView() != null) { + state.putString(URL_KEY, tab.getUrl()); + outState.putBundle(BUNDLE_KEY + n, state); + } + } + FileUtils.writeBundleToStorage(mApp, outState, BUNDLE_STORAGE); + } + + /** + * Use this method to clear the saved + * state if you do not wish it to be + * restored when the browser next starts. + */ + public void clearSavedState() { + FileUtils.deleteBundleInStorage(mApp, BUNDLE_STORAGE); + } + + /** + * Restores the previously saved tabs from the + * bundle stored in peristent file storage. + * It will create new tabs for each tab saved + * and will delete the saved instance file when + * restoration is complete. + */ + private Observable restoreState() { + return Observable.create(new Action() { + @Override + public void onSubscribe(@NonNull Subscriber subscriber) { + Bundle savedState = FileUtils.readBundleFromStorage(mApp, BUNDLE_STORAGE); + if (savedState != null) { + Log.d(Constants.TAG, "Restoring previous WebView state now"); + for (String key : savedState.keySet()) { + if (key.startsWith(BUNDLE_KEY)) { + subscriber.onNext(savedState.getBundle(key)); + } + } + } + FileUtils.deleteBundleInStorage(mApp, BUNDLE_STORAGE); + subscriber.onComplete(); + } + }); + } + + /** + * Return the {@link WebView} associated to the current tab, + * or null if there is no current tab. + * + * @return a {@link WebView} or null if there is no current tab. + */ + @Nullable + public synchronized WebView getCurrentWebView() { + return mCurrentTab != null ? mCurrentTab.getWebView() : null; + } + + /** + * Returns the index of the current tab. + * + * @return Return the index of the current tab, or -1 if the + * current tab is null. + */ + public int indexOfCurrentTab() { + return mTabList.indexOf(mCurrentTab); + } + + /** + * Returns the index of the tab. + * + * @return Return the index of the tab, or -1 if the tab isn't in the list. + */ + public int indexOfTab(LightningView tab) { + return mTabList.indexOf(tab); + } + + /** + * Return the current {@link LightningView} or null if + * no current tab has been set. + * + * @return a {@link LightningView} or null if there + * is no current tab. + */ + @Nullable + public synchronized LightningView getCurrentTab() { + return mCurrentTab; + } + + /** + * Switch the current tab to the one at the given position. + * It returns the selected tab that has been switced to. + * + * @return the selected tab or null if position is out of tabs range. + */ + @Nullable + public synchronized LightningView switchToTab(final int position) { + Log.d(TAG, "switch to tab: " + position); + if (position < 0 || position >= mTabList.size()) { + Log.e(TAG, "Returning a null LightningView requested for position: " + position); + return null; + } else { + final LightningView tab = mTabList.get(position); + if (tab != null) { + mCurrentTab = tab; + } + return tab; + } + } + +} diff --git a/app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java b/app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java index 88a01dca6..dc04c6c08 100644 --- a/app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java @@ -5,18 +5,24 @@ import android.os.Bundle; import android.support.v7.app.AppCompatActivity; +import javax.inject.Inject; + import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.preference.PreferenceManager; public abstract class ThemableBrowserActivity extends AppCompatActivity { + @Inject PreferenceManager mPreferences; + private int mTheme; private boolean mShowTabsInDrawer; @Override protected void onCreate(Bundle savedInstanceState) { - mTheme = PreferenceManager.getInstance().getUseTheme(); - mShowTabsInDrawer = PreferenceManager.getInstance().getShowTabsInDrawer(!isTablet()); + BrowserApp.getAppComponent().inject(this); + mTheme = mPreferences.getUseTheme(); + mShowTabsInDrawer = mPreferences.getShowTabsInDrawer(!isTablet()); // set the theme if (mTheme == 1) { @@ -30,8 +36,8 @@ protected void onCreate(Bundle savedInstanceState) { @Override protected void onResume() { super.onResume(); - int theme = PreferenceManager.getInstance().getUseTheme(); - boolean drawerTabs = PreferenceManager.getInstance().getShowTabsInDrawer(!isTablet()); + int theme = mPreferences.getUseTheme(); + boolean drawerTabs = mPreferences.getShowTabsInDrawer(!isTablet()); if (theme != mTheme || mShowTabsInDrawer != drawerTabs) { restart(); } @@ -42,8 +48,7 @@ boolean isTablet() { } private void restart() { - Intent intent = getIntent(); finish(); - startActivity(intent); + startActivity(new Intent(this, getClass())); } } diff --git a/app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java b/app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java index c5a457355..4d3cbe5f9 100644 --- a/app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java @@ -3,7 +3,10 @@ import android.graphics.drawable.ColorDrawable; import android.os.Bundle; +import javax.inject.Inject; + import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.ThemeUtils; @@ -11,9 +14,12 @@ public abstract class ThemableSettingsActivity extends AppCompatPreferenceActivi private int mTheme; + @Inject PreferenceManager mPreferenceManager; + @Override protected void onCreate(Bundle savedInstanceState) { - mTheme = PreferenceManager.getInstance().getUseTheme(); + BrowserApp.getAppComponent().inject(this); + mTheme = mPreferenceManager.getUseTheme(); // set the theme if (mTheme == 0) { @@ -32,7 +38,7 @@ protected void onCreate(Bundle savedInstanceState) { @Override protected void onResume() { super.onResume(); - if (PreferenceManager.getInstance().getUseTheme() != mTheme) { + if (mPreferenceManager.getUseTheme() != mTheme) { restart(); } } diff --git a/app/src/main/java/acr/browser/lightning/app/AppComponent.java b/app/src/main/java/acr/browser/lightning/app/AppComponent.java index dbaf0bc4b..0f8187e49 100644 --- a/app/src/main/java/acr/browser/lightning/app/AppComponent.java +++ b/app/src/main/java/acr/browser/lightning/app/AppComponent.java @@ -3,16 +3,26 @@ import javax.inject.Singleton; import acr.browser.lightning.activity.BrowserActivity; -import acr.browser.lightning.constant.BookmarkPage; -import acr.browser.lightning.dialog.BookmarksDialogBuilder; +import acr.browser.lightning.activity.ReadingActivity; +import acr.browser.lightning.activity.TabsManager; +import acr.browser.lightning.activity.ThemableBrowserActivity; +import acr.browser.lightning.activity.ThemableSettingsActivity; +import acr.browser.lightning.browser.BrowserPresenter; +import acr.browser.lightning.constant.StartPage; +import acr.browser.lightning.dialog.LightningDialogBuilder; +import acr.browser.lightning.download.LightningDownloadListener; import acr.browser.lightning.fragment.BookmarkSettingsFragment; import acr.browser.lightning.fragment.BookmarksFragment; -import acr.browser.lightning.object.SearchAdapter; +import acr.browser.lightning.fragment.LightningPreferenceFragment; +import acr.browser.lightning.fragment.PrivacySettingsFragment; +import acr.browser.lightning.fragment.TabsFragment; +import acr.browser.lightning.search.SuggestionsAdapter; +import acr.browser.lightning.utils.AdBlock; +import acr.browser.lightning.utils.ProxyUtils; +import acr.browser.lightning.view.LightningView; +import acr.browser.lightning.view.LightningWebClient; import dagger.Component; -/** - * Created by Stefano Pacifici on 01/09/15. - */ @Singleton @Component(modules = {AppModule.class}) public interface AppComponent { @@ -23,9 +33,38 @@ public interface AppComponent { void inject(BookmarkSettingsFragment fragment); - void inject(SearchAdapter adapter); + void inject(SuggestionsAdapter adapter); - void inject(BookmarksDialogBuilder builder); + void inject(LightningDialogBuilder builder); + + void inject(TabsFragment fragment); + + void inject(LightningView lightningView); + + void inject(ThemableBrowserActivity activity); + + void inject(LightningPreferenceFragment fragment); + + void inject(BrowserApp app); + + void inject(ProxyUtils proxyUtils); + + void inject(ReadingActivity activity); + + void inject(LightningWebClient webClient); + + void inject(ThemableSettingsActivity activity); + + void inject(AdBlock adBlock); + + void inject(LightningDownloadListener listener); + + void inject(PrivacySettingsFragment fragment); + + void inject(StartPage startPage); + + void inject(BrowserPresenter presenter); + + void inject(TabsManager manager); - void inject(BookmarkPage bookmarkPage); } diff --git a/app/src/main/java/acr/browser/lightning/app/AppModule.java b/app/src/main/java/acr/browser/lightning/app/AppModule.java index d2738803a..9a4811eef 100644 --- a/app/src/main/java/acr/browser/lightning/app/AppModule.java +++ b/app/src/main/java/acr/browser/lightning/app/AppModule.java @@ -1,41 +1,49 @@ package acr.browser.lightning.app; +import android.app.Application; import android.content.Context; +import android.support.annotation.NonNull; import com.squareup.otto.Bus; +import net.i2p.android.ui.I2PAndroidHelper; + import javax.inject.Singleton; -import acr.browser.lightning.database.BookmarkManager; import dagger.Module; import dagger.Provides; -/** - * Created by Stefano Pacifici on 01/09/15. - */ @Module public class AppModule { - private final BrowserApp app; - private final Bus bus; + private final BrowserApp mApp; + @NonNull private final Bus mBus; public AppModule(BrowserApp app) { - this.app = app; - this.bus = new Bus(); + this.mApp = app; + this.mBus = new Bus(); } @Provides - public Context provideContext() { - return app.getApplicationContext(); + public Application provideApplication() { + return mApp; } @Provides - @Singleton - public BookmarkManager provideBookmarkManager() { - return new BookmarkManager(app.getApplicationContext()); + public Context provideContext() { + return mApp.getApplicationContext(); } + @NonNull @Provides public Bus provideBus() { - return bus; + return mBus; } + + @NonNull + @Provides + @Singleton + public I2PAndroidHelper provideI2PAndroidHelper() { + return new I2PAndroidHelper(mApp.getApplicationContext()); + } + } diff --git a/app/src/main/java/acr/browser/lightning/app/BrowserApp.java b/app/src/main/java/acr/browser/lightning/app/BrowserApp.java index e5f00fa74..f0689d68e 100644 --- a/app/src/main/java/acr/browser/lightning/app/BrowserApp.java +++ b/app/src/main/java/acr/browser/lightning/app/BrowserApp.java @@ -2,32 +2,60 @@ import android.app.Application; import android.content.Context; +import android.os.Build; +import android.support.annotation.NonNull; +import android.webkit.WebView; import com.squareup.leakcanary.LeakCanary; +import com.squareup.otto.Bus; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import javax.inject.Inject; + +import acr.browser.lightning.BuildConfig; public class BrowserApp extends Application { - private static Context context; - private static AppComponent appComponent; + private static AppComponent mAppComponent; + private static final Executor mIOThread = Executors.newSingleThreadExecutor(); + private static final Executor mTaskThread = Executors.newCachedThreadPool(); + + @Inject Bus mBus; @Override public void onCreate() { super.onCreate(); - context = getApplicationContext(); + mAppComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build(); + mAppComponent.inject(this); LeakCanary.install(this); - buildDepencyGraph(); + if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + WebView.setWebContentsDebuggingEnabled(true); + } } - public static Context getAppContext() { - return context; + @NonNull + public static BrowserApp get(@NonNull Context context) { + return (BrowserApp) context.getApplicationContext(); } public static AppComponent getAppComponent() { - return appComponent; + return mAppComponent; + } + + @NonNull + public static Executor getIOThread() { + return mIOThread; + } + + @NonNull + public static Executor getTaskThread() { + return mTaskThread; } - private void buildDepencyGraph() { - appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build(); + public static Bus getBus(@NonNull Context context) { + return get(context).mBus; } } diff --git a/app/src/main/java/acr/browser/lightning/async/AsyncExecutor.java b/app/src/main/java/acr/browser/lightning/async/AsyncExecutor.java index 5dec5a2ee..7dea22476 100644 --- a/app/src/main/java/acr/browser/lightning/async/AsyncExecutor.java +++ b/app/src/main/java/acr/browser/lightning/async/AsyncExecutor.java @@ -22,6 +22,7 @@ public class AsyncExecutor implements Executor { private AsyncExecutor() {} + @NonNull public static AsyncExecutor getInstance() { return INSTANCE; } diff --git a/app/src/main/java/acr/browser/lightning/async/ImageDownloadTask.java b/app/src/main/java/acr/browser/lightning/async/ImageDownloadTask.java index e4da77ef8..e229e8f25 100644 --- a/app/src/main/java/acr/browser/lightning/async/ImageDownloadTask.java +++ b/app/src/main/java/acr/browser/lightning/async/ImageDownloadTask.java @@ -1,10 +1,13 @@ package acr.browser.lightning.async; +import android.app.Activity; +import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.AsyncTask; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.Log; import android.widget.ImageView; @@ -15,7 +18,6 @@ import java.net.HttpURLConnection; import java.net.URL; -import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.utils.Utils; @@ -23,22 +25,27 @@ public class ImageDownloadTask extends AsyncTask { private static final String TAG = ImageDownloadTask.class.getSimpleName(); - private static final File mCacheDir = BrowserApp.getAppContext().getCacheDir(); - private final WeakReference bmImage; - private final HistoryItem mWeb; + @NonNull private final WeakReference mFaviconImage; + @NonNull private final WeakReference mContextReference; + @NonNull private final HistoryItem mWeb; private final String mUrl; - private final Bitmap mDefaultBitmap; + @NonNull private final Bitmap mDefaultBitmap; - public ImageDownloadTask(@NonNull ImageView bmImage, @NonNull HistoryItem web, @NonNull Bitmap defaultBitmap) { + public ImageDownloadTask(@NonNull ImageView bmImage, + @NonNull HistoryItem web, + @NonNull Bitmap defaultBitmap, + @NonNull Context context) { // Set a tag on the ImageView so we know if the view // has gone out of scope and should not be used bmImage.setTag(web.getUrl().hashCode()); - this.bmImage = new WeakReference<>(bmImage); + this.mFaviconImage = new WeakReference<>(bmImage); this.mWeb = web; this.mUrl = web.getUrl(); this.mDefaultBitmap = defaultBitmap; + this.mContextReference = new WeakReference<>(context.getApplicationContext()); } + @Nullable @Override protected Bitmap doInBackground(Void... params) { Bitmap mIcon = null; @@ -46,12 +53,17 @@ protected Bitmap doInBackground(Void... params) { if (mUrl == null) { return mDefaultBitmap; } + Context context = mContextReference.get(); + if (context == null) { + return mDefaultBitmap; + } + File cache = context.getCacheDir(); final Uri uri = Uri.parse(mUrl); if (uri.getHost() == null || uri.getScheme() == null) { return mDefaultBitmap; } final String hash = String.valueOf(uri.getHost().hashCode()); - final File image = new File(mCacheDir, hash + ".png"); + final File image = new File(cache, hash + ".png"); final String urlDisplay = uri.getScheme() + "://" + uri.getHost() + "/favicon.ico"; // checks to see if the image exists if (!image.exists()) { @@ -130,9 +142,19 @@ protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); AsyncExecutor.getInstance().notifyThreadFinish(); final Bitmap fav = Utils.padFavicon(bitmap); - ImageView view = bmImage.get(); + final ImageView view = mFaviconImage.get(); if (view != null && view.getTag().equals(mWeb.getUrl().hashCode())) { - view.setImageBitmap(fav); + Context context = view.getContext(); + if (context instanceof Activity) { + ((Activity) context).runOnUiThread(new Runnable() { + @Override + public void run() { + view.setImageBitmap(fav); + } + }); + } else { + view.setImageBitmap(fav); + } } mWeb.setBitmap(fav); } diff --git a/app/src/main/java/acr/browser/lightning/browser/BrowserPresenter.java b/app/src/main/java/acr/browser/lightning/browser/BrowserPresenter.java new file mode 100644 index 000000000..7b792085e --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/browser/BrowserPresenter.java @@ -0,0 +1,334 @@ +package acr.browser.lightning.browser; + +import android.app.Activity; +import android.content.DialogInterface; +import android.content.Intent; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import com.squareup.otto.Bus; + +import javax.inject.Inject; + +import acr.browser.lightning.R; +import acr.browser.lightning.activity.TabsManager; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.controller.UIController; +import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.react.OnSubscribe; +import acr.browser.lightning.utils.UrlUtils; +import acr.browser.lightning.view.LightningView; + +/** + * Presenter in charge of keeping track of + * the current tab and setting the current tab + * of the + */ +public class BrowserPresenter { + + private static final String TAG = BrowserPresenter.class.getSimpleName(); + + @NonNull private final TabsManager mTabsModel; + @Inject PreferenceManager mPreferences; + @Inject Bus mEventBus; + + @NonNull private final BrowserView mView; + @Nullable private LightningView mCurrentTab; + + private final boolean mIsIncognito; + private boolean mShouldClose; + + public BrowserPresenter(@NonNull BrowserView view, boolean isIncognito) { + BrowserApp.getAppComponent().inject(this); + mTabsModel = ((UIController) view).getTabModel(); + mView = view; + mIsIncognito = isIncognito; + mTabsModel.setTabNumberChangedListener(new TabsManager.TabNumberChangedListener() { + @Override + public void tabNumberChanged(int newNumber) { + mView.updateTabNumber(newNumber); + } + }); + } + + /** + * Initializes the tab manager with the new intent + * that is handed in by the BrowserActivity. + * + * @param intent the intent to handle, may be null. + */ + public void setupTabs(@Nullable Intent intent) { + mTabsModel.initializeTabs((Activity) mView, intent, mIsIncognito) + .subscribe(new OnSubscribe() { + @Override + public void onComplete() { + // At this point we always have at least a tab in the tab manager + tabChanged(mTabsModel.last()); + mView.updateTabNumber(mTabsModel.size()); + } + }); + } + + /** + * Notify the presenter that a change occurred to + * the current tab. Currently doesn't do anything + * other than tell the view to notify the adapter + * about the change. + * + * @param tab the tab that changed, may be null. + */ + public void tabChangeOccurred(@Nullable LightningView tab) { + mView.notifyTabViewChanged(mTabsModel.indexOfTab(tab)); + } + + private void onTabChanged(@Nullable LightningView newTab) { + Log.d(TAG, "On tab changed"); + if (newTab == null) { + mView.removeTabView(); + if (mCurrentTab != null) { + mCurrentTab.pauseTimers(); + mCurrentTab.onDestroy(); + } + } else { + if (newTab.getWebView() == null) { + mView.removeTabView(); + if (mCurrentTab != null) { + mCurrentTab.pauseTimers(); + mCurrentTab.onDestroy(); + } + } else { + if (mCurrentTab != null) { + // TODO: Restore this when Google fixes the bug where the WebView is + // blank after calling onPause followed by onResume. + // mCurrentTab.onPause(); + mCurrentTab.setForegroundTab(false); + } + + newTab.resumeTimers(); + newTab.onResume(); + newTab.setForegroundTab(true); + + mView.updateProgress(newTab.getProgress()); + mView.setBackButtonEnabled(newTab.canGoBack()); + mView.setForwardButtonEnabled(newTab.canGoForward()); + mView.updateUrl(newTab.getUrl(), true); + mView.setTabView(newTab.getWebView()); + int index = mTabsModel.indexOfTab(newTab); + if (index >= 0) { + mView.notifyTabViewChanged(mTabsModel.indexOfTab(newTab)); + } + } + } + + mCurrentTab = newTab; + } + + /** + * Closes all tabs but the current tab. + */ + public void closeAllOtherTabs() { + + while (mTabsModel.last() != mTabsModel.indexOfCurrentTab()) { + deleteTab(mTabsModel.last()); + } + + while (0 != mTabsModel.indexOfCurrentTab()) { + deleteTab(0); + } + + } + + /** + * Deletes the tab at the specified position. + * + * @param position the position at which to + * delete the tab. + */ + public void deleteTab(int position) { + Log.d(TAG, "delete Tab"); + final LightningView tabToDelete = mTabsModel.getTabAtPosition(position); + + if (tabToDelete == null) { + return; + } + + if (!UrlUtils.isSpecialUrl(tabToDelete.getUrl()) && !mIsIncognito) { + mPreferences.setSavedUrl(tabToDelete.getUrl()); + } + + final boolean isShown = tabToDelete.isShown(); + boolean shouldClose = mShouldClose && isShown && Boolean.TRUE.equals(tabToDelete.getTag()); + final LightningView currentTab = mTabsModel.getCurrentTab(); + if (mTabsModel.size() == 1 && currentTab != null && + (UrlUtils.isSpecialUrl(currentTab.getUrl()) || + currentTab.getUrl().equals(mPreferences.getHomepage()))) { + mView.closeActivity(); + return; + } else { + if (isShown) { + mView.removeTabView(); + } + boolean currentDeleted = mTabsModel.deleteTab(position); + if (currentDeleted) { + tabChanged(mTabsModel.indexOfCurrentTab()); + } + } + + final LightningView afterTab = mTabsModel.getCurrentTab(); + mView.notifyTabViewRemoved(position); + + if (afterTab == null) { + mView.closeBrowser(); + return; + } else if (afterTab != currentTab) { + //TODO remove this? +// switchTabs(currentTab, afterTab); +// if (currentTab != null) { +// currentTab.pauseTimers(); +// } + mView.notifyTabViewChanged(mTabsModel.indexOfCurrentTab()); + } + + if (shouldClose) { + mShouldClose = false; + mView.closeActivity(); + } + + mView.updateTabNumber(mTabsModel.size()); + + Log.d(TAG, "deleted tab"); + } + + /** + * Handle a new intent from the the main + * BrowserActivity. + * + * @param intent the intent to handle, + * may be null. + */ + public void onNewIntent(@Nullable final Intent intent) { + mTabsModel.doAfterInitialization(new Runnable() { + @Override + public void run() { + final String url; + if (intent != null) { + url = intent.getDataString(); + } else { + url = null; + } + int num = 0; + if (intent != null && intent.getExtras() != null) { + num = intent.getExtras().getInt(Constants.INTENT_ORIGIN); + } + + if (num == 1) { + loadUrlInCurrentView(url); + } else if (url != null) { + if (url.startsWith(Constants.FILE)) { + mView.showBlockedLocalFileDialog(new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + newTab(url, true); + } + }); + } else { + newTab(url, true); + } + mShouldClose = true; + LightningView tab = mTabsModel.lastTab(); + if (tab != null) { + tab.setTag(true); + } + } + } + }); + } + + /** + * Loads a URL in the current tab. + * + * @param url the URL to load, must + * not be null. + */ + public void loadUrlInCurrentView(@NonNull final String url) { + final LightningView currentTab = mTabsModel.getCurrentTab(); + if (currentTab == null) { + // This is a problem, probably an assert will be better than a return + return; + } + + currentTab.loadUrl(url); + } + + /** + * Notifies the presenter that it should + * shut down. This should be called when + * the BrowserActivity is destroyed so that + * we don't leak any memory. + */ + public void shutdown() { + onTabChanged(null); + mTabsModel.setTabNumberChangedListener(null); + mTabsModel.cancelPendingWork(); + } + + /** + * Notifies the presenter that we wish + * to switch to a different tab at the + * specified position. If the position + * is not in the model, this method will + * do nothing. + * + * @param position the position of the + * tab to switch to. + */ + public synchronized void tabChanged(int position) { + Log.d(TAG, "tabChanged: " + position); + if (position < 0 || position >= mTabsModel.size()) { + return; + } + LightningView tab = mTabsModel.switchToTab(position); + onTabChanged(tab); + } + + /** + * Open a new tab with the specified URL. You + * can choose to show the tab or load it in the + * background. + * + * @param url the URL to load, may be null if you + * don't wish to load anything. + * @param show whether or not to switch to this + * tab after opening it. + * @return true if we successfully created the tab, + * false if we have hit max tabs. + */ + public synchronized boolean newTab(@Nullable String url, boolean show) { + // Limit number of tabs for limited version of app + if (!Constants.FULL_VERSION && mTabsModel.size() >= 10) { + mView.showSnackbar(R.string.max_tabs); + return false; + } + + Log.d(TAG, "New tab, show: " + show); + + LightningView startingTab = mTabsModel.newTab((Activity) mView, url, mIsIncognito); + if (mTabsModel.size() == 1) { + startingTab.resumeTimers(); + } + + mView.notifyTabViewAdded(); + + if (show) { + LightningView tab = mTabsModel.switchToTab(mTabsModel.last()); + onTabChanged(tab); + } + + mView.updateTabNumber(mTabsModel.size()); + + return true; + } + +} diff --git a/app/src/main/java/acr/browser/lightning/browser/BrowserView.java b/app/src/main/java/acr/browser/lightning/browser/BrowserView.java new file mode 100644 index 000000000..4ef45f59f --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/browser/BrowserView.java @@ -0,0 +1,38 @@ +package acr.browser.lightning.browser; + +import android.content.DialogInterface; +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; +import android.view.View; + +public interface BrowserView { + + void setTabView(@NonNull View view); + + void removeTabView(); + + void updateUrl(String url, boolean shortUrl); + + void updateProgress(int progress); + + void updateTabNumber(int number); + + void closeBrowser(); + + void closeActivity(); + + void showBlockedLocalFileDialog(DialogInterface.OnClickListener listener); + + void showSnackbar(@StringRes int resource); + + void setForwardButtonEnabled(boolean enabled); + + void setBackButtonEnabled(boolean enabled); + + void notifyTabViewRemoved(int position); + + void notifyTabViewAdded(); + + void notifyTabViewChanged(int position); + +} diff --git a/app/src/main/java/acr/browser/lightning/browser/TabsView.java b/app/src/main/java/acr/browser/lightning/browser/TabsView.java new file mode 100644 index 000000000..8c4e64ee2 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/browser/TabsView.java @@ -0,0 +1,11 @@ +package acr.browser.lightning.browser; + +public interface TabsView { + + void tabAdded(); + + void tabRemoved(int position); + + void tabChanged(int position); + +} diff --git a/app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java b/app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java index 3331eb7e7..20c10e595 100644 --- a/app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java +++ b/app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java @@ -2,37 +2,12 @@ import acr.browser.lightning.database.HistoryItem; -/** - * Created by Stefano Pacifici on 26/08/15. - */ public final class BookmarkEvents { private BookmarkEvents() { // No instances } - /** - * A bookmark was clicked - */ - public final static class Clicked { - public final HistoryItem bookmark; - - public Clicked(final HistoryItem bookmark) { - this.bookmark = bookmark; - } - } - - /** - * The user ask to open the bookmark as new tab - */ - public final static class AsNewTab { - public final HistoryItem bookmark; - - public AsNewTab(final HistoryItem bookmark) { - this.bookmark = bookmark; - } - } - /** * The user ask to delete the selected bookmark */ @@ -45,29 +20,11 @@ public Deleted(final HistoryItem item) { } /** - * The user ask to bookmark the currently displayed page + * The user ask to add/del a bookmark to the currently displayed page */ - public static class WantToBookmarkCurrentPage { + public static class ToggleBookmarkForCurrentPage { } - /** - * The bookmark was added - */ - public static class Added { - public final HistoryItem item; - - public Added(final HistoryItem item) { - this.item = item; - } - } - - /** - * The {@link acr.browser.lightning.fragment.BookmarksFragment} want to know the url (and title) - * of the currently shown web page. - */ - // public static class WantInfoAboutCurrentPage { - // } - /** * Sended by the {@link acr.browser.lightning.fragment.BookmarksFragment} when it wants to close * itself (generally in reply to a {@link acr.browser.lightning.bus.BrowserEvents.UserPressedBack} diff --git a/app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java b/app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java index 15ff37369..17dbe6664 100644 --- a/app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java +++ b/app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java @@ -1,8 +1,8 @@ package acr.browser.lightning.bus; -/** - * Created by Stefano Pacifici on 26/08/15. - */ +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; + public final class BrowserEvents { private BrowserEvents() { @@ -10,23 +10,21 @@ private BrowserEvents() { } /** - * Used to reply to the {@link acr.browser.lightning.fragment.BookmarksFragment} message - * {@link acr.browser.lightning.bus.BookmarkEvents.WantToBookmarkCurrentPage}. The interaction - * result is a new bookmark added. + * The {@link acr.browser.lightning.activity.BrowserActivity} signal a new bookmark was added + * (mainly to the {@link acr.browser.lightning.fragment.BookmarksFragment}). */ - public static class AddBookmark { + public static class BookmarkAdded { public final String title, url; - public AddBookmark(final String title, final String url) { + public BookmarkAdded(final String title, final String url) { this.title = title; this.url = url; } } /** - * Used to reply to {@link acr.browser.lightning.fragment.BookmarksFragment} message - * {@link acr.browser.lightning.bus.BookmarkEvents.WantInfoAboutCurrentPage}. This is generally - * used to update the {@link acr.browser.lightning.fragment.BookmarksFragment} interface. + * Notify the current page has a new url. This is generally used to update the + * {@link acr.browser.lightning.fragment.BookmarksFragment} interface. */ public static class CurrentPageUrl { public final String url; @@ -41,4 +39,52 @@ public CurrentPageUrl(final String url) { */ public static class UserPressedBack { } + + /** + * + */ + + /** + * Notify the Browser to display a SnackBar in the main activity + */ + public static class ShowSnackBarMessage { + @Nullable public final String message; + @StringRes + public final int stringRes; + + public ShowSnackBarMessage(@Nullable final String message) { + this.message = message; + this.stringRes = -1; + } + + public ShowSnackBarMessage(@StringRes final int stringRes) { + this.message = null; + this.stringRes = stringRes; + } + } + + public final static class OpenHistoryInCurrentTab { + } + + /** + * The user want to open the given url in the current tab + */ + public final static class OpenUrlInCurrentTab { + public final String url; + + public OpenUrlInCurrentTab(final String url) { + this.url = url; + } + } + + /** + * The user ask to open the given url as new tab + */ + public final static class OpenUrlInNewTab { + public final String url; + + public OpenUrlInNewTab(final String url) { + this.url = url; + } + } } diff --git a/app/src/main/java/acr/browser/lightning/bus/NavigationEvents.java b/app/src/main/java/acr/browser/lightning/bus/NavigationEvents.java new file mode 100644 index 000000000..5f7519fa3 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/bus/NavigationEvents.java @@ -0,0 +1,34 @@ +package acr.browser.lightning.bus; + +/** + * Collections of navigation events, like go back or go forward + * + * @author Stefano Pacifici + * @date 2015/09/15 + */ +public class NavigationEvents { + private NavigationEvents() { + // No instances please + } + + /** + * Fired by {@link acr.browser.lightning.fragment.TabsFragment} when the user presses back + * button. + */ + public static class GoBack { + } + + /** + * Fired by {@link acr.browser.lightning.fragment.TabsFragment} when the user presses forward + * button. + */ + public static class GoForward { + } + + /** + * Fired by {@link acr.browser.lightning.fragment.TabsFragment} when the user presses the home + * button. + */ + public static class GoHome { + } +} diff --git a/app/src/main/java/acr/browser/lightning/bus/TabEvents.java b/app/src/main/java/acr/browser/lightning/bus/TabEvents.java new file mode 100644 index 000000000..a2bac5c9d --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/bus/TabEvents.java @@ -0,0 +1,65 @@ +package acr.browser.lightning.bus; + +/** + * A collection of events been sent by {@link acr.browser.lightning.fragment.TabsFragment} + * + * @author Stefano Pacifici + * @date 2015/09/14 + */ +public final class TabEvents { + + private TabEvents() { + // No instances + } + + + /** + * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user click on the + * tab exit button + */ + public static class CloseTab { + public final int position; + + public CloseTab(int position) { + this.position = position; + } + } + + /** + * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user click on the + * tab itself. + */ + public static class ShowTab { + public final int position; + + public ShowTab(int position) { + this.position = position; + } + } + + /** + * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user long press on the + * tab itself. + */ + public static class ShowCloseDialog { + public final int position; + + public ShowCloseDialog(int position) { + this.position = position; + } + } + + /** + * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user want to create a + * new tab. + */ + public static class NewTab { + } + + /** + * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user long presses on + * new tab button. + */ + public static class NewTabLongPress { + } +} diff --git a/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java b/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java index 67c176586..6e4a02ae1 100644 --- a/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java +++ b/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java @@ -3,31 +3,44 @@ */ package acr.browser.lightning.constant; -import android.content.Context; +import android.app.Activity; +import android.app.Application; +import android.graphics.Bitmap; +import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.List; -import javax.inject.Inject; - import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.database.BookmarkManager; import acr.browser.lightning.database.HistoryItem; +import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.Utils; +import acr.browser.lightning.view.LightningView; + +public final class BookmarkPage extends AsyncTask { -public final class BookmarkPage { + /** + * The bookmark page standard suffix + */ + public static final String FILENAME = "bookmarks.html"; - private static final String HEADING = "\n" + + private static final String HEADING_1 = "\n" + "\n" + "\n" + "\n" + - "\n" + - "" + - BrowserApp.getAppContext().getString(R.string.action_bookmarks) + - "\n" + + "\n" + + ""; + + private static final String HEADING_2 = "\n" + "\n" + "\n" + @@ -41,7 +54,7 @@ public final class BookmarkPage { "

\n" + ""; @@ -49,55 +62,90 @@ public final class BookmarkPage { private static final String END = ""; - @Inject - BookmarkManager manager; + private File mFilesDir; + private File mCacheDir; + + private final Application mApp; + private final BookmarkManager mManager; + @NonNull private final WeakReference mTabReference; + private final Bitmap mFolderIcon; + @NonNull private final String mTitle; + + public BookmarkPage(LightningView tab, @NonNull Activity activity, BookmarkManager manager) { + mApp = BrowserApp.get(activity); + final Bitmap folderIcon = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_folder, false); + mTitle = mApp.getString(R.string.action_bookmarks); + mManager = manager; + mTabReference = new WeakReference<>(tab); + mFolderIcon = folderIcon; + } - private final File FILES_DIR; - private final File CACHE_DIR; + @Override + protected Void doInBackground(Void... params) { + mCacheDir = mApp.getCacheDir(); + mFilesDir = mApp.getFilesDir(); + cacheDefaultFolderIcon(); + buildBookmarkPage(null, mManager); + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + LightningView tab = mTabReference.get(); + if (tab != null) { + File bookmarkWebPage = new File(mFilesDir, FILENAME); + tab.loadUrl(Constants.FILE + bookmarkWebPage); + } + } - @Inject - public BookmarkPage(Context context) { - BrowserApp.getAppComponent().inject(this); - FILES_DIR = context.getFilesDir(); - CACHE_DIR = context.getCacheDir(); + private void cacheDefaultFolderIcon() { + FileOutputStream outputStream = null; + File image = new File(mCacheDir, "folder.png"); + try { + outputStream = new FileOutputStream(image); + mFolderIcon.compress(Bitmap.CompressFormat.PNG, 100, outputStream); + mFolderIcon.recycle(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } finally { + Utils.close(outputStream); + } } - public void buildBookmarkPage(final String folder, final List list) { + private void buildBookmarkPage(@Nullable final String folder, @NonNull final BookmarkManager manager) { + final List list = manager.getBookmarksCopyFromFolder(folder, true); final File bookmarkWebPage; if (folder == null || folder.isEmpty()) { - bookmarkWebPage = new File(FILES_DIR, Constants.BOOKMARKS_FILENAME); + bookmarkWebPage = new File(mFilesDir, FILENAME); } else { - bookmarkWebPage = new File(FILES_DIR, folder + '-' + Constants.BOOKMARKS_FILENAME); + bookmarkWebPage = new File(mFilesDir, folder + '-' + FILENAME); } - final StringBuilder bookmarkBuilder = new StringBuilder(BookmarkPage.HEADING); + final StringBuilder bookmarkBuilder = new StringBuilder(HEADING_1 + mTitle + HEADING_2); - final String folderIconPath = Constants.FILE + CACHE_DIR + "/folder.png"; + final String folderIconPath = Constants.FILE + mCacheDir + "/folder.png"; for (int n = 0, size = list.size(); n < size; n++) { final HistoryItem item = list.get(n); - bookmarkBuilder.append(BookmarkPage.PART1); + bookmarkBuilder.append(PART1); if (item.isFolder()) { - final File folderPage = new File(FILES_DIR, item.getTitle() + '-' + Constants.BOOKMARKS_FILENAME); + final File folderPage = new File(mFilesDir, item.getTitle() + '-' + FILENAME); bookmarkBuilder.append(Constants.FILE).append(folderPage); - bookmarkBuilder.append(BookmarkPage.PART2); + bookmarkBuilder.append(PART2); bookmarkBuilder.append(folderIconPath); - new Thread(new Runnable() { - @Override - public void run() { - buildBookmarkPage(item.getTitle(), manager.getBookmarksFromFolder(item.getTitle(), true)); - } - }).run(); + buildBookmarkPage(item.getTitle(), manager); } else { bookmarkBuilder.append(item.getUrl()); - bookmarkBuilder.append(BookmarkPage.PART2).append(BookmarkPage.PART3); + bookmarkBuilder.append(PART2).append(PART3); bookmarkBuilder.append(item.getUrl()); } - bookmarkBuilder.append(BookmarkPage.PART4); + bookmarkBuilder.append(PART4); bookmarkBuilder.append(item.getTitle()); - bookmarkBuilder.append(BookmarkPage.PART5); + bookmarkBuilder.append(PART5); } - bookmarkBuilder.append(BookmarkPage.END); + bookmarkBuilder.append(END); FileWriter bookWriter = null; try { + //noinspection IOResourceOpenedButNotSafelyClosed bookWriter = new FileWriter(bookmarkWebPage, false); bookWriter.write(bookmarkBuilder.toString()); } catch (IOException e) { @@ -107,4 +155,8 @@ public void run() { } } + public void load() { + executeOnExecutor(BrowserApp.getIOThread()); + } + } diff --git a/app/src/main/java/acr/browser/lightning/constant/Constants.java b/app/src/main/java/acr/browser/lightning/constant/Constants.java index 33bc97335..3861a28e8 100644 --- a/app/src/main/java/acr/browser/lightning/constant/Constants.java +++ b/app/src/main/java/acr/browser/lightning/constant/Constants.java @@ -27,6 +27,22 @@ private Constants() { public static final String YANDEX_SEARCH = "https://yandex.ru/yandsearch?lr=21411&text="; public static final String JAVASCRIPT_INVERT_PAGE = "javascript:(function(){var e='img {-webkit-filter: invert(100%);'+'-moz-filter: invert(100%);'+'-o-filter: invert(100%);'+'-ms-filter: invert(100%); }',t=document.getElementsByTagName('head')[0],n=document.createElement('style');if(!window.counter){window.counter=1}else{window.counter++;if(window.counter%2==0){var e='html {-webkit-filter: invert(0%); -moz-filter: invert(0%); -o-filter: invert(0%); -ms-filter: invert(0%); }'}}n.type='text/css';if(n.styleSheet){n.styleSheet.cssText=e}else{n.appendChild(document.createTextNode(e))}t.appendChild(n)})();"; public static final String JAVASCRIPT_TEXT_REFLOW = "javascript:document.getElementsByTagName('body')[0].style.width=window.innerWidth+'px';"; + public static final String JAVASCRIPT_THEME_COLOR = "(function () {\n" + + " \"use strict\";\n" + + " var metas, i, tag;\n" + + " metas = document.getElementsByTagName('meta');\n" + + " if (metas !== null) {\n" + + " for (i = 0; i < metas.length; i++) {\n" + + " tag = metas[i].getAttribute('name');\n" + + " if (tag !== null && tag.toLowerCase() === 'theme-color') {\n" + + " return metas[i].getAttribute('content');\n" + + " }\n" + + " console.log(tag);\n" + + " }\n" + + " }\n" + + '\n' + + " return '';\n" + + "}());"; public static final String LOAD_READING_URL = "ReadingUrl"; @@ -43,12 +59,9 @@ private Constants() { public static final int PROXY_I2P = 2; public static final int PROXY_MANUAL = 3; - /** - * The bookmark page standard suffix - */ - public static final String BOOKMARKS_FILENAME = "bookmarks.html"; - public static final String DEFAULT_ENCODING = "UTF-8"; public static final String[] TEXT_ENCODINGS = {"ISO-8859-1", "UTF-8", "GBK", "Big5", "ISO-2022-JP", "SHIFT_JS", "EUC-JP", "EUC-KR"}; + + public static final String INTENT_ORIGIN = "URL_INTENT_ORIGIN"; } diff --git a/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java b/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java index 41dc907ad..e628e8ac6 100644 --- a/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java +++ b/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java @@ -3,27 +3,32 @@ */ package acr.browser.lightning.constant; +import android.app.Application; +import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.Iterator; import java.util.List; -import android.content.Context; - -import acr.browser.lightning.app.BrowserApp; -import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.database.HistoryDatabase; +import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.utils.Utils; +import acr.browser.lightning.view.LightningView; -public class HistoryPage { +public class HistoryPage extends AsyncTask { public static final String FILENAME = "history.html"; - private static final String HEADING = "" - + BrowserApp.getAppContext().getString(R.string.action_history) - + "

"; + private static final String HEADING_1 = ""; + + private static final String HEADING_2 = "
"; private static final String PART1 = "
historyList = getWebHistory(context); + @NonNull private final WeakReference mTabReference; + @NonNull private final Application mApp; + @NonNull private final String mTitle; + private final HistoryDatabase mHistoryDatabase; + + @Nullable private String mHistoryUrl = null; + + public HistoryPage(LightningView tab, @NonNull Application app, HistoryDatabase database) { + mTabReference = new WeakReference<>(tab); + mApp = app; + mTitle = app.getString(R.string.action_history); + mHistoryDatabase = database; + } + + @Nullable + @Override + protected Void doInBackground(Void... params) { + mHistoryUrl = getHistoryPage(); + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + LightningView tab = mTabReference.get(); + if (tab != null && mHistoryUrl != null) { + tab.loadUrl(mHistoryUrl); + } + } + + @NonNull + private String getHistoryPage() { + StringBuilder historyBuilder = new StringBuilder(HEADING_1 + mTitle + HEADING_2); + List historyList = mHistoryDatabase.getLastHundredItems(); Iterator it = historyList.iterator(); HistoryItem helper; while (it.hasNext()) { helper = it.next(); - historyBuilder.append(HistoryPage.PART1); + historyBuilder.append(PART1); historyBuilder.append(helper.getUrl()); - historyBuilder.append(HistoryPage.PART2); + historyBuilder.append(PART2); historyBuilder.append(helper.getTitle()); - historyBuilder.append(HistoryPage.PART3); + historyBuilder.append(PART3); historyBuilder.append(helper.getUrl()); - historyBuilder.append(HistoryPage.PART4); + historyBuilder.append(PART4); } - historyBuilder.append(HistoryPage.END); - File historyWebPage = new File(context.getFilesDir(), FILENAME); + historyBuilder.append(END); + File historyWebPage = new File(mApp.getFilesDir(), FILENAME); FileWriter historyWriter = null; try { + //noinspection IOResourceOpenedButNotSafelyClosed historyWriter = new FileWriter(historyWebPage, false); historyWriter.write(historyBuilder.toString()); } catch (IOException e) { @@ -65,8 +102,22 @@ public static String getHistoryPage(Context context) { return Constants.FILE + historyWebPage; } - private static List getWebHistory(Context context) { - HistoryDatabase databaseHandler = HistoryDatabase.getInstance(); - return databaseHandler.getLastHundredItems(); + public void load() { + executeOnExecutor(BrowserApp.getIOThread()); } -} + + /** + * Use this method to immediately delete the history + * page on the current thread. This will clear the + * cached history page that was stored on file. + * + * @param application the application object needed to get the file. + */ + public static void deleteHistoryPage(@NonNull Application application) { + File historyWebPage = new File(application.getFilesDir(), FILENAME); + if (historyWebPage.exists()) { + historyWebPage.delete(); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/acr/browser/lightning/constant/StartPage.java b/app/src/main/java/acr/browser/lightning/constant/StartPage.java index 35b5453af..15de36510 100644 --- a/app/src/main/java/acr/browser/lightning/constant/StartPage.java +++ b/app/src/main/java/acr/browser/lightning/constant/StartPage.java @@ -3,29 +3,36 @@ */ package acr.browser.lightning.constant; -import android.app.Activity; +import android.app.Application; +import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.lang.ref.WeakReference; + +import javax.inject.Inject; -import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.Utils; +import acr.browser.lightning.view.LightningView; -public class StartPage { +public class StartPage extends AsyncTask { - private static final String FILENAME = "homepage.html"; + public static final String FILENAME = "homepage.html"; - private static final String HEAD = "" + private static final String HEAD_1 = "" + "" + "" + "" + "" - + "" - + BrowserApp.getAppContext().getString(R.string.home) - + "" + + ""; + + private static final String HEAD_2 = "" + "" + "


" + private static final String MIDDLE = "\" >

" + "" + "


"; + @NonNull private final String mTitle; + @NonNull private final Application mApp; + @NonNull private final WeakReference mTabReference; + + @Inject PreferenceManager mPreferenceManager; + + private String mStartpageUrl; + + public StartPage(LightningView tab, @NonNull Application app) { + BrowserApp.getAppComponent().inject(this); + mTitle = app.getString(R.string.home); + mApp = app; + mTabReference = new WeakReference<>(tab); + } + + @Nullable + @Override + protected Void doInBackground(Void... params) { + mStartpageUrl = getHomepage(); + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + LightningView tab = mTabReference.get(); + if (tab != null) { + tab.loadUrl(mStartpageUrl); + } + } + /** * This method builds the homepage and returns the local URL to be loaded * when it finishes building. * * @return the URL to load */ - public static String getHomepage(Activity activity) { - StringBuilder homepageBuilder = new StringBuilder(StartPage.HEAD); + @NonNull + private String getHomepage() { + StringBuilder homepageBuilder = new StringBuilder(HEAD_1 + mTitle + HEAD_2); String icon; String searchUrl; - switch (PreferenceManager.getInstance().getSearchChoice()) { + switch (mPreferenceManager.getSearchChoice()) { case 0: // CUSTOM SEARCH icon = "file:///android_asset/lightning.png"; - searchUrl = PreferenceManager.getInstance().getSearchUrl(); + searchUrl = mPreferenceManager.getSearchUrl(); break; case 1: // GOOGLE_SEARCH; @@ -88,14 +127,14 @@ public static String getHomepage(Activity activity) { break; case 5: // STARTPAGE_SEARCH; - icon = "file:///android_asset/startpage.png"; - // "https://startpage.com/graphics/startp_logo.gif"; + icon = "file:///android_asset/png"; + // "https://com/graphics/startp_logo.gif"; searchUrl = Constants.STARTPAGE_SEARCH; break; case 6: // STARTPAGE_MOBILE - icon = "file:///android_asset/startpage.png"; - // "https://startpage.com/graphics/startp_logo.gif"; + icon = "file:///android_asset/png"; + // "https://com/graphics/startp_logo.gif"; searchUrl = Constants.STARTPAGE_MOBILE_SEARCH; break; case 7: @@ -131,13 +170,14 @@ public static String getHomepage(Activity activity) { } homepageBuilder.append(icon); - homepageBuilder.append(StartPage.MIDDLE); + homepageBuilder.append(MIDDLE); homepageBuilder.append(searchUrl); - homepageBuilder.append(StartPage.END); + homepageBuilder.append(END); - File homepage = new File(activity.getFilesDir(), StartPage.FILENAME); + File homepage = new File(mApp.getFilesDir(), FILENAME); FileWriter hWriter = null; try { + //noinspection IOResourceOpenedButNotSafelyClosed hWriter = new FileWriter(homepage, false); hWriter.write(homepageBuilder.toString()); } catch (IOException e) { @@ -148,4 +188,9 @@ public static String getHomepage(Activity activity) { return Constants.FILE + homepage; } + + public void load() { + executeOnExecutor(BrowserApp.getIOThread()); + } + } diff --git a/app/src/main/java/acr/browser/lightning/controller/BrowserController.java b/app/src/main/java/acr/browser/lightning/controller/BrowserController.java deleted file mode 100644 index 73c8e7fde..000000000 --- a/app/src/main/java/acr/browser/lightning/controller/BrowserController.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2014 A.C.R. Development - */ -package acr.browser.lightning.controller; - -import android.graphics.Bitmap; -import android.net.Uri; -import android.os.Message; -import android.view.View; -import android.webkit.ValueCallback; -import android.webkit.WebChromeClient.CustomViewCallback; -import android.webkit.WebView; - -import acr.browser.lightning.view.LightningView; - -public interface BrowserController { - - void updateUrl(String title, boolean shortUrl); - - void updateProgress(int n); - - void updateHistory(String title, String url); - - void openFileChooser(ValueCallback uploadMsg); - - void updateTabs(); - - void onLongPress(); - - void onShowCustomView(View view, CustomViewCallback callback); - - void onHideCustomView(); - - Bitmap getDefaultVideoPoster(); - - View getVideoLoadingProgressView(); - - void onCreateWindow(Message resultMsg); - - void onCloseWindow(LightningView view); - - void hideActionBar(); - - void showActionBar(); - - void longClickPage(String url); - - void openBookmarkPage(WebView view); - - void showFileChooser(ValueCallback filePathCallback); - - void closeEmptyTab(); - - boolean proxyIsNotReady(); - - // void updateBookmarkIndicator(String url); - -} diff --git a/app/src/main/java/acr/browser/lightning/controller/UIController.java b/app/src/main/java/acr/browser/lightning/controller/UIController.java new file mode 100644 index 000000000..e28ca136b --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/controller/UIController.java @@ -0,0 +1,67 @@ +/* + * Copyright 2014 A.C.R. Development + */ +package acr.browser.lightning.controller; + +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Message; +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.View; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient.CustomViewCallback; + +import acr.browser.lightning.activity.TabsManager; +import acr.browser.lightning.view.LightningView; + +public interface UIController { + + void changeToolbarBackground(@NonNull Bitmap favicon, @Nullable Drawable drawable); + + @ColorInt + int getUiColor(); + + boolean getUseDarkTheme(); + + void updateUrl(@Nullable String title, boolean shortUrl); + + void updateProgress(int n); + + void updateHistory(@Nullable String title, @NonNull String url); + + void openFileChooser(ValueCallback uploadMsg); + + void onShowCustomView(View view, CustomViewCallback callback); + + void onShowCustomView(View view, CustomViewCallback callback, int requestedOrienation); + + void onHideCustomView(); + + void onCreateWindow(Message resultMsg); + + void onCloseWindow(LightningView view); + + void hideActionBar(); + + void showActionBar(); + + void showFileChooser(ValueCallback filePathCallback); + + void closeEmptyTab(); + + void showCloseDialog(int position); + + void newTabClicked(); + + void setForwardButtonEnabled(boolean enabled); + + void setBackButtonEnabled(boolean enabled); + + void tabChanged(LightningView tab); + + TabsManager getTabModel(); + +} diff --git a/app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java b/app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java index 1c1b8c110..827c099b7 100644 --- a/app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java +++ b/app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java @@ -11,6 +11,9 @@ import java.util.ArrayList; import java.util.List; +import acr.browser.lightning.react.Action; +import acr.browser.lightning.react.Observable; +import acr.browser.lightning.react.Subscriber; import acr.browser.lightning.utils.Utils; public class BookmarkLocalSync { @@ -19,25 +22,29 @@ public class BookmarkLocalSync { private static final String STOCK_BOOKMARKS_CONTENT = "content://browser/bookmarks"; private static final String CHROME_BOOKMARKS_CONTENT = "content://com.android.chrome.browser/bookmarks"; + private static final String CHROME_BETA_BOOKMARKS_CONTENT = "content://com.chrome.beta.browser/bookmarks"; + private static final String CHROME_DEV_BOOKMARKS_CONTENT = "content://com.chrome.dev.browser/bookmarks"; private static final String COLUMN_TITLE = "title"; private static final String COLUMN_URL = "url"; private static final String COLUMN_BOOKMARK = "bookmark"; - private final Context mContext; + @NonNull private final Context mContext; - public BookmarkLocalSync(Context context) { + public enum Source { + STOCK, + CHROME_STABLE, + CHROME_BETA, + CHROME_DEV + } + + public BookmarkLocalSync(@NonNull Context context) { mContext = context; } - @NonNull - @WorkerThread - public List getBookmarksFromStockBrowser() { + public List getBookmarksFromContentUri(String contentUri) { List list = new ArrayList<>(); - if (!isStockSupported()) { - return list; - } - Cursor cursor = getStockCursor(); + Cursor cursor = getBrowserCursor(contentUri); try { if (cursor != null) { for (int n = 0; n < cursor.getColumnCount(); n++) { @@ -54,7 +61,9 @@ public List getBookmarksFromStockBrowser() { if (title == null || title.isEmpty()) { title = Utils.getDomainName(url); } - list.add(new HistoryItem(url, title)); + if (title != null) { + list.add(new HistoryItem(url, title)); + } } } } @@ -65,81 +74,133 @@ public List getBookmarksFromStockBrowser() { return list; } - @NonNull + @Nullable @WorkerThread - public List getBookmarksFromChrome() { - List list = new ArrayList<>(); - if (!isChromeSupported()) { - return list; - } - Cursor cursor = getStockCursor(); + private Cursor getBrowserCursor(String contentUri) { + Cursor cursor; + Uri uri = Uri.parse(contentUri); try { - if (cursor != null) { - for (int n = 0; n < cursor.getColumnCount(); n++) { - Log.d(TAG, cursor.getColumnName(n)); - } + cursor = mContext.getContentResolver().query(uri, + new String[]{COLUMN_URL, COLUMN_TITLE, COLUMN_BOOKMARK}, null, null, null); + } catch (IllegalArgumentException e) { + return null; + } + return cursor; + } - while (cursor.moveToNext()) { - if (cursor.getInt(2) == 1) { - String url = cursor.getString(0); - String title = cursor.getString(1); - if (url.isEmpty()) { - continue; - } - if (title == null || title.isEmpty()) { - title = Utils.getDomainName(url); - } - list.add(new HistoryItem(url, title)); - } + @NonNull + public Observable> getSupportedBrowsers() { + return Observable.create(new Action>() { + @Override + public void onSubscribe(@NonNull Subscriber> subscriber) { + List sources = new ArrayList<>(1); + if (isBrowserSupported(STOCK_BOOKMARKS_CONTENT)) { + sources.add(Source.STOCK); + } + if (isBrowserSupported(CHROME_BOOKMARKS_CONTENT)) { + sources.add(Source.CHROME_STABLE); + } + if (isBrowserSupported(CHROME_BETA_BOOKMARKS_CONTENT)) { + sources.add(Source.CHROME_BETA); } + if (isBrowserSupported(CHROME_DEV_BOOKMARKS_CONTENT)) { + sources.add(Source.CHROME_DEV); + } + subscriber.onNext(sources); + subscriber.onComplete(); } - } catch (Exception e) { - e.printStackTrace(); - } + }); + } + + private boolean isBrowserSupported(String contentUri) { + Cursor cursor = getBrowserCursor(contentUri); + boolean supported = cursor != null; Utils.close(cursor); - return list; + return supported; } + @NonNull @WorkerThread - public boolean isStockSupported() { - Cursor cursor = getStockCursor(); - Utils.close(cursor); - return cursor != null; + public List getBookmarksFromStockBrowser() { + return getBookmarksFromContentUri(STOCK_BOOKMARKS_CONTENT); } + @NonNull @WorkerThread - public boolean isChromeSupported() { - Cursor cursor = getChromeCursor(); - Utils.close(cursor); - return cursor != null; + public List getBookmarksFromChrome() { + return getBookmarksFromContentUri(CHROME_BOOKMARKS_CONTENT); + } + + @NonNull + @WorkerThread + public List getBookmarksFromChromeBeta() { + return getBookmarksFromContentUri(CHROME_BETA_BOOKMARKS_CONTENT); + } + + @NonNull + @WorkerThread + public List getBookmarksFromChromeDev() { + return getBookmarksFromContentUri(CHROME_DEV_BOOKMARKS_CONTENT); + } + + @WorkerThread + public boolean isBrowserImportSupported() { + Cursor chrome = getChromeCursor(); + Utils.close(chrome); + Cursor dev = getChromeDevCursor(); + Utils.close(dev); + Cursor beta = getChromeBetaCursor(); + Cursor stock = getStockCursor(); + Utils.close(stock); + return chrome != null || dev != null || beta != null || stock != null; + } + + @Nullable + @WorkerThread + private Cursor getChromeBetaCursor() { + return getBrowserCursor(CHROME_BETA_BOOKMARKS_CONTENT); + } + + @Nullable + @WorkerThread + private Cursor getChromeDevCursor() { + return getBrowserCursor(CHROME_DEV_BOOKMARKS_CONTENT); } @Nullable @WorkerThread private Cursor getChromeCursor() { - Cursor cursor; - Uri uri = Uri.parse(CHROME_BOOKMARKS_CONTENT); - try { - cursor = mContext.getContentResolver().query(uri, - new String[]{COLUMN_URL, COLUMN_TITLE, COLUMN_BOOKMARK}, null, null, null); - } catch (IllegalArgumentException e) { - return null; - } - return cursor; + return getBrowserCursor(CHROME_BOOKMARKS_CONTENT); } @Nullable @WorkerThread private Cursor getStockCursor() { - Cursor cursor; - Uri uri = Uri.parse(STOCK_BOOKMARKS_CONTENT); + return getBrowserCursor(STOCK_BOOKMARKS_CONTENT); + } + + public void printAllColumns() { + printColumns(CHROME_BETA_BOOKMARKS_CONTENT); + printColumns(CHROME_BOOKMARKS_CONTENT); + printColumns(CHROME_DEV_BOOKMARKS_CONTENT); + printColumns(STOCK_BOOKMARKS_CONTENT); + } + + public void printColumns(String contentProvider) { + Cursor cursor = null; + Log.e(TAG, contentProvider); + Uri uri = Uri.parse(contentProvider); try { - cursor = mContext.getContentResolver().query(uri, - new String[]{COLUMN_URL, COLUMN_TITLE, COLUMN_BOOKMARK}, null, null, null); - } catch (IllegalArgumentException e) { - return null; + cursor = mContext.getContentResolver().query(uri, null, null, null, null); + } catch (Exception e) { + Log.e(TAG, "Error Occurred", e); + } + if (cursor != null) { + for (int n = 0; n < cursor.getColumnCount(); n++) { + Log.d(TAG, cursor.getColumnName(n)); + } + cursor.close(); } - return cursor; } } diff --git a/app/src/main/java/acr/browser/lightning/database/BookmarkManager.java b/app/src/main/java/acr/browser/lightning/database/BookmarkManager.java index 3790146f9..60e61783f 100644 --- a/app/src/main/java/acr/browser/lightning/database/BookmarkManager.java +++ b/app/src/main/java/acr/browser/lightning/database/BookmarkManager.java @@ -32,6 +32,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import javax.inject.Inject; import javax.inject.Singleton; import acr.browser.lightning.R; @@ -49,29 +50,20 @@ public class BookmarkManager { private static final String ORDER = "order"; private static final String FILE_BOOKMARKS = "bookmarks.dat"; - private final String DEFAULT_BOOKMARK_TITLE; + @NonNull private final String DEFAULT_BOOKMARK_TITLE; private Map mBookmarksMap; - // private final List mBookmarkList = new ArrayList<>(); - private String mCurrentFolder = ""; - private final ExecutorService mExecutor; - private boolean mReady = false; - private final File mFilesDir; + @NonNull private String mCurrentFolder = ""; + @NonNull private final ExecutorService mExecutor; + private File mFilesDir; - public BookmarkManager(Context context) { + @Inject + public BookmarkManager(@NonNull Context context) { mExecutor = Executors.newSingleThreadExecutor(); - mFilesDir = context.getFilesDir(); DEFAULT_BOOKMARK_TITLE = context.getString(R.string.untitled); mExecutor.execute(new BookmarkInitializer(context)); } - /** - * @return true if the BookmarkManager was initialized, false otherwise - */ - public boolean isReady() { - return mReady; - } - /** * Look for bookmark using the url * @@ -97,6 +89,7 @@ public BookmarkInitializer(Context context) { @Override public void run() { synchronized (BookmarkManager.this) { + mFilesDir = mContext.getFilesDir(); final Map bookmarks = new HashMap<>(); final File bookmarksFile = new File(mFilesDir, FILE_BOOKMARKS); @@ -104,10 +97,12 @@ public void run() { InputStream inputStream = null; try { if (bookmarksFile.exists() && bookmarksFile.isFile()) { + //noinspection IOResourceOpenedButNotSafelyClosed inputStream = new FileInputStream(bookmarksFile); } else { inputStream = mContext.getResources().openRawResource(R.raw.default_bookmarks); } + //noinspection IOResourceOpenedButNotSafelyClosed bookmarksReader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = bookmarksReader.readLine()) != null) { @@ -132,7 +127,6 @@ public void run() { Utils.close(inputStream); } mBookmarksMap = bookmarks; - mReady = true; } } @@ -152,11 +146,12 @@ public BookmarksWriter(List bookmarks) { @Override public void run() { final File tempFile = new File(mFilesDir, - String.format("bm_%d.dat", System.currentTimeMillis())); + String.format(Locale.US, "bm_%d.dat", System.currentTimeMillis())); final File bookmarksFile = new File(mFilesDir, FILE_BOOKMARKS); boolean success = false; BufferedWriter bookmarkWriter = null; try { + //noinspection IOResourceOpenedButNotSafelyClosed bookmarkWriter = new BufferedWriter(new FileWriter(tempFile, false)); JSONObject object = new JSONObject(); for (HistoryItem item : mBookmarks) { @@ -168,7 +163,7 @@ public void run() { bookmarkWriter.newLine(); } success = true; - } catch (IOException | JSONException e) { + } catch (@NonNull IOException | JSONException e) { e.printStackTrace(); } finally { Utils.close(bookmarkWriter); @@ -176,6 +171,7 @@ public void run() { if (success) { // Overwrite the bookmarks file by renaming the temp file + //noinspection ResultOfMethodCallIgnored tempFile.renameTo(bookmarksFile); } } @@ -200,7 +196,7 @@ public boolean isBookmark(String url) { */ public synchronized boolean addBookmark(@NonNull HistoryItem item) { final String url = item.getUrl(); - if (url == null || mBookmarksMap.containsKey(url)) { + if (mBookmarksMap.containsKey(url)) { return false; } mBookmarksMap.put(url, item); @@ -213,13 +209,13 @@ public synchronized boolean addBookmark(@NonNull HistoryItem item) { * * @param list the list of HistoryItems to add to bookmarks */ - public synchronized void addBookmarkList(List list) { + public synchronized void addBookmarkList(@Nullable List list) { if (list == null || list.isEmpty()) { return; } for (HistoryItem item : list) { final String url = item.getUrl(); - if (url != null && !mBookmarksMap.containsKey(url)) { + if (!mBookmarksMap.containsKey(url)) { mBookmarksMap.put(url, item); } } @@ -232,7 +228,7 @@ public synchronized void addBookmarkList(List list) { * * @param deleteItem the bookmark item to delete */ - public synchronized boolean deleteBookmark(HistoryItem deleteItem) { + public synchronized boolean deleteBookmark(@Nullable HistoryItem deleteItem) { if (deleteItem == null || deleteItem.isFolder()) { return false; } @@ -286,13 +282,23 @@ public synchronized void deleteFolder(@NonNull String name) { mExecutor.execute(new BookmarksWriter(new LinkedList<>(mBookmarksMap.values()))); } + /** + * This method deletes ALL bookmarks created + * by the user. Use this method carefully and + * do not use it without explicit user consent. + */ + public synchronized void deleteAllBookmarks() { + mBookmarksMap = new HashMap<>(); + mExecutor.execute(new BookmarksWriter(new LinkedList<>(mBookmarksMap.values()))); + } + /** * This method edits a particular bookmark in the bookmark database * * @param oldItem This is the old item that you wish to edit * @param newItem This is the new item that will overwrite the old item */ - public synchronized void editBookmark(HistoryItem oldItem, HistoryItem newItem) { + public synchronized void editBookmark(@Nullable HistoryItem oldItem, @Nullable HistoryItem newItem) { if (oldItem == null || newItem == null || oldItem.isFolder()) { return; } @@ -317,7 +323,7 @@ public synchronized void editBookmark(HistoryItem oldItem, HistoryItem newItem) * This method exports the stored bookmarks to a text file in the device's * external download directory */ - public synchronized void exportBookmarks(Activity activity) { + public synchronized void exportBookmarks(@NonNull Activity activity) { List bookmarkList = getAllBookmarks(true); File bookmarksExport = new File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), @@ -331,6 +337,7 @@ public synchronized void exportBookmarks(Activity activity) { } BufferedWriter bookmarkWriter = null; try { + //noinspection IOResourceOpenedButNotSafelyClosed bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksExport, false)); JSONObject object = new JSONObject(); @@ -344,7 +351,7 @@ public synchronized void exportBookmarks(Activity activity) { } Utils.showSnackbar(activity, activity.getString(R.string.bookmark_export_path) + ' ' + bookmarksExport.getPath()); - } catch (IOException | JSONException e) { + } catch (@NonNull IOException | JSONException e) { e.printStackTrace(); } finally { Utils.close(bookmarkWriter); @@ -360,6 +367,7 @@ public synchronized void exportBookmarks(Activity activity) { * @param sort force to sort the returned bookmarkList * @return returns a list of bookmarks that can be sorted */ + @NonNull public synchronized List getAllBookmarks(boolean sort) { final List bookmarks = new ArrayList<>(mBookmarksMap.values()); if (sort) { @@ -377,8 +385,9 @@ public synchronized List getAllBookmarks(boolean sort) { * @param folder the name of the folder to retrieve bookmarks from * @return a list of bookmarks found in that folder */ - public synchronized List getBookmarksFromFolder(String folder, boolean sort) { - List bookmarks = new ArrayList<>(); + @NonNull + public synchronized List getBookmarksFromFolder(@Nullable String folder, boolean sort) { + List bookmarks = new ArrayList<>(1); if (folder == null || folder.isEmpty()) { bookmarks.addAll(getFolders(sort)); folder = ""; @@ -394,6 +403,36 @@ public synchronized List getBookmarksFromFolder(String folder, bool return bookmarks; } + /** + * Different from {@link #getBookmarksFromFolder(String, boolean)} only in + * that it doesn't affect the internal state of the bookmark manager which + * tracks the current folder used by the bookmark drawer. + *

+ * This method returns a list of bookmarks and folders located in the specified folder. + * This method should generally be used by the UI when it needs a list to display to the + * user as it returns a subset of all bookmarks and includes folders as well which are + * really 'fake' bookmarks. + * + * @param folder the name of the folder to retrieve bookmarks from + * @return a list of bookmarks found in that folder + */ + @NonNull + public synchronized List getBookmarksCopyFromFolder(@Nullable String folder, boolean sort) { + List bookmarks = new ArrayList<>(1); + if (folder == null || folder.isEmpty()) { + bookmarks.addAll(getFolders(sort)); + folder = ""; + } + for (HistoryItem item : mBookmarksMap.values()) { + if (item.getFolder().equals(folder)) + bookmarks.add(item); + } + if (sort) { + Collections.sort(bookmarks, new SortIgnoreCase()); + } + return bookmarks; + } + /** * Tells you if you are at the root folder or in a subfolder * @@ -408,24 +447,11 @@ public boolean isRootFolder() { * * @return the current folder */ + @Nullable public String getCurrentFolder() { return mCurrentFolder; } - /** - * Method is used internally for searching the bookmarks - * - * @return a sorted map of all bookmarks, useful for seeing if a bookmark exists - */ - private Set getBookmarkUrls(List list) { - Set set = new HashSet<>(); - for (HistoryItem item : mBookmarksMap.values()) { - if (!item.isFolder()) - set.add(item.getUrl()); - } - return set; - } - /** * This method returns a list of all folders. * Folders cannot be empty as they are generated from @@ -433,11 +459,12 @@ private Set getBookmarkUrls(List list) { * * @return a list of all folders */ + @NonNull private synchronized List getFolders(boolean sort) { final HashMap folders = new HashMap<>(); for (HistoryItem item : mBookmarksMap.values()) { final String folderName = item.getFolder(); - if (folderName != null && !folderName.isEmpty() && !folders.containsKey(folderName)) { + if (!folderName.isEmpty() && !folders.containsKey(folderName)) { final HistoryItem folder = new HistoryItem(); folder.setIsFolder(true); folder.setTitle(folderName); @@ -459,11 +486,12 @@ private synchronized List getFolders(boolean sort) { * * @return a list of folder title strings */ + @NonNull public synchronized List getFolderTitles() { final Set folders = new HashSet<>(); for (HistoryItem item : mBookmarksMap.values()) { final String folderName = item.getFolder(); - if (folderName != null && !folderName.isEmpty()) { + if (!folderName.isEmpty()) { folders.add(folderName); } } @@ -476,13 +504,14 @@ public synchronized List getFolderTitles() { * * @param file the file to attempt to import bookmarks from */ - public synchronized void importBookmarksFromFile(File file, Activity activity) { + public synchronized void importBookmarksFromFile(@Nullable File file, @NonNull Activity activity) { if (file == null) { return; } List list = new ArrayList<>(); BufferedReader bookmarksReader = null; try { + //noinspection IOResourceOpenedButNotSafelyClosed bookmarksReader = new BufferedReader(new FileReader(file)); String line; int number = 0; @@ -499,7 +528,7 @@ public synchronized void importBookmarksFromFile(File file, Activity activity) { addBookmarkList(list); String message = activity.getResources().getString(R.string.message_import); Utils.showSnackbar(activity, number + " " + message); - } catch (IOException | JSONException e) { + } catch (@NonNull IOException | JSONException e) { e.printStackTrace(); Utils.createInformativeDialog(activity, R.string.title_error, R.string.import_bookmark_error); } finally { @@ -507,29 +536,13 @@ public synchronized void importBookmarksFromFile(File file, Activity activity) { } } - /** - * find the index of a bookmark in a list using only its URL - * - * @param list the list to search - * @param url the url to compare - * @return returns the index of the bookmark or -1 if none was found - */ - public static int getIndexOfBookmark(final List list, final String url) { - for (int n = 0; n < list.size(); n++) { - if (list.get(n).getUrl().equals(url)) { - return n; - } - } - return -1; - } - /** * This class sorts bookmarks alphabetically, with folders coming after bookmarks */ - public static class SortIgnoreCase implements Comparator { + private static class SortIgnoreCase implements Comparator { - public int compare(HistoryItem o1, HistoryItem o2) { - if (o1 == null || o2 == null || o1.getTitle() == null || o2.getTitle() == null) { + public int compare(@Nullable HistoryItem o1, @Nullable HistoryItem o2) { + if (o1 == null || o2 == null) { return 0; } if (o1.isFolder() == o2.isFolder()) { diff --git a/app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java b/app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java index 82801c557..ecbcfd2cd 100644 --- a/app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java +++ b/app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java @@ -8,13 +8,19 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; + import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; +@Singleton public class HistoryDatabase extends SQLiteOpenHelper { // All Static variables @@ -33,27 +39,28 @@ public class HistoryDatabase extends SQLiteOpenHelper { private static final String KEY_TITLE = "title"; private static final String KEY_TIME_VISITED = "time"; - private SQLiteDatabase mDatabase; - - private static HistoryDatabase mInstance; - - private boolean mLock; + @Nullable private SQLiteDatabase mDatabase; - public static HistoryDatabase getInstance() { - if (mInstance == null || mInstance.isClosed()) { - mInstance = new HistoryDatabase(BrowserApp.getAppContext()); - } - return mInstance; + @Inject + public HistoryDatabase(@NonNull Context context) { + super(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION); + initialize(); } - private HistoryDatabase(Context context) { - super(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION); - mDatabase = this.getWritableDatabase(); + private void initialize() { + BrowserApp.getTaskThread().execute(new Runnable() { + @Override + public void run() { + synchronized (HistoryDatabase.this) { + mDatabase = HistoryDatabase.this.getWritableDatabase(); + } + } + }); } // Creating Tables @Override - public void onCreate(SQLiteDatabase db) { + public void onCreate(@NonNull SQLiteDatabase db) { String CREATE_HISTORY_TABLE = "CREATE TABLE " + TABLE_HISTORY + '(' + KEY_ID + " INTEGER PRIMARY KEY," + KEY_URL + " TEXT," + KEY_TITLE + " TEXT," + KEY_TIME_VISITED + " INTEGER" + ')'; @@ -62,78 +69,69 @@ public void onCreate(SQLiteDatabase db) { // Upgrading database @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + public void onUpgrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) { // Drop older table if it exists db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY); // Create tables again onCreate(db); } - public void deleteHistory() { + public synchronized void deleteHistory() { + mDatabase = openIfNecessary(); mDatabase.delete(TABLE_HISTORY, null, null); mDatabase.close(); mDatabase = this.getWritableDatabase(); } - public boolean isClosed() { - return mDatabase == null || !mDatabase.isOpen(); - } - @Override public synchronized void close() { - if (!mLock) { - if (mDatabase != null) { - mDatabase.close(); - mDatabase = null; - } + if (mDatabase != null) { + mDatabase.close(); + mDatabase = null; } super.close(); } - private void openIfNecessary() { - if (mDatabase == null) { + @NonNull + private SQLiteDatabase openIfNecessary() { + if (mDatabase == null || !mDatabase.isOpen()) { mDatabase = this.getWritableDatabase(); } + return mDatabase; } - public synchronized void deleteHistoryItem(String url) { - mLock = true; - openIfNecessary(); + public synchronized void deleteHistoryItem(@NonNull String url) { + mDatabase = openIfNecessary(); mDatabase.delete(TABLE_HISTORY, KEY_URL + " = ?", new String[]{url}); - mLock = false; } - public synchronized void visitHistoryItem(String url, String title) { - mLock = true; - openIfNecessary(); + public synchronized void visitHistoryItem(@NonNull String url, @Nullable String title) { + mDatabase = openIfNecessary(); ContentValues values = new ContentValues(); - values.put(KEY_TITLE, title); + values.put(KEY_TITLE, title == null ? "" : title); values.put(KEY_TIME_VISITED, System.currentTimeMillis()); Cursor q = mDatabase.query(false, TABLE_HISTORY, new String[]{KEY_URL}, KEY_URL + " = ?", new String[]{url}, null, null, null, "1"); if (q.getCount() > 0) { mDatabase.update(TABLE_HISTORY, values, KEY_URL + " = ?", new String[]{url}); } else { - addHistoryItem(new HistoryItem(url, title)); + addHistoryItem(new HistoryItem(url, title == null ? "" : title)); } q.close(); - mLock = false; } - private synchronized void addHistoryItem(HistoryItem item) { - mLock = true; - openIfNecessary(); + private synchronized void addHistoryItem(@NonNull HistoryItem item) { + mDatabase = openIfNecessary(); ContentValues values = new ContentValues(); values.put(KEY_URL, item.getUrl()); values.put(KEY_TITLE, item.getTitle()); values.put(KEY_TIME_VISITED, System.currentTimeMillis()); mDatabase.insert(TABLE_HISTORY, null, values); - mLock = false; } - String getHistoryItem(String url) { - mLock = true; - openIfNecessary(); + @Nullable + synchronized String getHistoryItem(@NonNull String url) { + mDatabase = openIfNecessary(); Cursor cursor = mDatabase.query(TABLE_HISTORY, new String[]{KEY_ID, KEY_URL, KEY_TITLE}, KEY_URL + " = ?", new String[]{url}, null, null, null, null); String m = null; @@ -143,14 +141,16 @@ String getHistoryItem(String url) { cursor.close(); } - mLock = false; return m; } - public List findItemsContaining(String search) { - mLock = true; - openIfNecessary(); + @NonNull + public synchronized List findItemsContaining(@Nullable String search) { + mDatabase = openIfNecessary(); List itemList = new ArrayList<>(5); + if (search == null) { + return itemList; + } String selectQuery = "SELECT * FROM " + TABLE_HISTORY + " WHERE " + KEY_TITLE + " LIKE '%" + search + "%' OR " + KEY_URL + " LIKE '%" + search + "%' " + "ORDER BY " + KEY_TIME_VISITED + " DESC LIMIT 5"; @@ -168,13 +168,12 @@ public List findItemsContaining(String search) { } while (cursor.moveToNext() && n < 5); } cursor.close(); - mLock = false; return itemList; } - public List getLastHundredItems() { - mLock = true; - openIfNecessary(); + @NonNull + public synchronized List getLastHundredItems() { + mDatabase = openIfNecessary(); List itemList = new ArrayList<>(100); String selectQuery = "SELECT * FROM " + TABLE_HISTORY + " ORDER BY " + KEY_TIME_VISITED + " DESC"; @@ -192,13 +191,12 @@ public List getLastHundredItems() { } while (cursor.moveToNext() && counter < 100); } cursor.close(); - mLock = false; return itemList; } - public List getAllHistoryItems() { - mLock = true; - openIfNecessary(); + @NonNull + public synchronized List getAllHistoryItems() { + mDatabase = openIfNecessary(); List itemList = new ArrayList<>(); String selectQuery = "SELECT * FROM " + TABLE_HISTORY + " ORDER BY " + KEY_TIME_VISITED + " DESC"; @@ -215,18 +213,15 @@ public List getAllHistoryItems() { } while (cursor.moveToNext()); } cursor.close(); - mLock = false; return itemList; } - public int getHistoryItemsCount() { - mLock = true; - openIfNecessary(); + public synchronized int getHistoryItemsCount() { + mDatabase = openIfNecessary(); String countQuery = "SELECT * FROM " + TABLE_HISTORY; Cursor cursor = mDatabase.rawQuery(countQuery, null); int n = cursor.getCount(); cursor.close(); - mLock = false; return n; } } diff --git a/app/src/main/java/acr/browser/lightning/database/HistoryItem.java b/app/src/main/java/acr/browser/lightning/database/HistoryItem.java index dcc8bc1fd..c0c662689 100644 --- a/app/src/main/java/acr/browser/lightning/database/HistoryItem.java +++ b/app/src/main/java/acr/browser/lightning/database/HistoryItem.java @@ -7,6 +7,8 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import acr.browser.lightning.utils.Preconditions; + public class HistoryItem implements Comparable { // private variables @@ -26,10 +28,9 @@ public class HistoryItem implements Comparable { private int mOrder = 0; private boolean mIsFolder = false; - // Empty constructor public HistoryItem() {} - public HistoryItem(HistoryItem item) { + public HistoryItem(@NonNull HistoryItem item) { this.mUrl = item.mUrl; this.mTitle = item.mTitle; this.mFolder = item.mFolder; @@ -37,15 +38,17 @@ public HistoryItem(HistoryItem item) { this.mIsFolder = item.mIsFolder; } - // constructor public HistoryItem(@NonNull String url, @NonNull String title) { + Preconditions.checkNonNull(url); + Preconditions.checkNonNull(title); this.mUrl = url; this.mTitle = title; this.mBitmap = null; } - // constructor public HistoryItem(@NonNull String url, @NonNull String title, int imageId) { + Preconditions.checkNonNull(url); + Preconditions.checkNonNull(title); this.mUrl = url; this.mTitle = title; this.mBitmap = null; @@ -64,7 +67,7 @@ public void setBitmap(Bitmap image) { mBitmap = image; } - public void setFolder(String folder) { + public void setFolder(@Nullable String folder) { mFolder = (folder == null) ? "" : folder; } @@ -76,31 +79,31 @@ public int getOrder() { return mOrder; } + @NonNull public String getFolder() { return mFolder; } + @Nullable public Bitmap getBitmap() { return mBitmap; } - // getting name + @NonNull public String getUrl() { return this.mUrl; } - // setting name - public void setUrl(String url) { + public void setUrl(@Nullable String url) { this.mUrl = (url == null) ? "" : url; } - // getting phone number + @NonNull public String getTitle() { return this.mTitle; } - // setting phone number - public void setTitle(String title) { + public void setTitle(@Nullable String title) { this.mTitle = (title == null) ? "" : title; } @@ -112,6 +115,7 @@ public boolean isFolder() { return mIsFolder; } + @NonNull @Override public String toString() { return mTitle; @@ -127,7 +131,7 @@ public int compareTo(@NonNull HistoryItem another) { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (this == object) return true; if (object == null) return false; diff --git a/app/src/main/java/acr/browser/lightning/dialog/BookmarksDialogBuilder.java b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java similarity index 51% rename from app/src/main/java/acr/browser/lightning/dialog/BookmarksDialogBuilder.java rename to app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java index ec8282c9e..25279f068 100644 --- a/app/src/main/java/acr/browser/lightning/dialog/BookmarksDialogBuilder.java +++ b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java @@ -1,8 +1,12 @@ package acr.browser.lightning.dialog; +import android.app.Activity; +import android.content.ClipData; +import android.content.ClipboardManager; import android.content.Context; import android.content.DialogInterface; import android.net.Uri; +import android.support.annotation.NonNull; import android.support.v7.app.AlertDialog; import android.view.View; import android.widget.ArrayAdapter; @@ -19,69 +23,75 @@ import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.bus.BookmarkEvents; +import acr.browser.lightning.bus.BrowserEvents; +import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.database.BookmarkManager; +import acr.browser.lightning.database.HistoryDatabase; import acr.browser.lightning.database.HistoryItem; +import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.Utils; /** + * TODO Rename this class it doesn't build dialogs only for bookmarks + *

* Created by Stefano Pacifici on 02/09/15, based on Anthony C. Restaino's code. */ -public class BookmarksDialogBuilder { +public class LightningDialogBuilder { - @Inject - BookmarkManager bookmarkManager; - - @Inject - Bus eventBus; + @Inject BookmarkManager mBookmarkManager; + @Inject PreferenceManager mPreferenceManager; + @Inject HistoryDatabase mHistoryDatabase; + @Inject Bus mEventBus; @Inject - public BookmarksDialogBuilder() { + public LightningDialogBuilder() { BrowserApp.getAppComponent().inject(this); } /** * Show the appropriated dialog for the long pressed link. It means that we try to understand * if the link is relative to a bookmark or is just a folder. - * @param context used to show the dialog - * @param url the long pressed url + * + * @param context used to show the dialog + * @param url the long pressed url */ - public void showLongPressedDialogForUrl(final Context context, final String url) { + public void showLongPressedDialogForBookmarkUrl(@NonNull final Context context, @NonNull final String url) { final HistoryItem item; - if (url.startsWith(Constants.FILE) && url.endsWith(Constants.BOOKMARKS_FILENAME)) { + if (url.startsWith(Constants.FILE) && url.endsWith(BookmarkPage.FILENAME)) { // TODO hacky, make a better bookmark mechanism in the future final Uri uri = Uri.parse(url); final String filename = uri.getLastPathSegment(); - final String folderTitle = filename.substring(0, filename.length() - Constants.BOOKMARKS_FILENAME.length() - 1); + final String folderTitle = filename.substring(0, filename.length() - BookmarkPage.FILENAME.length() - 1); item = new HistoryItem(); item.setIsFolder(true); item.setTitle(folderTitle); item.setImageId(R.drawable.ic_folder); item.setUrl(Constants.FOLDER + folderTitle); } else { - item = bookmarkManager.findBookmarkForUrl(url); + item = mBookmarkManager.findBookmarkForUrl(url); } if (item != null) { if (item.isFolder()) { showBookmarkFolderLongPressedDialog(context, item); } else { - showLongPressedDialogForUrl(context, item); + showLongPressedDialogForBookmarkUrl(context, item); } } } - public void showLongPressedDialogForUrl(final Context context, final HistoryItem item) { + public void showLongPressedDialogForBookmarkUrl(@NonNull final Context context, @NonNull final HistoryItem item) { final DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: - eventBus.post(new BookmarkEvents.AsNewTab(item)); + mEventBus.post(new BrowserEvents.OpenUrlInNewTab(item.getUrl())); break; case DialogInterface.BUTTON_NEGATIVE: - if (bookmarkManager.deleteBookmark(item)) { - eventBus.post(new BookmarkEvents.Deleted(item)); + if (mBookmarkManager.deleteBookmark(item)) { + mEventBus.post(new BookmarkEvents.Deleted(item)); } break; case DialogInterface.BUTTON_NEUTRAL: @@ -101,7 +111,7 @@ public void onClick(DialogInterface dialog, int which) { .show(); } - private void showEditBookmarkDialog(final Context context, final HistoryItem item) { + private void showEditBookmarkDialog(@NonNull final Context context, @NonNull final HistoryItem item) { final AlertDialog.Builder editBookmarkDialog = new AlertDialog.Builder(context); editBookmarkDialog.setTitle(R.string.title_edit_bookmark); final View dialogLayout = View.inflate(context, R.layout.dialog_edit_bookmark, null); @@ -113,7 +123,7 @@ private void showEditBookmarkDialog(final Context context, final HistoryItem ite (AutoCompleteTextView) dialogLayout.findViewById(R.id.bookmark_folder); getFolder.setHint(R.string.folder); getFolder.setText(item.getFolder()); - final List folders = bookmarkManager.getFolderTitles(); + final List folders = mBookmarkManager.getFolderTitles(); final ArrayAdapter suggestionsAdapter = new ArrayAdapter<>(context, android.R.layout.simple_dropdown_item_1line, folders); getFolder.setThreshold(1); @@ -129,14 +139,14 @@ public void onClick(DialogInterface dialog, int which) { editedItem.setUrl(getUrl.getText().toString()); editedItem.setUrl(getUrl.getText().toString()); editedItem.setFolder(getFolder.getText().toString()); - bookmarkManager.editBookmark(item, editedItem); - eventBus.post(new BookmarkEvents.BookmarkChanged(item, editedItem)); + mBookmarkManager.editBookmark(item, editedItem); + mEventBus.post(new BookmarkEvents.BookmarkChanged(item, editedItem)); } }); editBookmarkDialog.show(); } - public void showBookmarkFolderLongPressedDialog(final Context context, final HistoryItem item) { + public void showBookmarkFolderLongPressedDialog(@NonNull final Context context, @NonNull final HistoryItem item) { // assert item.isFolder(); final DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @@ -148,9 +158,9 @@ public void onClick(DialogInterface dialog, int which) { break; case DialogInterface.BUTTON_NEGATIVE: - bookmarkManager.deleteFolder(item.getTitle()); + mBookmarkManager.deleteFolder(item.getTitle()); // setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), false); - eventBus.post(new BookmarkEvents.Deleted(item)); + mEventBus.post(new BookmarkEvents.Deleted(item)); break; } } @@ -165,7 +175,7 @@ public void onClick(DialogInterface dialog, int which) { .show(); } - private void showRenameFolderDialog(final Context context, final HistoryItem item) { + private void showRenameFolderDialog(@NonNull final Context context, @NonNull final HistoryItem item) { // assert item.isFolder(); final AlertDialog.Builder editFolderDialog = new AlertDialog.Builder(context); editFolderDialog.setTitle(R.string.title_rename_folder); @@ -191,10 +201,105 @@ public void onClick(DialogInterface dialog, int which) { editedItem.setUrl(Constants.FOLDER + newTitle); editedItem.setFolder(item.getFolder()); editedItem.setIsFolder(true); - bookmarkManager.renameFolder(oldTitle, newTitle); - eventBus.post(new BookmarkEvents.BookmarkChanged(item, editedItem)); + mBookmarkManager.renameFolder(oldTitle, newTitle); + mEventBus.post(new BookmarkEvents.BookmarkChanged(item, editedItem)); } }); editFolderDialog.show(); } + + public void showLongPressedHistoryLinkDialog(final Context context, @NonNull final String url) { + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + mEventBus.post(new BrowserEvents.OpenUrlInNewTab(url)); + break; + case DialogInterface.BUTTON_NEGATIVE: + mHistoryDatabase.deleteHistoryItem(url); + // openHistory(); + mEventBus.post(new BrowserEvents.OpenHistoryInCurrentTab()); + break; + case DialogInterface.BUTTON_NEUTRAL: + mEventBus.post(new BrowserEvents.OpenUrlInCurrentTab(url)); + break; + default: + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.action_history) + .setMessage(R.string.dialog_history_long_press) + .setCancelable(true) + .setPositiveButton(R.string.action_new_tab, dialogClickListener) + .setNegativeButton(R.string.action_delete, dialogClickListener) + .setNeutralButton(R.string.action_open, dialogClickListener) + .show(); + } + + // TODO There should be a way in which we do not need an activity reference to dowload a file + public void showLongPressImageDialog(@NonNull final Activity activity, @NonNull final String url, + @NonNull final String userAgent) { + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + mEventBus.post(new BrowserEvents.OpenUrlInNewTab(url)); + break; + case DialogInterface.BUTTON_NEGATIVE: + mEventBus.post(new BrowserEvents.OpenUrlInCurrentTab(url)); + break; + case DialogInterface.BUTTON_NEUTRAL: + Utils.downloadFile(activity, mPreferenceManager, url, userAgent, "attachment"); + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(url.replace(Constants.HTTP, "")) + .setCancelable(true) + .setMessage(R.string.dialog_image) + .setPositiveButton(R.string.action_new_tab, dialogClickListener) + .setNegativeButton(R.string.action_open, dialogClickListener) + .setNeutralButton(R.string.action_download, dialogClickListener) + .show(); + } + + public void showLongPressLinkDialog(@NonNull final Context context, final String url) { + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + mEventBus.post(new BrowserEvents.OpenUrlInNewTab(url)); + break; + + case DialogInterface.BUTTON_NEGATIVE: + mEventBus.post(new BrowserEvents.OpenUrlInCurrentTab(url)); + break; + + case DialogInterface.BUTTON_NEUTRAL: + ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("label", url); + clipboard.setPrimaryClip(clip); + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(context); // dialog + builder.setTitle(url) + .setCancelable(true) + .setMessage(R.string.dialog_link) + .setPositiveButton(R.string.action_new_tab, dialogClickListener) + .setNegativeButton(R.string.action_open, dialogClickListener) + .setNeutralButton(R.string.action_copy, dialogClickListener) + .show(); + } + } diff --git a/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java b/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java index 82d25aaf3..03350fbe5 100644 --- a/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java +++ b/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java @@ -3,29 +3,35 @@ */ package acr.browser.lightning.download; -import android.app.Activity; import android.app.DownloadManager; import android.content.ActivityNotFoundException; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; +import android.os.Build; import android.os.Environment; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.text.TextUtils; import android.util.Log; import android.webkit.CookieManager; import android.webkit.URLUtil; +import com.squareup.otto.Bus; + import java.io.File; import java.io.IOException; +import acr.browser.lightning.BuildConfig; import acr.browser.lightning.R; +import acr.browser.lightning.activity.MainActivity; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.bus.BrowserEvents; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.preference.PreferenceManager; -import acr.browser.lightning.utils.Utils; /** * Handle download requests @@ -44,14 +50,14 @@ public class DownloadHandler { * Notify the host application a download should be done, or that the data * should be streamed if a streaming viewer is available. * - * @param activity Activity requesting the download. + * @param context The context in which the download was requested. * @param url The full url to the content that should be downloaded * @param userAgent User agent of the downloading application. * @param contentDisposition Content-disposition http header, if present. * @param mimetype The mimetype of the content reported by the server */ - public static void onDownloadStart(Activity activity, String url, String userAgent, - String contentDisposition, String mimetype) { + public static void onDownloadStart(@NonNull Context context, @NonNull PreferenceManager manager, String url, String userAgent, + @Nullable String contentDisposition, String mimetype) { // if we're dealing wih A/V content that's not explicitly marked // for download, check if it's streamable. if (contentDisposition == null @@ -61,18 +67,22 @@ public static void onDownloadStart(Activity activity, String url, String userAge Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse(url), mimetype); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - ResolveInfo info = activity.getPackageManager().resolveActivity(intent, + intent.addCategory(Intent.CATEGORY_BROWSABLE); + intent.setComponent(null); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + intent.setSelector(null); + } + ResolveInfo info = context.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); if (info != null) { - ComponentName myName = activity.getComponentName(); // If we resolved to ourselves, we don't want to attempt to // load the url only to try and download it again. - if (!myName.getPackageName().equals(info.activityInfo.packageName) - || !myName.getClassName().equals(info.activityInfo.name)) { + if (BuildConfig.APPLICATION_ID.equals(info.activityInfo.packageName) + || MainActivity.class.getName().equals(info.activityInfo.name)) { // someone (other than us) knows how to handle this mime // type with this scheme, don't download. try { - activity.startActivity(intent); + context.startActivity(intent); return; } catch (ActivityNotFoundException ex) { // Best behavior is to fall back to a download in this @@ -81,14 +91,14 @@ public static void onDownloadStart(Activity activity, String url, String userAge } } } - onDownloadStartNoStream(activity, url, userAgent, contentDisposition, mimetype - ); + onDownloadStartNoStream(context, manager, url, userAgent, contentDisposition, mimetype); } // This is to work around the fact that java.net.URI throws Exceptions // instead of just encoding URL's properly // Helper method for onDownloadStartNoStream - private static String encodePath(String path) { + @NonNull + private static String encodePath(@NonNull String path) { char[] chars = path.toCharArray(); boolean needed = false; @@ -119,17 +129,18 @@ private static String encodePath(String path) { * Notify the host application a download should be done, even if there is a * streaming viewer available for thise type. * - * @param activity Activity requesting the download. + * @param context The context in which the download is requested. * @param url The full url to the content that should be downloaded * @param userAgent User agent of the downloading application. * @param contentDisposition Content-disposition http header, if present. * @param mimetype The mimetype of the content reported by the server */ /* package */ - private static void onDownloadStartNoStream(final Activity activity, String url, String userAgent, - String contentDisposition, String mimetype) { - - String filename = URLUtil.guessFileName(url, contentDisposition, mimetype); + private static void onDownloadStartNoStream(@NonNull final Context context, @NonNull PreferenceManager preferences, + String url, String userAgent, + String contentDisposition, @Nullable String mimetype) { + final Bus eventBus = BrowserApp.getBus(context); + final String filename = URLUtil.guessFileName(url, contentDisposition, mimetype); // Check to see if we have an SDCard String status = Environment.getExternalStorageState(); @@ -139,14 +150,14 @@ private static void onDownloadStartNoStream(final Activity activity, String url, // Check to see if the SDCard is busy, same as the music app if (status.equals(Environment.MEDIA_SHARED)) { - msg = activity.getString(R.string.download_sdcard_busy_dlg_msg); + msg = context.getString(R.string.download_sdcard_busy_dlg_msg); title = R.string.download_sdcard_busy_dlg_title; } else { - msg = activity.getString(R.string.download_no_sdcard_dlg_msg, filename); + msg = context.getString(R.string.download_no_sdcard_dlg_msg); title = R.string.download_no_sdcard_dlg_title; } - new AlertDialog.Builder(activity).setTitle(title) + new AlertDialog.Builder(context).setTitle(title) .setIcon(android.R.drawable.ic_dialog_alert).setMessage(msg) .setPositiveButton(R.string.action_ok, null).show(); return; @@ -162,7 +173,7 @@ private static void onDownloadStartNoStream(final Activity activity, String url, // This only happens for very bad urls, we want to catch the // exception here Log.e(TAG, "Exception while trying to parse url '" + url + '\'', e); - Utils.showSnackbar(activity, R.string.problem_download); + eventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.problem_download)); return; } @@ -172,7 +183,7 @@ private static void onDownloadStartNoStream(final Activity activity, String url, try { request = new DownloadManager.Request(uri); } catch (IllegalArgumentException e) { - Utils.showSnackbar(activity, R.string.cannot_download); + eventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.cannot_download)); return; } request.setMimeType(mimetype); @@ -180,26 +191,20 @@ private static void onDownloadStartNoStream(final Activity activity, String url, // or, should it be set to one of several Environment.DIRECTORY* dirs // depending on mimetype? - String location = PreferenceManager.getInstance().getDownloadDirectory(); + String location = preferences.getDownloadDirectory(); Uri downloadFolder; - if (location != null) { - location = addNecessarySlashes(location); - downloadFolder = Uri.parse(location); - } else { - location = addNecessarySlashes(DEFAULT_DOWNLOAD_PATH); - downloadFolder = Uri.parse(location); - PreferenceManager.getInstance().setDownloadDirectory(location); - } + location = addNecessarySlashes(location); + downloadFolder = Uri.parse(location); File dir = new File(downloadFolder.getPath()); if (!dir.isDirectory() && !dir.mkdirs()) { // Cannot make the directory - Utils.showSnackbar(activity, R.string.problem_location_download); + eventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.problem_location_download)); return; } if (!isWriteAccessAvailable(downloadFolder)) { - Utils.showSnackbar(activity, R.string.problem_location_download); + eventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.problem_location_download)); return; } request.setDestinationUri(Uri.parse(Constants.FILE + location + filename)); @@ -214,32 +219,30 @@ private static void onDownloadStartNoStream(final Activity activity, String url, request.addRequestHeader(COOKIE_REQUEST_HEADER, cookies); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); if (mimetype == null) { + Log.d(TAG, "Mimetype is null"); if (TextUtils.isEmpty(addressString)) { return; } // We must have long pressed on a link or image to download it. We // are not sure of the mimetype in this case, so do a head request - new FetchUrlMimeType(activity, request, addressString, cookies, userAgent).start(); + new FetchUrlMimeType(context, request, addressString, cookies, userAgent).start(); } else { - final DownloadManager manager = (DownloadManager) activity + Log.d(TAG, "Valid mimetype, attempting to download"); + final DownloadManager manager = (DownloadManager) context .getSystemService(Context.DOWNLOAD_SERVICE); - new Thread() { - @Override - public void run() { - try { - manager.enqueue(request); - } catch (IllegalArgumentException e) { - // Probably got a bad URL or something - e.printStackTrace(); - Utils.showSnackbar(activity, R.string.cannot_download); - } catch (SecurityException e) { - // TODO write a download utility that downloads files rather than rely on the system - // because the system can only handle Environment.getExternal... as a path - Utils.showSnackbar(activity, R.string.problem_location_download); - } - } - }.start(); - Utils.showSnackbar(activity, activity.getString(R.string.download_pending) + ' ' + filename); + try { + manager.enqueue(request); + } catch (IllegalArgumentException e) { + // Probably got a bad URL or something + Log.e(TAG, "Unable to enqueue request", e); + eventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.cannot_download)); + } catch (SecurityException e) { + // TODO write a download utility that downloads files rather than rely on the system + // because the system can only handle Environment.getExternal... as a path + eventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.problem_location_download)); + } + eventBus.post(new BrowserEvents.ShowSnackBarMessage( + context.getString(R.string.download_pending) + ' ' + filename)); } } @@ -255,7 +258,7 @@ public void run() { * @return returns true if the directory can be written to or is in a directory that can * be written to. false if there is no write access. */ - public static boolean isWriteAccessAvailable(String directory) { + public static boolean isWriteAccessAvailable(@Nullable String directory) { if (directory == null || directory.isEmpty()) { return false; } @@ -266,6 +269,7 @@ public static boolean isWriteAccessAvailable(String directory) { if (!file.exists()) { try { if (file.createNewFile()) { + //noinspection ResultOfMethodCallIgnored file.delete(); } return true; @@ -286,34 +290,38 @@ public static boolean isWriteAccessAvailable(String directory) { * @param directory the directory to find the first existent parent * @return the first existent parent */ - private static String getFirstRealParentDirectory(String directory) { - if (directory == null || directory.isEmpty()) { - return "/"; - } - directory = addNecessarySlashes(directory); - File file = new File(directory); - if (!file.isDirectory()) { - int indexSlash = directory.lastIndexOf('/'); - if (indexSlash > 0) { - String parent = directory.substring(0, indexSlash); - int previousIndex = parent.lastIndexOf('/'); - if (previousIndex > 0) { - return getFirstRealParentDirectory(parent.substring(0, previousIndex)); + @Nullable + private static String getFirstRealParentDirectory(@Nullable String directory) { + while (true) { + if (directory == null || directory.isEmpty()) { + return "/"; + } + directory = addNecessarySlashes(directory); + File file = new File(directory); + if (!file.isDirectory()) { + int indexSlash = directory.lastIndexOf('/'); + if (indexSlash > 0) { + String parent = directory.substring(0, indexSlash); + int previousIndex = parent.lastIndexOf('/'); + if (previousIndex > 0) { + directory = parent.substring(0, previousIndex); + } else { + return "/"; + } } else { return "/"; } } else { - return "/"; + return directory; } - } else { - return directory; } } - private static boolean isWriteAccessAvailable(Uri fileUri) { + private static boolean isWriteAccessAvailable(@NonNull Uri fileUri) { File file = new File(fileUri.getPath()); try { if (file.createNewFile()) { + //noinspection ResultOfMethodCallIgnored file.delete(); } return true; @@ -322,7 +330,8 @@ private static boolean isWriteAccessAvailable(Uri fileUri) { } } - public static String addNecessarySlashes(String originalPath) { + @NonNull + public static String addNecessarySlashes(@Nullable String originalPath) { if (originalPath == null || originalPath.length() == 0) { return "/"; } diff --git a/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java b/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java index 39da66f66..de9d7f648 100644 --- a/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java +++ b/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java @@ -3,19 +3,24 @@ */ package acr.browser.lightning.download; -import android.app.Activity; import android.app.DownloadManager; import android.content.Context; import android.os.Environment; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.NonNull; import android.webkit.MimeTypeMap; import android.webkit.URLUtil; +import com.squareup.otto.Bus; + import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import acr.browser.lightning.R; -import acr.browser.lightning.utils.Utils; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.bus.BrowserEvents; /** * This class is used to pull down the http headers of a given URL so that we @@ -27,7 +32,7 @@ */ class FetchUrlMimeType extends Thread { - private final Activity mActivity; + private final Context mContext; private final DownloadManager.Request mRequest; @@ -37,9 +42,9 @@ class FetchUrlMimeType extends Thread { private final String mUserAgent; - public FetchUrlMimeType(Activity activity, DownloadManager.Request request, String uri, + public FetchUrlMimeType(Context context, DownloadManager.Request request, String uri, String cookies, String userAgent) { - mActivity = activity; + mContext = context; mRequest = request; mUri = uri; mCookies = cookies; @@ -50,6 +55,7 @@ public FetchUrlMimeType(Activity activity, DownloadManager.Request request, Stri public void run() { // User agent is likely to be null, though the AndroidHttpClient // seems ok with that. + final Bus eventBus = BrowserApp.getBus(mContext); String mimeType = null; String contentDisposition = null; HttpURLConnection connection = null; @@ -78,7 +84,7 @@ public void run() { contentDisposition = contentDispositionHeader; } } - } catch (IllegalArgumentException | IOException ex) { + } catch (@NonNull IllegalArgumentException | IOException ex) { if (connection != null) connection.disconnect(); } finally { @@ -101,9 +107,16 @@ public void run() { } // Start the download - DownloadManager manager = (DownloadManager) mActivity + DownloadManager manager = (DownloadManager) mContext .getSystemService(Context.DOWNLOAD_SERVICE); manager.enqueue(mRequest); - Utils.showSnackbar(mActivity, mActivity.getString(R.string.download_pending) + ' ' + filename); + Handler handler = new Handler(Looper.getMainLooper()); + final String file = filename; + handler.post(new Runnable() { + @Override + public void run() { + eventBus.post(new BrowserEvents.ShowSnackBarMessage(mContext.getString(R.string.download_pending) + ' ' + file)); + } + }); } } diff --git a/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java b/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java index 72f3ab8cf..946e71f30 100644 --- a/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java +++ b/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java @@ -3,6 +3,7 @@ */ package acr.browser.lightning.download; +import android.Manifest; import android.app.Activity; import android.content.DialogInterface; import android.support.v7.app.AlertDialog; @@ -11,43 +12,64 @@ import android.webkit.URLUtil; import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.preference.PreferenceManager; + +import com.anthonycr.grant.PermissionsManager; +import com.anthonycr.grant.PermissionsResultAction; + +import javax.inject.Inject; public class LightningDownloadListener implements DownloadListener { private final Activity mActivity; - public LightningDownloadListener(Activity activity) { - mActivity = activity; + @Inject PreferenceManager mPreferenceManager; + + public LightningDownloadListener(Activity context) { + BrowserApp.getAppComponent().inject(this); + mActivity = context; } @Override public void onDownloadStart(final String url, final String userAgent, - final String contentDisposition, final String mimetype, long contentLength) { - String fileName = URLUtil.guessFileName(url, contentDisposition, mimetype); - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - DownloadHandler.onDownloadStart(mActivity, url, userAgent, - contentDisposition, mimetype); - break; - - case DialogInterface.BUTTON_NEGATIVE: - break; - } - } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); // dialog - builder.setTitle(fileName) - .setMessage(mActivity.getResources().getString(R.string.dialog_download)) - .setPositiveButton(mActivity.getResources().getString(R.string.action_download), - dialogClickListener) - .setNegativeButton(mActivity.getResources().getString(R.string.action_cancel), - dialogClickListener).show(); - Log.i(Constants.TAG, "Downloading" + fileName); + final String contentDisposition, final String mimetype, long contentLength) { + PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(mActivity, + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, + new PermissionsResultAction() { + @Override + public void onGranted() { + String fileName = URLUtil.guessFileName(url, contentDisposition, mimetype); + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + DownloadHandler.onDownloadStart(mActivity, mPreferenceManager, url, userAgent, + contentDisposition, mimetype); + break; + + case DialogInterface.BUTTON_NEGATIVE: + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); // dialog + builder.setTitle(fileName) + .setMessage(mActivity.getResources().getString(R.string.dialog_download)) + .setPositiveButton(mActivity.getResources().getString(R.string.action_download), + dialogClickListener) + .setNegativeButton(mActivity.getResources().getString(R.string.action_cancel), + dialogClickListener).show(); + Log.i(Constants.TAG, "Downloading" + fileName); + } + @Override + public void onDenied(String permission) { + //TODO show message + } + }); } } diff --git a/app/src/main/java/acr/browser/lightning/download/WebAddress.java b/app/src/main/java/acr/browser/lightning/download/WebAddress.java index 96852a660..c50bf2561 100644 --- a/app/src/main/java/acr/browser/lightning/download/WebAddress.java +++ b/app/src/main/java/acr/browser/lightning/download/WebAddress.java @@ -4,6 +4,7 @@ package acr.browser.lightning.download; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import java.util.Locale; import java.util.regex.Matcher; @@ -35,17 +36,17 @@ class WebAddress { private static final int MATCH_GROUP_PORT = 4; private static final int MATCH_GROUP_PATH = 5; private static final Pattern sAddressPattern = Pattern.compile( - /* scheme */"(?:(http|https|file)\\:\\/\\/)?" + - /* authority */"(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" + + /* scheme */"(?:(http|https|file)://)?" + + /* authority */"(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?::[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" + /* host */"([" + GOOD_IRI_CHAR + "%_-][" + GOOD_IRI_CHAR + "%_\\.-]*|\\[[0-9a-fA-F:\\.]+\\])?" + - /* port */"(?:\\:([0-9]*))?" + - /* path */"(\\/?[^#]*)?" + + /* port */"(?::([0-9]*))?" + + /* path */"(/?[^#]*)?" + /* anchor */".*", Pattern.CASE_INSENSITIVE); /** * Parses given URI-like string. */ - public WebAddress(String address) throws IllegalArgumentException { + public WebAddress(@Nullable String address) throws IllegalArgumentException { if (address == null) { throw new IllegalArgumentException("address can't be null"); @@ -113,6 +114,7 @@ public WebAddress(String address) throws IllegalArgumentException { } } + @NonNull @Override public String toString() { diff --git a/app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java index 4f4b7a657..2b4fcd5d5 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java @@ -8,7 +8,7 @@ import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceFragment; +import android.support.annotation.NonNull; import android.support.v7.app.AlertDialog; import java.util.Arrays; @@ -16,9 +16,8 @@ import acr.browser.lightning.R; import acr.browser.lightning.constant.Constants; -import acr.browser.lightning.preference.PreferenceManager; -public class AdvancedSettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { +public class AdvancedSettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { private static final String SETTINGS_NEWWINDOW = "allow_new_window"; private static final String SETTINGS_ENABLECOOKIES = "allow_cookies"; @@ -29,7 +28,6 @@ public class AdvancedSettingsFragment extends PreferenceFragment implements Pref private static final String SETTINGS_TEXTENCODING = "text_encoding"; private Activity mActivity; - private PreferenceManager mPreferences; private CheckBoxPreference cbAllowPopups, cbenablecookies, cbcookiesInkognito, cbrestoreTabs; private Preference renderingmode, urlcontent, textEncoding; private CharSequence[] mUrlOptions; @@ -46,8 +44,6 @@ public void onCreate(Bundle savedInstanceState) { } private void initPrefs() { - // mPreferences storage - mPreferences = PreferenceManager.getInstance(); renderingmode = findPreference(SETTINGS_RENDERINGMODE); textEncoding = findPreference(SETTINGS_TEXTENCODING); @@ -65,7 +61,7 @@ private void initPrefs() { cbcookiesInkognito.setOnPreferenceChangeListener(this); cbrestoreTabs.setOnPreferenceChangeListener(this); - switch (mPreferences.getRenderingMode()) { + switch (mPreferenceManager.getRenderingMode()) { case 0: renderingmode.setSummary(getString(R.string.name_normal)); break; @@ -78,22 +74,25 @@ private void initPrefs() { case 3: renderingmode.setSummary(getString(R.string.name_inverted_grayscale)); break; + case 4: + renderingmode.setSummary(getString(R.string.name_increase_contrast)); + break; } - textEncoding.setSummary(mPreferences.getTextEncoding()); + textEncoding.setSummary(mPreferenceManager.getTextEncoding()); mUrlOptions = getResources().getStringArray(R.array.url_content_array); - int option = mPreferences.getUrlBoxContentChoice(); + int option = mPreferenceManager.getUrlBoxContentChoice(); urlcontent.setSummary(mUrlOptions[option]); - cbAllowPopups.setChecked(mPreferences.getPopupsEnabled()); - cbenablecookies.setChecked(mPreferences.getCookiesEnabled()); - cbcookiesInkognito.setChecked(mPreferences.getIncognitoCookiesEnabled()); - cbrestoreTabs.setChecked(mPreferences.getRestoreLostTabsEnabled()); + cbAllowPopups.setChecked(mPreferenceManager.getPopupsEnabled()); + cbenablecookies.setChecked(mPreferenceManager.getCookiesEnabled()); + cbcookiesInkognito.setChecked(mPreferenceManager.getIncognitoCookiesEnabled()); + cbrestoreTabs.setChecked(mPreferenceManager.getRestoreLostTabsEnabled()); } @Override - public boolean onPreferenceClick(Preference preference) { + public boolean onPreferenceClick(@NonNull Preference preference) { switch (preference.getKey()) { case SETTINGS_RENDERINGMODE: renderPicker(); @@ -110,23 +109,23 @@ public boolean onPreferenceClick(Preference preference) { } @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { + public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) { // switch preferences switch (preference.getKey()) { case SETTINGS_NEWWINDOW: - mPreferences.setPopupsEnabled((Boolean) newValue); + mPreferenceManager.setPopupsEnabled((Boolean) newValue); cbAllowPopups.setChecked((Boolean) newValue); return true; case SETTINGS_ENABLECOOKIES: - mPreferences.setCookiesEnabled((Boolean) newValue); + mPreferenceManager.setCookiesEnabled((Boolean) newValue); cbenablecookies.setChecked((Boolean) newValue); return true; case SETTINGS_COOKIESINKOGNITO: - mPreferences.setIncognitoCookiesEnabled((Boolean) newValue); + mPreferenceManager.setIncognitoCookiesEnabled((Boolean) newValue); cbcookiesInkognito.setChecked((Boolean) newValue); return true; case SETTINGS_RESTORETABS: - mPreferences.setRestoreLostTabsEnabled((Boolean) newValue); + mPreferenceManager.setRestoreLostTabsEnabled((Boolean) newValue); cbrestoreTabs.setChecked((Boolean) newValue); return true; default: @@ -140,14 +139,15 @@ private void renderPicker() { CharSequence[] chars = {mActivity.getString(R.string.name_normal), mActivity.getString(R.string.name_inverted), mActivity.getString(R.string.name_grayscale), - mActivity.getString(R.string.name_inverted_grayscale)}; + mActivity.getString(R.string.name_inverted_grayscale), + mActivity.getString(R.string.name_increase_contrast)}; - int n = mPreferences.getRenderingMode(); + int n = mPreferenceManager.getRenderingMode(); picker.setSingleChoiceItems(chars, n, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setRenderingMode(which); + mPreferenceManager.setRenderingMode(which); switch (which) { case 0: renderingmode.setSummary(getString(R.string.name_normal)); @@ -161,6 +161,9 @@ public void onClick(DialogInterface dialog, int which) { case 3: renderingmode.setSummary(getString(R.string.name_inverted_grayscale)); break; + case 4: + renderingmode.setSummary(getString(R.string.name_increase_contrast)); + break; } } }); @@ -172,12 +175,12 @@ private void textEncodingPicker() { AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.text_encoding)); final List textEncodingList = Arrays.asList(Constants.TEXT_ENCODINGS); - int n = textEncodingList.indexOf(mPreferences.getTextEncoding()); + int n = textEncodingList.indexOf(mPreferenceManager.getTextEncoding()); picker.setSingleChoiceItems(Constants.TEXT_ENCODINGS, n, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setTextEncoding(Constants.TEXT_ENCODINGS[which]); + mPreferenceManager.setTextEncoding(Constants.TEXT_ENCODINGS[which]); textEncoding.setSummary(Constants.TEXT_ENCODINGS[which]); } }); @@ -189,12 +192,12 @@ private void urlBoxPicker() { AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.url_contents)); - int n = mPreferences.getUrlBoxContentChoice(); + int n = mPreferenceManager.getUrlBoxContentChoice(); picker.setSingleChoiceItems(mUrlOptions, n, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setUrlBoxContentChoice(which); + mPreferenceManager.setUrlBoxContentChoice(which); if (which < mUrlOptions.length) { urlcontent.setSummary(mUrlOptions[which]); } diff --git a/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java index 8698603ba..3246821c4 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java @@ -6,15 +6,26 @@ import android.Manifest; import android.app.Activity; import android.content.DialogInterface; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.preference.Preference; import android.preference.PreferenceFragment; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; +import android.util.Log; +import android.widget.ArrayAdapter; + +import com.anthonycr.grant.PermissionsManager; +import com.anthonycr.grant.PermissionsResultAction; import java.io.File; +import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; @@ -23,10 +34,14 @@ import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.constant.Constants; import acr.browser.lightning.database.BookmarkLocalSync; +import acr.browser.lightning.database.BookmarkLocalSync.Source; import acr.browser.lightning.database.BookmarkManager; import acr.browser.lightning.database.HistoryItem; -import acr.browser.lightning.utils.PermissionsManager; +import acr.browser.lightning.react.OnSubscribe; +import acr.browser.lightning.react.Schedulers; +import acr.browser.lightning.utils.Preconditions; import acr.browser.lightning.utils.Utils; public class BookmarkSettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener { @@ -34,13 +49,15 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref private static final String SETTINGS_EXPORT = "export_bookmark"; private static final String SETTINGS_IMPORT = "import_bookmark"; private static final String SETTINGS_IMPORT_BROWSER = "import_browser"; + private static final String SETTINGS_DELETE_BOOKMARKS = "delete_bookmarks"; + + @Nullable private Activity mActivity; - private Activity mActivity; - @Inject - BookmarkManager mBookmarkManager; + @Inject BookmarkManager mBookmarkManager; private File[] mFileList; private String[] mFileNameList; - private BookmarkLocalSync mSync; + @Nullable private BookmarkLocalSync mSync; + private static final String[] REQUIRED_PERMISSIONS = new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE @@ -48,16 +65,38 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref private static final File mPath = new File(Environment.getExternalStorageDirectory().toString()); private class ImportBookmarksTask extends AsyncTask { + + @NonNull private final WeakReference mActivityReference; + private final Source mSource; + + public ImportBookmarksTask(Activity activity, Source source) { + mActivityReference = new WeakReference<>(activity); + mSource = source; + } + @Override protected Integer doInBackground(Void... params) { - List list = null; - if (mSync.isStockSupported()) { - list = mSync.getBookmarksFromStockBrowser(); - } else if (mSync.isChromeSupported()) { - list = mSync.getBookmarksFromChrome(); + List list; + Log.d(Constants.TAG, "Loading bookmarks from: " + mSource.name()); + switch (mSource) { + case STOCK: + list = getSync().getBookmarksFromStockBrowser(); + break; + case CHROME_STABLE: + list = getSync().getBookmarksFromChrome(); + break; + case CHROME_BETA: + list = getSync().getBookmarksFromChromeBeta(); + break; + case CHROME_DEV: + list = getSync().getBookmarksFromChromeDev(); + break; + default: + list = new ArrayList<>(0); + break; } int count = 0; - if (list != null && !list.isEmpty()) { + if (!list.isEmpty()) { mBookmarkManager.addBookmarkList(list); count = list.size(); } @@ -67,14 +106,25 @@ protected Integer doInBackground(Void... params) { @Override protected void onPostExecute(Integer num) { super.onPostExecute(num); - if (mActivity != null) { + Activity activity = mActivityReference.get(); + if (activity != null) { int number = num; - final String message = mActivity.getResources().getString(R.string.message_import); - Utils.showSnackbar(mActivity, number + " " + message); + final String message = activity.getResources().getString(R.string.message_import); + Utils.showSnackbar(activity, number + " " + message); } } } + @NonNull + private BookmarkLocalSync getSync() { + Preconditions.checkNonNull(mActivity); + if (mSync == null) { + mSync = new BookmarkLocalSync(mActivity); + } + + return mSync; + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -83,12 +133,13 @@ public void onCreate(Bundle savedInstanceState) { addPreferencesFromResource(R.xml.preference_bookmarks); mActivity = getActivity(); + mSync = new BookmarkLocalSync(mActivity); initPrefs(); PermissionsManager permissionsManager = PermissionsManager.getInstance(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - permissionsManager.requestPermissionsIfNecessary(getActivity(), REQUIRED_PERMISSIONS); + permissionsManager.requestPermissionsIfNecessaryForResult(getActivity(), REQUIRED_PERMISSIONS, null); } } @@ -100,49 +151,184 @@ public void onDestroy() { private void initPrefs() { - Preference exportpref = findPreference(SETTINGS_EXPORT); - Preference importpref = findPreference(SETTINGS_IMPORT); + Preference exportPref = findPreference(SETTINGS_EXPORT); + Preference importPref = findPreference(SETTINGS_IMPORT); + Preference deletePref = findPreference(SETTINGS_DELETE_BOOKMARKS); - mSync = new BookmarkLocalSync(mActivity); + exportPref.setOnPreferenceClickListener(this); + importPref.setOnPreferenceClickListener(this); + deletePref.setOnPreferenceClickListener(this); - exportpref.setOnPreferenceClickListener(this); - importpref.setOnPreferenceClickListener(this); + BrowserApp.getTaskThread().execute(new Runnable() { + @Override + public void run() { + final boolean isBrowserImportSupported = getSync().isBrowserImportSupported(); + Schedulers.main().execute(new Runnable() { + @Override + public void run() { + Preference importStock = findPreference(SETTINGS_IMPORT_BROWSER); + importStock.setEnabled(isBrowserImportSupported); + importStock.setOnPreferenceClickListener(BookmarkSettingsFragment.this); + } + }); + } + }); - new Thread(mInitializeImportPreference).start(); } - private final Runnable mInitializeImportPreference = new Runnable() { - @Override - public void run() { - Preference importStock = findPreference(SETTINGS_IMPORT_BROWSER); - importStock.setEnabled(mSync.isStockSupported() || mSync.isChromeSupported()); - importStock.setOnPreferenceClickListener(BookmarkSettingsFragment.this); - } - }; - @Override - public boolean onPreferenceClick(Preference preference) { + public boolean onPreferenceClick(@NonNull Preference preference) { switch (preference.getKey()) { case SETTINGS_EXPORT: - if (PermissionsManager.checkPermissions(getActivity(), REQUIRED_PERMISSIONS)) { - mBookmarkManager.exportBookmarks(getActivity()); - } + PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(getActivity(), REQUIRED_PERMISSIONS, + new PermissionsResultAction() { + @Override + public void onGranted() { + mBookmarkManager.exportBookmarks(getActivity()); + } + + @Override + public void onDenied(String permission) { + //TODO Show message + } + }); return true; case SETTINGS_IMPORT: - if (PermissionsManager.checkPermissions(getActivity(), REQUIRED_PERMISSIONS)) { - loadFileList(null); - createDialog(); - } + PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(getActivity(), REQUIRED_PERMISSIONS, + new PermissionsResultAction() { + @Override + public void onGranted() { + loadFileList(null); + createDialog(); + } + + @Override + public void onDenied(String permission) { + //TODO Show message + } + }); return true; case SETTINGS_IMPORT_BROWSER: - new ImportBookmarksTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + getSync().getSupportedBrowsers().subscribeOn(Schedulers.worker()) + .observeOn(Schedulers.main()).subscribe(new OnSubscribe>() { + @Override + public void onNext(@Nullable List items) { + Activity activity = getActivity(); + if (items == null || activity == null) { + return; + } + List titles = buildTitleList(activity, items); + showChooserDialog(activity, titles); + } + }); + return true; + case SETTINGS_DELETE_BOOKMARKS: + showDeleteBookmarksDialog(); return true; default: return false; } } - private void loadFileList(File path) { + private void showDeleteBookmarksDialog() { + Activity activity = getActivity(); + if (activity == null) { + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(R.string.action_delete); + builder.setMessage(R.string.action_delete_all_bookmarks); + builder.setNegativeButton(R.string.no, null); + builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + mBookmarkManager.deleteAllBookmarks(); + } + }); + builder.show(); + } + + @NonNull + private List buildTitleList(@NonNull Activity activity, @NonNull List items) { + List titles = new ArrayList<>(); + String title; + for (Source source : items) { + switch (source) { + case STOCK: + titles.add(getString(R.string.stock_browser)); + break; + case CHROME_STABLE: + title = getTitle(activity, "com.android.chrome"); + if (title != null) { + titles.add(title); + } + break; + case CHROME_BETA: + title = getTitle(activity, "com.chrome.beta"); + if (title != null) { + titles.add(title); + } + break; + case CHROME_DEV: + title = getTitle(activity, "com.chrome.beta"); + if (title != null) { + titles.add(title); + } + break; + default: + break; + } + } + return titles; + } + + private void showChooserDialog(final Activity activity, List list) { + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + final ArrayAdapter adapter = new ArrayAdapter<>(activity, + android.R.layout.simple_list_item_1); + for (String title : list) { + adapter.add(title); + } + builder.setTitle(R.string.supported_browsers_title); + builder.setAdapter(adapter, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + String title = adapter.getItem(which); + Source source = null; + if (title.equals(getString(R.string.stock_browser))) { + source = Source.STOCK; + } else if (title.equals(getTitle(activity, "com.android.chrome"))) { + source = Source.CHROME_STABLE; + } else if (title.equals(getTitle(activity, "com.android.beta"))) { + source = Source.CHROME_BETA; + } else if (title.equals(getTitle(activity, "com.android.dev"))) { + source = Source.CHROME_DEV; + } + if (source != null) { + new ImportBookmarksTask(activity, source).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + }); + builder.show(); + } + + @Nullable + private static String getTitle(@NonNull Activity activity, @NonNull String packageName) { + PackageManager pm = activity.getPackageManager(); + try { + ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA); + CharSequence title = pm.getApplicationLabel(info); + if (title != null) { + return title.toString(); + } + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + private void loadFileList(@Nullable File path) { File file; if (path != null) { file = path; @@ -175,7 +361,7 @@ private void loadFileList(File path) { private static class SortName implements Comparator { @Override - public int compare(File a, File b) { + public int compare(@NonNull File a, @NonNull File b) { if (a.isDirectory() && b.isDirectory()) return a.getName().compareTo(b.getName()); diff --git a/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java b/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java index 80aa3f96d..4560d52b5 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java @@ -2,6 +2,7 @@ import android.app.Activity; import android.content.Context; +import android.content.Intent; import android.graphics.Bitmap; import android.graphics.PorterDuff; import android.os.Bundle; @@ -30,40 +31,50 @@ import com.squareup.otto.Subscribe; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.activity.BrowserActivity; +import acr.browser.lightning.activity.ReadingActivity; +import acr.browser.lightning.activity.TabsManager; import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.async.AsyncExecutor; import acr.browser.lightning.bus.BookmarkEvents; import acr.browser.lightning.bus.BrowserEvents; +import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.controller.UIController; import acr.browser.lightning.database.BookmarkManager; import acr.browser.lightning.database.HistoryItem; -import acr.browser.lightning.dialog.BookmarksDialogBuilder; +import acr.browser.lightning.dialog.LightningDialogBuilder; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.async.ImageDownloadTask; +import acr.browser.lightning.react.Action; +import acr.browser.lightning.react.Observable; +import acr.browser.lightning.react.OnSubscribe; +import acr.browser.lightning.react.Schedulers; +import acr.browser.lightning.react.Subscriber; import acr.browser.lightning.utils.ThemeUtils; +import acr.browser.lightning.view.LightningView; -/** - * Created by Stefano Pacifici on 25/08/15. Based on Anthony C. Restaino's code. - */ public class BookmarksFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener { + private final static String TAG = BookmarksFragment.class.getSimpleName(); + + public final static String INCOGNITO_MODE = TAG + ".INCOGNITO_MODE"; + // Managers - @Inject - BookmarkManager mBookmarkManager; + @Inject BookmarkManager mBookmarkManager; // Event bus - @Inject - Bus mEventBus; + @Inject Bus mEventBus; // Dialog builder - @Inject - BookmarksDialogBuilder mBookmarksDialogBuilder; + @Inject LightningDialogBuilder mBookmarksDialogBuilder; + + @Inject PreferenceManager mPreferenceManager; + + private TabsManager mTabsManager; // Adapter private BookmarkViewAdapter mBookmarkAdapter; @@ -81,21 +92,36 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener, // Colors private int mIconColor, mScrollIndex; - // Init asynchronously the bookmark manager - private final Runnable mInitBookmarkManager = new Runnable() { - @Override - public void run() { - final Context context = getContext(); - mBookmarkAdapter = new BookmarkViewAdapter(context, mBookmarks); - setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), false); - mBookmarksListView.setAdapter(mBookmarkAdapter); - } - }; + private boolean mIsIncognito; + + private Observable initBookmarkManager() { + return Observable.create(new Action() { + @Override + public void onSubscribe(@NonNull Subscriber subscriber) { + Context context = getContext(); + if (context != null) { + mBookmarkAdapter = new BookmarkViewAdapter(context, mBookmarks); + setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), false); + subscriber.onNext(mBookmarkAdapter); + } + subscriber.onComplete(); + } + }); + } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); BrowserApp.getAppComponent().inject(this); + final Bundle arguments = getArguments(); + final Context context = getContext(); + mTabsManager = ((UIController) context).getTabModel(); + mIsIncognito = arguments.getBoolean(INCOGNITO_MODE, false); + boolean darkTheme = mPreferenceManager.getUseTheme() != 0 || mIsIncognito; + mWebpageBitmap = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, darkTheme); + mFolderBitmap = ThemeUtils.getThemedBitmap(context, R.drawable.ic_folder, darkTheme); + mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(context) : + ThemeUtils.getIconLightThemeColor(context); } // Handle bookmark click @@ -107,7 +133,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) mScrollIndex = mBookmarksListView.getFirstVisiblePosition(); setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(item.getTitle(), true), true); } else { - mEventBus.post(new BookmarkEvents.Clicked(item)); + mEventBus.post(new BrowserEvents.OpenUrlInCurrentTab(item.getUrl())); } } }; @@ -116,7 +142,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) @Override public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { final HistoryItem item = mBookmarks.get(position); - handleLongPress(item, position); + handleLongPress(item); return true; } }; @@ -124,17 +150,20 @@ public boolean onItemLongClick(AdapterView parent, View view, int position, l @Override public void onResume() { super.onResume(); - setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), false); + if (mBookmarkAdapter != null) { + setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), false); + } } @Nullable @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.bookmark_drawer, container, false); mBookmarksListView = (ListView) view.findViewById(R.id.right_drawer_list); mBookmarksListView.setOnItemClickListener(mItemClickListener); mBookmarksListView.setOnItemLongClickListener(mItemLongClickListener); mBookmarkTitleImage = (ImageView) view.findViewById(R.id.starIcon); + mBookmarkTitleImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); mBookmarkImage = (ImageView) view.findViewById(R.id.icon_star); final View backView = view.findViewById(R.id.bookmark_back_button); backView.setOnClickListener(new View.OnClickListener() { @@ -148,26 +177,20 @@ public void onClick(View v) { } }); setupNavigationButton(view, R.id.action_add_bookmark, R.id.icon_star); - - // Must be called here, only here we have a reference to the ListView - new Thread(mInitBookmarkManager).run(); + setupNavigationButton(view, R.id.action_reading, R.id.icon_reading); + setupNavigationButton(view, R.id.action_toggle_desktop, R.id.icon_desktop); + + initBookmarkManager().subscribeOn(Schedulers.io()) + .observeOn(Schedulers.main()) + .subscribe(new OnSubscribe() { + @Override + public void onNext(@Nullable BookmarkViewAdapter item) { + mBookmarksListView.setAdapter(mBookmarkAdapter); + } + }); return view; } - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - // TODO remove dependency on BrowserActivity - super.onActivityCreated(savedInstanceState); - final Activity activity = getActivity(); - final PreferenceManager preferenceManager = PreferenceManager.getInstance(); - boolean darkTheme = preferenceManager.getUseTheme() != 0 || ((BrowserActivity) activity).isIncognito(); - mWebpageBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_webpage, darkTheme); - mFolderBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_folder, darkTheme); - mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(activity) : - ThemeUtils.getIconLightThemeColor(activity); - mBookmarkTitleImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); - } - @Override public void onStart() { super.onStart(); @@ -180,21 +203,30 @@ public void onStop() { mEventBus.unregister(this); } - @Subscribe - public void addBookmark(final BrowserEvents.AddBookmark event) { - final HistoryItem item = new HistoryItem(event.url, event.title); - if (mBookmarkManager.addBookmark(item)) { - mBookmarks.add(item); - Collections.sort(mBookmarks, new BookmarkManager.SortIgnoreCase()); - mBookmarkAdapter.notifyDataSetChanged(); - mEventBus.post(new BookmarkEvents.Added(item)); - updateBookmarkIndicator(event.url); + public void reinitializePreferences() { + Activity activity = getActivity(); + if (activity == null) { + return; } + boolean darkTheme = mPreferenceManager.getUseTheme() != 0 || mIsIncognito; + mWebpageBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_webpage, darkTheme); + mFolderBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_folder, darkTheme); + mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(activity) : + ThemeUtils.getIconLightThemeColor(activity); + } + + @Subscribe + public void addBookmark(@NonNull final BrowserEvents.BookmarkAdded event) { + updateBookmarkIndicator(event.url); + String folder = mBookmarkManager.getCurrentFolder(); + setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(folder, true), false); } @Subscribe - public void currentPageInfo(final BrowserEvents.CurrentPageUrl event) { + public void currentPageInfo(@NonNull final BrowserEvents.CurrentPageUrl event) { updateBookmarkIndicator(event.url); + String folder = mBookmarkManager.getCurrentFolder(); + setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(folder, true), false); } @Subscribe @@ -224,7 +256,7 @@ public void userPressedBack(final BrowserEvents.UserPressedBack event) { } @Subscribe - public void bookmarkDeleted(final BookmarkEvents.Deleted event) { + public void bookmarkDeleted(@NonNull final BookmarkEvents.Deleted event) { mBookmarks.remove(event.item); if (event.item.isFolder()) { setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), false); @@ -233,7 +265,7 @@ public void bookmarkDeleted(final BookmarkEvents.Deleted event) { } } - private void setBookmarkDataSet(List items, boolean animate) { + private void setBookmarkDataSet(@NonNull List items, boolean animate) { mBookmarks.clear(); mBookmarks.addAll(items); mBookmarkAdapter.notifyDataSetChanged(); @@ -291,19 +323,35 @@ private void setupNavigationButton(@NonNull View view, @IdRes int buttonId, @IdR buttonImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); } - private void handleLongPress(final HistoryItem item, final int position) { + private void handleLongPress(@NonNull final HistoryItem item) { if (item.isFolder()) { mBookmarksDialogBuilder.showBookmarkFolderLongPressedDialog(getContext(), item); } else { - mBookmarksDialogBuilder.showLongPressedDialogForUrl(getContext(), item); + mBookmarksDialogBuilder.showLongPressedDialogForBookmarkUrl(getContext(), item); } } @Override - public void onClick(View v) { + public void onClick(@NonNull View v) { switch (v.getId()) { case R.id.action_add_bookmark: - mEventBus.post(new BookmarkEvents.WantToBookmarkCurrentPage()); + mEventBus.post(new BookmarkEvents.ToggleBookmarkForCurrentPage()); + break; + case R.id.action_reading: + LightningView currentTab = mTabsManager.getCurrentTab(); + if (currentTab != null) { + Intent read = new Intent(getActivity(), ReadingActivity.class); + read.putExtra(Constants.LOAD_READING_URL, currentTab.getUrl()); + startActivity(read); + } + break; + case R.id.action_toggle_desktop: + LightningView current = mTabsManager.getCurrentTab(); + if (current != null) { + current.toggleDesktopUA(getActivity()); + current.reload(); + // TODO add back drawer closing + } break; default: break; @@ -319,7 +367,7 @@ private class BookmarkViewAdapter extends ArrayAdapter { final Context context; - public BookmarkViewAdapter(Context context, List data) { + public BookmarkViewAdapter(Context context, @NonNull List data) { super(context, R.layout.bookmark_list_item, data); this.context = context; } @@ -349,7 +397,7 @@ public View getView(int position, View convertView, ViewGroup parent) { holder.favicon.setImageBitmap(mFolderBitmap); } else if (web.getBitmap() == null) { holder.favicon.setImageBitmap(mWebpageBitmap); - new ImageDownloadTask(holder.favicon, web, mWebpageBitmap) + new ImageDownloadTask(holder.favicon, web, mWebpageBitmap, context) .executeOnExecutor(AsyncExecutor.getInstance()); } else { holder.favicon.setImageBitmap(web.getBitmap()); diff --git a/app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java index 23180e933..772d13584 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java @@ -8,7 +8,7 @@ import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceFragment; +import android.support.annotation.NonNull; import android.support.v7.app.AlertDialog; import android.view.Gravity; import android.view.LayoutInflater; @@ -19,9 +19,8 @@ import android.widget.TextView; import acr.browser.lightning.R; -import acr.browser.lightning.preference.PreferenceManager; -public class DisplaySettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { +public class DisplaySettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { private static final String SETTINGS_HIDESTATUSBAR = "fullScreenOption"; private static final String SETTINGS_FULLSCREEN = "fullscreen"; @@ -38,7 +37,6 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe private static final float XSMALL = 10.0f; private Activity mActivity; - private PreferenceManager mPreferences; private CheckBoxPreference cbstatus, cbfullscreen, cbviewport, cboverview, cbreflow; private Preference theme; private String[] mThemeOptions; @@ -57,9 +55,8 @@ public void onCreate(Bundle savedInstanceState) { private void initPrefs() { // mPreferences storage - mPreferences = PreferenceManager.getInstance(); mThemeOptions = this.getResources().getStringArray(R.array.themes); - mCurrentTheme = mPreferences.getUseTheme(); + mCurrentTheme = mPreferenceManager.getUseTheme(); theme = findPreference(SETTINGS_THEME); Preference textsize = findPreference(SETTINGS_TEXTSIZE); @@ -77,17 +74,17 @@ private void initPrefs() { cboverview.setOnPreferenceChangeListener(this); cbreflow.setOnPreferenceChangeListener(this); - cbstatus.setChecked(mPreferences.getHideStatusBarEnabled()); - cbfullscreen.setChecked(mPreferences.getFullScreenEnabled()); - cbviewport.setChecked(mPreferences.getUseWideViewportEnabled()); - cboverview.setChecked(mPreferences.getOverviewModeEnabled()); - cbreflow.setChecked(mPreferences.getTextReflowEnabled()); + cbstatus.setChecked(mPreferenceManager.getHideStatusBarEnabled()); + cbfullscreen.setChecked(mPreferenceManager.getFullScreenEnabled()); + cbviewport.setChecked(mPreferenceManager.getUseWideViewportEnabled()); + cboverview.setChecked(mPreferenceManager.getOverviewModeEnabled()); + cbreflow.setChecked(mPreferenceManager.getTextReflowEnabled()); - theme.setSummary(mThemeOptions[mPreferences.getUseTheme()]); + theme.setSummary(mThemeOptions[mPreferenceManager.getUseTheme()]); } @Override - public boolean onPreferenceClick(Preference preference) { + public boolean onPreferenceClick(@NonNull Preference preference) { switch (preference.getKey()) { case SETTINGS_THEME: themePicker(); @@ -101,27 +98,27 @@ public boolean onPreferenceClick(Preference preference) { } @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { + public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) { // switch preferences switch (preference.getKey()) { case SETTINGS_HIDESTATUSBAR: - mPreferences.setHideStatusBarEnabled((Boolean) newValue); + mPreferenceManager.setHideStatusBarEnabled((Boolean) newValue); cbstatus.setChecked((Boolean) newValue); return true; case SETTINGS_FULLSCREEN: - mPreferences.setFullScreenEnabled((Boolean) newValue); + mPreferenceManager.setFullScreenEnabled((Boolean) newValue); cbfullscreen.setChecked((Boolean) newValue); return true; case SETTINGS_VIEWPORT: - mPreferences.setUseWideViewportEnabled((Boolean) newValue); + mPreferenceManager.setUseWideViewportEnabled((Boolean) newValue); cbviewport.setChecked((Boolean) newValue); return true; case SETTINGS_OVERVIEWMODE: - mPreferences.setOverviewModeEnabled((Boolean) newValue); + mPreferenceManager.setOverviewModeEnabled((Boolean) newValue); cboverview.setChecked((Boolean) newValue); return true; case SETTINGS_REFLOW: - mPreferences.setTextReflowEnabled((Boolean) newValue); + mPreferenceManager.setTextReflowEnabled((Boolean) newValue); cbreflow.setChecked((Boolean) newValue); return true; default: @@ -142,14 +139,14 @@ private void textSizePicker() { bar.setOnSeekBarChangeListener(new TextSeekBarListener(sample)); final int MAX = 5; bar.setMax(MAX); - bar.setProgress(MAX - mPreferences.getTextSize()); + bar.setProgress(MAX - mPreferenceManager.getTextSize()); builder.setView(view); builder.setTitle(R.string.title_text_size); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { - mPreferences.setTextSize(MAX - bar.getProgress()); + mPreferenceManager.setTextSize(MAX - bar.getProgress()); } }); @@ -179,12 +176,12 @@ private void themePicker() { AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.theme)); - int n = mPreferences.getUseTheme(); + int n = mPreferenceManager.getUseTheme(); picker.setSingleChoiceItems(mThemeOptions, n, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setUseTheme(which); + mPreferenceManager.setUseTheme(which); if (which < mThemeOptions.length) { theme.setSummary(mThemeOptions[which]); } @@ -195,7 +192,7 @@ public void onClick(DialogInterface dialog, int which) { @Override public void onClick(DialogInterface dialog, int which) { - if (mCurrentTheme != mPreferences.getUseTheme()) { + if (mCurrentTheme != mPreferenceManager.getUseTheme()) { getActivity().onBackPressed(); } } @@ -203,7 +200,7 @@ public void onClick(DialogInterface dialog, int which) { picker.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { - if (mCurrentTheme != mPreferences.getUseTheme()) { + if (mCurrentTheme != mPreferenceManager.getUseTheme()) { getActivity().onBackPressed(); } } diff --git a/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java index 951b6b987..5896f5065 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java @@ -5,11 +5,12 @@ import android.app.Activity; import android.content.DialogInterface; +import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceFragment; +import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.text.Editable; @@ -23,12 +24,11 @@ import acr.browser.lightning.R; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.download.DownloadHandler; -import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.ProxyUtils; import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.Utils; -public class GeneralSettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { +public class GeneralSettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { private static final String SETTINGS_PROXY = "proxy"; private static final String SETTINGS_FLASH = "cb_flash"; @@ -45,13 +45,11 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe private Activity mActivity; private static final int API = android.os.Build.VERSION.SDK_INT; - private PreferenceManager mPreferences; private CharSequence[] mProxyChoices; private Preference proxy, useragent, downloadloc, home, searchengine; private String mDownloadLocation; private int mAgentChoice; private String mHomepage; - private CheckBoxPreference cbFlash, cbAds, cbImages, cbJsScript, cbColorMode, cbgooglesuggest, cbDrawerTabs; @Override public void onCreate(Bundle savedInstanceState) { @@ -65,22 +63,19 @@ public void onCreate(Bundle savedInstanceState) { } private void initPrefs() { - // mPreferences storage - mPreferences = PreferenceManager.getInstance(); - proxy = findPreference(SETTINGS_PROXY); useragent = findPreference(SETTINGS_USERAGENT); downloadloc = findPreference(SETTINGS_DOWNLOAD); home = findPreference(SETTINGS_HOME); searchengine = findPreference(SETTINGS_SEARCHENGINE); - cbFlash = (CheckBoxPreference) findPreference(SETTINGS_FLASH); - cbAds = (CheckBoxPreference) findPreference(SETTINGS_ADS); - cbImages = (CheckBoxPreference) findPreference(SETTINGS_IMAGES); - cbJsScript = (CheckBoxPreference) findPreference(SETTINGS_JAVASCRIPT); - cbColorMode = (CheckBoxPreference) findPreference(SETTINGS_COLORMODE); - cbgooglesuggest = (CheckBoxPreference) findPreference(SETTINGS_GOOGLESUGGESTIONS); - cbDrawerTabs = (CheckBoxPreference) findPreference(SETTINGS_DRAWERTABS); + CheckBoxPreference cbFlash = (CheckBoxPreference) findPreference(SETTINGS_FLASH); + CheckBoxPreference cbAds = (CheckBoxPreference) findPreference(SETTINGS_ADS); + CheckBoxPreference cbImages = (CheckBoxPreference) findPreference(SETTINGS_IMAGES); + CheckBoxPreference cbJsScript = (CheckBoxPreference) findPreference(SETTINGS_JAVASCRIPT); + CheckBoxPreference cbColorMode = (CheckBoxPreference) findPreference(SETTINGS_COLORMODE); + CheckBoxPreference cbgooglesuggest = (CheckBoxPreference) findPreference(SETTINGS_GOOGLESUGGESTIONS); + CheckBoxPreference cbDrawerTabs = (CheckBoxPreference) findPreference(SETTINGS_DRAWERTABS); proxy.setOnPreferenceClickListener(this); useragent.setOnPreferenceClickListener(this); @@ -95,23 +90,23 @@ private void initPrefs() { cbgooglesuggest.setOnPreferenceChangeListener(this); cbDrawerTabs.setOnPreferenceChangeListener(this); - mAgentChoice = mPreferences.getUserAgentChoice(); - mHomepage = mPreferences.getHomepage(); - mDownloadLocation = mPreferences.getDownloadDirectory(); + mAgentChoice = mPreferenceManager.getUserAgentChoice(); + mHomepage = mPreferenceManager.getHomepage(); + mDownloadLocation = mPreferenceManager.getDownloadDirectory(); mProxyChoices = getResources().getStringArray(R.array.proxy_choices_array); - int choice = mPreferences.getProxyChoice(); + int choice = mPreferenceManager.getProxyChoice(); if (choice == Constants.PROXY_MANUAL) { - proxy.setSummary(mPreferences.getProxyHost() + ':' + mPreferences.getProxyPort()); + proxy.setSummary(mPreferenceManager.getProxyHost() + ':' + mPreferenceManager.getProxyPort()); } else { proxy.setSummary(mProxyChoices[choice]); } - if (API >= 19) { - mPreferences.setFlashSupport(0); + if (API >= Build.VERSION_CODES.KITKAT) { + mPreferenceManager.setFlashSupport(0); } - setSearchEngineSummary(mPreferences.getSearchChoice()); + setSearchEngineSummary(mPreferenceManager.getSearchChoice()); downloadloc.setSummary(mDownloadLocation); @@ -139,28 +134,27 @@ private void initPrefs() { useragent.setSummary(getResources().getString(R.string.agent_custom)); } - int flashNum = mPreferences.getFlashSupport(); - boolean imagesBool = mPreferences.getBlockImagesEnabled(); - boolean enableJSBool = mPreferences.getJavaScriptEnabled(); + int flashNum = mPreferenceManager.getFlashSupport(); + boolean imagesBool = mPreferenceManager.getBlockImagesEnabled(); + boolean enableJSBool = mPreferenceManager.getJavaScriptEnabled(); -// proxy.setEnabled(Constants.FULL_VERSION); cbAds.setEnabled(Constants.FULL_VERSION); - cbFlash.setEnabled(API < 19); + cbFlash.setEnabled(API < Build.VERSION_CODES.KITKAT); cbImages.setChecked(imagesBool); cbJsScript.setChecked(enableJSBool); cbFlash.setChecked(flashNum > 0); - cbAds.setChecked(Constants.FULL_VERSION && mPreferences.getAdBlockEnabled()); - cbColorMode.setChecked(mPreferences.getColorModeEnabled()); - cbgooglesuggest.setChecked(mPreferences.getGoogleSearchSuggestionsEnabled()); - cbDrawerTabs.setChecked(mPreferences.getShowTabsInDrawer(true)); + cbAds.setChecked(Constants.FULL_VERSION && mPreferenceManager.getAdBlockEnabled()); + cbColorMode.setChecked(mPreferenceManager.getColorModeEnabled()); + cbgooglesuggest.setChecked(mPreferenceManager.getGoogleSearchSuggestionsEnabled()); + cbDrawerTabs.setChecked(mPreferenceManager.getShowTabsInDrawer(true)); } private void searchUrlPicker() { final AlertDialog.Builder urlPicker = new AlertDialog.Builder(mActivity); urlPicker.setTitle(getResources().getString(R.string.custom_url)); final EditText getSearchUrl = new EditText(mActivity); - String mSearchUrl = mPreferences.getSearchUrl(); + String mSearchUrl = mPreferenceManager.getSearchUrl(); getSearchUrl.setText(mSearchUrl); urlPicker.setView(getSearchUrl); urlPicker.setPositiveButton(getResources().getString(R.string.action_ok), @@ -168,7 +162,7 @@ private void searchUrlPicker() { @Override public void onClick(DialogInterface dialog, int which) { String text = getSearchUrl.getText().toString(); - mPreferences.setSearchUrl(text); + mPreferenceManager.setSearchUrl(text); searchengine.setSummary(getResources().getString(R.string.custom_url) + ": " + text); } @@ -185,7 +179,7 @@ private void getFlashChoice() { new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { - mPreferences.setFlashSupport(1); + mPreferenceManager.setFlashSupport(1); } }) .setNegativeButton(getResources().getString(R.string.action_auto), @@ -193,13 +187,13 @@ public void onClick(DialogInterface dialog, int id) { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setFlashSupport(2); + mPreferenceManager.setFlashSupport(2); } }).setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { - mPreferences.setFlashSupport(0); + mPreferenceManager.setFlashSupport(0); } }); @@ -210,7 +204,7 @@ public void onCancel(DialogInterface dialog) { private void proxyChoicePicker() { AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.http_proxy)); - picker.setSingleChoiceItems(mProxyChoices, mPreferences.getProxyChoice(), + picker.setSingleChoiceItems(mProxyChoices, mPreferenceManager.getProxyChoice(), new DialogInterface.OnClickListener() { @Override @@ -235,7 +229,7 @@ private void setProxyChoice(int choice) { break; } - mPreferences.setProxyChoice(choice); + mPreferenceManager.setProxyChoice(choice); if (choice < mProxyChoices.length) proxy.setSummary(mProxyChoices[choice]); } @@ -253,8 +247,8 @@ private void manualProxyPicker() { filterArray[0] = new InputFilter.LengthFilter(maxCharacters - 1); eProxyPort.setFilters(filterArray); - eProxyHost.setText(mPreferences.getProxyHost()); - eProxyPort.setText(Integer.toString(mPreferences.getProxyPort())); + eProxyHost.setText(mPreferenceManager.getProxyHost()); + eProxyPort.setText(Integer.toString(mPreferenceManager.getProxyPort())); new AlertDialog.Builder(mActivity) .setTitle(R.string.manual_proxy) @@ -269,10 +263,10 @@ public void onClick(DialogInterface dialogInterface, int i) { // larger than max integer proxyPort = Integer.parseInt(eProxyPort.getText().toString()); } catch (NumberFormatException ignored) { - proxyPort = mPreferences.getProxyPort(); + proxyPort = mPreferenceManager.getProxyPort(); } - mPreferences.setProxyHost(proxyHost); - mPreferences.setProxyPort(proxyPort); + mPreferenceManager.setProxyHost(proxyHost); + mPreferenceManager.setProxyPort(proxyPort); proxy.setSummary(proxyHost + ':' + proxyPort); } }).show(); @@ -286,13 +280,13 @@ private void searchDialog() { "DuckDuckGo (Privacy)", "DuckDuckGo Lite (Privacy)", "Baidu (Chinese)", "Yandex (Russian)"}; - int n = mPreferences.getSearchChoice(); + int n = mPreferenceManager.getSearchChoice(); picker.setSingleChoiceItems(chars, n, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setSearchChoice(which); + mPreferenceManager.setSearchChoice(which); setSearchEngineSummary(which); } }); @@ -303,7 +297,7 @@ public void onClick(DialogInterface dialog, int which) { private void homepageDialog() { AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.home)); - mHomepage = mPreferences.getHomepage(); + mHomepage = mPreferenceManager.getHomepage(); int n; if (mHomepage.contains("about:home")) { n = 1; @@ -321,15 +315,15 @@ private void homepageDialog() { public void onClick(DialogInterface dialog, int which) { switch (which + 1) { case 1: - mPreferences.setHomepage("about:home"); + mPreferenceManager.setHomepage("about:home"); home.setSummary(getResources().getString(R.string.action_homepage)); break; case 2: - mPreferences.setHomepage("about:blank"); + mPreferenceManager.setHomepage("about:blank"); home.setSummary(getResources().getString(R.string.action_blank)); break; case 3: - mPreferences.setHomepage("about:bookmarks"); + mPreferenceManager.setHomepage("about:bookmarks"); home.setSummary(getResources().getString(R.string.action_bookmarks)); break; case 4: @@ -346,11 +340,12 @@ private void homePicker() { final AlertDialog.Builder homePicker = new AlertDialog.Builder(mActivity); homePicker.setTitle(getResources().getString(R.string.title_custom_homepage)); final EditText getHome = new EditText(mActivity); - mHomepage = mPreferences.getHomepage(); + mHomepage = mPreferenceManager.getHomepage(); if (!mHomepage.startsWith("about:")) { getHome.setText(mHomepage); } else { - getHome.setText("http://www.google.com"); + String defaultUrl = "https://www.google.com"; + getHome.setText(defaultUrl); } homePicker.setView(getHome); homePicker.setPositiveButton(getResources().getString(R.string.action_ok), @@ -358,7 +353,7 @@ private void homePicker() { @Override public void onClick(DialogInterface dialog, int which) { String text = getHome.getText().toString(); - mPreferences.setHomepage(text); + mPreferenceManager.setHomepage(text); home.setSummary(text); } }); @@ -368,7 +363,7 @@ public void onClick(DialogInterface dialog, int which) { private void downloadLocDialog() { AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.title_download_location)); - mDownloadLocation = mPreferences.getDownloadDirectory(); + mDownloadLocation = mPreferenceManager.getDownloadDirectory(); int n; if (mDownloadLocation.contains(Environment.DIRECTORY_DOWNLOADS)) { n = 0; @@ -382,7 +377,7 @@ private void downloadLocDialog() { public void onClick(DialogInterface dialog, int which) { switch (which) { case 0: - mPreferences.setDownloadDirectory(DownloadHandler.DEFAULT_DOWNLOAD_PATH); + mPreferenceManager.setDownloadDirectory(DownloadHandler.DEFAULT_DOWNLOAD_PATH); downloadloc.setSummary(DownloadHandler.DEFAULT_DOWNLOAD_PATH); break; case 1: @@ -398,12 +393,12 @@ public void onClick(DialogInterface dialog, int which) { private void agentDialog() { AlertDialog.Builder agentPicker = new AlertDialog.Builder(mActivity); agentPicker.setTitle(getResources().getString(R.string.title_user_agent)); - mAgentChoice = mPreferences.getUserAgentChoice(); + mAgentChoice = mPreferenceManager.getUserAgentChoice(); agentPicker.setSingleChoiceItems(R.array.user_agent, mAgentChoice - 1, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setUserAgentChoice(which + 1); + mPreferenceManager.setUserAgentChoice(which + 1); switch (which + 1) { case 1: useragent.setSummary(getResources().getString(R.string.agent_default)); @@ -435,7 +430,7 @@ private void agentPicker() { @Override public void onClick(DialogInterface dialog, int which) { String text = getAgent.getText().toString(); - mPreferences.setUserAgentString(text); + mPreferenceManager.setUserAgentString(text); useragent.setSummary(getResources().getString(R.string.agent_custom)); } }); @@ -449,12 +444,12 @@ private void downPicker() { final EditText getDownload = new EditText(mActivity); getDownload.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - getDownload.setText(PreferenceManager.getInstance().getDownloadDirectory()); + getDownload.setText(mPreferenceManager.getDownloadDirectory()); final int errorColor = ContextCompat.getColor(getActivity(), R.color.error_red); final int regularColor = ThemeUtils.getTextColor(getActivity()); getDownload.setTextColor(regularColor); getDownload.addTextChangedListener(new DownloadLocationTextWatcher(getDownload, errorColor, regularColor)); - getDownload.setText(mPreferences.getDownloadDirectory()); + getDownload.setText(mPreferenceManager.getDownloadDirectory()); layout.addView(getDownload); downLocationPicker.setView(layout); @@ -464,7 +459,7 @@ private void downPicker() { public void onClick(DialogInterface dialog, int which) { String text = getDownload.getText().toString(); text = DownloadHandler.addNecessarySlashes(text); - mPreferences.setDownloadDirectory(text); + mPreferenceManager.setDownloadDirectory(text); downloadloc.setSummary(text); } }); @@ -509,7 +504,7 @@ private void setSearchEngineSummary(int which) { } @Override - public boolean onPreferenceClick(Preference preference) { + public boolean onPreferenceClick(@NonNull Preference preference) { switch (preference.getKey()) { case SETTINGS_PROXY: proxyChoicePicker(); @@ -532,45 +527,43 @@ public boolean onPreferenceClick(Preference preference) { } @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - // switch preferences + public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) { + boolean checked = false; + if (newValue instanceof Boolean) { + checked = (Boolean) newValue; + } switch (preference.getKey()) { case SETTINGS_FLASH: - if (cbFlash.isChecked()) { - getFlashChoice(); - } else { - mPreferences.setFlashSupport(0); - } - if (!Utils.isFlashInstalled(mActivity) && cbFlash.isChecked()) { + if (!Utils.isFlashInstalled(mActivity) && checked) { Utils.createInformativeDialog(mActivity, R.string.title_warning, R.string.dialog_adobe_not_installed); - cbFlash.setEnabled(false); - mPreferences.setFlashSupport(0); + mPreferenceManager.setFlashSupport(0); + return false; + } else { + if (checked) { + getFlashChoice(); + } else { + mPreferenceManager.setFlashSupport(0); + } } - cbFlash.setChecked((Boolean) newValue); return true; case SETTINGS_ADS: - mPreferences.setAdBlockEnabled((Boolean) newValue); - cbAds.setChecked((Boolean) newValue); + mPreferenceManager.setAdBlockEnabled(checked); return true; case SETTINGS_IMAGES: - mPreferences.setBlockImagesEnabled((Boolean) newValue); - cbImages.setChecked((Boolean) newValue); + mPreferenceManager.setBlockImagesEnabled(checked); return true; case SETTINGS_JAVASCRIPT: - mPreferences.setJavaScriptEnabled((Boolean) newValue); - cbJsScript.setChecked((Boolean) newValue); + mPreferenceManager.setJavaScriptEnabled(checked); return true; case SETTINGS_COLORMODE: - mPreferences.setColorModeEnabled((Boolean) newValue); - cbColorMode.setChecked((Boolean) newValue); + mPreferenceManager.setColorModeEnabled(checked); return true; case SETTINGS_GOOGLESUGGESTIONS: - mPreferences.setGoogleSearchSuggestionsEnabled((Boolean) newValue); - cbgooglesuggest.setChecked((Boolean) newValue); + mPreferenceManager.setGoogleSearchSuggestionsEnabled(checked); return true; case SETTINGS_DRAWERTABS: - mPreferences.setShowTabsInDrawer((Boolean) newValue); - cbDrawerTabs.setChecked((Boolean) newValue); + mPreferenceManager.setShowTabsInDrawer(checked); + return true; default: return false; } @@ -594,7 +587,7 @@ public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override - public void afterTextChanged(Editable s) { + public void afterTextChanged(@NonNull Editable s) { if (!DownloadHandler.isWriteAccessAvailable(s.toString())) { this.getDownload.setTextColor(this.errorColor); } else { diff --git a/app/src/main/java/acr/browser/lightning/fragment/LightningPreferenceFragment.java b/app/src/main/java/acr/browser/lightning/fragment/LightningPreferenceFragment.java new file mode 100644 index 000000000..ab7c48d95 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/fragment/LightningPreferenceFragment.java @@ -0,0 +1,27 @@ +package acr.browser.lightning.fragment; + +import android.os.Bundle; +import android.preference.PreferenceFragment; + +import javax.inject.Inject; + +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.preference.PreferenceManager; + +/** + * Simplify {@link PreferenceManager} inject in all the PreferenceFragments + * + * @author Stefano Pacifici + * @date 2015/09/16 + */ +public class LightningPreferenceFragment extends PreferenceFragment { + + @Inject + PreferenceManager mPreferenceManager; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + BrowserApp.getAppComponent().inject(this); + } +} diff --git a/app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java index 9fc225c87..24162a1dc 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java @@ -11,16 +11,20 @@ import android.os.Message; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceFragment; +import android.support.annotation.NonNull; import android.support.v7.app.AlertDialog; import android.webkit.WebView; +import javax.inject.Inject; + import acr.browser.lightning.R; -import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.database.HistoryDatabase; import acr.browser.lightning.utils.Utils; import acr.browser.lightning.utils.WebUtils; +import acr.browser.lightning.view.LightningView; -public class PrivacySettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { +public class PrivacySettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { private static final String SETTINGS_LOCATION = "location"; private static final String SETTINGS_THIRDPCOOKIES = "third_party"; @@ -33,16 +37,18 @@ public class PrivacySettingsFragment extends PreferenceFragment implements Prefe private static final String SETTINGS_CLEARCOOKIES = "clear_cookies"; private static final String SETTINGS_CLEARWEBSTORAGE = "clear_webstorage"; private static final String SETTINGS_WEBSTORAGEEXIT = "clear_webstorage_exit"; + private static final String SETTINGS_DONOTTRACK = "do_not_track"; + private static final String SETTINGS_IDENTIFYINGHEADERS = "remove_identifying_headers"; private Activity mActivity; - private PreferenceManager mPreferences; - private CheckBoxPreference cblocation, cb3cookies, cbsavepasswords, cbcacheexit, cbhistoryexit, - cbcookiesexit, cbwebstorageexit; - private Handler messageHandler; + private Handler mMessageHandler; + + @Inject HistoryDatabase mHistoryDatabase; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + BrowserApp.getAppComponent().inject(this); // Load the preferences from an XML resource addPreferencesFromResource(R.xml.preference_privacy); @@ -52,21 +58,20 @@ public void onCreate(Bundle savedInstanceState) { } private void initPrefs() { - // mPreferences storage - mPreferences = PreferenceManager.getInstance(); - Preference clearcache = findPreference(SETTINGS_CLEARCACHE); Preference clearhistory = findPreference(SETTINGS_CLEARHISTORY); Preference clearcookies = findPreference(SETTINGS_CLEARCOOKIES); Preference clearwebstorage = findPreference(SETTINGS_CLEARWEBSTORAGE); - cblocation = (CheckBoxPreference) findPreference(SETTINGS_LOCATION); - cb3cookies = (CheckBoxPreference) findPreference(SETTINGS_THIRDPCOOKIES); - cbsavepasswords = (CheckBoxPreference) findPreference(SETTINGS_SAVEPASSWORD); - cbcacheexit = (CheckBoxPreference) findPreference(SETTINGS_CACHEEXIT); - cbhistoryexit = (CheckBoxPreference) findPreference(SETTINGS_HISTORYEXIT); - cbcookiesexit = (CheckBoxPreference) findPreference(SETTINGS_COOKIEEXIT); - cbwebstorageexit = (CheckBoxPreference) findPreference(SETTINGS_WEBSTORAGEEXIT); + CheckBoxPreference cblocation = (CheckBoxPreference) findPreference(SETTINGS_LOCATION); + CheckBoxPreference cb3cookies = (CheckBoxPreference) findPreference(SETTINGS_THIRDPCOOKIES); + CheckBoxPreference cbsavepasswords = (CheckBoxPreference) findPreference(SETTINGS_SAVEPASSWORD); + CheckBoxPreference cbcacheexit = (CheckBoxPreference) findPreference(SETTINGS_CACHEEXIT); + CheckBoxPreference cbhistoryexit = (CheckBoxPreference) findPreference(SETTINGS_HISTORYEXIT); + CheckBoxPreference cbcookiesexit = (CheckBoxPreference) findPreference(SETTINGS_COOKIEEXIT); + CheckBoxPreference cbwebstorageexit = (CheckBoxPreference) findPreference(SETTINGS_WEBSTORAGEEXIT); + CheckBoxPreference cbDoNotTrack = (CheckBoxPreference) findPreference(SETTINGS_DONOTTRACK); + CheckBoxPreference cbIdentifyingHeaders = (CheckBoxPreference) findPreference(SETTINGS_IDENTIFYINGHEADERS); clearcache.setOnPreferenceClickListener(this); clearhistory.setOnPreferenceClickListener(this); @@ -80,18 +85,28 @@ private void initPrefs() { cbhistoryexit.setOnPreferenceChangeListener(this); cbcookiesexit.setOnPreferenceChangeListener(this); cbwebstorageexit.setOnPreferenceChangeListener(this); + cbDoNotTrack.setOnPreferenceChangeListener(this); + cbIdentifyingHeaders.setOnPreferenceChangeListener(this); + + cblocation.setChecked(mPreferenceManager.getLocationEnabled()); + cbsavepasswords.setChecked(mPreferenceManager.getSavePasswordsEnabled()); + cbcacheexit.setChecked(mPreferenceManager.getClearCacheExit()); + cbhistoryexit.setChecked(mPreferenceManager.getClearHistoryExitEnabled()); + cbcookiesexit.setChecked(mPreferenceManager.getClearCookiesExitEnabled()); + cb3cookies.setChecked(mPreferenceManager.getBlockThirdPartyCookiesEnabled()); + cbwebstorageexit.setChecked(mPreferenceManager.getClearWebStorageExitEnabled()); + cbDoNotTrack.setChecked(mPreferenceManager.getDoNotTrackEnabled() && Utils.doesSupportHeaders()); + cbIdentifyingHeaders.setChecked(mPreferenceManager.getRemoveIdentifyingHeadersEnabled() && Utils.doesSupportHeaders()); - cblocation.setChecked(mPreferences.getLocationEnabled()); - cbsavepasswords.setChecked(mPreferences.getSavePasswordsEnabled()); - cbcacheexit.setChecked(mPreferences.getClearCacheExit()); - cbhistoryexit.setChecked(mPreferences.getClearHistoryExitEnabled()); - cbcookiesexit.setChecked(mPreferences.getClearCookiesExitEnabled()); - cb3cookies.setChecked(mPreferences.getBlockThirdPartyCookiesEnabled()); - cbwebstorageexit.setChecked(mPreferences.getClearWebStorageExitEnabled()); + cbDoNotTrack.setEnabled(Utils.doesSupportHeaders()); + cbIdentifyingHeaders.setEnabled(Utils.doesSupportHeaders()); + + String identifyingHeadersSummary = LightningView.HEADER_REQUESTED_WITH + ", " + LightningView.HEADER_WAP_PROFILE; + cbIdentifyingHeaders.setSummary(identifyingHeadersSummary); cb3cookies.setEnabled(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP); - messageHandler = new MessageHandler(mActivity); + mMessageHandler = new MessageHandler(mActivity); } private static class MessageHandler extends Handler { @@ -103,7 +118,7 @@ public MessageHandler(Activity context) { } @Override - public void handleMessage(Message msg) { + public void handleMessage(@NonNull Message msg) { switch (msg.what) { case 1: Utils.showSnackbar(mHandlerContext, R.string.message_clear_history); @@ -117,7 +132,7 @@ public void handleMessage(Message msg) { } @Override - public boolean onPreferenceClick(Preference preference) { + public boolean onPreferenceClick(@NonNull Preference preference) { switch (preference.getKey()) { case SETTINGS_CLEARCACHE: clearCache(); @@ -144,13 +159,12 @@ private void clearHistoryDialog() { new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { - Thread clear = new Thread(new Runnable() { + BrowserApp.getIOThread().execute(new Runnable() { @Override public void run() { clearHistory(); } }); - clear.start(); } }) .setNegativeButton(getResources().getString(R.string.action_no), null).show(); @@ -164,13 +178,12 @@ private void clearCookiesDialog() { new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { - Thread clear = new Thread(new Runnable() { + BrowserApp.getTaskThread().execute(new Runnable() { @Override public void run() { clearCookies(); } }); - clear.start(); } }) .setNegativeButton(getResources().getString(R.string.action_no), null).show(); @@ -184,13 +197,13 @@ private void clearCache() { } private void clearHistory() { - WebUtils.clearHistory(getActivity()); - messageHandler.sendEmptyMessage(1); + WebUtils.clearHistory(getActivity(), mHistoryDatabase); + mMessageHandler.sendEmptyMessage(1); } private void clearCookies() { WebUtils.clearCookies(getActivity()); - messageHandler.sendEmptyMessage(2); + mMessageHandler.sendEmptyMessage(2); } private void clearWebStorage() { @@ -199,36 +212,34 @@ private void clearWebStorage() { } @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - // switch preferences + public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) { switch (preference.getKey()) { case SETTINGS_LOCATION: - mPreferences.setLocationEnabled((Boolean) newValue); - cblocation.setChecked((Boolean) newValue); + mPreferenceManager.setLocationEnabled((Boolean) newValue); return true; case SETTINGS_THIRDPCOOKIES: - mPreferences.setBlockThirdPartyCookiesEnabled((Boolean) newValue); - cb3cookies.setChecked((Boolean) newValue); + mPreferenceManager.setBlockThirdPartyCookiesEnabled((Boolean) newValue); return true; case SETTINGS_SAVEPASSWORD: - mPreferences.setSavePasswordsEnabled((Boolean) newValue); - cbsavepasswords.setChecked((Boolean) newValue); + mPreferenceManager.setSavePasswordsEnabled((Boolean) newValue); return true; case SETTINGS_CACHEEXIT: - mPreferences.setClearCacheExit((Boolean) newValue); - cbcacheexit.setChecked((Boolean) newValue); + mPreferenceManager.setClearCacheExit((Boolean) newValue); return true; case SETTINGS_HISTORYEXIT: - mPreferences.setClearHistoryExitEnabled((Boolean) newValue); - cbhistoryexit.setChecked((Boolean) newValue); + mPreferenceManager.setClearHistoryExitEnabled((Boolean) newValue); return true; case SETTINGS_COOKIEEXIT: - mPreferences.setClearCookiesExitEnabled((Boolean) newValue); - cbcookiesexit.setChecked((Boolean) newValue); + mPreferenceManager.setClearCookiesExitEnabled((Boolean) newValue); return true; case SETTINGS_WEBSTORAGEEXIT: - mPreferences.setClearWebStorageExitEnabled((Boolean) newValue); - cbwebstorageexit.setChecked((Boolean) newValue); + mPreferenceManager.setClearWebStorageExitEnabled((Boolean) newValue); + return true; + case SETTINGS_DONOTTRACK: + mPreferenceManager.setDoNotTrackEnabled((Boolean) newValue); + return true; + case SETTINGS_IDENTIFYINGHEADERS: + mPreferenceManager.setRemoveIdentifyingHeadersEnabled((Boolean) newValue); return true; default: return false; diff --git a/app/src/main/java/acr/browser/lightning/fragment/TabsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/TabsFragment.java new file mode 100644 index 000000000..7d9c5a360 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/fragment/TabsFragment.java @@ -0,0 +1,413 @@ +package acr.browser.lightning.fragment; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.IdRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.view.ViewCompat; +import android.support.v4.widget.TextViewCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.LayoutManager; +import android.support.v7.widget.SimpleItemAnimator; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.squareup.otto.Bus; + +import javax.inject.Inject; + +import acr.browser.lightning.R; +import acr.browser.lightning.activity.TabsManager; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.browser.TabsView; +import acr.browser.lightning.bus.NavigationEvents; +import acr.browser.lightning.bus.TabEvents; +import acr.browser.lightning.controller.UIController; +import acr.browser.lightning.fragment.anim.HorizontalItemAnimator; +import acr.browser.lightning.fragment.anim.VerticalItemAnimator; +import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.utils.ThemeUtils; +import acr.browser.lightning.utils.Utils; +import acr.browser.lightning.view.LightningView; + +/** + * A fragment that holds and manages the tabs and interaction with the tabs. + * It is reliant on the BrowserController in order to get the current UI state + * of the browser. It also uses the BrowserController to signal that the UI needs + * to change. This class contains the adapter used by both the drawer tabs and + * the desktop tabs. It delegates touch events for the tab UI appropriately. + */ +public class TabsFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener, TabsView { + + private static final String TAG = TabsFragment.class.getSimpleName(); + + /** + * Arguments boolean to tell the fragment it is displayed in the drawner or on the tab strip + * If true, the fragment is in the left drawner in the strip otherwise. + */ + public static final String VERTICAL_MODE = TAG + ".VERTICAL_MODE"; + public static final String IS_INCOGNITO = TAG + ".IS_INCOGNITO"; + + private boolean mIsIncognito, mDarkTheme; + private int mIconColor; + private boolean mColorMode = true; + private boolean mShowInNavigationDrawer; + + @Nullable private LightningViewAdapter mTabsAdapter; + private UIController mUiController; + private RecyclerView mRecyclerView; + + private TabsManager mTabsManager; + @Inject Bus mBus; + @Inject PreferenceManager mPreferences; + + public TabsFragment() { + BrowserApp.getAppComponent().inject(this); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final Bundle arguments = getArguments(); + final Context context = getContext(); + mUiController = (UIController) getActivity(); + mTabsManager = mUiController.getTabModel(); + mIsIncognito = arguments.getBoolean(IS_INCOGNITO, false); + mShowInNavigationDrawer = arguments.getBoolean(VERTICAL_MODE, true); + mDarkTheme = mPreferences.getUseTheme() != 0 || mIsIncognito; + mColorMode = mPreferences.getColorModeEnabled(); + mColorMode &= !mDarkTheme; + mIconColor = mDarkTheme ? + ThemeUtils.getIconDarkThemeColor(context) : + ThemeUtils.getIconLightThemeColor(context); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view; + final LayoutManager layoutManager; + if (mShowInNavigationDrawer) { + view = inflater.inflate(R.layout.tab_drawer, container, false); + layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false); + setupFrameLayoutButton(view, R.id.tab_header_button, R.id.plusIcon); + setupFrameLayoutButton(view, R.id.new_tab_button, R.id.icon_plus); + setupFrameLayoutButton(view, R.id.action_back, R.id.icon_back); + setupFrameLayoutButton(view, R.id.action_forward, R.id.icon_forward); + setupFrameLayoutButton(view, R.id.action_home, R.id.icon_home); + } else { + view = inflater.inflate(R.layout.tab_strip, container, false); + layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false); + ImageView newTab = (ImageView) view.findViewById(R.id.new_tab_button); + newTab.setColorFilter(ThemeUtils.getIconDarkThemeColor(getActivity())); + newTab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mUiController.newTabClicked(); + } + }); + } + mRecyclerView = (RecyclerView) view.findViewById(R.id.tabs_list); + SimpleItemAnimator animator; + if (mShowInNavigationDrawer) { + animator = new VerticalItemAnimator(); + } else { + animator = new HorizontalItemAnimator(); + } + animator.setSupportsChangeAnimations(false); + animator.setAddDuration(200); + animator.setChangeDuration(0); + animator.setRemoveDuration(200); + animator.setMoveDuration(200); + mRecyclerView.setLayerType(View.LAYER_TYPE_NONE, null); + mRecyclerView.setItemAnimator(animator); + mRecyclerView.setLayoutManager(layoutManager); + mTabsAdapter = new LightningViewAdapter(mShowInNavigationDrawer); + mRecyclerView.setAdapter(mTabsAdapter); + mRecyclerView.setHasFixedSize(true); + return view; + } + + private void setupFrameLayoutButton(@NonNull final View root, @IdRes final int buttonId, + @IdRes final int imageId) { + final View frameButton = root.findViewById(buttonId); + final ImageView buttonImage = (ImageView) root.findViewById(imageId); + frameButton.setOnClickListener(this); + frameButton.setOnLongClickListener(this); + buttonImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + mTabsAdapter = null; + } + + @Override + public void onStart() { + super.onStart(); + mBus.register(this); + } + + @Override + public void onResume() { + super.onResume(); + // Force adapter refresh + if (mTabsAdapter != null) { + mTabsAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onStop() { + super.onStop(); + mBus.unregister(this); + } + + public void reinitializePreferences() { + Activity activity = getActivity(); + if (activity == null) { + return; + } + mDarkTheme = mPreferences.getUseTheme() != 0 || mIsIncognito; + mColorMode = mPreferences.getColorModeEnabled(); + mColorMode &= !mDarkTheme; + mIconColor = mDarkTheme ? + ThemeUtils.getIconDarkThemeColor(activity) : + ThemeUtils.getIconLightThemeColor(activity); + if (mTabsAdapter != null) { + mTabsAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onClick(@NonNull View v) { + switch (v.getId()) { + case R.id.tab_header_button: + mUiController.showCloseDialog(mTabsManager.indexOfCurrentTab()); + break; + case R.id.new_tab_button: + mBus.post(new TabEvents.NewTab()); + break; + case R.id.action_back: + mBus.post(new NavigationEvents.GoBack()); + break; + case R.id.action_forward: + mBus.post(new NavigationEvents.GoForward()); + break; + case R.id.action_home: + mBus.post(new NavigationEvents.GoHome()); + default: + break; + } + } + + @Override + public boolean onLongClick(@NonNull View v) { + switch (v.getId()) { + case R.id.action_new_tab: + mBus.post(new TabEvents.NewTabLongPress()); + break; + default: + break; + } + return true; + } + + @Override + public void tabAdded() { + if (mTabsAdapter != null) { + mTabsAdapter.notifyItemInserted(mTabsManager.last()); + mRecyclerView.postDelayed(new Runnable() { + @Override + public void run() { + mRecyclerView.smoothScrollToPosition(mTabsAdapter.getItemCount() - 1); + } + }, 500); + } + } + + @Override + public void tabRemoved(int position) { + if (mTabsAdapter != null) { + mTabsAdapter.notifyItemRemoved(position); + } + } + + @Override + public void tabChanged(int position) { + if (mTabsAdapter != null) { + mTabsAdapter.notifyItemChanged(position); + } + } + + private class LightningViewAdapter extends RecyclerView.Adapter { + + private final int mLayoutResourceId; + @Nullable private final Drawable mBackgroundTabDrawable; + @Nullable private final Drawable mForegroundTabDrawable; + @Nullable private final Bitmap mForegroundTabBitmap; + private ColorMatrix mColorMatrix; + private Paint mPaint; + private ColorFilter mFilter; + private static final float DESATURATED = 0.5f; + + private final boolean mDrawerTabs; + + public LightningViewAdapter(final boolean vertical) { + this.mLayoutResourceId = vertical ? R.layout.tab_list_item : R.layout.tab_list_item_horizontal; + this.mDrawerTabs = vertical; + + if (vertical) { + mBackgroundTabDrawable = null; + mForegroundTabBitmap = null; + mForegroundTabDrawable = ThemeUtils.getSelectedBackground(getContext(), mDarkTheme); + } else { + int backgroundColor = Utils.mixTwoColors(ThemeUtils.getPrimaryColor(getContext()), Color.BLACK, 0.75f); + Bitmap backgroundTabBitmap = Bitmap.createBitmap(Utils.dpToPx(175), Utils.dpToPx(30), Bitmap.Config.ARGB_8888); + Utils.drawTrapezoid(new Canvas(backgroundTabBitmap), backgroundColor, true); + mBackgroundTabDrawable = new BitmapDrawable(getResources(), backgroundTabBitmap); + + int foregroundColor = ThemeUtils.getPrimaryColor(getContext()); + mForegroundTabBitmap = Bitmap.createBitmap(Utils.dpToPx(175), Utils.dpToPx(30), Bitmap.Config.ARGB_8888); + Utils.drawTrapezoid(new Canvas(mForegroundTabBitmap), foregroundColor, false); + mForegroundTabDrawable = null; + } + } + + @NonNull + @Override + public LightningViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { + LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); + View view = inflater.inflate(mLayoutResourceId, viewGroup, false); + return new LightningViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull final LightningViewHolder holder, int position) { + holder.exitButton.setTag(position); + + ViewCompat.jumpDrawablesToCurrentState(holder.exitButton); + + LightningView web = mTabsManager.getTabAtPosition(position); + if (web == null) { + return; + } + holder.txtTitle.setText(web.getTitle()); + + final Bitmap favicon = web.getFavicon(); + if (web.isForegroundTab()) { + TextViewCompat.setTextAppearance(holder.txtTitle, R.style.boldText); + Drawable foregroundDrawable; + if (!mDrawerTabs) { + foregroundDrawable = new BitmapDrawable(getResources(), mForegroundTabBitmap); + if (!mIsIncognito && mColorMode) { + foregroundDrawable.setColorFilter(mUiController.getUiColor(), PorterDuff.Mode.SRC_IN); + } + } else { + foregroundDrawable = mForegroundTabDrawable; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + holder.layout.setBackground(foregroundDrawable); + } else { + holder.layout.setBackgroundDrawable(foregroundDrawable); + } + if (!mIsIncognito && mColorMode) { + mUiController.changeToolbarBackground(favicon, foregroundDrawable); + } + holder.favicon.setImageBitmap(favicon); + } else { + TextViewCompat.setTextAppearance(holder.txtTitle, R.style.normalText); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + holder.layout.setBackground(mBackgroundTabDrawable); + } else { + holder.layout.setBackgroundDrawable(mBackgroundTabDrawable); + } + holder.favicon.setImageBitmap(getDesaturatedBitmap(favicon)); + } + } + + @Override + public int getItemCount() { + return mTabsManager.size(); + } + + public Bitmap getDesaturatedBitmap(@NonNull Bitmap favicon) { + Bitmap grayscaleBitmap = Bitmap.createBitmap(favicon.getWidth(), + favicon.getHeight(), Bitmap.Config.ARGB_8888); + + Canvas c = new Canvas(grayscaleBitmap); + if (mColorMatrix == null || mFilter == null || mPaint == null) { + mPaint = new Paint(); + mColorMatrix = new ColorMatrix(); + mColorMatrix.setSaturation(DESATURATED); + mFilter = new ColorMatrixColorFilter(mColorMatrix); + mPaint.setColorFilter(mFilter); + } + + c.drawBitmap(favicon, 0, 0, mPaint); + return grayscaleBitmap; + } + + public class LightningViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { + + public LightningViewHolder(@NonNull View view) { + super(view); + txtTitle = (TextView) view.findViewById(R.id.textTab); + favicon = (ImageView) view.findViewById(R.id.faviconTab); + exit = (ImageView) view.findViewById(R.id.deleteButton); + layout = (LinearLayout) view.findViewById(R.id.tab_item_background); + exitButton = (FrameLayout) view.findViewById(R.id.deleteAction); + exit.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); + + exitButton.setOnClickListener(this); + layout.setOnClickListener(this); + layout.setOnLongClickListener(this); + } + + @NonNull final TextView txtTitle; + @NonNull final ImageView favicon; + @NonNull final ImageView exit; + @NonNull final FrameLayout exitButton; + @NonNull final LinearLayout layout; + + @Override + public void onClick(View v) { + if (v == exitButton) { + // Close tab + mBus.post(new TabEvents.CloseTab(getAdapterPosition())); + } + if (v == layout) { + mBus.post(new TabEvents.ShowTab(getAdapterPosition())); + } + } + + @Override + public boolean onLongClick(View v) { + // Show close dialog + mBus.post(new TabEvents.ShowCloseDialog(getAdapterPosition())); + return true; + } + } + } +} diff --git a/app/src/main/java/acr/browser/lightning/fragment/anim/HorizontalItemAnimator.java b/app/src/main/java/acr/browser/lightning/fragment/anim/HorizontalItemAnimator.java new file mode 100644 index 000000000..2015f3c8d --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/fragment/anim/HorizontalItemAnimator.java @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package acr.browser.lightning.fragment.anim; + +import android.support.v4.animation.AnimatorCompatHelper; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.support.v4.view.ViewPropertyAnimatorListener; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.support.v7.widget.SimpleItemAnimator; +import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; + +import java.util.ArrayList; +import java.util.List; + +/** + * This implementation of {@link RecyclerView.ItemAnimator} provides basic + * animations on remove, add, and move events that happen to the items in + * a RecyclerView. RecyclerView uses a HorizontalItemAnimator by default. + * + * @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator) + */ +public class HorizontalItemAnimator extends SimpleItemAnimator { + private static final boolean DEBUG = false; + + private ArrayList mPendingRemovals = new ArrayList<>(); + private ArrayList mPendingAdditions = new ArrayList<>(); + private ArrayList mPendingMoves = new ArrayList<>(); + private ArrayList mPendingChanges = new ArrayList<>(); + + private ArrayList> mAdditionsList = new ArrayList<>(); + private ArrayList> mMovesList = new ArrayList<>(); + private ArrayList> mChangesList = new ArrayList<>(); + + private ArrayList mAddAnimations = new ArrayList<>(); + private ArrayList mMoveAnimations = new ArrayList<>(); + private ArrayList mRemoveAnimations = new ArrayList<>(); + private ArrayList mChangeAnimations = new ArrayList<>(); + + private static class MoveInfo { + public ViewHolder holder; + public int fromX, fromY, toX, toY; + + private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) { + this.holder = holder; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + } + + private static class ChangeInfo { + public ViewHolder oldHolder, newHolder; + public int fromX, fromY, toX, toY; + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) { + this.oldHolder = oldHolder; + this.newHolder = newHolder; + } + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + this(oldHolder, newHolder); + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + + @Override + public String toString() { + return "ChangeInfo{" + + "oldHolder=" + oldHolder + + ", newHolder=" + newHolder + + ", fromX=" + fromX + + ", fromY=" + fromY + + ", toX=" + toX + + ", toY=" + toY + + '}'; + } + } + + @Override + public void runPendingAnimations() { + boolean removalsPending = !mPendingRemovals.isEmpty(); + boolean movesPending = !mPendingMoves.isEmpty(); + boolean changesPending = !mPendingChanges.isEmpty(); + boolean additionsPending = !mPendingAdditions.isEmpty(); + if (!removalsPending && !movesPending && !additionsPending && !changesPending) { + // nothing to animate + return; + } + // First, remove stuff + for (ViewHolder holder : mPendingRemovals) { + animateRemoveImpl(holder); + } + mPendingRemovals.clear(); + // Next, move stuff + if (movesPending) { + final ArrayList moves = new ArrayList<>(); + moves.addAll(mPendingMoves); + mMovesList.add(moves); + mPendingMoves.clear(); + Runnable mover = new Runnable() { + @Override + public void run() { + for (MoveInfo moveInfo : moves) { + animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, + moveInfo.toX, moveInfo.toY); + } + moves.clear(); + mMovesList.remove(moves); + } + }; + if (removalsPending) { + View view = moves.get(0).holder.itemView; + ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration()); + } else { + mover.run(); + } + } + // Next, change stuff, to run in parallel with move animations + if (changesPending) { + final ArrayList changes = new ArrayList<>(); + changes.addAll(mPendingChanges); + mChangesList.add(changes); + mPendingChanges.clear(); + Runnable changer = new Runnable() { + @Override + public void run() { + for (ChangeInfo change : changes) { + animateChangeImpl(change); + } + changes.clear(); + mChangesList.remove(changes); + } + }; + if (removalsPending) { + ViewHolder holder = changes.get(0).oldHolder; + ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration()); + } else { + changer.run(); + } + } + // Next, add stuff + if (additionsPending) { + final ArrayList additions = new ArrayList<>(); + additions.addAll(mPendingAdditions); + mAdditionsList.add(additions); + mPendingAdditions.clear(); + Runnable adder = new Runnable() { + public void run() { + for (ViewHolder holder : additions) { + animateAddImpl(holder); + } + additions.clear(); + mAdditionsList.remove(additions); + } + }; + if (removalsPending || movesPending || changesPending) { + long removeDuration = removalsPending ? getRemoveDuration() : 0; + long moveDuration = movesPending ? getMoveDuration() : 0; + long changeDuration = changesPending ? getChangeDuration() : 0; + long totalDelay = removeDuration + Math.max(moveDuration, changeDuration); + View view = additions.get(0).itemView; + ViewCompat.postOnAnimationDelayed(view, adder, totalDelay); + } else { + adder.run(); + } + } + } + + @Override + public boolean animateRemove(final ViewHolder holder) { + resetAnimation(holder); + mPendingRemovals.add(holder); + return true; + } + + private void animateRemoveImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mRemoveAnimations.add(holder); + animation.setDuration(getRemoveDuration()) + .alpha(0).translationY(holder.itemView.getHeight()) + .setInterpolator(new AccelerateInterpolator()).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchRemoveStarting(holder); + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + ViewCompat.setAlpha(view, 1); + ViewCompat.setTranslationY(view, 0); + dispatchRemoveFinished(holder); + mRemoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateAdd(final ViewHolder holder) { + resetAnimation(holder); + ViewCompat.setAlpha(holder.itemView, 0); + ViewCompat.setTranslationY(holder.itemView, holder.itemView.getHeight()); + mPendingAdditions.add(holder); + return true; + } + + private void animateAddImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mAddAnimations.add(holder); + animation.alpha(1).translationY(0) + .setInterpolator(new DecelerateInterpolator()).setDuration(getAddDuration()) + .setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchAddStarting(holder); + } + + @Override + public void onAnimationCancel(View view) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setAlpha(view, 1); + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchAddFinished(holder); + mAddAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateMove(final ViewHolder holder, int fromX, int fromY, + int toX, int toY) { + final View view = holder.itemView; + fromX += ViewCompat.getTranslationX(holder.itemView); + fromY += ViewCompat.getTranslationY(holder.itemView); + int deltaX = toX - fromX; + int deltaY = toY - fromY; + if (deltaX == 0 && deltaY == 0) { + dispatchMoveFinished(holder); + return false; + } + resetAnimation(holder); + if (deltaX != 0) { + ViewCompat.setTranslationX(view, -deltaX); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, -deltaY); + } + mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY)); + return true; + } + + private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) { + final View view = holder.itemView; + final int deltaX = toX - fromX; + final int deltaY = toY - fromY; + if (deltaX != 0) { + ViewCompat.animate(view).translationX(0); + } + if (deltaY != 0) { + ViewCompat.animate(view).translationY(0); + } + // TODO: make EndActions end listeners instead, since end actions aren't called when + // vpas are canceled (and can't end them. why?) + // need listener functionality in VPACompat for this. Ick. + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mMoveAnimations.add(holder); + animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchMoveStarting(holder); + } + + @Override + public void onAnimationCancel(View view) { + if (deltaX != 0) { + ViewCompat.setTranslationX(view, 0); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, 0); + } + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchMoveFinished(holder); + mMoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { +// if (oldHolder != newHolder) { +// if (oldHolder != null) { +// dispatchChangeFinished(oldHolder, true); +// } +// if (newHolder != null) { +// dispatchChangeFinished(newHolder, false); +// } +// } else if (oldHolder != null) { +// dispatchChangeFinished(oldHolder, true); +// } +// return false; + if (oldHolder == newHolder) { + // Don't know how to run change animations when the same view holder is re-used. + // run a move animation to handle position changes. + if ((fromX - toX) == 0 && (fromY - toY) == 0) { + dispatchMoveFinished(oldHolder); + return false; + } + return animateMove(oldHolder, fromX, fromY, toX, toY); + } + final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView); + final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView); + final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView); + resetAnimation(oldHolder); + int deltaX = (int) (toX - fromX - prevTranslationX); + int deltaY = (int) (toY - fromY - prevTranslationY); + // recover prev translation state after ending animation + ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX); + ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY); + ViewCompat.setAlpha(oldHolder.itemView, prevAlpha); + if (newHolder != null) { + // carry over translation values + resetAnimation(newHolder); + ViewCompat.setTranslationX(newHolder.itemView, -deltaX); + ViewCompat.setTranslationY(newHolder.itemView, -deltaY); + ViewCompat.setAlpha(newHolder.itemView, 0); + } + mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); + return true; + } + + private void animateChangeImpl(final ChangeInfo changeInfo) { + final ViewHolder holder = changeInfo.oldHolder; + final View view = holder == null ? null : holder.itemView; + final ViewHolder newHolder = changeInfo.newHolder; + final View newView = newHolder != null ? newHolder.itemView : null; + if (view != null) { + final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration( + getChangeDuration()); + mChangeAnimations.add(changeInfo.oldHolder); + oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX); + oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY); + oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.oldHolder, true); + } + + @Override + public void onAnimationEnd(View view) { + oldViewAnim.setListener(null); + ViewCompat.setAlpha(view, 1); + ViewCompat.setTranslationX(view, 0); + ViewCompat.setTranslationY(view, 0); + dispatchChangeFinished(changeInfo.oldHolder, true); + mChangeAnimations.remove(changeInfo.oldHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + if (newView != null) { + final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView); + mChangeAnimations.add(changeInfo.newHolder); + newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()). + alpha(1).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.newHolder, false); + } + + @Override + public void onAnimationEnd(View view) { + newViewAnimation.setListener(null); + ViewCompat.setAlpha(newView, 1); + ViewCompat.setTranslationX(newView, 0); + ViewCompat.setTranslationY(newView, 0); + dispatchChangeFinished(changeInfo.newHolder, false); + mChangeAnimations.remove(changeInfo.newHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + } + + private void endChangeAnimation(List infoList, ViewHolder item) { + for (int i = infoList.size() - 1; i >= 0; i--) { + ChangeInfo changeInfo = infoList.get(i); + if (endChangeAnimationIfNecessary(changeInfo, item)) { + if (changeInfo.oldHolder == null && changeInfo.newHolder == null) { + infoList.remove(changeInfo); + } + } + } + } + + private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) { + if (changeInfo.oldHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder); + } + if (changeInfo.newHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder); + } + } + + private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) { + boolean oldItem = false; + if (changeInfo.newHolder == item) { + changeInfo.newHolder = null; + } else if (changeInfo.oldHolder == item) { + changeInfo.oldHolder = null; + oldItem = true; + } else { + return false; + } + ViewCompat.setAlpha(item.itemView, 1); + ViewCompat.setTranslationX(item.itemView, 0); + ViewCompat.setTranslationY(item.itemView, 0); + dispatchChangeFinished(item, oldItem); + return true; + } + + @Override + public void endAnimation(ViewHolder item) { + final View view = item.itemView; + // this will trigger end callback which should set properties to their target values. + ViewCompat.animate(view).cancel(); + // TODO if some other animations are chained to end, how do we cancel them as well? + for (int i = mPendingMoves.size() - 1; i >= 0; i--) { + MoveInfo moveInfo = mPendingMoves.get(i); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + mPendingMoves.remove(i); + } + } + endChangeAnimation(mPendingChanges, item); + if (mPendingRemovals.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchRemoveFinished(item); + } + if (mPendingAdditions.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + } + + for (int i = mChangesList.size() - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + endChangeAnimation(changes, item); + if (changes.isEmpty()) { + mChangesList.remove(i); + } + } + for (int i = mMovesList.size() - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + for (int j = moves.size() - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(i); + } + break; + } + } + } + for (int i = mAdditionsList.size() - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + if (additions.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + if (additions.isEmpty()) { + mAdditionsList.remove(i); + } + } + } + + // animations should be ended by the cancel above. + //noinspection PointlessBooleanExpression,ConstantConditions + if (mRemoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mRemoveAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mAddAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mAddAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mChangeAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mChangeAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mMoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mMoveAnimations list"); + } + dispatchFinishedWhenDone(); + } + + private void resetAnimation(ViewHolder holder) { + AnimatorCompatHelper.clearInterpolator(holder.itemView); + endAnimation(holder); + } + + @Override + public boolean isRunning() { + return (!mPendingAdditions.isEmpty() || + !mPendingChanges.isEmpty() || + !mPendingMoves.isEmpty() || + !mPendingRemovals.isEmpty() || + !mMoveAnimations.isEmpty() || + !mRemoveAnimations.isEmpty() || + !mAddAnimations.isEmpty() || + !mChangeAnimations.isEmpty() || + !mMovesList.isEmpty() || + !mAdditionsList.isEmpty() || + !mChangesList.isEmpty()); + } + + /** + * Check the state of currently pending and running animations. If there are none + * pending/running, call {@link #dispatchAnimationsFinished()} to notify any + * listeners. + */ + private void dispatchFinishedWhenDone() { + if (!isRunning()) { + dispatchAnimationsFinished(); + } + } + + @Override + public void endAnimations() { + int count = mPendingMoves.size(); + for (int i = count - 1; i >= 0; i--) { + MoveInfo item = mPendingMoves.get(i); + View view = item.holder.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item.holder); + mPendingMoves.remove(i); + } + count = mPendingRemovals.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingRemovals.get(i); + dispatchRemoveFinished(item); + mPendingRemovals.remove(i); + } + count = mPendingAdditions.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingAdditions.get(i); + View view = item.itemView; + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + mPendingAdditions.remove(i); + } + count = mPendingChanges.size(); + for (int i = count - 1; i >= 0; i--) { + endChangeAnimationIfNecessary(mPendingChanges.get(i)); + } + mPendingChanges.clear(); + if (!isRunning()) { + return; + } + + int listCount = mMovesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + count = moves.size(); + for (int j = count - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + ViewHolder item = moveInfo.holder; + View view = item.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(moveInfo.holder); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(moves); + } + } + } + listCount = mAdditionsList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + count = additions.size(); + for (int j = count - 1; j >= 0; j--) { + ViewHolder item = additions.get(j); + View view = item.itemView; + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + additions.remove(j); + if (additions.isEmpty()) { + mAdditionsList.remove(additions); + } + } + } + listCount = mChangesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + count = changes.size(); + for (int j = count - 1; j >= 0; j--) { + endChangeAnimationIfNecessary(changes.get(j)); + if (changes.isEmpty()) { + mChangesList.remove(changes); + } + } + } + + cancelAll(mRemoveAnimations); + cancelAll(mMoveAnimations); + cancelAll(mAddAnimations); + cancelAll(mChangeAnimations); + + dispatchAnimationsFinished(); + } + + static void cancelAll(List viewHolders) { + for (int i = viewHolders.size() - 1; i >= 0; i--) { + ViewCompat.animate(viewHolders.get(i).itemView).cancel(); + } + } + + private static class VpaListenerAdapter implements ViewPropertyAnimatorListener { + @Override + public void onAnimationStart(View view) {} + + @Override + public void onAnimationEnd(View view) {} + + @Override + public void onAnimationCancel(View view) {} + } +} diff --git a/app/src/main/java/acr/browser/lightning/fragment/anim/VerticalItemAnimator.java b/app/src/main/java/acr/browser/lightning/fragment/anim/VerticalItemAnimator.java new file mode 100644 index 000000000..3db62e913 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/fragment/anim/VerticalItemAnimator.java @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package acr.browser.lightning.fragment.anim; + +import android.support.v4.animation.AnimatorCompatHelper; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.support.v4.view.ViewPropertyAnimatorListener; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.support.v7.widget.SimpleItemAnimator; +import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; + +import java.util.ArrayList; +import java.util.List; + +/** + * This implementation of {@link RecyclerView.ItemAnimator} provides basic + * animations on remove, add, and move events that happen to the items in + * a RecyclerView. RecyclerView uses a VerticalItemAnimator by default. + * + * @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator) + */ +public class VerticalItemAnimator extends SimpleItemAnimator { + private static final boolean DEBUG = false; + + private ArrayList mPendingRemovals = new ArrayList<>(); + private ArrayList mPendingAdditions = new ArrayList<>(); + private ArrayList mPendingMoves = new ArrayList<>(); + private ArrayList mPendingChanges = new ArrayList<>(); + + private ArrayList> mAdditionsList = new ArrayList<>(); + private ArrayList> mMovesList = new ArrayList<>(); + private ArrayList> mChangesList = new ArrayList<>(); + + private ArrayList mAddAnimations = new ArrayList<>(); + private ArrayList mMoveAnimations = new ArrayList<>(); + private ArrayList mRemoveAnimations = new ArrayList<>(); + private ArrayList mChangeAnimations = new ArrayList<>(); + + private static class MoveInfo { + public ViewHolder holder; + public int fromX, fromY, toX, toY; + + private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) { + this.holder = holder; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + } + + private static class ChangeInfo { + public ViewHolder oldHolder, newHolder; + public int fromX, fromY, toX, toY; + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) { + this.oldHolder = oldHolder; + this.newHolder = newHolder; + } + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + this(oldHolder, newHolder); + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + + @Override + public String toString() { + return "ChangeInfo{" + + "oldHolder=" + oldHolder + + ", newHolder=" + newHolder + + ", fromX=" + fromX + + ", fromY=" + fromY + + ", toX=" + toX + + ", toY=" + toY + + '}'; + } + } + + @Override + public void runPendingAnimations() { + boolean removalsPending = !mPendingRemovals.isEmpty(); + boolean movesPending = !mPendingMoves.isEmpty(); + boolean changesPending = !mPendingChanges.isEmpty(); + boolean additionsPending = !mPendingAdditions.isEmpty(); + if (!removalsPending && !movesPending && !additionsPending && !changesPending) { + // nothing to animate + return; + } + // First, remove stuff + for (ViewHolder holder : mPendingRemovals) { + animateRemoveImpl(holder); + } + mPendingRemovals.clear(); + // Next, move stuff + if (movesPending) { + final ArrayList moves = new ArrayList<>(); + moves.addAll(mPendingMoves); + mMovesList.add(moves); + mPendingMoves.clear(); + Runnable mover = new Runnable() { + @Override + public void run() { + for (MoveInfo moveInfo : moves) { + animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, + moveInfo.toX, moveInfo.toY); + } + moves.clear(); + mMovesList.remove(moves); + } + }; + if (removalsPending) { + View view = moves.get(0).holder.itemView; + ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration()); + } else { + mover.run(); + } + } + // Next, change stuff, to run in parallel with move animations + if (changesPending) { + final ArrayList changes = new ArrayList<>(); + changes.addAll(mPendingChanges); + mChangesList.add(changes); + mPendingChanges.clear(); + Runnable changer = new Runnable() { + @Override + public void run() { + for (ChangeInfo change : changes) { + animateChangeImpl(change); + } + changes.clear(); + mChangesList.remove(changes); + } + }; + if (removalsPending) { + ViewHolder holder = changes.get(0).oldHolder; + ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration()); + } else { + changer.run(); + } + } + // Next, add stuff + if (additionsPending) { + final ArrayList additions = new ArrayList<>(); + additions.addAll(mPendingAdditions); + mAdditionsList.add(additions); + mPendingAdditions.clear(); + Runnable adder = new Runnable() { + public void run() { + for (ViewHolder holder : additions) { + animateAddImpl(holder); + } + additions.clear(); + mAdditionsList.remove(additions); + } + }; + if (removalsPending || movesPending || changesPending) { + long removeDuration = removalsPending ? getRemoveDuration() : 0; + long moveDuration = movesPending ? getMoveDuration() : 0; + long changeDuration = changesPending ? getChangeDuration() : 0; + long totalDelay = removeDuration + Math.max(moveDuration, changeDuration); + View view = additions.get(0).itemView; + ViewCompat.postOnAnimationDelayed(view, adder, totalDelay); + } else { + adder.run(); + } + } + } + + @Override + public boolean animateRemove(final ViewHolder holder) { + resetAnimation(holder); + mPendingRemovals.add(holder); + return true; + } + + private void animateRemoveImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mRemoveAnimations.add(holder); + animation.setDuration(getRemoveDuration()) + .alpha(0).translationX(-holder.itemView.getWidth() / 2) + .setInterpolator(new AccelerateInterpolator()).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchRemoveStarting(holder); + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + ViewCompat.setAlpha(view, 1); + ViewCompat.setTranslationX(view, 0); + dispatchRemoveFinished(holder); + mRemoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateAdd(final ViewHolder holder) { + resetAnimation(holder); + ViewCompat.setAlpha(holder.itemView, 0); + ViewCompat.setTranslationX(holder.itemView, -holder.itemView.getWidth() / 2); + mPendingAdditions.add(holder); + return true; + } + + private void animateAddImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mAddAnimations.add(holder); + animation.alpha(1).translationX(0).setDuration(getAddDuration()) + .setInterpolator(new DecelerateInterpolator()).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchAddStarting(holder); + } + + @Override + public void onAnimationCancel(View view) { + ViewCompat.setAlpha(view, 1); + ViewCompat.setTranslationX(view, 0); + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchAddFinished(holder); + mAddAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateMove(final ViewHolder holder, int fromX, int fromY, + int toX, int toY) { + final View view = holder.itemView; + fromX += ViewCompat.getTranslationX(holder.itemView); + fromY += ViewCompat.getTranslationY(holder.itemView); + int deltaX = toX - fromX; + int deltaY = toY - fromY; + if (deltaX == 0 && deltaY == 0) { + dispatchMoveFinished(holder); + return false; + } + resetAnimation(holder); + if (deltaX != 0) { + ViewCompat.setTranslationX(view, -deltaX); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, -deltaY); + } + mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY)); + return true; + } + + private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) { + final View view = holder.itemView; + final int deltaX = toX - fromX; + final int deltaY = toY - fromY; + if (deltaX != 0) { + ViewCompat.animate(view).translationX(0); + } + if (deltaY != 0) { + ViewCompat.animate(view).translationY(0); + } + // TODO: make EndActions end listeners instead, since end actions aren't called when + // vpas are canceled (and can't end them. why?) + // need listener functionality in VPACompat for this. Ick. + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mMoveAnimations.add(holder); + animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchMoveStarting(holder); + } + + @Override + public void onAnimationCancel(View view) { + if (deltaX != 0) { + ViewCompat.setTranslationX(view, 0); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, 0); + } + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchMoveFinished(holder); + mMoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { +// if (oldHolder != newHolder) { +// if (oldHolder != null) { +// dispatchChangeFinished(oldHolder, true); +// } +// if (newHolder != null) { +// dispatchChangeFinished(newHolder, false); +// } +// } else if (oldHolder != null) { +// dispatchChangeFinished(oldHolder, true); +// } +// return false; + if (oldHolder == newHolder) { + // Don't know how to run change animations when the same view holder is re-used. + // run a move animation to handle position changes. + if ((fromX - toX) == 0 && (fromY - toY) == 0) { + dispatchMoveFinished(oldHolder); + return false; + } + return animateMove(oldHolder, fromX, fromY, toX, toY); + } + final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView); + final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView); + final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView); + resetAnimation(oldHolder); + int deltaX = (int) (toX - fromX - prevTranslationX); + int deltaY = (int) (toY - fromY - prevTranslationY); + // recover prev translation state after ending animation + ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX); + ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY); + ViewCompat.setAlpha(oldHolder.itemView, prevAlpha); + if (newHolder != null) { + // carry over translation values + resetAnimation(newHolder); + ViewCompat.setTranslationX(newHolder.itemView, -deltaX); + ViewCompat.setTranslationY(newHolder.itemView, -deltaY); + ViewCompat.setAlpha(newHolder.itemView, 0); + } + mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); + return true; + } + + private void animateChangeImpl(final ChangeInfo changeInfo) { + final ViewHolder holder = changeInfo.oldHolder; + final View view = holder == null ? null : holder.itemView; + final ViewHolder newHolder = changeInfo.newHolder; + final View newView = newHolder != null ? newHolder.itemView : null; + if (view != null) { + final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration( + getChangeDuration()); + mChangeAnimations.add(changeInfo.oldHolder); + oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX); + oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY); + oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.oldHolder, true); + } + + @Override + public void onAnimationEnd(View view) { + oldViewAnim.setListener(null); + ViewCompat.setAlpha(view, 1); + ViewCompat.setTranslationX(view, 0); + ViewCompat.setTranslationY(view, 0); + dispatchChangeFinished(changeInfo.oldHolder, true); + mChangeAnimations.remove(changeInfo.oldHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + if (newView != null) { + final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView); + mChangeAnimations.add(changeInfo.newHolder); + newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()). + alpha(1).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.newHolder, false); + } + + @Override + public void onAnimationEnd(View view) { + newViewAnimation.setListener(null); + ViewCompat.setAlpha(newView, 1); + ViewCompat.setTranslationX(newView, 0); + ViewCompat.setTranslationY(newView, 0); + dispatchChangeFinished(changeInfo.newHolder, false); + mChangeAnimations.remove(changeInfo.newHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + } + + private void endChangeAnimation(List infoList, ViewHolder item) { + for (int i = infoList.size() - 1; i >= 0; i--) { + ChangeInfo changeInfo = infoList.get(i); + if (endChangeAnimationIfNecessary(changeInfo, item)) { + if (changeInfo.oldHolder == null && changeInfo.newHolder == null) { + infoList.remove(changeInfo); + } + } + } + } + + private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) { + if (changeInfo.oldHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder); + } + if (changeInfo.newHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder); + } + } + + private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) { + boolean oldItem = false; + if (changeInfo.newHolder == item) { + changeInfo.newHolder = null; + } else if (changeInfo.oldHolder == item) { + changeInfo.oldHolder = null; + oldItem = true; + } else { + return false; + } + ViewCompat.setAlpha(item.itemView, 1); + ViewCompat.setTranslationX(item.itemView, 0); + ViewCompat.setTranslationY(item.itemView, 0); + dispatchChangeFinished(item, oldItem); + return true; + } + + @Override + public void endAnimation(ViewHolder item) { + final View view = item.itemView; + // this will trigger end callback which should set properties to their target values. + ViewCompat.animate(view).cancel(); + // TODO if some other animations are chained to end, how do we cancel them as well? + for (int i = mPendingMoves.size() - 1; i >= 0; i--) { + MoveInfo moveInfo = mPendingMoves.get(i); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + mPendingMoves.remove(i); + } + } + endChangeAnimation(mPendingChanges, item); + if (mPendingRemovals.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchRemoveFinished(item); + } + if (mPendingAdditions.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + } + + for (int i = mChangesList.size() - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + endChangeAnimation(changes, item); + if (changes.isEmpty()) { + mChangesList.remove(i); + } + } + for (int i = mMovesList.size() - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + for (int j = moves.size() - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(i); + } + break; + } + } + } + for (int i = mAdditionsList.size() - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + if (additions.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + if (additions.isEmpty()) { + mAdditionsList.remove(i); + } + } + } + + // animations should be ended by the cancel above. + //noinspection PointlessBooleanExpression,ConstantConditions + if (mRemoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mRemoveAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mAddAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mAddAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mChangeAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mChangeAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mMoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mMoveAnimations list"); + } + dispatchFinishedWhenDone(); + } + + private void resetAnimation(ViewHolder holder) { + AnimatorCompatHelper.clearInterpolator(holder.itemView); + endAnimation(holder); + } + + @Override + public boolean isRunning() { + return (!mPendingAdditions.isEmpty() || + !mPendingChanges.isEmpty() || + !mPendingMoves.isEmpty() || + !mPendingRemovals.isEmpty() || + !mMoveAnimations.isEmpty() || + !mRemoveAnimations.isEmpty() || + !mAddAnimations.isEmpty() || + !mChangeAnimations.isEmpty() || + !mMovesList.isEmpty() || + !mAdditionsList.isEmpty() || + !mChangesList.isEmpty()); + } + + /** + * Check the state of currently pending and running animations. If there are none + * pending/running, call {@link #dispatchAnimationsFinished()} to notify any + * listeners. + */ + private void dispatchFinishedWhenDone() { + if (!isRunning()) { + dispatchAnimationsFinished(); + } + } + + @Override + public void endAnimations() { + int count = mPendingMoves.size(); + for (int i = count - 1; i >= 0; i--) { + MoveInfo item = mPendingMoves.get(i); + View view = item.holder.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item.holder); + mPendingMoves.remove(i); + } + count = mPendingRemovals.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingRemovals.get(i); + dispatchRemoveFinished(item); + mPendingRemovals.remove(i); + } + count = mPendingAdditions.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingAdditions.get(i); + View view = item.itemView; + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + mPendingAdditions.remove(i); + } + count = mPendingChanges.size(); + for (int i = count - 1; i >= 0; i--) { + endChangeAnimationIfNecessary(mPendingChanges.get(i)); + } + mPendingChanges.clear(); + if (!isRunning()) { + return; + } + + int listCount = mMovesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + count = moves.size(); + for (int j = count - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + ViewHolder item = moveInfo.holder; + View view = item.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(moveInfo.holder); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(moves); + } + } + } + listCount = mAdditionsList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + count = additions.size(); + for (int j = count - 1; j >= 0; j--) { + ViewHolder item = additions.get(j); + View view = item.itemView; + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + additions.remove(j); + if (additions.isEmpty()) { + mAdditionsList.remove(additions); + } + } + } + listCount = mChangesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + count = changes.size(); + for (int j = count - 1; j >= 0; j--) { + endChangeAnimationIfNecessary(changes.get(j)); + if (changes.isEmpty()) { + mChangesList.remove(changes); + } + } + } + + cancelAll(mRemoveAnimations); + cancelAll(mMoveAnimations); + cancelAll(mAddAnimations); + cancelAll(mChangeAnimations); + + dispatchAnimationsFinished(); + } + + static void cancelAll(List viewHolders) { + for (int i = viewHolders.size() - 1; i >= 0; i--) { + ViewCompat.animate(viewHolders.get(i).itemView).cancel(); + } + } + + private static class VpaListenerAdapter implements ViewPropertyAnimatorListener { + @Override + public void onAnimationStart(View view) {} + + @Override + public void onAnimationEnd(View view) {} + + @Override + public void onAnimationCancel(View view) {} + } +} diff --git a/app/src/main/java/acr/browser/lightning/object/ClickHandler.java b/app/src/main/java/acr/browser/lightning/object/ClickHandler.java deleted file mode 100644 index 841c03f28..000000000 --- a/app/src/main/java/acr/browser/lightning/object/ClickHandler.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2014 A.C.R. Development - */ -package acr.browser.lightning.object; - -import android.content.Context; -import android.os.Handler; -import android.os.Message; - -import acr.browser.lightning.controller.BrowserController; - -public class ClickHandler extends Handler { - - private BrowserController mBrowserController; - - public ClickHandler(Context context) { - try { - mBrowserController = (BrowserController) context; - } catch (ClassCastException e) { - throw new ClassCastException(context + " must implement BrowserController"); - } - } - - @Override - public void handleMessage(Message msg) { - super.handleMessage(msg); - String url = msg.getData().getString("url"); - mBrowserController.longClickPage(url); - } -} diff --git a/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java b/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java index 0fda30e4f..b0e9af0b2 100644 --- a/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java +++ b/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java @@ -1,11 +1,17 @@ package acr.browser.lightning.preference; +import android.content.Context; import android.content.SharedPreferences; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import javax.inject.Inject; +import javax.inject.Singleton; -import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.download.DownloadHandler; +@Singleton public class PreferenceManager { private static class Name { @@ -29,7 +35,6 @@ private static class Name { public static final String SEARCH_URL = "searchurl"; public static final String TEXT_REFLOW = "textreflow"; public static final String TEXT_SIZE = "textsize"; - public static final String URL_MEMORY = "memory"; public static final String USE_WIDE_VIEWPORT = "wideviewport"; public static final String USER_AGENT = "agentchoose"; public static final String USER_AGENT_STRING = "userAgentString"; @@ -47,6 +52,8 @@ private static class Name { public static final String TEXT_ENCODING = "textEncoding"; public static final String CLEAR_WEBSTORAGE_EXIT = "clearWebStorageExit"; public static final String SHOW_TABS_IN_DRAWER = "showTabsInDrawer"; + public static final String DO_NOT_TRACK = "doNotTrack"; + public static final String IDENTIFYING_HEADERS = "removeIdentifyingHeaders"; public static final String USE_PROXY = "useProxy"; public static final String PROXY_CHOICE = "proxyChoice"; @@ -56,20 +63,13 @@ private static class Name { public static final String INITIAL_CHECK_FOR_I2P = "checkForI2P"; } - private static PreferenceManager mInstance; - private final SharedPreferences mPrefs; + @NonNull private final SharedPreferences mPrefs; private static final String PREFERENCES = "settings"; - public static PreferenceManager getInstance() { - if (mInstance == null) { - mInstance = new PreferenceManager(); - } - return mInstance; - } - - private PreferenceManager() { - mPrefs = BrowserApp.getAppContext().getSharedPreferences(PREFERENCES, 0); + @Inject + PreferenceManager(@NonNull final Context context) { + mPrefs = context.getSharedPreferences(PREFERENCES, 0); } public boolean getAdBlockEnabled() { @@ -109,13 +109,14 @@ public boolean getClearHistoryExitEnabled() { } public boolean getColorModeEnabled() { - return mPrefs.getBoolean(Name.ENABLE_COLOR_MODE, false); + return mPrefs.getBoolean(Name.ENABLE_COLOR_MODE, true); } public boolean getCookiesEnabled() { return mPrefs.getBoolean(Name.COOKIES, true); } + @NonNull public String getDownloadDirectory() { return mPrefs.getString(Name.DOWNLOAD_DIRECTORY, DownloadHandler.DEFAULT_DOWNLOAD_PATH); } @@ -136,6 +137,7 @@ public boolean getHideStatusBarEnabled() { return mPrefs.getBoolean(Name.HIDE_STATUS_BAR, false); } + @NonNull public String getHomepage() { return mPrefs.getString(Name.HOMEPAGE, Constants.HOMEPAGE); } @@ -156,10 +158,6 @@ public boolean getLocationEnabled() { return mPrefs.getBoolean(Name.LOCATION, false); } - public String getMemoryUrl() { - return mPrefs.getString(Name.URL_MEMORY, ""); - } - public boolean getOverviewModeEnabled() { return mPrefs.getBoolean(Name.OVERVIEW_MODE, true); } @@ -168,6 +166,7 @@ public boolean getPopupsEnabled() { return mPrefs.getBoolean(Name.POPUPS, true); } + @NonNull public String getProxyHost() { return mPrefs.getString(Name.USE_PROXY_HOST, "localhost"); } @@ -188,6 +187,7 @@ public boolean getRestoreLostTabsEnabled() { return mPrefs.getBoolean(Name.RESTORE_LOST_TABS, true); } + @Nullable public String getSavedUrl() { return mPrefs.getString(Name.SAVE_URL, null); } @@ -200,6 +200,7 @@ public int getSearchChoice() { return mPrefs.getInt(Name.SEARCH, 1); } + @NonNull public String getSearchUrl() { return mPrefs.getString(Name.SEARCH_URL, Constants.GOOGLE_SEARCH); } @@ -232,7 +233,8 @@ public int getUserAgentChoice() { return mPrefs.getInt(Name.USER_AGENT, 1); } - public String getUserAgentString(String def) { + @Nullable + public String getUserAgentString(@Nullable String def) { return mPrefs.getString(Name.USER_AGENT_STRING, def); } @@ -240,6 +242,7 @@ public boolean getUseWideViewportEnabled() { return mPrefs.getBoolean(Name.USE_WIDE_VIEWPORT, true); } + @NonNull public String getTextEncoding() { return mPrefs.getString(Name.TEXT_ENCODING, Constants.DEFAULT_ENCODING); } @@ -248,23 +251,39 @@ public boolean getShowTabsInDrawer(boolean defaultValue) { return mPrefs.getBoolean(Name.SHOW_TABS_IN_DRAWER, defaultValue); } - private void putBoolean(String name, boolean value) { + public boolean getDoNotTrackEnabled() { + return mPrefs.getBoolean(Name.DO_NOT_TRACK, false); + } + + public boolean getRemoveIdentifyingHeadersEnabled() { + return mPrefs.getBoolean(Name.IDENTIFYING_HEADERS, false); + } + + private void putBoolean(@NonNull String name, boolean value) { mPrefs.edit().putBoolean(name, value).apply(); } - private void putInt(String name, int value) { + private void putInt(@NonNull String name, int value) { mPrefs.edit().putInt(name, value).apply(); } - private void putString(String name, String value) { + private void putString(@NonNull String name, @Nullable String value) { mPrefs.edit().putString(name, value).apply(); } + public void setRemoveIdentifyingHeadersEnabled(boolean enabled) { + putBoolean(Name.IDENTIFYING_HEADERS, enabled); + } + + public void setDoNotTrackEnabled(boolean doNotTrack) { + putBoolean(Name.DO_NOT_TRACK, doNotTrack); + } + public void setShowTabsInDrawer(boolean show) { putBoolean(Name.SHOW_TABS_IN_DRAWER, show); } - public void setTextEncoding(String encoding) { + public void setTextEncoding(@NonNull String encoding) { putString(Name.TEXT_ENCODING, encoding); } @@ -312,7 +331,7 @@ public void setCookiesEnabled(boolean enable) { putBoolean(Name.COOKIES, enable); } - public void setDownloadDirectory(String directory) { + public void setDownloadDirectory(@NonNull String directory) { putString(Name.DOWNLOAD_DIRECTORY, directory); } @@ -332,7 +351,7 @@ public void setHideStatusBarEnabled(boolean enable) { putBoolean(Name.HIDE_STATUS_BAR, enable); } - public void setHomepage(String homepage) { + public void setHomepage(@NonNull String homepage) { putString(Name.HOMEPAGE, homepage); } @@ -352,10 +371,6 @@ public void setLocationEnabled(boolean enable) { putBoolean(Name.LOCATION, enable); } - public void setMemoryUrl(String url) { - putString(Name.URL_MEMORY, url); - } - public void setOverviewModeEnabled(boolean enable) { putBoolean(Name.OVERVIEW_MODE, enable); } @@ -376,7 +391,7 @@ public void setRestoreLostTabsEnabled(boolean enable) { putBoolean(Name.RESTORE_LOST_TABS, enable); } - public void setSavedUrl(String url) { + public void setSavedUrl(@Nullable String url) { putString(Name.SAVE_URL, url); } @@ -388,7 +403,7 @@ public void setSearchChoice(int choice) { putInt(Name.SEARCH, choice); } - public void setSearchUrl(String url) { + public void setSearchUrl(@NonNull String url) { putString(Name.SEARCH_URL, url); } @@ -423,7 +438,7 @@ public void setProxyChoice(int choice) { putInt(Name.PROXY_CHOICE, choice); } - public void setProxyHost(String proxyHost) { + public void setProxyHost(@NonNull String proxyHost) { putString(Name.USE_PROXY_HOST, proxyHost); } @@ -435,7 +450,7 @@ public void setUserAgentChoice(int choice) { putInt(Name.USER_AGENT, choice); } - public void setUserAgentString(String agent) { + public void setUserAgentString(@Nullable String agent) { putString(Name.USER_AGENT_STRING, agent); } diff --git a/app/src/main/java/acr/browser/lightning/react/Action.java b/app/src/main/java/acr/browser/lightning/react/Action.java new file mode 100644 index 000000000..0c1a22efa --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/react/Action.java @@ -0,0 +1,16 @@ +package acr.browser.lightning.react; + +import android.support.annotation.NonNull; + +public interface Action { + /** + * Should be overridden to send the subscriber + * events such as {@link Subscriber#onNext(Object)} + * or {@link Subscriber#onComplete()}. + * + * @param subscriber the subscriber that is sent in + * when the user of the Observable + * subscribes. + */ + void onSubscribe(@NonNull Subscriber subscriber); +} diff --git a/app/src/main/java/acr/browser/lightning/react/Observable.java b/app/src/main/java/acr/browser/lightning/react/Observable.java new file mode 100644 index 000000000..700a39ec4 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/react/Observable.java @@ -0,0 +1,254 @@ +package acr.browser.lightning.react; + +import android.os.Looper; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import java.util.concurrent.Executor; + +import acr.browser.lightning.utils.Preconditions; + +/** + * An RxJava implementation. This class allows work + * to be done on a certain thread and then allows + * items to be emitted on a different thread. It is + * a replacement for {@link android.os.AsyncTask}. + * + * @param the type that the Observable will emit. + */ +public class Observable { + + private static final String TAG = Observable.class.getSimpleName(); + + @NonNull private final Action mAction; + @Nullable private Executor mSubscriberThread; + @Nullable private Executor mObserverThread; + @NonNull private final Executor mDefault; + + private Observable(@NonNull Action action) { + mAction = action; + Looper looper = Looper.myLooper(); + Preconditions.checkNonNull(looper); + mDefault = new ThreadExecutor(looper); + } + + /** + * Static creator method that creates an Observable from the + * {@link Action} that is passed in as the parameter. Action + * must not be null. + * + * @param action the Action to perform + * @param the type that will be emitted to the onSubscribe + * @return a valid non-null Observable. + */ + @NonNull + public static Observable create(@NonNull Action action) { + Preconditions.checkNonNull(action); + return new Observable<>(action); + } + + /** + * Tells the Observable what Executor that the onSubscribe + * work should run on. + * + * @param subscribeExecutor the Executor to run the work on. + * @return returns this so that calls can be conveniently chained. + */ + public Observable subscribeOn(@NonNull Executor subscribeExecutor) { + mSubscriberThread = subscribeExecutor; + return this; + } + + /** + * Tells the Observable what Executor the onSubscribe should observe + * the work on. + * + * @param observerExecutor the Executor to run to callback on. + * @return returns this so that calls can be conveniently chained. + */ + public Observable observeOn(@NonNull Executor observerExecutor) { + mObserverThread = observerExecutor; + return this; + } + + /** + * Subscribes immediately to the Observable and ignores + * all onComplete and onNext calls. + */ + public void subscribe() { + executeOnSubscriberThread(new Runnable() { + @Override + public void run() { + mAction.onSubscribe(new Subscriber() { + @Override + public void unsubscribe() {} + + @Override + public void onComplete() {} + + @Override + public void onStart() {} + + @Override + public void onError(@NonNull Throwable throwable) {} + + @Override + public void onNext(T item) {} + }); + } + }); + } + + /** + * Immediately subscribes to the Observable and starts + * sending events from the Observable to the {@link OnSubscribe}. + * + * @param onSubscribe the class that wishes to receive onNext and + * onComplete callbacks from the Observable. + */ + public Subscription subscribe(@NonNull OnSubscribe onSubscribe) { + + Preconditions.checkNonNull(onSubscribe); + + final Subscriber subscriber = new SubscriberImpl<>(onSubscribe, this); + + subscriber.onStart(); + + executeOnSubscriberThread(new Runnable() { + @Override + public void run() { + mAction.onSubscribe(subscriber); + } + }); + + return subscriber; + } + + private void executeOnObserverThread(@NonNull Runnable runnable) { + if (mObserverThread != null) { + mObserverThread.execute(runnable); + } else { + mDefault.execute(runnable); + } + } + + private void executeOnSubscriberThread(@NonNull Runnable runnable) { + if (mSubscriberThread != null) { + mSubscriberThread.execute(runnable); + } else { + mDefault.execute(runnable); + } + } + + private static class SubscriberImpl implements Subscriber { + + @Nullable private volatile OnSubscribe mOnSubscribe; + @NonNull private final Observable mObservable; + private boolean mOnCompleteExecuted = false; + private boolean mOnError = false; + + public SubscriberImpl(@NonNull OnSubscribe onSubscribe, @NonNull Observable observable) { + mOnSubscribe = onSubscribe; + mObservable = observable; + } + + @Override + public void unsubscribe() { + mOnSubscribe = null; + } + + @Override + public void onComplete() { + OnSubscribe onSubscribe = mOnSubscribe; + if (!mOnCompleteExecuted && onSubscribe != null && !mOnError) { + mOnCompleteExecuted = true; + mObservable.executeOnObserverThread(new OnCompleteRunnable<>(onSubscribe)); + } else if (!mOnError) { + Log.e(TAG, "onComplete called more than once"); + throw new RuntimeException("onComplete called more than once"); + } + } + + @Override + public void onStart() { + OnSubscribe onSubscribe = mOnSubscribe; + if (onSubscribe != null) { + mObservable.executeOnObserverThread(new OnStartRunnable<>(onSubscribe)); + } + } + + @Override + public void onError(@NonNull final Throwable throwable) { + OnSubscribe onSubscribe = mOnSubscribe; + if (onSubscribe != null) { + mOnError = true; + mObservable.executeOnObserverThread(new OnErrorRunnable<>(onSubscribe, throwable)); + } + } + + @Override + public void onNext(final T item) { + OnSubscribe onSubscribe = mOnSubscribe; + if (!mOnCompleteExecuted && onSubscribe != null) { + mObservable.executeOnObserverThread(new OnNextRunnable<>(onSubscribe, item)); + } else { + Log.e(TAG, "onComplete has been already called, onNext should not be called"); + throw new RuntimeException("onNext should not be called after onComplete has been called"); + } + } + } + + private static class OnCompleteRunnable implements Runnable { + private final OnSubscribe onSubscribe; + + public OnCompleteRunnable(@NonNull OnSubscribe onSubscribe) {this.onSubscribe = onSubscribe;} + + @Override + public void run() { + onSubscribe.onComplete(); + } + } + + private static class OnNextRunnable implements Runnable { + private final OnSubscribe onSubscribe; + private final T item; + + public OnNextRunnable(@NonNull OnSubscribe onSubscribe, T item) { + this.onSubscribe = onSubscribe; + this.item = item; + } + + @Override + public void run() { + onSubscribe.onNext(item); + } + } + + private static class OnErrorRunnable implements Runnable { + private final OnSubscribe onSubscribe; + private final Throwable throwable; + + public OnErrorRunnable(@NonNull OnSubscribe onSubscribe, @NonNull Throwable throwable) { + this.onSubscribe = onSubscribe; + this.throwable = throwable; + } + + @Override + public void run() { + onSubscribe.onError(throwable); + } + } + + private static class OnStartRunnable implements Runnable { + private final OnSubscribe onSubscribe; + + public OnStartRunnable(@NonNull OnSubscribe onSubscribe) {this.onSubscribe = onSubscribe;} + + @Override + public void run() { + onSubscribe.onStart(); + } + } +} + diff --git a/app/src/main/java/acr/browser/lightning/react/OnSubscribe.java b/app/src/main/java/acr/browser/lightning/react/OnSubscribe.java new file mode 100644 index 000000000..fb65d7f5d --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/react/OnSubscribe.java @@ -0,0 +1,47 @@ +package acr.browser.lightning.react; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public abstract class OnSubscribe { + + /** + * Called when the observable + * runs into an error that will + * cause it to abort and not finish. + * Receiving this callback means that + * the observable is dead and no + * {@link #onComplete()} or {@link #onNext(Object)} + * callbacks will be called. + * + * @param throwable an optional throwable that could + * be sent. + */ + public void onError(@NonNull Throwable throwable) {} + + /** + * Called before the observer begins + * to process and emit items or complete. + */ + public void onStart() {} + + /** + * Called when the Observer emits an + * item. It can be called multiple times. + * It cannot be called after onComplete + * has been called. + * + * @param item the item that has been emitted, + * can be null. + */ + public void onNext(@Nullable T item) {} + + /** + * This method is called when the observer is + * finished sending the subscriber events. It + * is guaranteed that no other methods will be + * called on the OnSubscribe after this method + * has been called. + */ + public void onComplete() {} +} diff --git a/app/src/main/java/acr/browser/lightning/react/Schedulers.java b/app/src/main/java/acr/browser/lightning/react/Schedulers.java new file mode 100644 index 000000000..4368c96c3 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/react/Schedulers.java @@ -0,0 +1,46 @@ +package acr.browser.lightning.react; + +import android.os.Looper; +import android.support.annotation.NonNull; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +public class Schedulers { + private static final Executor sWorker = Executors.newFixedThreadPool(4); + private static final Executor sIOWorker = Executors.newSingleThreadExecutor(); + private static final Executor sMain = new ThreadExecutor(Looper.getMainLooper()); + + /** + * The worker thread executor, will + * execute work on any one of multiple + * threads. + * + * @return a non-null executor. + */ + @NonNull + public static Executor worker() { + return sWorker; + } + + /** + * The main thread. + * + * @return a non-null executor that does work on the main thread. + */ + @NonNull + public static Executor main() { + return sMain; + } + + /** + * The io thread. + * + * @return a non-null executor that does + * work on a single thread off the main thread. + */ + @NonNull + public static Executor io() { + return sIOWorker; + } +} diff --git a/app/src/main/java/acr/browser/lightning/react/Subscriber.java b/app/src/main/java/acr/browser/lightning/react/Subscriber.java new file mode 100644 index 000000000..72920f16b --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/react/Subscriber.java @@ -0,0 +1,51 @@ +package acr.browser.lightning.react; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public interface Subscriber extends Subscription { + + /** + * Called immediately upon subscribing + * and before the Observable begins + * emitting items. This should not be + * called by the creator of the Observable + * and is rather called internally by the + * Observable class itself. + */ + void onStart(); + + /** + * Called when the observable + * runs into an error that will + * cause it to abort and not finish. + * Receiving this callback means that + * the observable is dead and no + * {@link #onComplete()} or {@link #onNext(Object)} + * callbacks will be called. + * + * @param throwable an optional throwable that could + * be sent. + */ + void onError(@NonNull Throwable throwable); + + /** + * Called when the Observer emits an + * item. It can be called multiple times. + * It cannot be called after onComplete + * has been called. + * + * @param item the item that has been emitted, + * can be null. + */ + void onNext(@Nullable T item); + + /** + * This method is called when the observer is + * finished sending the subscriber events. It + * is guaranteed that no other methods will be + * called on the OnSubscribe after this method + * has been called. + */ + void onComplete(); +} diff --git a/app/src/main/java/acr/browser/lightning/react/Subscription.java b/app/src/main/java/acr/browser/lightning/react/Subscription.java new file mode 100644 index 000000000..627b60bf7 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/react/Subscription.java @@ -0,0 +1,7 @@ +package acr.browser.lightning.react; + +public interface Subscription { + + void unsubscribe(); + +} diff --git a/app/src/main/java/acr/browser/lightning/react/ThreadExecutor.java b/app/src/main/java/acr/browser/lightning/react/ThreadExecutor.java new file mode 100644 index 000000000..e3527260a --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/react/ThreadExecutor.java @@ -0,0 +1,21 @@ +package acr.browser.lightning.react; + +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.NonNull; + +import java.util.concurrent.Executor; + +class ThreadExecutor implements Executor { + + private final Handler mHandler; + + public ThreadExecutor(@NonNull Looper looper) { + mHandler = new Handler(looper); + } + + @Override + public void execute(@NonNull Runnable command) { + mHandler.post(command); + } +} diff --git a/app/src/main/java/acr/browser/lightning/reading/ArticleTextExtractor.java b/app/src/main/java/acr/browser/lightning/reading/ArticleTextExtractor.java index 07eb8abbc..1de050709 100644 --- a/app/src/main/java/acr/browser/lightning/reading/ArticleTextExtractor.java +++ b/app/src/main/java/acr/browser/lightning/reading/ArticleTextExtractor.java @@ -491,8 +491,7 @@ private static Date extractDate(Document doc) { Element el = elems.get(0); if (el.hasAttr("content")) { dateStr = el.attr("content"); - Date parsedDate = parseDate(dateStr); - return parsedDate; + return parseDate(dateStr); } } diff --git a/app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java b/app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java index 1af5d3b13..85a186224 100644 --- a/app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java +++ b/app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java @@ -33,6 +33,8 @@ import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; +import acr.browser.lightning.utils.Utils; + /** * Class to fetch articles. This class is thread safe. * @@ -49,28 +51,36 @@ public class HtmlFetcher { } public static void main(String[] args) throws Exception { - BufferedReader reader = new BufferedReader(new FileReader("urls.txt")); - String line; - Set existing = new LinkedHashSet<>(); - while ((line = reader.readLine()) != null) { - int index1 = line.indexOf('\"'); - int index2 = line.indexOf('\"', index1 + 1); - String url = line.substring(index1 + 1, index2); - String domainStr = SHelper.extractDomain(url, true); - String counterStr = ""; - // TODO more similarities - if (existing.contains(domainStr)) - counterStr = "2"; - else - existing.add(domainStr); - - String html = new HtmlFetcher().fetchAsString(url, 2000); - String outFile = domainStr + counterStr + ".html"; - BufferedWriter writer = new BufferedWriter(new FileWriter(outFile)); - writer.write(html); - writer.close(); + BufferedReader reader = null; + BufferedWriter writer = null; + try { + + //noinspection IOResourceOpenedButNotSafelyClosed + reader = new BufferedReader(new FileReader("urls.txt")); + String line; + Set existing = new LinkedHashSet<>(); + while ((line = reader.readLine()) != null) { + int index1 = line.indexOf('\"'); + int index2 = line.indexOf('\"', index1 + 1); + String url = line.substring(index1 + 1, index2); + String domainStr = SHelper.extractDomain(url, true); + String counterStr = ""; + // TODO more similarities + if (existing.contains(domainStr)) + counterStr = "2"; + else + existing.add(domainStr); + + String html = new HtmlFetcher().fetchAsString(url, 2000); + String outFile = domainStr + counterStr + ".html"; + //noinspection IOResourceOpenedButNotSafelyClosed + writer = new BufferedWriter(new FileWriter(outFile)); + writer.write(html); + } + } finally { + Utils.close(reader); + Utils.close(writer); } - reader.close(); } private String referrer = "http://jetsli.de/crawler"; @@ -386,8 +396,8 @@ private String getResolvedUrl(String urlAsString, int timeout, if (responseCode / 100 == 3 && newUrl != null && num_redirects < 5) { newUrl = SPACE.matcher(newUrl).replaceAll("+"); // some services use (none-standard) utf8 in their location header - if (urlAsString.startsWith("http://bit.ly") - || urlAsString.startsWith("http://is.gd")) + if (urlAsString.contains("://bit.ly") + || urlAsString.contains("://is.gd")) newUrl = encodeUriFromHeader(newUrl); // AP: This code is not longer need, instead we always follow diff --git a/app/src/main/java/acr/browser/lightning/reading/MapEntry.java b/app/src/main/java/acr/browser/lightning/reading/MapEntry.java index e93106241..7e1bef7b9 100644 --- a/app/src/main/java/acr/browser/lightning/reading/MapEntry.java +++ b/app/src/main/java/acr/browser/lightning/reading/MapEntry.java @@ -1,12 +1,12 @@ /** * Copyright (C) 2010 Peter Karich <> - * + *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + *

* http://www.apache.org/licenses/LICENSE-2.0 - * + *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -20,7 +20,7 @@ /** * Simple impl of Map.Entry. So that we can have ordered maps. - * + * * @author Peter Karich, peat_hal ‘at’ users ‘dot’ sourceforge ‘dot’ * net */ @@ -60,13 +60,12 @@ public String toString() { public boolean equals(Object obj) { if (obj == null) return false; - if (getClass() != obj.getClass()) - return false; - final MapEntry other = (MapEntry) obj; - if (this.key != other.key && (this.key == null || !this.key.equals(other.key))) + if (!(obj instanceof Map)) return false; - return !(this.value != other.value && (this.value == null || !this.value - .equals(other.value))); + final MapEntry other = (MapEntry) obj; + + return !(this.key != other.key && (this.key == null || !this.key.equals(other.key))) && + !(this.value != other.value && (this.value == null || !this.value.equals(other.value))); } @Override diff --git a/app/src/main/java/acr/browser/lightning/reading/OutputFormatter.java b/app/src/main/java/acr/browser/lightning/reading/OutputFormatter.java index f57a1da8f..fe63ca443 100644 --- a/app/src/main/java/acr/browser/lightning/reading/OutputFormatter.java +++ b/app/src/main/java/acr/browser/lightning/reading/OutputFormatter.java @@ -23,7 +23,7 @@ public class OutputFormatter { private static final int MIN_FIRST_PARAGRAPH_TEXT = 50; // Min size of first paragraph private static final int MIN_PARAGRAPH_TEXT = 30; // Min size of any other paragraphs private static final List NODES_TO_REPLACE = Arrays.asList("strong", "b", "i"); - private Pattern unlikelyPattern = Pattern.compile("display\\:none|visibility\\:hidden"); + private Pattern unlikelyPattern = Pattern.compile("display:none|visibility:hidden"); private final int minFirstParagraphText; private final int minParagraphText; private final List nodesToReplace; diff --git a/app/src/main/java/acr/browser/lightning/reading/SHelper.java b/app/src/main/java/acr/browser/lightning/reading/SHelper.java index 6e2ed97df..1554ed78f 100644 --- a/app/src/main/java/acr/browser/lightning/reading/SHelper.java +++ b/app/src/main/java/acr/browser/lightning/reading/SHelper.java @@ -251,9 +251,9 @@ public static void enableUserAgentOverwrite() { } public static String getUrlFromUglyGoogleRedirect(String url) { - if (url.startsWith("http://www.google.com/url?")) { - url = url.substring("http://www.google.com/url?".length()); - String arr[] = urlDecode(url).split("\\&"); + if (url.startsWith("https://www.google.com/url?")) { + url = url.substring("https://www.google.com/url?".length()); + String arr[] = urlDecode(url).split("&"); for (String str : arr) { if (str.startsWith("q=")) return str.substring("q=".length()); @@ -264,8 +264,8 @@ public static String getUrlFromUglyGoogleRedirect(String url) { } public static String getUrlFromUglyFacebookRedirect(String url) { - if (url.startsWith("http://www.facebook.com/l.php?u=")) { - url = url.substring("http://www.facebook.com/l.php?u=".length()); + if (url.startsWith("https://www.facebook.com/l.php?u=")) { + url = url.substring("https://www.facebook.com/l.php?u=".length()); return urlDecode(url); } diff --git a/app/src/main/java/acr/browser/lightning/receiver/NetworkReceiver.java b/app/src/main/java/acr/browser/lightning/receiver/NetworkReceiver.java index 3c3947518..97ffe389d 100644 --- a/app/src/main/java/acr/browser/lightning/receiver/NetworkReceiver.java +++ b/app/src/main/java/acr/browser/lightning/receiver/NetworkReceiver.java @@ -5,14 +5,14 @@ import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.support.annotation.NonNull; public class NetworkReceiver extends BroadcastReceiver { @Override - public void onReceive(Context context, Intent intent) { - } + public void onReceive(Context context, Intent intent) {} - public static boolean isConnected(Context context) { + public static boolean isConnected(@NonNull Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); if (cm == null) return false; diff --git a/app/src/main/java/acr/browser/lightning/search/RetrieveSuggestionsTask.java b/app/src/main/java/acr/browser/lightning/search/RetrieveSuggestionsTask.java new file mode 100644 index 000000000..53bf7fa4f --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/RetrieveSuggestionsTask.java @@ -0,0 +1,201 @@ +package acr.browser.lightning.search; + +import android.app.Application; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.util.Log; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.ref.WeakReference; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; + +import acr.browser.lightning.R; +import acr.browser.lightning.database.HistoryItem; +import acr.browser.lightning.utils.Utils; + +class RetrieveSuggestionsTask extends AsyncTask> { + + private static final String TAG = RetrieveSuggestionsTask.class.getSimpleName(); + + private static final Pattern SPACE_PATTERN = Pattern.compile(" ", Pattern.LITERAL); + private static final String CACHE_FILE_TYPE = ".sgg"; + private static final String ENCODING = "ISO-8859-1"; + private static final long INTERVAL_DAY = 86400000; + private static final String DEFAULT_LANGUAGE = "en"; + @Nullable private static XmlPullParser sXpp; + @Nullable private static String sLanguage; + @NonNull private final WeakReference mResultCallback; + @NonNull private final Application mApplication; + @NonNull private final String mSearchSubtitle; + @NonNull private String mQuery; + + public RetrieveSuggestionsTask(@NonNull String query, + @NonNull SuggestionsResult callback, + @NonNull Application application) { + mQuery = query; + mResultCallback = new WeakReference<>(callback); + mApplication = application; + mSearchSubtitle = mApplication.getString(R.string.suggestion); + } + + @NonNull + private static synchronized String getLanguage() { + if (sLanguage == null) { + sLanguage = Locale.getDefault().getLanguage(); + } + if (TextUtils.isEmpty(sLanguage)) { + sLanguage = DEFAULT_LANGUAGE; + } + return sLanguage; + } + + @NonNull + private static synchronized XmlPullParser getParser() throws XmlPullParserException { + if (sXpp == null) { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + sXpp = factory.newPullParser(); + } + return sXpp; + } + + @NonNull + @Override + protected List doInBackground(Void... voids) { + List filter = new ArrayList<>(5); + try { + mQuery = SPACE_PATTERN.matcher(mQuery).replaceAll("+"); + URLEncoder.encode(mQuery, ENCODING); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + File cache = downloadSuggestionsForQuery(mQuery, getLanguage(), mApplication); + if (!cache.exists()) { + return filter; + } + InputStream fileInput = null; + try { + fileInput = new BufferedInputStream(new FileInputStream(cache)); + XmlPullParser parser = getParser(); + parser.setInput(fileInput, ENCODING); + int eventType = parser.getEventType(); + int counter = 0; + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG && "suggestion".equals(parser.getName())) { + String suggestion = parser.getAttributeValue(null, "data"); + filter.add(new HistoryItem(mSearchSubtitle + " \"" + suggestion + '"', + suggestion, R.drawable.ic_search)); + counter++; + if (counter >= 5) { + break; + } + } + eventType = parser.next(); + } + } catch (Exception e) { + return filter; + } finally { + Utils.close(fileInput); + } + return filter; + } + + @Override + protected void onPostExecute(@NonNull List result) { + SuggestionsResult callback = mResultCallback.get(); + if (callback != null) { + callback.resultReceived(result); + } + } + + /** + * This method downloads the search suggestions for the specific query. + * NOTE: This is a blocking operation, do not run on the UI thread. + * + * @param query the query to get suggestions for + * @return the cache file containing the suggestions + */ + @NonNull + private static File downloadSuggestionsForQuery(@NonNull String query, String language, @NonNull Application app) { + File cacheFile = new File(app.getCacheDir(), query.hashCode() + CACHE_FILE_TYPE); + if (System.currentTimeMillis() - INTERVAL_DAY < cacheFile.lastModified()) { + return cacheFile; + } + if (!isNetworkConnected(app)) { + return cacheFile; + } + InputStream in = null; + FileOutputStream fos = null; + try { + // Old API that doesn't support HTTPS + // http://google.com/complete/search?q= + query + &output=toolbar&hl= + language + URL url = new URL("https://suggestqueries.google.com/complete/search?output=toolbar&hl=" + + language + "&q=" + query); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setDoInput(true); + connection.connect(); + if (connection.getResponseCode() >= HttpURLConnection.HTTP_MULT_CHOICE || + connection.getResponseCode() < HttpURLConnection.HTTP_OK) { + Log.e(TAG, "Search API Responded with code: " + connection.getResponseCode()); + connection.disconnect(); + return cacheFile; + } + in = connection.getInputStream(); + + if (in != null) { + //noinspection IOResourceOpenedButNotSafelyClosed + fos = new FileOutputStream(cacheFile); + int buffer; + while ((buffer = in.read()) != -1) { + fos.write(buffer); + } + fos.flush(); + } + connection.disconnect(); + cacheFile.setLastModified(System.currentTimeMillis()); + } catch (Exception e) { + Log.w(TAG, "Problem getting search suggestions", e); + } finally { + Utils.close(in); + Utils.close(fos); + } + return cacheFile; + } + + private static boolean isNetworkConnected(@NonNull Context context) { + NetworkInfo networkInfo = getActiveNetworkInfo(context); + return networkInfo != null && networkInfo.isConnected(); + } + + @Nullable + private static NetworkInfo getActiveNetworkInfo(@NonNull Context context) { + ConnectivityManager connectivity = (ConnectivityManager) context + .getApplicationContext() + .getSystemService(Context.CONNECTIVITY_SERVICE); + if (connectivity == null) { + return null; + } + return connectivity.getActiveNetworkInfo(); + } + +} \ No newline at end of file diff --git a/app/src/main/java/acr/browser/lightning/object/SearchAdapter.java b/app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java similarity index 51% rename from app/src/main/java/acr/browser/lightning/object/SearchAdapter.java rename to app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java index 6db5ef1ab..4dd83786e 100644 --- a/app/src/main/java/acr/browser/lightning/object/SearchAdapter.java +++ b/app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java @@ -1,11 +1,12 @@ -package acr.browser.lightning.object; +package acr.browser.lightning.search; +import android.app.Application; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.Drawable; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -15,26 +16,14 @@ import android.widget.ImageView; import android.widget.TextView; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserFactory; - -import java.io.BufferedInputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.FilenameFilter; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Locale; -import java.util.regex.Pattern; import javax.inject.Inject; @@ -45,86 +34,60 @@ import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.ThemeUtils; -import acr.browser.lightning.utils.Utils; -public class SearchAdapter extends BaseAdapter implements Filterable { +public class SuggestionsAdapter extends BaseAdapter implements Filterable, SuggestionsResult { + + private static final String TAG = SuggestionsAdapter.class.getSimpleName(); - private static final Pattern SPACE_PATTERN = Pattern.compile(" ", Pattern.LITERAL); private final List mHistory = new ArrayList<>(5); private final List mBookmarks = new ArrayList<>(5); private final List mSuggestions = new ArrayList<>(5); private final List mFilteredList = new ArrayList<>(5); private final List mAllBookmarks = new ArrayList<>(5); - private final Object mLock = new Object(); - private HistoryDatabase mDatabaseHandler; - private final Context mContext; + private boolean mUseGoogle = true; private boolean mIsExecuting = false; private final boolean mDarkTheme; private final boolean mIncognito; - @Inject - BookmarkManager mBookmarkManager; private static final String CACHE_FILE_TYPE = ".sgg"; - private static final String ENCODING = "ISO-8859-1"; private static final long INTERVAL_DAY = 86400000; private static final int MAX_SUGGESTIONS = 5; - private static final SuggestionsComparator mComparator = new SuggestionsComparator(); - private final String mSearchSubtitle; - private SearchFilter mFilter; - private final Drawable mSearchDrawable; - private final Drawable mHistoryDrawable; - private final Drawable mBookmarkDrawable; - - public SearchAdapter(Context context, boolean dark, boolean incognito) { + private static final SuggestionsComparator sComparator = new SuggestionsComparator(); + + @NonNull private final Context mContext; + @Nullable private SearchFilter mFilter; + @NonNull private final Drawable mSearchDrawable; + @NonNull private final Drawable mHistoryDrawable; + @NonNull private final Drawable mBookmarkDrawable; + + @Inject HistoryDatabase mDatabaseHandler; + @Inject BookmarkManager mBookmarkManager; + @Inject PreferenceManager mPreferenceManager; + + public SuggestionsAdapter(@NonNull Context context, boolean dark, boolean incognito) { BrowserApp.getAppComponent().inject(this); - mDatabaseHandler = HistoryDatabase.getInstance(); mAllBookmarks.addAll(mBookmarkManager.getAllBookmarks(true)); - mUseGoogle = PreferenceManager.getInstance().getGoogleSearchSuggestionsEnabled(); + mUseGoogle = mPreferenceManager.getGoogleSearchSuggestionsEnabled(); mContext = context; - mSearchSubtitle = mContext.getString(R.string.suggestion); mDarkTheme = dark || incognito; mIncognito = incognito; - Thread delete = new Thread(new ClearCacheRunnable()); + BrowserApp.getTaskThread().execute(new ClearCacheRunnable(BrowserApp.get(context))); mSearchDrawable = ThemeUtils.getThemedDrawable(context, R.drawable.ic_search, mDarkTheme); mBookmarkDrawable = ThemeUtils.getThemedDrawable(context, R.drawable.ic_bookmark, mDarkTheme); mHistoryDrawable = ThemeUtils.getThemedDrawable(context, R.drawable.ic_history, mDarkTheme); - delete.setPriority(Thread.MIN_PRIORITY); - delete.start(); - } - - private static void deleteOldCacheFiles() { - File dir = new File(BrowserApp.getAppContext().getCacheDir().toString()); - String[] fileList = dir.list(new NameFilter()); - long earliestTimeAllowed = System.currentTimeMillis() - INTERVAL_DAY; - for (String fileName : fileList) { - File file = new File(dir.getPath() + fileName); - if (earliestTimeAllowed > file.lastModified()) { - file.delete(); - } - } - } - - private static class NameFilter implements FilenameFilter { - - @Override - public boolean accept(File dir, String filename) { - return filename.endsWith(CACHE_FILE_TYPE); - } - } public void refreshPreferences() { - mUseGoogle = PreferenceManager.getInstance().getGoogleSearchSuggestionsEnabled(); + mUseGoogle = mPreferenceManager.getGoogleSearchSuggestionsEnabled(); if (!mUseGoogle) { synchronized (mSuggestions) { mSuggestions.clear(); } } - mDatabaseHandler = HistoryDatabase.getInstance(); } public void refreshBookmarks() { - synchronized (mLock) { + synchronized (SuggestionsAdapter.this) { mAllBookmarks.clear(); mAllBookmarks.addAll(mBookmarkManager.getAllBookmarks(true)); } @@ -145,8 +108,15 @@ public long getItemId(int position) { return 0; } + private static class SuggestionHolder { + ImageView mImage; + TextView mTitle; + TextView mUrl; + } + + @Nullable @Override - public View getView(int position, View convertView, ViewGroup parent) { + public View getView(int position, @Nullable View convertView, ViewGroup parent) { SuggestionHolder holder; if (convertView == null) { @@ -208,30 +178,56 @@ public Filter getFilter() { private static class ClearCacheRunnable implements Runnable { + @NonNull + private final Application app; + + public ClearCacheRunnable(@NonNull Application app) { + this.app = app; + } + @Override public void run() { - deleteOldCacheFiles(); + File dir = new File(app.getCacheDir().toString()); + String[] fileList = dir.list(new NameFilter()); + long earliestTimeAllowed = System.currentTimeMillis() - INTERVAL_DAY; + for (String fileName : fileList) { + File file = new File(dir.getPath() + fileName); + if (earliestTimeAllowed > file.lastModified()) { + file.delete(); + } + } + } + + private static class NameFilter implements FilenameFilter { + + @Override + public boolean accept(File dir, @NonNull String filename) { + return filename.endsWith(CACHE_FILE_TYPE); + } + } } private class SearchFilter extends Filter { + @NonNull @Override - protected FilterResults performFiltering(CharSequence constraint) { + protected FilterResults performFiltering(@Nullable CharSequence constraint) { FilterResults results = new FilterResults(); if (constraint == null) { return results; } String query = constraint.toString().toLowerCase(Locale.getDefault()); if (mUseGoogle && !mIncognito && !mIsExecuting) { - new RetrieveSearchSuggestions().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, query); + mIsExecuting = true; + new RetrieveSuggestionsTask(query, SuggestionsAdapter.this, BrowserApp.get(mContext)).executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); } int counter = 0; synchronized (mBookmarks) { mBookmarks.clear(); - synchronized (mLock) { + synchronized (SuggestionsAdapter.this) { for (int n = 0; n < mAllBookmarks.size(); n++) { if (counter >= 5) { break; @@ -244,13 +240,10 @@ protected FilterResults performFiltering(CharSequence constraint) { mBookmarks.add(mAllBookmarks.get(n)); counter++; } - } } } - if (mDatabaseHandler == null || mDatabaseHandler.isClosed()) { - mDatabaseHandler = HistoryDatabase.getInstance(); - } + List historyList = mDatabaseHandler.findItemsContaining(constraint.toString()); synchronized (mHistory) { mHistory.clear(); @@ -261,7 +254,7 @@ protected FilterResults performFiltering(CharSequence constraint) { } @Override - public CharSequence convertResultToString(Object resultValue) { + public CharSequence convertResultToString(@NonNull Object resultValue) { return ((HistoryItem) resultValue).getUrl(); } @@ -270,7 +263,7 @@ protected void publishResults(CharSequence constraint, FilterResults results) { synchronized (mFilteredList) { mFilteredList.clear(); List filtered = getFilteredList(); - Collections.sort(filtered, mComparator); + Collections.sort(filtered, sComparator); mFilteredList.addAll(filtered); } notifyDataSetChanged(); @@ -278,142 +271,24 @@ protected void publishResults(CharSequence constraint, FilterResults results) { } - private static class SuggestionHolder { - ImageView mImage; - TextView mTitle; - TextView mUrl; - } - - private class RetrieveSearchSuggestions extends AsyncTask> { - - private XmlPullParserFactory mFactory; - private XmlPullParser mXpp; - - @Override - protected List doInBackground(String... arg0) { - mIsExecuting = true; - - List filter = new ArrayList<>(); - String query = arg0[0]; - try { - query = SPACE_PATTERN.matcher(query).replaceAll("+"); - URLEncoder.encode(query, ENCODING); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - File cache = downloadSuggestionsForQuery(query); - if (!cache.exists()) { - return filter; - } - InputStream fileInput = null; - try { - fileInput = new BufferedInputStream(new FileInputStream(cache)); - if (mFactory == null) { - mFactory = XmlPullParserFactory.newInstance(); - mFactory.setNamespaceAware(true); - } - if (mXpp == null) { - mXpp = mFactory.newPullParser(); - } - mXpp.setInput(fileInput, ENCODING); - int eventType = mXpp.getEventType(); - int counter = 0; - while (eventType != XmlPullParser.END_DOCUMENT) { - if (eventType == XmlPullParser.START_TAG && "suggestion".equals(mXpp.getName())) { - String suggestion = mXpp.getAttributeValue(null, "data"); - filter.add(new HistoryItem(mSearchSubtitle + " \"" + suggestion + '"', - suggestion, R.drawable.ic_search)); - counter++; - if (counter >= 5) { - break; - } - } - eventType = mXpp.next(); - } - } catch (Exception e) { - return filter; - } finally { - Utils.close(fileInput); - } - return filter; - } - - @Override - protected void onPostExecute(List result) { - mIsExecuting = false; - synchronized (mSuggestions) { - mSuggestions.clear(); - mSuggestions.addAll(result); - } - synchronized (mFilteredList) { - mFilteredList.clear(); - List filtered = getFilteredList(); - Collections.sort(filtered, mComparator); - mFilteredList.addAll(filtered); - notifyDataSetChanged(); - } - } - - } - - /** - * This method downloads the search suggestions for the specific query. - * NOTE: This is a blocking operation, do not run on the UI thread. - * - * @param query the query to get suggestions for - * @return the cache file containing the suggestions - */ - private static File downloadSuggestionsForQuery(String query) { - File cacheFile = new File(BrowserApp.getAppContext().getCacheDir(), query.hashCode() + CACHE_FILE_TYPE); - if (System.currentTimeMillis() - INTERVAL_DAY < cacheFile.lastModified()) { - return cacheFile; - } - if (!isNetworkConnected(BrowserApp.getAppContext())) { - return cacheFile; - } - InputStream in = null; - FileOutputStream fos = null; - try { - URL url = new URL("http://google.com/complete/search?q=" + query - + "&output=toolbar&hl=en"); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setDoInput(true); - connection.connect(); - in = connection.getInputStream(); - - if (in != null) { - fos = new FileOutputStream(cacheFile); - int buffer; - while ((buffer = in.read()) != -1) { - fos.write(buffer); - } - fos.flush(); - } - cacheFile.setLastModified(System.currentTimeMillis()); - } catch (Exception e) { - e.printStackTrace(); - } finally { - Utils.close(in); - Utils.close(fos); + @Override + public void resultReceived(@NonNull List searchResults) { + mIsExecuting = false; + synchronized (mSuggestions) { + mSuggestions.clear(); + mSuggestions.addAll(searchResults); } - return cacheFile; - } - - private static boolean isNetworkConnected(Context context) { - NetworkInfo networkInfo = getActiveNetworkInfo(context); - return networkInfo != null && networkInfo.isConnected(); - } - - private static NetworkInfo getActiveNetworkInfo(Context context) { - ConnectivityManager connectivity = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); - if (connectivity == null) { - return null; + synchronized (mFilteredList) { + mFilteredList.clear(); + List filtered = getFilteredList(); + Collections.sort(filtered, sComparator); + mFilteredList.addAll(filtered); + notifyDataSetChanged(); } - return connectivity.getActiveNetworkInfo(); } - private List getFilteredList() { + @NonNull + private synchronized List getFilteredList() { List list = new ArrayList<>(5); synchronized (mBookmarks) { synchronized (mHistory) { @@ -444,7 +319,7 @@ private List getFilteredList() { private static class SuggestionsComparator implements Comparator { @Override - public int compare(HistoryItem lhs, HistoryItem rhs) { + public int compare(@NonNull HistoryItem lhs, @NonNull HistoryItem rhs) { if (lhs.getImageId() == rhs.getImageId()) return 0; if (lhs.getImageId() == R.drawable.ic_bookmark) return -1; if (rhs.getImageId() == R.drawable.ic_bookmark) return 1; diff --git a/app/src/main/java/acr/browser/lightning/search/SuggestionsResult.java b/app/src/main/java/acr/browser/lightning/search/SuggestionsResult.java new file mode 100644 index 000000000..63e420cdc --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/SuggestionsResult.java @@ -0,0 +1,21 @@ +package acr.browser.lightning.search; + +import android.support.annotation.NonNull; + +import java.util.List; + +import acr.browser.lightning.database.HistoryItem; + +interface SuggestionsResult { + + /** + * Called when the search suggestions have + * been retrieved from the server. + * + * @param searchResults the results, a valid + * list of results. May + * be empty. + */ + void resultReceived(@NonNull List searchResults); + +} diff --git a/app/src/main/java/acr/browser/lightning/utils/AdBlock.java b/app/src/main/java/acr/browser/lightning/utils/AdBlock.java index 706596f20..9012603ed 100644 --- a/app/src/main/java/acr/browser/lightning/utils/AdBlock.java +++ b/app/src/main/java/acr/browser/lightning/utils/AdBlock.java @@ -2,6 +2,8 @@ import android.content.Context; import android.content.res.AssetManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.Log; import java.io.BufferedReader; @@ -13,9 +15,14 @@ import java.util.Locale; import java.util.Set; +import javax.inject.Inject; +import javax.inject.Singleton; + +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.preference.PreferenceManager; +@Singleton public class AdBlock { private static final String TAG = "AdBlock"; @@ -31,34 +38,31 @@ public class AdBlock { private final Set mBlockedDomainsList = new HashSet<>(); private boolean mBlockAds; private static final Locale mLocale = Locale.getDefault(); - private static AdBlock mInstance; - public static AdBlock getInstance(Context context) { - if (mInstance == null) { - mInstance = new AdBlock(context); - } - return mInstance; - } + @Inject PreferenceManager mPreferenceManager; - private AdBlock(Context context) { + @Inject + public AdBlock(@NonNull Context context) { + BrowserApp.getAppComponent().inject(this); if (mBlockedDomainsList.isEmpty() && Constants.FULL_VERSION) { loadHostsFile(context); } - mBlockAds = PreferenceManager.getInstance().getAdBlockEnabled(); + mBlockAds = mPreferenceManager.getAdBlockEnabled(); } public void updatePreference() { - mBlockAds = PreferenceManager.getInstance().getAdBlockEnabled(); + mBlockAds = mPreferenceManager.getAdBlockEnabled(); } - private void loadBlockedDomainsList(final Context context) { - Thread thread = new Thread(new Runnable() { + private void loadBlockedDomainsList(@NonNull final Context context) { + BrowserApp.getTaskThread().execute(new Runnable() { @Override public void run() { AssetManager asset = context.getAssets(); BufferedReader reader = null; try { + //noinspection IOResourceOpenedButNotSafelyClosed reader = new BufferedReader(new InputStreamReader( asset.open(BLOCKED_DOMAINS_LIST_FILE_NAME))); String line; @@ -73,16 +77,16 @@ public void run() { } } }); - thread.start(); } /** * a method that determines if the given URL is an ad or not. It performs * a search of the URL's domain on the blocked domain hash set. + * * @param url the URL to check for being an ad * @return true if it is an ad, false if it is not an ad */ - public boolean isAd(String url) { + public boolean isAd(@Nullable String url) { if (!mBlockAds || url == null) { return false; } @@ -104,11 +108,13 @@ public boolean isAd(String url) { /** * Returns the probable domain name for a given URL + * * @param url the url to parse * @return returns the domain * @throws URISyntaxException throws an exception if the string cannot form a URI */ - private static String getDomainName(String url) throws URISyntaxException { + @NonNull + private static String getDomainName(@NonNull String url) throws URISyntaxException { int index = url.indexOf('/', 8); if (index != -1) { url = url.substring(0, index); @@ -129,16 +135,18 @@ private static String getDomainName(String url) throws URISyntaxException { * simply have a list of hostnames to block, or it can handle a full blown hosts file. * It will strip out comments, references to the base IP address and just extract the * domains to be used + * * @param context the context needed to read the file */ - private void loadHostsFile(final Context context) { - Thread thread = new Thread(new Runnable() { + private void loadHostsFile(@NonNull final Context context) { + BrowserApp.getTaskThread().execute(new Runnable() { @Override public void run() { AssetManager asset = context.getAssets(); BufferedReader reader = null; try { + //noinspection IOResourceOpenedButNotSafelyClosed reader = new BufferedReader(new InputStreamReader( asset.open(BLOCKED_DOMAINS_LIST_FILE_NAME))); String line; @@ -172,6 +180,5 @@ public void run() { } } }); - thread.start(); } } diff --git a/app/src/main/java/acr/browser/lightning/utils/DrawableUtils.java b/app/src/main/java/acr/browser/lightning/utils/DrawableUtils.java new file mode 100644 index 000000000..49bb6b58d --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/utils/DrawableUtils.java @@ -0,0 +1,74 @@ +package acr.browser.lightning.utils; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.RectF; +import android.graphics.Typeface; + +public class DrawableUtils { + + public static Bitmap getRoundedNumberImage(int number, int width, int height, int color, int thickness) { + String text; + + if (number > 99) { + text = "\u221E"; + } else { + text = String.valueOf(number); + } + + Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(image); + Paint paint = new Paint(); + paint.setColor(color); + Typeface boldText = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD); + paint.setTypeface(boldText); + paint.setTextSize(Utils.dpToPx(14)); + paint.setAntiAlias(true); + paint.setTextAlign(Paint.Align.CENTER); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); + + int radius = Utils.dpToPx(2); + + RectF outer = new RectF(0, 0, canvas.getWidth(), canvas.getHeight()); + canvas.drawRoundRect(outer, radius, radius, paint); + + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + + radius--; + RectF inner = new RectF(thickness, thickness, canvas.getWidth() - thickness, canvas.getHeight() - thickness); + canvas.drawRoundRect(inner, radius, radius, paint); + + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); + + int xPos = (canvas.getWidth() / 2); + int yPos = (int) ((canvas.getHeight() / 2) - ((paint.descent() + paint.ascent()) / 2)); + + canvas.drawText(String.valueOf(text), xPos, yPos, paint); + + return image; + } + + + public static int mixColor(float fraction, int startValue, int endValue) { + int startInt = startValue; + int startA = (startInt >> 24) & 0xff; + int startR = (startInt >> 16) & 0xff; + int startG = (startInt >> 8) & 0xff; + int startB = startInt & 0xff; + + int endInt = endValue; + int endA = (endInt >> 24) & 0xff; + int endR = (endInt >> 16) & 0xff; + int endG = (endInt >> 8) & 0xff; + int endB = endInt & 0xff; + + return (startA + (int)(fraction * (endA - startA))) << 24 | + (startR + (int)(fraction * (endR - startR))) << 16 | + (startG + (int)(fraction * (endG - startG))) << 8 | + (startB + (int)(fraction * (endB - startB))); + } + +} diff --git a/app/src/main/java/acr/browser/lightning/utils/FileUtils.java b/app/src/main/java/acr/browser/lightning/utils/FileUtils.java new file mode 100644 index 000000000..290eb576e --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/utils/FileUtils.java @@ -0,0 +1,112 @@ +package acr.browser.lightning.utils; + +import android.app.Application; +import android.os.Bundle; +import android.os.Parcel; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.constant.Constants; + +/** + * A utility class containing helpful methods + * pertaining to file storage. + */ +public class FileUtils { + + /** + * Writes a bundle to persistent storage in the files directory + * using the specified file name. This method is a blocking + * operation. + * + * @param app the application needed to obtain the file directory. + * @param bundle the bundle to store in persistent storage. + * @param name the name of the file to store the bundle in. + */ + public static void writeBundleToStorage(final @NonNull Application app, final Bundle bundle, final @NonNull String name) { + BrowserApp.getIOThread().execute(new Runnable() { + @Override + public void run() { + File outputFile = new File(app.getFilesDir(), name); + FileOutputStream outputStream = null; + try { + //noinspection IOResourceOpenedButNotSafelyClosed + outputStream = new FileOutputStream(outputFile); + Parcel parcel = Parcel.obtain(); + parcel.writeBundle(bundle); + outputStream.write(parcel.marshall()); + outputStream.flush(); + parcel.recycle(); + } catch (IOException e) { + Log.e(Constants.TAG, "Unable to write bundle to storage"); + } finally { + Utils.close(outputStream); + } + } + }); + } + + /** + * Use this method to delete the bundle with the specified name. + * This is a blocking call and should be used within a worker + * thread unless immediate deletion is necessary. + * + * @param app the application object needed to get the file. + * @param name the name of the file. + */ + public static void deleteBundleInStorage(final @NonNull Application app, final @NonNull String name) { + File outputFile = new File(app.getFilesDir(), name); + if (outputFile.exists()) { + outputFile.delete(); + } + } + + /** + * Reads a bundle from the file with the specified + * name in the peristent storage files directory. + * This method is a blocking operation. + * + * @param app the application needed to obtain the files directory. + * @param name the name of the file to read from. + * @return a valid Bundle loaded using the system class loader + * or null if the method was unable to read the Bundle from storage. + */ + @Nullable + public static Bundle readBundleFromStorage(@NonNull Application app, @NonNull String name) { + File inputFile = new File(app.getFilesDir(), name); + FileInputStream inputStream = null; + try { + //noinspection IOResourceOpenedButNotSafelyClosed + inputStream = new FileInputStream(inputFile); + Parcel parcel = Parcel.obtain(); + byte[] data = new byte[(int) inputStream.getChannel().size()]; + + //noinspection ResultOfMethodCallIgnored + inputStream.read(data, 0, data.length); + parcel.unmarshall(data, 0, data.length); + parcel.setDataPosition(0); + Bundle out = parcel.readBundle(ClassLoader.getSystemClassLoader()); + out.putAll(out); + parcel.recycle(); + return out; + } catch (FileNotFoundException e) { + Log.e(Constants.TAG, "Unable to read bundle from storage"); + } catch (IOException e) { + e.printStackTrace(); + } finally { + //noinspection ResultOfMethodCallIgnored + inputFile.delete(); + Utils.close(inputStream); + } + return null; + } + +} diff --git a/app/src/main/java/acr/browser/lightning/utils/IntentUtils.java b/app/src/main/java/acr/browser/lightning/utils/IntentUtils.java index 21e679192..c212e442a 100644 --- a/app/src/main/java/acr/browser/lightning/utils/IntentUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/IntentUtils.java @@ -7,6 +7,9 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.Log; import android.webkit.WebView; @@ -15,6 +18,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import acr.browser.lightning.constant.Constants; + public class IntentUtils { private final Activity mActivity; @@ -23,14 +28,14 @@ public class IntentUtils { + // switch on case insensitive matching '(' + // begin group for schema - "(?:http|https|file):\\/\\/" + "|(?:inline|data|about|javascript):" + "|(?:.*:.*@)" + "(?:http|https|file)://" + "|(?:inline|data|about|javascript):" + "|(?:.*:.*@)" + ')' + "(.*)"); public IntentUtils(Activity activity) { mActivity = activity; } - public boolean startActivityForUrl(WebView tab, String url) { + public boolean startActivityForUrl(@Nullable WebView tab, @NonNull String url) { Intent intent; try { intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); @@ -39,6 +44,12 @@ public boolean startActivityForUrl(WebView tab, String url) { return false; } + intent.addCategory(Intent.CATEGORY_BROWSABLE); + intent.setComponent(null); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + intent.setSelector(null); + } + if (mActivity.getPackageManager().resolveActivity(intent, 0) == null) { String packagename = intent.getPackage(); if (packagename != null) { @@ -51,10 +62,8 @@ public boolean startActivityForUrl(WebView tab, String url) { return false; } } - intent.addCategory(Intent.CATEGORY_BROWSABLE); - intent.setComponent(null); if (tab != null) { - intent.putExtra(mActivity.getPackageName() + ".Origin", 1); + intent.putExtra(Constants.INTENT_ORIGIN, 1); } Matcher m = ACCEPTED_URI_SCHEMA.matcher(url); diff --git a/app/src/main/java/acr/browser/lightning/utils/KeyboardHelper.java b/app/src/main/java/acr/browser/lightning/utils/KeyboardHelper.java new file mode 100644 index 000000000..b7bbe8028 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/utils/KeyboardHelper.java @@ -0,0 +1,67 @@ +package acr.browser.lightning.utils; + +import android.graphics.Rect; +import android.support.annotation.NonNull; +import android.view.View; +import android.view.ViewTreeObserver; + +public class KeyboardHelper { + + public interface KeyboardListener { + /** + * Called when the visibility of the keyboard changes. + * Parameter tells whether the keyboard has been shown + * or hidden. + * + * @param visible true if the keyboard has been shown, + * false otherwise. + */ + void keyboardVisibilityChanged(boolean visible); + } + + @NonNull private final View mView; + private int mLastRight = -1; + private int mLastBottom = -1; + + /** + * Constructor + * + * @param view the view to listen on, should be + * the {@link android.R.id#content} view. + */ + public KeyboardHelper(@NonNull View view) { + mView = view; + } + + /** + * Registers a {@link KeyboardListener} to receive + * callbacks when the keyboard is shown for the specific + * view. The view used should be the content view as it + * will receive resize events from the system. + * + * @param listener the listener to register to receive events. + */ + public void registerKeyboardListener(@NonNull final KeyboardListener listener) { + mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + Rect rect = new Rect(); + if (mLastBottom == -1) { + mLastBottom = rect.bottom; + } + if (mLastRight == -1) { + mLastRight = rect.right; + } + mView.getWindowVisibleDisplayFrame(rect); + if (mLastRight == rect.right && rect.bottom < mLastBottom) { + listener.keyboardVisibilityChanged(true); + } else if (mLastRight == rect.right && rect.bottom > mLastBottom) { + listener.keyboardVisibilityChanged(false); + } + mLastBottom = rect.bottom; + mLastRight = rect.right; + } + }); + } + +} diff --git a/app/src/main/java/acr/browser/lightning/utils/PermissionsManager.java b/app/src/main/java/acr/browser/lightning/utils/PermissionsManager.java deleted file mode 100644 index c0cc10490..000000000 --- a/app/src/main/java/acr/browser/lightning/utils/PermissionsManager.java +++ /dev/null @@ -1,75 +0,0 @@ -package acr.browser.lightning.utils; - -import android.app.Activity; -import android.content.pm.PackageManager; -import android.os.Build; -import android.support.annotation.NonNull; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Copyright 8/22/2015 Anthony Restaino - */ -public class PermissionsManager { - - private static PermissionsManager mInstance; - private final Set mPendingRequests = new HashSet<>(); - - public static PermissionsManager getInstance() { - if (mInstance == null) { - mInstance = new PermissionsManager(); - } - return mInstance; - } - - public void requestPermissionsIfNecessary(Activity activity, @NonNull String[] permissions) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || activity == null) { - return; - } - List permList = new ArrayList<>(); - for (String perm : permissions) { - if (activity.checkSelfPermission(perm) != PackageManager.PERMISSION_GRANTED - && !mPendingRequests.contains(perm)) { - permList.add(perm); - } - } - if (!permList.isEmpty()) { - String[] permsToRequest = permList.toArray(new String[permList.size()]); - mPendingRequests.addAll(permList); - activity.requestPermissions(permsToRequest, 1); - } - - } - - public static boolean checkPermission(Activity activity, @NonNull String permission) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - return true; - } else if (activity == null) { - return false; - } - return activity.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED; - } - - public static boolean checkPermissions(Activity activity, @NonNull String[] permissions) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - return true; - } else if (activity == null) { - return false; - } - boolean permissionsNecessary = true; - for (String perm : permissions) { - permissionsNecessary &= activity.checkSelfPermission(perm) == PackageManager.PERMISSION_GRANTED; - } - return permissionsNecessary; - } - - public void notifyPermissionsChange(String[] permissions) { - for (String perm : permissions) { - mPendingRequests.remove(perm); - } - } - -} diff --git a/app/src/main/java/acr/browser/lightning/utils/Preconditions.java b/app/src/main/java/acr/browser/lightning/utils/Preconditions.java new file mode 100644 index 000000000..e9ffd8416 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/utils/Preconditions.java @@ -0,0 +1,16 @@ +package acr.browser.lightning.utils; + +public class Preconditions { + /** + * Ensure that an object is not null + * and throw a RuntimeException if it + * is null. + * + * @param object check nullness on this object. + */ + public static void checkNonNull(Object object) { + if (object == null) { + throw new RuntimeException("Object must not be null"); + } + } +} diff --git a/app/src/LightningLite/java/acr/browser/lightning/utils/ProxyUtils.java b/app/src/main/java/acr/browser/lightning/utils/ProxyUtils.java similarity index 86% rename from app/src/LightningLite/java/acr/browser/lightning/utils/ProxyUtils.java rename to app/src/main/java/acr/browser/lightning/utils/ProxyUtils.java index d49f9f2b5..a7037377a 100644 --- a/app/src/LightningLite/java/acr/browser/lightning/utils/ProxyUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/ProxyUtils.java @@ -1,48 +1,46 @@ package acr.browser.lightning.utils; import android.app.Activity; -import android.content.Context; import android.content.DialogInterface; +import android.support.annotation.NonNull; import android.support.v7.app.AlertDialog; import android.util.Log; +import com.squareup.otto.Bus; + import net.i2p.android.ui.I2PAndroidHelper; +import javax.inject.Inject; +import javax.inject.Singleton; + import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.bus.BrowserEvents; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.preference.PreferenceManager; import info.guardianproject.netcipher.proxy.OrbotHelper; import info.guardianproject.netcipher.web.WebkitProxy; -/** - * 6/4/2015 Anthony Restaino - */ +@Singleton public class ProxyUtils { // Helper - private final I2PAndroidHelper mI2PHelper; private static boolean mI2PHelperBound; private static boolean mI2PProxyInitialized; - private final PreferenceManager mPreferences; - private static ProxyUtils mInstance; - private ProxyUtils(Context context) { - mPreferences = PreferenceManager.getInstance(); - mI2PHelper = new I2PAndroidHelper(context.getApplicationContext()); - } + @Inject PreferenceManager mPreferences; + @Inject I2PAndroidHelper mI2PHelper; + @Inject Bus mBus; - public static ProxyUtils getInstance() { - if (mInstance == null) { - mInstance = new ProxyUtils(BrowserApp.getAppContext()); - } - return mInstance; + @Inject + public ProxyUtils() { + BrowserApp.getAppComponent().inject(this); } /* * If Orbot/Tor or I2P is installed, prompt the user if they want to enable * proxying for this session */ - public void checkForProxy(final Activity activity) { + public void checkForProxy(@NonNull final Activity activity) { boolean useProxy = mPreferences.getUseProxy(); final boolean orbotInstalled = OrbotHelper.isOrbotInstalled(activity); @@ -105,7 +103,7 @@ public void onClick(DialogInterface dialog, int which) { /* * Initialize WebKit Proxying */ - private void initializeProxy(Activity activity) { + private void initializeProxy(@NonNull Activity activity) { String host; int port; @@ -143,13 +141,13 @@ private void initializeProxy(Activity activity) { } - public boolean isProxyReady(Activity activity) { + public boolean isProxyReady() { if (mPreferences.getProxyChoice() == Constants.PROXY_I2P) { if (!mI2PHelper.isI2PAndroidRunning()) { - Utils.showSnackbar(activity, R.string.i2p_not_running); + mBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.i2p_not_running)); return false; } else if (!mI2PHelper.areTunnelsActive()) { - Utils.showSnackbar(activity, R.string.i2p_tunnels_not_ready); + mBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.i2p_tunnels_not_ready)); return false; } } @@ -157,7 +155,7 @@ public boolean isProxyReady(Activity activity) { return true; } - public void updateProxySettings(Activity activity) { + public void updateProxySettings(@NonNull Activity activity) { if (mPreferences.getUseProxy()) { initializeProxy(activity); } else { @@ -190,7 +188,7 @@ public void onI2PAndroidBound() { } } - public static int setProxyChoice(int choice, Activity activity) { + public static int setProxyChoice(int choice, @NonNull Activity activity) { switch (choice) { case Constants.PROXY_ORBOT: if (!OrbotHelper.isOrbotInstalled(activity)) { @@ -200,7 +198,7 @@ public static int setProxyChoice(int choice, Activity activity) { break; case Constants.PROXY_I2P: - I2PAndroidHelper ih = new I2PAndroidHelper(activity.getApplicationContext()); + I2PAndroidHelper ih = new I2PAndroidHelper(BrowserApp.get(activity)); if (!ih.isI2PAndroidInstalled()) { choice = Constants.NO_PROXY; ih.promptToInstall(activity); diff --git a/app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java b/app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java index 11d98e526..a8c42ac42 100644 --- a/app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java @@ -5,6 +5,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PorterDuff; @@ -12,9 +13,9 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.support.annotation.AttrRes; +import android.support.annotation.ColorInt; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.util.TypedValue; import android.widget.ImageView; @@ -44,20 +45,28 @@ private static int getColor(@NonNull Context context, @AttrRes int resource) { return color; } + @ColorInt public static int getIconLightThemeColor(@NonNull Context context) { return ContextCompat.getColor(context, R.color.icon_light_theme); } + @ColorInt public static int getIconDarkThemeColor(@NonNull Context context) { return ContextCompat.getColor(context, R.color.icon_dark_theme); } - public static void themeImageView(ImageView icon, Context context, boolean dark) { + @ColorInt + public static int getIconThemeColor(@NonNull Context context, boolean dark) { + return (dark) ? getIconDarkThemeColor(context) : getIconLightThemeColor(context); + } + + public static void themeImageView(@NonNull ImageView icon, @NonNull Context context, boolean dark) { int color = dark ? getIconDarkThemeColor(context) : getIconLightThemeColor(context); icon.setColorFilter(color, PorterDuff.Mode.SRC_IN); } - public static Bitmap getThemedBitmap(Context context, @DrawableRes int res, boolean dark) { + @NonNull + public static Bitmap getThemedBitmap(@NonNull Context context, @DrawableRes int res, boolean dark) { int color = dark ? getIconDarkThemeColor(context) : getIconLightThemeColor(context); Bitmap sourceBitmap = BitmapFactory.decodeResource(context.getResources(), res); Bitmap resultBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), sourceBitmap.getHeight(), Bitmap.Config.ARGB_8888); @@ -70,34 +79,27 @@ public static Bitmap getThemedBitmap(Context context, @DrawableRes int res, bool return resultBitmap; } - @Nullable + @NonNull public static Drawable getThemedDrawable(@NonNull Context context, @DrawableRes int res, boolean dark) { int color = dark ? getIconDarkThemeColor(context) : getIconLightThemeColor(context); final Drawable drawable = ContextCompat.getDrawable(context, res); - if (drawable == null) - return null; drawable.mutate(); drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN); return drawable; } - @Nullable - public static Drawable getLightThemedDrawable(@NonNull Context context, @DrawableRes int res) { - final Drawable drawable = ContextCompat.getDrawable(context, res); - if (drawable == null) - return null; - drawable.mutate(); - drawable.setColorFilter(getIconLightThemeColor(context), PorterDuff.Mode.SRC_IN); - return drawable; - } - + @NonNull public static ColorDrawable getSelectedBackground(@NonNull Context context, boolean dark) { - final int color = (dark) ? ContextCompat.getColor(context, R.color.selected_dark) : + @ColorInt final int color = (dark) ? ContextCompat.getColor(context, R.color.selected_dark) : ContextCompat.getColor(context, R.color.selected_light); return new ColorDrawable(color); } - public static int getTextColor(Context context) { + public static int getThemedTextHintColor(boolean dark){ + return 0x80ffffff & (dark ? Color.WHITE : Color.BLACK); + } + + public static int getTextColor(@NonNull Context context) { return getColor(context, android.R.attr.editTextColor); } } diff --git a/app/src/main/java/acr/browser/lightning/utils/UrlUtils.java b/app/src/main/java/acr/browser/lightning/utils/UrlUtils.java index 1cb5b9bf1..2baa6bd97 100644 --- a/app/src/main/java/acr/browser/lightning/utils/UrlUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/UrlUtils.java @@ -15,12 +15,19 @@ */ package acr.browser.lightning.utils; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.Patterns; import android.webkit.URLUtil; import java.util.regex.Matcher; import java.util.regex.Pattern; +import acr.browser.lightning.constant.BookmarkPage; +import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.constant.HistoryPage; +import acr.browser.lightning.constant.StartPage; + /** * Utility methods for Url manipulation */ @@ -28,7 +35,7 @@ public class UrlUtils { private static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile( "(?i)" + // switch on case insensitive matching '(' + // begin group for schema - "(?:http|https|file):\\/\\/" + + "(?:http|https|file)://" + "|(?:inline|data|about|javascript):" + "|(?:.*:.*@)" + ')' + @@ -53,7 +60,8 @@ private UrlUtils() { /* cannot be instantiated */ } * @return a stripped url like "www.google.com", or the original string if it could * not be stripped */ - public static String stripUrl(String url) { + @Nullable + public static String stripUrl(@Nullable String url) { if (url == null) return null; Matcher m = STRIP_URL_PATTERN.matcher(url); if (m.matches()) { @@ -74,7 +82,8 @@ public static String stripUrl(String url) { * URL. If false, invalid URLs will return null * @return Original or modified URL */ - public static String smartUrlFilter(String url, boolean canBeSearch, String searchUrl) { + @NonNull + public static String smartUrlFilter(@NonNull String url, boolean canBeSearch, String searchUrl) { String inUrl = url.trim(); boolean hasSpace = inUrl.indexOf(' ') != -1; Matcher matcher = ACCEPTED_URI_SCHEMA.matcher(inUrl); @@ -99,11 +108,12 @@ public static String smartUrlFilter(String url, boolean canBeSearch, String sear return URLUtil.composeSearchUrl(inUrl, searchUrl, QUERY_PLACE_HOLDER); } - return null; + return ""; } /* package */ - static String fixUrl(String inUrl) { + @NonNull + static String fixUrl(@NonNull String inUrl) { // FIXME: Converting the url to lower case // duplicates functionality in smartUrlFilter(). // However, changing all current callers of fixUrl to @@ -135,7 +145,8 @@ static String fixUrl(String inUrl) { // Returns the filtered URL. Cannot return null, but can return an empty string /* package */ - static String filteredUrl(String inUrl) { + @Nullable + static String filteredUrl(@Nullable String inUrl) { if (inUrl == null) { return ""; } @@ -145,4 +156,44 @@ static String filteredUrl(String inUrl) { } return inUrl; } + + /** + * Returns whether the given url is the bookmarks/history page or a normal website + */ + public static boolean isSpecialUrl(@Nullable String url) { + return url != null && url.startsWith(Constants.FILE) && + (url.endsWith(BookmarkPage.FILENAME) || + url.endsWith(HistoryPage.FILENAME) || + url.endsWith(StartPage.FILENAME)); + } + + /** + * Determines if the url is a url for the bookmark page. + * + * @param url the url to check, may be null. + * @return true if the url is a bookmark url, false otherwise. + */ + public static boolean isBookmarkUrl(@Nullable String url) { + return url != null && url.startsWith(Constants.FILE) && url.endsWith(BookmarkPage.FILENAME); + } + + /** + * Determines if the url is a url for the history page. + * + * @param url the url to check, may be null. + * @return true if the url is a history url, false otherwise. + */ + public static boolean isHistoryUrl(@Nullable String url) { + return url != null && url.startsWith(Constants.FILE) && url.endsWith(HistoryPage.FILENAME); + } + + /** + * Determines if the url is a url for the start page. + * + * @param url the url to check, may be null. + * @return true if the url is a start page url, false otherwise. + */ + public static boolean isStartPageUrl(@Nullable String url) { + return url != null && url.startsWith(Constants.FILE) && url.endsWith(StartPage.FILENAME); + } } \ No newline at end of file diff --git a/app/src/main/java/acr/browser/lightning/utils/Utils.java b/app/src/main/java/acr/browser/lightning/utils/Utils.java index c5c59858a..705ed2c89 100644 --- a/app/src/main/java/acr/browser/lightning/utils/Utils.java +++ b/app/src/main/java/acr/browser/lightning/utils/Utils.java @@ -3,6 +3,7 @@ */ package acr.browser.lightning.utils; +import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; @@ -11,6 +12,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -18,16 +20,23 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.Shader; +import android.net.Uri; +import android.os.Build; import android.os.Environment; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.design.widget.Snackbar; import android.support.v7.app.AlertDialog; +import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.webkit.URLUtil; +import com.anthonycr.grant.PermissionsManager; +import com.anthonycr.grant.PermissionsResultAction; + import java.io.Closeable; import java.io.File; import java.io.IOException; @@ -37,19 +46,59 @@ import java.util.Date; import acr.browser.lightning.R; +import acr.browser.lightning.activity.MainActivity; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.download.DownloadHandler; +import acr.browser.lightning.preference.PreferenceManager; public final class Utils { - public static void downloadFile(final Activity activity, final String url, + public static boolean doesSupportHeaders() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + } + + /** + * Downloads a file from the specified URL. Handles permissions + * requests, and creates all the necessary dialogs that must be + * showed to the user. + * + * @param activity activity needed to created dialogs. + * @param url url to download from. + * @param userAgent the user agent of the browser. + * @param contentDisposition the content description of the file. + */ + public static void downloadFile(final Activity activity, final PreferenceManager manager, final String url, final String userAgent, final String contentDisposition) { - String fileName = URLUtil.guessFileName(url, null, null); - DownloadHandler.onDownloadStart(activity, url, userAgent, contentDisposition, null - ); - Log.i(Constants.TAG, "Downloading" + fileName); + PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(activity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE}, new PermissionsResultAction() { + @Override + public void onGranted() { + String fileName = URLUtil.guessFileName(url, null, null); + DownloadHandler.onDownloadStart(activity, manager, url, userAgent, contentDisposition, null); + Log.i(Constants.TAG, "Downloading" + fileName); + } + + @Override + public void onDenied(String permission) { + // TODO Show Message + } + }); + } + /** + * Creates a new intent that can launch the email + * app with a subject, address, body, and cc. It + * is used to handle mail:to links. + * + * @param address the address to send the email to. + * @param subject the subject of the email. + * @param body the body of the email. + * @param cc extra addresses to CC. + * @return a valid intent. + */ + @NonNull public static Intent newEmailIntent(String address, String subject, String body, String cc) { Intent intent = new Intent(Intent.ACTION_SEND); @@ -61,12 +110,19 @@ public static Intent newEmailIntent(String address, String subject, return intent; } - public static void createInformativeDialog(Context context, @StringRes int title, @StringRes int message) { - AlertDialog.Builder builder = new AlertDialog.Builder(context); + /** + * Creates a dialog with only a title, message, and okay button. + * + * @param activity the activity needed to create a dialog. + * @param title the title of the dialog. + * @param message the message of the dialog. + */ + public static void createInformativeDialog(@NonNull Activity activity, @StringRes int title, @StringRes int message) { + AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(title); builder.setMessage(message) .setCancelable(true) - .setPositiveButton(context.getResources().getString(R.string.action_ok), + .setPositiveButton(activity.getResources().getString(R.string.action_ok), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { @@ -76,30 +132,51 @@ public void onClick(DialogInterface dialog, int id) { alert.show(); } + /** + * Displays a snackbar to the user with a String resource. + * + * @param activity the activity needed to create a snackbar. + * @param resource the string resource to show to the user. + */ public static void showSnackbar(@NonNull Activity activity, @StringRes int resource) { View view = activity.findViewById(android.R.id.content); if (view == null) return; Snackbar.make(view, resource, Snackbar.LENGTH_SHORT).show(); } - public static void showSnackbar(@NonNull Activity activity, String message) { + /** + * Displays a snackbar to the user with a string message. + * + * @param activity the activity needed to create a snackbar. + * @param message the string message to show to the user. + */ + public static void showSnackbar(@NonNull Activity activity, @NonNull String message) { View view = activity.findViewById(android.R.id.content); if (view == null) return; Snackbar.make(view, message, Snackbar.LENGTH_SHORT).show(); } /** - * Converts Density Pixels (DP) to Pixels (PX) + * Converts Density Pixels (DP) to Pixels (PX). * - * @param dp the number of density pixels to convert - * @return the number of pixels + * @param dp the number of density pixels to convert. + * @return the number of pixels that the conversion generates. */ - public static int dpToPx(int dp) { + public static int dpToPx(float dp) { DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics(); return (int) (dp * metrics.density + 0.5f); } - public static String getDomainName(String url) { + /** + * Extracts the domain name from a URL. + * + * @param url the URL to extract the domain from. + * @return the domain name, or the URL if the domain + * could not be extracted. The domain name may include + * HTTPS if the URL is an SSL supported URL. + */ + @Nullable + public static String getDomainName(@Nullable String url) { if (url == null || url.isEmpty()) return ""; boolean ssl = url.startsWith(Constants.HTTPS); @@ -127,16 +204,11 @@ public static String getDomainName(String url) { return domain.startsWith("www.") ? domain.substring(4) : domain; } - public static String getProtocol(String url) { - int index = url.indexOf('/'); - return url.substring(0, index + 2); - } - - public static String[] getArray(String input) { + public static String[] getArray(@NonNull String input) { return input.split(Constants.SEPARATOR); } - public static void trimCache(Context context) { + public static void trimCache(@NonNull Context context) { try { File dir = context.getCacheDir(); @@ -148,7 +220,7 @@ public static void trimCache(Context context) { } } - private static boolean deleteDir(File dir) { + private static boolean deleteDir(@Nullable File dir) { if (dir != null && dir.isDirectory()) { String[] children = dir.list(); for (String aChildren : children) { @@ -169,7 +241,7 @@ private static boolean deleteDir(File dir) { * @param bitmap is the bitmap to pad. * @return the padded bitmap. */ - public static Bitmap padFavicon(Bitmap bitmap) { + public static Bitmap padFavicon(@NonNull Bitmap bitmap) { int padding = Utils.dpToPx(4); Bitmap paddedBitmap = Bitmap.createBitmap(bitmap.getWidth() + padding, bitmap.getHeight() @@ -230,7 +302,7 @@ public static File createImageFile() throws IOException { * @param context the context needed to obtain the PackageManager * @return true if flash is installed, false otherwise */ - public static boolean isFlashInstalled(Context context) { + public static boolean isFlashInstalled(@NonNull Context context) { try { PackageManager pm = context.getPackageManager(); ApplicationInfo ai = pm.getApplicationInfo("com.adobe.flashplayer", 0); @@ -249,7 +321,7 @@ public static boolean isFlashInstalled(Context context) { * * @param closeable the object to close */ - public static void close(Closeable closeable) { + public static void close(@Nullable Closeable closeable) { if (closeable == null) return; try { @@ -259,6 +331,24 @@ public static void close(Closeable closeable) { } } + /** + * Utility method to close cursors. Cursor did not + * implement Closeable until API 16, so using this + * method for when we want to close a cursor. + * + * @param cursor the cursor to close + */ + public static void close(@Nullable Cursor cursor) { + if (cursor == null) { + return; + } + try { + cursor.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + /** * Draws the trapezoid background for the horizontal tabs on a canvas object using * the specified color. @@ -266,7 +356,7 @@ public static void close(Closeable closeable) { * @param canvas the canvas to draw upon * @param color the color to use to draw the tab */ - public static void drawTrapezoid(Canvas canvas, int color, boolean withShader) { + public static void drawTrapezoid(@NonNull Canvas canvas, int color, boolean withShader) { Paint paint = new Paint(); paint.setColor(color); @@ -297,4 +387,34 @@ color, mixTwoColors(Color.BLACK, color, 0.5f), canvas.drawPath(wallpath, paint); } + + /** + * Creates a shortcut on the homescreen using the + * {@link HistoryItem} information that opens the + * browser. The icon, URL, and title are used in + * the creation of the shortcut. + * + * @param activity the activity needed to create + * the intent and show a snackbar message + * @param item the HistoryItem to create the shortcut from + */ + public static void createShortcut(@NonNull Activity activity, @NonNull HistoryItem item) { + if (TextUtils.isEmpty(item.getUrl())) { + return; + } + Log.d(Constants.TAG, "Creating shortcut: " + item.getTitle() + ' ' + item.getUrl()); + Intent shortcutIntent = new Intent(activity, MainActivity.class); + shortcutIntent.setData(Uri.parse(item.getUrl())); + + final String title = TextUtils.isEmpty(item.getTitle()) ? activity.getString(R.string.untitled) : item.getTitle(); + + Intent addIntent = new Intent(); + addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); + addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, title); + addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, item.getBitmap()); + addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); + activity.sendBroadcast(addIntent); + Utils.showSnackbar(activity, R.string.message_added_to_homescreen); + } + } diff --git a/app/src/main/java/acr/browser/lightning/utils/WebUtils.java b/app/src/main/java/acr/browser/lightning/utils/WebUtils.java index a5e5fd85e..1d165d40e 100644 --- a/app/src/main/java/acr/browser/lightning/utils/WebUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/WebUtils.java @@ -3,6 +3,7 @@ import android.content.Context; import android.os.Build; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import android.webkit.WebIconDatabase; @@ -33,8 +34,8 @@ public static void clearWebStorage() { WebStorage.getInstance().deleteAllData(); } - public static void clearHistory(@NonNull Context context) { - HistoryDatabase.getInstance().deleteHistory(); + public static void clearHistory(@NonNull Context context, @NonNull HistoryDatabase historyDatabase) { + historyDatabase.deleteHistory(); WebViewDatabase m = WebViewDatabase.getInstance(context); m.clearFormData(); m.clearHttpAuthUsernamePassword(); @@ -47,7 +48,7 @@ public static void clearHistory(@NonNull Context context) { Utils.trimCache(context); } - public static void clearCache(WebView view) { + public static void clearCache(@Nullable WebView view) { if (view == null) return; view.clearCache(true); } diff --git a/app/src/main/java/acr/browser/lightning/view/AnimatedProgressBar.java b/app/src/main/java/acr/browser/lightning/view/AnimatedProgressBar.java index 4be5e03cc..de145f123 100644 --- a/app/src/main/java/acr/browser/lightning/view/AnimatedProgressBar.java +++ b/app/src/main/java/acr/browser/lightning/view/AnimatedProgressBar.java @@ -8,6 +8,7 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.Parcelable; +import android.support.annotation.NonNull; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.animation.Animation; @@ -39,12 +40,12 @@ public class AnimatedProgressBar extends LinearLayout { private int mDrawWidth = 0; private int mProgressColor; - public AnimatedProgressBar(Context context, AttributeSet attrs) { + public AnimatedProgressBar(@NonNull Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } - public AnimatedProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { + public AnimatedProgressBar(@NonNull Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } @@ -55,7 +56,7 @@ public AnimatedProgressBar(Context context, AttributeSet attrs, int defStyleAttr * @param context is the context passed by the constructor * @param attrs is the attribute set passed by the constructor */ - private void init(final Context context, AttributeSet attrs) { + private void init(@NonNull final Context context, AttributeSet attrs) { TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AnimatedProgressBar, 0, 0); int backgroundColor; try { // Retrieve the style of the progress bar that the user hopefully set @@ -90,7 +91,7 @@ public int getProgress() { private final Rect mRect = new Rect(); @Override - protected void onDraw(Canvas canvas) { + protected void onDraw(@NonNull Canvas canvas) { mPaint.setColor(mProgressColor); mPaint.setStrokeWidth(10); mRect.right = mRect.left + mDrawWidth; @@ -209,6 +210,7 @@ protected void onRestoreInstanceState(Parcelable state) { super.onRestoreInstanceState(state); } + @NonNull @Override protected Parcelable onSaveInstanceState() { diff --git a/app/src/main/java/acr/browser/lightning/view/IconCacheTask.java b/app/src/main/java/acr/browser/lightning/view/IconCacheTask.java new file mode 100644 index 000000000..ec4018860 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/view/IconCacheTask.java @@ -0,0 +1,42 @@ +package acr.browser.lightning.view; + +import android.app.Application; +import android.graphics.Bitmap; +import android.net.Uri; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.utils.Utils; + +class IconCacheTask implements Runnable { + private final Uri uri; + private final Bitmap icon; + private final Application app; + + public IconCacheTask(final Uri uri, final Bitmap icon, final Application app) { + this.uri = uri; + this.icon = icon; + this.app = app; + } + + @Override + public void run() { + String hash = String.valueOf(uri.getHost().hashCode()); + Log.d(Constants.TAG, "Caching icon for " + uri.getHost()); + FileOutputStream fos = null; + try { + File image = new File(app.getCacheDir(), hash + ".png"); + fos = new FileOutputStream(image); + icon.compress(Bitmap.CompressFormat.PNG, 100, fos); + fos.flush(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + Utils.close(fos); + } + } +} diff --git a/app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java b/app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java new file mode 100644 index 000000000..0bbef7ee5 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java @@ -0,0 +1,208 @@ +package acr.browser.lightning.view; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Message; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AlertDialog; +import android.view.LayoutInflater; +import android.view.View; +import android.webkit.GeolocationPermissions; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; +import android.webkit.WebView; + +import com.anthonycr.grant.PermissionsManager; +import com.anthonycr.grant.PermissionsResultAction; + +import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.controller.UIController; +import acr.browser.lightning.utils.Preconditions; + +class LightningChromeClient extends WebChromeClient { + + private static final String TAG = LightningChromeClient.class.getSimpleName(); + + private static final String[] PERMISSIONS = new String[]{Manifest.permission.ACCESS_FINE_LOCATION}; + + @NonNull private final Activity mActivity; + @NonNull private final LightningView mLightningView; + @NonNull private final UIController mUIController; + + LightningChromeClient(@NonNull Activity activity, @NonNull LightningView lightningView) { + Preconditions.checkNonNull(activity); + Preconditions.checkNonNull(lightningView); + mActivity = activity; + mUIController = (UIController) activity; + mLightningView = lightningView; + } + + @Override + public void onProgressChanged(WebView view, int newProgress) { + if (mLightningView.isShown()) { + mUIController.updateProgress(newProgress); + } + } + + @Override + public void onReceivedIcon(@NonNull WebView view, Bitmap icon) { + mLightningView.getTitleInfo().setFavicon(icon); + mUIController.tabChanged(mLightningView); + cacheFavicon(view.getUrl(), icon, mActivity); + } + + /** + * Naive caching of the favicon according to the domain name of the URL + * + * @param icon the icon to cache + */ + private static void cacheFavicon(@Nullable final String url, @Nullable final Bitmap icon, @NonNull final Context context) { + if (icon == null || url == null) return; + final Uri uri = Uri.parse(url); + if (uri.getHost() == null) { + return; + } + BrowserApp.getIOThread().execute(new IconCacheTask(uri, icon, BrowserApp.get(context))); + } + + + @Override + public void onReceivedTitle(@Nullable WebView view, @Nullable String title) { + if (title != null && !title.isEmpty()) { + mLightningView.getTitleInfo().setTitle(title); + } else { + mLightningView.getTitleInfo().setTitle(mActivity.getString(R.string.untitled)); + } + mUIController.tabChanged(mLightningView); + if (view != null && view.getUrl() != null) { + mUIController.updateHistory(title, view.getUrl()); + } + } + + @Override + public void onGeolocationPermissionsShowPrompt(@NonNull final String origin, + @NonNull final GeolocationPermissions.Callback callback) { + PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(mActivity, PERMISSIONS, new PermissionsResultAction() { + @Override + public void onGranted() { + final boolean remember = true; + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); + builder.setTitle(mActivity.getString(R.string.location)); + String org; + if (origin.length() > 50) { + org = origin.subSequence(0, 50) + "..."; + } else { + org = origin; + } + builder.setMessage(org + mActivity.getString(R.string.message_location)) + .setCancelable(true) + .setPositiveButton(mActivity.getString(R.string.action_allow), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + callback.invoke(origin, true, remember); + } + }) + .setNegativeButton(mActivity.getString(R.string.action_dont_allow), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + callback.invoke(origin, false, remember); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + } + + @Override + public void onDenied(String permission) { + //TODO show message and/or turn off setting + } + }); + } + + @Override + public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, + Message resultMsg) { + mUIController.onCreateWindow(resultMsg); + return true; + } + + @Override + public void onCloseWindow(WebView window) { + mUIController.onCloseWindow(mLightningView); + } + + @SuppressWarnings("unused") + public void openFileChooser(ValueCallback uploadMsg) { + mUIController.openFileChooser(uploadMsg); + } + + @SuppressWarnings("unused") + public void openFileChooser(ValueCallback uploadMsg, String acceptType) { + mUIController.openFileChooser(uploadMsg); + } + + @SuppressWarnings("unused") + public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) { + mUIController.openFileChooser(uploadMsg); + } + + @Override + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, + WebChromeClient.FileChooserParams fileChooserParams) { + mUIController.showFileChooser(filePathCallback); + return true; + } + + /** + * Obtain an image that is displayed as a placeholder on a video until the video has initialized + * and can begin loading. + * + * @return a Bitmap that can be used as a place holder for videos. + */ + @Nullable + @Override + public Bitmap getDefaultVideoPoster() { + final Resources resources = mActivity.getResources(); + return BitmapFactory.decodeResource(resources, android.R.drawable.spinner_background); + } + + /** + * Inflate a view to send to a LightningView when it needs to display a video and has to + * show a loading dialog. Inflates a progress view and returns it. + * + * @return A view that should be used to display the state + * of a video's loading progress. + */ + @Override + public View getVideoLoadingProgressView() { + LayoutInflater inflater = LayoutInflater.from(mActivity); + return inflater.inflate(R.layout.video_loading_progress, null); + } + + @Override + public void onHideCustomView() { + mUIController.onHideCustomView(); + } + + @Override + public void onShowCustomView(View view, CustomViewCallback callback) { + mUIController.onShowCustomView(view, callback); + } + + @SuppressWarnings("deprecation") + @Override + public void onShowCustomView(View view, int requestedOrientation, + CustomViewCallback callback) { + mUIController.onShowCustomView(view, callback, requestedOrientation); + } +} diff --git a/app/src/main/java/acr/browser/lightning/view/LightningView.java b/app/src/main/java/acr/browser/lightning/view/LightningView.java index a14f65df8..04c994d58 100644 --- a/app/src/main/java/acr/browser/lightning/view/LightningView.java +++ b/app/src/main/java/acr/browser/lightning/view/LightningView.java @@ -4,28 +4,20 @@ package acr.browser.lightning.view; -import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; -import android.net.MailTo; -import android.net.Uri; -import android.net.http.SslError; import android.os.Build; +import android.os.Handler; import android.os.Message; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v7.app.AlertDialog; -import android.text.InputType; -import android.text.method.PasswordTransformationMethod; +import android.support.v4.util.ArrayMap; import android.util.Log; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; @@ -33,92 +25,105 @@ import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewConfiguration; +import android.view.ViewGroup; import android.webkit.CookieManager; -import android.webkit.GeolocationPermissions; -import android.webkit.HttpAuthHandler; -import android.webkit.SslErrorHandler; -import android.webkit.ValueCallback; -import android.webkit.WebChromeClient; -import android.webkit.WebResourceRequest; -import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebSettings.LayoutAlgorithm; import android.webkit.WebSettings.PluginState; import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.EditText; -import android.widget.LinearLayout; -import java.io.ByteArrayInputStream; +import com.squareup.otto.Bus; + import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.List; +import java.lang.ref.WeakReference; +import java.util.Map; + +import javax.inject.Inject; -import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.constant.StartPage; -import acr.browser.lightning.controller.BrowserController; +import acr.browser.lightning.controller.UIController; +import acr.browser.lightning.database.BookmarkManager; +import acr.browser.lightning.dialog.LightningDialogBuilder; import acr.browser.lightning.download.LightningDownloadListener; import acr.browser.lightning.preference.PreferenceManager; -import acr.browser.lightning.utils.AdBlock; -import acr.browser.lightning.utils.IntentUtils; -import acr.browser.lightning.utils.PermissionsManager; -import acr.browser.lightning.utils.ThemeUtils; +import acr.browser.lightning.react.Action; +import acr.browser.lightning.react.Observable; +import acr.browser.lightning.react.Schedulers; +import acr.browser.lightning.react.Subscriber; +import acr.browser.lightning.react.OnSubscribe; +import acr.browser.lightning.utils.ProxyUtils; +import acr.browser.lightning.utils.UrlUtils; import acr.browser.lightning.utils.Utils; +/** + * {@link LightningView} acts as a tab for the browser, + * handling WebView creation and handling logic, as well + * as properly initialing it. All interactions with the + * WebView should be made through this class. + */ public class LightningView { - private final Title mTitle; - private WebView mWebView; - private final boolean mIsIncognitoTab; - private final BrowserController mBrowserController; - private final GestureDetector mGestureDetector; - private final Activity mActivity; - private static String mHomepage; - private static String mDefaultUserAgent; - // TODO fix so that mWebpageBitmap can be static - static changes the icon when switching from light to dark and then back to light - private final Bitmap mWebpageBitmap; - private static PreferenceManager mPreferences; - private final AdBlock mAdBlock; - private final IntentUtils mIntentUtils; - private final Paint mPaint = new Paint(); - private boolean isForegroundTab; - private boolean mTextReflow = false; - private boolean mInvertPage = false; - private boolean mToggleDesktop = false; - private static float mMaxFling; + private static final String TAG = LightningView.class.getSimpleName(); + + public static final String HEADER_REQUESTED_WITH = "X-Requested-With"; + public static final String HEADER_WAP_PROFILE = "X-Wap-Profile"; + private static final String HEADER_DNT = "DNT"; private static final int API = android.os.Build.VERSION.SDK_INT; private static final int SCROLL_UP_THRESHOLD = Utils.dpToPx(10); - private static final float[] mNegativeColorArray = { + + private static String sHomepage; + private static String sDefaultUserAgent; + private static float sMaxFling; + private static final float[] sNegativeColorArray = { -1.0f, 0, 0, 0, 255, // red 0, -1.0f, 0, 0, 255, // green 0, 0, -1.0f, 0, 255, // blue 0, 0, 0, 1.0f, 0 // alpha }; - private final PermissionsManager mPermissionsManager; - private static final String[] PERMISSIONS = new String[]{Manifest.permission.ACCESS_FINE_LOCATION}; + private static final float[] sIncreaseContrastColorArray = { + 2.0f, 0, 0, 0, -160.f, // red + 0, 2.0f, 0, 0, -160.f, // green + 0, 0, 2.0f, 0, -160.f, // blue + 0, 0, 0, 1.0f, 0 // alpha + }; - @SuppressLint("NewApi") - public LightningView(Activity activity, String url, boolean darkTheme, boolean isIncognito, BrowserController controller) { + @NonNull private final LightningViewTitle mTitle; + @Nullable private WebView mWebView; + @NonNull private final UIController mUIController; + @NonNull private final GestureDetector mGestureDetector; + @NonNull private final Activity mActivity; + @NonNull private final Paint mPaint = new Paint(); + @Nullable private Object mTag; + private final boolean mIsIncognitoTab; + private boolean isForegroundTab; + private boolean mInvertPage = false; + private boolean mToggleDesktop = false; + @NonNull private final WebViewHandler mWebViewHandler = new WebViewHandler(this); + @NonNull private final Map mRequestHeaders = new ArrayMap<>(); + + @Inject Bus mEventBus; + @Inject PreferenceManager mPreferences; + @Inject LightningDialogBuilder mBookmarksDialogBuilder; + @Inject ProxyUtils mProxyUtils; + @Inject BookmarkManager mBookmarkManager; + public LightningView(@NonNull Activity activity, @Nullable String url, boolean isIncognito) { + BrowserApp.getAppComponent().inject(this); mActivity = activity; + mUIController = (UIController) activity; mWebView = new WebView(activity); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) { + mWebView.setId(View.generateViewId()); + } mIsIncognitoTab = isIncognito; - mTitle = new Title(activity, darkTheme); - mAdBlock = AdBlock.getInstance(activity.getApplicationContext()); - mPermissionsManager = PermissionsManager.getInstance(); - - mWebpageBitmap = mTitle.mDefaultIcon; - - mMaxFling = ViewConfiguration.get(activity).getScaledMaximumFlingVelocity(); + mTitle = new LightningViewTitle(activity); - mBrowserController = controller; + sMaxFling = ViewConfiguration.get(activity).getScaledMaximumFlingVelocity(); - mIntentUtils = new IntentUtils(mActivity); mWebView.setDrawingCacheBackgroundColor(Color.WHITE); mWebView.setFocusableInTouchMode(true); mWebView.setFocusable(true); @@ -135,18 +140,18 @@ public LightningView(Activity activity, String url, boolean darkTheme, boolean i mWebView.setScrollbarFadingEnabled(true); mWebView.setSaveEnabled(true); mWebView.setNetworkAvailable(true); - mWebView.setWebChromeClient(new LightningChromeClient(activity)); - mWebView.setWebViewClient(new LightningWebClient(activity)); + mWebView.setWebChromeClient(new LightningChromeClient(activity, this)); + mWebView.setWebViewClient(new LightningWebClient(activity, this)); mWebView.setDownloadListener(new LightningDownloadListener(activity)); mGestureDetector = new GestureDetector(activity, new CustomGestureListener()); mWebView.setOnTouchListener(new TouchListener()); - mDefaultUserAgent = mWebView.getSettings().getUserAgentString(); - initializeSettings(mWebView.getSettings(), activity); - initializePreferences(mWebView.getSettings(), activity); + sDefaultUserAgent = mWebView.getSettings().getUserAgentString(); + initializeSettings(); + initializePreferences(activity); if (url != null) { if (!url.trim().isEmpty()) { - mWebView.loadUrl(url); + mWebView.loadUrl(url, mRequestHeaders); } else { // don't load anything, the user is looking for a blank tab } @@ -155,46 +160,106 @@ public LightningView(Activity activity, String url, boolean darkTheme, boolean i } } + /** + * Sets the tag on the object, + * a reference to this object is held + * indefinitely. + * + * @param tag the tag to set, may be null. + */ + public void setTag(@Nullable Object tag) { + mTag = tag; + } + + /** + * The tag set on the object. + * + * @return the tag set on the object, + * may be null. + */ + @Nullable + public Object getTag() { + return mTag; + } + + /** + * This method loads the homepage for the browser. Either + * it loads the URL stored as the homepage, or loads the + * startpage or bookmark page if either of those are set + * as the homepage. + */ public void loadHomepage() { if (mWebView == null) { return; } - if (mHomepage.startsWith("about:home")) { - mWebView.loadUrl(StartPage.getHomepage(mActivity)); - } else if (mHomepage.startsWith("about:bookmarks")) { - mBrowserController.openBookmarkPage(mWebView); - } else { - mWebView.loadUrl(mHomepage); + switch (sHomepage) { + case "about:home": + loadStartpage(); + break; + case "about:bookmarks": + loadBookmarkpage(); + break; + default: + mWebView.loadUrl(sHomepage, mRequestHeaders); + break; } } /** - * Initialize the preference driven settings of the WebView + * This method gets the startpage URL from the {@link StartPage} + * class asynchronously and loads the URL in the WebView on the + * UI thread. + */ + private void loadStartpage() { + new StartPage(this, BrowserApp.get(mActivity)).load(); + } + + /** + * This method gets the bookmark page URL from the {@link BookmarkPage} + * class asynchronously and loads the URL in the WebView on the + * UI thread. It also caches the default folder icon locally. + */ + public void loadBookmarkpage() { + if (mWebView == null) + return; + new BookmarkPage(this, mActivity, mBookmarkManager).load(); + } + + /** + * Initialize the preference driven settings of the WebView. This method + * must be called whenever the preferences are changed within SharedPreferences. * - * @param settings the WebSettings object to use, you can pass in null - * if you don't have a reference to them - * @param context the context in which the WebView was created + * @param context the context in which the WebView was created, it is used + * to get the default UserAgent for the WebView. */ - @SuppressLint("NewApi") - public synchronized void initializePreferences(@Nullable WebSettings settings, Context context) { - if (settings == null && mWebView == null) { + @SuppressLint({"NewApi", "SetJavaScriptEnabled"}) + public synchronized void initializePreferences(Context context) { + if (mWebView == null) { return; - } else if (settings == null) { - settings = mWebView.getSettings(); } - mPreferences = PreferenceManager.getInstance(); - settings.setDefaultTextEncodingName(mPreferences.getTextEncoding()); - mHomepage = mPreferences.getHomepage(); - mAdBlock.updatePreference(); + WebSettings settings = mWebView.getSettings(); + + if (mPreferences.getDoNotTrackEnabled()) { + mRequestHeaders.put(HEADER_DNT, "1"); + } else { + mRequestHeaders.remove(HEADER_DNT); + } + if (mPreferences.getRemoveIdentifyingHeadersEnabled()) { + mRequestHeaders.put(HEADER_REQUESTED_WITH, ""); + mRequestHeaders.put(HEADER_WAP_PROFILE, ""); + } else { + mRequestHeaders.remove(HEADER_REQUESTED_WITH); + mRequestHeaders.remove(HEADER_WAP_PROFILE); + } + + settings.setDefaultTextEncodingName(mPreferences.getTextEncoding()); + sHomepage = mPreferences.getHomepage(); setColorMode(mPreferences.getRenderingMode()); if (!mIsIncognitoTab) { settings.setGeolocationEnabled(mPreferences.getLocationEnabled()); - if (mPreferences.getLocationEnabled() && !PermissionsManager.checkPermissions(mActivity, PERMISSIONS)) { - mPermissionsManager.requestPermissionsIfNecessary(mActivity, PERMISSIONS); - } } else { settings.setGeolocationEnabled(false); } @@ -242,7 +307,6 @@ public synchronized void initializePreferences(@Nullable WebSettings settings, C } if (mPreferences.getTextReflowEnabled()) { - mTextReflow = true; settings.setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS); if (API >= android.os.Build.VERSION_CODES.KITKAT) { try { @@ -250,11 +314,10 @@ public synchronized void initializePreferences(@Nullable WebSettings settings, C } catch (Exception e) { // This shouldn't be necessary, but there are a number // of KitKat devices that crash trying to set this - Log.e(Constants.TAG, "Problem setting LayoutAlgorithm to TEXT_AUTOSIZING"); + Log.e(TAG, "Problem setting LayoutAlgorithm to TEXT_AUTOSIZING"); } } } else { - mTextReflow = false; settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL); } @@ -295,12 +358,13 @@ public synchronized void initializePreferences(@Nullable WebSettings settings, C /** * Initialize the settings of the WebView that are intrinsic to Lightning and cannot * be altered by the user. Distinguish between Incognito and Regular tabs here. - * - * @param settings the WebSettings object to use. - * @param context the Context which was used to construct the WebView. */ @SuppressLint("NewApi") - private void initializeSettings(WebSettings settings, Context context) { + private void initializeSettings() { + if (mWebView == null) { + return; + } + final WebSettings settings = mWebView.getSettings(); if (API < Build.VERSION_CODES.JELLY_BEAN_MR2) { //noinspection deprecation settings.setAppCacheMaxSize(Long.MAX_VALUE); @@ -339,14 +403,87 @@ private void initializeSettings(WebSettings settings, Context context) { settings.setAllowUniversalAccessFromFileURLs(false); } - settings.setAppCachePath(context.getDir("appcache", 0).getPath()); - settings.setGeolocationDatabasePath(context.getDir("geolocation", 0).getPath()); - if (API < Build.VERSION_CODES.KITKAT) { - //noinspection deprecation - settings.setDatabasePath(context.getDir("databases", 0).getPath()); - } + getPathObservable("appcache") + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.main()) + .subscribe(new OnSubscribe() { + @Override + public void onNext(File item) { + settings.setAppCachePath(item.getPath()); + } + + @Override + public void onComplete() {} + }); + + getPathObservable("geolocation") + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.main()) + .subscribe(new OnSubscribe() { + @Override + public void onNext(File item) { + settings.setGeolocationDatabasePath(item.getPath()); + } + + @Override + public void onComplete() {} + }); + + getPathObservable("databases") + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.main()) + .subscribe(new OnSubscribe() { + @Override + public void onNext(File item) { + if (API < Build.VERSION_CODES.KITKAT) { + //noinspection deprecation + settings.setDatabasePath(item.getPath()); + } + } + + @Override + public void onComplete() {} + }); + + } + + private Observable getPathObservable(final String subFolder) { + return Observable.create(new Action() { + @Override + public void onSubscribe(@NonNull Subscriber subscriber) { + File file = BrowserApp.get(mActivity).getDir(subFolder, 0); + subscriber.onNext(file); + subscriber.onComplete(); + } + }); + } + + /** + * Getter for the {@link LightningViewTitle} of the + * current LightningView instance. + * + * @return a NonNull instance of LightningViewTitle + */ + @NonNull + public LightningViewTitle getTitleInfo() { + return mTitle; } + /** + * Returns whether or not the current tab is incognito or not. + * + * @return true if this tab is incognito, false otherwise + */ + public boolean isIncognito() { + return mIsIncognitoTab; + } + + /** + * This method is used to toggle the user agent between desktop + * and the current preference of the user. + * + * @param context the Context needed to set the user agent + */ public void toggleDesktopUA(@NonNull Context context) { if (mWebView == null) return; @@ -357,6 +494,22 @@ public void toggleDesktopUA(@NonNull Context context) { mToggleDesktop = !mToggleDesktop; } + /** + * This method sets the user agent of the current tab. + * There are four options, 1, 2, 3, 4. + *

+ * 1. use the default user agent + *

+ * 2. use the desktop user agent + *

+ * 3. use the mobile user agent + *

+ * 4. use a custom user agent, or the default user agent + * if none was set. + * + * @param context the context needed to get the default user agent. + * @param choice the choice of user agent to use, see above comments. + */ @SuppressLint("NewApi") private void setUserAgent(Context context, int choice) { if (mWebView == null) return; @@ -366,7 +519,7 @@ private void setUserAgent(Context context, int choice) { if (API >= Build.VERSION_CODES.JELLY_BEAN_MR1) { settings.setUserAgentString(WebSettings.getDefaultUserAgent(context)); } else { - settings.setUserAgentString(mDefaultUserAgent); + settings.setUserAgentString(sDefaultUserAgent); } break; case 2: @@ -376,7 +529,7 @@ private void setUserAgent(Context context, int choice) { settings.setUserAgentString(Constants.MOBILE_USER_AGENT); break; case 4: - String ua = mPreferences.getUserAgentString(mDefaultUserAgent); + String ua = mPreferences.getUserAgentString(sDefaultUserAgent); if (ua == null || ua.isEmpty()) { ua = " "; } @@ -385,20 +538,53 @@ private void setUserAgent(Context context, int choice) { } } + /** + * This method gets the additional headers that should be + * added with each request the browser makes. + * + * @return a non null Map of Strings with the additional + * request headers. + */ + @NonNull + Map getRequestHeaders() { + return mRequestHeaders; + } + + /** + * This method determines whether the current tab is visible or not. + * + * @return true if the WebView is non-null and visible, false otherwise. + */ public boolean isShown() { return mWebView != null && mWebView.isShown(); } + /** + * Pause the current WebView instance. + */ public synchronized void onPause() { - if (mWebView != null) + if (mWebView != null) { mWebView.onPause(); + Log.d(TAG, "WebView onPause: " + mWebView.getId()); + } } + /** + * Resume the current WebView instance. + */ public synchronized void onResume() { - if (mWebView != null) + if (mWebView != null) { mWebView.onResume(); + Log.d(TAG, "WebView onResume: " + mWebView.getId()); + } } + /** + * Notify the LightningView that there is low memory and + * for the WebView to free memory. Only applicable on + * pre-Lollipop devices. + */ + @Deprecated public synchronized void freeMemory() { if (mWebView != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { //noinspection deprecation @@ -406,15 +592,35 @@ public synchronized void freeMemory() { } } + /** + * This method sets the tab as the foreground tab or + * the background tab. + * + * @param isForeground true if the tab should be set as + * foreground, false otherwise. + */ public void setForegroundTab(boolean isForeground) { isForegroundTab = isForeground; - mBrowserController.updateTabs(); + mUIController.tabChanged(this); } + /** + * Determines if the tab is in the foreground or not. + * + * @return true if the tab is the foreground tab, + * false otherwise. + */ public boolean isForegroundTab() { return isForegroundTab; } + /** + * Gets the current progress of the WebView. + * + * @return returns a number between 0 and 100 with + * the current progress of the WebView. If the WebView + * is null, then the progress returned will be 100. + */ public int getProgress() { if (mWebView != null) { return mWebView.getProgress(); @@ -423,24 +629,62 @@ public int getProgress() { } } + /** + * Notify the WebView to stop the current load. + */ public synchronized void stopLoading() { if (mWebView != null) { mWebView.stopLoading(); } } + /** + * This method forces the layer type to hardware, which + * enables hardware rendering on the WebView instance + * of the current LightningView. + */ private void setHardwareRendering() { + if (mWebView == null) { + return; + } mWebView.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); } + /** + * This method sets the layer type to none, which + * means that either the GPU and CPU can both compose + * the layers when necessary. + */ private void setNormalRendering() { + if (mWebView == null) { + return; + } mWebView.setLayerType(View.LAYER_TYPE_NONE, null); } + /** + * This method forces the layer type to software, which + * disables hardware rendering on the WebView instance + * of the current LightningView and makes the CPU render + * the view. + */ public void setSoftwareRendering() { + if (mWebView == null) { + return; + } mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } + /** + * Sets the current rendering color of the WebView instance + * of the current LightningView. The for modes are normal + * rendering (0), inverted rendering (1), grayscale rendering (2), + * and inverted grayscale rendering (3) + * + * @param mode the integer mode to set as the rendering mode. + * see the numbers in documentation above for the + * values this method accepts. + */ private void setColorMode(int mode) { mInvertPage = false; switch (mode) { @@ -454,7 +698,7 @@ private void setColorMode(int mode) { break; case 1: ColorMatrixColorFilter filterInvert = new ColorMatrixColorFilter( - mNegativeColorArray); + sNegativeColorArray); mPaint.setColorFilter(filterInvert); setHardwareRendering(); @@ -469,7 +713,7 @@ private void setColorMode(int mode) { break; case 3: ColorMatrix matrix = new ColorMatrix(); - matrix.set(mNegativeColorArray); + matrix.set(sNegativeColorArray); ColorMatrix matrixGray = new ColorMatrix(); matrixGray.setSaturation(0); ColorMatrix concat = new ColorMatrix(); @@ -481,37 +725,73 @@ private void setColorMode(int mode) { mInvertPage = true; break; + case 4: + ColorMatrixColorFilter IncreaseHighContrast = new ColorMatrixColorFilter( + sIncreaseContrastColorArray); + mPaint.setColorFilter(IncreaseHighContrast); + setHardwareRendering(); + break; + } } + /** + * Pauses the JavaScript timers of the + * WebView instance, which will trigger a + * pause for all WebViews in the app. + */ public synchronized void pauseTimers() { if (mWebView != null) { mWebView.pauseTimers(); + Log.d(TAG, "Pausing JS timers"); } } + /** + * Resumes the JavaScript timers of the + * WebView instance, which will trigger a + * resume for all WebViews in the app. + */ public synchronized void resumeTimers() { if (mWebView != null) { mWebView.resumeTimers(); + Log.d(TAG, "Resuming JS timers"); } } + /** + * Requests focus down on the WebView instance + * if the view does not already have focus. + */ public void requestFocus() { if (mWebView != null && !mWebView.hasFocus()) { mWebView.requestFocus(); } } + /** + * Sets the visibility of the WebView to either + * View.GONE, View.VISIBLE, or View.INVISIBLE. + * other values passed in will have no effect. + * + * @param visible the visibility to set on the WebView. + */ public void setVisibility(int visible) { if (mWebView != null) { mWebView.setVisibility(visible); } } + /** + * Tells the WebView to reload the current page. + * If the proxy settings are not ready then the + * this method will not have an affect as the + * proxy must start before the load occurs. + */ public synchronized void reload() { // Check if configured proxy is available - if (mBrowserController.proxyIsNotReady()) { + if (!mProxyUtils.isProxyReady()) { // User has been notified return; } @@ -522,18 +802,12 @@ public synchronized void reload() { } /** - * Naive caching of the favicon according to the domain name of the URL - * @param icon the icon to cache + * Finds all the instances of the text passed to this + * method and highlights the instances of that text + * in the WebView. + * + * @param text the text to search for. */ - private void cacheFavicon(final Bitmap icon) { - if (icon == null) return; - final Uri uri = Uri.parse(getUrl()); - if (uri.getHost() == null) { - return; - } - new Thread(new IconCacheTask(uri, icon)).start(); - } - @SuppressLint("NewApi") public synchronized void find(String text) { if (mWebView != null) { @@ -546,26 +820,70 @@ public synchronized void find(String text) { } } + /** + * Notify the tab to shutdown and destroy + * its WebView instance and to remove the reference + * to it. After this method is called, the current + * instance of the LightningView is useless as + * the WebView cannot be recreated using the public + * api. + */ + // TODO fix bug where WebView.destroy is being called before the tab + // is removed and would cause a memory leak if the parent check + // was not in place. public synchronized void onDestroy() { if (mWebView != null) { + // Check to make sure the WebView has been removed + // before calling destroy() so that a memory leak is not created + ViewGroup parent = (ViewGroup) mWebView.getParent(); + if (parent != null) { + Log.e(TAG, "WebView was not detached from window before onDestroy"); + parent.removeView(mWebView); + } mWebView.stopLoading(); mWebView.onPause(); mWebView.clearHistory(); mWebView.setVisibility(View.GONE); mWebView.removeAllViews(); mWebView.destroyDrawingCache(); - // mWebView.destroy(); //this is causing the segfault + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + //this is causing the segfault occasionally below 4.2 + mWebView.destroy(); + } mWebView = null; } } + /** + * Tell the WebView to navigate backwards + * in its history to the previous page. + */ public synchronized void goBack() { if (mWebView != null) { mWebView.goBack(); } } - public String getUserAgent() { + /** + * Tell the WebView to navigate forwards + * in its history to the next page. + */ + public synchronized void goForward() { + if (mWebView != null) { + mWebView.goForward(); + } + } + + /** + * Get the current user agent used + * by the WebView. + * + * @return retuns the current user agent + * of the WebView instance, or an empty + * string if the WebView is null. + */ + @NonNull + private String getUserAgent() { if (mWebView != null) { return mWebView.getSettings().getUserAgentString(); } else { @@ -573,45 +891,189 @@ public String getUserAgent() { } } - public synchronized void goForward() { + /** + * Move the highlighted text in the WebView + * to the next matched text. This method will + * only have an affect after {@link LightningView#find(String)} + * is called. Otherwise it will do nothing. + */ + public synchronized void findNext() { if (mWebView != null) { - mWebView.goForward(); + mWebView.findNext(true); + } + } + + /** + * Move the highlighted text in the WebView + * to the previous matched text. This method will + * only have an affect after {@link LightningView#find(String)} + * is called. Otherwise it will do nothing. + */ + public synchronized void findPrevious() { + if (mWebView != null) { + mWebView.findNext(false); + } + } + + /** + * Clear the highlighted text in the WebView after + * {@link LightningView#find(String)} has been called. + * Otherwise it will have no affect. + */ + public synchronized void clearFindMatches() { + if (mWebView != null) { + mWebView.clearMatches(); } } + /** + * Gets whether or not the page rendering is inverted or not. + * The main purpose of this is to indicate that JavaScript + * should be run at the end of a page load to invert only + * the images back to their uninverted states. + * + * @return true if the page is in inverted mode, false otherwise. + */ + public boolean getInvertePage() { + return mInvertPage; + } + + /** + * Handles a long click on the page and delegates the URL to the + * proper dialog if it is not null, otherwise, it tries to get the + * URL using HitTestResult. + * + * @param url the url that should have been obtained from the WebView touch node + * thingy, if it is null, this method tries to deal with it and find + * a workaround. + */ + private void longClickPage(@Nullable final String url) { + if (mWebView == null) { + return; + } + final WebView.HitTestResult result = mWebView.getHitTestResult(); + String currentUrl = mWebView.getUrl(); + if (currentUrl != null && UrlUtils.isSpecialUrl(currentUrl)) { + if (currentUrl.endsWith(HistoryPage.FILENAME)) { + if (url != null) { + mBookmarksDialogBuilder.showLongPressedHistoryLinkDialog(mActivity, url); + } else if (result != null && result.getExtra() != null) { + final String newUrl = result.getExtra(); + mBookmarksDialogBuilder.showLongPressedHistoryLinkDialog(mActivity, newUrl); + } + } else if (currentUrl.endsWith(BookmarkPage.FILENAME)) { + if (url != null) { + mBookmarksDialogBuilder.showLongPressedDialogForBookmarkUrl(mActivity, url); + } else if (result != null && result.getExtra() != null) { + final String newUrl = result.getExtra(); + mBookmarksDialogBuilder.showLongPressedDialogForBookmarkUrl(mActivity, newUrl); + } + } + } else { + if (url != null) { + if (result != null) { + if (result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == WebView.HitTestResult.IMAGE_TYPE) { + mBookmarksDialogBuilder.showLongPressImageDialog(mActivity, url, getUserAgent()); + } else { + mBookmarksDialogBuilder.showLongPressLinkDialog(mActivity, url); + } + } else { + mBookmarksDialogBuilder.showLongPressLinkDialog(mActivity, url); + } + } else if (result != null && result.getExtra() != null) { + final String newUrl = result.getExtra(); + if (result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == WebView.HitTestResult.IMAGE_TYPE) { + mBookmarksDialogBuilder.showLongPressImageDialog(mActivity, newUrl, getUserAgent()); + } else { + mBookmarksDialogBuilder.showLongPressLinkDialog(mActivity, newUrl); + } + } + } + } + + /** + * Determines whether or not the WebView can go + * backward or if it as the end of its history. + * + * @return true if the WebView can go back, false otherwise. + */ public boolean canGoBack() { return mWebView != null && mWebView.canGoBack(); } + /** + * Determine whether or not the WebView can go + * forward or if it is at the front of its history. + * + * @return true if it can go forward, false otherwise. + */ public boolean canGoForward() { return mWebView != null && mWebView.canGoForward(); } + /** + * Gets the current WebView instance of the tab. + * + * @return the WebView instance of the tab, which + * can be null. + */ @Nullable - public WebView getWebView() { + public synchronized WebView getWebView() { return mWebView; } + /** + * Gets the favicon currently in use by + * the page. If the current page does not + * have a favicon, it returns a default + * icon. + * + * @return a non-null Bitmap with the + * current favicon. + */ + @NonNull public Bitmap getFavicon() { - return mTitle.getFavicon(); + return mTitle.getFavicon(mUIController.getUseDarkTheme()); } - public synchronized void loadUrl(String url) { + /** + * Loads the URL in the WebView. If the proxy settings + * are still initializing, then the URL will not load + * as it is necessary to have the settings initialized + * before a load occurs. + * + * @param url the non-null URL to attempt to load in + * the WebView. + */ + public synchronized void loadUrl(@NonNull String url) { // Check if configured proxy is available - if (mBrowserController.proxyIsNotReady()) { - // User has been notified + if (!mProxyUtils.isProxyReady()) { return; } if (mWebView != null) { - mWebView.loadUrl(url); + mWebView.loadUrl(url, mRequestHeaders); } } + /** + * Get the current title of the page, retrieved from + * the title object. + * + * @return the title of the page, or an empty string + * if there is no title. + */ + @NonNull public String getTitle() { return mTitle.getTitle(); } + /** + * Get the current URL of the WebView, or an empty + * string if the WebView is null or the URL is null. + * + * @return the current URL or an empty string. + */ @NonNull public String getUrl() { if (mWebView != null && mWebView.getUrl() != null) { @@ -621,500 +1083,11 @@ public String getUrl() { } } - private static class IconCacheTask implements Runnable { - private final Uri uri; - private final Bitmap icon; - - public IconCacheTask(Uri uri, Bitmap icon) { - this.uri = uri; - this.icon = icon; - } - - @Override - public void run() { - String hash = String.valueOf(uri.getHost().hashCode()); - Log.d(Constants.TAG, "Caching icon for " + uri.getHost()); - FileOutputStream fos = null; - try { - File image = new File(BrowserApp.getAppContext().getCacheDir(), hash + ".png"); - fos = new FileOutputStream(image); - icon.compress(Bitmap.CompressFormat.PNG, 100, fos); - fos.flush(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - Utils.close(fos); - } - } - } - - public class LightningWebClient extends WebViewClient { - - final Activity mActivity; - - LightningWebClient(Activity activity) { - mActivity = activity; - } - - @Override - public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (mAdBlock.isAd(request.getUrl().toString())) { - ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes()); - return new WebResourceResponse("text/plain", "utf-8", EMPTY); - } - } - return super.shouldInterceptRequest(view, request); - } - - @SuppressWarnings("deprecation") - @Override - public WebResourceResponse shouldInterceptRequest(WebView view, String url) { - if (mAdBlock.isAd(url)) { - ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes()); - return new WebResourceResponse("text/plain", "utf-8", EMPTY); - } - return null; - } - - @SuppressLint("NewApi") - @Override - public void onPageFinished(WebView view, String url) { - if (view.isShown()) { - mBrowserController.updateUrl(url, true); - view.postInvalidate(); - } - if (view.getTitle() == null || view.getTitle().isEmpty()) { - mTitle.setTitle(mActivity.getString(R.string.untitled)); - } else { - mTitle.setTitle(view.getTitle()); - } - if (API >= android.os.Build.VERSION_CODES.KITKAT && mInvertPage) { - view.evaluateJavascript(Constants.JAVASCRIPT_INVERT_PAGE, null); - } - mBrowserController.updateTabs(); - } - - @Override - public void onPageStarted(WebView view, String url, Bitmap favicon) { - if (isShown()) { - mBrowserController.updateUrl(url, false); - mBrowserController.showActionBar(); - } - mTitle.setFavicon(mWebpageBitmap); - mBrowserController.updateTabs(); - } - - @Override - public void onReceivedHttpAuthRequest(final WebView view, @NonNull final HttpAuthHandler handler, - final String host, final String realm) { - - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); - final EditText name = new EditText(mActivity); - final EditText password = new EditText(mActivity); - LinearLayout passLayout = new LinearLayout(mActivity); - passLayout.setOrientation(LinearLayout.VERTICAL); - - passLayout.addView(name); - passLayout.addView(password); - - name.setHint(mActivity.getString(R.string.hint_username)); - name.setSingleLine(); - password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); - password.setSingleLine(); - password.setTransformationMethod(new PasswordTransformationMethod()); - password.setHint(mActivity.getString(R.string.hint_password)); - builder.setTitle(mActivity.getString(R.string.title_sign_in)); - builder.setView(passLayout); - builder.setCancelable(true) - .setPositiveButton(mActivity.getString(R.string.title_sign_in), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - String user = name.getText().toString(); - String pass = password.getText().toString(); - handler.proceed(user.trim(), pass.trim()); - Log.d(Constants.TAG, "Request Login"); - - } - }) - .setNegativeButton(mActivity.getString(R.string.action_cancel), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - handler.cancel(); - - } - }); - AlertDialog alert = builder.create(); - alert.show(); - - } - - private boolean mIsRunning = false; - private float mZoomScale = 0.0f; - - @SuppressLint("NewApi") - @Override - public void onScaleChanged(final WebView view, final float oldScale, final float newScale) { - if (view.isShown() && mTextReflow && API >= android.os.Build.VERSION_CODES.KITKAT) { - if (mIsRunning) - return; - if (Math.abs(mZoomScale - newScale) > 0.01f) { - mIsRunning = view.postDelayed(new Runnable() { - - @Override - public void run() { - mZoomScale = newScale; - view.evaluateJavascript(Constants.JAVASCRIPT_TEXT_REFLOW, null); - mIsRunning = false; - } - - }, 100); - } - - } - } - - private List getAllSslErrorMessageCodes(SslError error) { - - List errorCodeMessageCodes = new ArrayList<>(); - - if (error.hasError(SslError.SSL_DATE_INVALID)) { - errorCodeMessageCodes.add(R.string.message_certificate_date_invalid); - } - if (error.hasError(SslError.SSL_EXPIRED)) { - errorCodeMessageCodes.add(R.string.message_certificate_expired); - } - if (error.hasError(SslError.SSL_IDMISMATCH)) { - errorCodeMessageCodes.add(R.string.message_certificate_domain_mismatch); - } - if (error.hasError(SslError.SSL_NOTYETVALID)) { - errorCodeMessageCodes.add(R.string.message_certificate_not_yet_valid); - } - if (error.hasError(SslError.SSL_UNTRUSTED)) { - errorCodeMessageCodes.add(R.string.message_certificate_untrusted); - } - if (error.hasError(SslError.SSL_INVALID)) { - errorCodeMessageCodes.add(R.string.message_certificate_invalid); - } - - return errorCodeMessageCodes; - } - - @Override - public void onReceivedSslError(WebView view, @NonNull final SslErrorHandler handler, SslError error) { - - List errorCodeMessageCodes = getAllSslErrorMessageCodes(error); - - StringBuilder stringBuilder = new StringBuilder(); - for (Integer messageCode : errorCodeMessageCodes) { - stringBuilder.append(" - ").append(mActivity.getString(messageCode)).append('\n'); - } - String alertMessage = - mActivity.getString(R.string.message_insecure_connection, stringBuilder.toString()); - - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); - builder.setTitle(mActivity.getString(R.string.title_warning)); - builder.setMessage(alertMessage) - .setCancelable(true) - .setPositiveButton(mActivity.getString(R.string.action_yes), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - handler.proceed(); - } - }) - .setNegativeButton(mActivity.getString(R.string.action_no), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - handler.cancel(); - } - }); - builder.create().show(); - } - - @Override - public void onFormResubmission(WebView view, @NonNull final Message dontResend, final Message resend) { - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); - builder.setTitle(mActivity.getString(R.string.title_form_resubmission)); - builder.setMessage(mActivity.getString(R.string.message_form_resubmission)) - .setCancelable(true) - .setPositiveButton(mActivity.getString(R.string.action_yes), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - - resend.sendToTarget(); - } - }) - .setNegativeButton(mActivity.getString(R.string.action_no), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - - dontResend.sendToTarget(); - } - }); - AlertDialog alert = builder.create(); - alert.show(); - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - // Check if configured proxy is available - if (mBrowserController.proxyIsNotReady()) { - // User has been notified - return true; - } - - if (mIsIncognitoTab) { - return super.shouldOverrideUrlLoading(view, url); - } - if (url.startsWith("about:")) { - return super.shouldOverrideUrlLoading(view, url); - } - if (url.contains("mailto:")) { - MailTo mailTo = MailTo.parse(url); - Intent i = Utils.newEmailIntent(mailTo.getTo(), mailTo.getSubject(), - mailTo.getBody(), mailTo.getCc()); - mActivity.startActivity(i); - view.reload(); - return true; - } else if (url.startsWith("intent://")) { - Intent intent; - try { - intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); - } catch (URISyntaxException ex) { - return false; - } - if (intent != null) { - try { - mActivity.startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.e(Constants.TAG, "ActivityNotFoundException"); - } - return true; - } - } - return mIntentUtils.startActivityForUrl(mWebView, url); - } - } - - public class LightningChromeClient extends WebChromeClient { - - final Activity mActivity; - - LightningChromeClient(Activity activity) { - mActivity = activity; - } - - @Override - public void onProgressChanged(WebView view, int newProgress) { - if (isShown()) { - mBrowserController.updateProgress(newProgress); - } - } - - @Override - public void onReceivedIcon(WebView view, Bitmap icon) { - if (icon == null) - return; - mTitle.setFavicon(icon); - mBrowserController.updateTabs(); - cacheFavicon(icon); - } - - @Override - public void onReceivedTitle(WebView view, String title) { - if (title != null && !title.isEmpty()) { - mTitle.setTitle(title); - } else { - mTitle.setTitle(mActivity.getString(R.string.untitled)); - } - mBrowserController.updateTabs(); - if (view != null) - mBrowserController.updateHistory(title, view.getUrl()); - } - - @Override - public void onGeolocationPermissionsShowPrompt(final String origin, - final GeolocationPermissions.Callback callback) { - mPermissionsManager.requestPermissionsIfNecessary(mActivity, PERMISSIONS); - final boolean remember = true; - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); - builder.setTitle(mActivity.getString(R.string.location)); - String org; - if (origin.length() > 50) { - org = origin.subSequence(0, 50) + "..."; - } else { - org = origin; - } - builder.setMessage(org + mActivity.getString(R.string.message_location)) - .setCancelable(true) - .setPositiveButton(mActivity.getString(R.string.action_allow), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - callback.invoke(origin, true, remember); - } - }) - .setNegativeButton(mActivity.getString(R.string.action_dont_allow), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - callback.invoke(origin, false, remember); - } - }); - AlertDialog alert = builder.create(); - alert.show(); - - } - - @Override - public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, - Message resultMsg) { - mBrowserController.onCreateWindow(resultMsg); - return true; - } - - @Override - public void onCloseWindow(WebView window) { - mBrowserController.onCloseWindow(LightningView.this); - } - - @SuppressWarnings("unused") - public void openFileChooser(ValueCallback uploadMsg) { - mBrowserController.openFileChooser(uploadMsg); - } - - @SuppressWarnings("unused") - public void openFileChooser(ValueCallback uploadMsg, String acceptType) { - mBrowserController.openFileChooser(uploadMsg); - } - - @SuppressWarnings("unused") - public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) { - mBrowserController.openFileChooser(uploadMsg); - } - - public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, - WebChromeClient.FileChooserParams fileChooserParams) { - mBrowserController.showFileChooser(filePathCallback); - return true; - } - - @Override - public Bitmap getDefaultVideoPoster() { - return mBrowserController.getDefaultVideoPoster(); - } - - @Override - public View getVideoLoadingProgressView() { - return mBrowserController.getVideoLoadingProgressView(); - } - - @Override - public void onHideCustomView() { - mBrowserController.onHideCustomView(); - super.onHideCustomView(); - } - - @Override - public void onShowCustomView(View view, CustomViewCallback callback) { - // While these lines might look like they work, in practice, - // Full-screen videos won't work correctly. I may test this out some - // more - // if (view instanceof FrameLayout) { - // FrameLayout frame = (FrameLayout) view; - // if (frame.getFocusedChild() instanceof VideoView) { - // VideoView video = (VideoView) frame.getFocusedChild(); - // video.stopPlayback(); - // frame.removeView(video); - // video.setVisibility(View.GONE); - // } - // } else { - mBrowserController.onShowCustomView(view, callback); - - // } - - super.onShowCustomView(view, callback); - } - - @SuppressWarnings("deprecation") - @Override - @Deprecated - public void onShowCustomView(View view, int requestedOrientation, - CustomViewCallback callback) { - // While these lines might look like they work, in practice, - // Full-screen videos won't work correctly. I may test this out some - // more - // if (view instanceof FrameLayout) { - // FrameLayout frame = (FrameLayout) view; - // if (frame.getFocusedChild() instanceof VideoView) { - // VideoView video = (VideoView) frame.getFocusedChild(); - // video.stopPlayback(); - // frame.removeView(video); - // video.setVisibility(View.GONE); - // } - // } else { - mBrowserController.onShowCustomView(view, callback); - - // } - - super.onShowCustomView(view, requestedOrientation, callback); - } - } - - public class Title { - - private Bitmap mFavicon; - private String mTitle; - private final Bitmap mDefaultIcon; - - public Title(Context context, boolean darkTheme) { - mDefaultIcon = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, darkTheme); - mFavicon = mDefaultIcon; - mTitle = mActivity.getString(R.string.action_new_tab); - } - - public void setFavicon(Bitmap favicon) { - if (favicon == null) { - mFavicon = mDefaultIcon; - } else { - mFavicon = Utils.padFavicon(favicon); - } - } - - public void setTitle(String title) { - if (title == null) { - mTitle = ""; - } else { - mTitle = title; - } - } - - public void setTitleAndFavicon(String title, Bitmap favicon) { - mTitle = title; - - if (favicon == null) { - mFavicon = mDefaultIcon; - } else { - mFavicon = Utils.padFavicon(favicon); - } - } - - public String getTitle() { - return mTitle; - } - - public Bitmap getFavicon() { - return mFavicon; - } - - } - + /** + * The OnTouchListener used by the WebView so we can + * get scroll events and show/hide the action bar when + * the page is scrolled up/down. + */ private class TouchListener implements OnTouchListener { float mLocation; @@ -1123,7 +1096,7 @@ private class TouchListener implements OnTouchListener { @SuppressLint("ClickableViewAccessibility") @Override - public boolean onTouch(View view, MotionEvent arg1) { + public boolean onTouch(@Nullable View view, @NonNull MotionEvent arg1) { if (view == null) return false; @@ -1137,9 +1110,9 @@ public boolean onTouch(View view, MotionEvent arg1) { } else if (mAction == MotionEvent.ACTION_UP) { final float distance = (mY - mLocation); if (distance > SCROLL_UP_THRESHOLD && view.getScrollY() < SCROLL_UP_THRESHOLD) { - mBrowserController.showActionBar(); + mUIController.showActionBar(); } else if (distance < -SCROLL_UP_THRESHOLD) { - mBrowserController.hideActionBar(); + mUIController.hideActionBar(); } mLocation = 0; } @@ -1148,15 +1121,21 @@ public boolean onTouch(View view, MotionEvent arg1) { } } + /** + * The SimpleOnGestureListener used by the {@link TouchListener} + * in order to delegate show/hide events to the action bar when + * the user flings the page. Also handles long press events so + * that we can capture them accurately. + */ private class CustomGestureListener extends SimpleOnGestureListener { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - int power = (int) (velocityY * 100 / mMaxFling); + int power = (int) (velocityY * 100 / sMaxFling); if (power < -10) { - mBrowserController.hideActionBar(); + mUIController.hideActionBar(); } else if (power > 15) { - mBrowserController.showActionBar(); + mUIController.showActionBar(); } return super.onFling(e1, e2, velocityX, velocityY); } @@ -1172,8 +1151,16 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve @Override public void onLongPress(MotionEvent e) { - if (mCanTriggerLongPress) - mBrowserController.onLongPress(); + if (mCanTriggerLongPress) { + Message msg = mWebViewHandler.obtainMessage(); + if (msg != null) { + msg.setTarget(mWebViewHandler); + if (mWebView == null) { + return; + } + mWebView.requestFocusNodeHref(msg); + } + } } /** @@ -1195,4 +1182,29 @@ public void onShowPress(MotionEvent e) { mCanTriggerLongPress = true; } } + + /** + * A Handler used to get the URL from a long click + * event on the WebView. It does not hold a hard + * reference to the WebView and therefore will not + * leak it if the WebView is garbage collected. + */ + private static class WebViewHandler extends Handler { + + @NonNull private final WeakReference mReference; + + public WebViewHandler(LightningView view) { + mReference = new WeakReference<>(view); + } + + @Override + public void handleMessage(@NonNull Message msg) { + super.handleMessage(msg); + final String url = msg.getData().getString("url"); + LightningView view = mReference.get(); + if (view != null) { + view.longClickPage(url); + } + } + } } diff --git a/app/src/main/java/acr/browser/lightning/view/LightningViewTitle.java b/app/src/main/java/acr/browser/lightning/view/LightningViewTitle.java new file mode 100644 index 000000000..abbf07cc8 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/view/LightningViewTitle.java @@ -0,0 +1,106 @@ +package acr.browser.lightning.view; + +import android.content.Context; +import android.graphics.Bitmap; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import acr.browser.lightning.R; +import acr.browser.lightning.utils.ThemeUtils; +import acr.browser.lightning.utils.Utils; + +/** + * {@link LightningViewTitle} acts as a container class + * for the favicon and page title, the information used + * by the tab adapters to show the tabs to the user. + */ +class LightningViewTitle { + + @Nullable private static Bitmap DEFAULT_DARK_ICON; + @Nullable private static Bitmap DEFAULT_LIGHT_ICON; + + @Nullable private Bitmap mFavicon = null; + @NonNull private String mTitle; + @NonNull private final Context mContext; + + public LightningViewTitle(@NonNull Context context) { + mContext = context; + mTitle = context.getString(R.string.action_new_tab); + } + + /** + * Set the current favicon to a new Bitmap. + * May be null, if null, the default will be used. + * + * @param favicon the potentially null favicon to set. + */ + public void setFavicon(@Nullable Bitmap favicon) { + if (favicon == null) { + mFavicon = null; + } else { + mFavicon = Utils.padFavicon(favicon); + } + } + + /** + * Helper method to initialize the DEFAULT_ICON variables + * + * @param context the context needed to initialize the Bitmap. + * @param darkTheme whether the icon should be themed dark or not. + * @return a not null icon. + */ + @NonNull + private static Bitmap getDefaultIcon(@NonNull Context context, boolean darkTheme) { + if (darkTheme) { + if (DEFAULT_DARK_ICON == null) { + DEFAULT_DARK_ICON = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, true); + } + return DEFAULT_DARK_ICON; + } else { + if (DEFAULT_LIGHT_ICON == null) { + DEFAULT_LIGHT_ICON = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, false); + } + return DEFAULT_LIGHT_ICON; + } + } + + /** + * Set the current title to a new title. + * Must not be null. + * + * @param title the non-null title to set. + */ + public void setTitle(@Nullable String title) { + if (title == null) { + mTitle = ""; + } else { + mTitle = title; + } + } + + /** + * Gets the current title, which is not null. + * Can be an empty string. + * + * @return the non-null title. + */ + @NonNull + public String getTitle() { + return mTitle; + } + + /** + * Gets the favicon of the page, which is not null. + * Either the favicon, or a default icon. + * + * @return the favicon or a default if that is null. + */ + @NonNull + public Bitmap getFavicon(boolean darkTheme) { + if (mFavicon == null) { + return getDefaultIcon(mContext, darkTheme); + } + return mFavicon; + } + +} diff --git a/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java b/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java new file mode 100644 index 000000000..838ebabd2 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java @@ -0,0 +1,325 @@ +package acr.browser.lightning.view; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.MailTo; +import android.net.http.SslError; +import android.os.Build; +import android.os.Message; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AlertDialog; +import android.text.InputType; +import android.text.method.PasswordTransformationMethod; +import android.util.Log; +import android.webkit.HttpAuthHandler; +import android.webkit.SslErrorHandler; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.EditText; +import android.widget.LinearLayout; + +import java.io.ByteArrayInputStream; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; + +import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.controller.UIController; +import acr.browser.lightning.utils.AdBlock; +import acr.browser.lightning.utils.IntentUtils; +import acr.browser.lightning.utils.Preconditions; +import acr.browser.lightning.utils.ProxyUtils; +import acr.browser.lightning.utils.Utils; + +public class LightningWebClient extends WebViewClient { + + @NonNull private final Activity mActivity; + @NonNull private final LightningView mLightningView; + @NonNull private final UIController mUIController; + @NonNull private final IntentUtils mIntentUtils; + + @Inject ProxyUtils mProxyUtils; + @Inject AdBlock mAdBlock; + + LightningWebClient(@NonNull Activity activity, @NonNull LightningView lightningView) { + BrowserApp.getAppComponent().inject(this); + Preconditions.checkNonNull(activity); + Preconditions.checkNonNull(lightningView); + mActivity = activity; + mUIController = (UIController) activity; + mLightningView = lightningView; + mAdBlock.updatePreference(); + mIntentUtils = new IntentUtils(activity); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, @NonNull WebResourceRequest request) { + if (mAdBlock.isAd(request.getUrl().toString())) { + ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes()); + return new WebResourceResponse("text/plain", "utf-8", EMPTY); + } + return super.shouldInterceptRequest(view, request); + } + + @Nullable + @SuppressWarnings("deprecation") + @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + if (mAdBlock.isAd(url)) { + ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes()); + return new WebResourceResponse("text/plain", "utf-8", EMPTY); + } + return null; + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + @Override + public void onPageFinished(@NonNull WebView view, String url) { + if (view.isShown()) { + mUIController.updateUrl(url, true); + mUIController.setBackButtonEnabled(view.canGoBack()); + mUIController.setForwardButtonEnabled(view.canGoForward()); + view.postInvalidate(); + } + if (view.getTitle() == null || view.getTitle().isEmpty()) { + mLightningView.getTitleInfo().setTitle(mActivity.getString(R.string.untitled)); + } else { + mLightningView.getTitleInfo().setTitle(view.getTitle()); + } + if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && + mLightningView.getInvertePage()) { + view.evaluateJavascript(Constants.JAVASCRIPT_INVERT_PAGE, null); + } + mUIController.tabChanged(mLightningView); + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + mLightningView.getTitleInfo().setFavicon(null); + if (mLightningView.isShown()) { + mUIController.updateUrl(url, false); + mUIController.showActionBar(); + } + mUIController.tabChanged(mLightningView); + } + + @Override + public void onReceivedHttpAuthRequest(final WebView view, @NonNull final HttpAuthHandler handler, + final String host, final String realm) { + + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); + final EditText name = new EditText(mActivity); + final EditText password = new EditText(mActivity); + LinearLayout passLayout = new LinearLayout(mActivity); + passLayout.setOrientation(LinearLayout.VERTICAL); + + passLayout.addView(name); + passLayout.addView(password); + + name.setHint(mActivity.getString(R.string.hint_username)); + name.setSingleLine(); + password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); + password.setSingleLine(); + password.setTransformationMethod(new PasswordTransformationMethod()); + password.setHint(mActivity.getString(R.string.hint_password)); + builder.setTitle(mActivity.getString(R.string.title_sign_in)); + builder.setView(passLayout); + builder.setCancelable(true) + .setPositiveButton(mActivity.getString(R.string.title_sign_in), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + String user = name.getText().toString(); + String pass = password.getText().toString(); + handler.proceed(user.trim(), pass.trim()); + Log.d(Constants.TAG, "Request Login"); + + } + }) + .setNegativeButton(mActivity.getString(R.string.action_cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + handler.cancel(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + + } + + private boolean mIsRunning = false; + private float mZoomScale = 0.0f; + + @TargetApi(Build.VERSION_CODES.KITKAT) + @Override + public void onScaleChanged(@NonNull final WebView view, final float oldScale, final float newScale) { + if (view.isShown() && mLightningView.mPreferences.getTextReflowEnabled() && + Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + if (mIsRunning) + return; + if (Math.abs(mZoomScale - newScale) > 0.01f) { + mIsRunning = view.postDelayed(new Runnable() { + + @Override + public void run() { + mZoomScale = newScale; + view.evaluateJavascript(Constants.JAVASCRIPT_TEXT_REFLOW, null); + mIsRunning = false; + } + + }, 100); + } + + } + } + + @NonNull + private static List getAllSslErrorMessageCodes(@NonNull SslError error) { + List errorCodeMessageCodes = new ArrayList<>(1); + + if (error.hasError(SslError.SSL_DATE_INVALID)) { + errorCodeMessageCodes.add(R.string.message_certificate_date_invalid); + } + if (error.hasError(SslError.SSL_EXPIRED)) { + errorCodeMessageCodes.add(R.string.message_certificate_expired); + } + if (error.hasError(SslError.SSL_IDMISMATCH)) { + errorCodeMessageCodes.add(R.string.message_certificate_domain_mismatch); + } + if (error.hasError(SslError.SSL_NOTYETVALID)) { + errorCodeMessageCodes.add(R.string.message_certificate_not_yet_valid); + } + if (error.hasError(SslError.SSL_UNTRUSTED)) { + errorCodeMessageCodes.add(R.string.message_certificate_untrusted); + } + if (error.hasError(SslError.SSL_INVALID)) { + errorCodeMessageCodes.add(R.string.message_certificate_invalid); + } + + return errorCodeMessageCodes; + } + + @Override + public void onReceivedSslError(WebView view, @NonNull final SslErrorHandler handler, @NonNull SslError error) { + List errorCodeMessageCodes = getAllSslErrorMessageCodes(error); + + StringBuilder stringBuilder = new StringBuilder(); + for (Integer messageCode : errorCodeMessageCodes) { + stringBuilder.append(" - ").append(mActivity.getString(messageCode)).append('\n'); + } + String alertMessage = + mActivity.getString(R.string.message_insecure_connection, stringBuilder.toString()); + + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); + builder.setTitle(mActivity.getString(R.string.title_warning)); + builder.setMessage(alertMessage) + .setCancelable(true) + .setPositiveButton(mActivity.getString(R.string.action_yes), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + handler.proceed(); + } + }) + .setNegativeButton(mActivity.getString(R.string.action_no), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + handler.cancel(); + } + }); + builder.create().show(); + } + + @Override + public void onFormResubmission(WebView view, @NonNull final Message dontResend, @NonNull final Message resend) { + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); + builder.setTitle(mActivity.getString(R.string.title_form_resubmission)); + builder.setMessage(mActivity.getString(R.string.message_form_resubmission)) + .setCancelable(true) + .setPositiveButton(mActivity.getString(R.string.action_yes), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + resend.sendToTarget(); + } + }) + .setNegativeButton(mActivity.getString(R.string.action_no), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dontResend.sendToTarget(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + } + + @Override + public boolean shouldOverrideUrlLoading(@NonNull WebView view, @NonNull String url) { + // Check if configured proxy is available + if (!mProxyUtils.isProxyReady()) { + // User has been notified + return true; + } + + Map headers = mLightningView.getRequestHeaders(); + + if (mLightningView.isIncognito() && Utils.doesSupportHeaders()) { + view.loadUrl(url, headers); + return true; + } + if (url.startsWith("about:") && Utils.doesSupportHeaders()) { + view.loadUrl(url, headers); + return true; + } + if (url.startsWith("mailto:")) { + MailTo mailTo = MailTo.parse(url); + Intent i = Utils.newEmailIntent(mailTo.getTo(), mailTo.getSubject(), + mailTo.getBody(), mailTo.getCc()); + mActivity.startActivity(i); + view.reload(); + return true; + } else if (url.startsWith("intent://")) { + Intent intent; + try { + intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); + } catch (URISyntaxException ignored) { + intent = null; + } + if (intent != null) { + intent.addCategory(Intent.CATEGORY_BROWSABLE); + intent.setComponent(null); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + intent.setSelector(null); + } + try { + mActivity.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.e(Constants.TAG, "ActivityNotFoundException"); + } + return true; + } + } + + if (!mIntentUtils.startActivityForUrl(view, url) && Utils.doesSupportHeaders()) { + view.loadUrl(url, headers); + } + return Utils.doesSupportHeaders() || super.shouldOverrideUrlLoading(view, url); + } +} diff --git a/app/src/main/res/drawable-xhdpi/empty.png b/app/src/main/res/drawable-xhdpi/empty.png new file mode 100644 index 000000000..927ae2d43 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/empty.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 31777df80..313800b5d 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,6 +1,7 @@ - - + - + - + - - + + - - - + android:layout_height="match_parent" + android:layout_gravity="end" + android:background="?attr/drawerBackground" + android:fitsSystemWindows="true" + android:weightSum="1"/> + \ No newline at end of file diff --git a/app/src/main/res/layout/bookmark_drawer.xml b/app/src/main/res/layout/bookmark_drawer.xml index 1ed789c0e..416bfe9fc 100644 --- a/app/src/main/res/layout/bookmark_drawer.xml +++ b/app/src/main/res/layout/bookmark_drawer.xml @@ -1,7 +1,9 @@ - @@ -26,7 +28,7 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:contentDescription="Favicon" - android:src="@drawable/ic_action_star" /> + android:src="@drawable/ic_action_star"/> + android:textAppearance="?android:attr/textAppearanceLarge"/> + android:listSelector="?attr/listBackground"/> + android:src="@drawable/ic_action_desktop"/> + android:src="@drawable/ic_action_star"/> + android:src="@drawable/ic_action_reading"/> \ No newline at end of file diff --git a/app/src/main/res/layout/browser_content.xml b/app/src/main/res/layout/browser_content.xml index 3eb6969aa..d0839e65b 100644 --- a/app/src/main/res/layout/browser_content.xml +++ b/app/src/main/res/layout/browser_content.xml @@ -1,7 +1,9 @@ - + android:background="@color/primary_color" + android:clipChildren="true"/> diff --git a/app/src/main/res/layout/search_interface.xml b/app/src/main/res/layout/search_interface.xml index 6978bd220..0baba299d 100644 --- a/app/src/main/res/layout/search_interface.xml +++ b/app/src/main/res/layout/search_interface.xml @@ -30,7 +30,7 @@ android:orientation="horizontal"> - + android:src="@drawable/ic_action_tabs"/> + android:textAppearance="?android:attr/textAppearanceLarge"/> + android:dividerHeight="0dp" + android:overScrollMode="ifContentScrolls"/> + android:weightSum="4"> + android:src="@drawable/ic_action_back"/> + android:contentDescription="@string/action_homepage" + android:src="@drawable/ic_action_home"/> + android:src="@drawable/ic_action_forward"/> + + + + + diff --git a/app/src/main/res/layout/tab_list_item.xml b/app/src/main/res/layout/tab_list_item.xml index 630a75870..fa1cf46c5 100644 --- a/app/src/main/res/layout/tab_list_item.xml +++ b/app/src/main/res/layout/tab_list_item.xml @@ -1,61 +1,53 @@ - + - + + + android:maxLines="1" + android:minHeight="?android:attr/listPreferredItemHeightSmall" + android:singleLine="true" + android:textAppearance="?android:attr/textAppearanceListItemSmall"/> - + - - - - - - - - + android:layout_gravity="center" + android:contentDescription="Delete Tab" + android:src="@drawable/ic_action_delete"/> + \ No newline at end of file diff --git a/app/src/main/res/layout/tab_strip.xml b/app/src/main/res/layout/tab_strip.xml new file mode 100644 index 000000000..973dc46fe --- /dev/null +++ b/app/src/main/res/layout/tab_strip.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/app/src/main/res/layout/toolbar.xml b/app/src/main/res/layout/toolbar.xml index 9b144f32e..51f755dba 100644 --- a/app/src/main/res/layout/toolbar.xml +++ b/app/src/main/res/layout/toolbar.xml @@ -1,31 +1,30 @@ - - + android:layout_height="30dp"/> + custom:contentInsetStart="0dp"/> + custom:progressColor="?attr/colorAccent"/> \ No newline at end of file diff --git a/app/src/main/res/layout/toolbar_content.xml b/app/src/main/res/layout/toolbar_content.xml index 06789f50e..e7368c39d 100644 --- a/app/src/main/res/layout/toolbar_content.xml +++ b/app/src/main/res/layout/toolbar_content.xml @@ -7,7 +7,7 @@ diff --git a/app/src/main/res/menu-large/main.xml b/app/src/main/res/menu-large/main.xml index a66f8871e..92b63e110 100644 --- a/app/src/main/res/menu-large/main.xml +++ b/app/src/main/res/menu-large/main.xml @@ -17,7 +17,7 @@ -->

+ xmlns:app="http://schemas.android.com/apk/res-auto"> + diff --git a/app/src/main/res/menu-xlarge/main.xml b/app/src/main/res/menu-xlarge/main.xml index a66f8871e..92b63e110 100644 --- a/app/src/main/res/menu-xlarge/main.xml +++ b/app/src/main/res/menu-xlarge/main.xml @@ -17,7 +17,7 @@ --> + xmlns:app="http://schemas.android.com/apk/res-auto"> + diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml index 1aad8562b..9bfa01bf0 100644 --- a/app/src/main/res/menu/main.xml +++ b/app/src/main/res/menu/main.xml @@ -15,7 +15,7 @@ --> - + + diff --git a/app/src/main/res/raw/default_bookmarks.dat b/app/src/main/res/raw/default_bookmarks.dat index 4607c15a8..789234162 100644 --- a/app/src/main/res/raw/default_bookmarks.dat +++ b/app/src/main/res/raw/default_bookmarks.dat @@ -1,4 +1,4 @@ -{"url": "https://twitter.com/RestainoAnthony", "title": "The Developer", "folder": "", "order": 0} +{"url": "https://twitter.com/RestainoAnthony", "title": "Contact Me", "folder": "", "order": 0} {"url": "https://www.facebook.com/", "title": "Facebook", "folder": "", "order": 2} {"url": "https://twitter.com", "title": "Twitter", "folder": "", "order": 3} {"url": "https://www.google.com/", "title": "Google", "folder": "", "order": 4} diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 6f874a736..06c5eaa59 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -30,6 +30,7 @@ (دعم مخزون المتصفح ) إخفاء شريط الحالة أثناء التصفح مسح ملفات تعريف الارتباط + Clear Browser History ماذا تريد أن تفعل مع هذه الصورة؟ تحميل فتح diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c332fc10d..45d66c56c 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -54,6 +54,7 @@ (Unterstützter Standardbrowser gefunden) Statusleiste beim Surfen ausblenden Cookies löschen + Verlauf löschen Was möchten Sie mit diesem Bild tun? Herunterladen Öffnen @@ -183,4 +184,36 @@ URL Titel + Ordner + Umbenennen + Schwarzes Design (AMOLED) + Webinhalte leeren + Webinhalte beim Beenden leeren + Dunkles Design + Was möchten Sie mit diesem Ordner tun? + Was möchten Sie mit diesem Verlaufs-Eintrag tun? + Ordnername + Farben invertieren + Helles Design + Manueller Proxy + Die Verbindung zu dieser Seite ist nicht sicher:\n%1$s\nTrotzdem fortfahren? + Webinhalte geleert + Port: + Tabs + App-Design + Ordner umbenennen + HTTP-Proxy + I2P läuft nicht. + I2P Tunnel sind noch nicht bereit. + Zertifikats-Datum ungültig + Domain in Zertifikat stimmt nicht mit aktueller Domain überein + Zertifikat abgelaufen + Zertifikat ungültig + Zertifikat noch nicht gültig + Zertifikat wird nicht vertraut + Ungültige Adresse gefunden. Kann nicht herunterladen + Kann nicht an den eingestellten Ort herunterladen + Es sieht aus, als ob I2P installiert wäre. Möchten Sie es verwenden? + Textcodierung + Tabs in Drawer anzeigen diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 15035517a..8b474b1c2 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -37,6 +37,7 @@ (Detectado navegador de sistema soportado) Ocultar barra de estado al navegar Limpiar cookies + Limpiar historial ¿Qué quieres hacer con esta imagen? Descargar Abrir diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 52a778799..a4f180e78 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1,36 +1,230 @@ - + + + + + Lightning - Onglets + Nouvel onglet + Partager + Historique + Favoris + Ajouter aux favoris + Copier l\'adresse + Transférer Paramètres Accéder à ma position Enregistrer les mots de passe User Agent - Effacer les cookies en quittant - Effacer l\'historique en quittant - Activer Adobe Flash Player - Bloquer les publicités - Bloquer les images - Bloquer les cookies tiers - Au démarrage - Activer le mode plein écran + Activer Adobe Flash + Page d\'accueil + Mode plein écran Activer JavaScript - Inverser les couleurs - Utiliser le thème sombre - Historique - Favoris - Ajouter aux favoris - Copier l\'URL - Partager + Téléchargement + Paramètres avancés + Apache License 2.0 + Version application + Effacer le cache en quittant + Activer repositionnement du texte + Bloquer les images + Autoriser les sites à ouvrir de nouvelles fenêtres + Activer les cookies + Importer les favoris depuis un navigateur + Taille du texte + Recommendé + Moteur de recherche + Rechercher + Utiliser large fenêtre d\'affichage + Charger les pages en mode aperçu + Restaurer les onglets perdus au démarrage + Aucun navigateur détecté + Navigateur compatible détecté + Masquer la barre d\'état lors de la navigation + Effacer les cookies du navigateur + Effacer l\'historique du navigateur + Que voulez-vous faire avec cette image ? + Télécharger + Ouvrir + Que voulez-vous faire avec ce lien ? + Partager cette page + Que voulez-vous faire avec cet historique ? + Que voulez-vous faire avec ce favori ? + Supprimer + Page vide + Par défaut + Bureau + Mobile + Personnalisé + Moteur de recherche + OK + Voulez-vous télécharger ce fichier ? + Annuler + Avertissement + Adobe Flash Player n\'a pas été détecté.\nVeuillez installer Flash Player. + User Agent + Téléchargement + Page d\'accueil personnalisée + Page internet + Effacer l\'historique + Effacer les cookies + Voulez-vous effacer tout l\'historique du navigateur ? + Voulez-vous effacer tous les cookies du navigateur ? + Oui + Non + Taille du texte + Plus grand + Grand + Normal + Petit + Plus petit + Erreur + Aucun navigateur n\'a été détecté pour importer les favoris. + Titre + Adresse + Éditer le favori + Éditer Onglet incognito - Nouvel onglet + Par défaut + Retour Rechercher dans la page - Passer en mode lecture - Texte copié - URL copiée + Démarrage du téléchargement\u2026 + Téléchargement d\'adresses \"http\" ou \"https\" uniquement. + Adresse invalide, impossible de télécharger + Impossible de télécharger à l\'emplacement spécifié + Aucune carte SD + Le stockage USB est nécessaire pour télécharger le fichier. + Stockage USB indisponible + Le stockage est occupé. Pour autoriser les téléchargements, appuyez sur Désactiver le stockage USB dans la notification. + Activer les cookies en mode incognito + Adobe Flash + Manuel + Auto + Contact + twitter.com/RestainoAnthony + Effacer le cache + Cache effacé + Favoris ont été importés + Historique effacé + Cookies effacés + Onglets maximuns atteint + Texte copié dans le presse-papiers + Adresse copiée dans le presse-papiers + Adresse personnalisée + Le fichier local a été bloqué au chargement + Licences open source + Recherche + Bloquer les publicités + La connexion à ce site n\'est pas sécurisée :\n%1$s\nContinuer ? + la date du certificat est incorrecte + le certificat a expiré + le domaine du certificat ne correspond pas au domaine du site + le certificat est incorrect + le certificat n\'est toujours pas correct + le certificat n\'est pas approuvé + Nouvel envoi du formulaire + Voulez-vous renvoyer les données ? + \nSouhaiterait utiliser votre position + Autoriser + Refuser + Connexion + Nom d\'utilisateur + Mot de passe + Suggestions de recherche + Proposé par Google + Proxy HTTP + + Aucun + Orbot + I2P + Manuel + + Proxy manuel + Host : + Port : + Il semble que vous avez installé Orbot. Voulez-vous utiliser Tor ? + Il semble que vous avez installé I2P. Voulez-vous utiliser I2P ? + Veuillez installer Orbot comme proxy avec Tor. + I2P n\'est pas exécution. + Les tunnels I2P ne sont pas encore prêts. + Oui + Non + Effacer les cookies en quittant + Effacer l\'historique en quittant + Par défaut + Personnalisé + Exemple + Licence Mozilla Public v. 2.0 + Freeware + Android Open Source Project + hpHosts Ad Server List + Réouvrir l\'ancien onglet + Mode de rendu + Inversé + Niveaux de gris + Niveaux de gris inversés + Normal + Synchroniser l\'historique avec Google + Explorateur de fichiers + NetCipher + Licence GNU Lesser General Public + Exporter les favoris à sauvegarder + Importer les favoris depuis une sauvegarde + Favoris exportés dans + Favoris + Impossible d\'importer les favoris depuis le fichier + Choisir un fichier Général - Favoris Affichage Vie privée - A propos - Tout savoir sur la version, l\'auteur et la licence. + À propos + En savoir plus sur la version, l\'auteur et la licence. + Fermer l\'onglet + Fermer tous les onglets + Bloquer les cookies tiers + Activer le mode couleur + Mode lecteur + Chargement… + Impossible de charger quoi que ce soit depuis la page. + Snacktory + jsoup: Java HTML Parser + Licence MIT + Contenu du champs adresse + Encodage du texte + + Domaine (par défaut) + Adresse + Titre + + Inverser la couleur + Thème sombre + Onglets + Thème + Thème clair + Thème noir (AMOLED) + Nom du dossier + Dossier + Renommer + Renommer le dossier + Que voulez-vous faire avec ce dossier ? + Effacer le stockage internet + Effacer le stockage internet en quittant + Stockage internet effacé + Fichier Hosts source de blocage des publicités + Blocage des publicités + Afficher les onglets dans le volet de navigation + Demander de \'Ne pas pister\' + Supprimer les en-têtes d\'dentification diff --git a/app/src/main/res/values-gr/strings.xml b/app/src/main/res/values-gr/strings.xml index fc5a410a4..bdf439f5b 100644 --- a/app/src/main/res/values-gr/strings.xml +++ b/app/src/main/res/values-gr/strings.xml @@ -38,6 +38,7 @@ Βρέθηκε υποστηριζόμενος browser Απόκρυψη της γραμμής κατάστασης κατα την χρήση Καθαρισμός των cookies + Clear Browser History Τι θα θέλατε να κάνετε με την εικόνα; Κατέβασμα Άνοιγμα diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 39d13dbe8..cd31452a8 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -54,6 +54,7 @@ Támogatott gyári böngésző észlelve Állapotsáv elrejtése böngészés közben Cookie-k törlése + Clear Browser History Mit szeretnél csinálni ezzel a képpel? Letöltés Megnyitás diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index f8d4205bd..dd4a7933b 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -22,189 +22,201 @@ Cronologia Segnalibri Aggiungi segnalibro - Copia link + Copia Link Avanti - Impostazioni del browser - Accesso alla posizione - Salva Password + Impostazioni + Localizzazione + Salva password User Agent Abilita Adobe Flash - Homepage - Mod. Schermo intero + Home + Schermo Intero Abilita JavaScript - Percorso download - Impostazioni avanzate - Licenza Apache 2.0 - Versione Applicazione - Pulisci la cache alla chiusura - Abilità reflow del testo - Blocca immagini - Consenti l\'apertura di nuove finestre + Scegli un percorso + Avanzate + Apache License 2.0 + Versione App + Cancella la cache in chiusura + Abilità il reflow del testo + Blocca Immagini + Consenti a siti di aprire nuove finestre Abilita Cookies - Importa Segnalibri - Dimensione testo + Importa preferiti da browser + Dimensioni carattere Consigliato Motore di ricerca Cerca - Use wide viewport - Carica le pagine in modalità overview - Ricarica le schede perse all\'avvio + Utilizza ampia finestra + Carica le pagine in modalità panoramica + Restore lost tabs on start Nessun browser stock trovato Browser stock supportato trovato - Nascondi la barra di stato durante la navigazione + Nascondi la barra di stato mentre navighi Cancella i cookies - Cosa vorresti fare con questa immagine? + Cancella la cronologia + Cosa vuoi fare con questa immagine? Scarica Apri - Cosa vorresti fare con questo link? + Cosa vuoi fare con questo link? Condividi questa pagina - Cosa vorresti fare con questo segnalibro? - Cancella + Cosa vuoi fare con questo elemento di cronologia? + Cosa vorresti fare con questo preferito? + Elimina Pagina vuota Default Desktop Mobile - Personalizzato - Motore di ricerca + Custom + Motore di Ricerca OK - Vuoi scaricare questo file? + Vuoi scaricarlo? Annulla Attenzione - Adobe Flash Player non rilevato.\nSi prega di installare Flash Player. + Adobe Flash Player non è stato trovato.\nSi prega di installare Flash Player. User Agent - Percorso download - Homepage personale - Cancella cronologia - Cancella i cookie - Vorresti eliminare tutta la cronologia del browser? - Vorresti eliminare tutti i cookie del browser? + Percorso Download + Pagina iniziale + Pagina Web + Cancella la cronologia + Cancella i cookies + Vorresti cancellare tutta la cronologia? + Vorresti cancellare tutti i cookies? Si No - Dimensione testo - Più Grande + Dimensioni carattere + Più grande Grande Normale Piccolo - Più Piccolo + Più piccolo Errore - Nessun browser rilevato da cui importare segnalibri. + Non è stato trovato alcun browser. Titolo URL - Modifica Segnalibro + Modifica segnalibro Modifica - Nuova scheda anonima + Nuova scheda in incognito Default Indietro Trova nella pagina - Avvio scaricamento\u2026 - Possibile scaricare solo da URL \"http\" o \"https\". - Nessuna scheda SD - Una memoria USB è necessaria per scaricare il file. - Memoria USB non disponibile - La memoria di archiviazione è occupata. Per permettere i download, tocca Disattiva Memoria USB nella notifica. - Abilita Cookies in modalità incognito + Avvio download\u2026 + Puoi scaricare solo da URLs \"http\" o \"https\". + Nessuna SD Card trovata. + USB storage is required to download the file. + USB storage unavailable + The storage is busy. To allow downloads, touch Turn Off USB Storage in the notification. + Abilita i cookies nella modalità incognito Adobe Flash Manuale - Auto + Automatica Contattami - twitter.com/ACRDevelopment - Pulisci la Cache - Cache Cancellata - Segnalibri importati! - Cronologia Cancellata - Cookies Cancellati - Numero massimo di pagine raggiunto + twitter.com/RestainoAnthony + Cancella la cache + Cache cancellata + Segnalibri importati + Cronologia cancellata + Cookies cancellati + Massimo numero di schede raggiunto! Testo copiato negli appunti Link copiato negli appunti - Blocca Annunci - URL personale - Il caricamento del file locale è stato bloccato + URL Personalizzato + Local file has been blocked from loading + Licenze Open Source Cerca - Reinvio form - Vuoi reinviare i dati? + Blocca Annunci + Reinvio Modulo + Vuoi inviare nuovamente i dati? \nVorrebbe usare la tua posizione - Permetti - Nega - Accedi - Nome utente + Consenti + Non consentire + Registrati + Username Password Suggerimenti di ricerca - In collaborazione con Google + Powered by Google + HTTP Proxy + + Nessuno + Orbot + I2P + Manuale + Proxy manuale Host: Porta: - Sembra che tu abbia Orbot installato. Vuoi usare Tor? - Sembra che tu abbia I2P installato. Vuoi usare I2P? - Devi installare Orbot per navigare con Tor. - I2P non è attivo. - I tunnel I2P non sono ancora pronti. - + Sembra che tu abbia Orbot installato. Vorresti usare Tor? + Sembra che tu abbia I2P installato. Vorresti usare I2P? + Si prega di installare Orbot per creare un proxy con Tor. + I2P non è in esecuzione. + I tunnel I2P non sono pronti al momento. + Si No - Elimina i cookie all\'uscita - Elimina la cronologia all\'uscita - Predefinito - Personale + Cancella i cookies in chiusura + Cancella la cronologia in chiusura + Default + Custom Senza titolo Mozilla Public License v. 2.0 Freeware Android Open Source Project hpHosts Ad Server List - Vecchia scheda riaperta - Modalità rendering - Sincronizza cronologia con Google - Selezione file + Scheda precedente riaperta + Rendering Mode + Inverso + Scala di grigi + Scala di grigi invertita + Normale + Sincronizza la cronologia con l\'account Google + Seleziona file NetCipher GNU Lesser General Public License - Esporta segnalibri - Importa segnalibri da backup + Esporta i segnalibri per il backup + Importa i segnalibri da un backup Segnalibri esportati Segnalibri Impossibile importare i segnalibri dal file - Seleziona un file + Scegli un file Generali Display Privacy Informazioni - Informazioni sull\'autore, versione e license. - Inverso - Scala di grigi - Scala di grigi inversa - Normale - Licenze Open Source - Pagina Web + Informazioni sulla versione, l\'autore e licenze. Chiudi scheda Chiudi tutte le schede - Blocca i cookie di terze parti + Blocca cookies di terze parti. Attiva modalità colore Modalità lettura Caricamento… - Impossibile caricare la pagina. + Impossibile caricare la pagina web. Snacktory jsoup: Java HTML Parser - Licenza MIT - Contenuti campo URL + MIT License + Contenuto del campo URL + Codifica del testo - Dominio (predefinito) - URL - Titolo + Dominio (default) + URL + Titolo - Inverti colori - Tema Scuro - Schede - Temi - Tema Chiaro - Tema Nero (AMOLED) - Nomne Cartella + Inverti colori + Scuro + Schede + Tema App + Chiaro + Nero (AMOLED) + Nome cartella Cartella Rinomina - Rinomina Cartellar - Cosa vorresti fare con questa cartella? + Rinomina la cartella + Cosa vuoi fare con questa cartella? Clear Web Storage - Proxy HTTP - - Nessuno - Orbot - I2P - Manuale - + Clear web storage on exit + Web Storage Cleared + Fonte file host + Impostazioni di Ad Block + Mostra le schede nel menù + \'Non tenere traccia\' + Rimuovi header di identificazione + Aggiungi a Homescreen + Scorciatoia Aggiunta diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index d1bc5b6f3..132e09015 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -51,6 +51,7 @@ 読み込み可能なブラウザがあります ブラウズ中ステータスバーを非表示にする Cookiesを消去する + Clear Browser History この画像をどうしますか? ダウンロード 開く @@ -97,8 +98,8 @@ ページ内検索 Starting download\u2026 \"http\" や \"https\" から始まるURLのみダウンロードできます - SDカードがありません - ファイルをダウンロードするにはUSBストレージが必要です + SDカードがありません + ファイルをダウンロードするにはUSBストレージが必要です USBストレージが利用不可です ストレージがビジー状態です。ダウンロードするには、ステータスバーからUSB接続を変更してください。 シークレットタブでCookiesを有効にする @@ -164,4 +165,18 @@ 個人設定 About バージョン・開発者・ライセンスについての詳細 + 全てのタブを閉じる + タブを閉じる + カラーモードを有効にする + フォルダ + リネーム + Webストレージをクリア + 終了時にWebストレージをクリア + フォルダ名 + 色を反転する + リーダーモード + タブ + サードパーティCookieを無効にする + ナビゲーションドロワでタブを表示する + URL-Boxコンテンツ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 2da2861bc..7d95a0a4d 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -37,6 +37,7 @@ 지원되는 기본 브라우저 감지됨 브라우징 도중에는 상태 표시줄 숨기기 브라우저 쿠키 지우기 + Clear Browser History 이 이미지로 무엇을 할까요? 다운로드 열기 @@ -134,6 +135,7 @@ 반전 그레이스케일 반전된 그레이스케일 + 대비 증가 보통 Google과 기록 동기화 파일 선택기 diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 261345b3b..3016e7524 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -5,7 +5,7 @@ Udostępnij Historia Zakładki - Dodaj do zakładek + Dodaj zakładkę Kopiuj link Dalej Ustawienia @@ -18,7 +18,7 @@ Włącz Javascript Miejsce zapisu pobieranych plików Zaawansowane - Apache License 2.0 + Licencja Apache 2.0 Wersja aplikacji Wyczyść pamięć podręczną po zamknięciu Zawijaj tekst do rozmiaru ekranu @@ -37,11 +37,13 @@ Wspierana systemowa przeglądarka internetowa wykryta Ukryj pasek statusu Wyczyść ciasteczka + Clear Browser History Co chciałbyś zrobić z tym obrazkiem? Pobierz Otwórz Co chciałbyś zrobić z tym linkiem? Udostępnij tę stronę + Co chciałbyś zrobić z tym wpisem historii przeglądania? Co chciałbyś zrobić z tą zakładką? Usuń Pusta @@ -83,6 +85,8 @@ Znajdź na stronie Rozpoczynanie pobierania\u2026 Pliki mogą być pobierane tylko z linków zaczynających się od \"http\" lub \"https\". + Nie można pobrać - nieprawidłowy URL + Nie można pobrać pliku do wskazanej lokalizacji Brak karty SD Pamięć USB jest wymagana do pobrania tego pliku. Pamięć USB niedostępna @@ -106,6 +110,13 @@ Licencje Open Source Wyszukaj Blokuj reklamy + Połączenie z tą stroną jest niezaufane:\n%1$s\nKontynuuować mimo wszystko? + data ważności certyfikatu jest nieprawidłowa + domena dla której wystawiono certyfikat jest różna od domeny odwiedzanej strony + data ważności certyfikatu już upłynęła + ceryfikat jest nieprawidłowy + certyfikat nie jest jeszcze ważny + certyfikat jest niezaufany Ponowne wysyłanie formularza Czy chciałbyś ponownie wysłać dane wprowadzone do formularza? \nTa strona prosi o dostęp do twojej geolokalizacji @@ -116,8 +127,21 @@ Hasło Podpowiedzi wyszukiwania Dostarczane przez Google - Wygląda na to że Orbot jest zainstalowany. Chciałbyś go włączyć i użyć sieci Tor do łączenia się z internetem? - Zanistaluj Orbota by móc używać sieci Tor do łączenia się z internetem. + Konfiguracja serwera proxy + + Bez proxy + Orbot + I2P + Ręczne ustawienia + + Ręczne ustawienia proxy + Host: + Port: + Wygląda na to że Orbot jest zainstalowany. Chciałbyś go włączyć i użyć sieci TOR do łączenia się z internetem? + Wygląda na to że I2P jest zainstalowany. Chciałbyś go użyć do łączenia się z internetem? + Zainstaluj Orbota by móc używać sieci TOR do łączenia się z internetem. + I2P nie jest uruchomiony. + Tunele I2P nie są jeszcze gotowe. Tak Nie Wyczyść ciasteczka po zamknięciu @@ -128,19 +152,19 @@ Mozilla Public License v. 2.0 Freeware Android Open Source Project - hpHosts Ad Server List + Lista filtrów hpHosts Przywrócono kartę - Tryb Renderowania + Tryb renderowania Odwrócony - Odcienie Szarości - Odwrócone Odcienie Szarości + Odcienie szarości + Odwrócone odcienie szarości Normalny Synchronizuj historię z Google Wybierz plik NetCipher GNU Lesser General Public License - Eksportuj zakładki do backupu - Importuj zakładki z backupu + Eksportuj zakładki + Importuj zakładki Zakładki wyeksportowane do Zakładki Nie udało się zaimportować zakładek z @@ -148,10 +172,40 @@ Ogólne Wyświetlanie Prywatność - O Przeglądarce + O przeglądarce Szczegóły o wersji, autorze i licencji. Zamknij kartę Zamknij wszystkie karty Blokuj ciasteczka z innych witryn - Włącz Tryb zmieniania koloru + Włącz tryb zmieniania koloru + Tryb czytania + Ładowanie… + Nie udało się wczytać tekstu ze strony. + Snacktory + jsoup: Java HTML Parser + Licencja MIT + Zawartość paska adresu + Kodowanie tekstu + + Tylko domena (domyślne) + Pełny URL + Tytuł strony + + Odwróć kolory + Ciemny motyw + Karty + Wygląd aplikacji + Jasny motyw + Czarny motyw (AMOLED) + Nazwa folderu + Folder + Zmień nazwę + Zmień nazwę folderu + Co chciałbyś zrobić z tym folderem? + Wyczyść dane i ustawienia witryn + Wyczyść dane i ustawienia witryn po zamknięciu + Dane i ustawienia witryn zostały wyczyszczone + Źródło filtrów blokujących reklamy + Ustawienia blokowania reklam + Pokazuj karty w bocznym menu diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index e05a5d63b..68309dafb 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -53,6 +53,7 @@ Navegador detetado Ocultar barra de estado ao navegar Limpar cookies + Limpar histórico O que gostaria de fazer com esta imagem? Transferir Abrir diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index be8cc51b5..ccfe11ac0 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -37,6 +37,7 @@ Встроенный браузер обнаружен Без строки состояния Очистить cookies + Очистить историю Выберите действие для изображения Загрузить Открыть diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index d2951cdc7..4d624c7b9 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -1,10 +1,13 @@ - + Муња Нови језичак @@ -51,11 +54,13 @@ Откривен подржани уграђени прегледач Сакриј траку стања током прегледања Очисти колачиће + Очисти историјат Шта желите да урадите са овом сликом? Преузми Отвори Шта желите да урадите са овом везом? Подели ову страницу + Шта желите да урадите са овом ставком историјата? Шта желите да урадите са овим обележивачем? Обриши Празна страница @@ -96,7 +101,9 @@ Назад Нађи на страници Покрећем преузимање\u2026 - Преузимање је могуће само са „http“ или „https“ адреса. + Могу да преузмем само са „http“ или „https“ адреса. + Неисправан УРЛ, не могу да преузмем + Не могу да преузмем у наведену локацију Нема СД картице УСБ складиште је потребно за преузимање фајла. УСБ складиште није доступно @@ -120,6 +127,13 @@ Лиценце отвореног кôда Тражи Блокирај рекламе + Веза са овим сајтом није безбедна:\n%1$s\nДа наставим свеједно? + датум сертификата је неважећи + сертификат је истекао + домен на сертификату се не поклапа са доменом сајта + сертификат је неважећи + сертификат још није важећи + сертификат није поуздан Поновно слање формулара Желите ли да поново пошаљете податке? \nЖелите ли да користите вашу локацију @@ -130,8 +144,21 @@ Лозинка Предлози претраге Погоњено Гуглом + ХТТП прокси + + Ништа + Орбот + I2P + Ручно + + Ручне поставке проксија + Домаћин: + Порт: Изгледа да имате Орбот инсталиран. Желите ли да користите Тор? + Изгледа да имате I2P инсталиран. Желите ли да користите I2P? Инсталирајте Орбот да бисте користили Тор. + I2P није покренут. + I2P тунели још нису спремни. Да Не Очисти колачиће по затварању @@ -168,13 +195,14 @@ Затвори све језичке Блокирај колачиће треће стране Режим боје - Режим исцртавања + Режим за читање Учитавам… Нисам могао ништа да учитам са странице. Snacktory jsoup: Јава ХТМЛ рашчлањивач МИТ лиценца Садржај УРЛ бокса + Кодирање текста Домен (подраз.) УРЛ @@ -182,5 +210,19 @@ Обрнута боја Језичци - Тамна тема + Тема + Светла + Тамна + Црна (АМОЛЕД) + Назив фасцикле + Фасцикла + Преименуј + Преименуј фасциклу + Шта желите да урадите са овом фасциклом? + Очисти веб складиште + Очисти веб складиште по затварању + Веб складиште је очишћено + Извор hosts фајла за блокирање реклама + Поставке Адблока + Језичци у фиоци навигације diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index c39a4705e..e38368419 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -38,6 +38,7 @@ Desteklenen stock tarayıcı bulundu Gezinirken durum çubuğunu gizle Tarayıcı çerezlerini temizle + Clear Browser History Bu resimle ne yapmak istersin? İndir diff --git a/app/src/main/res/values-v16/styles.xml b/app/src/main/res/values-v16/styles.xml new file mode 100644 index 000000000..e5a495e9f --- /dev/null +++ b/app/src/main/res/values-v16/styles.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml index fd34e4935..64ad0eb9a 100644 --- a/app/src/main/res/values-v21/styles.xml +++ b/app/src/main/res/values-v21/styles.xml @@ -105,7 +105,7 @@ - - \ No newline at end of file diff --git a/app/src/main/res/xml/preference_bookmarks.xml b/app/src/main/res/xml/preference_bookmarks.xml index afb06c857..348fcaf84 100644 --- a/app/src/main/res/xml/preference_bookmarks.xml +++ b/app/src/main/res/xml/preference_bookmarks.xml @@ -4,12 +4,15 @@ + android:title="@string/export_bookmarks"/> + android:title="@string/import_backup"/> + android:title="@string/importbookmarks"/> + \ No newline at end of file diff --git a/app/src/main/res/xml/preference_privacy.xml b/app/src/main/res/xml/preference_privacy.xml index bf709e34b..b0a5e4a74 100644 --- a/app/src/main/res/xml/preference_privacy.xml +++ b/app/src/main/res/xml/preference_privacy.xml @@ -5,43 +5,51 @@ + android:title="@string/location"/> + android:title="@string/third_party"/> + android:title="@string/password"/> + + + android:title="@string/cache"/> + android:title="@string/clear_history_exit"/> + android:title="@string/clear_cookies_exit"/> + android:title="@string/clear_web_storage_exit"/> + android:title="@string/clear_cache"/> + android:title="@string/clear_history"/> + android:title="@string/clear_cookies"/> + android:title="@string/clear_web_storage"/> \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_headers.xml b/app/src/main/res/xml/preferences_headers.xml index 339554d4b..607000195 100644 --- a/app/src/main/res/xml/preferences_headers.xml +++ b/app/src/main/res/xml/preferences_headers.xml @@ -2,21 +2,32 @@
+ android:title="@string/settings_general"/>
+ android:title="@string/bookmark_settings"/>
+ android:title="@string/settings_display"/>
+ android:title="@string/settings_privacy"/>
+ android:title="@string/settings_advanced"/>
+ android:summary="@string/settings_about_explain" + android:title="@string/settings_about"/> +
+ + + +
\ No newline at end of file diff --git a/build.gradle b/build.gradle index 44b68983f..dcb49fc70 100644 --- a/build.gradle +++ b/build.gradle @@ -1,16 +1,17 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.1' + classpath 'com.android.tools.build:gradle:1.5.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.7' + classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.2.1' } } allprojects { repositories { jcenter() + mavenCentral() } }