diff --git a/.travis.yml b/.travis.yml index d0075eb88..66f370d0d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,8 @@ jdk: android: components: - tools - - build-tools-25.0.3 - - android-25 + - build-tools-26.0.0 + - android-26 - extra-android-support - extra-android-m2repository licenses: @@ -18,4 +18,5 @@ before_install: install: - ./gradlew script: - - ./gradlew assembleDebug --stacktrace + - ./gradlew :app:assembleDebug --stacktrace + - ./gradlew :app:test --stacktrace diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d1521ce8..c83a801fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,18 @@ Change Log ========== +Version 4.5.1 *(2017-06-28)* +---------------------------- +- Fixed bug with folders disappearing on bookmark homepage +- Updated history page +- Updated bookmark page +- Updating target to Android O +- Updating default bookmark favicons +- Fixed occasional bug with bookmark long press +- Updated downloads page design +- Enhanced keyboard shortcuts +- Fixed bug in google search suggestions for certain languages + Version 4.5.0 *(2017-06-08)* ---------------------------- - Translation updates diff --git a/README.md b/README.md index 43a7f3bab..5c4d40cbf 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,10 @@ ![](ic_launcher_small.png) #### Download -* [Download APK from here](https://github.com/anthonycr/Lightning-Browser/releases) - -* [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) +[Get it on F-Droid](https://f-droid.org/app/acr.browser.lightning) [](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) diff --git a/app/build.gradle b/app/build.gradle index 451c6d713..beb699bb5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,14 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' apply plugin: 'com.getkeepsafe.dexcount' android { - compileSdkVersion 25 - buildToolsVersion '25.0.3' + compileSdkVersion project.targetSdkVersion + buildToolsVersion project.buildToolsVersion defaultConfig { - minSdkVersion 14 - targetSdkVersion 25 + minSdkVersion project.minSdkVersion + targetSdkVersion project.targetSdkVersion versionName project.versionName vectorDrawables.useSupportLibrary = true } @@ -63,9 +64,10 @@ dexcount { } dependencies { + testCompile 'junit:junit:4.12' // support libraries - def supportLibVersion = '25.3.1' + def supportLibVersion = '25.4.0' compile "com.android.support:palette-v7:$supportLibVersion" compile "com.android.support:appcompat-v7:$supportLibVersion" compile "com.android.support:design:$supportLibVersion" @@ -107,4 +109,7 @@ dependencies { def leakCanaryVersion = '1.5.1' debugCompile "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion" releaseCompile "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion" + + // Kotlin + compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" } diff --git a/app/src/LightningPlus/assets/hosts.txt b/app/src/LightningPlus/assets/hosts.txt index e9d12ca49..0d66715b8 100644 --- a/app/src/LightningPlus/assets/hosts.txt +++ b/app/src/LightningPlus/assets/hosts.txt @@ -1033,6 +1033,7 @@ 127.0.0.1 4228414.fls.doubleclick.net 127.0.0.1 4235225.fls.doubleclick.net 127.0.0.1 4236808.fls.doubleclick.net +127.0.0.1 4294919531.kt.mixmarket.biz 127.0.0.1 4299553.fls.doubleclick.net 127.0.0.1 43.6.87.194.dynamic.dol.ru 127.0.0.1 4331660.fls.doubleclick.net @@ -14754,6 +14755,7 @@ 127.0.0.1 ad1.hpg.com.br 127.0.0.1 ad1.icorp.net 127.0.0.1 ad1.kde.cz +127.0.0.1 ad1.nend.net 127.0.0.1 ad1.pamedia.com.au 127.0.0.1 ad1.paycount.com 127.0.0.1 ad1.primorye.ru @@ -15137,6 +15139,7 @@ 127.0.0.1 ad2.doublepimp.com 127.0.0.1 ad2.ero-advertising.com 127.0.0.1 ad2.firehousezone.com +127.0.0.1 ad2.fivecdm.com 127.0.0.1 ad2.hpg.com.br 127.0.0.1 ad2.ip.ro 127.0.0.1 ad2.linxcz.cz @@ -16026,6 +16029,7 @@ 127.0.0.1 addeppo.com 127.0.0.1 addesktop.com 127.0.0.1 addfreestats.com +127.0.0.1 addiliate.com 127.0.0.1 addiply.com 127.0.0.1 addltest.timesink.com 127.0.0.1 addoer.com @@ -16055,6 +16059,8 @@ 127.0.0.1 adespresso.com 127.0.0.1 adex.adchakra.net 127.0.0.1 adex.admailtiser.com +127.0.0.1 adexc.net +127.0.0.1 adexce.net 127.0.0.1 adexcite.com 127.0.0.1 adexit.com 127.0.0.1 adexit.de @@ -16197,6 +16203,7 @@ 127.0.0.1 adg.bzgint.com 127.0.0.1 adgaem.go2cloud.org 127.0.0.1 adgalax.com +127.0.0.1 adgale.com 127.0.0.1 adgardener.com 127.0.0.1 adgatemedia.com 127.0.0.1 adgatemedia.go2cloud.org @@ -16537,6 +16544,7 @@ 127.0.0.1 adrspain.go2cloud.org 127.0.0.1 adrtrklnk.com 127.0.0.1 adrunner.superstats.com +127.0.0.1 adrunnr.com 127.0.0.1 ads-03.tor.focusin.ads.targetnet.com 127.0.0.1 ads-aa.wunderground.com 127.0.0.1 ads-au.onelouder.com @@ -16838,6 +16846,7 @@ 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.diamonds 127.0.0.1 ads.digital-digest.com 127.0.0.1 ads.digitalhealthcare.com 127.0.0.1 ads.digitalmedianet.com @@ -17074,6 +17083,7 @@ 127.0.0.1 ads.keywordblocks.com 127.0.0.1 ads.kinobox.cz 127.0.0.1 ads.kinxxx.com +127.0.0.1 ads.kiosked.com 127.0.0.1 ads.kizu.org 127.0.0.1 ads.klixxx.com 127.0.0.1 ads.komli.com @@ -17720,6 +17730,7 @@ 127.0.0.1 ads.webmastersdirect.com 127.0.0.1 ads.webmd.com 127.0.0.1 ads.webme.com +127.0.0.1 ads.webseoanalytics.com 127.0.0.1 ads.webshots.com 127.0.0.1 ads.website-guru.com 127.0.0.1 ads.whaleads.com @@ -18294,6 +18305,7 @@ 127.0.0.1 adserver.adreactor.com 127.0.0.1 adserver.ads.com.ph 127.0.0.1 adserver.ads360.com +127.0.0.1 adserver.adtech.advertising.com 127.0.0.1 adserver.adtech.de 127.0.0.1 adserver.adtech.de.1754.9018.302br.net 127.0.0.1 adserver.adtech.de.1756.9018.302br.net @@ -18441,6 +18453,7 @@ 127.0.0.1 adserver.trellian.com 127.0.0.1 adserver.tweakers.net 127.0.0.1 adserver.ubiyoo.com +127.0.0.1 adserver.unityads.unity3d.com 127.0.0.1 adserver.veruta.com 127.0.0.1 adserver.virgin.net 127.0.0.1 adserver.weakgame.com @@ -19197,7 +19210,6 @@ 127.0.0.1 affiliatenetwork.com 127.0.0.1 affiliateprogram.keywordspy.com 127.0.0.1 affiliatequality.com -127.0.0.1 affiliates-cdn.mozilla.org 127.0.0.1 affiliates.5dimes.com 127.0.0.1 affiliates.a2hosting.com 127.0.0.1 affiliates.about.com @@ -19384,6 +19396,7 @@ 127.0.0.1 akamai.bizrate.com 127.0.0.1 akamai.smartadserver.com 127.0.0.1 akillimeyveler.reklamagaci.com +127.0.0.1 akm.playnow.guru 127.0.0.1 akmnetwork.com 127.0.0.1 aknoj.voluumtrk.com 127.0.0.1 aksb-a.akamaihd.net @@ -19731,7 +19744,6 @@ 127.0.0.1 antoinette-grabowski.us 127.0.0.1 antoinette-hanlon.us 127.0.0.1 antoinette-meisner.us -127.0.0.1 anx.mindspark.com 127.0.0.1 any.gs 127.0.0.1 anycast.fw.adsafeprotected.com 127.0.0.1 anycast.pixel.adsafeprotected.com @@ -19886,7 +19898,6 @@ 127.0.0.1 api.vungle.com 127.0.0.1 api.w3i.com 127.0.0.1 api.widgetbucks.com -127.0.0.1 api.wipmania.com 127.0.0.1 api.xdirectx.com 127.0.0.1 api.yeahmobi.com 127.0.0.1 api.yieldkit.com @@ -20187,6 +20198,7 @@ 127.0.0.1 assets.servedby-buysellads.com 127.0.0.1 assets.sharethrough.com 127.0.0.1 assets.tapad.com +127.0.0.1 assistonglobal.com 127.0.0.1 assoc-amazon.co.uk 127.0.0.1 associatedcontent.112.2o7.net 127.0.0.1 associateprograms.com @@ -20510,7 +20522,6 @@ 127.0.0.1 bam-3.nr-data.net 127.0.0.1 bam-4.nr-data.net 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 @@ -21500,6 +21511,7 @@ 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 bc2.fivecdm.com 127.0.0.1 bc84-88b8-96b7-6515.reporo.net 127.0.0.1 bcanalytics.bigcommerce.com 127.0.0.1 bcast.pw @@ -21706,7 +21718,6 @@ 127.0.0.1 blog.adspaces.ero-advertising.com 127.0.0.1 blog.chartboost.com 127.0.0.1 blog.data.ero-advertising.com -127.0.0.1 blog.disqus.com 127.0.0.1 blog.duba.net 127.0.0.1 blog.ero-advertising.com 127.0.0.1 blog.intext.ero-advertising.com @@ -23301,6 +23312,7 @@ 127.0.0.1 c.aol.com 127.0.0.1 c.apple.com 127.0.0.1 c.atdmt.com +127.0.0.1 c.ato.mx 127.0.0.1 c.baidu.com 127.0.0.1 c.betrad.com 127.0.0.1 c.bigmir.net @@ -23365,6 +23377,7 @@ 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.us1.dyntrk.com 127.0.0.1 c.viewsecure.net 127.0.0.1 c.vindicosuite.com 127.0.0.1 c.vrvm.com @@ -23574,7 +23587,6 @@ 127.0.0.1 campaigns.f2.com.au 127.0.0.1 campaigns.interclick.com 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 @@ -23694,6 +23706,7 @@ 127.0.0.1 cbvtest.2cnt.net 127.0.0.1 cbx.net 127.0.0.1 cc.amazingcounters.com +127.0.0.1 cc.chango.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 @@ -23714,6 +23727,7 @@ 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-highwinds.unityads.unity3d.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 @@ -24245,6 +24259,7 @@ 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 chokoladsrv.net 127.0.0.1 chope-co.b.appier.net 127.0.0.1 christie-hanlon.us 127.0.0.1 christie-pearlman.us @@ -26626,6 +26641,7 @@ 127.0.0.1 d.refinedads.com 127.0.0.1 d.rmgserving.com 127.0.0.1 d.skimresources.com +127.0.0.1 d.socdm.com 127.0.0.1 d.tds.adlabs.ru 127.0.0.1 d.thanksearch.com 127.0.0.1 d.thelocal.com @@ -27259,6 +27275,7 @@ 127.0.0.1 dimpact.co.il 127.0.0.1 dina-rish.us 127.0.0.1 dinclinx.com +127.0.0.1 dintandnesin.ru 127.0.0.1 direct-revenue.com 127.0.0.1 direct-stats.com 127.0.0.1 direct-xxx-access.com @@ -27477,7 +27494,6 @@ 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 @@ -27586,7 +27602,6 @@ 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 @@ -28022,6 +28037,7 @@ 127.0.0.1 enews.bfast.com 127.0.0.1 enewsletters.ziffdavisinternet.com 127.0.0.1 enfintrouver.com +127.0.0.1 engage.morespeck.com 127.0.0.1 engagebdr.com 127.0.0.1 engine.4dsply.com 127.0.0.1 engine.a.redditmedia.com @@ -28103,6 +28119,7 @@ 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 eqs-redserver.appspot.com 127.0.0.1 equantum.com 127.0.0.1 equitymarketingsolutions.com 127.0.0.1 eqx.smartadserver.com @@ -28258,6 +28275,7 @@ 127.0.0.1 everyfreegift.com 127.0.0.1 everyscape.com 127.0.0.1 everythingbts.com +127.0.0.1 evewronsedent.ru 127.0.0.1 evidencecleanergold.com 127.0.0.1 evisit.exeter.ac.uk 127.0.0.1 evisitcs2.com @@ -28493,6 +28511,7 @@ 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 fenix-translation.com.ua 127.0.0.1 ferdy.org 127.0.0.1 fermakontenta.justclick.ru 127.0.0.1 ferrago.uk.intellitxt.com @@ -29471,7 +29490,6 @@ 127.0.0.1 geo.hyperlinksecure.com 127.0.0.1 geo.ivwbox.de 127.0.0.1 geo.metronews.ca -127.0.0.1 geo.mozilla.org 127.0.0.1 geo.mtvnn.com 127.0.0.1 geo.offermatica.com 127.0.0.1 geo.q5media.net @@ -30117,6 +30135,7 @@ 127.0.0.1 hc.rapidshare.com 127.0.0.1 hc1.humanclick.com 127.0.0.1 hc2.humanclick.com +127.0.0.1 hcharbach.de 127.0.0.1 hchrmain.112.2o7.net 127.0.0.1 hclrm.voluumtrk.com 127.0.0.1 hcu1u.voluumtrk.com @@ -30153,6 +30172,7 @@ 127.0.0.1 help.doubleclick.net 127.0.0.1 help.qualaroo.com 127.0.0.1 hernando.com +127.0.0.1 hetsedryropt.com 127.0.0.1 hexacash.com 127.0.0.1 heywire.com 127.0.0.1 heyzap.com @@ -30350,6 +30370,7 @@ 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 hostpedia.ro 127.0.0.1 hostpost4.xyz 127.0.0.1 hot50.net 127.0.0.1 hotdatinglist.com @@ -30366,6 +30387,7 @@ 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 howrowthettof.com 127.0.0.1 hoz01.voluumtrk.com 127.0.0.1 hpglobal.112.2o7.net 127.0.0.1 hphqglobal.112.2o7.net @@ -30486,13 +30508,16 @@ 127.0.0.1 i.n.jwpltx.com 127.0.0.1 i.nuseek.com 127.0.0.1 i.offerx.co.uk +127.0.0.1 i.playnow.guru 127.0.0.1 i.plug.it 127.0.0.1 i.po.st 127.0.0.1 i.psa-ads.openx.com 127.0.0.1 i.r1-cdn.net 127.0.0.1 i.sahcdn.com 127.0.0.1 i.sbitinbsjs.info +127.0.0.1 i.serves.live 127.0.0.1 i.simpli.fi +127.0.0.1 i.socdm.com 127.0.0.1 i.tapit.com 127.0.0.1 i.tfag.de 127.0.0.1 i.theuseful.com @@ -30659,7 +30684,6 @@ 127.0.0.1 iframe.mediaplazza.com 127.0.0.1 iframe.sponsorpay.com 127.0.0.1 iframe.travel.yahoo.com -127.0.0.1 iframehost.com 127.0.0.1 iframes.hustler.com 127.0.0.1 iframesrccdn1.adexprt.com 127.0.0.1 iframesrccdn2.adexprt.com @@ -30846,7 +30870,6 @@ 127.0.0.1 imaginemedia.net 127.0.0.1 imap.linkshare.com 127.0.0.1 imatmobile.com -127.0.0.1 imb.mobitkr.com 127.0.0.1 imedia.co.il 127.0.0.1 imeds.ero-advertising.com 127.0.0.1 imeseonetwork.go2cloud.org @@ -30945,6 +30968,7 @@ 127.0.0.1 img04.webtrekk.net 127.0.0.1 img05.webtrekk.net 127.0.0.1 img06.webtrekk.net +127.0.0.1 img1.nend.net 127.0.0.1 img1.webring.com 127.0.0.1 img2.freeze.com 127.0.0.1 img2.paipaiimg.com @@ -31314,7 +31338,6 @@ 127.0.0.1 j.adlooxtracking.com 127.0.0.1 j.clickdensity.com 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 @@ -33074,6 +33097,7 @@ 127.0.0.1 mediauk.247realmedia.com 127.0.0.1 mediavisor.doubleclick.com 127.0.0.1 mediavisor.doubleclick.net +127.0.0.1 mediawhirl.net 127.0.0.1 mediciinternet.go2cloud.org 127.0.0.1 medicinenet.us.intellitxt.com 127.0.0.1 medio.com @@ -33159,7 +33183,6 @@ 127.0.0.1 metrics.loomia.com 127.0.0.1 metrics.mcafee.com 127.0.0.1 metrics.mmailhost.com -127.0.0.1 metrics.mozilla.com 127.0.0.1 metrics.nexgen.neustar.biz 127.0.0.1 metrics.payback.de 127.0.0.1 metrics.premiereradio.net @@ -33609,7 +33632,6 @@ 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 @@ -33716,6 +33738,7 @@ 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.fclick-adnow.com 127.0.0.1 n.gemini.yahoo.com 127.0.0.1 n.ladycash.ru 127.0.0.1 n.lcads.ru @@ -33782,6 +33805,7 @@ 127.0.0.1 nancy-mosca.us 127.0.0.1 nandp.go2cloud.org 127.0.0.1 nanigans.com +127.0.0.1 nanoadexchange.com 127.0.0.1 nanostats.nanopress.it 127.0.0.1 naomi-thorn.us 127.0.0.1 napster.searchwho.com @@ -34006,6 +34030,7 @@ 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 nlett3r.com 127.0.0.1 nlhra.voluumtrk.com 127.0.0.1 nm.netmng.com 127.0.0.1 nmaio.voluumtrk.com @@ -34144,6 +34169,7 @@ 127.0.0.1 nuera.go2cloud.org 127.0.0.1 nuggad.net 127.0.0.1 numb.hotshare.biz +127.0.0.1 nurno.com 127.0.0.1 nv.ad.naver.com 127.0.0.1 nv4n8.voluumtrk.com 127.0.0.1 nvk.realsecuredredirect.com @@ -34196,6 +34222,7 @@ 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.ss2.us 127.0.0.1 o.swisscom.ch 127.0.0.1 o.xbox.com 127.0.0.1 o.yieldsquare.com @@ -34443,6 +34470,7 @@ 127.0.0.1 onclickads.net 127.0.0.1 onclickads2.net 127.0.0.1 onclickrev.com +127.0.0.1 onclkds.com 127.0.0.1 one-time-offer.com 127.0.0.1 one.123counters.com 127.0.0.1 oneandonlynetwork.com @@ -34625,6 +34653,7 @@ 127.0.0.1 outils.acf-webmaster.net 127.0.0.1 outils.f5biz.com 127.0.0.1 outpost.real.com +127.0.0.1 output.nend.net 127.0.0.1 outsidethebeltway.us.intellitxt.com 127.0.0.1 ov.yahoo.co.jp 127.0.0.1 overpro.com @@ -34842,6 +34871,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 pardtosinsing.ru 127.0.0.1 parenting.searchwho.com 127.0.0.1 paris.typepad.com 127.0.0.1 paritycube.go2cloud.org @@ -35247,6 +35277,7 @@ 127.0.0.1 player-services.goviral-content.com 127.0.0.1 player.piksel.com 127.0.0.1 playminigolf.com +127.0.0.1 playnow.guru 127.0.0.1 playtomic.com 127.0.0.1 plb27.voluumtrk.com 127.0.0.1 pleasedontslaymy.download @@ -35427,6 +35458,7 @@ 127.0.0.1 preciselylocate.com 127.0.0.1 precisionleads.go2cloud.org 127.0.0.1 predictad.com +127.0.0.1 predictivadvertising.com 127.0.0.1 predictivenetworks.com 127.0.0.1 preferences.truste.com 127.0.0.1 preferredpublishers.go2cloud.org @@ -35907,6 +35939,7 @@ 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.af-share.jp 127.0.0.1 r.aol.com 127.0.0.1 r.apina.biz 127.0.0.1 r.bbci.co.uk @@ -35942,7 +35975,6 @@ 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---sn-vgqsen7z.googlevideo.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 @@ -35950,27 +35982,20 @@ 127.0.0.1 r1.fmpub.net 127.0.0.1 r1.pcwelt.de 127.0.0.1 r1.plugrush.com -127.0.0.1 r1.sn-vgqsen7z.googlevideo.com 127.0.0.1 r1.zedo.com 127.0.0.1 r10.cooleremail.com 127.0.0.1 r11.cooleremail.com -127.0.0.1 r17---sn-vgqsenes.googlevideo.com -127.0.0.1 r2---sn-vgqs7n7k.googlevideo.com 127.0.0.1 r2.cooleremail.com 127.0.0.1 r2.linksynergy.com 127.0.0.1 r2.plugrush.com -127.0.0.1 r20---sn-vgqs7ne7.googlevideo.com 127.0.0.1 r20.rs6.net -127.0.0.1 r20.sn-vgqs7ne7.googlevideo.com 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---sn-vgqs7nez.googlevideo.com 127.0.0.1 r4.cooleremail.com 127.0.0.1 r4.plugrush.com -127.0.0.1 r4.sn-vgqs7nez.googlevideo.com 127.0.0.1 r4zih.voluumtrk.com 127.0.0.1 r5.cooleremail.com 127.0.0.1 r5.plugrush.com @@ -36032,6 +36057,7 @@ 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 ratsparropret.ru 127.0.0.1 ratx9.voluumtrk.com 127.0.0.1 ravenstonedigital.go2cloud.org 127.0.0.1 rawdinner.justclick.ru @@ -36153,6 +36179,7 @@ 127.0.0.1 redirects.timesink.com 127.0.0.1 reditions.net 127.0.0.1 redmas.com +127.0.0.1 redrct.site 127.0.0.1 redshiftleads.steele.net 127.0.0.1 redtube.yoshatia.com 127.0.0.1 reduxmedia.com @@ -36395,12 +36422,12 @@ 127.0.0.1 roia.hutchmedia.com 127.0.0.1 roiadtracker.com 127.0.0.1 roitracker.com -127.0.0.1 rollbar.com 127.0.0.1 romanticfm.2cnt.net 127.0.0.1 romview.atdmt.com.915.9004.302br.net 127.0.0.1 ronaldheft.com 127.0.0.1 roomkey.d1.sc.omtrdc.net 127.0.0.1 rootzwiki.us.intellitxt.com +127.0.0.1 ropretratspar.com 127.0.0.1 rose.ixbt.com 127.0.0.1 rosettastone.tt.omtrdc.net 127.0.0.1 rotabanner.kulichki.net @@ -36529,11 +36556,13 @@ 127.0.0.1 ryerose.net 127.0.0.1 rygpq.voluumtrk.com 127.0.0.1 ryield.jmp9.com +127.0.0.1 ryropthetsed.ru 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 127.0.0.1 s-assets.tp-cdn.com +127.0.0.1 s-cs.send.microad.jp 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 @@ -36748,6 +36777,7 @@ 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-receiver.sematext.com 127.0.0.1 sa.bbc.co.uk 127.0.0.1 sa.bbc.com 127.0.0.1 sa.seotoaster.com @@ -36845,7 +36875,6 @@ 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 127.0.0.1 scripts.affiliatefuture.com @@ -37042,7 +37071,6 @@ 127.0.0.1 selina-wimmer.us 127.0.0.1 sellads.eu 127.0.0.1 sem.shopexplorer.com -127.0.0.1 sematext.com 127.0.0.1 semrush.com 127.0.0.1 send.microad.jp 127.0.0.1 send4fun.com @@ -37513,8 +37541,6 @@ 127.0.0.1 sndkorea.co.kr 127.0.0.1 sndkorea.nowcdn.co.kr 127.0.0.1 snimi-sam.justclick.ru -127.0.0.1 snippets-stats.mozilla.org -127.0.0.1 snippets.mozilla.com 127.0.0.1 snonline.ivwbox.de 127.0.0.1 snowplow-collector.sugarops.com 127.0.0.1 soa.adition.com @@ -38244,6 +38270,7 @@ 127.0.0.1 stats.united-domains.de 127.0.0.1 stats.unity3d.com 127.0.0.1 stats.uswitch.com +127.0.0.1 stats.valaffiliates.com 127.0.0.1 stats.vertriebsassistent.de 127.0.0.1 stats.viddler.com 127.0.0.1 stats.virtuemart.net @@ -38491,6 +38518,7 @@ 127.0.0.1 sync.livejasmin.com 127.0.0.1 sync.mathtag.com 127.0.0.1 sync.smarttds.ru +127.0.0.1 sync.teads.tv 127.0.0.1 sync.zenoviaexchange.com 127.0.0.1 syndicate.payloadz.com 127.0.0.1 syndicate.powerofads.com @@ -38675,7 +38703,6 @@ 127.0.0.1 tag.gamecentral.hiro.tv 127.0.0.1 tag.gstat.orangeportails.net 127.0.0.1 tag.myplay.com -127.0.0.1 tag.navdmp.com 127.0.0.1 tag.reachadv.it 127.0.0.1 tag.researchnow.com 127.0.0.1 tag.shopping-feed.com @@ -38944,6 +38971,7 @@ 127.0.0.1 tfn.das.tamedia.ch 127.0.0.1 tfncdn.thaflynation.com 127.0.0.1 tfp.2ref.co +127.0.0.1 tg.socdm.com 127.0.0.1 tga.acs86.com 127.0.0.1 tga.csbew.com 127.0.0.1 tgbvfr.website @@ -38969,7 +38997,6 @@ 127.0.0.1 thehitsusa.com 127.0.0.1 theinterwebs.space 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 @@ -39040,7 +39067,6 @@ 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 @@ -39203,6 +39229,7 @@ 127.0.0.1 tr.ilius.net 127.0.0.1 tr.interlake.net 127.0.0.1 tr.newsletter.capdecision.fr +127.0.0.1 tr.note001.com 127.0.0.1 tr.sjc.contextweb.com 127.0.0.1 tr.x-tk.net 127.0.0.1 tr1.myroitracking.com @@ -39353,6 +39380,7 @@ 127.0.0.1 track.reinvigorate.net 127.0.0.1 track.right-ads.com 127.0.0.1 track.ringcentral.com +127.0.0.1 track.scanguard.com 127.0.0.1 track.scanmyphones.com 127.0.0.1 track.scorpiointeractive.com 127.0.0.1 track.searchignite.com @@ -39478,6 +39506,7 @@ 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.4v4jfe79erfxfu8z8.com 127.0.0.1 tracking.actionads.ru 127.0.0.1 tracking.adjug.com 127.0.0.1 tracking.admarketplace.net @@ -39582,6 +39611,8 @@ 127.0.0.1 tracking.netzathleten-media.de 127.0.0.1 tracking.nuasti.com 127.0.0.1 tracking.olx-st.com +127.0.0.1 tracking.orixa-media.com +127.0.0.1 tracking.perfecttoolmedia.com 127.0.0.1 tracking.performancerevenues.com 127.0.0.1 tracking.plattformad.com 127.0.0.1 tracking.practicefusion.com @@ -39808,6 +39839,7 @@ 127.0.0.1 trk.kissmetrics.com 127.0.0.1 trk.m.libero.it 127.0.0.1 trk.mailtoward.com +127.0.0.1 trk.meacashtrk.com 127.0.0.1 trk.mobile-setting.com 127.0.0.1 trk.pswec.com 127.0.0.1 trk.rrcpm.com @@ -39817,6 +39849,7 @@ 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 trk1.opanw.com 127.0.0.1 trk2it4.com 127.0.0.1 trk4.com 127.0.0.1 trkag1.com @@ -40270,6 +40303,7 @@ 127.0.0.1 uk.nedstatpro.net 127.0.0.1 uk.sitestat.com 127.0.0.1 uk.static.planet49.com +127.0.0.1 uk.websearchnow.net 127.0.0.1 uk1.siteimprove.com 127.0.0.1 ukaffiliates2.com 127.0.0.1 ukbanners.com @@ -40343,6 +40377,7 @@ 127.0.0.1 urlcash.net 127.0.0.1 urlcheck.hulu.com 127.0.0.1 urlstats.com +127.0.0.1 urlzzz.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 @@ -40361,7 +40396,6 @@ 127.0.0.1 us.js.yimg.com 127.0.0.1 us.marketgid.com 127.0.0.1 us.static.planet49.com -127.0.0.1 us.upgradenow24.com 127.0.0.1 us.wa.ui-portal.com 127.0.0.1 us.webprodcdn.com 127.0.0.1 us.winninganswers.net @@ -44182,6 +44216,7 @@ 127.0.0.1 vt.adition.com 127.0.0.1 vtncmn.com 127.0.0.1 vtot.proxy.aol.com +127.0.0.1 vtrack.larvtrk.com 127.0.0.1 vtracking.in.com 127.0.0.1 vtrtl.de 127.0.0.1 vu.moatads.com @@ -45057,10 +45092,13 @@ 127.0.0.1 www.adclix.com 127.0.0.1 www.adclub.net 127.0.0.1 www.adcron.com +127.0.0.1 www.addiliate.com 127.0.0.1 www.addinto.com 127.0.0.1 www.addme.com 127.0.0.1 www.addthis.com 127.0.0.1 www.adelina-ashman.us +127.0.0.1 www.adexc.net +127.0.0.1 www.adexce.net 127.0.0.1 www.adexcite.com 127.0.0.1 www.adexm.com 127.0.0.1 www.adfest.com @@ -45094,6 +45132,7 @@ 127.0.0.1 www.admarketplace.net 127.0.0.1 www.admarvel.com 127.0.0.1 www.admaya.in +127.0.0.1 www.admaym.com 127.0.0.1 www.admedo.com 127.0.0.1 www.admeld.com 127.0.0.1 www.admestate.ru @@ -45464,6 +45503,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.casalparis.cat 127.0.0.1 www.cash-duck.com 127.0.0.1 www.cash4files.com 127.0.0.1 www.cash4webmaster.de @@ -45499,6 +45539,7 @@ 127.0.0.1 www.checkm8.com 127.0.0.1 www.cherish-mauk.us 127.0.0.1 www.cheryl-edelstein.us +127.0.0.1 www.chokoladsrv.net 127.0.0.1 www.christi-canfield.us 127.0.0.1 www.christie-hanlon.us 127.0.0.1 www.chrystal-hollins.us @@ -45538,6 +45579,7 @@ 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.clicksyndicatetracking.com 127.0.0.1 www.clickterra.net 127.0.0.1 www.clicktracksolutions.com 127.0.0.1 www.clicktraffix.com @@ -45576,6 +45618,7 @@ 127.0.0.1 www.compete.com 127.0.0.1 www.competeinc.com 127.0.0.1 www.comscore.com +127.0.0.1 www.comunedeicittadini.it 127.0.0.1 www.con.vmsn.de 127.0.0.1 www.conductedresearch.com 127.0.0.1 www.connectlinking1.com @@ -45756,7 +45799,6 @@ 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 @@ -46130,7 +46172,6 @@ 127.0.0.1 www.ientry.com 127.0.0.1 www.ientrymail.com 127.0.0.1 www.ientrynetwork.net -127.0.0.1 www.iframehost.com 127.0.0.1 www.ifsmarketing.com 127.0.0.1 www.igain-mail.co.uk 127.0.0.1 www.ignitad.com @@ -46491,6 +46532,7 @@ 127.0.0.1 www.mediamind.com 127.0.0.1 www.mediaplex.com 127.0.0.1 www.mediasheva.com +127.0.0.1 www.mediawhirl.net 127.0.0.1 www.medio.com 127.0.0.1 www.meethotties.mobi 127.0.0.1 www.megacounter.de @@ -46623,6 +46665,7 @@ 127.0.0.1 www.n78adserv.com 127.0.0.1 www.n79adserv.com 127.0.0.1 www.nadia-carlyle.us +127.0.0.1 www.nanoadexchange.com 127.0.0.1 www.naomi-thorn.us 127.0.0.1 www.naturalsearchtoolresults.com 127.0.0.1 www.navteq.com @@ -46647,11 +46690,13 @@ 127.0.0.1 www.nicolette-brier.us 127.0.0.1 www.nicolette-salas.us 127.0.0.1 www.njashka.ru +127.0.0.1 www.nlett3r.com 127.0.0.1 www.nngalleries2.com 127.0.0.1 www.noelle-traxler.us 127.0.0.1 www.noelle-trotter.us 127.0.0.1 www.nonames.tk 127.0.0.1 www.noowho.com +127.0.0.1 www.nurno.com 127.0.0.1 www.nvtrak.com 127.0.0.1 www.nxsrv1.com 127.0.0.1 www.o2onbusiness.de @@ -46675,6 +46720,7 @@ 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.onclkds.com 127.0.0.1 www.oneandonlynetwork.com 127.0.0.1 www.onelouder.com 127.0.0.1 www.onestat.com @@ -46781,6 +46827,7 @@ 127.0.0.1 www.pptrk.com 127.0.0.1 www.prchecker.info 127.0.0.1 www.preciselylocate.com +127.0.0.1 www.predictivadvertising.com 127.0.0.1 www.premiumhdv.com 127.0.0.1 www.primosearch.com 127.0.0.1 www.pringotrack.com @@ -46883,7 +46930,6 @@ 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.rollbar.com 127.0.0.1 www.rotrk.com 127.0.0.1 www.roxyaffiliates.com 127.0.0.1 www.rsmrttracking.com @@ -46960,7 +47006,6 @@ 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.sematext.com 127.0.0.1 www.semrush.com 127.0.0.1 www.send4fun.com 127.0.0.1 www.seniorttub.bid @@ -47260,6 +47305,7 @@ 127.0.0.1 www.upsellit.com 127.0.0.1 www.upv4.moatads.com 127.0.0.1 www.urlstats.com +127.0.0.1 www.urlzzz.com 127.0.0.1 www.urmediazone.com 127.0.0.1 www.urx.io 127.0.0.1 www.usabilla.com @@ -47275,6 +47321,7 @@ 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.venturead.com 127.0.0.1 www.veruta.com 127.0.0.1 www.vibrantmedia.com 127.0.0.1 www.video-loader.com @@ -47672,6 +47719,7 @@ 127.0.0.1 xiazai.duba.net 127.0.0.1 xiti.com 127.0.0.1 xl.topstat.com +127.0.0.1 xl415.com 127.0.0.1 xlite.counterpath.com 127.0.0.1 xml.adtech.fr 127.0.0.1 xml.fusionxml.com @@ -47873,6 +47921,7 @@ 127.0.0.1 zbxproxy01.con.local.vmsn.de 127.0.0.1 zc.zeroredirect1.com 127.0.0.1 zc.zeroredirect2.com +127.0.0.1 zc2.quebec-bin.com 127.0.0.1 zd.zeroredirect1.com 127.0.0.1 zdau-builder.122.2o7.net 127.0.0.1 zdau-zdnetau.122.2o7.net @@ -47974,4 +48023,4 @@ 127.0.0.1 zz.zeroredirect1.com 127.0.0.1 zzz.clickbank.net 127.0.0.1 _thums.ero-advertising.com -# Hosts: 47966 \ No newline at end of file +# Hosts: 48015 \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f955ce5b3..ea99c1edb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,7 +25,7 @@ android:required="false"/> @@ -133,9 +133,9 @@ diff --git a/app/src/main/java/acr/browser/lightning/app/BrowserApp.java b/app/src/main/java/acr/browser/lightning/BrowserApp.java similarity index 96% rename from app/src/main/java/acr/browser/lightning/app/BrowserApp.java rename to app/src/main/java/acr/browser/lightning/BrowserApp.java index 1bbc19a56..7d5fdf9ad 100644 --- a/app/src/main/java/acr/browser/lightning/app/BrowserApp.java +++ b/app/src/main/java/acr/browser/lightning/BrowserApp.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.app; +package acr.browser.lightning; import android.app.Activity; import android.app.Application; @@ -20,11 +20,13 @@ import javax.inject.Inject; -import acr.browser.lightning.BuildConfig; import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.bookmark.BookmarkExporter; -import acr.browser.lightning.database.bookmark.legacy.LegacyBookmarkManager; import acr.browser.lightning.database.bookmark.BookmarkModel; +import acr.browser.lightning.database.bookmark.legacy.LegacyBookmarkManager; +import acr.browser.lightning.di.AppComponent; +import acr.browser.lightning.di.AppModule; +import acr.browser.lightning.di.DaggerAppComponent; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.FileUtils; import acr.browser.lightning.utils.MemoryLeakUtils; diff --git a/app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java b/app/src/main/java/acr/browser/lightning/IncognitoActivity.java similarity index 95% rename from app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java rename to app/src/main/java/acr/browser/lightning/IncognitoActivity.java index 9f085b507..ecdb90837 100644 --- a/app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java +++ b/app/src/main/java/acr/browser/lightning/IncognitoActivity.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.activity; +package acr.browser.lightning; import android.content.Intent; import android.os.Build; @@ -12,7 +12,7 @@ import com.anthonycr.bonsai.CompletableAction; import com.anthonycr.bonsai.CompletableSubscriber; -import acr.browser.lightning.R; +import acr.browser.lightning.browser.activity.BrowserActivity; @SuppressWarnings("deprecation") public class IncognitoActivity extends BrowserActivity { diff --git a/app/src/main/java/acr/browser/lightning/activity/MainActivity.java b/app/src/main/java/acr/browser/lightning/MainActivity.java similarity index 72% rename from app/src/main/java/acr/browser/lightning/activity/MainActivity.java rename to app/src/main/java/acr/browser/lightning/MainActivity.java index 058085937..298c362ee 100644 --- a/app/src/main/java/acr/browser/lightning/activity/MainActivity.java +++ b/app/src/main/java/acr/browser/lightning/MainActivity.java @@ -1,9 +1,10 @@ -package acr.browser.lightning.activity; +package acr.browser.lightning; import android.content.Intent; import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.view.KeyEvent; import android.view.Menu; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; @@ -12,7 +13,7 @@ import com.anthonycr.bonsai.CompletableAction; import com.anthonycr.bonsai.CompletableSubscriber; -import acr.browser.lightning.R; +import acr.browser.lightning.browser.activity.BrowserActivity; @SuppressWarnings("deprecation") public class MainActivity extends BrowserActivity { @@ -76,5 +77,22 @@ public void run() { }); } + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN && event.isCtrlPressed()) { + switch (event.getKeyCode()) { + case KeyEvent.KEYCODE_P: + // Open a new private window + if(event.isShiftPressed()) { + startActivity(new Intent(this, IncognitoActivity.class)); + overridePendingTransition(R.anim.slide_up_in, R.anim.fade_out_scale); + return true; + } + break; + } + } + return super.dispatchKeyEvent(event); + } + } diff --git a/app/src/main/java/acr/browser/lightning/activity/AppCompatPreferenceActivity.java b/app/src/main/java/acr/browser/lightning/activity/AppCompatPreferenceActivity.java index 14f70b128..9110e6eab 100644 --- a/app/src/main/java/acr/browser/lightning/activity/AppCompatPreferenceActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/AppCompatPreferenceActivity.java @@ -40,11 +40,11 @@ protected void onPostCreate(Bundle savedInstanceState) { getDelegate().onPostCreate(savedInstanceState); } - ActionBar getSupportActionBar() { + protected final ActionBar getSupportActionBar() { return getDelegate().getSupportActionBar(); } - void setSupportActionBar(@Nullable Toolbar toolbar) { + protected final void setSupportActionBar(@Nullable Toolbar toolbar) { getDelegate().setSupportActionBar(toolbar); } diff --git a/app/src/main/java/acr/browser/lightning/utils/AdBlock.java b/app/src/main/java/acr/browser/lightning/adblock/AdBlock.java similarity index 72% rename from app/src/main/java/acr/browser/lightning/utils/AdBlock.java rename to app/src/main/java/acr/browser/lightning/adblock/AdBlock.java index 0fd6fc07a..698b23150 100644 --- a/app/src/main/java/acr/browser/lightning/utils/AdBlock.java +++ b/app/src/main/java/acr/browser/lightning/adblock/AdBlock.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.utils; +package acr.browser.lightning.adblock; import android.app.Application; import android.content.res.AssetManager; @@ -16,7 +16,9 @@ import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.inject.Inject; @@ -24,6 +26,8 @@ import acr.browser.lightning.BuildConfig; import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.utils.StringBuilderUtils; +import acr.browser.lightning.utils.Utils; @Singleton public class AdBlock { @@ -132,42 +136,16 @@ public void onSubscribe(@NonNull CompletableSubscriber subscriber) { String line; long time = System.currentTimeMillis(); + final List domains = new ArrayList<>(1); + while ((line = reader.readLine()) != null) { lineBuilder.append(line); - if (!StringBuilderUtils.isEmpty(lineBuilder) && - !StringBuilderUtils.startsWith(lineBuilder, COMMENT)) { - StringBuilderUtils.replace(lineBuilder, LOCAL_IP_V4, EMPTY); - StringBuilderUtils.replace(lineBuilder, LOCAL_IP_V4_ALT, EMPTY); - StringBuilderUtils.replace(lineBuilder, LOCAL_IP_V6, EMPTY); - StringBuilderUtils.replace(lineBuilder, TAB, EMPTY); - - int comment = lineBuilder.indexOf(COMMENT); - if (comment >= 0) { - lineBuilder.replace(comment, lineBuilder.length(), EMPTY); - } - - StringBuilderUtils.trim(lineBuilder); - - if (!StringBuilderUtils.isEmpty(lineBuilder) && - !StringBuilderUtils.equals(lineBuilder, LOCALHOST)) { - while (StringBuilderUtils.contains(lineBuilder, SPACE)) { - int space = lineBuilder.indexOf(SPACE); - StringBuilder partial = StringBuilderUtils.substring(lineBuilder, 0, space); - StringBuilderUtils.trim(partial); - - String partialLine = partial.toString(); - mBlockedDomainsList.add(partialLine); - StringBuilderUtils.replace(lineBuilder, partialLine, EMPTY); - StringBuilderUtils.trim(lineBuilder); - } - if (lineBuilder.length() > 0) { - mBlockedDomainsList.add(lineBuilder.toString()); - } - } - } + parseString(lineBuilder, domains); lineBuilder.setLength(0); } + + mBlockedDomainsList.addAll(domains); Log.d(TAG, "Loaded ad list in: " + (System.currentTimeMillis() - time) + " ms"); } catch (IOException e) { Log.wtf(TAG, "Reading blocked domains list from file '" @@ -179,4 +157,41 @@ public void onSubscribe(@NonNull CompletableSubscriber subscriber) { }); } + private static void parseString(@NonNull StringBuilder lineBuilder, @NonNull List parsedList) { + if (!StringBuilderUtils.isEmpty(lineBuilder) && + !StringBuilderUtils.startsWith(lineBuilder, COMMENT)) { + StringBuilderUtils.replace(lineBuilder, LOCAL_IP_V4, EMPTY); + StringBuilderUtils.replace(lineBuilder, LOCAL_IP_V4_ALT, EMPTY); + StringBuilderUtils.replace(lineBuilder, LOCAL_IP_V6, EMPTY); + StringBuilderUtils.replace(lineBuilder, TAB, EMPTY); + + int comment = lineBuilder.indexOf(COMMENT); + if (comment >= 0) { + lineBuilder.replace(comment, lineBuilder.length(), EMPTY); + } + + StringBuilderUtils.trim(lineBuilder); + + if (!StringBuilderUtils.isEmpty(lineBuilder) && + !StringBuilderUtils.equals(lineBuilder, LOCALHOST)) { + while (StringBuilderUtils.contains(lineBuilder, SPACE)) { + int space = lineBuilder.indexOf(SPACE); + StringBuilder partial = StringBuilderUtils.substring(lineBuilder, 0, space); + StringBuilderUtils.trim(partial); + + String partialLine = partial.toString(); + + // Add string to list + parsedList.add(partialLine); + StringBuilderUtils.replace(lineBuilder, partialLine, EMPTY); + StringBuilderUtils.trim(lineBuilder); + } + if (lineBuilder.length() > 0) { + // Add string to list. + parsedList.add(lineBuilder.toString()); + } + } + } + } + } diff --git a/app/src/main/java/acr/browser/lightning/browser/BrowserPresenter.java b/app/src/main/java/acr/browser/lightning/browser/BrowserPresenter.java index 5ccfa72d2..a92b6a47f 100644 --- a/app/src/main/java/acr/browser/lightning/browser/BrowserPresenter.java +++ b/app/src/main/java/acr/browser/lightning/browser/BrowserPresenter.java @@ -1,11 +1,13 @@ package acr.browser.lightning.browser; import android.app.Activity; +import android.app.Application; import android.content.DialogInterface; import android.content.Intent; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; +import android.webkit.URLUtil; import com.anthonycr.bonsai.CompletableOnSubscribe; import com.anthonycr.bonsai.Schedulers; @@ -14,9 +16,10 @@ import acr.browser.lightning.BuildConfig; import acr.browser.lightning.R; -import acr.browser.lightning.activity.TabsManager; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; +import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.constant.StartPage; import acr.browser.lightning.controller.UIController; import acr.browser.lightning.preference.PreferenceManager; @@ -33,6 +36,7 @@ public class BrowserPresenter { private static final String TAG = "BrowserPresenter"; @NonNull private final TabsManager mTabsModel; + @Inject Application mApplication; @Inject PreferenceManager mPreferences; @NonNull private final BrowserView mView; @@ -116,7 +120,7 @@ private void onTabChanged(@Nullable LightningView newTab) { mView.updateProgress(newTab.getProgress()); mView.setBackButtonEnabled(newTab.canGoBack()); mView.setForwardButtonEnabled(newTab.canGoForward()); - mView.updateUrl(newTab.getUrl(), true); + mView.updateUrl(newTab.getUrl(), false); mView.setTabView(newTab.getWebView()); int index = mTabsModel.indexOfTab(newTab); if (index >= 0) { @@ -143,6 +147,19 @@ public void closeAllOtherTabs() { } + @NonNull + private String mapHomepageToCurrentUrl() { + String homepage = mPreferences.getHomepage(); + switch (homepage) { + case Constants.SCHEME_HOMEPAGE: + return Constants.FILE + StartPage.getStartPageFile(mApplication); + case Constants.SCHEME_BOOKMARKS: + return Constants.FILE + BookmarkPage.getBookmarkPage(mApplication, null); + default: + return homepage; + } + } + /** * Deletes the tab at the specified position. * @@ -165,8 +182,8 @@ public void deleteTab(int position) { boolean shouldClose = mShouldClose && isShown && tabToDelete.isNewTab(); final LightningView currentTab = mTabsModel.getCurrentTab(); if (mTabsModel.size() == 1 && currentTab != null && - (UrlUtils.isStartPageUrl(currentTab.getUrl()) || - currentTab.getUrl().equals(mPreferences.getHomepage()))) { + URLUtil.isFileUrl(currentTab.getUrl()) && + currentTab.getUrl().equals(mapHomepageToCurrentUrl())) { mView.closeActivity(); return; } else { @@ -232,7 +249,7 @@ public void run() { tab.loadUrl(url); } } else if (url != null) { - if (url.startsWith(Constants.FILE)) { + if (URLUtil.isFileUrl(url)) { mView.showBlockedLocalFileDialog(new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { diff --git a/app/src/main/java/acr/browser/lightning/browser/BrowserView.java b/app/src/main/java/acr/browser/lightning/browser/BrowserView.java index b701bbb9e..e301a0c84 100644 --- a/app/src/main/java/acr/browser/lightning/browser/BrowserView.java +++ b/app/src/main/java/acr/browser/lightning/browser/BrowserView.java @@ -2,6 +2,7 @@ import android.content.DialogInterface; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.view.View; @@ -11,7 +12,7 @@ public interface BrowserView { void removeTabView(); - void updateUrl(String url, boolean shortUrl); + void updateUrl(@Nullable String url, boolean isLoading); void updateProgress(int progress); @@ -21,7 +22,7 @@ public interface BrowserView { void closeActivity(); - void showBlockedLocalFileDialog(DialogInterface.OnClickListener listener); + void showBlockedLocalFileDialog(@NonNull DialogInterface.OnClickListener listener); void showSnackbar(@StringRes int resource); diff --git a/app/src/main/java/acr/browser/lightning/browser/SearchBoxModel.java b/app/src/main/java/acr/browser/lightning/browser/SearchBoxModel.java new file mode 100644 index 000000000..4e3e07339 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/browser/SearchBoxModel.java @@ -0,0 +1,71 @@ +package acr.browser.lightning.browser; + +import android.app.Application; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import javax.inject.Inject; + +import acr.browser.lightning.R; +import acr.browser.lightning.BrowserApp; +import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.utils.UrlUtils; +import acr.browser.lightning.utils.Utils; + +/** + * A UI model for the search box. + */ +public class SearchBoxModel { + + @Inject PreferenceManager mPreferences; + @Inject Application mApplication; + + @NonNull private final String mUntitledTitle; + + @Inject + public SearchBoxModel() { + BrowserApp.getAppComponent().inject(this); + mUntitledTitle = mApplication.getString(R.string.untitled); + } + + /** + * Returns the contents of the search box based on a variety of factors. + *
    + *
  • The user's preference to show either the URL, domain, or page title
  • + *
  • Whether or not the current page is loading
  • + *
  • Whether or not the current page is a Lightning generated page.
  • + *
+ * This method uses the URL, title, and loading information to determine what + * should be displayed by the search box. + * + * @param url the URL of the current page. + * @param title the title of the current page, if known. + * @param isLoading whether the page is currently loading or not. + * @return the string that should be displayed by the search box. + */ + @NonNull + public String getDisplayContent(@NonNull String url, @Nullable String title, boolean isLoading) { + if (UrlUtils.isSpecialUrl(url)) { + return ""; + } else if (isLoading) { + return url; + } else { + switch (mPreferences.getUrlBoxContentChoice()) { + default: + case 0: // Default, show only the domain + String domain = Utils.getDomainName(url); + return domain != null ? domain : url; + case 1: // URL, show the entire URL + return url; + case 2: // Title, show the page's title + if (!TextUtils.isEmpty(title)) { + return title; + } else { + return mUntitledTitle; + } + } + } + } + +} diff --git a/app/src/main/java/acr/browser/lightning/activity/TabsManager.java b/app/src/main/java/acr/browser/lightning/browser/TabsManager.java similarity index 96% rename from app/src/main/java/acr/browser/lightning/activity/TabsManager.java rename to app/src/main/java/acr/browser/lightning/browser/TabsManager.java index 11ce58cc8..a7098a479 100644 --- a/app/src/main/java/acr/browser/lightning/activity/TabsManager.java +++ b/app/src/main/java/acr/browser/lightning/browser/TabsManager.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.activity; +package acr.browser.lightning.browser; import android.app.Activity; import android.app.Application; @@ -12,6 +12,7 @@ import android.support.v7.app.AlertDialog; import android.text.TextUtils; import android.util.Log; +import android.webkit.URLUtil; import android.webkit.WebView; import com.anthonycr.bonsai.Completable; @@ -30,9 +31,8 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.constant.BookmarkPage; -import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.DownloadsPage; import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.constant.StartPage; @@ -174,15 +174,15 @@ public void onItem(@Nullable String item) { }); } else if (UrlUtils.isDownloadsUrl(url)) { new DownloadsPage().getDownloadsPage() - .subscribeOn(Schedulers.io()) - .observeOn(Schedulers.main()) - .subscribe(new SingleOnSubscribe() { - @Override - public void onItem(@Nullable String item) { - Preconditions.checkNonNull(item); - tab.loadUrl(item); - } - }); + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.main()) + .subscribe(new SingleOnSubscribe() { + @Override + public void onItem(@Nullable String item) { + Preconditions.checkNonNull(item); + tab.loadUrl(item); + } + }); } else if (UrlUtils.isStartPageUrl(url)) { new StartPage().getHomepage() .subscribeOn(Schedulers.io()) @@ -214,7 +214,7 @@ public void onItem(@Nullable String item) { @Override public void onComplete() { if (url != null) { - if (url.startsWith(Constants.FILE)) { + if (URLUtil.isFileUrl(url)) { AlertDialog.Builder builder = new AlertDialog.Builder(activity); Dialog dialog = builder.setCancelable(true) .setTitle(R.string.title_warning) diff --git a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java b/app/src/main/java/acr/browser/lightning/browser/activity/BrowserActivity.java similarity index 93% rename from app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java rename to app/src/main/java/acr/browser/lightning/browser/activity/BrowserActivity.java index 56edc7202..81b7a1f33 100644 --- a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java +++ b/app/src/main/java/acr/browser/lightning/browser/activity/BrowserActivity.java @@ -2,7 +2,7 @@ * Copyright 2015 Anthony Restaino */ -package acr.browser.lightning.activity; +package acr.browser.lightning.browser.activity; import android.app.Activity; import android.app.Dialog; @@ -90,12 +90,15 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.reading.activity.ReadingActivity; import acr.browser.lightning.browser.BookmarksView; import acr.browser.lightning.browser.BrowserPresenter; import acr.browser.lightning.browser.BrowserView; +import acr.browser.lightning.IncognitoActivity; +import acr.browser.lightning.browser.SearchBoxModel; +import acr.browser.lightning.browser.TabsManager; import acr.browser.lightning.browser.TabsView; -import acr.browser.lightning.constant.BookmarkPage; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.DownloadsPage; import acr.browser.lightning.constant.HistoryPage; @@ -105,11 +108,14 @@ import acr.browser.lightning.database.history.HistoryModel; import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.dialog.LightningDialogBuilder; -import acr.browser.lightning.fragment.BookmarksFragment; -import acr.browser.lightning.fragment.TabsFragment; +import acr.browser.lightning.browser.fragment.BookmarksFragment; +import acr.browser.lightning.browser.fragment.TabsFragment; import acr.browser.lightning.interpolator.BezierDecelerateInterpolator; import acr.browser.lightning.receiver.NetworkReceiver; +import acr.browser.lightning.search.SearchEngineProvider; import acr.browser.lightning.search.SuggestionsAdapter; +import acr.browser.lightning.search.engine.BaseSearchEngine; +import acr.browser.lightning.settings.activity.SettingsActivity; import acr.browser.lightning.utils.DrawableUtils; import acr.browser.lightning.utils.IntentUtils; import acr.browser.lightning.utils.Preconditions; @@ -185,8 +191,14 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements // The singleton BookmarkManager @Inject BookmarkModel mBookmarkManager; + @Inject HistoryModel mHistoryModel; + @Inject LightningDialogBuilder mBookmarksDialogBuilder; + @Inject SearchBoxModel mSearchBoxModel; + + @Inject SearchEngineProvider mSearchEngineProvider; + private TabsManager mTabsManager; // Image @@ -216,7 +228,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements public abstract void updateHistory(@Nullable final String title, @NonNull final String url); @NonNull - abstract Completable updateCookiePreference(); + protected abstract Completable updateCookiePreference(); @Override protected void onCreate(Bundle savedInstanceState) { @@ -327,8 +339,8 @@ public void onDrawerStateChanged(int newState) { lp.height = LayoutParams.MATCH_PARENT; customView.setLayoutParams(lp); - mArrowImage = (ImageView) customView.findViewById(R.id.arrow); - FrameLayout arrowButton = (FrameLayout) customView.findViewById(R.id.arrow_button); + mArrowImage = customView.findViewById(R.id.arrow); + FrameLayout arrowButton = customView.findViewById(R.id.arrow_button); if (mShowTabsInDrawer) { if (mArrowImage.getWidth() <= 0) { mArrowImage.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); @@ -366,7 +378,7 @@ public void run() { arrowButton.setOnClickListener(this); // create the search EditText in the ToolBar - mSearch = (SearchView) customView.findViewById(R.id.search); + mSearch = customView.findViewById(R.id.search); mSearchBackground = customView.findViewById(R.id.search_container); // initialize search background color @@ -443,11 +455,11 @@ private int getTabsFragmentViewId() { * @return true if the panic trigger sent * the intent, false otherwise. */ - static boolean isPanicTrigger(@Nullable Intent intent) { + protected static boolean isPanicTrigger(@Nullable Intent intent) { return intent != null && INTENT_PANIC_TRIGGER.equals(intent.getAction()); } - void panicClean() { + protected void panicClean() { Log.d(TAG, "Closing browser"); mTabsManager.newTab(this, "", false); mTabsManager.switchToTab(0); @@ -508,7 +520,7 @@ public void onFocusChange(final View v, final boolean hasFocus) { final LightningView currentView = mTabsManager.getCurrentTab(); if (!hasFocus && currentView != null) { setIsLoading(currentView.getProgress() < 100); - updateUrl(currentView.getUrl(), true); + updateUrl(currentView.getUrl(), false); } else if (hasFocus && currentView != null) { // Hack to make sure the text gets selected @@ -652,45 +664,8 @@ private void initializePreferences() { setFullscreen(mPreferences.getHideStatusBarEnabled(), false); - switch (mPreferences.getSearchChoice()) { - case 0: - mSearchText = mPreferences.getSearchUrl(); - if (!mSearchText.startsWith(Constants.HTTP) - && !mSearchText.startsWith(Constants.HTTPS)) { - mSearchText = Constants.GOOGLE_SEARCH; - } - break; - case 1: - mSearchText = Constants.GOOGLE_SEARCH; - break; - case 2: - mSearchText = Constants.ASK_SEARCH; - break; - case 3: - mSearchText = Constants.BING_SEARCH; - break; - case 4: - mSearchText = Constants.YAHOO_SEARCH; - break; - case 5: - mSearchText = Constants.STARTPAGE_SEARCH; - break; - case 6: - mSearchText = Constants.STARTPAGE_MOBILE_SEARCH; - break; - case 7: - mSearchText = Constants.DUCK_SEARCH; - break; - case 8: - mSearchText = Constants.DUCK_LITE_SEARCH; - break; - case 9: - mSearchText = Constants.BAIDU_SEARCH; - break; - case 10: - mSearchText = Constants.YANDEX_SEARCH; - break; - } + BaseSearchEngine currentSearchEngine = mSearchEngineProvider.getCurrentSearchEngine(); + mSearchText = currentSearchEngine.getQueryUrl(); updateCookiePreference().subscribeOn(Schedulers.worker()).subscribe(); mProxyUtils.updateProxySettings(this); @@ -749,52 +724,70 @@ public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { @Override public boolean dispatchKeyEvent(KeyEvent event) { // Keyboard shortcuts - if (event.isCtrlPressed() && event.getAction() == KeyEvent.ACTION_DOWN) { - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_T: - // Open new tab - newTab(null, true); - return true; - case KeyEvent.KEYCODE_W: - // Close current tab - mPresenter.deleteTab(mTabsManager.indexOfCurrentTab()); - return true; - case KeyEvent.KEYCODE_Q: - // Close browser - closeBrowser(); - return true; - case KeyEvent.KEYCODE_R: - // Refresh current tab - LightningView currentTab = mTabsManager.getCurrentTab(); - if (currentTab != null) { - currentTab.reload(); - } - return true; - case KeyEvent.KEYCODE_TAB: - int nextIndex = 0; - if (event.isShiftPressed()) { - // Go back one tab - if (mTabsManager.indexOfCurrentTab() > 0) { - nextIndex = mTabsManager.indexOfCurrentTab() - 1; - } else { - nextIndex = mTabsManager.last(); + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (event.isCtrlPressed()) { + switch (event.getKeyCode()) { + case KeyEvent.KEYCODE_F: + // Search in page + findInPage(); + return true; + case KeyEvent.KEYCODE_T: + // Open new tab + newTab(null, true); + return true; + case KeyEvent.KEYCODE_W: + // Close current tab + mPresenter.deleteTab(mTabsManager.indexOfCurrentTab()); + return true; + case KeyEvent.KEYCODE_Q: + // Close browser + closeBrowser(); + return true; + case KeyEvent.KEYCODE_R: + // Refresh current tab + LightningView currentTab = mTabsManager.getCurrentTab(); + if (currentTab != null) { + currentTab.reload(); } - } else { - // Go forward one tab - if (mTabsManager.indexOfCurrentTab() < mTabsManager.last()) { - nextIndex = mTabsManager.indexOfCurrentTab() + 1; + return true; + case KeyEvent.KEYCODE_TAB: + int nextIndex; + if (event.isShiftPressed()) { + // Go back one tab + if (mTabsManager.indexOfCurrentTab() > 0) { + nextIndex = mTabsManager.indexOfCurrentTab() - 1; + } else { + nextIndex = mTabsManager.last(); + } } else { - nextIndex = 0; + // Go forward one tab + if (mTabsManager.indexOfCurrentTab() < mTabsManager.last()) { + nextIndex = mTabsManager.indexOfCurrentTab() + 1; + } else { + nextIndex = 0; + } } + mPresenter.tabChanged(nextIndex); + return true; + } + } else if (event.getKeyCode() == KeyEvent.KEYCODE_SEARCH) { + // Highlight search field + mSearch.requestFocus(); + mSearch.selectAll(); + return true; + } else if (event.isAltPressed()) { + // Alt + tab number + if (KeyEvent.KEYCODE_0 <= event.getKeyCode() && event.getKeyCode() <= KeyEvent.KEYCODE_9) { + int nextIndex; + if (event.getKeyCode() > mTabsManager.last() + KeyEvent.KEYCODE_1 || event.getKeyCode() == KeyEvent.KEYCODE_0) { + nextIndex = mTabsManager.last(); + } else { + nextIndex = event.getKeyCode() - KeyEvent.KEYCODE_1; } mPresenter.tabChanged(nextIndex); return true; + } } - } else if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_SEARCH) { - // Highlight search field - mSearch.requestFocus(); - mSearch.selectAll(); - return true; } return super.dispatchKeyEvent(event); } @@ -1111,7 +1104,7 @@ public void run() { } @Override - public void showBlockedLocalFileDialog(DialogInterface.OnClickListener listener) { + public void showBlockedLocalFileDialog(@NonNull DialogInterface.OnClickListener listener) { AlertDialog.Builder builder = new AlertDialog.Builder(this); Dialog dialog = builder.setCancelable(true) .setTitle(R.string.title_warning) @@ -1219,7 +1212,7 @@ private static void removeViewFromParent(@Nullable View view) { } } - void handleNewIntent(Intent intent) { + protected void handleNewIntent(Intent intent) { mPresenter.onNewIntent(intent); } @@ -1243,14 +1236,14 @@ private synchronized boolean newTab(String url, boolean show) { return mPresenter.newTab(url, show); } - void performExitCleanUp() { + protected 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); + WebUtils.clearHistory(this, mHistoryModel); Log.d(TAG, "History Cleared"); } if (mPreferences.getClearCookiesExitEnabled() && !isIncognito()) { @@ -1369,7 +1362,7 @@ protected void onPause() { } } - void saveOpenTabs() { + protected void saveOpenTabs() { if (mPreferences.getRestoreLostTabsEnabled()) { mTabsManager.saveState(); } @@ -1533,36 +1526,16 @@ public int getUiColor() { } @Override - public void updateUrl(@Nullable String url, boolean shortUrl) { + public void updateUrl(@Nullable String url, boolean isLoading) { if (url == null || mSearch == null || mSearch.hasFocus()) { return; } final LightningView currentTab = mTabsManager.getCurrentTab(); mBookmarksView.handleUpdatedUrl(url); - if (shortUrl && !UrlUtils.isSpecialUrl(url)) { - switch (mPreferences.getUrlBoxContentChoice()) { - case 0: // Default, show only the domain - url = url.replaceFirst(Constants.HTTP, ""); - url = Utils.getDomainName(url); - mSearch.setText(url); - break; - case 1: // URL, show the entire URL - mSearch.setText(url); - break; - case 2: // Title, show the page's title - if (currentTab != null && !currentTab.getTitle().isEmpty()) { - mSearch.setText(currentTab.getTitle()); - } else { - mSearch.setText(mUntitledTitle); - } - break; - } - } else { - if (UrlUtils.isSpecialUrl(url)) { - url = ""; - } - mSearch.setText(url); - } + + String currentTitle = currentTab != null ? currentTab.getTitle() : null; + + mSearch.setText(mSearchBoxModel.getDisplayContent(url, currentTitle, isLoading)); } @Override @@ -1579,12 +1552,12 @@ public void updateProgress(int n) { mProgressBar.setProgress(n); } - void addItemToHistory(@Nullable final String title, @NonNull final String url) { + protected void addItemToHistory(@Nullable final String title, @NonNull final String url) { if (UrlUtils.isSpecialUrl(url)) { return; } - HistoryModel.visitHistoryItem(url, title) + mHistoryModel.visitHistoryItem(url, title) .subscribeOn(Schedulers.io()) .subscribe(new CompletableOnSubscribe() { @Override @@ -1696,7 +1669,7 @@ private void openBookmarks() { * @param runnable an optional runnable to run after * the drawers are closed. */ - void closeDrawers(@Nullable final Runnable runnable) { + protected final void closeDrawers(@Nullable final Runnable runnable) { if (!mDrawerLayout.isDrawerOpen(mDrawerLeft) && !mDrawerLayout.isDrawerOpen(mDrawerRight)) { if (runnable != null) { runnable.run(); @@ -2176,8 +2149,7 @@ protected void applyTransformation(float interpolatedTime, Transformation t) { @Override public void handleBookmarksChange() { final LightningView currentTab = mTabsManager.getCurrentTab(); - if (currentTab != null && currentTab.getUrl().startsWith(Constants.FILE) - && currentTab.getUrl().endsWith(BookmarkPage.FILENAME)) { + if (currentTab != null && UrlUtils.isBookmarkUrl(currentTab.getUrl())) { currentTab.loadBookmarkpage(); } if (currentTab != null) { @@ -2188,8 +2160,7 @@ public void handleBookmarksChange() { @Override public void handleDownloadDeleted() { final LightningView currentTab = mTabsManager.getCurrentTab(); - if (currentTab != null && currentTab.getUrl().startsWith(Constants.FILE) - && currentTab.getUrl().endsWith(DownloadsPage.FILENAME)) { + if (currentTab != null && UrlUtils.isDownloadsUrl(currentTab.getUrl())) { currentTab.loadDownloadspage(); } if (currentTab != null) { diff --git a/app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java b/app/src/main/java/acr/browser/lightning/browser/activity/ThemableBrowserActivity.java similarity index 93% rename from app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java rename to app/src/main/java/acr/browser/lightning/browser/activity/ThemableBrowserActivity.java index 3a29986da..00848828c 100644 --- a/app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java +++ b/app/src/main/java/acr/browser/lightning/browser/activity/ThemableBrowserActivity.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.activity; +package acr.browser.lightning.browser.activity; import android.content.Intent; import android.content.res.Configuration; @@ -10,13 +10,14 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.ThemeUtils; public abstract class ThemableBrowserActivity extends AppCompatActivity { - @Inject PreferenceManager mPreferences; + // TODO: 6/26/17 get rid fo protected reference + @Inject protected PreferenceManager mPreferences; private int mTheme; private boolean mShowTabsInDrawer; diff --git a/app/src/main/java/acr/browser/lightning/activity/BookmarkUiModel.java b/app/src/main/java/acr/browser/lightning/browser/bookmark/BookmarkUiModel.java similarity index 95% rename from app/src/main/java/acr/browser/lightning/activity/BookmarkUiModel.java rename to app/src/main/java/acr/browser/lightning/browser/bookmark/BookmarkUiModel.java index 457a98417..0d42c0bb2 100644 --- a/app/src/main/java/acr/browser/lightning/activity/BookmarkUiModel.java +++ b/app/src/main/java/acr/browser/lightning/browser/bookmark/BookmarkUiModel.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.activity; +package acr.browser.lightning.browser.bookmark; import android.support.annotation.Nullable; diff --git a/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java b/app/src/main/java/acr/browser/lightning/browser/fragment/BookmarksFragment.java similarity index 97% rename from app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java rename to app/src/main/java/acr/browser/lightning/browser/fragment/BookmarksFragment.java index 62776b26b..ebe0c3491 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java +++ b/app/src/main/java/acr/browser/lightning/browser/fragment/BookmarksFragment.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.fragment; +package acr.browser.lightning.browser.fragment; import android.app.Activity; import android.content.Context; @@ -34,11 +34,11 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.activity.BookmarkUiModel; -import acr.browser.lightning.activity.ReadingActivity; -import acr.browser.lightning.activity.TabsManager; +import acr.browser.lightning.browser.bookmark.BookmarkUiModel; +import acr.browser.lightning.reading.activity.ReadingActivity; +import acr.browser.lightning.browser.TabsManager; import acr.browser.lightning.animation.AnimationUtils; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.browser.BookmarksView; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.controller.UIController; @@ -320,10 +320,10 @@ private void setBookmarkDataSet(@NonNull List items, boolean animat } private void setupNavigationButton(@NonNull View view, @IdRes int buttonId, @IdRes int imageId) { - FrameLayout frameButton = (FrameLayout) view.findViewById(buttonId); + FrameLayout frameButton = view.findViewById(buttonId); frameButton.setOnClickListener(this); frameButton.setOnLongClickListener(this); - ImageView buttonImage = (ImageView) view.findViewById(imageId); + ImageView buttonImage = view.findViewById(imageId); buttonImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); } @@ -537,8 +537,8 @@ public void onBindViewHolder(final BookmarkViewHolder holder, int position) { Subscription oldSubscription = mFaviconFetchSubscriptions.get(url); SubscriptionUtils.safeUnsubscribe(oldSubscription); - final Subscription faviconSubscription = mFaviconModel.faviconForUrl(url, mWebpageBitmap, true) - .subscribeOn(Schedulers.worker()) + final Subscription faviconSubscription = mFaviconModel.faviconForUrl(url, web.getTitle()) + .subscribeOn(Schedulers.io()) .observeOn(Schedulers.main()) .subscribe(new SingleOnSubscribe() { @Override diff --git a/app/src/main/java/acr/browser/lightning/fragment/TabsFragment.java b/app/src/main/java/acr/browser/lightning/browser/fragment/TabsFragment.java similarity index 95% rename from app/src/main/java/acr/browser/lightning/fragment/TabsFragment.java rename to app/src/main/java/acr/browser/lightning/browser/fragment/TabsFragment.java index 62642cfe2..beed6ca72 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/TabsFragment.java +++ b/app/src/main/java/acr/browser/lightning/browser/fragment/TabsFragment.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.fragment; +package acr.browser.lightning.browser.fragment; import android.app.Activity; import android.content.Context; @@ -34,12 +34,12 @@ 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.TabsManager; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.browser.TabsView; import acr.browser.lightning.controller.UIController; -import acr.browser.lightning.fragment.anim.HorizontalItemAnimator; -import acr.browser.lightning.fragment.anim.VerticalItemAnimator; +import acr.browser.lightning.browser.fragment.anim.HorizontalItemAnimator; +import acr.browser.lightning.browser.fragment.anim.VerticalItemAnimator; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.DrawableUtils; import acr.browser.lightning.utils.ThemeUtils; @@ -130,7 +130,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, } 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); + ImageView newTab = view.findViewById(R.id.new_tab_button); newTab.setColorFilter(ThemeUtils.getIconDarkThemeColor(getActivity())); newTab.setOnClickListener(new View.OnClickListener() { @Override @@ -183,7 +183,7 @@ private TabsManager getTabsManager() { 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); + final ImageView buttonImage = root.findViewById(imageId); frameButton.setOnClickListener(this); frameButton.setOnLongClickListener(this); buttonImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); @@ -399,11 +399,11 @@ public class LightningViewHolder extends RecyclerView.ViewHolder implements View 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); + txtTitle = view.findViewById(R.id.textTab); + favicon = view.findViewById(R.id.faviconTab); + exit = view.findViewById(R.id.deleteButton); + layout = view.findViewById(R.id.tab_item_background); + exitButton = view.findViewById(R.id.deleteAction); exit.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); exitButton.setOnClickListener(this); diff --git a/app/src/main/java/acr/browser/lightning/fragment/anim/HorizontalItemAnimator.java b/app/src/main/java/acr/browser/lightning/browser/fragment/anim/HorizontalItemAnimator.java similarity index 99% rename from app/src/main/java/acr/browser/lightning/fragment/anim/HorizontalItemAnimator.java rename to app/src/main/java/acr/browser/lightning/browser/fragment/anim/HorizontalItemAnimator.java index 732cf9652..dce288676 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/anim/HorizontalItemAnimator.java +++ b/app/src/main/java/acr/browser/lightning/browser/fragment/anim/HorizontalItemAnimator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package acr.browser.lightning.fragment.anim; +package acr.browser.lightning.browser.fragment.anim; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; diff --git a/app/src/main/java/acr/browser/lightning/fragment/anim/VerticalItemAnimator.java b/app/src/main/java/acr/browser/lightning/browser/fragment/anim/VerticalItemAnimator.java similarity index 99% rename from app/src/main/java/acr/browser/lightning/fragment/anim/VerticalItemAnimator.java rename to app/src/main/java/acr/browser/lightning/browser/fragment/anim/VerticalItemAnimator.java index 4a2f5eedb..ea18b3382 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/anim/VerticalItemAnimator.java +++ b/app/src/main/java/acr/browser/lightning/browser/fragment/anim/VerticalItemAnimator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package acr.browser.lightning.fragment.anim; +package acr.browser.lightning.browser.fragment.anim; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; 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 e4179329f..0f1707fab 100644 --- a/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java +++ b/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java @@ -6,8 +6,10 @@ import android.app.Activity; import android.app.Application; import android.graphics.Bitmap; +import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.TextUtils; import com.anthonycr.bonsai.Single; import com.anthonycr.bonsai.SingleAction; @@ -24,9 +26,11 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.bookmark.BookmarkModel; +import acr.browser.lightning.favicon.FaviconModel; +import acr.browser.lightning.favicon.FaviconUtils; import acr.browser.lightning.utils.Preconditions; import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.Utils; @@ -74,12 +78,27 @@ public final class BookmarkPage { private static final String END = ""; private static final String FOLDER_ICON = "folder.png"; + private static final String DEFAULT_ICON = "default.png"; - private File mFilesDir; - private File mCacheDir; + @NonNull + public static File getBookmarkPage(@NonNull Application application, @Nullable String folder) { + String prefix = !TextUtils.isEmpty(folder) ? folder + '-' : ""; + return new File(application.getFilesDir(), prefix + FILENAME); + } + + @NonNull + private static File getFaviconFile(@NonNull Application application) { + return new File(application.getCacheDir(), FOLDER_ICON); + } + + @NonNull + private static File getDefaultIconFile(@NonNull Application application) { + return new File(application.getCacheDir(), DEFAULT_ICON); + } @Inject Application mApp; - @Inject BookmarkModel mManager; + @Inject BookmarkModel mBookmarkModel; + @Inject FaviconModel mFaviconModel; @NonNull private final Bitmap mFolderIcon; @NonNull private final String mTitle; @@ -95,12 +114,11 @@ public Single getBookmarkPage() { return Single.create(new SingleAction() { @Override public void onSubscribe(@NonNull SingleSubscriber subscriber) { - mCacheDir = mApp.getCacheDir(); - mFilesDir = mApp.getFilesDir(); - cacheDefaultFolderIcon(); + cacheIcon(mFolderIcon, getFaviconFile(mApp)); + cacheIcon(mFaviconModel.getDefaultBitmapForString(null), getDefaultIconFile(mApp)); buildBookmarkPage(null); - File bookmarkWebPage = new File(mFilesDir, FILENAME); + File bookmarkWebPage = getBookmarkPage(mApp, null); subscriber.onItem(Constants.FILE + bookmarkWebPage); subscriber.onComplete(); @@ -108,13 +126,12 @@ public void onSubscribe(@NonNull SingleSubscriber subscriber) { }); } - private void cacheDefaultFolderIcon() { + private void cacheIcon(@NonNull Bitmap icon, @NonNull File file) { FileOutputStream outputStream = null; - File image = new File(mCacheDir, FOLDER_ICON); try { - outputStream = new FileOutputStream(image); - mFolderIcon.compress(Bitmap.CompressFormat.PNG, 100, outputStream); - mFolderIcon.recycle(); + outputStream = new FileOutputStream(file); + icon.compress(Bitmap.CompressFormat.PNG, 100, outputStream); + icon.recycle(); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { @@ -123,52 +140,84 @@ private void cacheDefaultFolderIcon() { } private void buildBookmarkPage(@Nullable final String folder) { - mManager.getBookmarksFromFolderSorted(folder) + mBookmarkModel.getBookmarksFromFolderSorted(folder) .subscribe(new SingleOnSubscribe>() { @Override - public void onItem(@Nullable List list) { + public void onItem(@Nullable final List list) { Preconditions.checkNonNull(list); - final File bookmarkWebPage; - if (folder == null || folder.isEmpty()) { - bookmarkWebPage = new File(mFilesDir, FILENAME); + if (folder == null) { + mBookmarkModel.getFoldersSorted() + .subscribe(new SingleOnSubscribe>() { + @Override + public void onItem(@Nullable List item) { + Preconditions.checkNonNull(item); + + list.addAll(item); + + buildPageHtml(list, null); + } + }); } else { - bookmarkWebPage = new File(mFilesDir, folder + '-' + FILENAME); - } - final StringBuilder bookmarkBuilder = new StringBuilder(HEADING_1 + mTitle + HEADING_2); - - final String folderIconPath = Constants.FILE + mCacheDir + '/' + FOLDER_ICON; - for (int n = 0, size = list.size(); n < size; n++) { - final HistoryItem item = list.get(n); - bookmarkBuilder.append(PART1); - if (item.isFolder()) { - final File folderPage = new File(mFilesDir, item.getTitle() + '-' + FILENAME); - bookmarkBuilder.append(Constants.FILE).append(folderPage); - bookmarkBuilder.append(PART2); - bookmarkBuilder.append(folderIconPath); - buildBookmarkPage(item.getTitle()); - } else { - bookmarkBuilder.append(item.getUrl()); - bookmarkBuilder.append(PART2).append(PART3); - bookmarkBuilder.append(item.getUrl()); - } - bookmarkBuilder.append(PART4); - bookmarkBuilder.append(item.getTitle()); - bookmarkBuilder.append(PART5); - } - bookmarkBuilder.append(END); - FileWriter bookWriter = null; - try { - //noinspection IOResourceOpenedButNotSafelyClosed - bookWriter = new FileWriter(bookmarkWebPage, false); - bookWriter.write(bookmarkBuilder.toString()); - } catch (IOException e) { - e.printStackTrace(); - } finally { - Utils.close(bookWriter); + buildPageHtml(list, folder); } } }); } + private void buildPageHtml(@NonNull List bookmarksAndFolders, @Nullable String folder) { + final File bookmarkWebPage = getBookmarkPage(mApp, folder); + + final StringBuilder bookmarkBuilder = new StringBuilder(HEADING_1 + mTitle + HEADING_2); + + final String folderIconPath = getFaviconFile(mApp).toString(); + + for (int n = 0, size = bookmarksAndFolders.size(); n < size; n++) { + final HistoryItem item = bookmarksAndFolders.get(n); + bookmarkBuilder.append(PART1); + if (item.isFolder()) { + final File folderPage = getBookmarkPage(mApp, item.getTitle()); + bookmarkBuilder.append(Constants.FILE).append(folderPage); + bookmarkBuilder.append(PART2); + bookmarkBuilder.append(folderIconPath); + buildBookmarkPage(item.getTitle()); + } else { + + Uri bookmarkUri = FaviconUtils.safeUri(item.getUrl()); + + String faviconFileUrl; + + if (bookmarkUri != null) { + File faviconFile = FaviconModel.getFaviconCacheFile(mApp, bookmarkUri); + if (!faviconFile.exists()) { + Bitmap defaultFavicon = mFaviconModel.getDefaultBitmapForString(item.getTitle()); + mFaviconModel.cacheFaviconForUrl(defaultFavicon, item.getUrl()).subscribe(); + } + + faviconFileUrl = Constants.FILE + faviconFile; + } else { + faviconFileUrl = Constants.FILE + getDefaultIconFile(mApp); + } + + + bookmarkBuilder.append(item.getUrl()); + bookmarkBuilder.append(PART2).append(faviconFileUrl); + } + bookmarkBuilder.append(PART4); + bookmarkBuilder.append(item.getTitle()); + bookmarkBuilder.append(PART5); + } + bookmarkBuilder.append(END); + FileWriter bookWriter = null; + try { + //noinspection IOResourceOpenedButNotSafelyClosed + bookWriter = new FileWriter(bookmarkWebPage, false); + bookWriter.write(bookmarkBuilder.toString()); + } catch (IOException e) { + e.printStackTrace(); + } finally { + Utils.close(bookWriter); + } + } + } 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 731d4a5fd..9644374e4 100644 --- a/app/src/main/java/acr/browser/lightning/constant/Constants.java +++ b/app/src/main/java/acr/browser/lightning/constant/Constants.java @@ -30,9 +30,9 @@ private Constants() { public static final String YANDEX_SEARCH = "https://yandex.ru/yandsearch?lr=21411&text="; // Custom local page schemes - public static final String SCHEME_HOMEPAGE = "about:home"; - public static final String SCHEME_BLANK = "about:blank"; - public static final String SCHEME_BOOKMARKS = "about:bookmarks"; + public static final String SCHEME_HOMEPAGE = Constants.ABOUT + "home"; + public static final String SCHEME_BLANK = Constants.ABOUT + "blank"; + public static final String SCHEME_BOOKMARKS = Constants.ABOUT + "bookmarks"; // Miscellaneous JavaScript 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)})();"; diff --git a/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java b/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java index 5e5eb8ad7..4b621bbc6 100644 --- a/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java +++ b/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java @@ -6,6 +6,7 @@ import android.app.Application; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.util.Log; import com.anthonycr.bonsai.Single; import com.anthonycr.bonsai.SingleAction; @@ -20,7 +21,7 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.database.downloads.DownloadItem; import acr.browser.lightning.database.downloads.DownloadsModel; import acr.browser.lightning.preference.PreferenceManager; @@ -29,9 +30,8 @@ public final class DownloadsPage { - /** - * The download page standard suffix - */ + private static final String TAG = "DownloadsPage"; + public static final String FILENAME = "downloads.html"; private static final String HEADING_1 = "\n" + @@ -41,10 +41,12 @@ public final class DownloadsPage { "\n" + ""; - private static final String HEADING_2 = "" + - "" + - "" + - "
"; + private static final String HEADING_2 = "
"; private static final String PART1 = "
findBookmarkForUrl(@NonNull final String url) { return Single.create(new SingleAction() { @Override public void onSubscribe(@NonNull SingleSubscriber subscriber) { - Cursor cursor = lazyDatabase().query(TABLE_BOOKMARK, null, KEY_URL + "=?", new String[]{url}, null, null, null, "1"); + Cursor cursor = queryWithOptionalEndSlash(url); if (cursor.moveToFirst()) { subscriber.onItem(bindCursorToHistoryItem(cursor)); @@ -168,7 +269,7 @@ public Single isBookmark(@NonNull final String url) { return Single.create(new SingleAction() { @Override public void onSubscribe(@NonNull SingleSubscriber subscriber) { - Cursor cursor = lazyDatabase().query(TABLE_BOOKMARK, null, KEY_URL + "=?", new String[]{url}, null, null, null, "1"); + Cursor cursor = queryWithOptionalEndSlash(url); subscriber.onItem(cursor.moveToFirst()); @@ -184,7 +285,7 @@ public Single addBookmarkIfNotExists(@NonNull final HistoryItem item) { return Single.create(new SingleAction() { @Override public void onSubscribe(@NonNull SingleSubscriber subscriber) { - Cursor cursor = lazyDatabase().query(TABLE_BOOKMARK, null, KEY_URL + "=?", new String[]{item.getUrl()}, null, null, null, "1"); + Cursor cursor = queryWithOptionalEndSlash(item.getUrl()); if (cursor.moveToFirst()) { cursor.close(); @@ -229,7 +330,7 @@ public Single deleteBookmark(@NonNull final HistoryItem bookmark) { return Single.create(new SingleAction() { @Override public void onSubscribe(@NonNull SingleSubscriber subscriber) { - int rows = lazyDatabase().delete(TABLE_BOOKMARK, KEY_URL + "=?", new String[]{bookmark.getUrl()}); + int rows = deleteWithOptionalEndSlash(bookmark.getUrl()); subscriber.onItem(rows > 0); subscriber.onComplete(); @@ -290,7 +391,7 @@ public void onSubscribe(@NonNull CompletableSubscriber subscriber) { } ContentValues contentValues = bindBookmarkToContentValues(newBookmark); - lazyDatabase().update(TABLE_BOOKMARK, contentValues, KEY_URL + "=?", new String[]{oldBookmark.getUrl()}); + updateWithOptionalEndSlash(oldBookmark.getUrl(), contentValues); subscriber.onComplete(); } diff --git a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java index 90025ba37..6585f3560 100644 --- a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java +++ b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java @@ -10,15 +10,9 @@ public class DownloadItem implements Comparable { - // private variables - @NonNull - private String mUrl = ""; - - @NonNull - private String mTitle = ""; - - @NonNull - private String mContentSize = ""; + @NonNull private String mUrl = ""; + @NonNull private String mTitle = ""; + @NonNull private String mContentSize = ""; public DownloadItem() {} @@ -66,7 +60,7 @@ public String toString() { @Override public int compareTo(@NonNull DownloadItem another) { - int compare = this.mTitle.compareTo(another.mTitle); + int compare = this.mTitle.compareToIgnoreCase(another.mTitle); if (compare == 0) { return this.mUrl.compareTo(another.mUrl); } @@ -74,23 +68,23 @@ public int compareTo(@NonNull DownloadItem another) { } @Override - public boolean equals(@Nullable Object object) { + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - if (this == object) return true; - if (object == null) return false; - if (!(object instanceof DownloadItem)) return false; + DownloadItem that = (DownloadItem) o; - DownloadItem that = (DownloadItem) object; + return mUrl.equals(that.mUrl) && + mTitle.equals(that.mTitle) && + mContentSize.equals(that.mContentSize); - return this.mTitle.equals(that.mTitle) && this.mUrl.equals(that.mUrl) - && this.mContentSize.equals(that.mContentSize); } @Override public int hashCode() { - int result = mUrl.hashCode(); result = 31 * result + mTitle.hashCode(); + result = 31 * result + mContentSize.hashCode(); return result; } diff --git a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java index a14faff43..895205f34 100644 --- a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java +++ b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java @@ -68,7 +68,7 @@ public DownloadsDatabase(@NonNull Application application) { */ @WorkerThread @NonNull - private SQLiteDatabase lazyDatabase() { + private synchronized SQLiteDatabase lazyDatabase() { if (mDatabase == null || !mDatabase.isOpen()) { mDatabase = getWritableDatabase(); } diff --git a/app/src/main/java/acr/browser/lightning/database/history/HistoryDatabase.java b/app/src/main/java/acr/browser/lightning/database/history/HistoryDatabase.java index e4e04ec3d..8ceab0805 100644 --- a/app/src/main/java/acr/browser/lightning/database/history/HistoryDatabase.java +++ b/app/src/main/java/acr/browser/lightning/database/history/HistoryDatabase.java @@ -13,6 +13,13 @@ import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; +import com.anthonycr.bonsai.Completable; +import com.anthonycr.bonsai.CompletableAction; +import com.anthonycr.bonsai.CompletableSubscriber; +import com.anthonycr.bonsai.Single; +import com.anthonycr.bonsai.SingleAction; +import com.anthonycr.bonsai.SingleSubscriber; + import java.util.ArrayList; import java.util.List; @@ -22,9 +29,15 @@ import acr.browser.lightning.R; import acr.browser.lightning.database.HistoryItem; + +/** + * The disk backed download database. + * See {@link HistoryModel} for method + * documentation. + */ @Singleton @WorkerThread -public class HistoryDatabase extends SQLiteOpenHelper { +public class HistoryDatabase extends SQLiteOpenHelper implements HistoryModel { // All Static variables // Database Version @@ -45,9 +58,8 @@ public class HistoryDatabase extends SQLiteOpenHelper { @Nullable private SQLiteDatabase mDatabase; @Inject - HistoryDatabase(@NonNull Application application) { + public HistoryDatabase(@NonNull Application application) { super(application, DATABASE_NAME, null, DATABASE_VERSION); - mDatabase = HistoryDatabase.this.getWritableDatabase(); } // Creating Tables @@ -80,40 +92,108 @@ private static HistoryItem fromCursor(@NonNull Cursor cursor) { @WorkerThread @NonNull - private SQLiteDatabase lazyDatabase() { + private synchronized SQLiteDatabase lazyDatabase() { if (mDatabase == null || !mDatabase.isOpen()) { mDatabase = this.getWritableDatabase(); } return mDatabase; } - @WorkerThread - synchronized void deleteHistory() { - lazyDatabase().delete(TABLE_HISTORY, null, null); - lazyDatabase().close(); + @NonNull + @Override + public Completable deleteHistory() { + return Completable.create(new CompletableAction() { + @Override + public void onSubscribe(@NonNull CompletableSubscriber subscriber) { + lazyDatabase().delete(TABLE_HISTORY, null, null); + lazyDatabase().close(); + + subscriber.onComplete(); + } + }); } - @WorkerThread - synchronized void deleteHistoryItem(@NonNull String url) { - lazyDatabase().delete(TABLE_HISTORY, KEY_URL + " = ?", new String[]{url}); + @NonNull + @Override + public Completable deleteHistoryItem(@NonNull final String url) { + return Completable.create(new CompletableAction() { + @Override + public void onSubscribe(@NonNull CompletableSubscriber subscriber) { + lazyDatabase().delete(TABLE_HISTORY, KEY_URL + " = ?", new String[]{url}); + + subscriber.onComplete(); + } + }); } - @WorkerThread - synchronized void visitHistoryItem(@NonNull String url, @Nullable String title) { - ContentValues values = new ContentValues(); - values.put(KEY_TITLE, title == null ? "" : title); - values.put(KEY_TIME_VISITED, System.currentTimeMillis()); + @NonNull + @Override + public Completable visitHistoryItem(@NonNull final String url, @Nullable final String title) { + return Completable.create(new CompletableAction() { + @Override + public void onSubscribe(@NonNull CompletableSubscriber subscriber) { + ContentValues values = new ContentValues(); + values.put(KEY_TITLE, title == null ? "" : title); + values.put(KEY_TIME_VISITED, System.currentTimeMillis()); + + Cursor cursor = lazyDatabase().query(false, TABLE_HISTORY, new String[]{KEY_URL}, + KEY_URL + " = ?", new String[]{url}, null, null, null, "1"); + + if (cursor.getCount() > 0) { + lazyDatabase().update(TABLE_HISTORY, values, KEY_URL + " = ?", new String[]{url}); + } else { + addHistoryItem(new HistoryItem(url, title == null ? "" : title)); + } + + cursor.close(); + } + }); + } - Cursor cursor = lazyDatabase().query(false, TABLE_HISTORY, new String[]{KEY_URL}, - KEY_URL + " = ?", new String[]{url}, null, null, null, "1"); + @NonNull + @Override + public Single> findHistoryItemsContaining(@NonNull final String query) { + return Single.create(new SingleAction>() { + @Override + public void onSubscribe(@NonNull SingleSubscriber> subscriber) { + List itemList = new ArrayList<>(5); - if (cursor.getCount() > 0) { - lazyDatabase().update(TABLE_HISTORY, values, KEY_URL + " = ?", new String[]{url}); - } else { - addHistoryItem(new HistoryItem(url, title == null ? "" : title)); - } + String search = '%' + query + '%'; - cursor.close(); + Cursor cursor = lazyDatabase().query(TABLE_HISTORY, null, KEY_TITLE + " LIKE ? OR " + KEY_URL + " LIKE ?", + new String[]{search, search}, null, null, KEY_TIME_VISITED + " DESC", "5"); + + while (cursor.moveToNext()) { + itemList.add(fromCursor(cursor)); + } + + cursor.close(); + + subscriber.onItem(itemList); + subscriber.onComplete(); + } + }); + } + + @NonNull + @Override + public Single> lastHundredVisitedHistoryItems() { + return Single.create(new SingleAction>() { + @Override + public void onSubscribe(@NonNull SingleSubscriber> subscriber) { + List itemList = new ArrayList<>(100); + Cursor cursor = lazyDatabase().query(TABLE_HISTORY, null, null, null, null, null, KEY_TIME_VISITED + " DESC", "100"); + + while (cursor.moveToNext()) { + itemList.add(fromCursor(cursor)); + } + + cursor.close(); + + subscriber.onItem(itemList); + subscriber.onComplete(); + } + }); } @WorkerThread @@ -140,43 +220,6 @@ synchronized String getHistoryItem(@NonNull String url) { return m; } - @WorkerThread - @NonNull - synchronized List findItemsContaining(@Nullable String search) { - List itemList = new ArrayList<>(5); - if (search == null) { - return itemList; - } - - search = '%' + search + '%'; - - Cursor cursor = lazyDatabase().query(TABLE_HISTORY, null, KEY_TITLE + " LIKE ? OR " + KEY_URL + " LIKE ?", - new String[]{search, search}, null, null, KEY_TIME_VISITED + " DESC", "5"); - - while (cursor.moveToNext()) { - itemList.add(fromCursor(cursor)); - } - - cursor.close(); - - return itemList; - } - - @WorkerThread - @NonNull - synchronized List getLastHundredItems() { - List itemList = new ArrayList<>(100); - Cursor cursor = lazyDatabase().query(TABLE_HISTORY, null, null, null, null, null, KEY_TIME_VISITED + " DESC", "100"); - - while (cursor.moveToNext()) { - itemList.add(fromCursor(cursor)); - } - - cursor.close(); - - return itemList; - } - @WorkerThread @NonNull synchronized List getAllHistoryItems() { diff --git a/app/src/main/java/acr/browser/lightning/database/history/HistoryModel.java b/app/src/main/java/acr/browser/lightning/database/history/HistoryModel.java index 185ebcf92..2aeb63141 100644 --- a/app/src/main/java/acr/browser/lightning/database/history/HistoryModel.java +++ b/app/src/main/java/acr/browser/lightning/database/history/HistoryModel.java @@ -4,24 +4,19 @@ import android.support.annotation.Nullable; import com.anthonycr.bonsai.Completable; -import com.anthonycr.bonsai.CompletableAction; -import com.anthonycr.bonsai.CompletableSubscriber; import com.anthonycr.bonsai.Single; -import com.anthonycr.bonsai.SingleAction; -import com.anthonycr.bonsai.SingleSubscriber; import java.util.List; -import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.database.HistoryItem; /** - * A model class providing reactive bindings - * with the underlying history database. + * An interface that should be used to communicate + * with the history database. + *

+ * Created by anthonycr on 6/9/17. */ -public final class HistoryModel { - - private HistoryModel() {} +public interface HistoryModel { /** * An observable that deletes browser history. @@ -29,18 +24,7 @@ private HistoryModel() {} * @return a valid observable. */ @NonNull - public static Completable deleteHistory() { - return Completable.create(new CompletableAction() { - @Override - public void onSubscribe(@NonNull CompletableSubscriber subscriber) { - BrowserApp.getAppComponent() - .historyDatabase() - .deleteHistory(); - - subscriber.onComplete(); - } - }); - } + Completable deleteHistory(); /** * An observable that deletes the history @@ -50,18 +34,7 @@ public void onSubscribe(@NonNull CompletableSubscriber subscriber) { * @return a valid observable. */ @NonNull - public static Completable deleteHistoryItem(@NonNull final String url) { - return Completable.create(new CompletableAction() { - @Override - public void onSubscribe(@NonNull CompletableSubscriber subscriber) { - BrowserApp.getAppComponent() - .historyDatabase() - .deleteHistoryItem(url); - - subscriber.onComplete(); - } - }); - } + Completable deleteHistoryItem(@NonNull final String url); /** * An observable that visits the URL by @@ -74,18 +47,7 @@ public void onSubscribe(@NonNull CompletableSubscriber subscriber) { * @return a valid observable. */ @NonNull - public static Completable visitHistoryItem(@NonNull final String url, @Nullable final String title) { - return Completable.create(new CompletableAction() { - @Override - public void onSubscribe(@NonNull CompletableSubscriber subscriber) { - BrowserApp.getAppComponent() - .historyDatabase() - .visitHistoryItem(url, title); - - subscriber.onComplete(); - } - }); - } + Completable visitHistoryItem(@NonNull final String url, @Nullable final String title); /** * An observable that finds all history items @@ -100,18 +62,7 @@ public void onSubscribe(@NonNull CompletableSubscriber subscriber) { * a list of history items. */ @NonNull - public static Single> findHistoryItemsContaining(@NonNull final String query) { - return Single.create(new SingleAction>() { - @Override - public void onSubscribe(@NonNull SingleSubscriber> subscriber) { - List result = BrowserApp.getAppComponent() - .historyDatabase().findItemsContaining(query); - - subscriber.onItem(result); - subscriber.onComplete(); - } - }); - } + Single> findHistoryItemsContaining(@NonNull final String query); /** * An observable that emits a list of the @@ -121,16 +72,5 @@ public void onSubscribe(@NonNull SingleSubscriber> subscriber) * a list of history items. */ @NonNull - public static Single> lastHundredVisitedHistoryItems() { - return Single.create(new SingleAction>() { - @Override - public void onSubscribe(@NonNull SingleSubscriber> subscriber) { - List result = BrowserApp.getAppComponent() - .historyDatabase().getLastHundredItems(); - - subscriber.onItem(result); - subscriber.onComplete(); - } - }); - } + Single> lastHundredVisitedHistoryItems(); } diff --git a/app/src/main/java/acr/browser/lightning/app/AppComponent.java b/app/src/main/java/acr/browser/lightning/di/AppComponent.java similarity index 60% rename from app/src/main/java/acr/browser/lightning/app/AppComponent.java rename to app/src/main/java/acr/browser/lightning/di/AppComponent.java index 8ca9d5c4f..635b3f9a2 100644 --- a/app/src/main/java/acr/browser/lightning/app/AppComponent.java +++ b/app/src/main/java/acr/browser/lightning/di/AppComponent.java @@ -1,26 +1,30 @@ -package acr.browser.lightning.app; +package acr.browser.lightning.di; import javax.inject.Singleton; -import acr.browser.lightning.activity.BrowserActivity; -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.activity.BrowserActivity; +import acr.browser.lightning.reading.activity.ReadingActivity; +import acr.browser.lightning.browser.TabsManager; +import acr.browser.lightning.browser.activity.ThemableBrowserActivity; +import acr.browser.lightning.settings.activity.ThemableSettingsActivity; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.browser.BrowserPresenter; +import acr.browser.lightning.browser.SearchBoxModel; import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.DownloadsPage; import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.constant.StartPage; -import acr.browser.lightning.database.history.HistoryDatabase; import acr.browser.lightning.dialog.LightningDialogBuilder; +import acr.browser.lightning.download.DownloadHandler; import acr.browser.lightning.download.LightningDownloadListener; -import acr.browser.lightning.fragment.BookmarkSettingsFragment; -import acr.browser.lightning.fragment.BookmarksFragment; -import acr.browser.lightning.fragment.DebugSettingsFragment; -import acr.browser.lightning.fragment.LightningPreferenceFragment; -import acr.browser.lightning.fragment.PrivacySettingsFragment; -import acr.browser.lightning.fragment.TabsFragment; +import acr.browser.lightning.settings.fragment.BookmarkSettingsFragment; +import acr.browser.lightning.browser.fragment.BookmarksFragment; +import acr.browser.lightning.settings.fragment.DebugSettingsFragment; +import acr.browser.lightning.settings.fragment.GeneralSettingsFragment; +import acr.browser.lightning.settings.fragment.LightningPreferenceFragment; +import acr.browser.lightning.settings.fragment.PrivacySettingsFragment; +import acr.browser.lightning.browser.fragment.TabsFragment; +import acr.browser.lightning.search.SearchEngineProvider; import acr.browser.lightning.search.SuggestionsAdapter; import acr.browser.lightning.utils.ProxyUtils; import acr.browser.lightning.view.LightningChromeClient; @@ -80,6 +84,12 @@ public interface AppComponent { void inject(LightningChromeClient chromeClient); - HistoryDatabase historyDatabase(); + void inject(DownloadHandler downloadHandler); + + void inject(SearchBoxModel searchBoxModel); + + void inject(SearchEngineProvider searchEngineProvider); + + void inject(GeneralSettingsFragment generalSettingsFragment); } diff --git a/app/src/main/java/acr/browser/lightning/app/AppModule.java b/app/src/main/java/acr/browser/lightning/di/AppModule.java similarity index 65% rename from app/src/main/java/acr/browser/lightning/app/AppModule.java rename to app/src/main/java/acr/browser/lightning/di/AppModule.java index 400d78374..f0ec05074 100644 --- a/app/src/main/java/acr/browser/lightning/app/AppModule.java +++ b/app/src/main/java/acr/browser/lightning/di/AppModule.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.app; +package acr.browser.lightning.di; import android.app.Application; import android.content.Context; @@ -8,10 +8,14 @@ import javax.inject.Singleton; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.database.bookmark.BookmarkDatabase; import acr.browser.lightning.database.bookmark.BookmarkModel; import acr.browser.lightning.database.downloads.DownloadsDatabase; import acr.browser.lightning.database.downloads.DownloadsModel; +import acr.browser.lightning.database.history.HistoryDatabase; +import acr.browser.lightning.database.history.HistoryModel; +import acr.browser.lightning.download.DownloadHandler; import dagger.Module; import dagger.Provides; @@ -36,17 +40,31 @@ public Context provideContext() { @NonNull @Provides @Singleton - public BookmarkModel provideBookmarkMode() { + public BookmarkModel provideBookmarkModel() { return new BookmarkDatabase(mApp); } @NonNull @Provides @Singleton - public DownloadsModel provideDownloadsMode() { + public DownloadsModel provideDownloadsModel() { return new DownloadsDatabase(mApp); } + @NonNull + @Provides + @Singleton + public HistoryModel providesHistoryModel() { + return new HistoryDatabase(mApp); + } + + @NonNull + @Provides + @Singleton + public DownloadHandler provideDownloadHandler() { + return new DownloadHandler(); + } + @NonNull @Provides @Singleton diff --git a/app/src/main/java/acr/browser/lightning/dialog/BrowserDialog.java b/app/src/main/java/acr/browser/lightning/dialog/BrowserDialog.java index e6e8f2826..c137a8407 100644 --- a/app/src/main/java/acr/browser/lightning/dialog/BrowserDialog.java +++ b/app/src/main/java/acr/browser/lightning/dialog/BrowserDialog.java @@ -82,8 +82,8 @@ public static void show(@NonNull Activity activity, @Nullable String title, @Non View layout = LayoutInflater.from(activity).inflate(R.layout.list_dialog, null); - TextView titleView = (TextView) layout.findViewById(R.id.dialog_title); - ListView listView = (ListView) layout.findViewById(R.id.dialog_list); + TextView titleView = layout.findViewById(R.id.dialog_title); + ListView listView = layout.findViewById(R.id.dialog_list); ArrayAdapter adapter = new ArrayAdapter<>(activity, android.R.layout.simple_list_item_1); @@ -136,7 +136,7 @@ public static void showEditText(@NonNull Activity activity, @StringRes int action, @NonNull final EditorListener listener) { View dialogView = LayoutInflater.from(activity).inflate(R.layout.dialog_edit_text, null); - final EditText editText = (EditText) dialogView.findViewById(R.id.dialog_edit_text); + final EditText editText = dialogView.findViewById(R.id.dialog_edit_text); editText.setHint(hint); if (currentText != null) { diff --git a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java index ee4c0dd30..d925a6f30 100644 --- a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java +++ b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java @@ -8,9 +8,7 @@ import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.text.TextUtils; -import android.util.Log; import android.view.View; -import android.webkit.URLUtil; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.EditText; @@ -24,20 +22,20 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.activity.MainActivity; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.MainActivity; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.controller.UIController; import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.bookmark.BookmarkModel; -import acr.browser.lightning.database.downloads.DownloadItem; import acr.browser.lightning.database.downloads.DownloadsModel; import acr.browser.lightning.database.history.HistoryModel; +import acr.browser.lightning.download.DownloadHandler; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.IntentUtils; import acr.browser.lightning.utils.Preconditions; -import acr.browser.lightning.utils.Utils; +import acr.browser.lightning.utils.UrlUtils; /** * TODO Rename this class it doesn't build dialogs only for bookmarks @@ -55,7 +53,9 @@ public enum NewTab { @Inject BookmarkModel mBookmarkManager; @Inject DownloadsModel mDownloadsModel; + @Inject HistoryModel mHistoryModel; @Inject PreferenceManager mPreferenceManager; + @Inject DownloadHandler mDownloadHandler; @Inject public LightningDialogBuilder() { @@ -73,7 +73,7 @@ public void showLongPressedDialogForBookmarkUrl(@NonNull final Activity activity @NonNull final UIController uiController, @NonNull final String url) { final HistoryItem item; - if (url.startsWith(Constants.FILE) && url.endsWith(BookmarkPage.FILENAME)) { + if (UrlUtils.isBookmarkUrl(url)) { // TODO hacky, make a better bookmark mechanism in the future final Uri uri = Uri.parse(url); final String filename = uri.getLastPathSegment(); @@ -91,6 +91,7 @@ public void showLongPressedDialogForBookmarkUrl(@NonNull final Activity activity .subscribe(new SingleOnSubscribe() { @Override public void onItem(@Nullable HistoryItem historyItem) { + // TODO: 6/14/17 figure out solution to case where slashes get appended to root urls causing the item to be null if (historyItem != null) { showLongPressedDialogForBookmarkUrl(activity, uiController, historyItem); } @@ -191,12 +192,12 @@ private void showEditBookmarkDialog(@NonNull final Activity activity, final AlertDialog.Builder editBookmarkDialog = new AlertDialog.Builder(activity); editBookmarkDialog.setTitle(R.string.title_edit_bookmark); final View dialogLayout = View.inflate(activity, R.layout.dialog_edit_bookmark, null); - final EditText getTitle = (EditText) dialogLayout.findViewById(R.id.bookmark_title); + final EditText getTitle = dialogLayout.findViewById(R.id.bookmark_title); getTitle.setText(item.getTitle()); - final EditText getUrl = (EditText) dialogLayout.findViewById(R.id.bookmark_url); + final EditText getUrl = dialogLayout.findViewById(R.id.bookmark_url); getUrl.setText(item.getUrl()); final AutoCompleteTextView getFolder = - (AutoCompleteTextView) dialogLayout.findViewById(R.id.bookmark_folder); + dialogLayout.findViewById(R.id.bookmark_folder); getFolder.setHint(R.string.folder); getFolder.setText(item.getFolder()); @@ -332,7 +333,7 @@ public void onClick() { new BrowserDialog.Item(R.string.dialog_remove_from_history) { @Override public void onClick() { - HistoryModel.deleteHistoryItem(url) + mHistoryModel.deleteHistoryItem(url) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.main()) .subscribe(new CompletableOnSubscribe() { @@ -384,16 +385,7 @@ public void onClick() { new BrowserDialog.Item(R.string.dialog_download_image) { @Override public void onClick() { - Utils.downloadFile(activity, mPreferenceManager, url, userAgent, "attachment"); - - mDownloadsModel.addDownloadIfNotExists(new DownloadItem(url, URLUtil.guessFileName(url, null, null), "")) - .subscribe(new SingleOnSubscribe() { - @Override - public void onItem(@Nullable Boolean item) { - if (item != null && !item) - Log.i(TAG, "error saving download to database"); - } - }); + mDownloadHandler.onDownloadStart(activity, mPreferenceManager, url, userAgent, "attachment", null, ""); } }); } 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 6f02b92dc..dd315ae43 100644 --- a/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java +++ b/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java @@ -23,16 +23,26 @@ import android.webkit.MimeTypeMap; import android.webkit.URLUtil; +import com.anthonycr.bonsai.SingleOnSubscribe; + import java.io.File; import java.io.IOException; +import javax.inject.Inject; + import acr.browser.lightning.BuildConfig; import acr.browser.lightning.R; -import acr.browser.lightning.activity.MainActivity; +import acr.browser.lightning.MainActivity; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.controller.UIController; +import acr.browser.lightning.database.downloads.DownloadItem; +import acr.browser.lightning.database.downloads.DownloadsModel; import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.utils.FileUtils; import acr.browser.lightning.utils.Utils; +import acr.browser.lightning.view.LightningView; /** * Handle download requests @@ -43,9 +53,11 @@ public class DownloadHandler { private static final String COOKIE_REQUEST_HEADER = "Cookie"; - public static final String DEFAULT_DOWNLOAD_PATH = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) - .getPath(); + @Inject DownloadsModel downloadsModel; + + public DownloadHandler() { + BrowserApp.getAppComponent().inject(this); + } /** * Notify the host application a download should be done, or that the data @@ -56,9 +68,10 @@ public class DownloadHandler { * @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 + * @param contentSize The size of the content */ - public static void onDownloadStart(@NonNull Activity context, @NonNull PreferenceManager manager, String url, String userAgent, - @Nullable String contentDisposition, String mimetype) { + public void onDownloadStart(@NonNull Activity context, @NonNull PreferenceManager manager, String url, String userAgent, + @Nullable String contentDisposition, String mimetype, String contentSize) { Log.d(TAG, "DOWNLOAD: Trying to download from URL: " + url); Log.d(TAG, "DOWNLOAD: Content disposition: " + contentDisposition); @@ -98,7 +111,7 @@ public static void onDownloadStart(@NonNull Activity context, @NonNull Preferenc } } } - onDownloadStartNoStream(context, manager, url, userAgent, contentDisposition, mimetype); + onDownloadStartNoStream(context, manager, url, userAgent, contentDisposition, mimetype, contentSize); } // This is to work around the fact that java.net.URI throws Exceptions @@ -141,11 +154,12 @@ private static String encodePath(@NonNull String path) { * @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 + * @param contentSize The size of the content */ /* package */ - private static void onDownloadStartNoStream(@NonNull final Activity context, @NonNull PreferenceManager preferences, - String url, String userAgent, - String contentDisposition, @Nullable String mimetype) { + private void onDownloadStartNoStream(@NonNull final Activity context, @NonNull PreferenceManager preferences, + String url, String userAgent, + String contentDisposition, @Nullable String mimetype, String contentSize) { final String filename = URLUtil.guessFileName(url, contentDisposition, mimetype); // Check to see if we have an SDCard @@ -198,16 +212,8 @@ private static void onDownloadStartNoStream(@NonNull final Activity context, @No // or, should it be set to one of several Environment.DIRECTORY* dirs // depending on mimetype? String location = preferences.getDownloadDirectory(); - Uri downloadFolder; - location = addNecessarySlashes(location); - downloadFolder = Uri.parse(location); - - File dir = new File(downloadFolder.getPath()); - if (!dir.isDirectory() && !dir.mkdirs()) { - // Cannot make the directory - Utils.showSnackbar(context, R.string.problem_location_download); - return; - } + location = FileUtils.addNecessarySlashes(location); + Uri downloadFolder = Uri.parse(location); if (!isWriteAccessAvailable(downloadFolder)) { Utils.showSnackbar(context, R.string.problem_location_download); @@ -254,80 +260,30 @@ private static void onDownloadStartNoStream(@NonNull final Activity context, @No } Utils.showSnackbar(context, context.getString(R.string.download_pending) + ' ' + filename); } - } - private static final String sFileName = "test"; - private static final String sFileExtension = ".txt"; + // save download in database + UIController browserActivity = (UIController) context; + LightningView view = browserActivity.getTabModel().getCurrentTab(); - /** - * Determine whether there is write access in the given directory. Returns false if a - * file cannot be created in the directory or if the directory does not exist. - * - * @param directory the directory to check for write access - * @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(@Nullable String directory) { - if (directory == null || directory.isEmpty()) { - return false; - } - String dir = addNecessarySlashes(directory); - dir = getFirstRealParentDirectory(dir); - File file = new File(dir + sFileName + sFileExtension); - for (int n = 0; n < 100; n++) { - if (!file.exists()) { - try { - if (file.createNewFile()) { - //noinspection ResultOfMethodCallIgnored - file.delete(); + if (view != null && !view.isIncognito()) { + downloadsModel.addDownloadIfNotExists(new DownloadItem(url, filename, contentSize)) + .subscribe(new SingleOnSubscribe() { + @Override + public void onItem(@Nullable Boolean item) { + if (item != null && !item) + Log.i(TAG, "error saving download to database"); } - return true; - } catch (IOException ignored) { - return false; - } - } else { - file = new File(dir + sFileName + '-' + n + sFileExtension); - } - } - return file.canWrite(); - } - - /** - * Returns the first parent directory of a directory that exists. This is useful - * for subdirectories that do not exist but their parents do. - * - * @param directory the directory to find the first existent parent - * @return the first existent parent - */ - @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 directory; - } + }); } } private static boolean isWriteAccessAvailable(@NonNull Uri fileUri) { File file = new File(fileUri.getPath()); + + if (!file.isDirectory() && !file.mkdirs()) { + return false; + } + try { if (file.createNewFile()) { //noinspection ResultOfMethodCallIgnored @@ -338,19 +294,4 @@ private static boolean isWriteAccessAvailable(@NonNull Uri fileUri) { return false; } } - - @NonNull - public static String addNecessarySlashes(@Nullable String originalPath) { - if (originalPath == null || originalPath.length() == 0) { - return "/"; - } - if (originalPath.charAt(originalPath.length() - 1) != '/') { - originalPath = originalPath + '/'; - } - if (originalPath.charAt(0) != '/') { - originalPath = '/' + originalPath; - } - return originalPath; - } - -} +} \ No newline at end of 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 a38bb93cd..227da4467 100644 --- a/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java +++ b/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java @@ -7,7 +7,6 @@ import android.app.Activity; import android.app.Dialog; import android.content.DialogInterface; -import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.text.format.Formatter; import android.util.Log; @@ -15,13 +14,11 @@ import android.webkit.URLUtil; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; -import acr.browser.lightning.database.downloads.DownloadItem; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.database.downloads.DownloadsModel; import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.preference.PreferenceManager; -import com.anthonycr.bonsai.SingleOnSubscribe; import com.anthonycr.grant.PermissionsManager; import com.anthonycr.grant.PermissionsResultAction; @@ -34,7 +31,7 @@ public class LightningDownloadListener implements DownloadListener { private final Activity mActivity; @Inject PreferenceManager mPreferenceManager; - + @Inject DownloadHandler mDownloadHandler; @Inject DownloadsModel downloadsModel; public LightningDownloadListener(Activity context) { @@ -64,17 +61,7 @@ public void onGranted() { public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: - DownloadHandler.onDownloadStart(mActivity, mPreferenceManager, url, userAgent, - contentDisposition, mimetype); - - downloadsModel.addDownloadIfNotExists(new DownloadItem(url, fileName, downloadSize)) - .subscribe(new SingleOnSubscribe() { - @Override - public void onItem(@Nullable Boolean item) { - if (item != null && !item) - Log.i(TAG, "error saving download to database"); - } - }); + mDownloadHandler.onDownloadStart(mActivity, mPreferenceManager, url, userAgent, contentDisposition, mimetype, downloadSize); break; case DialogInterface.BUTTON_NEGATIVE: break; diff --git a/app/src/main/java/acr/browser/lightning/favicon/FaviconModel.java b/app/src/main/java/acr/browser/lightning/favicon/FaviconModel.java index e9ecf6c80..ecca9256e 100644 --- a/app/src/main/java/acr/browser/lightning/favicon/FaviconModel.java +++ b/app/src/main/java/acr/browser/lightning/favicon/FaviconModel.java @@ -2,9 +2,13 @@ import android.app.Application; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.net.Uri; +import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.WorkerThread; +import android.text.TextUtils; import android.util.Log; import android.util.LruCache; @@ -22,6 +26,8 @@ import javax.inject.Inject; import javax.inject.Singleton; +import acr.browser.lightning.R; +import acr.browser.lightning.utils.DrawableUtils; import acr.browser.lightning.utils.FileUtils; import acr.browser.lightning.utils.Preconditions; import acr.browser.lightning.utils.Utils; @@ -35,7 +41,7 @@ public class FaviconModel { private static final String TAG = "FaviconModel"; - @NonNull private final ImageFetcher mImageFetcher; + @NonNull private final BitmapFactory.Options mLoaderOptions = new BitmapFactory.Options(); @NonNull private final Application mApplication; @NonNull private final LruCache mFaviconCache = new LruCache((int) FileUtils.megabytesToBytes(1)) { @Override @@ -44,10 +50,12 @@ protected int sizeOf(String key, Bitmap value) { } }; + private final int mBookmarkIconSize; + @Inject FaviconModel(@NonNull Application application) { - mImageFetcher = new ImageFetcher(); mApplication = application; + mBookmarkIconSize = mApplication.getResources().getDimensionPixelSize(R.dimen.bookmark_item_icon_size); } /** @@ -68,6 +76,18 @@ private Bitmap getFaviconFromMemCache(@NonNull String url) { } } + @NonNull + public Bitmap getDefaultBitmapForString(@Nullable String title) { + Character firstTitleCharacter = !TextUtils.isEmpty(title) ? title.charAt(0) : '?'; + + @ColorInt int defaultFaviconColor = DrawableUtils.characterToColorHash(firstTitleCharacter, mApplication); + + return DrawableUtils.getRoundedLetterImage(firstTitleCharacter, + mBookmarkIconSize, + mBookmarkIconSize, + defaultFaviconColor); + } + /** * Adds a bitmap to the memory cache * for the given URL. @@ -93,8 +113,9 @@ private void addFaviconToMemCache(@NonNull String url, @NonNull Bitmap bitmap) { * @param uri the URI to use as a unique identifier. * @return a valid cache file. */ + @WorkerThread @NonNull - private static File createFaviconCacheFile(@NonNull Application app, @NonNull Uri uri) { + public static File getFaviconCacheFile(@NonNull Application app, @NonNull Uri uri) { FaviconUtils.assertUriSafe(uri); String hash = String.valueOf(uri.getHost().hashCode()); @@ -106,19 +127,13 @@ private static File createFaviconCacheFile(@NonNull Application app, @NonNull Ur * Retrieves the favicon for a URL, * may be from network or cache. * - * @param url the URL that we should retrieve the - * favicon for. - * @param defaultFavicon the default favicon if no - * favicon is found. - * @param allowGoogleService true to allow grabbing favicons - * from Google, false otherwise. - * @return an observable that emits a bitmap if one is found, - * or the default if none was found. + * @param url The URL that we should retrieve the + * favicon for. + * @param title The title for the web page. */ @NonNull public Single faviconForUrl(@NonNull final String url, - @NonNull final Bitmap defaultFavicon, - final boolean allowGoogleService) { + @NonNull final String title) { return Single.create(new SingleAction() { @Override public void onSubscribe(@NonNull SingleSubscriber subscriber) { @@ -126,7 +141,7 @@ public void onSubscribe(@NonNull SingleSubscriber subscriber) { if (uri == null) { - Bitmap newFavicon = Utils.padFavicon(defaultFavicon); + Bitmap newFavicon = Utils.padFavicon(getDefaultBitmapForString(title)); subscriber.onItem(newFavicon); subscriber.onComplete(); @@ -134,19 +149,19 @@ public void onSubscribe(@NonNull SingleSubscriber subscriber) { return; } - File faviconCacheFile = createFaviconCacheFile(mApplication, uri); - + File faviconCacheFile = getFaviconCacheFile(mApplication, uri); Bitmap favicon = getFaviconFromMemCache(url); if (faviconCacheFile.exists() && favicon == null) { - favicon = mImageFetcher.retrieveFaviconFromCache(faviconCacheFile); + favicon = BitmapFactory.decodeFile(faviconCacheFile.getPath(), mLoaderOptions); + + if (favicon != null) { + addFaviconToMemCache(url, favicon); + } } - if (favicon == null) { - // TODO: 6/5/17 figure out if optimistic favicon retrieval should be added back or dropped - // favicon = mImageFetcher.retrieveBitmapFromDomain(uri); - } else { + if (favicon != null) { Bitmap newFavicon = Utils.padFavicon(favicon); subscriber.onItem(newFavicon); @@ -155,18 +170,7 @@ public void onSubscribe(@NonNull SingleSubscriber subscriber) { return; } - // if (favicon == null && allowGoogleService) { - // favicon = mImageFetcher.retrieveBitmapFromGoogle(uri); - // } - - // if (favicon != null) { - // addFaviconToMemCache(url, favicon); - // cacheFaviconForUrl(favicon, url).subscribe(); - // } - - // if (favicon == null) { - favicon = defaultFavicon; - // } + favicon = getDefaultBitmapForString(title); Bitmap newFavicon = Utils.padFavicon(favicon); @@ -201,7 +205,7 @@ public void onSubscribe(@NonNull CompletableSubscriber subscriber) { FileOutputStream fos = null; try { - File image = createFaviconCacheFile(mApplication, uri); + File image = getFaviconCacheFile(mApplication, uri); fos = new FileOutputStream(image); favicon.compress(Bitmap.CompressFormat.PNG, 100, fos); fos.flush(); diff --git a/app/src/main/java/acr/browser/lightning/favicon/FaviconUtils.java b/app/src/main/java/acr/browser/lightning/favicon/FaviconUtils.java index a32420b0f..19cd3241c 100644 --- a/app/src/main/java/acr/browser/lightning/favicon/FaviconUtils.java +++ b/app/src/main/java/acr/browser/lightning/favicon/FaviconUtils.java @@ -8,9 +8,9 @@ /** * Simple utils for favicon fetching. */ -class FaviconUtils { +public class FaviconUtils { @Nullable - static Uri safeUri(@NonNull String url) { + public static Uri safeUri(@NonNull String url) { if (TextUtils.isEmpty(url)) { return null; } diff --git a/app/src/main/java/acr/browser/lightning/favicon/ImageFetcher.java b/app/src/main/java/acr/browser/lightning/favicon/ImageFetcher.java deleted file mode 100644 index 337e40844..000000000 --- a/app/src/main/java/acr/browser/lightning/favicon/ImageFetcher.java +++ /dev/null @@ -1,110 +0,0 @@ -package acr.browser.lightning.favicon; - -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -import acr.browser.lightning.utils.Utils; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.ResponseBody; - -/** - * An image fetcher that creates image - * loading requests on demand. - */ -class ImageFetcher { - - private static final String TAG = "ImageFetcher"; - - @NonNull private final BitmapFactory.Options mLoaderOptions = new BitmapFactory.Options(); - @NonNull private final OkHttpClient mHttpClient = new OkHttpClient(); - - ImageFetcher() {} - - @Nullable - Bitmap retrieveFaviconFromCache(@NonNull File cacheFile) { - return BitmapFactory.decodeFile(cacheFile.getPath(), mLoaderOptions); - } - - @Nullable - Bitmap retrieveBitmapFromDomain(@NonNull Uri uri) { - FaviconUtils.assertUriSafe(uri); - - String faviconUrlGuess = uri.getScheme() + "://" + uri.getHost() + "/favicon.ico"; - - return retrieveBitmapFromUrl(faviconUrlGuess); - } - - @Nullable - Bitmap retrieveBitmapFromGoogle(@NonNull Uri uri) { - FaviconUtils.assertUriSafe(uri); - - String googleFaviconUrl = "https://www.google.com/s2/favicons?domain_url=" + uri.getHost(); - - return retrieveBitmapFromUrl(googleFaviconUrl); - } - - @Nullable - private Bitmap retrieveBitmapFromUrl(@NonNull String url) { - Bitmap icon = null; - - InputStream boundsStream = null; - InputStream iconStream = null; - - try { - mLoaderOptions.inSampleSize = 1; - mLoaderOptions.inJustDecodeBounds = true; - - Request imageRequest = new Request.Builder().url(url).build(); - - Response boundsResponse = mHttpClient.newCall(imageRequest).execute(); - ResponseBody boundsBody = boundsResponse.body(); - - if (boundsBody == null) { - return null; - } - - boundsStream = boundsBody.byteStream(); - - BitmapFactory.decodeStream(boundsStream, null, mLoaderOptions); - - boundsBody.close(); - - int size = Utils.dpToPx(24); - - mLoaderOptions.inSampleSize = Utils.calculateInSampleSize(mLoaderOptions, size, size); - mLoaderOptions.inJustDecodeBounds = false; - - Response imageResponse = mHttpClient.newCall(imageRequest).execute(); - - ResponseBody imageBody = imageResponse.body(); - - if (imageBody == null) { - return null; - } - - iconStream = imageBody.byteStream(); - - icon = BitmapFactory.decodeStream(iconStream, null, mLoaderOptions); - - imageBody.close(); - } catch (IOException exception) { - Log.d(TAG, "Unable to download icon: " + url); - } finally { - Utils.close(boundsStream); - Utils.close(iconStream); - } - - return icon; - } - -} diff --git a/app/src/main/java/acr/browser/lightning/interpolator/BezierDecelerateInterpolator.java b/app/src/main/java/acr/browser/lightning/interpolator/BezierDecelerateInterpolator.java index 5ee0a3ecb..9756f8bd6 100644 --- a/app/src/main/java/acr/browser/lightning/interpolator/BezierDecelerateInterpolator.java +++ b/app/src/main/java/acr/browser/lightning/interpolator/BezierDecelerateInterpolator.java @@ -1,10 +1,8 @@ package acr.browser.lightning.interpolator; -import android.os.Build; import android.support.annotation.NonNull; -import android.view.animation.DecelerateInterpolator; +import android.support.v4.view.animation.PathInterpolatorCompat; import android.view.animation.Interpolator; -import android.view.animation.PathInterpolator; /** * Bezier decelerate curve similar to iOS. @@ -17,11 +15,7 @@ public class BezierDecelerateInterpolator implements Interpolator { private static final Interpolator PATH_INTERPOLATOR; static { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - PATH_INTERPOLATOR = new PathInterpolator(0.25f, 0.1f, 0.25f, 1); - } else { - PATH_INTERPOLATOR = new DecelerateInterpolator(); - } + PATH_INTERPOLATOR = PathInterpolatorCompat.create(0.25f, 0.1f, 0.25f, 1); } @Override 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 8be0ac56b..d052eac30 100644 --- a/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java +++ b/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java @@ -9,7 +9,7 @@ import javax.inject.Singleton; import acr.browser.lightning.constant.Constants; -import acr.browser.lightning.download.DownloadHandler; +import acr.browser.lightning.utils.FileUtils; @Singleton public class PreferenceManager { @@ -150,7 +150,7 @@ public boolean getCookiesEnabled() { @NonNull public String getDownloadDirectory() { - return mPrefs.getString(Name.DOWNLOAD_DIRECTORY, DownloadHandler.DEFAULT_DOWNLOAD_PATH); + return mPrefs.getString(Name.DOWNLOAD_DIRECTORY, FileUtils.DEFAULT_DOWNLOAD_PATH); } public int getFlashSupport() { 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 85a186224..f49b815bd 100644 --- a/app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java +++ b/app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java @@ -376,8 +376,8 @@ private static Converter createConverter(String url) { */ private String getResolvedUrl(String urlAsString, int timeout, int num_redirects) { - String newUrl = null; - int responseCode = -1; + String newUrl; + int responseCode; try { HttpURLConnection hConn = createUrlConnection(urlAsString, timeout, true); // force no follow diff --git a/app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java b/app/src/main/java/acr/browser/lightning/reading/activity/ReadingActivity.java similarity index 98% rename from app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java rename to app/src/main/java/acr/browser/lightning/reading/activity/ReadingActivity.java index f554c6fd8..fd351ac6a 100644 --- a/app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java +++ b/app/src/main/java/acr/browser/lightning/reading/activity/ReadingActivity.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.activity; +package acr.browser.lightning.reading.activity; import android.animation.ObjectAnimator; import android.app.Dialog; @@ -26,7 +26,7 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.preference.PreferenceManager; @@ -297,7 +297,7 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.text_size_item: View view = LayoutInflater.from(this).inflate(R.layout.dialog_seek_bar, null); - final SeekBar bar = (SeekBar) view.findViewById(R.id.text_size_seekbar); + final SeekBar bar = view.findViewById(R.id.text_size_seekbar); bar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override diff --git a/app/src/main/java/acr/browser/lightning/search/SearchEngineProvider.java b/app/src/main/java/acr/browser/lightning/search/SearchEngineProvider.java new file mode 100644 index 000000000..8889d62f0 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/SearchEngineProvider.java @@ -0,0 +1,112 @@ +package acr.browser.lightning.search; + +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import acr.browser.lightning.BrowserApp; +import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.search.engine.AskSearch; +import acr.browser.lightning.search.engine.BaiduSearch; +import acr.browser.lightning.search.engine.BaseSearchEngine; +import acr.browser.lightning.search.engine.BingSearch; +import acr.browser.lightning.search.engine.CustomSearch; +import acr.browser.lightning.search.engine.DuckLiteSearch; +import acr.browser.lightning.search.engine.DuckSearch; +import acr.browser.lightning.search.engine.GoogleSearch; +import acr.browser.lightning.search.engine.StartPageMobileSearch; +import acr.browser.lightning.search.engine.StartPageSearch; +import acr.browser.lightning.search.engine.YahooSearch; +import acr.browser.lightning.search.engine.YandexSearch; + +/** + * The model that provides the search engine based + * on the user's preference. + */ +public class SearchEngineProvider { + + @Inject PreferenceManager mPreferenceManager; + + @Inject + public SearchEngineProvider() { + BrowserApp.getAppComponent().inject(this); + } + + @NonNull + public BaseSearchEngine getCurrentSearchEngine() { + switch (mPreferenceManager.getSearchChoice()) { + case 0: + return new CustomSearch(mPreferenceManager.getSearchUrl()); + case 1: + default: + return new GoogleSearch(); + case 2: + return new AskSearch(); + case 3: + return new BingSearch(); + case 4: + return new YahooSearch(); + case 5: + return new StartPageSearch(); + case 6: + return new StartPageMobileSearch(); + case 7: + return new DuckSearch(); + case 8: + return new DuckLiteSearch(); + case 9: + return new BaiduSearch(); + case 10: + return new YandexSearch(); + } + } + + public int mapSearchEngineToPreferenceIndex(@NonNull BaseSearchEngine searchEngine) { + if (searchEngine instanceof CustomSearch) { + return 0; + } else if (searchEngine instanceof GoogleSearch) { + return 1; + } else if (searchEngine instanceof AskSearch) { + return 2; + } else if (searchEngine instanceof BingSearch) { + return 3; + } else if (searchEngine instanceof YahooSearch) { + return 4; + } else if (searchEngine instanceof StartPageSearch) { + return 5; + } else if (searchEngine instanceof StartPageMobileSearch) { + return 6; + } else if (searchEngine instanceof DuckSearch) { + return 7; + } else if (searchEngine instanceof DuckLiteSearch) { + return 8; + } else if (searchEngine instanceof BaiduSearch) { + return 9; + } else if (searchEngine instanceof YandexSearch) { + return 10; + } else { + throw new UnsupportedOperationException("Unknown search engine provided: " + searchEngine.getClass()); + } + } + + @NonNull + public List getAllSearchEngines() { + return new ArrayList(11) {{ + add(new CustomSearch(mPreferenceManager.getSearchUrl())); + add(new GoogleSearch()); + add(new AskSearch()); + add(new BingSearch()); + add(new YahooSearch()); + add(new StartPageSearch()); + add(new StartPageMobileSearch()); + add(new DuckSearch()); + add(new DuckLiteSearch()); + add(new BaiduSearch()); + add(new YandexSearch()); + }}; + } + +} diff --git a/app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java b/app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java index a14a9192b..314639cc5 100644 --- a/app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java +++ b/app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java @@ -37,7 +37,7 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.bookmark.BookmarkModel; import acr.browser.lightning.database.history.HistoryModel; @@ -67,6 +67,7 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable { @Inject BookmarkModel mBookmarkManager; @Inject PreferenceManager mPreferenceManager; + @Inject HistoryModel mHistoryModel; @Inject Application mApplication; private final List mAllBookmarks = new ArrayList<>(5); @@ -136,9 +137,9 @@ public long getItemId(int position) { private static class SuggestionHolder { SuggestionHolder(@NonNull View view) { - mTitle = (TextView) view.findViewById(R.id.title); - mUrl = (TextView) view.findViewById(R.id.url); - mImage = (ImageView) view.findViewById(R.id.suggestionIcon); + mTitle = view.findViewById(R.id.title); + mUrl = view.findViewById(R.id.url); + mImage = view.findViewById(R.id.suggestionIcon); } @NonNull final ImageView mImage; @@ -197,7 +198,7 @@ public View getView(int position, @Nullable View convertView, ViewGroup parent) @NonNull @Override public Filter getFilter() { - return new SearchFilter(this); + return new SearchFilter(this, mHistoryModel); } private synchronized void publishResults(@NonNull List list) { @@ -318,9 +319,12 @@ private boolean shouldRequestNetwork() { private static class SearchFilter extends Filter { @NonNull private final SuggestionsAdapter mSuggestionsAdapter; + @NonNull private final HistoryModel mHistoryModel; - SearchFilter(@NonNull SuggestionsAdapter suggestionsAdapter) { + SearchFilter(@NonNull SuggestionsAdapter suggestionsAdapter, + @NonNull HistoryModel historyModel) { mSuggestionsAdapter = suggestionsAdapter; + mHistoryModel = historyModel; } @NonNull @@ -355,7 +359,7 @@ public void onItem(@Nullable List item) { } }); - HistoryModel.findHistoryItemsContaining(query) + mHistoryModel.findHistoryItemsContaining(query) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.main()) .subscribe(new SingleOnSubscribe>() { diff --git a/app/src/main/java/acr/browser/lightning/search/SuggestionsManager.java b/app/src/main/java/acr/browser/lightning/search/SuggestionsManager.java deleted file mode 100644 index 13edb8c81..000000000 --- a/app/src/main/java/acr/browser/lightning/search/SuggestionsManager.java +++ /dev/null @@ -1,67 +0,0 @@ -package acr.browser.lightning.search; - -import android.app.Application; -import android.support.annotation.NonNull; - -import com.anthonycr.bonsai.Single; -import com.anthonycr.bonsai.SingleAction; -import com.anthonycr.bonsai.SingleSubscriber; - -import java.util.List; - -import acr.browser.lightning.database.HistoryItem; - -class SuggestionsManager { - - private static volatile boolean sIsTaskExecuting; - - static boolean isRequestInProgress() { - return sIsTaskExecuting; - } - - @NonNull - static Single> createGoogleQueryObservable(@NonNull final String query, - @NonNull final Application application) { - return Single.create(new SingleAction>() { - @Override - public void onSubscribe(@NonNull final SingleSubscriber> subscriber) { - sIsTaskExecuting = true; - List results = new GoogleSuggestionsModel(application).getResults(query); - subscriber.onItem(results); - subscriber.onComplete(); - sIsTaskExecuting = false; - } - }); - } - - @NonNull - static Single> createBaiduQueryObservable(@NonNull final String query, - @NonNull final Application application) { - return Single.create(new SingleAction>() { - @Override - public void onSubscribe(@NonNull final SingleSubscriber> subscriber) { - sIsTaskExecuting = true; - List results = new BaiduSuggestionsModel(application).getResults(query); - subscriber.onItem(results); - subscriber.onComplete(); - sIsTaskExecuting = false; - } - }); - } - - @NonNull - static Single> createDuckQueryObservable(@NonNull final String query, - @NonNull final Application application) { - return Single.create(new SingleAction>() { - @Override - public void onSubscribe(@NonNull final SingleSubscriber> subscriber) { - sIsTaskExecuting = true; - List results = new DuckSuggestionsModel(application).getResults(query); - subscriber.onItem(results); - subscriber.onComplete(); - sIsTaskExecuting = false; - } - }); - } - -} diff --git a/app/src/main/java/acr/browser/lightning/search/SuggestionsManager.kt b/app/src/main/java/acr/browser/lightning/search/SuggestionsManager.kt new file mode 100644 index 000000000..b28414e1c --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/SuggestionsManager.kt @@ -0,0 +1,46 @@ +package acr.browser.lightning.search + +import acr.browser.lightning.database.HistoryItem +import acr.browser.lightning.search.suggestions.BaiduSuggestionsModel +import acr.browser.lightning.search.suggestions.DuckSuggestionsModel +import acr.browser.lightning.search.suggestions.GoogleSuggestionsModel +import android.app.Application +import com.anthonycr.bonsai.Single +import com.anthonycr.bonsai.SingleAction + +internal object SuggestionsManager { + + @JvmStatic + @Volatile var isRequestInProgress: Boolean = false + + @JvmStatic + fun createGoogleQueryObservable(query: String, application: Application) = + Single.create(SingleAction> { subscriber -> + isRequestInProgress = true + val results = GoogleSuggestionsModel(application).fetchResults(query) + subscriber.onItem(results) + subscriber.onComplete() + isRequestInProgress = false + }) + + @JvmStatic + fun createBaiduQueryObservable(query: String, application: Application) = + Single.create(SingleAction> { subscriber -> + isRequestInProgress = true + val results = BaiduSuggestionsModel(application).fetchResults(query) + subscriber.onItem(results) + subscriber.onComplete() + isRequestInProgress = false + }) + + @JvmStatic + fun createDuckQueryObservable(query: String, application: Application) = + Single.create(SingleAction> { subscriber -> + isRequestInProgress = true + val results = DuckSuggestionsModel(application).fetchResults(query) + subscriber.onItem(results) + subscriber.onComplete() + isRequestInProgress = false + }) + +} diff --git a/app/src/main/java/acr/browser/lightning/search/engine/AskSearch.kt b/app/src/main/java/acr/browser/lightning/search/engine/AskSearch.kt new file mode 100644 index 000000000..5ffb7bca6 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/engine/AskSearch.kt @@ -0,0 +1,13 @@ +package acr.browser.lightning.search.engine + +import acr.browser.lightning.R +import acr.browser.lightning.constant.Constants + +/** + * The Ask search engine. + */ +class AskSearch : BaseSearchEngine( + "file:///android_asset/ask.png", + Constants.ASK_SEARCH, + R.string.search_engine_ask +) diff --git a/app/src/main/java/acr/browser/lightning/search/engine/BaiduSearch.kt b/app/src/main/java/acr/browser/lightning/search/engine/BaiduSearch.kt new file mode 100644 index 000000000..f87366381 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/engine/BaiduSearch.kt @@ -0,0 +1,15 @@ +package acr.browser.lightning.search.engine + +import acr.browser.lightning.R +import acr.browser.lightning.constant.Constants + +/** + * The Baidu search engine. + * + * See http://www.baidu.com/img/bdlogo.gif for the icon. + */ +class BaiduSearch : BaseSearchEngine( + "file:///android_asset/baidu.png", + Constants.BAIDU_SEARCH, + R.string.search_engine_baidu +) diff --git a/app/src/main/java/acr/browser/lightning/search/engine/BaseSearchEngine.kt b/app/src/main/java/acr/browser/lightning/search/engine/BaseSearchEngine.kt new file mode 100644 index 000000000..b066e5522 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/engine/BaseSearchEngine.kt @@ -0,0 +1,15 @@ +package acr.browser.lightning.search.engine + +import android.support.annotation.StringRes + +/** + * A class representative of a search engine. + * + * Contains three key pieces of information: + * * The icon shown for the search engine, should point to a local assets URL. + * * The query URL for the search engine, the query will be appended to the end. + * * The title string resource for the search engine. + */ +open class BaseSearchEngine internal constructor(val iconUrl: String, + val queryUrl: String, + @StringRes val titleRes: Int) diff --git a/app/src/main/java/acr/browser/lightning/search/engine/BingSearch.kt b/app/src/main/java/acr/browser/lightning/search/engine/BingSearch.kt new file mode 100644 index 000000000..466a263da --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/engine/BingSearch.kt @@ -0,0 +1,16 @@ +package acr.browser.lightning.search.engine + +import acr.browser.lightning.R +import acr.browser.lightning.constant.Constants + +/** + * The Bing search engine. + * + * See http://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Bing_logo_%282013%29.svg/500px-Bing_logo_%282013%29.svg.png + * for the icon. + */ +class BingSearch : BaseSearchEngine( + "file:///android_asset/bing.png", + Constants.BING_SEARCH, + R.string.search_engine_bing +) diff --git a/app/src/main/java/acr/browser/lightning/search/engine/CustomSearch.kt b/app/src/main/java/acr/browser/lightning/search/engine/CustomSearch.kt new file mode 100644 index 000000000..f48de75aa --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/engine/CustomSearch.kt @@ -0,0 +1,12 @@ +package acr.browser.lightning.search.engine + +import acr.browser.lightning.R + +/** + * A custom search engine. + */ +class CustomSearch(queryUrl: String) : BaseSearchEngine( + "file:///android_asset/lightning.png", + queryUrl, + R.string.search_engine_custom +) diff --git a/app/src/main/java/acr/browser/lightning/search/engine/DuckLiteSearch.kt b/app/src/main/java/acr/browser/lightning/search/engine/DuckLiteSearch.kt new file mode 100644 index 000000000..2135a6fb6 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/engine/DuckLiteSearch.kt @@ -0,0 +1,15 @@ +package acr.browser.lightning.search.engine + +import acr.browser.lightning.R +import acr.browser.lightning.constant.Constants + +/** + * The DuckDuckGo Lite search engine. + * + * See https://duckduckgo.com/assets/logo_homepage.normal.v101.png for the icon. + */ +class DuckLiteSearch : BaseSearchEngine( + "file:///android_asset/duckduckgo.png", + Constants.DUCK_LITE_SEARCH, + R.string.search_engine_duckduckgo_lite +) diff --git a/app/src/main/java/acr/browser/lightning/search/engine/DuckSearch.kt b/app/src/main/java/acr/browser/lightning/search/engine/DuckSearch.kt new file mode 100644 index 000000000..dd110c76e --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/engine/DuckSearch.kt @@ -0,0 +1,15 @@ +package acr.browser.lightning.search.engine + +import acr.browser.lightning.R +import acr.browser.lightning.constant.Constants + +/** + * The DuckDuckGo search engine. + * + * See https://duckduckgo.com/assets/logo_homepage.normal.v101.png for the icon. + */ +class DuckSearch : BaseSearchEngine( + "file:///android_asset/duckduckgo.png", + Constants.DUCK_SEARCH, + R.string.search_engine_duckduckgo +) diff --git a/app/src/main/java/acr/browser/lightning/search/engine/GoogleSearch.kt b/app/src/main/java/acr/browser/lightning/search/engine/GoogleSearch.kt new file mode 100644 index 000000000..79547a1af --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/engine/GoogleSearch.kt @@ -0,0 +1,15 @@ +package acr.browser.lightning.search.engine + +import acr.browser.lightning.R +import acr.browser.lightning.constant.Constants + +/** + * The Google search engine. + * + * See https://www.google.com/images/srpr/logo11w.png for the icon. + */ +class GoogleSearch : BaseSearchEngine( + "file:///android_asset/google.png", + Constants.GOOGLE_SEARCH, + R.string.search_engine_google +) diff --git a/app/src/main/java/acr/browser/lightning/search/engine/StartPageMobileSearch.kt b/app/src/main/java/acr/browser/lightning/search/engine/StartPageMobileSearch.kt new file mode 100644 index 000000000..f83bab4b5 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/engine/StartPageMobileSearch.kt @@ -0,0 +1,13 @@ +package acr.browser.lightning.search.engine + +import acr.browser.lightning.R +import acr.browser.lightning.constant.Constants + +/** + * The StartPage mobile search engine. + */ +class StartPageMobileSearch : BaseSearchEngine( + "file:///android_asset/startpage.png", + Constants.STARTPAGE_MOBILE_SEARCH, + R.string.search_engine_startpage_mobile +) diff --git a/app/src/main/java/acr/browser/lightning/search/engine/StartPageSearch.kt b/app/src/main/java/acr/browser/lightning/search/engine/StartPageSearch.kt new file mode 100644 index 000000000..a16715594 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/engine/StartPageSearch.kt @@ -0,0 +1,13 @@ +package acr.browser.lightning.search.engine + +import acr.browser.lightning.R +import acr.browser.lightning.constant.Constants + +/** + * The StartPage search engine. + */ +class StartPageSearch : BaseSearchEngine( + "file:///android_asset/startpage.png", + Constants.STARTPAGE_SEARCH, + R.string.search_engine_startpage +) diff --git a/app/src/main/java/acr/browser/lightning/search/engine/YahooSearch.kt b/app/src/main/java/acr/browser/lightning/search/engine/YahooSearch.kt new file mode 100644 index 000000000..2355ab6d3 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/engine/YahooSearch.kt @@ -0,0 +1,16 @@ +package acr.browser.lightning.search.engine + +import acr.browser.lightning.R +import acr.browser.lightning.constant.Constants + +/** + * The Yahoo search engine. + * + * See http://upload.wikimedia.org/wikipedia/commons/thumb/2/24/Yahoo%21_logo.svg/799px-Yahoo%21_logo.svg.png + * for the icon. + */ +class YahooSearch : BaseSearchEngine( + "file:///android_asset/yahoo.png", + Constants.YAHOO_SEARCH, + R.string.search_engine_yahoo +) diff --git a/app/src/main/java/acr/browser/lightning/search/engine/YandexSearch.kt b/app/src/main/java/acr/browser/lightning/search/engine/YandexSearch.kt new file mode 100644 index 000000000..8d58957e2 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/search/engine/YandexSearch.kt @@ -0,0 +1,16 @@ +package acr.browser.lightning.search.engine + +import acr.browser.lightning.R +import acr.browser.lightning.constant.Constants + +/** + * The Yandex search engine. + * + * See http://upload.wikimedia.org/wikipedia/commons/thumb/9/91/Yandex.svg/600px-Yandex.svg.png + * for the icon. + */ +class YandexSearch : BaseSearchEngine( + "file:///android_asset/yandex.png", + Constants.YANDEX_SEARCH, + R.string.search_engine_yandex +) diff --git a/app/src/main/java/acr/browser/lightning/search/BaiduSuggestionsModel.java b/app/src/main/java/acr/browser/lightning/search/suggestions/BaiduSuggestionsModel.java similarity index 74% rename from app/src/main/java/acr/browser/lightning/search/BaiduSuggestionsModel.java rename to app/src/main/java/acr/browser/lightning/search/suggestions/BaiduSuggestionsModel.java index 412bfd8d7..2c18ec3e7 100644 --- a/app/src/main/java/acr/browser/lightning/search/BaiduSuggestionsModel.java +++ b/app/src/main/java/acr/browser/lightning/search/suggestions/BaiduSuggestionsModel.java @@ -1,11 +1,9 @@ -package acr.browser.lightning.search; +package acr.browser.lightning.search.suggestions; import android.app.Application; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import org.json.JSONArray; -import org.xmlpull.v1.XmlPullParser; import java.io.InputStream; import java.util.List; @@ -14,23 +12,23 @@ import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.utils.FileUtils; -// http://unionsug.baidu.com/su?wd=encodeURIComponent(U) -// http://suggestion.baidu.com/s?wd=encodeURIComponent(U)&action=opensearch - - -class BaiduSuggestionsModel extends BaseSuggestionsModel { +/** + * The search suggestions provider for the Baidu search engine. + */ +public class BaiduSuggestionsModel extends BaseSuggestionsModel { @NonNull private static final String ENCODING = "UTF-8"; - @Nullable private static XmlPullParser sXpp; @NonNull private final String mSearchSubtitle; - BaiduSuggestionsModel(@NonNull Application application) { + public BaiduSuggestionsModel(@NonNull Application application) { super(application, ENCODING); mSearchSubtitle = application.getString(R.string.suggestion); } @NonNull protected String createQueryUrl(@NonNull String query, @NonNull String language) { + // see http://unionsug.baidu.com/su?wd=encodeURIComponent(U) + // see http://suggestion.baidu.com/s?wd=encodeURIComponent(U)&action=opensearch return "http://suggestion.baidu.com/s?wd=" + query + "&action=opensearch"; } @@ -39,12 +37,14 @@ protected void parseResults(@NonNull InputStream inputStream, @NonNull List= MAX_RESULTS) { break; } diff --git a/app/src/main/java/acr/browser/lightning/search/BaseSuggestionsModel.java b/app/src/main/java/acr/browser/lightning/search/suggestions/BaseSuggestionsModel.java similarity index 78% rename from app/src/main/java/acr/browser/lightning/search/BaseSuggestionsModel.java rename to app/src/main/java/acr/browser/lightning/search/suggestions/BaseSuggestionsModel.java index 7dfcf47e0..961715f00 100644 --- a/app/src/main/java/acr/browser/lightning/search/BaseSuggestionsModel.java +++ b/app/src/main/java/acr/browser/lightning/search/suggestions/BaseSuggestionsModel.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.search; +package acr.browser.lightning.search.suggestions; import android.app.Application; import android.support.annotation.NonNull; @@ -28,7 +28,12 @@ import okhttp3.Response; import okhttp3.ResponseBody; -abstract class BaseSuggestionsModel { +/** + * The base search suggestions API. Provides common + * fetching and caching functionality for each potential + * suggestions provider. + */ +public abstract class BaseSuggestionsModel { private static final String TAG = "BaseSuggestionsModel"; @@ -41,9 +46,23 @@ abstract class BaseSuggestionsModel { @NonNull private final String mEncoding; @NonNull private final String mLanguage; + /** + * Create a URL for the given query in the given language. + * + * @param query the query that was made. + * @param language the locale of the user. + * @return should return a URL that can be fetched using a GET. + */ @NonNull protected abstract String createQueryUrl(@NonNull String query, @NonNull String language); + /** + * Parse the results of an input stream into a list of {@link HistoryItem}. + * + * @param inputStream the raw input to parse. + * @param results the list to populate. + * @throws Exception throw an exception if anything goes wrong. + */ protected abstract void parseResults(@NonNull InputStream inputStream, @NonNull List results) throws Exception; BaseSuggestionsModel(@NonNull Application application, @NonNull String encoding) { @@ -57,23 +76,25 @@ abstract class BaseSuggestionsModel { mCacheControl = new CacheControl.Builder().maxStale(1, TimeUnit.DAYS).build(); } + /** + * Retrieves the results for a query. + * + * @param rawQuery the raw query to retrieve the results for. + * @return a list of history items for the query. + */ @NonNull - private static String getLanguage() { - String language = Locale.getDefault().getLanguage(); - if (TextUtils.isEmpty(language)) { - language = DEFAULT_LANGUAGE; - } - return language; - } - - @NonNull - List getResults(@NonNull String query) { + public final List fetchResults(@NonNull final String rawQuery) { List filter = new ArrayList<>(5); + + String query; try { - query = URLEncoder.encode(query, mEncoding); + query = URLEncoder.encode(rawQuery, mEncoding); } catch (UnsupportedEncodingException e) { Log.e(TAG, "Unable to encode the URL", e); + + return filter; } + InputStream inputStream = downloadSuggestionsForQuery(query, mLanguage); if (inputStream == null) { // There are no suggestions for this query, return an empty list. @@ -83,7 +104,6 @@ List getResults(@NonNull String query) { parseResults(inputStream, filter); } catch (Exception e) { Log.e(TAG, "Unable to parse results", e); - return filter; } finally { Utils.close(inputStream); } @@ -93,7 +113,7 @@ List getResults(@NonNull String query) { /** * This method downloads the search suggestions for the specific query. - * NOTE: This is a blocking operation, do not getResults on the UI thread. + * NOTE: This is a blocking operation, do not fetchResults on the UI thread. * * @param query the query to get suggestions for * @return the cache file containing the suggestions @@ -122,6 +142,16 @@ private InputStream downloadSuggestionsForQuery(@NonNull String query, @NonNull return null; } + @NonNull + private static String getLanguage() { + String language = Locale.getDefault().getLanguage(); + if (TextUtils.isEmpty(language)) { + language = DEFAULT_LANGUAGE; + } + return language; + } + + @NonNull private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() { @Override public Response intercept(@NonNull Chain chain) throws IOException { diff --git a/app/src/main/java/acr/browser/lightning/search/DuckSuggestionsModel.java b/app/src/main/java/acr/browser/lightning/search/suggestions/DuckSuggestionsModel.java similarity index 84% rename from app/src/main/java/acr/browser/lightning/search/DuckSuggestionsModel.java rename to app/src/main/java/acr/browser/lightning/search/suggestions/DuckSuggestionsModel.java index b07b3039f..91f6f56b7 100644 --- a/app/src/main/java/acr/browser/lightning/search/DuckSuggestionsModel.java +++ b/app/src/main/java/acr/browser/lightning/search/suggestions/DuckSuggestionsModel.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.search; +package acr.browser.lightning.search.suggestions; import android.app.Application; import android.support.annotation.NonNull; @@ -13,12 +13,15 @@ import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.utils.FileUtils; -final class DuckSuggestionsModel extends BaseSuggestionsModel { +/** + * The search suggestions provider for the DuckDuckGo search engine. + */ +public final class DuckSuggestionsModel extends BaseSuggestionsModel { @NonNull private static final String ENCODING = "UTF-8"; @NonNull private final String mSearchSubtitle; - DuckSuggestionsModel(@NonNull Application application) { + public DuckSuggestionsModel(@NonNull Application application) { super(application, ENCODING); mSearchSubtitle = application.getString(R.string.suggestion); } diff --git a/app/src/main/java/acr/browser/lightning/search/GoogleSuggestionsModel.java b/app/src/main/java/acr/browser/lightning/search/suggestions/GoogleSuggestionsModel.java similarity index 86% rename from app/src/main/java/acr/browser/lightning/search/GoogleSuggestionsModel.java rename to app/src/main/java/acr/browser/lightning/search/suggestions/GoogleSuggestionsModel.java index ed892a094..53ce9b918 100644 --- a/app/src/main/java/acr/browser/lightning/search/GoogleSuggestionsModel.java +++ b/app/src/main/java/acr/browser/lightning/search/suggestions/GoogleSuggestionsModel.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.search; +package acr.browser.lightning.search.suggestions; import android.app.Application; import android.support.annotation.NonNull; @@ -15,20 +15,23 @@ import acr.browser.lightning.R; import acr.browser.lightning.database.HistoryItem; -class GoogleSuggestionsModel extends BaseSuggestionsModel { +/** + * Search suggestions provider for Google search engine. + */ +public class GoogleSuggestionsModel extends BaseSuggestionsModel { - @NonNull private static final String ENCODING = "ISO-8859-1"; + @NonNull private static final String ENCODING = "UTF-8"; @Nullable private static XmlPullParser sXpp; @NonNull private final String mSearchSubtitle; - GoogleSuggestionsModel(@NonNull Application application) { + public GoogleSuggestionsModel(@NonNull Application application) { super(application, ENCODING); mSearchSubtitle = application.getString(R.string.suggestion); } @NonNull protected String createQueryUrl(@NonNull String query, @NonNull String language) { - return "https://suggestqueries.google.com/complete/search?output=toolbar&oe=latin1&hl=" + return "https://suggestqueries.google.com/complete/search?output=toolbar&hl=" + language + "&q=" + query; } diff --git a/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java b/app/src/main/java/acr/browser/lightning/settings/activity/SettingsActivity.java similarity index 83% rename from app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java rename to app/src/main/java/acr/browser/lightning/settings/activity/SettingsActivity.java index 498bd06bf..687e9a3b0 100644 --- a/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java +++ b/app/src/main/java/acr/browser/lightning/settings/activity/SettingsActivity.java @@ -1,7 +1,7 @@ /* * Copyright 2014 A.C.R. Development */ -package acr.browser.lightning.activity; +package acr.browser.lightning.settings.activity; import android.os.Build; import android.os.Bundle; @@ -19,17 +19,17 @@ import java.util.List; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; public class SettingsActivity extends ThemableSettingsActivity { - @NonNull private static final List mFragments = new ArrayList<>(7); + @NonNull private static final List sFragments = new ArrayList<>(7); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // this is a workaround for the Toolbar in PreferenceActitivty - ViewGroup root = (ViewGroup) findViewById(android.R.id.content); + // this is a workaround for the Toolbar in PreferenceActivity + ViewGroup root = findViewById(android.R.id.content); LinearLayout content = (LinearLayout) root.getChildAt(0); LinearLayout toolbarContainer = (LinearLayout) View.inflate(this, R.layout.toolbar_settings, null); @@ -38,7 +38,7 @@ protected void onCreate(Bundle savedInstanceState) { root.addView(toolbarContainer); // now we can set the Toolbar using AppCompatPreferenceActivity - Toolbar toolbar = (Toolbar) toolbarContainer.findViewById(R.id.toolbar); + Toolbar toolbar = toolbarContainer.findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } @@ -46,7 +46,7 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onBuildHeaders(List

target) { loadHeadersFromResource(R.xml.preferences_headers, target); - mFragments.clear(); + sFragments.clear(); Iterator
headerIterator = target.iterator(); while (headerIterator.hasNext()) { Header header = headerIterator.next(); @@ -59,17 +59,17 @@ public void onBuildHeaders(List
target) { if (BrowserApp.isRelease()) { headerIterator.remove(); } else { - mFragments.add(header.fragment); + sFragments.add(header.fragment); } } else { - mFragments.add(header.fragment); + sFragments.add(header.fragment); } } } @Override protected boolean isValidFragment(String fragmentName) { - return mFragments.contains(fragmentName); + return sFragments.contains(fragmentName); } @Override diff --git a/app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java b/app/src/main/java/acr/browser/lightning/settings/activity/ThemableSettingsActivity.java similarity index 92% rename from app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java rename to app/src/main/java/acr/browser/lightning/settings/activity/ThemableSettingsActivity.java index ebc2c1e78..008ce7f46 100644 --- a/app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java +++ b/app/src/main/java/acr/browser/lightning/settings/activity/ThemableSettingsActivity.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.activity; +package acr.browser.lightning.settings.activity; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; @@ -8,7 +8,8 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.activity.AppCompatPreferenceActivity; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.ThemeUtils; diff --git a/app/src/main/java/acr/browser/lightning/fragment/AboutSettingsFragment.java b/app/src/main/java/acr/browser/lightning/settings/fragment/AboutSettingsFragment.java similarity index 67% rename from app/src/main/java/acr/browser/lightning/fragment/AboutSettingsFragment.java rename to app/src/main/java/acr/browser/lightning/settings/fragment/AboutSettingsFragment.java index ce4c4be89..483e253d8 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/AboutSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/settings/fragment/AboutSettingsFragment.java @@ -1,7 +1,7 @@ /* * Copyright 2014 A.C.R. Development */ -package acr.browser.lightning.fragment; +package acr.browser.lightning.settings.fragment; import android.app.Activity; import android.content.pm.PackageInfo; @@ -9,13 +9,13 @@ import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceFragment; +import android.util.Log; import acr.browser.lightning.R; public class AboutSettingsFragment extends PreferenceFragment { - private Activity mActivity; - + private static final String TAG = "AboutSettingsFragment"; private static final String SETTINGS_VERSION = "pref_version"; @Override @@ -24,18 +24,18 @@ public void onCreate(Bundle savedInstanceState) { // Load the preferences from an XML resource addPreferencesFromResource(R.xml.preference_about); - mActivity = getActivity(); - Preference version = findPreference(SETTINGS_VERSION); version.setSummary(getVersion()); } private String getVersion() { try { - PackageInfo p = mActivity.getPackageManager().getPackageInfo(mActivity.getPackageName(), 0); - return p.versionName; + Activity activity = getActivity(); + String packageName = activity.getPackageName(); + PackageInfo packageInfo = activity.getPackageManager().getPackageInfo(packageName, 0); + return packageInfo.versionName; } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); + Log.e(TAG, "getVersion: error", e); return "1.0"; } } diff --git a/app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java b/app/src/main/java/acr/browser/lightning/settings/fragment/AdvancedSettingsFragment.java similarity index 99% rename from app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java rename to app/src/main/java/acr/browser/lightning/settings/fragment/AdvancedSettingsFragment.java index ba5d7da42..ca89161f4 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/settings/fragment/AdvancedSettingsFragment.java @@ -1,7 +1,7 @@ /* * Copyright 2014 A.C.R. Development */ -package acr.browser.lightning.fragment; +package acr.browser.lightning.settings.fragment; import android.app.Activity; import android.app.Dialog; diff --git a/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java b/app/src/main/java/acr/browser/lightning/settings/fragment/BookmarkSettingsFragment.java similarity index 99% rename from app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java rename to app/src/main/java/acr/browser/lightning/settings/fragment/BookmarkSettingsFragment.java index 4e9b8808f..9a2de5ace 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/settings/fragment/BookmarkSettingsFragment.java @@ -1,7 +1,7 @@ /* * Copyright 2014 A.C.R. Development */ -package acr.browser.lightning.fragment; +package acr.browser.lightning.settings.fragment; import android.Manifest; import android.app.Activity; @@ -23,6 +23,7 @@ import android.widget.ArrayAdapter; import com.anthonycr.bonsai.CompletableOnSubscribe; +import com.anthonycr.bonsai.Schedulers; import com.anthonycr.bonsai.SingleOnSubscribe; import com.anthonycr.bonsai.Subscription; import com.anthonycr.grant.PermissionsManager; @@ -38,14 +39,11 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; +import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.bookmark.BookmarkExporter; import acr.browser.lightning.database.bookmark.BookmarkLocalSync; import acr.browser.lightning.database.bookmark.BookmarkLocalSync.Source; -import acr.browser.lightning.database.HistoryItem; - -import com.anthonycr.bonsai.Schedulers; - import acr.browser.lightning.database.bookmark.BookmarkModel; import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.utils.Preconditions; @@ -495,7 +493,7 @@ public void onItem(@Nullable final List importList) { public void onComplete() { Activity activity = getActivity(); if (activity != null) { - String message = activity.getResources().getString(R.string.message_import); + String message = activity.getString(R.string.message_import); Utils.showSnackbar(activity, importList.size() + " " + message); } } diff --git a/app/src/main/java/acr/browser/lightning/fragment/DebugSettingsFragment.java b/app/src/main/java/acr/browser/lightning/settings/fragment/DebugSettingsFragment.java similarity index 95% rename from app/src/main/java/acr/browser/lightning/fragment/DebugSettingsFragment.java rename to app/src/main/java/acr/browser/lightning/settings/fragment/DebugSettingsFragment.java index dba40b710..be6109ae9 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/DebugSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/settings/fragment/DebugSettingsFragment.java @@ -1,4 +1,4 @@ -package acr.browser.lightning.fragment; +package acr.browser.lightning.settings.fragment; import android.app.Activity; import android.os.Bundle; @@ -10,7 +10,7 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.Utils; diff --git a/app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java b/app/src/main/java/acr/browser/lightning/settings/fragment/DisplaySettingsFragment.java similarity index 98% rename from app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java rename to app/src/main/java/acr/browser/lightning/settings/fragment/DisplaySettingsFragment.java index d5d796c36..575c20327 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/settings/fragment/DisplaySettingsFragment.java @@ -1,7 +1,7 @@ /* * Copyright 2014 A.C.R. Development */ -package acr.browser.lightning.fragment; +package acr.browser.lightning.settings.fragment; import android.app.Activity; import android.app.Dialog; @@ -152,7 +152,7 @@ private void textSizePicker() { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); LayoutInflater inflater = getActivity().getLayoutInflater(); LinearLayout view = (LinearLayout) inflater.inflate(R.layout.dialog_seek_bar, null); - final SeekBar bar = (SeekBar) view.findViewById(R.id.text_size_seekbar); + final SeekBar bar = view.findViewById(R.id.text_size_seekbar); final TextView sample = new TextView(getActivity()); sample.setText(R.string.untitled); sample.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT)); diff --git a/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java b/app/src/main/java/acr/browser/lightning/settings/fragment/GeneralSettingsFragment.java similarity index 89% rename from app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java rename to app/src/main/java/acr/browser/lightning/settings/fragment/GeneralSettingsFragment.java index d22d11271..7fdaef709 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/settings/fragment/GeneralSettingsFragment.java @@ -1,7 +1,7 @@ /* * Copyright 2014 A.C.R. Development */ -package acr.browser.lightning.fragment; +package acr.browser.lightning.settings.fragment; import android.app.Activity; import android.app.Dialog; @@ -19,13 +19,22 @@ import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; +import android.webkit.URLUtil; import android.widget.EditText; +import java.util.List; + +import javax.inject.Inject; + import acr.browser.lightning.BuildConfig; import acr.browser.lightning.R; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.dialog.BrowserDialog; -import acr.browser.lightning.download.DownloadHandler; +import acr.browser.lightning.search.SearchEngineProvider; +import acr.browser.lightning.search.engine.BaseSearchEngine; +import acr.browser.lightning.search.engine.CustomSearch; +import acr.browser.lightning.utils.FileUtils; import acr.browser.lightning.utils.ProxyUtils; import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.Utils; @@ -53,12 +62,16 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme private int mAgentChoice; private String mHomepage; + @Inject SearchEngineProvider mSearchEngineProvider; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Load the preferences from an XML resource addPreferencesFromResource(R.xml.preference_general); + BrowserApp.getAppComponent().inject(this); + mActivity = getActivity(); initPrefs(); @@ -106,7 +119,8 @@ private void initPrefs() { mPreferenceManager.setFlashSupport(0); } - setSearchEngineSummary(mPreferenceManager.getSearchChoice()); + BaseSearchEngine currentSearchEngine = mSearchEngineProvider.getCurrentSearchEngine(); + setSearchEngineSummary(currentSearchEngine); downloadloc.setSummary(mDownloadLocation); @@ -156,11 +170,15 @@ private void initPrefs() { cbAds.setEnabled(BuildConfig.FULL_VERSION); + if (!BuildConfig.FULL_VERSION) { + cbAds.setSummary(R.string.upsell_plus_version); + } + if (API < Build.VERSION_CODES.KITKAT) { cbFlash.setEnabled(true); } else { cbFlash.setEnabled(false); - cbFlash.setSummary(getResources().getString(R.string.flash_not_supported)); + cbFlash.setSummary(R.string.flash_not_supported); } cbImages.setChecked(imagesBool); @@ -170,18 +188,18 @@ private void initPrefs() { cbColorMode.setChecked(mPreferenceManager.getColorModeEnabled()); } - private void searchUrlPicker() { + private void showUrlPicker(@NonNull final CustomSearch customSearch) { BrowserDialog.showEditText(mActivity, - R.string.custom_url, - R.string.custom_url, + R.string.search_engine_custom, + R.string.search_engine_custom, mPreferenceManager.getSearchUrl(), R.string.action_ok, new BrowserDialog.EditorListener() { @Override public void onClick(String text) { mPreferenceManager.setSearchUrl(text); - searchengine.setSummary(mActivity.getString(R.string.custom_url) + ": " + text); + setSearchEngineSummary(customSearch); } }); @@ -257,8 +275,8 @@ private void setProxyChoice(@Constants.Proxy int choice) { private void manualProxyPicker() { View v = mActivity.getLayoutInflater().inflate(R.layout.dialog_manual_proxy, null); - final EditText eProxyHost = (EditText) v.findViewById(R.id.proxyHost); - final EditText eProxyPort = (EditText) v.findViewById(R.id.proxyPort); + final EditText eProxyHost = v.findViewById(R.id.proxyHost); + final EditText eProxyPort = v.findViewById(R.id.proxyPort); // Limit the number of characters since the port needs to be of type int // Use input filters to limite the EditText length and determine the max @@ -294,13 +312,24 @@ public void onClick(DialogInterface dialogInterface, int i) { BrowserDialog.setDialogSize(mActivity, dialog); } + @NonNull + private CharSequence[] convertSearchEngineToString(@NonNull List searchEngines) { + CharSequence[] titles = new CharSequence[searchEngines.size()]; + + for (int n = 0; n < searchEngines.size(); n++) { + titles[n] = getString(searchEngines.get(n).getTitleRes()); + } + + return titles; + } + private void searchDialog() { AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.title_search_engine)); - CharSequence[] chars = {getResources().getString(R.string.custom_url), "Google", - "Ask", "Bing", "Yahoo", "StartPage", "StartPage (Mobile)", - "DuckDuckGo (Privacy)", "DuckDuckGo Lite (Privacy)", "Baidu (Chinese)", - "Yandex (Russian)"}; + + final List searchEngineList = mSearchEngineProvider.getAllSearchEngines(); + + CharSequence[] chars = convertSearchEngineToString(searchEngineList); int n = mPreferenceManager.getSearchChoice(); @@ -308,8 +337,19 @@ private void searchDialog() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferenceManager.setSearchChoice(which); - setSearchEngineSummary(which); + BaseSearchEngine searchEngine = searchEngineList.get(which); + + // Store the search engine preference + int preferencesIndex = mSearchEngineProvider.mapSearchEngineToPreferenceIndex(searchEngine); + mPreferenceManager.setSearchChoice(preferencesIndex); + + if (searchEngine instanceof CustomSearch) { + // Show the URL picker + showUrlPicker((CustomSearch) searchEngine); + } else { + // Set the new search engine summary + setSearchEngineSummary(searchEngine); + } } }); picker.setPositiveButton(R.string.action_ok, null); @@ -418,7 +458,7 @@ public void onClick(DialogInterface dialog, int which) { private void homePicker() { String currentHomepage; mHomepage = mPreferenceManager.getHomepage(); - if (!mHomepage.startsWith(Constants.ABOUT)) { + if (!URLUtil.isAboutUrl(mHomepage)) { currentHomepage = mHomepage; } else { currentHomepage = "https://www.google.com"; @@ -455,8 +495,8 @@ private void downloadLocDialog() { public void onClick(DialogInterface dialog, int which) { switch (which) { case 0: - mPreferenceManager.setDownloadDirectory(DownloadHandler.DEFAULT_DOWNLOAD_PATH); - downloadloc.setSummary(DownloadHandler.DEFAULT_DOWNLOAD_PATH); + mPreferenceManager.setDownloadDirectory(FileUtils.DEFAULT_DOWNLOAD_PATH); + downloadloc.setSummary(FileUtils.DEFAULT_DOWNLOAD_PATH); break; case 1: downPicker(); @@ -519,7 +559,7 @@ public void onClick(String text) { private void downPicker() { View dialogView = LayoutInflater.from(mActivity).inflate(R.layout.dialog_edit_text, null); - final EditText getDownload = (EditText) dialogView.findViewById(R.id.dialog_edit_text); + final EditText getDownload = dialogView.findViewById(R.id.dialog_edit_text); final int errorColor = ContextCompat.getColor(mActivity, R.color.error_red); final int regularColor = ThemeUtils.getTextColor(mActivity); @@ -535,7 +575,7 @@ private void downPicker() { @Override public void onClick(DialogInterface dialog, int which) { String text = getDownload.getText().toString(); - text = DownloadHandler.addNecessarySlashes(text); + text = FileUtils.addNecessarySlashes(text); mPreferenceManager.setDownloadDirectory(text); downloadloc.setSummary(text); } @@ -544,40 +584,11 @@ public void onClick(DialogInterface dialog, int which) { BrowserDialog.setDialogSize(mActivity, dialog); } - private void setSearchEngineSummary(int which) { - switch (which) { - case 0: - searchUrlPicker(); - break; - case 1: - searchengine.setSummary("Google"); - break; - case 2: - searchengine.setSummary("Ask"); - break; - case 3: - searchengine.setSummary("Bing"); - break; - case 4: - searchengine.setSummary("Yahoo"); - break; - case 5: - searchengine.setSummary("StartPage"); - break; - case 6: - searchengine.setSummary("StartPage (Mobile)"); - break; - case 7: - searchengine.setSummary("DuckDuckGo"); - break; - case 8: - searchengine.setSummary("DuckDuckGo Lite"); - break; - case 9: - searchengine.setSummary("Baidu"); - break; - case 10: - searchengine.setSummary("Yandex"); + private void setSearchEngineSummary(BaseSearchEngine baseSearchEngine) { + if (baseSearchEngine instanceof CustomSearch) { + searchengine.setSummary(mPreferenceManager.getSearchUrl()); + } else { + searchengine.setSummary(getString(baseSearchEngine.getTitleRes())); } } @@ -663,7 +674,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void afterTextChanged(@NonNull Editable s) { - if (!DownloadHandler.isWriteAccessAvailable(s.toString())) { + if (!FileUtils.isWriteAccessAvailable(s.toString())) { this.getDownload.setTextColor(this.errorColor); } else { this.getDownload.setTextColor(this.regularColor); diff --git a/app/src/main/java/acr/browser/lightning/fragment/LightningPreferenceFragment.java b/app/src/main/java/acr/browser/lightning/settings/fragment/LightningPreferenceFragment.java similarity index 86% rename from app/src/main/java/acr/browser/lightning/fragment/LightningPreferenceFragment.java rename to app/src/main/java/acr/browser/lightning/settings/fragment/LightningPreferenceFragment.java index ab7c48d95..02192674c 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/LightningPreferenceFragment.java +++ b/app/src/main/java/acr/browser/lightning/settings/fragment/LightningPreferenceFragment.java @@ -1,11 +1,11 @@ -package acr.browser.lightning.fragment; +package acr.browser.lightning.settings.fragment; import android.os.Bundle; import android.preference.PreferenceFragment; import javax.inject.Inject; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.preference.PreferenceManager; /** diff --git a/app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java b/app/src/main/java/acr/browser/lightning/settings/fragment/PrivacySettingsFragment.java similarity index 97% rename from app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java rename to app/src/main/java/acr/browser/lightning/settings/fragment/PrivacySettingsFragment.java index f3d6ecc40..5a35eda54 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/settings/fragment/PrivacySettingsFragment.java @@ -1,7 +1,7 @@ /* * Copyright 2014 A.C.R. Development */ -package acr.browser.lightning.fragment; +package acr.browser.lightning.settings.fragment; import android.app.Activity; import android.app.Dialog; @@ -20,8 +20,11 @@ import com.anthonycr.bonsai.CompletableSubscriber; import com.anthonycr.bonsai.Schedulers; +import javax.inject.Inject; + import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; +import acr.browser.lightning.database.history.HistoryModel; import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.utils.Utils; import acr.browser.lightning.utils.WebUtils; @@ -45,6 +48,8 @@ public class PrivacySettingsFragment extends LightningPreferenceFragment impleme private Activity mActivity; + @Inject HistoryModel mHistoryModel; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -187,7 +192,8 @@ private Completable clearHistory() { public void onSubscribe(@NonNull CompletableSubscriber subscriber) { Activity activity = getActivity(); if (activity != null) { - WebUtils.clearHistory(activity); + // TODO: 6/9/17 clearHistory is not synchronous + WebUtils.clearHistory(activity, mHistoryModel); subscriber.onComplete(); } subscriber.onError(new RuntimeException("Activity was null in clearHistory")); diff --git a/app/src/main/java/acr/browser/lightning/utils/DrawableUtils.java b/app/src/main/java/acr/browser/lightning/utils/DrawableUtils.java index 19e426cf4..259ce15d3 100644 --- a/app/src/main/java/acr/browser/lightning/utils/DrawableUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/DrawableUtils.java @@ -1,7 +1,9 @@ package acr.browser.lightning.utils; +import android.app.Application; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; @@ -9,12 +11,17 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Build; +import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; import android.view.View; +import acr.browser.lightning.R; + public class DrawableUtils { + @NonNull public static Bitmap getRoundedNumberImage(int number, int width, int height, int color, int thickness) { String text; @@ -56,6 +63,68 @@ public static Bitmap getRoundedNumberImage(int number, int width, int height, in return image; } + /** + * Creates a rounded square of a certain color with + * a character imprinted in white on it. + * + * @param character the character to write on the image. + * @param width the width of the final image. + * @param height the height of the final image. + * @param color the background color of the rounded square. + * @return a valid bitmap of a rounded square with a character on it. + */ + @NonNull + public static Bitmap getRoundedLetterImage(@NonNull Character character, int width, int height, int color) { + 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); + + int xPos = (canvas.getWidth() / 2); + int yPos = (int) ((canvas.getHeight() / 2) - ((paint.descent() + paint.ascent()) / 2)); + + paint.setColor(Color.WHITE); + canvas.drawText(character.toString(), xPos, yPos, paint); + + return image; + } + + /** + * Hashes a character to one of four colors: + * blue, green, red, or orange. + * + * @param character the character to hash. + * @param app the application needed to get the color. + * @return one of the above colors, or black something goes wrong. + */ + @ColorInt + public static int characterToColorHash(@NonNull Character character, @NonNull Application app) { + int smallHash = Character.getNumericValue(character) % 4; + switch (Math.abs(smallHash)) { + case 0: + return ContextCompat.getColor(app, R.color.bookmark_default_blue); + case 1: + return ContextCompat.getColor(app, R.color.bookmark_default_green); + case 2: + return ContextCompat.getColor(app, R.color.bookmark_default_red); + case 3: + return ContextCompat.getColor(app, R.color.bookmark_default_orange); + default: + return Color.BLACK; + } + } + public static int mixColor(float fraction, int startValue, int endValue) { int startA = (startValue >> 24) & 0xff; diff --git a/app/src/main/java/acr/browser/lightning/utils/FileUtils.java b/app/src/main/java/acr/browser/lightning/utils/FileUtils.java index 0ac81358c..d272c4352 100644 --- a/app/src/main/java/acr/browser/lightning/utils/FileUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/FileUtils.java @@ -28,6 +28,9 @@ public class FileUtils { private static final String TAG = "FileUtils"; + public static final String DEFAULT_DOWNLOAD_PATH = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(); + /** * Writes a bundle to persistent storage in the files directory * using the specified file name. This method is a blocking @@ -160,4 +163,87 @@ public static long megabytesToBytes(long megaBytes) { return megaBytes * 1024 * 1024; } + /** + * Determine whether there is write access in the given directory. Returns false if a + * file cannot be created in the directory or if the directory does not exist. + * + * @param directory the directory to check for write access + * @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(@Nullable String directory) { + if (directory == null || directory.isEmpty()) { + return false; + } + + final String sFileName = "test"; + final String sFileExtension = ".txt"; + String dir = addNecessarySlashes(directory); + dir = getFirstRealParentDirectory(dir); + File file = new File(dir + sFileName + sFileExtension); + for (int n = 0; n < 100; n++) { + if (!file.exists()) { + try { + if (file.createNewFile()) { + //noinspection ResultOfMethodCallIgnored + file.delete(); + } + return true; + } catch (IOException ignored) { + return false; + } + } else { + file = new File(dir + sFileName + '-' + n + sFileExtension); + } + } + return file.canWrite(); + } + + /** + * Returns the first parent directory of a directory that exists. This is useful + * for subdirectories that do not exist but their parents do. + * + * @param directory the directory to find the first existent parent + * @return the first existent parent + */ + @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 directory; + } + } + } + + @NonNull + public static String addNecessarySlashes(@Nullable String originalPath) { + if (originalPath == null || originalPath.length() == 0) { + return "/"; + } + if (originalPath.charAt(originalPath.length() - 1) != '/') { + originalPath = originalPath + '/'; + } + if (originalPath.charAt(0) != '/') { + originalPath = '/' + originalPath; + } + return originalPath; + } } 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 7f953f4eb..1e36c861a 100644 --- a/app/src/main/java/acr/browser/lightning/utils/IntentUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/IntentUtils.java @@ -1,14 +1,12 @@ package acr.browser.lightning.utils; import android.app.Activity; -import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Build; -import android.os.FileUriExposedException; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; diff --git a/app/src/main/java/acr/browser/lightning/utils/ProxyUtils.java b/app/src/main/java/acr/browser/lightning/utils/ProxyUtils.java index 9c7e8daf0..dada3780e 100644 --- a/app/src/main/java/acr/browser/lightning/utils/ProxyUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/ProxyUtils.java @@ -13,7 +13,7 @@ import javax.inject.Singleton; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.preference.PreferenceManager; diff --git a/app/src/main/java/acr/browser/lightning/utils/ResourceUtils.java b/app/src/main/java/acr/browser/lightning/utils/ResourceUtils.java index e2e85c894..5892e5cbd 100644 --- a/app/src/main/java/acr/browser/lightning/utils/ResourceUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/ResourceUtils.java @@ -4,11 +4,21 @@ import android.support.annotation.DimenRes; import android.support.annotation.NonNull; +/** + * Utils related to resources. + */ public final class ResourceUtils { private ResourceUtils() {} + /** + * Returns the dimension in pixels. + * + * @param context the context needed to get the dimension. + * @param res the resource to get. + * @return the dimension value in pixels. + */ public static int dimen(@NonNull Context context, @DimenRes int res) { - return Math.round(context.getResources().getDimension(res)); + return context.getResources().getDimensionPixelSize(res); } } diff --git a/app/src/main/java/acr/browser/lightning/utils/StringBuilderUtils.java b/app/src/main/java/acr/browser/lightning/utils/StringBuilderUtils.java index 3b2e56530..09ef24fe9 100644 --- a/app/src/main/java/acr/browser/lightning/utils/StringBuilderUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/StringBuilderUtils.java @@ -14,16 +14,16 @@ public class StringBuilderUtils { private static final String EMPTY = ""; /** - * Replace a string in a string - * builder with another string. + * Replace the first string found in a + * string builder with another string. * * @param stringBuilder the string builder. * @param toReplace the string to replace. * @param replacement the replacement string. */ public static void replace(@NonNull StringBuilder stringBuilder, - @NonNull String toReplace, - @NonNull String replacement) { + @NonNull String toReplace, + @NonNull String replacement) { int index = stringBuilder.indexOf(toReplace); if (index >= 0) { stringBuilder.replace(index, index + toReplace.length(), replacement); @@ -42,7 +42,7 @@ public static void trim(@NonNull StringBuilder stringBuilder) { stringBuilder.replace(0, 1, EMPTY); } - while (stringBuilder.lastIndexOf(SPACE) == (stringBuilder.length() - 1)) { + while (stringBuilder.lastIndexOf(SPACE) == (stringBuilder.length() - 1) && stringBuilder.length() > 0) { stringBuilder.replace(stringBuilder.length() - 1, stringBuilder.length(), EMPTY); } } 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 b626582aa..708664054 100644 --- a/app/src/main/java/acr/browser/lightning/utils/Utils.java +++ b/app/src/main/java/acr/browser/lightning/utils/Utils.java @@ -3,7 +3,6 @@ */ package acr.browser.lightning.utils; -import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; @@ -36,9 +35,6 @@ import android.webkit.URLUtil; import android.widget.Toast; -import com.anthonycr.grant.PermissionsManager; -import com.anthonycr.grant.PermissionsResultAction; - import java.io.Closeable; import java.io.File; import java.io.IOException; @@ -48,12 +44,10 @@ import java.util.Date; import acr.browser.lightning.R; -import acr.browser.lightning.activity.MainActivity; +import acr.browser.lightning.MainActivity; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.dialog.BrowserDialog; -import acr.browser.lightning.download.DownloadHandler; -import acr.browser.lightning.preference.PreferenceManager; public final class Utils { @@ -63,35 +57,6 @@ 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(@NonNull final Activity activity, @NonNull final PreferenceManager manager, final String url, - final String userAgent, final String contentDisposition) { - 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(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 @@ -211,7 +176,7 @@ public static int dpToPx(float dp) { public static String getDomainName(@Nullable String url) { if (url == null || url.isEmpty()) return ""; - boolean ssl = url.startsWith(Constants.HTTPS); + boolean ssl = URLUtil.isHttpsUrl(url); int index = url.indexOf('/', 8); if (index != -1) { url = url.substring(0, index); 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 2ae62c605..7b7825cb4 100644 --- a/app/src/main/java/acr/browser/lightning/utils/WebUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/WebUtils.java @@ -36,8 +36,8 @@ public static void clearWebStorage() { WebStorage.getInstance().deleteAllData(); } - public static void clearHistory(@NonNull Context context) { - HistoryModel.deleteHistory() + public static void clearHistory(@NonNull Context context, @NonNull HistoryModel historyModel) { + historyModel.deleteHistory() .subscribeOn(Schedulers.io()) .subscribe(); WebViewDatabase m = WebViewDatabase.getInstance(context); diff --git a/app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java b/app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java index 80f40bc06..a17803ca4 100644 --- a/app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java +++ b/app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java @@ -25,7 +25,7 @@ import javax.inject.Inject; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.controller.UIController; import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.favicon.FaviconModel; @@ -73,15 +73,9 @@ private void cacheFavicon(@Nullable final String url, @Nullable final Bitmap ico return; } - Uri uri = Uri.parse(url); - - if (uri.getHost() == null) { - return; - } - mFaviconModel.cacheFaviconForUrl(icon, url) - .subscribeOn(Schedulers.io()) - .subscribe(); + .subscribeOn(Schedulers.io()) + .subscribe(); } @@ -114,21 +108,21 @@ public void onGranted() { 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); - } - }); + .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(); BrowserDialog.setDialogSize(mActivity, alert); 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 8532deae4..bbc4439b6 100644 --- a/app/src/main/java/acr/browser/lightning/view/LightningView.java +++ b/app/src/main/java/acr/browser/lightning/view/LightningView.java @@ -44,11 +44,10 @@ import javax.inject.Inject; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.DownloadsPage; -import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.constant.StartPage; import acr.browser.lightning.controller.UIController; import acr.browser.lightning.dialog.LightningDialogBuilder; @@ -106,7 +105,7 @@ public class LightningView { @NonNull private final Map mRequestHeaders = new ArrayMap<>(); @Inject PreferenceManager mPreferences; - @Inject LightningDialogBuilder mBookmarksDialogBuilder; + @Inject LightningDialogBuilder mDialogBuilder; @Inject ProxyUtils mProxyUtils; public LightningView(@NonNull Activity activity, @Nullable String url, boolean isIncognito) { @@ -250,15 +249,15 @@ public void onItem(@Nullable String item) { */ public void loadDownloadspage() { new DownloadsPage().getDownloadsPage() - .subscribeOn(Schedulers.io()) - .observeOn(Schedulers.main()) - .subscribe(new SingleOnSubscribe() { - @Override - public void onItem(@Nullable String item) { - Preconditions.checkNonNull(item); - loadUrl(item); - } - }); + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.main()) + .subscribe(new SingleOnSubscribe() { + @Override + public void onItem(@Nullable String item) { + Preconditions.checkNonNull(item); + loadUrl(item); + } + }); } /** @@ -989,45 +988,45 @@ private void longClickPage(@Nullable final String url) { final WebView.HitTestResult result = mWebView.getHitTestResult(); String currentUrl = mWebView.getUrl(); if (currentUrl != null && UrlUtils.isSpecialUrl(currentUrl)) { - if (currentUrl.endsWith(HistoryPage.FILENAME)) { + if (UrlUtils.isHistoryUrl(currentUrl)) { if (url != null) { - mBookmarksDialogBuilder.showLongPressedHistoryLinkDialog(mActivity, mUIController, url); + mDialogBuilder.showLongPressedHistoryLinkDialog(mActivity, mUIController, url); } else if (result != null && result.getExtra() != null) { final String newUrl = result.getExtra(); - mBookmarksDialogBuilder.showLongPressedHistoryLinkDialog(mActivity, mUIController, newUrl); + mDialogBuilder.showLongPressedHistoryLinkDialog(mActivity, mUIController, newUrl); } - } else if (currentUrl.endsWith(BookmarkPage.FILENAME)) { + } else if (UrlUtils.isBookmarkUrl(currentUrl)) { if (url != null) { - mBookmarksDialogBuilder.showLongPressedDialogForBookmarkUrl(mActivity, mUIController, url); + mDialogBuilder.showLongPressedDialogForBookmarkUrl(mActivity, mUIController, url); } else if (result != null && result.getExtra() != null) { final String newUrl = result.getExtra(); - mBookmarksDialogBuilder.showLongPressedDialogForBookmarkUrl(mActivity, mUIController, newUrl); + mDialogBuilder.showLongPressedDialogForBookmarkUrl(mActivity, mUIController, newUrl); } - } else if (currentUrl.endsWith(DownloadsPage.FILENAME)) { + } else if (UrlUtils.isDownloadsUrl(currentUrl)) { if (url != null) { - mBookmarksDialogBuilder.showLongPressedDialogForDownloadUrl(mActivity, mUIController, url); + mDialogBuilder.showLongPressedDialogForDownloadUrl(mActivity, mUIController, url); } else if (result != null && result.getExtra() != null) { final String newUrl = result.getExtra(); - mBookmarksDialogBuilder.showLongPressedDialogForDownloadUrl(mActivity, mUIController, newUrl); + mDialogBuilder.showLongPressedDialogForDownloadUrl(mActivity, mUIController, 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, mUIController, url, getUserAgent()); + mDialogBuilder.showLongPressImageDialog(mActivity, mUIController, url, getUserAgent()); } else { - mBookmarksDialogBuilder.showLongPressLinkDialog(mActivity, mUIController, url); + mDialogBuilder.showLongPressLinkDialog(mActivity, mUIController, url); } } else { - mBookmarksDialogBuilder.showLongPressLinkDialog(mActivity, mUIController, url); + mDialogBuilder.showLongPressLinkDialog(mActivity, mUIController, 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, mUIController, newUrl, getUserAgent()); + mDialogBuilder.showLongPressImageDialog(mActivity, mUIController, newUrl, getUserAgent()); } else { - mBookmarksDialogBuilder.showLongPressLinkDialog(mActivity, mUIController, newUrl); + mDialogBuilder.showLongPressLinkDialog(mActivity, mUIController, newUrl); } } } diff --git a/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java b/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java index 8e3f4bb25..db5e40109 100644 --- a/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java +++ b/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java @@ -22,6 +22,7 @@ import android.webkit.HttpAuthHandler; import android.webkit.MimeTypeMap; import android.webkit.SslErrorHandler; +import android.webkit.URLUtil; import android.webkit.ValueCallback; import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; @@ -41,14 +42,15 @@ import acr.browser.lightning.BuildConfig; import acr.browser.lightning.R; -import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.BrowserApp; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.controller.UIController; import acr.browser.lightning.dialog.BrowserDialog; -import acr.browser.lightning.utils.AdBlock; +import acr.browser.lightning.adblock.AdBlock; import acr.browser.lightning.utils.IntentUtils; import acr.browser.lightning.utils.Preconditions; import acr.browser.lightning.utils.ProxyUtils; +import acr.browser.lightning.utils.UrlUtils; import acr.browser.lightning.utils.Utils; public class LightningWebClient extends WebViewClient { @@ -100,7 +102,7 @@ public WebResourceResponse shouldInterceptRequest(WebView view, String url) { @Override public void onPageFinished(@NonNull WebView view, String url) { if (view.isShown()) { - mUIController.updateUrl(url, true); + mUIController.updateUrl(url, false); mUIController.setBackButtonEnabled(view.canGoBack()); mUIController.setForwardButtonEnabled(view.canGoForward()); view.postInvalidate(); @@ -121,7 +123,7 @@ public void onPageFinished(@NonNull WebView view, String url) { public void onPageStarted(WebView view, String url, Bitmap favicon) { mLightningView.getTitleInfo().setFavicon(null); if (mLightningView.isShown()) { - mUIController.updateUrl(url, false); + mUIController.updateUrl(url, true); mUIController.showActionBar(); } mUIController.tabChanged(mLightningView); @@ -135,9 +137,9 @@ public void onReceivedHttpAuthRequest(final WebView view, @NonNull final HttpAut View dialogView = LayoutInflater.from(mActivity).inflate(R.layout.dialog_auth_request, null); - final TextView realmLabel = (TextView) dialogView.findViewById(R.id.auth_request_realm_textview); - final EditText name = (EditText) dialogView.findViewById(R.id.auth_request_username_edittext); - final EditText password = (EditText) dialogView.findViewById(R.id.auth_request_password_edittext); + final TextView realmLabel = dialogView.findViewById(R.id.auth_request_realm_textview); + final EditText name = dialogView.findViewById(R.id.auth_request_username_edittext); + final EditText password = dialogView.findViewById(R.id.auth_request_password_edittext); realmLabel.setText(mActivity.getString(R.string.label_realm, realm)); @@ -304,7 +306,7 @@ private boolean shouldOverrideLoading(@NonNull WebView view, @NonNull String url // If we are in incognito, immediately load, we don't want the url to leave the app return continueLoadingUrl(view, url, headers); } - if (url.startsWith(Constants.ABOUT)) { + if (URLUtil.isAboutUrl(url)) { // If this is an about page, immediately load, we don't need to leave the app return continueLoadingUrl(view, url, headers); } @@ -359,15 +361,14 @@ private boolean isMailOrIntent(@NonNull String url, @NonNull WebView view) { } return true; } - } else if (url.startsWith(Constants.FILE)) { + } else if (URLUtil.isFileUrl(url) && !UrlUtils.isSpecialUrl(url)) { File file = new File(url.replace(Constants.FILE, "")); if (file.exists()) { String newMimeType = MimeTypeMap.getSingleton() - .getMimeTypeFromExtension(Utils.guessFileExtension(file.toString())); + .getMimeTypeFromExtension(Utils.guessFileExtension(file.toString())); - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_VIEW); + Intent intent = new Intent(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri = FileProvider.getUriForFile(mActivity, BuildConfig.APPLICATION_ID + ".fileprovider", file); intent.setDataAndType(contentUri, newMimeType); @@ -377,8 +378,10 @@ private boolean isMailOrIntent(@NonNull String url, @NonNull WebView view) { } catch (Exception e) { System.out.println("LightningWebClient: cannot open downloaded file"); } - return true; + } else { + Utils.showSnackbar(mActivity, R.string.message_open_download_fail); } + return true; } return false; } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 2fa0d6229..be7042f3c 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,7 +5,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".activity.BrowserActivity"> + tools:context=".browser.activity.BrowserActivity"> Maximale Reiteranzahl erreicht! Text in Zwischenablage kopiert Link in Zwischenablage kopiert - Benutzerdefinierte URL + Benutzerdefinierte URL Lokale Datei wurde beim Laden blockiert Open Source-Lizenzen Suche nach @@ -160,7 +160,7 @@ Exportiere Datensicherung Importiere Lesezeichen aus Datensicherung Lesezeichen exportiert nach - Lesezeichen Einstellungen + Lesezeichen Lesezeichen konnten nicht importiert werden Wähle Datei aus Allgemein @@ -246,4 +246,5 @@ Host: Quelle für Werbeblocker Anzeige + Diese Datei existiert nicht mehr diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 42e1d345e..aaa3dbef1 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -103,7 +103,7 @@ Μέγιστος αριθμός καρτελών ξεπεράστηκε Το κείμενο αντιγράφτηκε Ο σύνδεσμος αντιγράφτηκε - Ειδικό URL + Ειδικό URL Τοπικά αρχεία εχουν μπλοκαριστεί απο την φόρτωση Άδειες ανοιχτού κώδικα Αναζήτηση για diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 581cf5cfb..5498448db 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -103,7 +103,7 @@ Se ha alcanzado el máximo de pestañas El texto se ha copiado al portapapeles El enlace se ha copiado al portapapeles - Dirección personalizada + Dirección personalizada Se ha bloqueado la carga del archivo local Licencias de código abierto (open source) Buscar diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2ca2c6775..b48d517cd 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -125,7 +125,7 @@ Onglets maximuns atteint Texte copié dans le presse-papiers Adresse copiée dans le presse-papiers - Adresse personnalisée + Adresse personnalisée Le fichier local a été bloqué au chargement Licences open source Recherche diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 49197e5fe..73e5026ed 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -124,7 +124,7 @@ Maximálisan megnyitható lapok száma elérve Szöveg másolva a vágólapra Link másolva a vágólapra - Egyéni URL + Egyéni URL Helyi fájl betöltése blokkolva Nyílt forráskódú licencek Keresés diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 5683968a0..5e5def16e 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -124,7 +124,7 @@ Hai raggiunto il numero massimo di schede apribili Testo copiato negli appunti Link copiato negli appunti - URL personalizzato + URL personalizzato Caricamento file locale bloccato Licenze Open Source Cerca @@ -237,7 +237,7 @@ Scorciatoia aggiunta Cancella tutti i segnalibri Inverti pannello segnalibri con schede - Flash Player non è supportato dal tuo sistema! + Flash Player non è supportato dal tuo sistema FAQ Domande più frequenti diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 59bca81af..06edf9603 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1,24 +1,12 @@ - - - + Lightning 新しいタブ 共有 履歴 ブックマーク + ダウンロード ブックマークに追加 ページの URL をコピー 進む @@ -70,6 +58,7 @@ 検索エンジン OK このファイルをダウンロードしますか? (%1$s) + サイズ不明 キャンセル 警告 Adobe Flash Player が見つかりません\nFlash Player をインストールしてください @@ -121,7 +110,7 @@ これ以上タブは開けません テキストをクリップボードにコピーしました リンクをクリップボードにコピーしました - カスタム URL + カスタム URL ファイルを読み込めませんでした オープンソース ライセンス 検索 : @@ -139,11 +128,13 @@ 許可する 許可しない サインイン + サーバー メッセージ: %s ユーザー名 パスワード 検索候補表示 Google を使用する DuckDuckGo を使用する + Baidu を使用する 検索候補を表示しない HTTP プロキシ @@ -185,6 +176,7 @@ ブックマークをエクスポートする ブックマークをインポートする ブックマークをエクスポートしました + ブックマークのエクスポートに失敗しました! ブックマーク ブックマークのインポートに失敗しました ファイルを選択してください @@ -213,6 +205,7 @@ 色を反転する ダーク + ステータスバーを暗色にする タブ アプリのテーマ ライト @@ -234,6 +227,9 @@ ホーム画面に追加 ホーム画面にショートカットを追加しました すべてのブックマークを削除する + このシステムは Flash Player をサポートしていません + 広告をブロックするには Lightning Plus へアップグレードしてください + このファイルは存在しません FAQ よくある質問と回答 @@ -255,4 +251,6 @@ フォルダ名を変更する フォルダを削除する タブの消去 + ダウンロードの削除 + ダウンロードの全削除 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index b1c7371c3..05193e10e 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -102,7 +102,7 @@ 최대 탭 수에 도달했습니다 텍스트가 클립보드로 복사되었습니다 링크가 클립보드로 복사되었습니다 - 임의의 URL + 임의의 URL 로컬 파일 불러오기가 차단되었습니다 오픈 소스 라이센스 다음을 검색 diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 1220d29f5..ee20abd68 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -124,7 +124,7 @@ Maximum aantal tabbladen bereikt Tekst gekopieerd naar klembord Link gekopieerd naar klembord - Aangepaste URL + Aangepaste URL Lokaal bestand werd geblokkeerd Open Source Licenses Zoek voor diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 16fc7bcd1..f97ab389d 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -105,7 +105,7 @@ Maksymalna liczba otwartych kart osiągnięta Tekst został skopiowany do schowka Link został skopiowany do schowka - Własny URL + Własny URL Wczytywanie lokalnego pliku zostało zablokowane Licencje Open Source Wyszukaj diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index e19b5d957..0a4976bd6 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -118,7 +118,7 @@ Atingido número máximo de abas Texto copiado para a área de transferência Link copiado para a área de transferência - URL personalizado + URL personalizado Bloqueado o carregamento do arquivo local Licenças Open Source Pesquisar por diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index ba4356986..42d1c3552 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -123,7 +123,7 @@ Atingido o número máximo de separadores Texto copiado para a área de transferência Ligação copiada para a área de transferência - URL personalizado + URL personalizado Bloqueado o carregamento do ficheiro local Licenças Open Source Pesquisar por diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index c55010b57..dc0d4539a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -107,7 +107,7 @@ Достигнуто максимальное число вкладок Текст скопирован в буфер обмена Ссылка скопирована в буфер обмена - Пользовательский URL + Пользовательский URL Локальный файл недоступен для загрузки Open Source Licenses Искать diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index f4324a1bf..1206ed3e2 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -124,7 +124,7 @@ Досегнут макс. број језичака Текст копиран на клипборд Веза копирана на клипборд - Посебна адреса + Посебна адреса Учитавање локалног фајла је блокирано Лиценце отвореног кôда Тражи diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index a70a21b10..d550bf1e9 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -108,7 +108,7 @@ Maksimum sekme sınırına ulaşıldı Yazı panoya kopyalandı Bağlantı panoya kopyalandı - Kişisel bağlantı + Kişisel bağlantı Yerel dosyanın yüklenmesi engellendi Açık Kaynak Lisansları Ara diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index a947e3f4e..943dc617e 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -102,7 +102,7 @@ 达到最大标签数 文本已复制到剪贴板 链接已复制到剪贴板 - 自定义 URL + 自定义 URL 本地文件已被阻止加载 开源许可 搜索目标 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 3961f2a44..5cb683cad 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -102,7 +102,7 @@ 達到最大標籤數 文本已複製到剪貼板 鏈接已複製到剪貼板 - 自定義 URL + 自定義 URL 本地檔已被阻止加載 開源許可 搜索目標 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 1e21279d6..e1815f150 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -37,4 +37,9 @@ #F44336 + #2196F3 + #4CAF50 + #EF5350 + #FF9800 + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 5059ea60d..d40476a6e 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -10,4 +10,6 @@ 8dp 16dp + 24dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cda38c490..2e8b2a73b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -110,7 +110,6 @@ Max tabs reached Text copied to clipboard Link copied to clipboard - Custom URL Local file has been blocked from loading Open Source Licenses Search for @@ -227,7 +226,9 @@ Add to home screen Shortcut added to home screen Delete all bookmarks - Flash Player is not supported by your system! + Flash Player is not supported by your system + Upgrade to Lightning Plus to block ads + This file doesn\'t exist anymore FAQ Frequently asked questions @@ -251,4 +252,17 @@ Close browser Delete download Delete all downloads + + + Custom URL + Google + Ask + Bing + Yahoo + StartPage + StartPage Mobile + DuckDuckGo + DuckDuckGo Lite + Baidu + Yandex diff --git a/app/src/main/res/xml/preferences_headers.xml b/app/src/main/res/xml/preferences_headers.xml index ebbae4163..977807264 100644 --- a/app/src/main/res/xml/preferences_headers.xml +++ b/app/src/main/res/xml/preferences_headers.xml @@ -1,22 +1,22 @@
\ No newline at end of file diff --git a/app/src/test/java/acr/browser/lightning/utils/PreconditionsTest.java b/app/src/test/java/acr/browser/lightning/utils/PreconditionsTest.java new file mode 100644 index 000000000..af4e75119 --- /dev/null +++ b/app/src/test/java/acr/browser/lightning/utils/PreconditionsTest.java @@ -0,0 +1,19 @@ +package acr.browser.lightning.utils; + +import org.junit.Test; + +/** + * Unit tests for {@link Preconditions}. + */ +public class PreconditionsTest { + + @Test(expected = RuntimeException.class) + public void checkNonNull_Null_ThrowsException() { + Preconditions.checkNonNull(null); + } + + @Test + public void checkNonNull_NonNull_Succeeds() { + Preconditions.checkNonNull(new Object()); + } +} \ No newline at end of file diff --git a/app/src/test/java/acr/browser/lightning/utils/StringBuilderUtilsTest.java b/app/src/test/java/acr/browser/lightning/utils/StringBuilderUtilsTest.java new file mode 100644 index 000000000..22961626e --- /dev/null +++ b/app/src/test/java/acr/browser/lightning/utils/StringBuilderUtilsTest.java @@ -0,0 +1,118 @@ +package acr.browser.lightning.utils; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Tests for {@link StringBuilderUtils}. + */ +public class StringBuilderUtilsTest { + + @Test + public void replace_SingleCharacter() { + StringBuilder stringBuilder = new StringBuilder("abbbaacccda"); + StringBuilderUtils.replace(stringBuilder, "a", ""); + Assert.assertEquals(stringBuilder.toString(), "bbbaacccda"); + } + + @Test + public void replace_MultipleCharacters() { + StringBuilder stringBuilder = new StringBuilder("___ab___cd___"); + StringBuilderUtils.replace(stringBuilder, "___", "---"); + Assert.assertEquals(stringBuilder.toString(), "---ab___cd___"); + } + + @Test + public void trim_LeadingAndTrailingSpaces() { + StringBuilder stringBuilder = new StringBuilder(" t e s t "); + StringBuilderUtils.trim(stringBuilder); + Assert.assertEquals(stringBuilder.toString(), "t e s t"); + } + + @Test + public void trim_OnlyContainsSpaces() { + StringBuilder stringBuilder = new StringBuilder(" "); + Assert.assertFalse(stringBuilder.toString().isEmpty()); + StringBuilderUtils.trim(stringBuilder); + Assert.assertTrue(stringBuilder.toString().isEmpty()); + } + + @Test + public void isEmpty_HasNoCharacters() { + StringBuilder stringBuilder = new StringBuilder(""); + Assert.assertTrue(StringBuilderUtils.isEmpty(stringBuilder)); + } + + @Test + public void isEmpty_HasCharacters() { + // Case with normal letters + StringBuilder stringBuilder = new StringBuilder("abcdefg"); + Assert.assertFalse(StringBuilderUtils.isEmpty(stringBuilder)); + + // Case with empty spaces + StringBuilder stringBuilder1 = new StringBuilder(" "); + Assert.assertFalse(StringBuilderUtils.isEmpty(stringBuilder1)); + } + + @Test + public void startsWith_SingleCharacter() { + StringBuilder stringBuilder = new StringBuilder("1234567890"); + Assert.assertTrue(StringBuilderUtils.startsWith(stringBuilder, "1")); + Assert.assertFalse(StringBuilderUtils.startsWith(stringBuilder, "2")); + } + + @Test + public void startsWith_MultipleCharacters() { + StringBuilder stringBuilder = new StringBuilder("1234567890"); + Assert.assertTrue(StringBuilderUtils.startsWith(stringBuilder, "12345")); + Assert.assertFalse(StringBuilderUtils.startsWith(stringBuilder, "23456")); + } + + @Test + public void contains_SingleCharacter() { + StringBuilder stringBuilder = new StringBuilder("abcdefg123456"); + + // This character is not in the string + Assert.assertFalse(StringBuilderUtils.contains(stringBuilder, "z")); + + // All these characters are in the string + for (int n = 0; n < stringBuilder.length(); n++) { + Assert.assertTrue(StringBuilderUtils.contains(stringBuilder, String.valueOf(stringBuilder.charAt(n)))); + } + } + + @Test + public void contains_MultipleCharacters() { + StringBuilder stringBuilder = new StringBuilder("abcdefg123456"); + + // Should return false since characters are in reverse order + Assert.assertFalse(StringBuilderUtils.contains(stringBuilder, "cba")); + + // Should return false since characters are not in string + Assert.assertFalse(StringBuilderUtils.contains(stringBuilder, "zyx")); + } + + @Test + public void equals_SameCharacters() { + StringBuilder stringBuilder = new StringBuilder("abcdefg"); + + Assert.assertTrue(StringBuilderUtils.equals(stringBuilder, "abcdefg")); + } + + @Test + public void equals_DifferentCharacters() { + StringBuilder stringBuilder = new StringBuilder("abcdefg"); + + Assert.assertFalse(StringBuilderUtils.equals(stringBuilder, "abcdefg1")); + } + + @Test + public void substring() { + StringBuilder stringBuilder = new StringBuilder("abcdefg"); + + String string = "abcdefg"; + + Assert.assertEquals(StringBuilderUtils.substring(stringBuilder, 1, 5).toString(), string.substring(1, 5)); + } + +} \ No newline at end of file diff --git a/app/src/test/java/acr/browser/lightning/utils/SubscriptionUtilsTest.java b/app/src/test/java/acr/browser/lightning/utils/SubscriptionUtilsTest.java new file mode 100644 index 000000000..7f2323216 --- /dev/null +++ b/app/src/test/java/acr/browser/lightning/utils/SubscriptionUtilsTest.java @@ -0,0 +1,41 @@ +package acr.browser.lightning.utils; + +import com.anthonycr.bonsai.Subscription; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for {@link SubscriptionUtils}. + */ +public class SubscriptionUtilsTest { + + @Test + public void safeUnsubscribe_NullSubscription_Succeeds() { + SubscriptionUtils.safeUnsubscribe(null); + } + + @Test + public void safeUnsubscribe_NonNullSubscription_SuccessfullyUnsubscribes() { + Subscription subscription = new Subscription() { + + boolean isUnsubscribed = false; + + @Override + public void unsubscribe() { + isUnsubscribed = true; + } + + @Override + public boolean isUnsubscribed() { + return isUnsubscribed; + } + }; + + Assert.assertFalse(subscription.isUnsubscribed()); + + SubscriptionUtils.safeUnsubscribe(subscription); + + Assert.assertTrue(subscription.isUnsubscribed()); + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 1325d31f3..cda737581 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,12 @@ buildscript { + ext.kotlin_version = '1.1.3' repositories { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' + classpath 'com.android.tools.build:gradle:2.3.3' classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.6.3' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -12,15 +14,16 @@ allprojects { repositories { jcenter() mavenCentral() + maven { url "https://maven.google.com" } } } ext { minSdkVersion = 14 - targetSdkVersion = 25 - buildToolsVersion = '25.0.2' + targetSdkVersion = 26 + buildToolsVersion = '26.0.0' - versionName = '4.5.0' - versionCode_lite = 97 - versionCode_plus = 95 + versionName = '4.5.1' + versionCode_lite = 98 + versionCode_plus = 96 }