Browse Source

Merge pull request #597 from anthonycr/dev

Version 4.5.1
master v4.5.1
Anthony Restaino 8 years ago committed by GitHub
parent
commit
e00bb88366
  1. 7
      .travis.yml
  2. 12
      CHANGELOG.md
  3. 11
      README.md
  4. 15
      app/build.gradle
  5. 121
      app/src/LightningPlus/assets/hosts.txt
  6. 18
      app/src/main/AndroidManifest.xml
  7. 8
      app/src/main/java/acr/browser/lightning/BrowserApp.java
  8. 4
      app/src/main/java/acr/browser/lightning/IncognitoActivity.java
  9. 22
      app/src/main/java/acr/browser/lightning/MainActivity.java
  10. 4
      app/src/main/java/acr/browser/lightning/activity/AppCompatPreferenceActivity.java
  11. 79
      app/src/main/java/acr/browser/lightning/adblock/AdBlock.java
  12. 29
      app/src/main/java/acr/browser/lightning/browser/BrowserPresenter.java
  13. 5
      app/src/main/java/acr/browser/lightning/browser/BrowserView.java
  14. 71
      app/src/main/java/acr/browser/lightning/browser/SearchBoxModel.java
  15. 26
      app/src/main/java/acr/browser/lightning/browser/TabsManager.java
  16. 225
      app/src/main/java/acr/browser/lightning/browser/activity/BrowserActivity.java
  17. 7
      app/src/main/java/acr/browser/lightning/browser/activity/ThemableBrowserActivity.java
  18. 2
      app/src/main/java/acr/browser/lightning/browser/bookmark/BookmarkUiModel.java
  19. 18
      app/src/main/java/acr/browser/lightning/browser/fragment/BookmarksFragment.java
  20. 24
      app/src/main/java/acr/browser/lightning/browser/fragment/TabsFragment.java
  21. 2
      app/src/main/java/acr/browser/lightning/browser/fragment/anim/HorizontalItemAnimator.java
  22. 2
      app/src/main/java/acr/browser/lightning/browser/fragment/anim/VerticalItemAnimator.java
  23. 151
      app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java
  24. 6
      app/src/main/java/acr/browser/lightning/constant/Constants.java
  25. 31
      app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java
  26. 21
      app/src/main/java/acr/browser/lightning/constant/HistoryPage.java
  27. 133
      app/src/main/java/acr/browser/lightning/constant/StartPage.java
  28. 4
      app/src/main/java/acr/browser/lightning/controller/UIController.java
  29. 44
      app/src/main/java/acr/browser/lightning/database/HistoryItem.java
  30. 113
      app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkDatabase.java
  31. 30
      app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java
  32. 2
      app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java
  33. 165
      app/src/main/java/acr/browser/lightning/database/history/HistoryDatabase.java
  34. 80
      app/src/main/java/acr/browser/lightning/database/history/HistoryModel.java
  35. 38
      app/src/main/java/acr/browser/lightning/di/AppComponent.java
  36. 24
      app/src/main/java/acr/browser/lightning/di/AppModule.java
  37. 6
      app/src/main/java/acr/browser/lightning/dialog/BrowserDialog.java
  38. 34
      app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java
  39. 145
      app/src/main/java/acr/browser/lightning/download/DownloadHandler.java
  40. 19
      app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java
  41. 72
      app/src/main/java/acr/browser/lightning/favicon/FaviconModel.java
  42. 4
      app/src/main/java/acr/browser/lightning/favicon/FaviconUtils.java
  43. 110
      app/src/main/java/acr/browser/lightning/favicon/ImageFetcher.java
  44. 10
      app/src/main/java/acr/browser/lightning/interpolator/BezierDecelerateInterpolator.java
  45. 4
      app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java
  46. 4
      app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java
  47. 6
      app/src/main/java/acr/browser/lightning/reading/activity/ReadingActivity.java
  48. 112
      app/src/main/java/acr/browser/lightning/search/SearchEngineProvider.java
  49. 18
      app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java
  50. 67
      app/src/main/java/acr/browser/lightning/search/SuggestionsManager.java
  51. 46
      app/src/main/java/acr/browser/lightning/search/SuggestionsManager.kt
  52. 13
      app/src/main/java/acr/browser/lightning/search/engine/AskSearch.kt
  53. 15
      app/src/main/java/acr/browser/lightning/search/engine/BaiduSearch.kt
  54. 15
      app/src/main/java/acr/browser/lightning/search/engine/BaseSearchEngine.kt
  55. 16
      app/src/main/java/acr/browser/lightning/search/engine/BingSearch.kt
  56. 12
      app/src/main/java/acr/browser/lightning/search/engine/CustomSearch.kt
  57. 15
      app/src/main/java/acr/browser/lightning/search/engine/DuckLiteSearch.kt
  58. 15
      app/src/main/java/acr/browser/lightning/search/engine/DuckSearch.kt
  59. 15
      app/src/main/java/acr/browser/lightning/search/engine/GoogleSearch.kt
  60. 13
      app/src/main/java/acr/browser/lightning/search/engine/StartPageMobileSearch.kt
  61. 13
      app/src/main/java/acr/browser/lightning/search/engine/StartPageSearch.kt
  62. 16
      app/src/main/java/acr/browser/lightning/search/engine/YahooSearch.kt
  63. 16
      app/src/main/java/acr/browser/lightning/search/engine/YandexSearch.kt
  64. 22
      app/src/main/java/acr/browser/lightning/search/suggestions/BaiduSuggestionsModel.java
  65. 60
      app/src/main/java/acr/browser/lightning/search/suggestions/BaseSuggestionsModel.java
  66. 9
      app/src/main/java/acr/browser/lightning/search/suggestions/DuckSuggestionsModel.java
  67. 13
      app/src/main/java/acr/browser/lightning/search/suggestions/GoogleSuggestionsModel.java
  68. 20
      app/src/main/java/acr/browser/lightning/settings/activity/SettingsActivity.java
  69. 5
      app/src/main/java/acr/browser/lightning/settings/activity/ThemableSettingsActivity.java
  70. 16
      app/src/main/java/acr/browser/lightning/settings/fragment/AboutSettingsFragment.java
  71. 2
      app/src/main/java/acr/browser/lightning/settings/fragment/AdvancedSettingsFragment.java
  72. 12
      app/src/main/java/acr/browser/lightning/settings/fragment/BookmarkSettingsFragment.java
  73. 4
      app/src/main/java/acr/browser/lightning/settings/fragment/DebugSettingsFragment.java
  74. 4
      app/src/main/java/acr/browser/lightning/settings/fragment/DisplaySettingsFragment.java
  75. 123
      app/src/main/java/acr/browser/lightning/settings/fragment/GeneralSettingsFragment.java
  76. 4
      app/src/main/java/acr/browser/lightning/settings/fragment/LightningPreferenceFragment.java
  77. 12
      app/src/main/java/acr/browser/lightning/settings/fragment/PrivacySettingsFragment.java
  78. 69
      app/src/main/java/acr/browser/lightning/utils/DrawableUtils.java
  79. 86
      app/src/main/java/acr/browser/lightning/utils/FileUtils.java
  80. 2
      app/src/main/java/acr/browser/lightning/utils/IntentUtils.java
  81. 2
      app/src/main/java/acr/browser/lightning/utils/ProxyUtils.java
  82. 12
      app/src/main/java/acr/browser/lightning/utils/ResourceUtils.java
  83. 10
      app/src/main/java/acr/browser/lightning/utils/StringBuilderUtils.java
  84. 39
      app/src/main/java/acr/browser/lightning/utils/Utils.java
  85. 4
      app/src/main/java/acr/browser/lightning/utils/WebUtils.java
  86. 42
      app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java
  87. 51
      app/src/main/java/acr/browser/lightning/view/LightningView.java
  88. 29
      app/src/main/java/acr/browser/lightning/view/LightningWebClient.java
  89. 2
      app/src/main/res/layout/activity_main.xml
  90. 4
      app/src/main/res/layout/bookmark_list_item.xml
  91. 8
      app/src/main/res/raw/default_bookmarks.dat
  92. 5
      app/src/main/res/values-de/strings.xml
  93. 2
      app/src/main/res/values-el/strings.xml
  94. 2
      app/src/main/res/values-es/strings.xml
  95. 2
      app/src/main/res/values-fr/strings.xml
  96. 2
      app/src/main/res/values-hu/strings.xml
  97. 4
      app/src/main/res/values-it/strings.xml
  98. 28
      app/src/main/res/values-ja/strings.xml
  99. 2
      app/src/main/res/values-ko/strings.xml
  100. 2
      app/src/main/res/values-nl/strings.xml
  101. Some files were not shown because too many files have changed in this diff Show More

7
.travis.yml

@ -5,8 +5,8 @@ jdk: @@ -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: @@ -18,4 +18,5 @@ before_install:
install:
- ./gradlew
script:
- ./gradlew assembleDebug --stacktrace
- ./gradlew :app:assembleDebug --stacktrace
- ./gradlew :app:test --stacktrace

12
CHANGELOG.md

@ -1,6 +1,18 @@ @@ -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

11
README.md

@ -4,13 +4,10 @@ @@ -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)
[<img src="https://f-droid.org/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/app/acr.browser.lightning) [<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Get it on Google Play" height="80">](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)

15
app/build.gradle

@ -1,13 +1,14 @@ @@ -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 { @@ -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 { @@ -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"
}

121
app/src/LightningPlus/assets/hosts.txt

@ -1033,6 +1033,7 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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
# Hosts: 48015

18
app/src/main/AndroidManifest.xml

@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
android:required="false"/>
<application
android:name=".app.BrowserApp"
android:name=".BrowserApp"
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher"
@ -38,9 +38,9 @@ @@ -38,9 +38,9 @@
android:value="2.1"/>
<activity
android:name=".activity.MainActivity"
android:name=".MainActivity"
android:alwaysRetainTaskState="true"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|keyboardHidden|keyboard"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/Theme.LightTheme"
@ -122,8 +122,8 @@ @@ -122,8 +122,8 @@
</intent-filter>
</activity>
<activity
android:name=".activity.SettingsActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:name=".settings.activity.SettingsActivity"
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|keyboardHidden|keyboard"
android:label="@string/settings"
android:theme="@style/Theme.SettingsTheme">
<intent-filter>
@ -133,9 +133,9 @@ @@ -133,9 +133,9 @@
</intent-filter>
</activity>
<activity
android:name=".activity.IncognitoActivity"
android:name=".IncognitoActivity"
android:alwaysRetainTaskState="true"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|keyboardHidden|keyboard"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/Theme.DarkTheme"
@ -147,8 +147,8 @@ @@ -147,8 +147,8 @@
</intent-filter>
</activity>
<activity
android:name=".activity.ReadingActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:name=".reading.activity.ReadingActivity"
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|keyboardHidden|keyboard"
android:label="@string/reading_mode"
android:theme="@style/Theme.SettingsTheme">
<intent-filter>

8
app/src/main/java/acr/browser/lightning/app/BrowserApp.java → app/src/main/java/acr/browser/lightning/BrowserApp.java

@ -1,4 +1,4 @@ @@ -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 java.util.List; @@ -20,11 +20,13 @@ import java.util.List;
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;

4
app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java → app/src/main/java/acr/browser/lightning/IncognitoActivity.java

@ -1,4 +1,4 @@ @@ -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.Completable; @@ -12,7 +12,7 @@ import com.anthonycr.bonsai.Completable;
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 {

22
app/src/main/java/acr/browser/lightning/activity/MainActivity.java → app/src/main/java/acr/browser/lightning/MainActivity.java

@ -1,9 +1,10 @@ @@ -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.Completable; @@ -12,7 +13,7 @@ import com.anthonycr.bonsai.Completable;
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 class MainActivity extends BrowserActivity { @@ -76,5 +77,22 @@ public class MainActivity extends BrowserActivity {
});
}
@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);
}
}

4
app/src/main/java/acr/browser/lightning/activity/AppCompatPreferenceActivity.java

@ -40,11 +40,11 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity { @@ -40,11 +40,11 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
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);
}

79
app/src/main/java/acr/browser/lightning/utils/AdBlock.java → app/src/main/java/acr/browser/lightning/adblock/AdBlock.java

@ -1,4 +1,4 @@ @@ -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.IOException; @@ -16,7 +16,9 @@ import java.io.IOException;
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 javax.inject.Singleton; @@ -24,6 +26,8 @@ import javax.inject.Singleton;
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 class AdBlock { @@ -132,42 +136,16 @@ public class AdBlock {
String line;
long time = System.currentTimeMillis();
final List<String> 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 class AdBlock { @@ -179,4 +157,41 @@ public class AdBlock {
});
}
private static void parseString(@NonNull StringBuilder lineBuilder, @NonNull List<String> 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());
}
}
}
}
}

29
app/src/main/java/acr/browser/lightning/browser/BrowserPresenter.java

@ -1,11 +1,13 @@ @@ -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 javax.inject.Inject; @@ -14,9 +16,10 @@ import javax.inject.Inject;
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 { @@ -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 @@ public class BrowserPresenter { @@ -116,7 +120,7 @@ public class BrowserPresenter {
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 class BrowserPresenter { @@ -143,6 +147,19 @@ public class BrowserPresenter {
}
@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 class BrowserPresenter { @@ -165,8 +182,8 @@ public class BrowserPresenter {
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 class BrowserPresenter { @@ -232,7 +249,7 @@ public class BrowserPresenter {
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) {

5
app/src/main/java/acr/browser/lightning/browser/BrowserView.java

@ -2,6 +2,7 @@ package acr.browser.lightning.browser; @@ -2,6 +2,7 @@ package acr.browser.lightning.browser;
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 { @@ -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 { @@ -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);

71
app/src/main/java/acr/browser/lightning/browser/SearchBoxModel.java

@ -0,0 +1,71 @@ @@ -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.
* <ul>
* <li>The user's preference to show either the URL, domain, or page title</li>
* <li>Whether or not the current page is loading</li>
* <li>Whether or not the current page is a Lightning generated page.</li>
* </ul>
* 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;
}
}
}
}
}

26
app/src/main/java/acr/browser/lightning/activity/TabsManager.java → app/src/main/java/acr/browser/lightning/browser/TabsManager.java

@ -1,4 +1,4 @@ @@ -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.annotation.Nullable; @@ -12,6 +12,7 @@ import android.support.annotation.Nullable;
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 java.util.List; @@ -30,9 +31,8 @@ import java.util.List;
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 class TabsManager { @@ -174,15 +174,15 @@ public class TabsManager {
});
} else if (UrlUtils.isDownloadsUrl(url)) {
new DownloadsPage().getDownloadsPage()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.main())
.subscribe(new SingleOnSubscribe<String>() {
@Override
public void onItem(@Nullable String item) {
Preconditions.checkNonNull(item);
tab.loadUrl(item);
}
});
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.main())
.subscribe(new SingleOnSubscribe<String>() {
@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 class TabsManager { @@ -214,7 +214,7 @@ public class TabsManager {
@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)

225
app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java → app/src/main/java/acr/browser/lightning/browser/activity/BrowserActivity.java

@ -2,7 +2,7 @@ @@ -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 java.io.IOException; @@ -90,12 +90,15 @@ import java.io.IOException;
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.bookmark.BookmarkModel; @@ -105,11 +108,14 @@ import acr.browser.lightning.database.bookmark.BookmarkModel;
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 @@ -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 @@ -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 abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -327,8 +339,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
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 abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -366,7 +378,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
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 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -443,11 +455,11 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
* @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 abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -508,7 +520,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
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 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -652,45 +664,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
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 abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -749,52 +724,70 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
@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 abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -1111,7 +1104,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
}
@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 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -1219,7 +1212,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
}
}
void handleNewIntent(Intent intent) {
protected void handleNewIntent(Intent intent) {
mPresenter.onNewIntent(intent);
}
@ -1243,14 +1236,14 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -1243,14 +1236,14 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
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 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -1369,7 +1362,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
}
}
void saveOpenTabs() {
protected void saveOpenTabs() {
if (mPreferences.getRestoreLostTabsEnabled()) {
mTabsManager.saveState();
}
@ -1533,36 +1526,16 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -1533,36 +1526,16 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
}
@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 abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -1579,12 +1552,12 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
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 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -1696,7 +1669,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
* @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 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -2176,8 +2149,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
@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 abstract class BrowserActivity extends ThemableBrowserActivity implements @@ -2188,8 +2160,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
@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) {

7
app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java → app/src/main/java/acr/browser/lightning/browser/activity/ThemableBrowserActivity.java

@ -1,4 +1,4 @@ @@ -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 android.support.v7.app.AppCompatActivity; @@ -10,13 +10,14 @@ import android.support.v7.app.AppCompatActivity;
import javax.inject.Inject;
import acr.browser.lightning.R;
import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.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;

2
app/src/main/java/acr/browser/lightning/activity/BookmarkUiModel.java → app/src/main/java/acr/browser/lightning/browser/bookmark/BookmarkUiModel.java

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
package acr.browser.lightning.activity;
package acr.browser.lightning.browser.bookmark;
import android.support.annotation.Nullable;

18
app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java → app/src/main/java/acr/browser/lightning/browser/fragment/BookmarksFragment.java

@ -1,4 +1,4 @@ @@ -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 java.util.concurrent.ConcurrentHashMap; @@ -34,11 +34,11 @@ import java.util.concurrent.ConcurrentHashMap;
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 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener, @@ -320,10 +320,10 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
}
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 class BookmarksFragment extends Fragment implements View.OnClickListener, @@ -537,8 +537,8 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
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<Bitmap>() {
@Override

24
app/src/main/java/acr/browser/lightning/fragment/TabsFragment.java → app/src/main/java/acr/browser/lightning/browser/fragment/TabsFragment.java

@ -1,4 +1,4 @@ @@ -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 android.widget.TextView; @@ -34,12 +34,12 @@ import android.widget.TextView;
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 class TabsFragment extends Fragment implements View.OnClickListener, View @@ -130,7 +130,7 @@ public class TabsFragment extends Fragment implements View.OnClickListener, View
} 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 @@ public class TabsFragment extends Fragment implements View.OnClickListener, View @@ -183,7 +183,7 @@ public class TabsFragment extends Fragment implements View.OnClickListener, View
private void setupFrameLayoutButton(@NonNull final View root, @IdRes final int buttonId,
@IdRes final int imageId) {
final View frameButton = root.findViewById(buttonId);
final ImageView buttonImage = (ImageView) root.findViewById(imageId);
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 TabsFragment extends Fragment implements View.OnClickListener, View @@ -399,11 +399,11 @@ public class TabsFragment extends Fragment implements View.OnClickListener, 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);

2
app/src/main/java/acr/browser/lightning/fragment/anim/HorizontalItemAnimator.java → app/src/main/java/acr/browser/lightning/browser/fragment/anim/HorizontalItemAnimator.java

@ -13,7 +13,7 @@ @@ -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;

2
app/src/main/java/acr/browser/lightning/fragment/anim/VerticalItemAnimator.java → app/src/main/java/acr/browser/lightning/browser/fragment/anim/VerticalItemAnimator.java

@ -13,7 +13,7 @@ @@ -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;

151
app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java

@ -6,8 +6,10 @@ package acr.browser.lightning.constant; @@ -6,8 +6,10 @@ package acr.browser.lightning.constant;
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 java.util.List; @@ -24,9 +26,11 @@ import java.util.List;
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 { @@ -74,12 +78,27 @@ public final class BookmarkPage {
private static final String END = "</div></body></html>";
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 final class BookmarkPage { @@ -95,12 +114,11 @@ public final class BookmarkPage {
return Single.create(new SingleAction<String>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<String> 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 final class BookmarkPage { @@ -108,13 +126,12 @@ public final class BookmarkPage {
});
}
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 @@ public final class BookmarkPage { @@ -123,52 +140,84 @@ public final class BookmarkPage {
}
private void buildBookmarkPage(@Nullable final String folder) {
mManager.getBookmarksFromFolderSorted(folder)
mBookmarkModel.getBookmarksFromFolderSorted(folder)
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
@Override
public void onItem(@Nullable List<HistoryItem> list) {
public void onItem(@Nullable final List<HistoryItem> 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<List<HistoryItem>>() {
@Override
public void onItem(@Nullable List<HistoryItem> 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<HistoryItem> 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);
}
}
}

6
app/src/main/java/acr/browser/lightning/constant/Constants.java

@ -30,9 +30,9 @@ public final class Constants { @@ -30,9 +30,9 @@ public final class 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)})();";

31
app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java

@ -6,6 +6,7 @@ package acr.browser.lightning.constant; @@ -6,6 +6,7 @@ package acr.browser.lightning.constant;
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 java.util.List; @@ -20,7 +21,7 @@ import java.util.List;
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 @@ import acr.browser.lightning.utils.Utils; @@ -29,9 +30,8 @@ import acr.browser.lightning.utils.Utils;
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 = "<!DOCTYPE html><html xmlns=http://www.w3.org/1999/xhtml>\n" +
@ -41,10 +41,12 @@ public final class DownloadsPage { @@ -41,10 +41,12 @@ public final class DownloadsPage {
"<meta name=viewport content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'>\n" +
"<title>";
private static final String HEADING_2 = "</title>" +
"</head>" +
"<style>body{background:#f5f5f5;}.box{vertical-align:middle;position:relative; display: block; margin: 10px;padding:10px; background-color:#fff;box-shadow: 0px 2px 4px rgba( 0, 0, 0, 0.25 );font-family: Arial;color: #444;font-size: 12px;-moz-border-radius: 2px;-webkit-border-radius: 2px;border-radius: 2px;}.box a { width: 100%; height: 100%; position: absolute; left: 0; top: 0;}.black {color: black;font-size: 15px;font-family: Arial; white-space: nowrap; overflow: hidden;margin:auto; text-overflow: ellipsis; -o-text-overflow: ellipsis; -ms-text-overflow: ellipsis;}.font {color: gray;font-size: 10px;font-family: Arial; white-space: nowrap; overflow: hidden;margin:auto; text-overflow: ellipsis; -o-text-overflow: ellipsis; -ms-text-overflow: ellipsis;}</style>" +
"<body><div id='content'>";
private static final String HEADING_2 = "</title></head><style>body,html {margin: 0px; padding: 0px;}" +
".box { vertical-align:middle;position:relative; display: block; margin: 0px;padding-left:14px;padding-right:14px;padding-top:9px;padding-bottom:9px; background-color:#fff;border-bottom: 1px solid #d2d2d2;font-family: Arial;color: #444;font-size: 12px;}" +
".box a { width: 100%; height: 100%; position: absolute; left: 0; top: 0;}" +
".black {color: black;font-size: 15px;font-family: Arial; white-space: nowrap; overflow: hidden;margin:auto; text-overflow: ellipsis; -o-text-overflow: ellipsis; -ms-text-overflow: ellipsis;}" +
".font {color: gray;font-size: 10px;font-family: Arial; white-space: nowrap; overflow: hidden;margin:auto; text-overflow: ellipsis; -o-text-overflow: ellipsis; -ms-text-overflow: ellipsis;}" +
"</style><body><div id=\"content\">";
private static final String PART1 = "<div class=box><a href='";
@ -56,7 +58,10 @@ public final class DownloadsPage { @@ -56,7 +58,10 @@ public final class DownloadsPage {
private static final String END = "</div></body></html>";
private File mFilesDir;
@NonNull
private static File getDownloadsPageFile(@NonNull Application application) {
return new File(application.getFilesDir(), FILENAME);
}
@Inject Application mApp;
@Inject PreferenceManager mPreferenceManager;
@ -74,11 +79,9 @@ public final class DownloadsPage { @@ -74,11 +79,9 @@ public final class DownloadsPage {
return Single.create(new SingleAction<String>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<String> subscriber) {
mFilesDir = mApp.getFilesDir();
buildDownloadsPage();
File downloadsWebPage = new File(mFilesDir, FILENAME);
File downloadsWebPage = getDownloadsPageFile(mApp);
subscriber.onItem(Constants.FILE + downloadsWebPage);
subscriber.onComplete();
@ -120,10 +123,10 @@ public final class DownloadsPage { @@ -120,10 +123,10 @@ public final class DownloadsPage {
FileWriter bookWriter = null;
try {
//noinspection IOResourceOpenedButNotSafelyClosed
bookWriter = new FileWriter(new File(mFilesDir, FILENAME), false);
bookWriter = new FileWriter(getDownloadsPageFile(mApp), false);
bookWriter.write(downloadsBuilder.toString());
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Unable to write download page to disk", e);
} finally {
Utils.close(bookWriter);
}

21
app/src/main/java/acr/browser/lightning/constant/HistoryPage.java

@ -25,7 +25,7 @@ import java.util.List; @@ -25,7 +25,7 @@ import java.util.List;
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.history.HistoryModel;
import acr.browser.lightning.utils.Preconditions;
@ -61,9 +61,22 @@ public class HistoryPage { @@ -61,9 +61,22 @@ public class HistoryPage {
private static final String END = "</div></body></html>";
/**
* Get the file that the history page is stored in
* or should be stored in.
*
* @param application the application used to access the file.
* @return a valid file object, note that the file might not exist.
*/
@NonNull
private static File getHistoryPageFile(@NonNull Application application) {
return new File(application.getFilesDir(), FILENAME);
}
@NonNull private final String mTitle;
@Inject Application mApp;
@Inject HistoryModel mHistoryModel;
public HistoryPage() {
BrowserApp.getAppComponent().inject(this);
@ -77,7 +90,7 @@ public class HistoryPage { @@ -77,7 +90,7 @@ public class HistoryPage {
public void onSubscribe(@NonNull final SingleSubscriber<String> subscriber) {
final StringBuilder historyBuilder = new StringBuilder(HEADING_1 + mTitle + HEADING_2);
HistoryModel.lastHundredVisitedHistoryItems()
mHistoryModel.lastHundredVisitedHistoryItems()
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
@Override
public void onItem(@Nullable List<HistoryItem> item) {
@ -97,7 +110,7 @@ public class HistoryPage { @@ -97,7 +110,7 @@ public class HistoryPage {
}
historyBuilder.append(END);
File historyWebPage = new File(mApp.getFilesDir(), FILENAME);
File historyWebPage = getHistoryPageFile(mApp);
FileWriter historyWriter = null;
try {
//noinspection IOResourceOpenedButNotSafelyClosed
@ -130,7 +143,7 @@ public class HistoryPage { @@ -130,7 +143,7 @@ public class HistoryPage {
return Completable.create(new CompletableAction() {
@Override
public void onSubscribe(@NonNull CompletableSubscriber subscriber) {
File historyWebPage = new File(application.getFilesDir(), FILENAME);
File historyWebPage = getHistoryPageFile(application);
if (historyWebPage.exists()) {
historyWebPage.delete();
}

133
app/src/main/java/acr/browser/lightning/constant/StartPage.java

@ -17,8 +17,9 @@ import java.io.IOException; @@ -17,8 +17,9 @@ import java.io.IOException;
import javax.inject.Inject;
import acr.browser.lightning.R;
import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.BrowserApp;
import acr.browser.lightning.search.SearchEngineProvider;
import acr.browser.lightning.search.engine.BaseSearchEngine;
import acr.browser.lightning.utils.Utils;
public class StartPage {
@ -26,38 +27,43 @@ public class StartPage { @@ -26,38 +27,43 @@ public class StartPage {
public static final String FILENAME = "homepage.html";
private static final String HEAD_1 = "<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ "<head>"
+ "<meta content=\"en-us\" http-equiv=\"Content-Language\" />"
+ "<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\" />"
+ "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">"
+ "<title>";
+ "<head>"
+ "<meta content=\"en-us\" http-equiv=\"Content-Language\" />"
+ "<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\" />"
+ "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">"
+ "<title>";
private static final String HEAD_2 = "</title>"
+ "</head>"
+ "<style>body{background:#f5f5f5;text-align:center;margin:0px;}#search_input{height:35px; "
+ "width:100%;outline:none;border:none;font-size: 16px;background-color:transparent;}"
+ "span { display: block; overflow: hidden; padding-left:5px;vertical-align:middle;}"
+ ".search_bar{display:table;vertical-align:middle;width:90%;height:35px;max-width:500px;margin:0 auto;background-color:#fff;box-shadow: 0px 2px 3px rgba( 0, 0, 0, 0.25 );"
+ "font-family: Arial;color: #444;-moz-border-radius: 2px;-webkit-border-radius: 2px;border-radius: 2px;}"
+ "#search_submit{outline:none;height:37px;float:right;color:#404040;font-size:16px;font-weight:bold;border:none;"
+ "background-color:transparent;}.outer { display: table; position: absolute; height: 100%; width: 100%;}"
+ ".middle { display: table-cell; vertical-align: middle;}.inner { margin-left: auto; margin-right: auto; "
+ "margin-bottom:10%; width: 100%;}img.smaller{width:50%;max-width:300px;}"
+ ".box { vertical-align:middle;position:relative; display: block; margin: 10px;padding-left:10px;padding-right:10px;padding-top:5px;padding-bottom:5px;"
+ " background-color:#fff;box-shadow: 0px 3px rgba( 0, 0, 0, 0.1 );font-family: Arial;color: #444;"
+ "font-size: 12px;-moz-border-radius: 2px;-webkit-border-radius: 2px;"
+ "border-radius: 2px;}</style><body> <div class=\"outer\"><div class=\"middle\"><div class=\"inner\"><img class=\"smaller\" src=\"";
+ "</head>"
+ "<style>body{background:#f5f5f5;text-align:center;margin:0px;}#search_input{height:35px; "
+ "width:100%;outline:none;border:none;font-size: 16px;background-color:transparent;}"
+ "span { display: block; overflow: hidden; padding-left:5px;vertical-align:middle;}"
+ ".search_bar{display:table;vertical-align:middle;width:90%;height:35px;max-width:500px;margin:0 auto;background-color:#fff;box-shadow: 0px 2px 3px rgba( 0, 0, 0, 0.25 );"
+ "font-family: Arial;color: #444;-moz-border-radius: 2px;-webkit-border-radius: 2px;border-radius: 2px;}"
+ "#search_submit{outline:none;height:37px;float:right;color:#404040;font-size:16px;font-weight:bold;border:none;"
+ "background-color:transparent;}.outer { display: table; position: absolute; height: 100%; width: 100%;}"
+ ".middle { display: table-cell; vertical-align: middle;}.inner { margin-left: auto; margin-right: auto; "
+ "margin-bottom:10%; width: 100%;}img.smaller{width:50%;max-width:300px;}"
+ ".box { vertical-align:middle;position:relative; display: block; margin: 10px;padding-left:10px;padding-right:10px;padding-top:5px;padding-bottom:5px;"
+ " background-color:#fff;box-shadow: 0px 3px rgba( 0, 0, 0, 0.1 );font-family: Arial;color: #444;"
+ "font-size: 12px;-moz-border-radius: 2px;-webkit-border-radius: 2px;"
+ "border-radius: 2px;}</style><body> <div class=\"outer\"><div class=\"middle\"><div class=\"inner\"><img class=\"smaller\" src=\"";
private static final String MIDDLE = "\" ></br></br><form onsubmit=\"return search()\" class=\"search_bar\" autocomplete=\"off\">"
+ "<input type=\"submit\" id=\"search_submit\" value=\"Search\" ><span><input class=\"search\" type=\"text\" value=\"\" id=\"search_input\" >"
+ "</span></form></br></br></div></div></div><script type=\"text/javascript\">function search(){if(document.getElementById(\"search_input\").value != \"\"){window.location.href = \"";
+ "<input type=\"submit\" id=\"search_submit\" value=\"Search\" ><span><input class=\"search\" type=\"text\" value=\"\" id=\"search_input\" >"
+ "</span></form></br></br></div></div></div><script type=\"text/javascript\">function search(){if(document.getElementById(\"search_input\").value != \"\"){window.location.href = \"";
private static final String END = "\" + document.getElementById(\"search_input\").value;document.getElementById(\"search_input\").value = \"\";}return false;}</script></body></html>";
@NonNull
public static File getStartPageFile(@NonNull Application application) {
return new File(application.getFilesDir(), FILENAME);
}
@NonNull private final String mTitle;
@Inject Application mApp;
@Inject PreferenceManager mPreferenceManager;
@Inject SearchEngineProvider mSearchEngineProvider;
public StartPage() {
BrowserApp.getAppComponent().inject(this);
@ -71,87 +77,18 @@ public class StartPage { @@ -71,87 +77,18 @@ public class StartPage {
public void onSubscribe(@NonNull SingleSubscriber<String> subscriber) {
StringBuilder homepageBuilder = new StringBuilder(HEAD_1 + mTitle + HEAD_2);
String icon;
String searchUrl;
switch (mPreferenceManager.getSearchChoice()) {
case 0:
// CUSTOM SEARCH
icon = "file:///android_asset/lightning.png";
searchUrl = mPreferenceManager.getSearchUrl();
break;
case 1:
// GOOGLE_SEARCH;
icon = "file:///android_asset/google.png";
// "https://www.google.com/images/srpr/logo11w.png";
searchUrl = Constants.GOOGLE_SEARCH;
break;
case 2:
// ANDROID SEARCH;
icon = "file:///android_asset/ask.png";
searchUrl = Constants.ASK_SEARCH;
break;
case 3:
// BING_SEARCH;
icon = "file:///android_asset/bing.png";
// "http://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Bing_logo_%282013%29.svg/500px-Bing_logo_%282013%29.svg.png";
searchUrl = Constants.BING_SEARCH;
break;
case 4:
// YAHOO_SEARCH;
icon = "file:///android_asset/yahoo.png";
// "http://upload.wikimedia.org/wikipedia/commons/thumb/2/24/Yahoo%21_logo.svg/799px-Yahoo%21_logo.svg.png";
searchUrl = Constants.YAHOO_SEARCH;
break;
case 5:
// STARTPAGE_SEARCH;
icon = "file:///android_asset/startpage.png";
// "https://com/graphics/startp_logo.gif";
searchUrl = Constants.STARTPAGE_SEARCH;
break;
case 6:
// STARTPAGE_MOBILE
icon = "file:///android_asset/startpage.png";
// "https://com/graphics/startp_logo.gif";
searchUrl = Constants.STARTPAGE_MOBILE_SEARCH;
break;
case 7:
// DUCK_SEARCH;
icon = "file:///android_asset/duckduckgo.png";
// "https://duckduckgo.com/assets/logo_homepage.normal.v101.png";
searchUrl = Constants.DUCK_SEARCH;
break;
case 8:
// DUCK_LITE_SEARCH;
icon = "file:///android_asset/duckduckgo.png";
// "https://duckduckgo.com/assets/logo_homepage.normal.v101.png";
searchUrl = Constants.DUCK_LITE_SEARCH;
break;
case 9:
// BAIDU_SEARCH;
icon = "file:///android_asset/baidu.png";
// "http://www.baidu.com/img/bdlogo.gif";
searchUrl = Constants.BAIDU_SEARCH;
break;
case 10:
// YANDEX_SEARCH;
icon = "file:///android_asset/yandex.png";
// "http://upload.wikimedia.org/wikipedia/commons/thumb/9/91/Yandex.svg/600px-Yandex.svg.png";
searchUrl = Constants.YANDEX_SEARCH;
break;
default:
// DEFAULT GOOGLE_SEARCH;
icon = "file:///android_asset/google.png";
searchUrl = Constants.GOOGLE_SEARCH;
break;
}
BaseSearchEngine currentSearchEngine = mSearchEngineProvider.getCurrentSearchEngine();
String icon = currentSearchEngine.getIconUrl();
String searchUrl = currentSearchEngine.getQueryUrl();
homepageBuilder.append(icon);
homepageBuilder.append(MIDDLE);
homepageBuilder.append(searchUrl);
homepageBuilder.append(END);
File homepage = new File(mApp.getFilesDir(), FILENAME);
File homepage = getStartPageFile(mApp);
FileWriter hWriter = null;
try {
//noinspection IOResourceOpenedButNotSafelyClosed

4
app/src/main/java/acr/browser/lightning/controller/UIController.java

@ -14,7 +14,7 @@ import android.view.View; @@ -14,7 +14,7 @@ import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient.CustomViewCallback;
import acr.browser.lightning.activity.TabsManager;
import acr.browser.lightning.browser.TabsManager;
import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.dialog.LightningDialogBuilder;
import acr.browser.lightning.view.LightningView;
@ -28,7 +28,7 @@ public interface UIController { @@ -28,7 +28,7 @@ public interface UIController {
boolean getUseDarkTheme();
void updateUrl(@Nullable String title, boolean shortUrl);
void updateUrl(@Nullable String title, boolean isLoading);
void updateProgress(int n);

44
app/src/main/java/acr/browser/lightning/database/HistoryItem.java

@ -11,21 +11,15 @@ import acr.browser.lightning.utils.Preconditions; @@ -11,21 +11,15 @@ import acr.browser.lightning.utils.Preconditions;
public class HistoryItem implements Comparable<HistoryItem> {
// private variables
@NonNull
private String mUrl = "";
@NonNull
private String mTitle = "";
@NonNull
private String mFolder = "";
@Nullable
private Bitmap mBitmap = null;
@NonNull private String mUrl = "";
@NonNull private String mTitle = "";
@NonNull private String mFolder = "";
@Nullable private Bitmap mBitmap = null;
private int mImageId = 0;
private int mPosition = 0;
private boolean mIsFolder = false;
public HistoryItem() {}
@ -115,7 +109,7 @@ public class HistoryItem implements Comparable<HistoryItem> { @@ -115,7 +109,7 @@ public class HistoryItem implements Comparable<HistoryItem> {
@Override
public int compareTo(@NonNull HistoryItem another) {
int compare = this.mTitle.compareTo(another.mTitle);
int compare = this.mTitle.compareToIgnoreCase(another.mTitle);
if (compare == 0) {
return this.mUrl.compareTo(another.mUrl);
}
@ -123,27 +117,29 @@ public class HistoryItem implements Comparable<HistoryItem> { @@ -123,27 +117,29 @@ public class HistoryItem implements Comparable<HistoryItem> {
}
@Override
public boolean equals(@Nullable Object object) {
if (this == object) return true;
if (object == null) return false;
if (!(object instanceof HistoryItem)) return false;
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HistoryItem that = (HistoryItem) object;
HistoryItem that = (HistoryItem) o;
return mImageId == that.mImageId &&
this.mTitle.equals(that.mTitle) && this.mUrl.equals(that.mUrl) &&
this.mFolder.equals(that.mFolder);
mPosition == that.mPosition &&
mIsFolder == that.mIsFolder &&
mUrl.equals(that.mUrl) &&
mTitle.equals(that.mTitle) &&
mFolder.equals(that.mFolder);
}
@Override
public int hashCode() {
int result = mUrl.hashCode();
result = 31 * result + mImageId;
result = 31 * result + mTitle.hashCode();
result = 32 * result + mFolder.hashCode();
result = 31 * result + mFolder.hashCode();
result = 31 * result + mImageId;
result = 31 * result + mPosition;
result = 31 * result + (mIsFolder ? 1 : 0);
return result;
}

113
app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkDatabase.java

@ -75,7 +75,7 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel @@ -75,7 +75,7 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel
*/
@WorkerThread
@NonNull
private SQLiteDatabase lazyDatabase() {
private synchronized SQLiteDatabase lazyDatabase() {
if (mDatabase == null || !mDatabase.isOpen()) {
mDatabase = getWritableDatabase();
}
@ -105,6 +105,13 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel @@ -105,6 +105,13 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel
onCreate(db);
}
/**
* Binds a {@link HistoryItem} to {@link ContentValues}.
*
* @param bookmarkItem the bookmark to bind.
* @return a valid values object that can be inserted
* into the database.
*/
@NonNull
private static ContentValues bindBookmarkToContentValues(@NonNull HistoryItem bookmarkItem) {
ContentValues contentValues = new ContentValues(4);
@ -116,6 +123,15 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel @@ -116,6 +123,15 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel
return contentValues;
}
/**
* Binds a cursor to a {@link HistoryItem}. This is
* a non consuming operation on the cursor. Note that
* this operation is not safe to perform on a cursor
* unless you know that the cursor is of history items.
*
* @param cursor the cursor to read from.
* @return a valid item containing all the pertinent information.
*/
@NonNull
private static HistoryItem bindCursorToHistoryItem(@NonNull Cursor cursor) {
HistoryItem bookmark = new HistoryItem();
@ -129,6 +145,13 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel @@ -129,6 +145,13 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel
return bookmark;
}
/**
* Binds a cursor to a list of {@link HistoryItem}.
* This operation consumes the cursor.
*
* @param cursor the cursor to bind.
* @return a valid list of history items, may be empty.
*/
@NonNull
private static List<HistoryItem> bindCursorToHistoryItemList(@NonNull Cursor cursor) {
List<HistoryItem> bookmarks = new ArrayList<>();
@ -142,13 +165,91 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel @@ -142,13 +165,91 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel
return bookmarks;
}
/**
* URLs can represent the same thing with or without a trailing slash,
* for instance, google.com/ is the same page as google.com. Since these
* can be represented as different bookmarks within the bookmark database,
* it is important to be able to get the alternate version of a URL.
*
* @param url the string that might have a trailing slash.
* @return a string without a trailing slash if the original had one,
* or a string with a trailing slash if the original did not.
*/
@NonNull
private static String alternateSlashUrl(@NonNull String url) {
if (url.endsWith("/")) {
return url.substring(0, url.length() - 1);
} else {
return url + '/';
}
}
/**
* Queries the database for bookmarks with the provided URL. If it
* cannot find any bookmarks with the given URL, it will try to query
* for bookmarks with the {@link #alternateSlashUrl(String)} as its URL.
*
* @param url the URL to query for.
* @return a cursor with bookmarks matching the URL.
*/
@NonNull
private Cursor queryWithOptionalEndSlash(@NonNull String url) {
Cursor cursor = lazyDatabase().query(TABLE_BOOKMARK, null, KEY_URL + "=?", new String[]{url}, null, null, null, "1");
if (cursor.getCount() == 0) {
String alternateUrl = alternateSlashUrl(url);
cursor = lazyDatabase().query(TABLE_BOOKMARK, null, KEY_URL + "=?", new String[]{alternateUrl}, null, null, null, "1");
}
return cursor;
}
/**
* Deletes a bookmark from the database with the provided URL. If it
* cannot find any bookmark with the given URL, it will try to delete
* a bookmark with the {@link #alternateSlashUrl(String)} as its URL.
*
* @param url the URL to delete.
* @return the number of deleted rows.
*/
private int deleteWithOptionalEndSlash(@NonNull String url) {
int deletedRows = lazyDatabase().delete(TABLE_BOOKMARK, KEY_URL + "=?", new String[]{url});
if (deletedRows == 0) {
String alternateUrl = alternateSlashUrl(url);
deletedRows = lazyDatabase().delete(TABLE_BOOKMARK, KEY_URL + "=?", new String[]{alternateUrl});
}
return deletedRows;
}
/**
* Updates a bookmark in the database with the provided URL. If it
* cannot find any bookmark with the given URL, it will try to update
* a bookmark with the {@link #alternateSlashUrl(String)} as its URL.
*
* @param url the URL to update.
* @param contentValues the new values to update to.
* @return the numebr of rows updated.
*/
private int updateWithOptionalEndSlash(@NonNull String url, @NonNull ContentValues contentValues) {
int updatedRows = lazyDatabase().update(TABLE_BOOKMARK, contentValues, KEY_URL + "=?", new String[]{url});
if (updatedRows == 0) {
String alternateUrl = alternateSlashUrl(url);
updatedRows = lazyDatabase().update(TABLE_BOOKMARK, contentValues, KEY_URL + "=?", new String[]{alternateUrl});
}
return updatedRows;
}
@NonNull
@Override
public Single<HistoryItem> findBookmarkForUrl(@NonNull final String url) {
return Single.create(new SingleAction<HistoryItem>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<HistoryItem> 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 class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel @@ -168,7 +269,7 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel
return Single.create(new SingleAction<Boolean>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<Boolean> 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 class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel @@ -184,7 +285,7 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel
return Single.create(new SingleAction<Boolean>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<Boolean> 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 class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel @@ -229,7 +330,7 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel
return Single.create(new SingleAction<Boolean>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<Boolean> 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 class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel @@ -290,7 +391,7 @@ public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel
}
ContentValues contentValues = bindBookmarkToContentValues(newBookmark);
lazyDatabase().update(TABLE_BOOKMARK, contentValues, KEY_URL + "=?", new String[]{oldBookmark.getUrl()});
updateWithOptionalEndSlash(oldBookmark.getUrl(), contentValues);
subscriber.onComplete();
}

30
app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java

@ -10,15 +10,9 @@ import acr.browser.lightning.utils.Preconditions; @@ -10,15 +10,9 @@ import acr.browser.lightning.utils.Preconditions;
public class DownloadItem implements Comparable<DownloadItem> {
// 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 class DownloadItem implements Comparable<DownloadItem> { @@ -66,7 +60,7 @@ public class DownloadItem implements Comparable<DownloadItem> {
@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 class DownloadItem implements Comparable<DownloadItem> { @@ -74,23 +68,23 @@ public class DownloadItem implements Comparable<DownloadItem> {
}
@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;
}

2
app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java

@ -68,7 +68,7 @@ public class DownloadsDatabase extends SQLiteOpenHelper implements DownloadsMode @@ -68,7 +68,7 @@ public class DownloadsDatabase extends SQLiteOpenHelper implements DownloadsMode
*/
@WorkerThread
@NonNull
private SQLiteDatabase lazyDatabase() {
private synchronized SQLiteDatabase lazyDatabase() {
if (mDatabase == null || !mDatabase.isOpen()) {
mDatabase = getWritableDatabase();
}

165
app/src/main/java/acr/browser/lightning/database/history/HistoryDatabase.java

@ -13,6 +13,13 @@ import android.support.annotation.NonNull; @@ -13,6 +13,13 @@ import android.support.annotation.NonNull;
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 javax.inject.Singleton; @@ -22,9 +29,15 @@ import javax.inject.Singleton;
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 { @@ -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 @@ public class HistoryDatabase extends SQLiteOpenHelper { @@ -80,40 +92,108 @@ public class HistoryDatabase extends SQLiteOpenHelper {
@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<List<HistoryItem>> findHistoryItemsContaining(@NonNull final String query) {
return Single.create(new SingleAction<List<HistoryItem>>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<List<HistoryItem>> subscriber) {
List<HistoryItem> 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<List<HistoryItem>> lastHundredVisitedHistoryItems() {
return Single.create(new SingleAction<List<HistoryItem>>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<List<HistoryItem>> subscriber) {
List<HistoryItem> 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 @@ public class HistoryDatabase extends SQLiteOpenHelper { @@ -140,43 +220,6 @@ public class HistoryDatabase extends SQLiteOpenHelper {
return m;
}
@WorkerThread
@NonNull
synchronized List<HistoryItem> findItemsContaining(@Nullable String search) {
List<HistoryItem> 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<HistoryItem> getLastHundredItems() {
List<HistoryItem> 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<HistoryItem> getAllHistoryItems() {

80
app/src/main/java/acr/browser/lightning/database/history/HistoryModel.java

@ -4,24 +4,19 @@ import android.support.annotation.NonNull; @@ -4,24 +4,19 @@ import android.support.annotation.NonNull;
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.
* <p>
* 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 @@ public final class HistoryModel { @@ -29,18 +24,7 @@ public final class 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 final class HistoryModel { @@ -50,18 +34,7 @@ public final class HistoryModel {
* @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 final class HistoryModel { @@ -74,18 +47,7 @@ public final class HistoryModel {
* @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 final class HistoryModel { @@ -100,18 +62,7 @@ public final class HistoryModel {
* a list of history items.
*/
@NonNull
public static Single<List<HistoryItem>> findHistoryItemsContaining(@NonNull final String query) {
return Single.create(new SingleAction<List<HistoryItem>>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<List<HistoryItem>> subscriber) {
List<HistoryItem> result = BrowserApp.getAppComponent()
.historyDatabase().findItemsContaining(query);
subscriber.onItem(result);
subscriber.onComplete();
}
});
}
Single<List<HistoryItem>> findHistoryItemsContaining(@NonNull final String query);
/**
* An observable that emits a list of the
@ -121,16 +72,5 @@ public final class HistoryModel { @@ -121,16 +72,5 @@ public final class HistoryModel {
* a list of history items.
*/
@NonNull
public static Single<List<HistoryItem>> lastHundredVisitedHistoryItems() {
return Single.create(new SingleAction<List<HistoryItem>>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<List<HistoryItem>> subscriber) {
List<HistoryItem> result = BrowserApp.getAppComponent()
.historyDatabase().getLastHundredItems();
subscriber.onItem(result);
subscriber.onComplete();
}
});
}
Single<List<HistoryItem>> lastHundredVisitedHistoryItems();
}

38
app/src/main/java/acr/browser/lightning/app/AppComponent.java → app/src/main/java/acr/browser/lightning/di/AppComponent.java

@ -1,26 +1,30 @@ @@ -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 { @@ -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);
}

24
app/src/main/java/acr/browser/lightning/app/AppModule.java → app/src/main/java/acr/browser/lightning/di/AppModule.java

@ -1,4 +1,4 @@ @@ -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 net.i2p.android.ui.I2PAndroidHelper; @@ -8,10 +8,14 @@ import net.i2p.android.ui.I2PAndroidHelper;
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 class AppModule { @@ -36,17 +40,31 @@ public class AppModule {
@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

6
app/src/main/java/acr/browser/lightning/dialog/BrowserDialog.java

@ -82,8 +82,8 @@ public class BrowserDialog { @@ -82,8 +82,8 @@ public class BrowserDialog {
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<String> adapter = new ArrayAdapter<>(activity,
android.R.layout.simple_list_item_1);
@ -136,7 +136,7 @@ public class BrowserDialog { @@ -136,7 +136,7 @@ public class BrowserDialog {
@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) {

34
app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java

@ -8,9 +8,7 @@ import android.support.annotation.NonNull; @@ -8,9 +8,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.webkit.URLUtil;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
@ -24,20 +22,20 @@ import java.util.List; @@ -24,20 +22,20 @@ import java.util.List;
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 class LightningDialogBuilder { @@ -55,7 +53,9 @@ public class LightningDialogBuilder {
@Inject BookmarkModel mBookmarkManager;
@Inject DownloadsModel mDownloadsModel;
@Inject HistoryModel mHistoryModel;
@Inject PreferenceManager mPreferenceManager;
@Inject DownloadHandler mDownloadHandler;
@Inject
public LightningDialogBuilder() {
@ -73,7 +73,7 @@ public class LightningDialogBuilder { @@ -73,7 +73,7 @@ public class LightningDialogBuilder {
@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 class LightningDialogBuilder { @@ -91,6 +91,7 @@ public class LightningDialogBuilder {
.subscribe(new SingleOnSubscribe<HistoryItem>() {
@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 @@ public class LightningDialogBuilder { @@ -191,12 +192,12 @@ public class LightningDialogBuilder {
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 class LightningDialogBuilder { @@ -332,7 +333,7 @@ public class LightningDialogBuilder {
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 class LightningDialogBuilder { @@ -384,16 +385,7 @@ public class LightningDialogBuilder {
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<Boolean>() {
@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, "");
}
});
}

145
app/src/main/java/acr/browser/lightning/download/DownloadHandler.java

@ -23,16 +23,26 @@ import android.webkit.CookieManager; @@ -23,16 +23,26 @@ import android.webkit.CookieManager;
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 { @@ -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 { @@ -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 class DownloadHandler { @@ -98,7 +111,7 @@ public class DownloadHandler {
}
}
}
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 @@ public class DownloadHandler { @@ -141,11 +154,12 @@ 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
*/
/* 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 @@ public class DownloadHandler { @@ -198,16 +212,8 @@ public class DownloadHandler {
// 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 @@ public class DownloadHandler { @@ -254,80 +260,30 @@ public class DownloadHandler {
}
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<Boolean>() {
@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 @@ public class DownloadHandler { @@ -338,19 +294,4 @@ public class DownloadHandler {
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;
}
}
}

19
app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java

@ -7,7 +7,6 @@ import android.Manifest; @@ -7,7 +7,6 @@ import android.Manifest;
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.DownloadListener; @@ -15,13 +14,11 @@ import android.webkit.DownloadListener;
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 { @@ -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 class LightningDownloadListener implements DownloadListener { @@ -64,17 +61,7 @@ public class LightningDownloadListener implements DownloadListener {
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<Boolean>() {
@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;

72
app/src/main/java/acr/browser/lightning/favicon/FaviconModel.java

@ -2,9 +2,13 @@ package acr.browser.lightning.favicon; @@ -2,9 +2,13 @@ package acr.browser.lightning.favicon;
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 java.io.IOException; @@ -22,6 +26,8 @@ import java.io.IOException;
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 { @@ -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<String, Bitmap> mFaviconCache = new LruCache<String, Bitmap>((int) FileUtils.megabytesToBytes(1)) {
@Override
@ -44,10 +50,12 @@ public class FaviconModel { @@ -44,10 +50,12 @@ public class FaviconModel {
}
};
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 @@ public class FaviconModel { @@ -68,6 +76,18 @@ public class FaviconModel {
}
}
@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 @@ public class FaviconModel { @@ -93,8 +113,9 @@ public class FaviconModel {
* @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 @@ public class FaviconModel { @@ -106,19 +127,13 @@ public class FaviconModel {
* 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<Bitmap> faviconForUrl(@NonNull final String url,
@NonNull final Bitmap defaultFavicon,
final boolean allowGoogleService) {
@NonNull final String title) {
return Single.create(new SingleAction<Bitmap>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<Bitmap> subscriber) {
@ -126,7 +141,7 @@ public class FaviconModel { @@ -126,7 +141,7 @@ public class FaviconModel {
if (uri == null) {
Bitmap newFavicon = Utils.padFavicon(defaultFavicon);
Bitmap newFavicon = Utils.padFavicon(getDefaultBitmapForString(title));
subscriber.onItem(newFavicon);
subscriber.onComplete();
@ -134,19 +149,19 @@ public class FaviconModel { @@ -134,19 +149,19 @@ public class FaviconModel {
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 class FaviconModel { @@ -155,18 +170,7 @@ public class FaviconModel {
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 class FaviconModel { @@ -201,7 +205,7 @@ public class FaviconModel {
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();

4
app/src/main/java/acr/browser/lightning/favicon/FaviconUtils.java

@ -8,9 +8,9 @@ import android.text.TextUtils; @@ -8,9 +8,9 @@ import android.text.TextUtils;
/**
* 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;
}

110
app/src/main/java/acr/browser/lightning/favicon/ImageFetcher.java

@ -1,110 +0,0 @@ @@ -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;
}
}

10
app/src/main/java/acr/browser/lightning/interpolator/BezierDecelerateInterpolator.java

@ -1,10 +1,8 @@ @@ -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 { @@ -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

4
app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java

@ -9,7 +9,7 @@ import javax.inject.Inject; @@ -9,7 +9,7 @@ import javax.inject.Inject;
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 class PreferenceManager { @@ -150,7 +150,7 @@ public class PreferenceManager {
@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() {

4
app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java

@ -376,8 +376,8 @@ public class HtmlFetcher { @@ -376,8 +376,8 @@ public class HtmlFetcher {
*/
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

6
app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java → app/src/main/java/acr/browser/lightning/reading/activity/ReadingActivity.java

@ -1,4 +1,4 @@ @@ -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 android.widget.TextView; @@ -26,7 +26,7 @@ import android.widget.TextView;
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 class ReadingActivity extends AppCompatActivity { @@ -297,7 +297,7 @@ public class ReadingActivity extends AppCompatActivity {
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

112
app/src/main/java/acr/browser/lightning/search/SearchEngineProvider.java

@ -0,0 +1,112 @@ @@ -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<BaseSearchEngine> getAllSearchEngines() {
return new ArrayList<BaseSearchEngine>(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());
}};
}
}

18
app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java

@ -37,7 +37,7 @@ import java.util.Locale; @@ -37,7 +37,7 @@ import java.util.Locale;
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 { @@ -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<HistoryItem> mAllBookmarks = new ArrayList<>(5);
@ -136,9 +137,9 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable { @@ -136,9 +137,9 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable {
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 class SuggestionsAdapter extends BaseAdapter implements Filterable { @@ -197,7 +198,7 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable {
@NonNull
@Override
public Filter getFilter() {
return new SearchFilter(this);
return new SearchFilter(this, mHistoryModel);
}
private synchronized void publishResults(@NonNull List<HistoryItem> list) {
@ -318,9 +319,12 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable { @@ -318,9 +319,12 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable {
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 class SuggestionsAdapter extends BaseAdapter implements Filterable { @@ -355,7 +359,7 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable {
}
});
HistoryModel.findHistoryItemsContaining(query)
mHistoryModel.findHistoryItemsContaining(query)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.main())
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {

67
app/src/main/java/acr/browser/lightning/search/SuggestionsManager.java

@ -1,67 +0,0 @@ @@ -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<List<HistoryItem>> createGoogleQueryObservable(@NonNull final String query,
@NonNull final Application application) {
return Single.create(new SingleAction<List<HistoryItem>>() {
@Override
public void onSubscribe(@NonNull final SingleSubscriber<List<HistoryItem>> subscriber) {
sIsTaskExecuting = true;
List<HistoryItem> results = new GoogleSuggestionsModel(application).getResults(query);
subscriber.onItem(results);
subscriber.onComplete();
sIsTaskExecuting = false;
}
});
}
@NonNull
static Single<List<HistoryItem>> createBaiduQueryObservable(@NonNull final String query,
@NonNull final Application application) {
return Single.create(new SingleAction<List<HistoryItem>>() {
@Override
public void onSubscribe(@NonNull final SingleSubscriber<List<HistoryItem>> subscriber) {
sIsTaskExecuting = true;
List<HistoryItem> results = new BaiduSuggestionsModel(application).getResults(query);
subscriber.onItem(results);
subscriber.onComplete();
sIsTaskExecuting = false;
}
});
}
@NonNull
static Single<List<HistoryItem>> createDuckQueryObservable(@NonNull final String query,
@NonNull final Application application) {
return Single.create(new SingleAction<List<HistoryItem>>() {
@Override
public void onSubscribe(@NonNull final SingleSubscriber<List<HistoryItem>> subscriber) {
sIsTaskExecuting = true;
List<HistoryItem> results = new DuckSuggestionsModel(application).getResults(query);
subscriber.onItem(results);
subscriber.onComplete();
sIsTaskExecuting = false;
}
});
}
}

46
app/src/main/java/acr/browser/lightning/search/SuggestionsManager.kt

@ -0,0 +1,46 @@ @@ -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<List<HistoryItem>> { 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<List<HistoryItem>> { 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<List<HistoryItem>> { subscriber ->
isRequestInProgress = true
val results = DuckSuggestionsModel(application).fetchResults(query)
subscriber.onItem(results)
subscriber.onComplete()
isRequestInProgress = false
})
}

13
app/src/main/java/acr/browser/lightning/search/engine/AskSearch.kt

@ -0,0 +1,13 @@ @@ -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
)

15
app/src/main/java/acr/browser/lightning/search/engine/BaiduSearch.kt

@ -0,0 +1,15 @@ @@ -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
)

15
app/src/main/java/acr/browser/lightning/search/engine/BaseSearchEngine.kt

@ -0,0 +1,15 @@ @@ -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)

16
app/src/main/java/acr/browser/lightning/search/engine/BingSearch.kt

@ -0,0 +1,16 @@ @@ -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
)

12
app/src/main/java/acr/browser/lightning/search/engine/CustomSearch.kt

@ -0,0 +1,12 @@ @@ -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
)

15
app/src/main/java/acr/browser/lightning/search/engine/DuckLiteSearch.kt

@ -0,0 +1,15 @@ @@ -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
)

15
app/src/main/java/acr/browser/lightning/search/engine/DuckSearch.kt

@ -0,0 +1,15 @@ @@ -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
)

15
app/src/main/java/acr/browser/lightning/search/engine/GoogleSearch.kt

@ -0,0 +1,15 @@ @@ -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
)

13
app/src/main/java/acr/browser/lightning/search/engine/StartPageMobileSearch.kt

@ -0,0 +1,13 @@ @@ -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
)

13
app/src/main/java/acr/browser/lightning/search/engine/StartPageSearch.kt

@ -0,0 +1,13 @@ @@ -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
)

16
app/src/main/java/acr/browser/lightning/search/engine/YahooSearch.kt

@ -0,0 +1,16 @@ @@ -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
)

16
app/src/main/java/acr/browser/lightning/search/engine/YandexSearch.kt

@ -0,0 +1,16 @@ @@ -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
)

22
app/src/main/java/acr/browser/lightning/search/BaiduSuggestionsModel.java → app/src/main/java/acr/browser/lightning/search/suggestions/BaiduSuggestionsModel.java

@ -1,11 +1,9 @@ @@ -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.R; @@ -14,23 +12,23 @@ import acr.browser.lightning.R;
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 @@ class BaiduSuggestionsModel extends BaseSuggestionsModel { @@ -39,12 +37,14 @@ class BaiduSuggestionsModel extends BaseSuggestionsModel {
String content = FileUtils.readStringFromStream(inputStream, "GBK");
JSONArray respArray = new JSONArray(content);
JSONArray jsonArray = respArray.getJSONArray(1);
int counter = 0;
for (int n = 0, size = jsonArray.length(); n < size; n++) {
String suggestion = jsonArray.getString(n);
results.add(new HistoryItem(mSearchSubtitle + " \"" + suggestion + '"',
suggestion, R.drawable.ic_search));
suggestion, R.drawable.ic_search));
counter++;
if (counter >= MAX_RESULTS) {
break;
}

60
app/src/main/java/acr/browser/lightning/search/BaseSuggestionsModel.java → app/src/main/java/acr/browser/lightning/search/suggestions/BaseSuggestionsModel.java

@ -1,4 +1,4 @@ @@ -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.Request; @@ -28,7 +28,12 @@ import okhttp3.Request;
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 { @@ -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<HistoryItem> results) throws Exception;
BaseSuggestionsModel(@NonNull Application application, @NonNull String encoding) {
@ -57,23 +76,25 @@ abstract class BaseSuggestionsModel { @@ -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<HistoryItem> getResults(@NonNull String query) {
public final List<HistoryItem> fetchResults(@NonNull final String rawQuery) {
List<HistoryItem> 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 @@ abstract class BaseSuggestionsModel { @@ -83,7 +104,6 @@ abstract class BaseSuggestionsModel {
parseResults(inputStream, filter);
} catch (Exception e) {
Log.e(TAG, "Unable to parse results", e);
return filter;
} finally {
Utils.close(inputStream);
}
@ -93,7 +113,7 @@ abstract class BaseSuggestionsModel { @@ -93,7 +113,7 @@ abstract class BaseSuggestionsModel {
/**
* 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 @@ abstract class BaseSuggestionsModel { @@ -122,6 +142,16 @@ abstract class BaseSuggestionsModel {
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 {

9
app/src/main/java/acr/browser/lightning/search/DuckSuggestionsModel.java → app/src/main/java/acr/browser/lightning/search/suggestions/DuckSuggestionsModel.java

@ -1,4 +1,4 @@ @@ -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.R; @@ -13,12 +13,15 @@ import acr.browser.lightning.R;
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);
}

13
app/src/main/java/acr/browser/lightning/search/GoogleSuggestionsModel.java → app/src/main/java/acr/browser/lightning/search/suggestions/GoogleSuggestionsModel.java

@ -1,4 +1,4 @@ @@ -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 java.util.List; @@ -15,20 +15,23 @@ import java.util.List;
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;
}

20
app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java → app/src/main/java/acr/browser/lightning/settings/activity/SettingsActivity.java

@ -1,7 +1,7 @@ @@ -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.Iterator; @@ -19,17 +19,17 @@ import java.util.Iterator;
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<String> mFragments = new ArrayList<>(7);
@NonNull private static final List<String> 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 @@ public class SettingsActivity extends ThemableSettingsActivity { @@ -38,7 +38,7 @@ public class SettingsActivity extends ThemableSettingsActivity {
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 @@ public class SettingsActivity extends ThemableSettingsActivity { @@ -46,7 +46,7 @@ public class SettingsActivity extends ThemableSettingsActivity {
@Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.preferences_headers, target);
mFragments.clear();
sFragments.clear();
Iterator<Header> headerIterator = target.iterator();
while (headerIterator.hasNext()) {
Header header = headerIterator.next();
@ -59,17 +59,17 @@ public class SettingsActivity extends ThemableSettingsActivity { @@ -59,17 +59,17 @@ public class SettingsActivity extends ThemableSettingsActivity {
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

5
app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java → app/src/main/java/acr/browser/lightning/settings/activity/ThemableSettingsActivity.java

@ -1,4 +1,4 @@ @@ -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 android.os.Bundle; @@ -8,7 +8,8 @@ import android.os.Bundle;
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;

16
app/src/main/java/acr/browser/lightning/fragment/AboutSettingsFragment.java → app/src/main/java/acr/browser/lightning/settings/fragment/AboutSettingsFragment.java

@ -1,7 +1,7 @@ @@ -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.content.pm.PackageManager; @@ -9,13 +9,13 @@ import android.content.pm.PackageManager;
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 class AboutSettingsFragment extends PreferenceFragment { @@ -24,18 +24,18 @@ public class AboutSettingsFragment extends PreferenceFragment {
// 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";
}
}

2
app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java → app/src/main/java/acr/browser/lightning/settings/fragment/AdvancedSettingsFragment.java

@ -1,7 +1,7 @@ @@ -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;

12
app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java → app/src/main/java/acr/browser/lightning/settings/fragment/BookmarkSettingsFragment.java

@ -1,7 +1,7 @@ @@ -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.util.Log; @@ -23,6 +23,7 @@ import android.util.Log;
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 java.util.List; @@ -38,14 +39,11 @@ import java.util.List;
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 class BookmarkSettingsFragment extends PreferenceFragment implements Pref @@ -495,7 +493,7 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref
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);
}
}

4
app/src/main/java/acr/browser/lightning/fragment/DebugSettingsFragment.java → app/src/main/java/acr/browser/lightning/settings/fragment/DebugSettingsFragment.java

@ -1,4 +1,4 @@ @@ -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 android.support.annotation.NonNull; @@ -10,7 +10,7 @@ import android.support.annotation.NonNull;
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;

4
app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java → app/src/main/java/acr/browser/lightning/settings/fragment/DisplaySettingsFragment.java

@ -1,7 +1,7 @@ @@ -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 @@ public class DisplaySettingsFragment extends LightningPreferenceFragment impleme @@ -152,7 +152,7 @@ public class DisplaySettingsFragment extends LightningPreferenceFragment impleme
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));

123
app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java → app/src/main/java/acr/browser/lightning/settings/fragment/GeneralSettingsFragment.java

@ -1,7 +1,7 @@ @@ -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.InputFilter; @@ -19,13 +19,22 @@ import android.text.InputFilter;
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 @@ -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 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme @@ -106,7 +119,8 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
mPreferenceManager.setFlashSupport(0);
}
setSearchEngineSummary(mPreferenceManager.getSearchChoice());
BaseSearchEngine currentSearchEngine = mSearchEngineProvider.getCurrentSearchEngine();
setSearchEngineSummary(currentSearchEngine);
downloadloc.setSummary(mDownloadLocation);
@ -156,11 +170,15 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme @@ -156,11 +170,15 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
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 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme @@ -170,18 +188,18 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
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 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme @@ -257,8 +275,8 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
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 class GeneralSettingsFragment extends LightningPreferenceFragment impleme @@ -294,13 +312,24 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
BrowserDialog.setDialogSize(mActivity, dialog);
}
@NonNull
private CharSequence[] convertSearchEngineToString(@NonNull List<BaseSearchEngine> 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<BaseSearchEngine> searchEngineList = mSearchEngineProvider.getAllSearchEngines();
CharSequence[] chars = convertSearchEngineToString(searchEngineList);
int n = mPreferenceManager.getSearchChoice();
@ -308,8 +337,19 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme @@ -308,8 +337,19 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
@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 class GeneralSettingsFragment extends LightningPreferenceFragment impleme @@ -418,7 +458,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
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 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme @@ -455,8 +495,8 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
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 class GeneralSettingsFragment extends LightningPreferenceFragment impleme @@ -519,7 +559,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
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 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme @@ -535,7 +575,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
@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 class GeneralSettingsFragment extends LightningPreferenceFragment impleme @@ -544,40 +584,11 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
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 class GeneralSettingsFragment extends LightningPreferenceFragment impleme @@ -663,7 +674,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
@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);

4
app/src/main/java/acr/browser/lightning/fragment/LightningPreferenceFragment.java → app/src/main/java/acr/browser/lightning/settings/fragment/LightningPreferenceFragment.java

@ -1,11 +1,11 @@ @@ -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;
/**

12
app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java → app/src/main/java/acr/browser/lightning/settings/fragment/PrivacySettingsFragment.java

@ -1,7 +1,7 @@ @@ -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.CompletableOnSubscribe; @@ -20,8 +20,11 @@ import com.anthonycr.bonsai.CompletableOnSubscribe;
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 @@ -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 @@ public class PrivacySettingsFragment extends LightningPreferenceFragment impleme @@ -187,7 +192,8 @@ public class PrivacySettingsFragment extends LightningPreferenceFragment impleme
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"));

69
app/src/main/java/acr/browser/lightning/utils/DrawableUtils.java

@ -1,7 +1,9 @@ @@ -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.RectF; @@ -9,12 +11,17 @@ import android.graphics.RectF;
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 class DrawableUtils { @@ -56,6 +63,68 @@ public class DrawableUtils {
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;

86
app/src/main/java/acr/browser/lightning/utils/FileUtils.java

@ -28,6 +28,9 @@ public class FileUtils { @@ -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 class FileUtils { @@ -160,4 +163,87 @@ public class FileUtils {
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;
}
}

2
app/src/main/java/acr/browser/lightning/utils/IntentUtils.java

@ -1,14 +1,12 @@ @@ -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;

2
app/src/main/java/acr/browser/lightning/utils/ProxyUtils.java

@ -13,7 +13,7 @@ import javax.inject.Inject; @@ -13,7 +13,7 @@ import javax.inject.Inject;
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;

12
app/src/main/java/acr/browser/lightning/utils/ResourceUtils.java

@ -4,11 +4,21 @@ import android.content.Context; @@ -4,11 +4,21 @@ import android.content.Context;
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);
}
}

10
app/src/main/java/acr/browser/lightning/utils/StringBuilderUtils.java

@ -14,16 +14,16 @@ public class StringBuilderUtils { @@ -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 class StringBuilderUtils { @@ -42,7 +42,7 @@ public class StringBuilderUtils {
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);
}
}

39
app/src/main/java/acr/browser/lightning/utils/Utils.java

@ -3,7 +3,6 @@ @@ -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.view.View; @@ -36,9 +35,6 @@ import android.view.View;
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.text.SimpleDateFormat; @@ -48,12 +44,10 @@ import java.text.SimpleDateFormat;
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 final class Utils { @@ -63,35 +57,6 @@ public final class Utils {
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 final class Utils { @@ -211,7 +176,7 @@ public final class Utils {
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);

4
app/src/main/java/acr/browser/lightning/utils/WebUtils.java

@ -36,8 +36,8 @@ public class WebUtils { @@ -36,8 +36,8 @@ public class WebUtils {
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);

42
app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java

@ -25,7 +25,7 @@ import com.anthonycr.grant.PermissionsResultAction; @@ -25,7 +25,7 @@ import com.anthonycr.grant.PermissionsResultAction;
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 @@ public class LightningChromeClient extends WebChromeClient { @@ -73,15 +73,9 @@ public class LightningChromeClient extends WebChromeClient {
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 class LightningChromeClient extends WebChromeClient { @@ -114,21 +108,21 @@ public class LightningChromeClient extends WebChromeClient {
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);

51
app/src/main/java/acr/browser/lightning/view/LightningView.java

@ -44,11 +44,10 @@ import java.util.Map; @@ -44,11 +44,10 @@ import java.util.Map;
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 { @@ -106,7 +105,7 @@ public class LightningView {
@NonNull private final Map<String, String> 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 class LightningView { @@ -250,15 +249,15 @@ public class LightningView {
*/
public void loadDownloadspage() {
new DownloadsPage().getDownloadsPage()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.main())
.subscribe(new SingleOnSubscribe<String>() {
@Override
public void onItem(@Nullable String item) {
Preconditions.checkNonNull(item);
loadUrl(item);
}
});
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.main())
.subscribe(new SingleOnSubscribe<String>() {
@Override
public void onItem(@Nullable String item) {
Preconditions.checkNonNull(item);
loadUrl(item);
}
});
}
/**
@ -989,45 +988,45 @@ public class LightningView { @@ -989,45 +988,45 @@ public class LightningView {
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);
}
}
}

29
app/src/main/java/acr/browser/lightning/view/LightningWebClient.java

@ -22,6 +22,7 @@ import android.view.View; @@ -22,6 +22,7 @@ import android.view.View;
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 javax.inject.Inject; @@ -41,14 +42,15 @@ import javax.inject.Inject;
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 class LightningWebClient extends WebViewClient { @@ -100,7 +102,7 @@ public class LightningWebClient extends WebViewClient {
@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 class LightningWebClient extends WebViewClient { @@ -121,7 +123,7 @@ public class LightningWebClient extends WebViewClient {
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 class LightningWebClient extends WebViewClient { @@ -135,9 +137,9 @@ public class LightningWebClient extends WebViewClient {
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 @@ public class LightningWebClient extends WebViewClient { @@ -304,7 +306,7 @@ public class LightningWebClient extends WebViewClient {
// 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 @@ public class LightningWebClient extends WebViewClient { @@ -359,15 +361,14 @@ public class LightningWebClient extends WebViewClient {
}
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 @@ public class LightningWebClient extends WebViewClient { @@ -377,8 +378,10 @@ public class LightningWebClient extends WebViewClient {
} 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;
}

2
app/src/main/res/layout/activity_main.xml

@ -5,7 +5,7 @@ @@ -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">
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"

4
app/src/main/res/layout/bookmark_list_item.xml

@ -9,8 +9,8 @@ @@ -9,8 +9,8 @@
<ImageView
android:id="@+id/faviconBookmark"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_width="@dimen/bookmark_item_icon_size"
android:layout_height="@dimen/bookmark_item_icon_size"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:contentDescription="Favicon"

8
app/src/main/res/raw/default_bookmarks.dat

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
{"url": "https://twitter.com/RestainoAnthony", "title": "Contact Me", "folder": "", "order": 0}
{"url": "https://www.facebook.com/", "title": "Facebook", "folder": "", "order": 2}
{"url": "https://twitter.com", "title": "Twitter", "folder": "", "order": 3}
{"url": "https://www.google.com/", "title": "Google", "folder": "", "order": 4}
{"url": "https://www.yahoo.com/", "title": "Yahoo", "folder": "", "order": 5}
{"url": "https://www.facebook.com/", "title": "Facebook", "folder": "", "order": 1}
{"url": "https://twitter.com/", "title": "Twitter", "folder": "", "order": 2}
{"url": "https://www.google.com/", "title": "Google", "folder": "", "order": 3}
{"url": "https://www.wikipedia.org/", "title": "Wikipedia", "folder": "", "order": 4}

5
app/src/main/res/values-de/strings.xml

@ -119,7 +119,7 @@ @@ -119,7 +119,7 @@
<string name="max_tabs">Maximale Reiteranzahl erreicht!</string>
<string name="message_text_copied">Text in Zwischenablage kopiert</string>
<string name="message_link_copied">Link in Zwischenablage kopiert</string>
<string name="custom_url">Benutzerdefinierte URL</string>
<string name="search_engine_custom">Benutzerdefinierte URL</string>
<string name="message_blocked_local">Lokale Datei wurde beim Laden blockiert</string>
<string name="licenses">Open Source-Lizenzen</string>
<string name="suggestion">Suche nach</string>
@ -160,7 +160,7 @@ @@ -160,7 +160,7 @@
<string name="export_bookmarks">Exportiere Datensicherung</string>
<string name="import_backup">Importiere Lesezeichen aus Datensicherung</string>
<string name="bookmark_export_path">Lesezeichen exportiert nach</string>
<string name="bookmark_settings">Lesezeichen Einstellungen</string>
<string name="bookmark_settings">Lesezeichen</string>
<string name="import_bookmark_error">Lesezeichen konnten nicht importiert werden</string>
<string name="title_chooser">Wähle Datei aus</string>
<string name="settings_general">Allgemein</string>
@ -246,4 +246,5 @@ @@ -246,4 +246,5 @@
<string name="host">Host:</string>
<string name="hosts_source">Quelle für Werbeblocker</string>
<string name="settings_display">Anzeige</string>
<string name="message_open_download_fail">Diese Datei existiert nicht mehr</string>
</resources>

2
app/src/main/res/values-el/strings.xml

@ -103,7 +103,7 @@ @@ -103,7 +103,7 @@
<string name="max_tabs">Μέγιστος αριθμός καρτελών ξεπεράστηκε</string>
<string name="message_text_copied">Το κείμενο αντιγράφτηκε</string>
<string name="message_link_copied">Ο σύνδεσμος αντιγράφτηκε</string>
<string name="custom_url">Ειδικό URL</string>
<string name="search_engine_custom">Ειδικό URL</string>
<string name="message_blocked_local">Τοπικά αρχεία εχουν μπλοκαριστεί απο την φόρτωση</string>
<string name="licenses">Άδειες ανοιχτού κώδικα</string>
<string name="suggestion">Αναζήτηση για</string>

2
app/src/main/res/values-es/strings.xml

@ -103,7 +103,7 @@ @@ -103,7 +103,7 @@
<string name="max_tabs">Se ha alcanzado el máximo de pestañas</string>
<string name="message_text_copied">El texto se ha copiado al portapapeles</string>
<string name="message_link_copied">El enlace se ha copiado al portapapeles</string>
<string name="custom_url">Dirección personalizada</string>
<string name="search_engine_custom">Dirección personalizada</string>
<string name="message_blocked_local">Se ha bloqueado la carga del archivo local</string>
<string name="licenses">Licencias de código abierto (open source)</string>
<string name="suggestion">Buscar</string>

2
app/src/main/res/values-fr/strings.xml

@ -125,7 +125,7 @@ @@ -125,7 +125,7 @@
<string name="max_tabs">Onglets maximuns atteint</string>
<string name="message_text_copied">Texte copié dans le presse-papiers</string>
<string name="message_link_copied">Adresse copiée dans le presse-papiers</string>
<string name="custom_url">Adresse personnalisée</string>
<string name="search_engine_custom">Adresse personnalisée</string>
<string name="message_blocked_local">Le fichier local a été bloqué au chargement</string>
<string name="licenses">Licences open source</string>
<string name="suggestion">Recherche</string>

2
app/src/main/res/values-hu/strings.xml

@ -124,7 +124,7 @@ @@ -124,7 +124,7 @@
<string name="max_tabs">Maximálisan megnyitható lapok száma elérve</string>
<string name="message_text_copied">Szöveg másolva a vágólapra</string>
<string name="message_link_copied">Link másolva a vágólapra</string>
<string name="custom_url">Egyéni URL</string>
<string name="search_engine_custom">Egyéni URL</string>
<string name="message_blocked_local">Helyi fájl betöltése blokkolva</string>
<string name="licenses">Nyílt forráskódú licencek</string>
<string name="suggestion">Keresés</string>

4
app/src/main/res/values-it/strings.xml

@ -124,7 +124,7 @@ @@ -124,7 +124,7 @@
<string name="max_tabs">Hai raggiunto il numero massimo di schede apribili</string>
<string name="message_text_copied">Testo copiato negli appunti</string>
<string name="message_link_copied">Link copiato negli appunti</string>
<string name="custom_url">URL personalizzato</string>
<string name="search_engine_custom">URL personalizzato</string>
<string name="message_blocked_local">Caricamento file locale bloccato</string>
<string name="licenses">Licenze Open Source</string>
<string name="suggestion">Cerca</string>
@ -237,7 +237,7 @@ @@ -237,7 +237,7 @@
<string name="message_added_to_homescreen">Scorciatoia aggiunta</string>
<string name="action_delete_all_bookmarks">Cancella tutti i segnalibri</string>
<string name="swap_bookmarks_and_tabs">Inverti pannello segnalibri con schede</string>
<string name="flash_not_supported">Flash Player non è supportato dal tuo sistema!</string>
<string name="flash_not_supported">Flash Player non è supportato dal tuo sistema</string>
<string name="faq">FAQ</string>
<string name="faq_description">Domande più frequenti</string>

28
app/src/main/res/values-ja/strings.xml

@ -1,24 +1,12 @@ @@ -1,24 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_name">Lightning</string>
<string name="action_new_tab">新しいタブ</string>
<string name="action_share">共有</string>
<string name="action_history">履歴</string>
<string name="action_bookmarks">ブックマーク</string>
<string name="action_downloads">ダウンロード</string>
<string name="action_add_bookmark">ブックマークに追加</string>
<string name="action_copy">ページの URL をコピー</string>
<string name="action_forward">進む</string>
@ -70,6 +58,7 @@ @@ -70,6 +58,7 @@
<string name="title_search_engine">検索エンジン</string>
<string name="action_ok">OK</string>
<string name="dialog_download">このファイルをダウンロードしますか? (%1$s)</string>
<string name="unknown_size">サイズ不明</string>
<string name="action_cancel">キャンセル</string>
<string name="title_warning">警告</string>
<string name="dialog_adobe_not_installed">Adobe Flash Player が見つかりません\nFlash Player をインストールしてください</string>
@ -121,7 +110,7 @@ @@ -121,7 +110,7 @@
<string name="max_tabs">これ以上タブは開けません</string>
<string name="message_text_copied">テキストをクリップボードにコピーしました</string>
<string name="message_link_copied">リンクをクリップボードにコピーしました</string>
<string name="custom_url">カスタム URL</string>
<string name="search_engine_custom">カスタム URL</string>
<string name="message_blocked_local">ファイルを読み込めませんでした</string>
<string name="licenses">オープンソース ライセンス</string>
<string name="suggestion">検索 :</string>
@ -139,11 +128,13 @@ @@ -139,11 +128,13 @@
<string name="action_allow">許可する</string>
<string name="action_dont_allow">許可しない</string>
<string name="title_sign_in">サインイン</string>
<string name="label_realm">サーバー メッセージ: %s</string>
<string name="hint_username">ユーザー名</string>
<string name="hint_password">パスワード</string>
<string name="search_suggestions">検索候補表示</string>
<string name="powered_by_google">Google を使用する</string>
<string name="powered_by_duck">DuckDuckGo を使用する</string>
<string name="powered_by_baidu">Baidu を使用する</string>
<string name="search_suggestions_off">検索候補を表示しない</string>
<string name="http_proxy">HTTP プロキシ</string>
<string-array name="proxy_choices_array">
@ -185,6 +176,7 @@ @@ -185,6 +176,7 @@
<string name="export_bookmarks">ブックマークをエクスポートする</string>
<string name="import_backup">ブックマークをインポートする</string>
<string name="bookmark_export_path">ブックマークをエクスポートしました</string>
<string name="bookmark_export_failure">ブックマークのエクスポートに失敗しました!</string>
<string name="bookmark_settings">ブックマーク</string>
<string name="import_bookmark_error">ブックマークのインポートに失敗しました</string>
<string name="title_chooser">ファイルを選択してください</string>
@ -213,6 +205,7 @@ @@ -213,6 +205,7 @@
</string-array>
<string name="invert_color">色を反転する</string>
<string name="dark_theme">ダーク</string>
<string name="settings_black_status_bar">ステータスバーを暗色にする</string>
<string name="tabs">タブ</string>
<string name="theme">アプリのテーマ</string>
<string name="light_theme">ライト</string>
@ -234,6 +227,9 @@ @@ -234,6 +227,9 @@
<string name="action_add_to_homescreen">ホーム画面に追加</string>
<string name="message_added_to_homescreen">ホーム画面にショートカットを追加しました</string>
<string name="action_delete_all_bookmarks">すべてのブックマークを削除する</string>
<string name="flash_not_supported">このシステムは Flash Player をサポートしていません</string>
<string name="upsell_plus_version">広告をブロックするには Lightning Plus へアップグレードしてください</string>
<string name="message_open_download_fail">このファイルは存在しません</string>
<string name="faq">FAQ</string>
<string name="faq_description">よくある質問と回答</string>
@ -255,4 +251,6 @@ @@ -255,4 +251,6 @@
<string name="dialog_rename_folder">フォルダ名を変更する</string>
<string name="dialog_remove_folder">フォルダを削除する</string>
<string name="dialog_title_close_browser">タブの消去</string>
<string name="dialog_delete_download">ダウンロードの削除</string>
<string name="dialog_delete_all_downloads">ダウンロードの全削除</string>
</resources>

2
app/src/main/res/values-ko/strings.xml

@ -102,7 +102,7 @@ @@ -102,7 +102,7 @@
<string name="max_tabs">최대 탭 수에 도달했습니다</string>
<string name="message_text_copied">텍스트가 클립보드로 복사되었습니다</string>
<string name="message_link_copied">링크가 클립보드로 복사되었습니다</string>
<string name="custom_url">임의의 URL</string>
<string name="search_engine_custom">임의의 URL</string>
<string name="message_blocked_local">로컬 파일 불러오기가 차단되었습니다</string>
<string name="licenses">오픈 소스 라이센스</string>
<string name="suggestion">다음을 검색</string>

2
app/src/main/res/values-nl/strings.xml

@ -124,7 +124,7 @@ @@ -124,7 +124,7 @@
<string name="max_tabs">Maximum aantal tabbladen bereikt</string>
<string name="message_text_copied">Tekst gekopieerd naar klembord</string>
<string name="message_link_copied">Link gekopieerd naar klembord</string>
<string name="custom_url">Aangepaste URL</string>
<string name="search_engine_custom">Aangepaste URL</string>
<string name="message_blocked_local">Lokaal bestand werd geblokkeerd</string>
<string name="licenses">Open Source Licenses</string>
<string name="suggestion">Zoek voor</string>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save