Repo created
950
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,950 @@
|
|||
# Changelog
|
||||
|
||||
## [6.1.4] - 2024-10-27
|
||||
|
||||
- Renew web server TLS certificate
|
||||
- Update Android gradle plugin
|
||||
|
||||
Special thanks to masudscloud for its bug report.
|
||||
|
||||
## [6.1.3] - 2024-07-06
|
||||
|
||||
- Fix mongoose web server
|
||||
- Update translations
|
||||
|
||||
Special thanks to mbangi for its bug report.
|
||||
|
||||
## [6.1.2] - 2024-06-17
|
||||
|
||||
- Fix VPN restart on network interface change
|
||||
- Fix crash when update is instantly download
|
||||
- Update mongoose web server
|
||||
- Update third party libraries
|
||||
- Update Android gradle plugin
|
||||
- Update translations
|
||||
|
||||
Special thanks to @Fs00 and @gallegonovato for their bug reports.
|
||||
|
||||
## [6.1.1] - 2023-06-20
|
||||
|
||||
- Improve adaptive launcher icon
|
||||
- Update AndroidX libraries
|
||||
- Update third party libraries
|
||||
- Update libsu
|
||||
- Update mongoose web server
|
||||
- Update Android gradle plugin
|
||||
- Update build tools
|
||||
- Update translations
|
||||
|
||||
Special thanks to AzusaHana for its contribution.
|
||||
|
||||
## [6.1.0] - 2023-03-26
|
||||
|
||||
- Add adaptive launcher icon
|
||||
- Add POST_NOTIFCATIONS runtime permission support
|
||||
- Improve settings UI
|
||||
- Update target SDK to Android 13
|
||||
- Update libsu
|
||||
- Update mongoose web server
|
||||
- Update AndroidX libraries
|
||||
- Update third party libraries
|
||||
- Update Android gradle plugin
|
||||
- Update NDK
|
||||
|
||||
## [6.0.3] - 2022-07-25
|
||||
|
||||
- Improve hosts source cache to reduce bandwidth usage
|
||||
- Improve GitHub API usage to reduce bandwidth usage
|
||||
- Update AndroidX libraries
|
||||
- Update translations
|
||||
|
||||
Special thanks to yoshimo for its bug report.
|
||||
|
||||
## [6.0.2] - 2022-06-16
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Fix wizard ad-block method check
|
||||
- Update translations
|
||||
|
||||
Special thanks to Dandu32, ipdev and zhmstar0310 for their bug reports.
|
||||
|
||||
## [6.0.1] - 2022-06-03
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Improve screen rotation handling on home screen
|
||||
- Update libsu
|
||||
- Update mongoose web server
|
||||
- Update Android gradle plugin
|
||||
- Update third party libraries
|
||||
- Update translations
|
||||
|
||||
Special thanks to kubalav and zgfg for their bug reports.
|
||||
|
||||
## [6.0.0] - 2022-05-18
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Add a new VPN ad-blocker implementation
|
||||
- Add initial DOH (DNS Over HTTPS) support
|
||||
- Add VPN connection monitor, heartbeat and throttler to improve reliability
|
||||
- Add long press action to copy hostname from user lists and DNS log to clipboard
|
||||
- Fix VPN state on network connectivity change and lost
|
||||
- Fix VPN restart when system kills it
|
||||
- Fix wrong DNS read on VPN restart
|
||||
- Fix VPN unwanted restart while paused
|
||||
- Fix crash on application update unknown size
|
||||
- Improve VPN user control reliability
|
||||
- Update logging system
|
||||
- Update libsu
|
||||
- Update Android gradle plugin
|
||||
- Update AndroidX libraries
|
||||
- Update NDK
|
||||
- Update third party libraries
|
||||
|
||||
## [5.12.1] - 2022-05-14
|
||||
|
||||
- Fix hosts file install on Android 13
|
||||
|
||||
Special thanks to AAGaming00 and KieronQuinn for their bug reports.
|
||||
|
||||
## [5.12.0] - 2022-02-28
|
||||
|
||||
- Implement key-value pairs backup service support
|
||||
- Improve VPN application exclusion UI
|
||||
- Update AndroidX libraries
|
||||
- Update mongoose web server
|
||||
- Update translations
|
||||
- Update gradle
|
||||
- Update build tools
|
||||
|
||||
## [5.11.0] - 2021-12-20
|
||||
|
||||
- Improve home screen with icon color and decoration
|
||||
- Improve settings UI elements
|
||||
- Improve search filter performance in hosts list
|
||||
- Improve resource clean up after parsing hosts source
|
||||
- Improve logs with Timber
|
||||
- Improve button descriptions
|
||||
- Update Android gradle plugin
|
||||
- Update AndroidX libraries
|
||||
- Update third party libraries
|
||||
- Update mongoose web server
|
||||
- Update translations
|
||||
|
||||
Special thanks to brijrajparmar27 for its contribution.
|
||||
|
||||
## [5.10.0] - 2021-12-01
|
||||
|
||||
- Fix connectivity change detection in VPN mode
|
||||
- Update mongoose web server
|
||||
- Update target SDK to Android 12
|
||||
- Update dependencies
|
||||
- Update translations
|
||||
|
||||
Special thanks to 8asuj6m2 for its contribution.
|
||||
|
||||
## [5.9.0] - 2021-11-05
|
||||
|
||||
- Improve update activity to add support links and open from notification
|
||||
- Update web server certificate to comply with maximum validity time
|
||||
- Update dependencies
|
||||
- Update Android gradle plugin
|
||||
- Update NDK
|
||||
|
||||
Special thanks to rany2 for its bug report.
|
||||
|
||||
## [5.8.0] - 2021-08-08
|
||||
|
||||
- Improve command receiver for task automation
|
||||
- Update mongoose web server
|
||||
- Update dependencies
|
||||
- Update Android gradle plugin
|
||||
- Update translations
|
||||
|
||||
Special thanks to Faedelity for its bug report.
|
||||
|
||||
## [5.7.0] - 2021-06-27
|
||||
|
||||
- Add quick settings tile to toggle ad-blocking
|
||||
- Fix crash on TLS and timeout issue during source update
|
||||
- Fix backup not listed for restoration on older devices
|
||||
- Update dependencies
|
||||
- Update translations
|
||||
|
||||
Special thanks to gwolf2u, opusforlife2, Vstory for their bug reports.
|
||||
|
||||
## [5.6.0] - 2021-04-30
|
||||
|
||||
- Improve navigation by moving DNS logs to home screen
|
||||
- Improve DNS logs usage by explaining usage and limitation
|
||||
- Update dependencies
|
||||
- Update translations
|
||||
|
||||
## [5.5.1] - 2021-04-02
|
||||
|
||||
- Add redirection validation
|
||||
- Improve application update screen
|
||||
- Update Android gradle plugin
|
||||
- Update NDK
|
||||
- Update dependencies
|
||||
- Fix VPN crash when the only system DNS server available uses IPv6 and IPv6 is disabled from settings
|
||||
- Remove html-textview dependency and jcenter repository
|
||||
|
||||
Special thanks to FrostbiterTy, SapphireExile, zgfg for their bug reports.
|
||||
|
||||
## [5.5.0] - 2021-03-17
|
||||
|
||||
- Add allow list support
|
||||
- Improve source edition UI
|
||||
- Improve source update check
|
||||
- Improve animations
|
||||
- Update mongoose web server
|
||||
- Update dependencies
|
||||
- Fix web server TLS issue
|
||||
|
||||
Special thanks to gallegonovato, jawz101 and zgfg for their bug reports.
|
||||
|
||||
## [5.4.0] - 2021-02-28
|
||||
|
||||
- Add VPN monitor option to prevent disconnection
|
||||
- Update source update status indicator
|
||||
- Update Android gradle plugin
|
||||
- Update Sentry DSN to support older TLS versions
|
||||
- Update mongoose web server
|
||||
- Update dependencies
|
||||
- Fix welcome screen telemetry preference
|
||||
- Fix VPN authorization check at startup
|
||||
|
||||
Special thanks to BearTM, elvissteinjr, ipdev99, patkarmandar, RobBeyer for their bug reports.
|
||||
|
||||
## [5.3.0] - 2021-01-17
|
||||
|
||||
- Add new unique source parser with parallel processing
|
||||
- Add an option to disable app update check at startup
|
||||
- Fix crash with source file when SAF permission is removed
|
||||
- Fix metered status vpn on Android 11
|
||||
|
||||
Special thanks to andy356, fusionneur and sr1canskhsia for their bug reports.
|
||||
|
||||
## [5.2.1] - 2021-01-06
|
||||
|
||||
- Fix F-Droid store detection
|
||||
|
||||
Special thanks to TheLonelyGhost, bdtipsntricks, faraz-b bege10, Nathan-Nesbitt and gallegonovato for their bug reports.
|
||||
|
||||
## [5.2.0] - 2021-01-03
|
||||
|
||||
- Add beta channel opt-in preferences
|
||||
- Update mongoose to latest stable version and rewrite web server
|
||||
- Update source parser to prevent stack overflow
|
||||
- Update dependencies
|
||||
- Update build tools
|
||||
- Update NDK
|
||||
- Update translations
|
||||
|
||||
## [5.1.0] - 2020-11-12
|
||||
|
||||
- Add application update UI
|
||||
- Add F-Droid apk support to built-in updater
|
||||
- Add custom hosts file parser for big hosts file (1M+ entries, way slower but memory friendly)
|
||||
- Fix VPN app exclusion on Android 11
|
||||
- Update AndroidX and Sentry dependencies
|
||||
- Update translations
|
||||
|
||||
Special thanks to spiou, drothenberger and gallegonovato for their bug reports.
|
||||
|
||||
## [5.0.10] - 2020-10-10
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Fix online modification date for unavailable source
|
||||
- Prevent missing url at source creation
|
||||
- Prevent invalid source url to be restored
|
||||
- Fix web server certificate install on Android 11
|
||||
|
||||
Special thanks to zgfg, ipdev99 and ingenium13 for their bug reports.
|
||||
|
||||
## [5.0.9] - 2020-09-13
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Fix no hosts to block from 4.x to 5.x migration
|
||||
- Fix wrong source type in source edition UI
|
||||
- Update AndroidX dependencies
|
||||
- Update mongoose web server
|
||||
|
||||
Special thanks to auanasgheps, lukjod, ridobe, sacrificialpawn for their bug reports.
|
||||
|
||||
## [5.0.8] - 2020-08-30
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Fix source not updated on automatic update
|
||||
- Fix user list not sync until source update
|
||||
- Update hosts source creation to disable allowed hosts by default
|
||||
- Update AndroidX dependencies
|
||||
|
||||
Special thanks to jeanrivera for its bug report.
|
||||
|
||||
## [5.0.7] - 2020-08-16
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Add hosts sources Storage Access Framework support
|
||||
- Add hosts sources label and host counter
|
||||
- Improve host sources list and edition UI
|
||||
- Improve allowed and redirected hosts settings by applying them per source
|
||||
- Fix file based hosts sources not installed due to missing permission
|
||||
- Update AndroidX dependencies
|
||||
|
||||
Special thanks to zgfg for its bug report.
|
||||
|
||||
## [5.0.6] - 2020-08-02
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Fix crash on domain enable/disable action
|
||||
- Fix source not applied if disabled and enabled back
|
||||
- Update target SDK to Android 11
|
||||
|
||||
Special thanks to ipdev99 and zgfg for their bug reports.
|
||||
|
||||
## [5.0.5] - 2020-06-28
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Improve hosts source server handling with future time
|
||||
- Improve hosts update by skipping already up-to-date sources
|
||||
- Fix hosts source disable action
|
||||
- Fix hosts list apply notification from non-user changes
|
||||
- Fix user excluded application settings
|
||||
- Remove navigation bar color customization
|
||||
- Update AndroidX dependencies and NDK version
|
||||
|
||||
Special thanks to CobalTitan, ipdev99, zgfg and sunmybun for their bug reports.
|
||||
|
||||
## [4.3.5] - 2020-06-27
|
||||
|
||||
- Fix project Fastlane description for F-Droid store
|
||||
|
||||
Special thanks to linsui and IzzySoft for their bug reports and Vankog for translations.
|
||||
|
||||
## [5.0.4] - 2020-05-24
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Improve overall host list computation
|
||||
- Add host redirected feature in VPN ad blocking
|
||||
- Remove WRITE_EXTERNAL_STORAGE permission (use Storage Access Framework instead)
|
||||
- Fix duplicate entries in generated hosts file
|
||||
- Fix allowed hosts settings in VPN ad blocking
|
||||
- Fix backup not exported as sdcard not writable
|
||||
- Fix source update period task preference
|
||||
- Fix host list paging
|
||||
|
||||
Special thanks to holysnipz, ipdev99, QingKongBaiYu, and zgfg for their bug reports.
|
||||
|
||||
## [5.0.3] - 2020-05-10
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Add TLS support for web server
|
||||
- Add web server status in preferences
|
||||
- Add option to install self signed certificate
|
||||
- Add option to display app icon instead of blank page
|
||||
- Add full timezone support for source date
|
||||
- Add workaround for negative source update time when server time is not accurate
|
||||
- Add follow system dark theme mode
|
||||
- Fix user host list lost on source update
|
||||
- Fix import failed toast
|
||||
- Fix web server not start on install
|
||||
- Fix duplicate host entry on backup import
|
||||
- Update mongoose server
|
||||
- Update translations
|
||||
|
||||
Special thanks to saltylemondrops, zgfg, ipdev99 and mickrussom for their bug reports.
|
||||
|
||||
## [5.0.2] - 2020-04-25
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Fix timezone issues with source modification date
|
||||
- Fix domain not removed when sources are disabled
|
||||
- Fix inverted host and ip while generating hosts file
|
||||
- Fix periodical hosts update check initialization
|
||||
- Improve overall search feature in list UI
|
||||
- Improve last online modification date after retrieval
|
||||
- Fix install snackbar not hiding
|
||||
- Fix potential deadlock in VPN
|
||||
- Add missing text on successful VPN update
|
||||
- Update translations
|
||||
|
||||
Special thanks to damoasda for its contribution, Vankog for all translations he merged and Ps24u and dhacke for theirs bug reports.
|
||||
|
||||
## [4.3.4] - 2020-04-25
|
||||
|
||||
- Fix crash in tcpdump log view on Lollipop
|
||||
- Fix timezone issues with source modification date
|
||||
- Fix NDK version
|
||||
|
||||
Special thanks to Ps24u and Indranil012 for their bug reports.
|
||||
|
||||
## [5.0.1] - 2020-04-15
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Fix redirect label from home screen
|
||||
- Fix preference screen duplication on screen orientation change
|
||||
- Improve hosts list readability
|
||||
- Update dependencies
|
||||
- Fix ndk configuration
|
||||
|
||||
Special thanks to TacoTheDank for its contribution and rmw98 and Luniz2k1 for its bug reports.
|
||||
|
||||
## [4.3.3] - 2020-04-10
|
||||
|
||||
hpHosts service is down.
|
||||
If you are looking for a replacement, give a try at [StevenBlack's one](https://github.com/StevenBlack/hosts).
|
||||
|
||||
- Replace hpHosts default hosts file by StevenBlack hosts file
|
||||
- Improve tcpdump icons
|
||||
- Update translations
|
||||
|
||||
Special thanks to damoasda its contribution and gallegonovato for its bug report.
|
||||
|
||||
## [5.0.0] - 2020-04-07
|
||||
|
||||
*This version is a pre-release*
|
||||
|
||||
- Add new home screen
|
||||
- Provides all main controls from one screen
|
||||
- Displays currently blocked, allowed and redirected domains
|
||||
- Displays current hosts sources status and control to force apply
|
||||
- Add non root ad-blocking feature
|
||||
- Uses a builtin local VPN to filter DNS request to blocked domains
|
||||
- Based on the work of [dns66 by julian-klode](https://github.com/julian-klode/dns66/issues/39)
|
||||
- Allows to excluded system applications and per user applications
|
||||
- Add builtin updater with changelog display
|
||||
- Add feature to quickly pause and resume ad-blocking
|
||||
- Add wizard screen for first run setup
|
||||
- Add feature to display and filter all blocked, allowed, redirect domains
|
||||
- Improve preferences screen
|
||||
- Add broadcast receiver to control ad-blocking from third party applications
|
||||
- Update Android target to Android 10
|
||||
- Improve root and shell support
|
||||
- Split translation files to easier understand their context
|
||||
- Add GitHub action test and build tasks
|
||||
|
||||
## [4.3.2] - 2019-12-29
|
||||
|
||||
- Fix GitLab source hosting
|
||||
- Update translations
|
||||
|
||||
## [4.3.1] - 2019-12-21
|
||||
|
||||
- Update help to include Magisk systemless module for read-only system partition
|
||||
- Update translations
|
||||
|
||||
## [4.3.0] - 2019-11-01
|
||||
|
||||
- Fix root not requested
|
||||
- Improve support for systemless hosts Magisk module
|
||||
- Update translations
|
||||
|
||||
## [4.2.9] - 2019-08-31
|
||||
|
||||
- Improve hosts file parsing
|
||||
- Improve hosts file install error message (add more details than _not enough space_)
|
||||
- Fix menu drawer translation issue
|
||||
- Update translations
|
||||
- Remove the start (opt-in only) telemetry messages
|
||||
|
||||
## [4.2.8] - 2019-07-28
|
||||
|
||||
- Fix TravisCI build issues
|
||||
- Update translations
|
||||
|
||||
## [4.2.7] - 2019-07-04
|
||||
|
||||
- Revert Android gradle plugin to fix F-Droid build issue
|
||||
|
||||
## [4.2.6] - 2019-06-23
|
||||
|
||||
- Improve backup feature (user lists and hosts source using JSON format)
|
||||
- Fix F-Droid build issue
|
||||
- Update translations
|
||||
|
||||
Special thanks to RichyHBM its contribution and andy356 for its bug report.
|
||||
|
||||
## [4.2.5] - 2019-06-06
|
||||
|
||||
- Add Gist and GitLab hosting support for hosts file
|
||||
- Add option to set default IPv6 redirection
|
||||
- Improve reboot command
|
||||
- Improve UI for overlays
|
||||
- Update translations
|
||||
|
||||
Special thanks to MSF-Jarvis and Ralayax for their contributions
|
||||
|
||||
## [4.2.4] - 2019-03-23
|
||||
|
||||
- Add dedicated no root error message
|
||||
- Fix connection requirement for automatic update
|
||||
- Fix crash on TCP dump views when root access is denied
|
||||
- Fix icon resources and colors
|
||||
- Improve exception reporting
|
||||
- Update translations
|
||||
- Update Android X dependencies
|
||||
- Update Android Gradle plugin and NDK versions
|
||||
|
||||
## [4.2.3] - 2019-03-02
|
||||
|
||||
- Fix update check on disabled sources
|
||||
- Fix cropped label on home screen
|
||||
- Prevent app installation on external storage (can't launch tcpdump or web server binary)
|
||||
- Update work manager and material dependencies
|
||||
|
||||
## [4.2.2] - 2019-02-16
|
||||
|
||||
- Improve Material Theming
|
||||
- Update build tools
|
||||
|
||||
## [4.2.1] - 2019-02-03
|
||||
|
||||
- Fix two buttons line when text too long
|
||||
|
||||
## [4.2.0] - 2019-02-02
|
||||
|
||||
- Add hosts source download cache
|
||||
- Add snackbar notification to update host from DNS request listing
|
||||
- Update UI from Material Design to Material Theming
|
||||
- Update gradle, plugins and dependencies
|
||||
- Fix crash parsing not defined host source last modified date
|
||||
- Fix native modules build script (required for F-Droid build server)
|
||||
- Fix Transifex issues
|
||||
|
||||
## [4.1.0] - 2018-12-13
|
||||
|
||||
- Add [telemetry feature](https://github.com/AdAway/AdAway/wiki/Telemetry)
|
||||
- Add snackbar notification to update host when editing hosts sources or lists
|
||||
- Update translations and fix english locale issues
|
||||
|
||||
## [4.0.12] - 2018-11-21
|
||||
|
||||
- Fix issue when getting last modified date on file:// hosts source
|
||||
- Fix excluded hostnames from source due to parser failure
|
||||
|
||||
Special thanks to DiamondJohn and Vankog for theirs helpful bug reports.
|
||||
|
||||
## [4.0.11] - 2018.11.06
|
||||
|
||||
- Update translations from Transifex
|
||||
- Fix crash using file:// protocol for hosts source
|
||||
- Fix redirect list import
|
||||
|
||||
Special thanks to ipdev, ktmom and shaqman89 for theirs helpful bug reports and Vankog for the locales update.
|
||||
|
||||
## [4.0.10] - 2018.10.14
|
||||
|
||||
- Last update time now works with GitHub hosted files (on https://raw.githubusercontent.com/ domain)
|
||||
- Fix infinite "update available" status when at least one host source failed to download
|
||||
- Fix hosts not installed by the background update service
|
||||
- Fix hosts source update time when reverting to default hosts file
|
||||
- Fix "download failed" status when no host source enabled
|
||||
- Fix a bunch of translation issues
|
||||
|
||||
Special thanks to Alain-Olivier and Vankog for theirs contributions and MarcAnt01 for its helpful bug report.
|
||||
|
||||
## [4.0.9] - 2018.09.26
|
||||
|
||||
- Fix missing reboot and error dialog when installing and checking for hosts update
|
||||
- Block http hosts source for security
|
||||
- Add project and support links to the menu
|
||||
- Fix missing notification channel for Oreo and later
|
||||
- Fix host name validation to add more complex domain name in black/white lists
|
||||
- Improve HTTP client connection pool
|
||||
- Add new internal architecture for hosts installation
|
||||
- Fix Indonesian locale code
|
||||
- Clean up a lot a unused resource (texts and graphics)
|
||||
- Clean up help from unrelated help elements
|
||||
- Update gradle itself, plugin, ndk and dependencies versions
|
||||
|
||||
Special thanks to adem4ik TacoTheDank and @Vankog for theirs contributions and towlie, ipdev for theirs helpful bug reports.
|
||||
|
||||
## [4.0.8] - 2018.08.22
|
||||
|
||||
- Add option to dismiss welcome card
|
||||
- Improve hosts update status
|
||||
- Change background job dependency from Evernote Android Job to Jetpack Work manager
|
||||
- Update translations
|
||||
|
||||
## [4.0.7] - 2018.08.08
|
||||
|
||||
- Fix host lists import
|
||||
- Fix default DNS requests when no log entry
|
||||
- Update translations from Transifex
|
||||
|
||||
Special thanks to Vankog for the translation update and GuardianUSMC and DiamondJohn for the bug reports.
|
||||
|
||||
## [4.0.6] - 2018.08.05
|
||||
|
||||
- Add web server status (icon and text) in the home card UI
|
||||
- Fix black and while list inversion bug
|
||||
|
||||
## [4.0.5] - 2018.07.02
|
||||
|
||||
- Change database to room:
|
||||
- Add migration from previous database
|
||||
- Update hosts source UI:
|
||||
- New animations
|
||||
- Dialogs validate user data so you do no more loose your input if format is wrong
|
||||
- New DNS logging UI:
|
||||
- Add new sort feature:
|
||||
- by name (old behavior)
|
||||
- by top level domain name (group entries by DNS so google.com, ads.google.com and www.google.com appears next to each other)
|
||||
- New controls and animations:
|
||||
- Block, allow or redirect from the directly from DNS logs
|
||||
- Currently set up domains (in your lists UI) will be displayed accordingly
|
||||
- Swipe to refresh!
|
||||
- Update build tools, target SDK (28) and dependencies
|
||||
|
||||
## [4.0.4] - 2018.06.03
|
||||
|
||||
- A new light (white) theme
|
||||
- A new adware UI (using LiveData and ViewModel for the 1st time!)
|
||||
- A fix for the overlapping status texts in the home screen
|
||||
|
||||
## [4.0.3] - 2018.05.20
|
||||
|
||||
- Fix tcpdump failed to start after being stopped
|
||||
|
||||
## [4.0.2] - 2018.05.09
|
||||
|
||||
- Add adaptive launcher icons (8+)
|
||||
- Add adaptive app shortcut (7.1+)
|
||||
- Add new hosts content screen
|
||||
- Fix application title not restored on configuration change
|
||||
- Fix screen change when opening hosts file
|
||||
|
||||
## [4.0.1] - 2018.05.07
|
||||
|
||||
- Fix redirection IP and custom target dialogs in preferences
|
||||
- Fix multiple lines in home card hedear.
|
||||
|
||||
## [4.0.0] - 2018.05.06
|
||||
|
||||
This version is a major update.
|
||||
First, There is a new design:
|
||||
|
||||
- The home is now card based (webserver card is added if enabled)
|
||||
- The navigation menu is now an humbugger menu
|
||||
- The hosts source UI is updated (floating add button, actionbar edition)
|
||||
- Your lists UI is updated (bottom navigation bar, same controls add, edit, remove like hosts sources)
|
||||
- Permission request at runtime to access storage to import or export your list
|
||||
- All other views are updated to use the latest support libraries
|
||||
|
||||
And a lot of changes under the hood including:
|
||||
|
||||
- Oreo support
|
||||
- Battery improvement and host update fix
|
||||
- Better support of root and systemless mode
|
||||
|
||||
## [3.3] - 2018.03.05
|
||||
- Add support for ChainFire's SuperSU "bind sbin" systemless mode - by PerfectSlayer
|
||||
- Improve systemless activation error handling - by PerfectSlayer
|
||||
- Improve su location detection - by tstaylor7
|
||||
- Update Mongoose webserver - by MrRobinson
|
||||
- Replace CyanogenMod references by LineageOS - by experience7
|
||||
- Update translations - by Mattter, mission712, ThomasSmallert and muzena
|
||||
- Fix translation attributes - by Vankog
|
||||
- Fix Magisk 15.x support - by pec0ra
|
||||
|
||||
## [3.2]
|
||||
- Systemless root support - extensive work by PerfectSlayer
|
||||
- Improvements to root mounter - by sanjay900
|
||||
- Translations updates
|
||||
- Various updates for support on Android 7.x
|
||||
|
||||
## [3.1.2]
|
||||
- Update hosts-file.net source to use https
|
||||
- Translations updates
|
||||
|
||||
## [3.1.1]
|
||||
- Minor bugfixes
|
||||
- Update mongoose internal webserver to 6.4 release
|
||||
- Translations updates
|
||||
- Update pgl.yoyo.org default source to use https
|
||||
- Add generated timestamp into header of hosts file
|
||||
|
||||
## [3.1]
|
||||
- Add 64bit arch which allows tcpdump to run on those devices
|
||||
- Adjust scan adware list and stop it from crashing
|
||||
- Update libpcap to 1.7.4 release
|
||||
- Update tcpdump to 4.7.4 release
|
||||
- Update mongoose internal webserver to 6.0 release and add IPv6 support
|
||||
- Add enable IPv6 option which adds ::1 to all hosts entries
|
||||
- Improve building hosts file speed by up to 2.5x
|
||||
- RevertService uses Target hosts file preference
|
||||
- Updates to support Android 6.0 SDK (23)
|
||||
|
||||
## [3.0.2]
|
||||
- Disable autocorrect/autocomplete for hosts input on whilelist and redirection inputs
|
||||
- Allow two pane layout as long as min of 600dp width available regardless of orientation
|
||||
- Add material colored status icons
|
||||
- Adjust asset layout per latest development guidelines
|
||||
- Minor other adjustments
|
||||
|
||||
## [3.0.1]
|
||||
- Make some text fields single line - by Phoenix09
|
||||
- Make daily update time randomized within a range
|
||||
- Minor fixes
|
||||
- Adjust hosts-file.net default source URL
|
||||
|
||||
## [3.0]
|
||||
This release has mainly been done by 0-kaladin.
|
||||
|
||||
- Min Android version increased to Android 4.1 to support Position Independent Executables (PIE)
|
||||
- Material design
|
||||
|
||||
## [2.9.2]
|
||||
This release has mainly been done by Dāvis Mošenkovs.
|
||||
|
||||
- Added Trove library for high performance collections
|
||||
- Adware scan improvements
|
||||
- Separate whitelisting and redirections options - "Allow whitelisting" defaults to checked; "Allow redirections" defaults to state of "Allow redirections and whitelisting" (or unchecked on new installations)
|
||||
- Fix tcpdump logging upon file deletion
|
||||
- Fix possible crash on DB update
|
||||
- Fix AdAway's default hosts source
|
||||
|
||||
## [2.9.1]
|
||||
- Fixing regression bugs
|
||||
|
||||
## [2.9]
|
||||
This release has mainly been done by Dāvis Mošenkovs, thanks!
|
||||
|
||||
- Workaround for Android 4.4 (see Help)
|
||||
- Fixed Hide reboot dialog setting being ignored after symlink creation
|
||||
- Fix crashes
|
||||
- Change AdAway's default hosts source to https
|
||||
|
||||
## [2.8.1]
|
||||
- Fix mobile hosts source
|
||||
|
||||
## [2.8]
|
||||
- Higher timeout for root commands
|
||||
- New version of RootCommands library (If you experience problems install busybox, AdAway will then use it!)
|
||||
- Remove unused billing permission
|
||||
|
||||
## [2.7]
|
||||
- Reduce apk size by switching from HtmlSpanner to HtmlTextView library
|
||||
|
||||
## [2.6]
|
||||
- Improve build for F-Droid
|
||||
|
||||
## [2.5]
|
||||
- Fix URLs in-app
|
||||
|
||||
## [2.4]
|
||||
- Fix Remounter for Android 4.3
|
||||
- Fix auto update on ethernet connection
|
||||
|
||||
## [2.3]
|
||||
- AdAway was removed from Google Play!
|
||||
- Because http://www.ismeh.com/HOSTS is down, a alternative source has been added
|
||||
|
||||
## [2.2]
|
||||
- Introduce SUPERUSER permission for new Superuser app
|
||||
- Allow backups of AdAway (for Carbon)
|
||||
- Logo reworked thanks to Alin Ţoţea-Radu
|
||||
|
||||
## [2.1]
|
||||
- Allow whitelist entries from hosts sources if enabled in preferences
|
||||
- Fixed missing menu entry for hosts sources on tablets
|
||||
- Webserver on boot should work more reliable
|
||||
- Webserver should not be killed on low memory
|
||||
|
||||
## [2.0]
|
||||
IF YOU HAVE PROBLEMS WITH 2.0: Please uninstall and then reinstall AdAway!
|
||||
- New library for root access: RootCommands
|
||||
- Tcpdump and Webserver now included for ARM, x86, MIPS (Please test and report problems!)
|
||||
- Google forced me to remove the possibility to donate via Flattr and PayPal
|
||||
- Hopefully fixes DNS logging for Android 4.1
|
||||
|
||||
## [1.37]
|
||||
- Skip unreachable hosts sources and show number of successful sources
|
||||
- Fix for crash on donations screen (Android 4.1)
|
||||
- Fix crash on help screen (Android 4.1)
|
||||
- Bind local webserver https only to localhost (thanks to Stebalien for finding that bug)
|
||||
|
||||
## [1.36]
|
||||
- Help and About screens reworked
|
||||
- Simple scanner for bad Adware apps like Airpush Notifications (derived from open source Airpush-Detector, thanks!)
|
||||
|
||||
## [1.35]
|
||||
- New method for background update checking
|
||||
- AdAway will now reschedule the update check to execute it when the Internet connection is established
|
||||
- New preference to update only when on Wifi
|
||||
- AdAway now allows empty hosts sources for people who only want to maintain their own lists
|
||||
|
||||
## [1.34]
|
||||
- In-app PayPal donations are now possible
|
||||
- Disabled hardware acceleration on ICS, caused black screens and glitches on some custom roms
|
||||
|
||||
## [1.33]
|
||||
- Fixed rare problems on some devices regarding remounting /system as read/write
|
||||
|
||||
## [1.32]
|
||||
- Fixes crashes with 1.31
|
||||
|
||||
## [1.31]
|
||||
- Check for APN proxy
|
||||
|
||||
## [1.30]
|
||||
- Design improvements for Android 4
|
||||
- Layout fixes for "Your Lists"
|
||||
|
||||
## [1.29]
|
||||
- Languages updated
|
||||
- Fixed copying when no cp command is available
|
||||
|
||||
## [1.28]
|
||||
- Now works on devices without cp command
|
||||
- New debug setting
|
||||
|
||||
## [1.27]
|
||||
- No longer needs busybox
|
||||
- Fixed problems with reverting
|
||||
- Method to restart Android changed
|
||||
|
||||
## [1.26]
|
||||
- Usability improvements: Buttons will now be disabled while applying, reverting is improved
|
||||
- Faster due to improved hosts parsing
|
||||
- Now including tcpdump
|
||||
|
||||
## [1.25]
|
||||
- Better tcpdump integration
|
||||
- Wildcard characters * and ? can be used in your whitelist
|
||||
- Updated translations
|
||||
- Fixes stuck on applying hopefully
|
||||
|
||||
## [1.24]
|
||||
- Tcpdump DNS request logging
|
||||
- Preference to allow redirection rules from Hosts Sources
|
||||
- New hosts source for mobile ads: http://www.ismeh.com/HOSTS
|
||||
- Webserver binary now updates correctly from old AdAway versions
|
||||
|
||||
## [1.23]
|
||||
- Fix for import/export
|
||||
|
||||
## [1.22]
|
||||
- More notification and status bug fixes
|
||||
- Fixed preference bug causing automatic update to be enabled
|
||||
- Fixed color of notifications
|
||||
- Fixed a crash under Android 3.2
|
||||
|
||||
## [1.21]
|
||||
- Fixed staying notification when update was failing
|
||||
- Hopefully fixes webserver crashes
|
||||
|
||||
## [1.20]
|
||||
- Fixed staying notification
|
||||
|
||||
## [1.19]
|
||||
- Automatic updating in background can be enabled in preferences
|
||||
- New method for checking for symlink, should fix some problems
|
||||
- Fixes for exporting entries
|
||||
- Fixes for donation screen, when no Google Android Market is available
|
||||
|
||||
## [1.18]
|
||||
- Local webserver now answers with blank page instead of 404 error page
|
||||
- Import and Export of Your Lists
|
||||
- Bug fix for symlink check
|
||||
- Last entry in hosts file is now working (missed new line at end of hosts file)
|
||||
- Fixes for daily update check
|
||||
- Allow hostnames without TLD ending
|
||||
|
||||
## [1.17]
|
||||
- Fix for translation problems
|
||||
|
||||
## [1.16]
|
||||
- Translations: German, French, Spanish
|
||||
Thanks to all contributors!
|
||||
- Better check for symlink
|
||||
- Newer version of web server mongoose
|
||||
- Better handling of AdAway database
|
||||
- Fixed "Not enough space available" bug
|
||||
- Fixed problem with applying
|
||||
|
||||
## [1.15]
|
||||
- Added permission for Google Android Market donations
|
||||
|
||||
## [1.14]
|
||||
- Force close on open hosts file fixed
|
||||
- Donations with Google Android Market added
|
||||
|
||||
## [1.13]
|
||||
- Webserver now hears on all local IP address (0.0.0.0)
|
||||
- Custom target can be set in preferences, for example to /data/etc/hosts
|
||||
- Hosts file can be opened from menu
|
||||
|
||||
## [1.12]
|
||||
- Delayed starting of webserver on boot
|
||||
- Disabled debug logging
|
||||
|
||||
## [1.11]
|
||||
- Fixed bugs in layout
|
||||
|
||||
## [1.10]
|
||||
- Webserver is now a preference, disabled by default
|
||||
- Fixed daily update again. Should schedule now correctly
|
||||
- Fixed crash on Android 3.0 and 3.1 in Your Lists
|
||||
- Fixed preferences on Android 3.x
|
||||
- Fixed rotation bug on Android 3.x
|
||||
|
||||
## [1.09]
|
||||
- New design for tablet sizes
|
||||
- New hosts source: http://pgl.yoyo.org/adservers
|
||||
- fixed crash on Android 3 Honeycomb
|
||||
- Fix for daily update check
|
||||
- Removed hosts source sysctl.org because of false positives
|
||||
|
||||
## [1.08]
|
||||
- AdAway got a redesign
|
||||
- AdAway ships with a webserver, that listens on localhost
|
||||
- Dates of all hosts sources are saved and can be seen in Hosts sources
|
||||
- Preference to hide reboot question dialog
|
||||
- Help page with information about AdAway
|
||||
- Added new hosts source sysctl.org
|
||||
- Daily update check can be enabled in preferences
|
||||
- Should now work on roms which doesn't symlink busybox commands
|
||||
|
||||
## [1.07]
|
||||
- AdAway can now create a symlink
|
||||
- better error handling
|
||||
- No update check on orientation change
|
||||
|
||||
## [1.06]
|
||||
- hosts file target can be choosen
|
||||
- Donation button
|
||||
- Fixed SQLite bug occuring on Android 2.1
|
||||
|
||||
## [1.05]
|
||||
- Update Check implemented
|
||||
- Fixed bug when changing orientation of device while downloading
|
||||
- better error handling when downloading
|
||||
- Fixed localhost entry again
|
||||
|
||||
## [1.04]
|
||||
- Implemented Redirection List
|
||||
- Fixed Layout bugs
|
||||
|
||||
## [1.03]
|
||||
- Implemented Blacklist and Whitelist
|
||||
|
||||
## [1.02]
|
||||
- Fixed localhost entry
|
||||
|
||||
## [1.01]
|
||||
- Fixed permissions on /system/etc/hosts
|
||||
76
CODE_OF_CONDUCT.md
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at report [at] adaway.org. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
169
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
# Contributing to AdAway
|
||||
|
||||
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
|
||||
|
||||
The following is a set of guidelines for contributing to AdAway.
|
||||
These are mostly guidelines, not rules.
|
||||
It will help you to understand the project, find answers, deal with the source code and interact with maitainers.
|
||||
The project is open to any kind of contribution so feel free to share your ideas and participate to the development.
|
||||
|
||||
#### Table of contents
|
||||
|
||||
[I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question)
|
||||
|
||||
[What should I know before I get started?](#what-should-i-know-before-i-get-started)
|
||||
* [Discovering the project structure](#discovering-the-project-structure)
|
||||
* [Building the project](#building-the-project)
|
||||
|
||||
[How can I contribute?](#how-can-i-contribute)
|
||||
* [Reporting bugs](#reporting-bugs)
|
||||
* [Suggesting enhancements](#suggesting-enhancements)
|
||||
* [Translating to your language](#translating-to-your-language)
|
||||
* [Your first code contribution](#your-first-code-contribution)
|
||||
|
||||
[Styleguides](#styleguides)
|
||||
* [Git commit messages](#git-commit-messages)
|
||||
* [Java styleguide](#java-styleguide)
|
||||
* [XML styleguide](#xml-styleguide)
|
||||
|
||||
[Additional notes](#additional-notes)
|
||||
* [tcpdump and webserver modules](#tcpdump-and-webserver-modules)
|
||||
|
||||
## I don't want to read this whole thing I just have a question!!!
|
||||
|
||||
> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below.
|
||||
|
||||
We have a dedicated forum with a welcoming community and a wiki to answer your questions:
|
||||
|
||||
* [Check the common issues and solutions on the wiki](https://github.com/AdAway/AdAway/wiki/Solutions)
|
||||
* [Read and post on the dedicated developer forum](https://forum.xda-developers.com/showthread.php?t=2190753)
|
||||
|
||||
## What should I know before I get started?
|
||||
|
||||
### Discovering the project structure
|
||||
|
||||
The AdAway source code is an Android project organized in modules.
|
||||
There are four main modules:
|
||||
* `app`: The Android application itself
|
||||
* `tcpdump`: A module dedicated to build the `pcap` library and the `tcpdump` binary
|
||||
* `webserver`: A module dedicated to build a simple HTTP server binary based on `mongoose`
|
||||
* `libraries/RootCommands`: A vendorize Android library to run root shell commands
|
||||
|
||||
The three last modules are independent and used by the `app` module.
|
||||
Modularizing the application allows for faster build times and simplier maintainance.
|
||||
|
||||
### Building the project
|
||||
|
||||
Building the project will require the latest versions of the Android SDK (Software Development Kit) and NDK (Native Development kit).
|
||||
They can easily be installed or updated using [Android Studio](https://developer.android.com/studio/).
|
||||
|
||||
#### Building with Gradle
|
||||
1. Ensure you have Android SDK and NDK installed.
|
||||
If not:
|
||||
* Option 1: [Install Android Studio](https://developer.android.com/studio/index.html) or,
|
||||
* Option 2: Install command line tools, build tools and ndk bundle with sdk manager:
|
||||
`tools/bin/sdkmanager "build-tools;x.y.z" ndk-bundle` where `x.y.z` is the latest version
|
||||
2. Export `ANDROID_HOME` environment variable pointing to your Android SDK:
|
||||
`export ANDROID_HOME=/path/to/your/sdk`
|
||||
3. Launch a build:
|
||||
`./gradlew assembleRelease`
|
||||
|
||||
The first full build of the apk can take a lot of time, about 20 minutes, whereas an incremental build of the `app` module takes less than a dozen seconds.
|
||||
|
||||
#### Running on an emulator
|
||||
|
||||
In order to test the application on an emulator, disable [the root check in the Constants source file](https://github.com/AdAway/AdAway/blob/c90336cb9b062220540317bc6c7cfedb19927c63/app/src/main/java/org/adaway/util/Constants.java#L28).
|
||||
|
||||
## How can I contribute?
|
||||
|
||||
### Reporting bugs
|
||||
|
||||
> **Note:** Before submitting a bug report, please use [the GitHub search on Issues page](https://github.com/AdAway/AdAway/issues) to check if there is already similar reports.
|
||||
|
||||
#### How do I submit a (good) bug report?
|
||||
|
||||
* **Use a clear and descriptive title** for the issue to identify the problem.
|
||||
* **Describe the exact steps which reproduce the problem** in the most detailed way possible.
|
||||
* **Provide specific examples to demonstrate the steps**.
|
||||
Include hosts sources or domains you use, web pages URL you test.
|
||||
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
|
||||
* **Explain which behavior you expected to see instead and why.**
|
||||
* **If you're reporting that AdAway crashed**, include a logcat.
|
||||
Use `adb logcat` if you have developer settings enabled on your device or use any application like [CatLog](https://play.google.com/store/apps/details?id=com.nolanlawson.logcat) to save logs.
|
||||
Include the crash report in the issue in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines), a [file attachment](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests/), or put it in a [gist](https://gist.github.com/) and provide link to it.
|
||||
* **Specify which version of AdAway you're using.**
|
||||
You can get the exact version by opening in-app help and checking the _About_ tab.
|
||||
* **Specify the Android version and the ROM you're using.**
|
||||
You can also include any root or customization related information like _Magisk_ or _SuperSU_ version and _Xposed_ modules is installed.
|
||||
|
||||
|
||||
### Suggesting enhancements
|
||||
|
||||
#### How do I submit a (good) enhancement suggestion?
|
||||
|
||||
Enhancement suggestions are welcome.
|
||||
After refining your idea or discussing it on the [development forum](https://forum.xda-developers.com/showthread.php?t=2190753), create an issue and provide the following information:
|
||||
|
||||
* **Use a clear and descriptive title** for the issue to identify the suggestion.
|
||||
* **Provide a step-by-step description of the suggested enhancement** in the most detailed way possible, including specific examples.
|
||||
* **Describe the current behavior** and **explain which behavior you expected to see instead** and why.
|
||||
* **Explain why this enhancement would be useful** to most users.
|
||||
|
||||
### Translating to your language
|
||||
|
||||
Translations are also welcome.
|
||||
Moreover, they do not require a development environment, only a web browser.
|
||||
So if you want to complete or edit your language support for the application, check [the translation guide](TRANSLATING.md).
|
||||
|
||||
### Your first code contribution
|
||||
|
||||
Unsure where to begin contributing?
|
||||
You can start by looking through these `good first issue` and `help wanted` issues:
|
||||
|
||||
* [Good first issues](https://github.com/AdAway/AdAway/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) - issues which should only require a few lines of code, and a test or two.
|
||||
* [Help wanted issues](https://github.com/AdAway/AdAway/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) - issues which should be a bit more involved than `beginner` issues.
|
||||
|
||||
Both issue lists are sorted by total number of comments. While not perfect, the number of comments is a reasonable way of determining the impact a given change will have.
|
||||
|
||||
## Style guidelines
|
||||
|
||||
### Git commit messages
|
||||
|
||||
* Use the present tense ("Add feature" not "Added feature")
|
||||
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
|
||||
* Limit the first line to 80 characters or less
|
||||
* Reference issues and pull requests liberally after the first line
|
||||
|
||||
### Java style guidelines
|
||||
* Indentation: 4 spaces, no tabs
|
||||
* Maximum line width for code and comments: 100
|
||||
* Opening braces don't go on their own line
|
||||
* Field names: Non-public, non-static fields start with m.
|
||||
* Acronyms are words: Treat acronyms as words in names, yielding !XmlHttpRequest, getUrl(), etc.
|
||||
|
||||
See https://source.android.com/source/code-style.html
|
||||
|
||||
### XML style guidelines
|
||||
* No maximum line width
|
||||
* Split multiple attributes each on a new line
|
||||
* Indent using spaces with Indention size 4
|
||||
|
||||
## Additional notes
|
||||
|
||||
### `tcpdump` and `webserver` modules
|
||||
|
||||
#### Origin
|
||||
|
||||
Forked from the following sources and slightly modified to compile:
|
||||
|
||||
* dnsmasq: https://github.com/CyanogenMod/android_external_dnsmasq
|
||||
* libpcap: https://github.com/the-tcpdump-group/libpcap/tree/libpcap-1.7.4
|
||||
* tcpdump: https://github.com/the-tcpdump-group/tcpdump/tree/tcpdump-4.7.4
|
||||
|
||||
#### Changes
|
||||
|
||||
Please review the following commits for the changes made to the sources above in order for them to compile in this project:
|
||||
|
||||
* Commit: https://github.com/AdAway/AdAway/commit/1f4ccb3cec3758757341ad90813506fc2a8fdf7b
|
||||
* Commit: https://github.com/AdAway/AdAway/commit/289df896c0ac4f96bd862e8a5054f1011ec07cac
|
||||
* Commit: https://github.com/AdAway/AdAway/commit/08da0745b0732b94221c0f5746160fef8126fd99
|
||||
674
LICENSE.md
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
120
README.md
|
|
@ -1,3 +1,119 @@
|
|||
# adaway
|
||||
#  AdAway
|
||||
|
||||
Android Ad Blocker
|
||||
[](https://github.com/AdAway/AdAway/actions/workflows/android-ci.yml)
|
||||
[](https://sonarcloud.io/project/overview?id=org.adaway)
|
||||
[](https://github.com/AdAway/AdAway/releases)
|
||||
[](https://github.com/sponsors/PerfectSlayer)
|
||||
[](/LICENSE.md)
|
||||
|
||||
AdAway is an open source ad blocker for Android using the hosts file and local vpn.
|
||||
|
||||
[<img src="metadata/en-US/phoneScreenshots/screenshot1.png"
|
||||
alt="Home screen"
|
||||
height="256">](metadata/en-US/phoneScreenshots/screenshot1.png)
|
||||
[<img src="metadata/en-US/phoneScreenshots/screenshot2.png"
|
||||
alt="Preferences screen"
|
||||
height="256">](metadata/en-US/phoneScreenshots/screenshot2.png)
|
||||
[<img src="metadata/en-US/phoneScreenshots/screenshot3.png"
|
||||
alt="Root based ad blocker screen"
|
||||
height="256">](metadata/en-US/phoneScreenshots/screenshot3.png)
|
||||
[<img src="metadata/en-US/phoneScreenshots/screenshot4.png"
|
||||
alt="Backup and restore screen"
|
||||
height="256">](metadata/en-US/phoneScreenshots/screenshot4.png)
|
||||
[<img src="metadata/en-US/phoneScreenshots/screenshot5.png"
|
||||
alt="Help screen"
|
||||
height="256">](metadata/en-US/phoneScreenshots/screenshot5.png)
|
||||
|
||||
For more information visit https://adaway.org
|
||||
|
||||
## Installing
|
||||
|
||||
There are two kinds of release:
|
||||
* The preview builds: on the bleeding edge of development - for testers or adventurous
|
||||
* The stable builds: ready for every day usage - for end users
|
||||
|
||||
### Preview builds
|
||||
|
||||
**Requirements:** Android 8 _Oreo_ or above
|
||||
|
||||
For users with bugs, there may be preview builds available from the [XDA development thread](https://forum.xda-developers.com/showthread.php?t=2190753) and [AdAway official website](https://app.adaway.org/beta.apk).
|
||||
It is recommended to try those builds to see if your issue is resolved before creating an issue.
|
||||
The preview builds may contain bug fixes or new features for new android versions.
|
||||
|
||||
[<img src="Resources/get-it-on-adaway.png"
|
||||
alt="Get it on official AdAway website"
|
||||
height="80">](https://app.adaway.org/beta.apk)
|
||||
[<img src="Resources/XDADevelopers.png"
|
||||
raw="true"
|
||||
alt="Get it on XDA forum"
|
||||
height="60">](https://forum.xda-developers.com/showthread.php?t=2190753)
|
||||
|
||||
### Stable builds
|
||||
|
||||
**Requirements:**
|
||||
* Android Android 8 _Oreo_ or above
|
||||
|
||||
After preview builds have been tested by the more technical or responsive community within the forums, we will then post the stable build to F-Droid.
|
||||
|
||||
[<img src="Resources/get-it-on-adaway.png"
|
||||
alt="Get it on official AdAway website"
|
||||
height="80">](https://app.adaway.org/adaway.apk)
|
||||
[<img src="Resources/get-it-on-fdroid.png"
|
||||
raw="true"
|
||||
alt="Get it on F-Droid"
|
||||
height="80">](https://f-droid.org/app/org.adaway)
|
||||
|
||||
For devices older than Android 8 _Oreo_, use the version 4 of AdAway.
|
||||
|
||||
## Get Host File Sources
|
||||
|
||||
See the [Wiki](https://github.com/AdAway/AdAway/wiki), in particular the page [HostsSources](https://github.com/AdAway/AdAway/wiki/HostsSources) for an assorted list of sources you can use in AdAway.
|
||||
Add the ones you like to the AdAway "Hosts sources" section.
|
||||
|
||||
## Getting Help
|
||||
|
||||
You can post [Issues](https://github.com/AdAway/AdAway/issues) here or obtain more detailed community support via the [XDA developer thread](http://forum.xda-developers.com/showthread.php?t=2190753).
|
||||
|
||||
## Contributing
|
||||
|
||||
You want to be involved in the project? Welcome onboard!
|
||||
Check [the contributing guide](CONTRIBUTING.md) to learn how to report bugs, suggest features and make you first code contribution :+1:
|
||||
|
||||
If you are looking for translating the application in your language, [the translating guide](TRANSLATING.md) is for you.
|
||||
|
||||
## Project Status
|
||||
|
||||
AdAway is actively developed by:
|
||||
* Bruce Bujon ([@PerfectSlayer](https://github.com/PerfectSlayer)) - Developer
|
||||
[PayPal](https://paypal.me/BruceBUJON) | [GitHub Sponsorship](https://github.com/sponsors/PerfectSlayer)
|
||||
* Daniel Mönch ([@Vankog](https://github.com/Vankog)) - Translations
|
||||
* Jawz101 ([@jawz101](https://github.com/jawz101)) - Hosts list
|
||||
* Anxhelo Lushka ([@AnXh3L0](https://github.com/AnXh3L0)) - Web site
|
||||
|
||||
We do not forget the past maintainers:
|
||||
* Dāvis Mošenkovs ([@DavisNT](https://github.com/DavisNT)) - Developer
|
||||
[Paypal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5GUHNXYE58RZS&lc=US&item_name=AdAway%20Donation&no_note=0&no_shipping=1)
|
||||
* [@0-kaladin](https://github.com/0-kaladin) - Developer and XDA OP
|
||||
* Sanjay Govind ([@sanjay900](https://github.com/sanjay900)) - Developer
|
||||
|
||||
And we thank a lot to the original author:
|
||||
* Dominik Schürmann ([@dschuermann](https://github.com/dschuermann)) - Original developer
|
||||
[Paypal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=android%40schuermann.eu&lc=US&item_name=AdAway%20Donation&no_note=0&no_shipping=1¤cy_code=EUR) | [Flattr](https://flattr.com/thing/369138/AdAway-Ad-blocker-for-Android) | BTC: `173kZxbkKuvnF5fa5b7t21kqU5XfEvvwTs`
|
||||
|
||||
## Permissions
|
||||
|
||||
AdAway requires the following permissions:
|
||||
|
||||
* `INTERNET` to download hosts files and application updates. It can send bug reports and telemetry [if the user wants to (opt-in only)](https://github.com/AdAway/AdAway/wiki/Telemetry)
|
||||
* `ACCESS_NETWORK_STATE` to restart VPN on network connection change
|
||||
* `RECEIVE_BOOT_COMPLETED` to start the VPN on boot
|
||||
* `FOREGROUND_SERVICE` to run the VPN service in foreground
|
||||
* `POST_NOTIFICATIONS` to post notifications about hosts source update, application update and VPN controls. All notifications can be enabled or disabled independently.
|
||||
* `REQUEST_INSTALL_PACKAGES` to update the application using the builtin updater
|
||||
* `QUERY_ALL_PACKAGES` to let the user pick the applications to exclude from VPN
|
||||
|
||||
## Licenses
|
||||
|
||||
AdAway is licensed under the GPLv3+.
|
||||
The file LICENSE includes the full license text.
|
||||
For more details, check [the license notes](LICENSE.md).
|
||||
|
|
|
|||
92
RELEASING.md
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
# Releasing
|
||||
|
||||
## 1. Checking bugs and technical debt
|
||||
|
||||
### Lint checks
|
||||
|
||||
Android development tools provide linter to check common errors.
|
||||
Use `./gradlew :app:lint` to run the linter and produce (human readable) reports as HTML file located at `app/build/reports/lint-results.html`.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Check no new warning was introduced before releasing.
|
||||
|
||||
### SonarCloud analysis
|
||||
|
||||
The AdAway application source code is [analyzed by SonarCloud](https://sonarcloud.io/dashboard?id=org.adaway) to find bugs, code smells and compute technical debt.
|
||||
While the overall score may be not perfect, each new release should not increase it.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Check no new bug nor debt was introduced before releasing.
|
||||
|
||||
## 2. Updating application version
|
||||
|
||||
Each version has its own number that follows the [Semantic Versioning](https://semver.org/) principle (starting from version 4).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Update application version name (`appName`) and code (`appCode`) from the `gradle/libs.versions.toml` catalog file.
|
||||
|
||||
## 3. Updating the changelog
|
||||
|
||||
The AdAway project provides [a global changelog](CHANGELOG.md).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Update the changelog to let users know what is inside each new version before releasing it.
|
||||
|
||||
## 4. Building release APK
|
||||
|
||||
The release apk must be built using the `release` flavor (not `debug`).
|
||||
Check the [contributing guide for building instructions](CONTRIBUTING.md#building-the-project).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Rename to release apk file to follow the format: `AdAway-<version_name>-<yyyymmdd>.apk`
|
||||
|
||||
Example: _AdAway-6.1.2-20220817.apk_ for the version 6.1.2 built the 08/17/22.
|
||||
|
||||
## 5. Distributing release
|
||||
|
||||
Before sharing the any release, remember to test it.
|
||||
Release variant apk does not behave like debug variant.
|
||||
Same goes for real device versus emulator.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Final tests should be done with release apk variant on real device.
|
||||
|
||||
Once tested, releases are posted on XDA development thread using the following template:
|
||||
```
|
||||
Hi all,
|
||||
|
||||
<welcoming message about the new version>
|
||||
|
||||
[U][SIZE="4"]Changelog:[/SIZE][/U]
|
||||
[LIST]
|
||||
[*] Item 1
|
||||
[*] Item 2
|
||||
[*] ...
|
||||
[*] Item n
|
||||
[/LIST]
|
||||
|
||||
[U][SIZE="4"]Thanks:[/SIZE][/U]
|
||||
|
||||
Special thanks to <contributors> for theirs contributions and <bug reporters> for theirs helpful bug reports.
|
||||
|
||||
[U][SIZE="4"]Download:[/SIZE][/U]
|
||||
|
||||
[URL="https://app.adaway.org/adaway.apk"]AdAway <application version>[/URL]
|
||||
```
|
||||
|
||||
### Beta releases
|
||||
|
||||
The beta releases are only announced in the XDA development thread.
|
||||
|
||||
### Stable releases
|
||||
|
||||
The stable releases are distributed through [GitHub releases](https://github.com/AdAway/AdAway/releases) and [F-Droid store](https://f-droid.org/packages/org.adaway/) and are posted of the first post of XDA development thread.
|
||||
Once ready, create and push a tag on GitHub repository using `vX.Y.Z` format (or `vX.Y.Zb` for pre-releases).
|
||||
To publish the application in GitHub:
|
||||
|
||||
* Create a new version based on this tag,
|
||||
* Copy the changelog part related to the version as description of the release,
|
||||
* Upload apk binary to the release.
|
||||
|
||||
Pushing a tag will publish the application to F-Droid store.
|
||||
It might takes some days to update but if it does not, build logs are available at the following address: `https://monitor.f-droid.org/builds/log/org.adaway/<versioncode>`.
|
||||
BIN
Resources/XDADevelopers.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
Resources/adaway_screenshot.png
Normal file
|
After Width: | Height: | Size: 666 KiB |
9
Resources/certificate/generate.sh
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
rm localhost.*
|
||||
|
||||
# Add compatibility for MINGW
|
||||
kernel=$(uname -s)
|
||||
if [[ $kernel == MINGW* ]]; then
|
||||
export MSYS_NO_PATHCONV=1
|
||||
fi
|
||||
|
||||
openssl req -x509 -out localhost.crt -keyout localhost.key -newkey rsa:2048 -nodes -sha256 -days 1126 -subj /CN=localhost -extensions EXT -config ssl.conf
|
||||
15
Resources/certificate/ssl.conf
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
[dn]
|
||||
CN=localhost
|
||||
[req]
|
||||
distinguished_name = dn
|
||||
[EXT]
|
||||
subjectAltName=@alternate_names
|
||||
keyUsage=digitalSignature
|
||||
extendedKeyUsage=serverAuth
|
||||
basicConstraints=CA:true
|
||||
[alternate_names]
|
||||
DNS.1=localhost
|
||||
DNS.2=*.doubleclick.net
|
||||
DNS.3=*.g.doubleclick.net
|
||||
IP.1=127.0.0.1
|
||||
|
||||
BIN
Resources/device-2013-06-19-105150.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
Resources/device-2013-06-19-105158.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
Resources/device-2013-06-19-105215.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
Resources/download_fdroid.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
Resources/download_google_play.png
Normal file
|
After Width: | Height: | Size: 4 KiB |
BIN
Resources/get-it-on-adaway.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
Resources/get-it-on-fdroid.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
112
Resources/icon.svg
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="32px"
|
||||
height="32px"
|
||||
id="svg3167"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
sodipodi:docname="icon.svg">
|
||||
<defs
|
||||
id="defs3169">
|
||||
<radialGradient
|
||||
r="17.497915"
|
||||
fy="40.636124"
|
||||
fx="33.772423"
|
||||
cy="40.636124"
|
||||
cx="33.772423"
|
||||
gradientTransform="matrix(1.161721,0,0,1.0498451,-2.6955965,978.41529)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3878"
|
||||
xlink:href="#linearGradient4263"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4263">
|
||||
<stop
|
||||
id="stop4265"
|
||||
offset="0"
|
||||
style="stop-color:#bfbfbf;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop4267"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4263"
|
||||
id="radialGradient3233"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.161721,0,0,1.0498451,-2.6955965,978.41529)"
|
||||
cx="33.772423"
|
||||
cy="40.636124"
|
||||
fx="33.772423"
|
||||
fy="40.636124"
|
||||
r="17.497915" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4263"
|
||||
id="radialGradient3238"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.61137216,0,0,0.55249588,-4.1660353,-4.330286)"
|
||||
cx="33.772423"
|
||||
cy="40.636124"
|
||||
fx="33.772423"
|
||||
fy="40.636124"
|
||||
r="17.497915" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4263"
|
||||
id="radialGradient3241"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.61137216,0,0,0.55249588,-3.9874288,-4.330286)"
|
||||
cx="33.772423"
|
||||
cy="40.636124"
|
||||
fx="33.772423"
|
||||
fy="40.636124"
|
||||
r="17.497915" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="7.9180417"
|
||||
inkscape:cx="32.306708"
|
||||
inkscape:cy="13.540991"
|
||||
inkscape:current-layer="svg3167"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="997"
|
||||
inkscape:window-x="1672"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata3172">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path
|
||||
style="fill:#cc0000;fill-opacity:1;stroke:#333333;stroke-width:0.46215421;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
d="M 9.90625 1.21875 L 1.25 9.875 L 1.21875 22.09375 L 9.875 30.75 L 22.09375 30.78125 L 30.75 22.125 L 30.78125 9.90625 L 22.125 1.25 L 9.90625 1.21875 z M 6 8.1875 C 6.6763721 8.1197446 10.285042 9.6406437 14.5625 9.65625 C 16.759542 9.791605 17.570434 10.416353 18.1875 11.0625 C 19.571912 12.366793 19.873633 13.03126 21.625 12.1875 C 22.338438 11.843849 23.228376 9.8114647 24.21875 9.59375 C 24.990847 9.481077 26.122457 10.438725 26.40625 10.71875 C 26.716053 11.02451 26.985342 11.333132 27.28125 11.46875 C 26.265273 11.52259 24.879528 13.892184 24.84375 15.90625 C 25.046345 21.533487 22.49174 21.215441 20 23.96875 C 19.205534 24.725992 18.781983 26.303089 18.78125 27.75 L 17.71875 27.71875 C 17.098386 27.73296 16.405511 26.947039 16.0625 26.40625 C 15.407551 25.314883 14.737631 27.401017 13.71875 25.6875 C 13.7659 25.120872 10.972706 25.011002 12.5625 23.6875 C 16.317406 21.029656 18.172296 20.45754 18.09375 19.65625 C 18.00095 18.709606 15.800894 19.560638 13.8125 19.46875 C 10.527778 19.317028 10.884928 17.249787 10.59375 16.28125 C 7.8361706 14.745664 12.704357 14.015508 10.5625 13.5625 C 9.5159322 13.396095 7.6703939 13.778783 7.28125 11.96875 C 7.6312051 12.401076 12.220507 11.538235 10.375 11.25 C 7.814251 10.088009 7.0930254 10.68347 6.03125 8.5 C 5.8473548 8.2768081 5.8439141 8.2031359 6 8.1875 z "
|
||||
id="path3852" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
BIN
Resources/icon.xcf
Normal file
BIN
Resources/icon_background.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
Resources/icon_background.xcf
Normal file
BIN
Resources/icon_foreground.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
Resources/icon_foreground.xcf
Normal file
356
Resources/icon_old.svg
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
height="72"
|
||||
id="svg2"
|
||||
inkscape:export-xdpi="120"
|
||||
inkscape:export-ydpi="120"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:version="0.32"
|
||||
width="72"
|
||||
version="1.1"
|
||||
sodipodi:docname="icon.svg"
|
||||
inkscape:export-filename="/home/ds1/Projekte/AdAway/adaway/org_adaway/res/drawable-xhdpi/icon.png">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient3906">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="0"
|
||||
id="stop3908" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0.3588765"
|
||||
id="stop3910" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.43333334;"
|
||||
offset="0.88415229"
|
||||
id="stop3912" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3914" />
|
||||
</linearGradient>
|
||||
<marker
|
||||
inkscape:stockid="Tail"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="Tail"
|
||||
style="overflow:visible">
|
||||
<g
|
||||
id="g4692"
|
||||
transform="scale(-1.2)">
|
||||
<path
|
||||
id="path4694"
|
||||
d="M -3.8048674,-3.9585227 L 0.54352094,0"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
||||
<path
|
||||
id="path4696"
|
||||
d="M -1.2866832,-3.9585227 L 3.0617053,0"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
||||
<path
|
||||
id="path4698"
|
||||
d="M 1.3053582,-3.9585227 L 5.6537466,0"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
||||
<path
|
||||
id="path4700"
|
||||
d="M -3.8048674,4.1775838 L 0.54352094,0.21974226"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
||||
<path
|
||||
id="path4702"
|
||||
d="M -1.2866832,4.1775838 L 3.0617053,0.21974226"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
||||
<path
|
||||
id="path4704"
|
||||
d="M 1.3053582,4.1775838 L 5.6537466,0.21974226"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;marker-start:none;marker-end:none;stroke-linecap:round" />
|
||||
</g>
|
||||
</marker>
|
||||
<linearGradient
|
||||
id="linearGradient4263">
|
||||
<stop
|
||||
style="stop-color:#bfbfbf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4265" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4267" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4158">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4160" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop4162" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3177">
|
||||
<stop
|
||||
id="stop3187"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
<stop
|
||||
id="stop3200"
|
||||
offset="0.4375"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3191"
|
||||
offset="0.9375"
|
||||
style="stop-color:#ffffff;stop-opacity:0.43333334;" />
|
||||
<stop
|
||||
id="stop3181"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3167">
|
||||
<stop
|
||||
id="stop3169"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3171"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
id="perspective10"
|
||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||
inkscape:vp_x="0 : 526.18109 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3167"
|
||||
id="linearGradient3341"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.08737273,0,0,0.08134103,83.682183,966.62592)"
|
||||
x1="337.49271"
|
||||
y1="402.10623"
|
||||
x2="416.38455"
|
||||
y2="645.40979" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3177"
|
||||
id="linearGradient3345"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="356.35681"
|
||||
y1="164.76173"
|
||||
x2="425.96066"
|
||||
y2="646.65405"
|
||||
gradientTransform="matrix(0.08334585,0,0,0.08334585,2.6106401,974.71004)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4263"
|
||||
id="radialGradient4275"
|
||||
cx="33.772423"
|
||||
cy="40.636124"
|
||||
fx="33.772423"
|
||||
fy="40.636124"
|
||||
r="17.497915"
|
||||
gradientTransform="matrix(1,0,0,0.90369811,0,3.9133352)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3906"
|
||||
id="linearGradient3037"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.08707623,0,0,0.08707623,1.0873306,972.09559)"
|
||||
x1="266.69391"
|
||||
y1="164.76173"
|
||||
x2="414.93436"
|
||||
y2="619.08826" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4263"
|
||||
id="radialGradient3878"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.161721,0,0,1.0498451,-2.6955965,978.41529)"
|
||||
cx="33.772423"
|
||||
cy="40.636124"
|
||||
fx="33.772423"
|
||||
fy="40.636124"
|
||||
r="17.497915" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4263"
|
||||
id="linearGradient3904"
|
||||
x1="-23.400173"
|
||||
y1="8.182107"
|
||||
x2="8.1473732"
|
||||
y2="-0.74950886"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
id="base"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:cx="-5.8491274"
|
||||
inkscape:cy="31.447789"
|
||||
inkscape:document-units="px"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-height="779"
|
||||
inkscape:window-width="1278"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="19"
|
||||
inkscape:zoom="6.8942911"
|
||||
pagecolor="#ffffff"
|
||||
showgrid="false"
|
||||
showguides="true"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
|
||||
<dc:title></dc:title>
|
||||
<dc:date />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Dominik Schürmann</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title />
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title />
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:subject>
|
||||
<rdf:Bag />
|
||||
</dc:subject>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title />
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/3.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="Ebene 1"
|
||||
transform="translate(0,-980.36215)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#cc0000;fill-opacity:1;stroke:#333333;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="path3852"
|
||||
sodipodi:sides="8"
|
||||
sodipodi:cx="10.353435"
|
||||
sodipodi:cy="8.182107"
|
||||
sodipodi:r1="34.570065"
|
||||
sodipodi:r2="31.938574"
|
||||
sodipodi:arg1="1.1510765"
|
||||
sodipodi:arg2="1.5437756"
|
||||
inkscape:flatsided="true"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 24.440895,39.751599 -2.0082281,40.466448 -21.216057,22.269568 -21.930906,-4.1795557 -3.7340262,-23.387385 22.715097,-24.102234 41.922926,-5.9053538 42.637775,20.54377 z"
|
||||
transform="matrix(1.020506,0.02898302,-0.02898302,1.020506,25.823557,1007.5416)" />
|
||||
<rect
|
||||
y="984.25385"
|
||||
x="4.0865383"
|
||||
width="63.578712"
|
||||
style="fill:none;stroke:none"
|
||||
id="rect2383"
|
||||
height="63.578712" />
|
||||
<path
|
||||
transform="matrix(0.08352444,0,0,0.08352444,70.275371,967.56248)"
|
||||
style="opacity:0.5;fill:none;stroke:#ffffff;stroke-width:29.08395386;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:ry="320.21835"
|
||||
sodipodi:rx="320.21835"
|
||||
sodipodi:cy="515.97119"
|
||||
sodipodi:cx="384.86813"
|
||||
id="path3165"
|
||||
d="m 705.08649,515.97119 c 0,176.85171 -143.36664,320.21835 -320.21836,320.21835 -176.85171,0 -320.21835,-143.36664 -320.21835,-320.21835 0,-176.85171 143.36664,-320.21835 320.21835,-320.21835 176.85172,0 320.21836,143.36664 320.21836,320.21835 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:0.34680134000000001;fill:url(#linearGradient3037);fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
sodipodi:nodetypes="csssssc"
|
||||
id="path3175"
|
||||
d="m 66.047903,1004.1251 c 0,5.2718 0.864169,23.3397 -0.180281,23.3932 -2.00487,0.1024 -5.900696,-6.6434 -16.012316,-7.0489 -12.732886,-0.5054 -37.233924,12.3723 -43.041824,7.4175 -1.283128,-1.0946 0.073036,-21.1464 0.073036,-23.9315 0,-15.39176 30.523091,-27.66476 41.801135,-17.19059 8.772231,8.14697 4.460889,4.68423 17.36025,17.36029 z" />
|
||||
<g
|
||||
id="g3874"
|
||||
transform="translate(0,-1.0183706)">
|
||||
<path
|
||||
sodipodi:nodetypes="cccscsccccccccssccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4253"
|
||||
d="m 16.327015,1002.823 c -1.863654,-2.2619 6.211627,2.1146 16.215279,2.1511 4.174788,0.2572 5.753469,1.4661 6.926009,2.6939 2.630641,2.4784 3.20235,3.7349 6.530274,2.1316 1.355664,-0.653 3.024421,-4.5164 4.906317,-4.9301 1.467128,-0.2141 3.619526,1.6381 4.158785,2.1702 0.588684,0.581 1.124104,1.1204 1.686384,1.3781 -1.930547,0.1023 -4.605827,4.6079 -4.673811,8.435 0.384969,10.6928 -4.428822,10.1031 -9.163591,15.3349 -1.509634,1.4389 -2.355972,4.4532 -2.357366,7.2026 l -1.98861,-0.061 c -1.178808,0.027 -2.524329,-1.5071 -3.176114,-2.5347 -1.244525,-2.0738 -2.531327,1.8998 -4.46739,-1.3562 0.0896,-1.0767 -5.168452,-1.2872 -2.147546,-3.8021 7.13502,-5.0504 10.618946,-6.1142 10.469694,-7.6368 -0.176332,-1.7988 -4.359873,-0.1699 -8.138192,-0.3445 -6.241584,-0.2883 -5.554758,-4.2434 -6.10805,-6.0838 -5.239916,-2.9179 4.02799,-4.2906 -0.04194,-5.1514 -1.988675,-0.3162 -5.497532,0.4234 -6.236978,-3.016 0.66498,0.8215 9.366113,-0.8583 5.859306,-1.406 -4.865901,-2.208 -6.234889,-1.0261 -8.25246,-5.1751 z"
|
||||
style="fill:url(#radialGradient3878);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.11195254;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
transform="matrix(2.8140812,0,0,2.8140812,-82.170599,925.02543)"
|
||||
d="m 47.889123,29.666016 c 0,0.152813 -0.123879,0.276692 -0.276692,0.276692 -0.152814,0 -0.276693,-0.123879 -0.276693,-0.276692 0,-0.152814 0.123879,-0.276693 0.276693,-0.276693 0.152813,0 0.276692,0.123879 0.276692,0.276693 z"
|
||||
sodipodi:ry="0.27669272"
|
||||
sodipodi:rx="0.27669272"
|
||||
sodipodi:cy="29.666016"
|
||||
sodipodi:cx="47.612431"
|
||||
id="path4279"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
sodipodi:type="arc" />
|
||||
</g>
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:none;stroke:url(#linearGradient3904);stroke-width:4.1448102;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="path3852-5"
|
||||
sodipodi:sides="8"
|
||||
sodipodi:cx="10.353435"
|
||||
sodipodi:cy="8.182107"
|
||||
sodipodi:r1="34.570065"
|
||||
sodipodi:r2="31.938574"
|
||||
sodipodi:arg1="1.1510765"
|
||||
sodipodi:arg2="1.5437756"
|
||||
inkscape:flatsided="true"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 24.440895,39.751599 -2.0082281,40.466448 -21.216057,22.269568 -21.930906,-4.1795557 -3.7340262,-23.387385 22.715097,-24.102234 41.922926,-5.9053538 42.637775,20.54377 z"
|
||||
transform="matrix(0.94528338,0.02684664,-0.02684664,0.94528338,26.524882,1008.0673)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
BIN
Resources/logo_googlecode.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
Resources/logo_market.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
Resources/paypal_donate_button.gif
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Resources/screenshot1.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
Resources/screenshot2.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
Resources/screenshot3.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
Resources/screenshot4.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
Resources/screenshot5.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
Resources/screenshot6.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
358
Resources/status_bar_icon.svg
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="38"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="status_bar_icon.svg"
|
||||
inkscape:export-filename="/home/ds1/Projekte/AdAway/org_adaway/res/drawable-ldpi/status_bar_icon.png"
|
||||
inkscape:export-xdpi="45"
|
||||
inkscape:export-ydpi="45">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient4354">
|
||||
<stop
|
||||
style="stop-color:#828282;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4356" />
|
||||
<stop
|
||||
style="stop-color:#919191;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4358" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4346">
|
||||
<stop
|
||||
style="stop-color:#828282;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4348" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop4350" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4321">
|
||||
<stop
|
||||
style="stop-color:#828282;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4323" />
|
||||
<stop
|
||||
style="stop-color:#919191;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4325" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter7799"
|
||||
inkscape:label="Inner Shadow"
|
||||
inkscape:menu="Shadows and Glows"
|
||||
inkscape:menu-tooltip="Adds a colorizable drop shadow inside"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur7801"
|
||||
stdDeviation="4"
|
||||
result="result8" />
|
||||
<feOffset
|
||||
id="feOffset7803"
|
||||
dx="4"
|
||||
dy="4"
|
||||
result="result11" />
|
||||
<feComposite
|
||||
id="feComposite7805"
|
||||
in2="result11"
|
||||
result="result6"
|
||||
in="SourceGraphic"
|
||||
operator="in" />
|
||||
<feFlood
|
||||
id="feFlood7807"
|
||||
result="result10"
|
||||
in="result6"
|
||||
flood-opacity="1"
|
||||
flood-color="rgb(0,0,0)" />
|
||||
<feBlend
|
||||
id="feBlend7809"
|
||||
in2="result10"
|
||||
mode="normal"
|
||||
in="result6"
|
||||
result="result12" />
|
||||
<feComposite
|
||||
id="feComposite7811"
|
||||
in2="SourceGraphic"
|
||||
result="result2"
|
||||
operator="in" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient7845"
|
||||
id="linearGradient7866"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="42.703548"
|
||||
y1="1017.2917"
|
||||
x2="60.975151"
|
||||
y2="1027.8625" />
|
||||
<linearGradient
|
||||
id="linearGradient7845">
|
||||
<stop
|
||||
id="stop7847"
|
||||
offset="0"
|
||||
style="stop-color:#e3e4e3;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0.33728361"
|
||||
id="stop7855" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0.77889121"
|
||||
id="stop7851" />
|
||||
<stop
|
||||
id="stop7853"
|
||||
offset="1"
|
||||
style="stop-color:#eeeeee;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4321"
|
||||
id="linearGradient4327"
|
||||
x1="-138.33235"
|
||||
y1="986.01508"
|
||||
x2="388.7706"
|
||||
y2="764.12341"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.06177898,0,0,0.06177898,-28.368932,961.00956)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4354"
|
||||
id="linearGradient4360"
|
||||
x1="19.386446"
|
||||
y1="1052.9519"
|
||||
x2="42.776966"
|
||||
y2="1029.1078"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4354"
|
||||
id="linearGradient4362"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="19.386446"
|
||||
y1="1052.9519"
|
||||
x2="42.776966"
|
||||
y2="1029.1078" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4354"
|
||||
id="linearGradient4364"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="19.386446"
|
||||
y1="1052.9519"
|
||||
x2="42.776966"
|
||||
y2="1029.1078" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4354"
|
||||
id="linearGradient4366"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="19.386446"
|
||||
y1="1052.9519"
|
||||
x2="42.776966"
|
||||
y2="1029.1078" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4354"
|
||||
id="linearGradient4373"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="19.386446"
|
||||
y1="1052.9519"
|
||||
x2="42.776966"
|
||||
y2="1029.1078"
|
||||
gradientTransform="matrix(0.53359487,0,0,0.53359487,-28.874041,477.57575)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4263"
|
||||
id="linearGradient3904"
|
||||
x1="-23.400173"
|
||||
y1="8.182107"
|
||||
x2="8.1473732"
|
||||
y2="-0.74950886"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient4263">
|
||||
<stop
|
||||
style="stop-color:#bfbfbf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4265" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4267" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4263"
|
||||
id="radialGradient3878"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.161721,0,0,1.0498451,-2.6955965,978.41529)"
|
||||
cx="33.772423"
|
||||
cy="40.636124"
|
||||
fx="33.772423"
|
||||
fy="40.636124"
|
||||
r="17.497915" />
|
||||
<linearGradient
|
||||
id="linearGradient3252">
|
||||
<stop
|
||||
style="stop-color:#bfbfbf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3254" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3256" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3906"
|
||||
id="linearGradient3037"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.02851737,0,0,0.02851737,0.39670267,1017.5151)"
|
||||
x1="266.69391"
|
||||
y1="164.76173"
|
||||
x2="414.93436"
|
||||
y2="619.08826" />
|
||||
<linearGradient
|
||||
id="linearGradient3906">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="0"
|
||||
id="stop3908" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0.3588765"
|
||||
id="stop3910" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.43333334;"
|
||||
offset="0.88415229"
|
||||
id="stop3912" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3914" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4321"
|
||||
id="radialGradient3320"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.40031375,0,0,0.36176279,-1.1666316,1019.8464)"
|
||||
cx="33.772423"
|
||||
cy="40.636124"
|
||||
fx="33.772423"
|
||||
fy="40.636124"
|
||||
r="17.497915" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4321"
|
||||
id="linearGradient4104"
|
||||
x1="-22.430906"
|
||||
y1="8.182107"
|
||||
x2="43.137775"
|
||||
y2="8.182107"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4321"
|
||||
id="linearGradient4106"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-23.400173"
|
||||
y1="8.182107"
|
||||
x2="8.1473732"
|
||||
y2="-0.74950886" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4321"
|
||||
id="linearGradient4005"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-22.430906"
|
||||
y1="8.182107"
|
||||
x2="43.137775"
|
||||
y2="8.182107" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="10.218937"
|
||||
inkscape:cx="12.813952"
|
||||
inkscape:cy="24.02604"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1278"
|
||||
inkscape:window-height="779"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="19"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1014.3622)">
|
||||
<g
|
||||
id="g3812"
|
||||
style="fill-rule:evenodd" />
|
||||
<rect
|
||||
y="1022.0656"
|
||||
x="1.1925927"
|
||||
width="21.908386"
|
||||
style="fill:none;stroke:none"
|
||||
id="rect2383"
|
||||
height="21.908386" />
|
||||
<path
|
||||
style="fill:url(#radialGradient3320);fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 5.3883104,1028.257 c -0.642191,-0.7795 2.140444,0.7286 5.5875716,0.7412 1.438577,0.088 1.98257,0.5052 2.386612,0.9283 0.906483,0.854 1.103487,1.287 2.250247,0.7345 0.467143,-0.2249 1.042176,-1.5562 1.690652,-1.6988 0.505553,-0.074 1.247241,0.5644 1.433062,0.7478 0.202853,0.2001 0.387351,0.386 0.581105,0.4749 -0.665241,0.036 -1.587106,1.5879 -1.610533,2.9065 0.132656,3.6847 -1.526113,3.4815 -3.157653,5.2842 -0.5202,0.4959 -0.811837,1.5345 -0.812317,2.482 l -0.685249,-0.021 c -0.406202,0.011 -0.86985,-0.5194 -1.094447,-0.8734 -0.428848,-0.7147 -0.872263,0.6545 -1.539404,-0.4674 0.03087,-0.371 -1.7809806,-0.4435 -0.7400166,-1.3101 2.4586346,-1.7404 3.6591496,-2.1069 3.6077196,-2.6315 -0.06076,-0.62 -1.502355,-0.059 -2.804313,-0.1188 -2.1507696,-0.099 -1.9140976,-1.4623 -2.1047556,-2.0965 -1.805606,-1.0054 1.387993,-1.4783 -0.01445,-1.775 -0.685271,-0.1087 -1.894377,0.1459 -2.14918,-1.0393 0.229143,0.2831 3.227439,-0.2957 2.01904,-0.4845 -1.676726,-0.7609 -2.148461,-0.3535 -2.843689,-1.7832 z"
|
||||
id="path4253"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccscsccccccccssccccc" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:none;stroke:url(#linearGradient4106);stroke-width:7.67192268;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="path3852-5"
|
||||
sodipodi:sides="8"
|
||||
sodipodi:cx="10.353435"
|
||||
sodipodi:cy="8.182107"
|
||||
sodipodi:r1="34.570065"
|
||||
sodipodi:r2="31.938574"
|
||||
sodipodi:arg1="1.1510765"
|
||||
sodipodi:arg2="1.5437756"
|
||||
inkscape:flatsided="true"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 24.440895,39.751599 -2.0082281,40.466448 -21.216057,22.269568 -21.930906,-4.1795557 -3.7340262,-23.387385 22.715097,-24.102234 41.922926,-5.9053538 42.637775,20.54377 z"
|
||||
transform="matrix(0.32573221,0.00925099,-0.00925099,0.32573221,8.7032455,1030.6012)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
BIN
Resources/symlink_hosts_to_data.zip
Normal file
BIN
Resources/tasker/1-create-tasks.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
Resources/tasker/2-enable-adblock-task.jpg
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
Resources/tasker/3-enable-adblock-task-details1.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
Resources/tasker/4-enable-adblock-task-details2.jpg
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
Resources/tasker/5-disable-adblock-task.jpg
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
Resources/tasker/6-disable-adblock-task-details1.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
Resources/tasker/7-disable-adblock-task-details2.jpg
Normal file
|
After Width: | Height: | Size: 92 KiB |
16
Resources/test-webserver.sh
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#
|
||||
# Launch web server from a root shell.
|
||||
#
|
||||
|
||||
APP_FOLDER=$(find /data/app -type d -name "org.adaway-*")
|
||||
LIB_FOLDER="${APP_FOLDER}/lib/arm64"
|
||||
WEBSERVER_EXEC="$LIB_FOLDER/libwebserver_exec.so"
|
||||
WEBSERVER_ARGS="--resources /data/user/0/org.adaway/files/webserver --debug"
|
||||
|
||||
if [ ! -e $WEBSERVER_EXEC ]; then
|
||||
echo "Web server exec not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Running LD_LIBRARY_PATH=$LIB_FOLDER $WEBSERVER_EXEC $WEBSERVER_ARGS"
|
||||
LD_LIBRARY_PATH=$LIB_FOLDER $WEBSERVER_EXEC $WEBSERVER_ARGS
|
||||
78
THIRD_PARTY_LICENSES.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# Licenses
|
||||
|
||||
AdAway is licensed under the GPLv3+.
|
||||
The file [LICENSE.md](LICENSE.md) includes the full license text.
|
||||
|
||||
## AdAway Application
|
||||
|
||||
AdAway is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
AdAway is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with AdAway.
|
||||
If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
## Third-Party Libraries
|
||||
|
||||
* Android Jetpack
|
||||
https://developer.android.com/jetpack/
|
||||
Apache License v2
|
||||
|
||||
* DNS66
|
||||
https://github.com/julian-klode/dns66
|
||||
GPLv3
|
||||
|
||||
* dnsjava
|
||||
https://github.com/dnsjava/dnsjava
|
||||
BSD-3-Clause License
|
||||
|
||||
* Guava
|
||||
https://github.com/google/guava
|
||||
Apache License v2
|
||||
|
||||
* libsu
|
||||
https://github.com/topjohnwu/libsu
|
||||
Apache License v2
|
||||
|
||||
* Material Components for Android
|
||||
https://github.com/material-components/material-components-android
|
||||
Apache License v2
|
||||
|
||||
* Mongoose Webserver
|
||||
https://github.com/cesanta/mongoose
|
||||
GPLv2 License
|
||||
|
||||
* OkHttp
|
||||
https://github.com/square/okhttp
|
||||
Apache License v2
|
||||
|
||||
* Openssl
|
||||
https://www.openssl.org/
|
||||
[OpenSSL license](https://www.openssl.org/source/license-openssl-ssleay.txt)
|
||||
|
||||
* Pcap4J
|
||||
https://github.com/kaitoy/pcap4j
|
||||
MIT License
|
||||
|
||||
* Sentry Java
|
||||
https://github.com/getsentry/sentry-java
|
||||
BSD 3-Clause License
|
||||
|
||||
* SLF4J
|
||||
https://www.slf4j.org/
|
||||
MIT License
|
||||
|
||||
* Tcpdump/Libpcap
|
||||
https://www.tcpdump.org/
|
||||
BSD 3-Clause License
|
||||
|
||||
## Images
|
||||
|
||||
* icon.svg, banner.svg
|
||||
AdAway by Dominik Schürmann
|
||||
New version by Alin Ţoţea-Radu
|
||||
GPLv3
|
||||
|
||||
* Menu Icons
|
||||
Original Android Icons
|
||||
39
TRANSLATING.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Translating to your language
|
||||
|
||||
You can help us with your translation efforts!
|
||||
|
||||
Please have a look at ticket No. [AdAway/AdAway#1050](https://github.com/AdAway/AdAway/issues/1050) for more detailed information, questions and discussions!
|
||||
We apreciate new contributors and translations are perfect for getting started with contributing at Github in general.
|
||||
|
||||
Here is the gist:
|
||||
Translations are managed via the **transifex.com website**! (and Transifex' website alone, that is)
|
||||
|
||||
Unfortunately, we cannot merge translations via Github directly. Please follow the steps below instead.
|
||||
Sorry, but this causes some major synchronization issues if not followed. We have to consolidate many contributions by translators and sync them up to the latest state. That is just not possible via Github.
|
||||
|
||||
1. Please go to **https://www.transifex.com/free-software-for-android/adaway/**
|
||||
1. Login or create a new account (you can conveniently login via Github as well)
|
||||
1. Enroll into the language you want to contribute to or even submit a request for a new language.
|
||||
* Please keep in mind that we want to stick to the basic languages where possible (e.g. `sr` for Serbian).
|
||||
Please refrain to request regional localizations (like `sr_RS`).
|
||||
1. In your language section, you can browse all available resources and start **translating strings right in your browser**.
|
||||
* You don't have to download anything. Just click "Translate". The downloads are meant for more advanced use cases.
|
||||
|
||||
## Here are some tips for using Transifex:
|
||||
* Make sure to have an eye on the **"Suggestions", "History", "Context" and "Glossary" tabs** on every entry.
|
||||
* Mind translation efforts that were already done or suggested.
|
||||
Basically, »*stand on the shoulders of giants*« where possible.
|
||||
* Sometimes source strings change only marginally, but their translations get cleared anyway.
|
||||
You can easily **recover their previous translations** by looking at the "Suggestions" tab. Just make sure they really fit the new source text and edit the translation if needed.
|
||||
* Help us validate translations by **reviewing others'**.
|
||||
* Some strings contain **placeholders** - like for HTML tags or numbers.
|
||||
You can click on them to add them or use keyboard shortcuts (see the page settings for an overview).
|
||||
* You can point out issues to the Translation Organizers via the **"Comments" tab** or just start a discussion.
|
||||
* Don't forget to save your work!
|
||||
* Please don't create any Translation Pull Request here on Github.
|
||||
* We will integrate your contributions from time to time into the code by exporting from Transifex directly. No need to provide any files from your side. ;-)
|
||||
* For more information about how to use Transifex, see https://docs.transifex.com/
|
||||
|
||||
|
||||
We will add all Transifex translations from time to time to the app.
|
||||
For our Translation Organizers: You can use the CLI tool for this to retrieve translated resources from the Transifex server and add them to the Github repo. (See https://docs.transifex.com/client/ for more details about the client tool.)
|
||||
36
app/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#Android specific
|
||||
bin
|
||||
gen
|
||||
obj
|
||||
libs/armeabi
|
||||
libs/armeabi-v7a
|
||||
libs/arm64-v8a
|
||||
libs/mips
|
||||
libs/mips64
|
||||
libs/x86
|
||||
libs/x86_64
|
||||
local.properties
|
||||
release.properties
|
||||
ant.properties
|
||||
*.class
|
||||
*.apk
|
||||
|
||||
#Gradle
|
||||
.gradle
|
||||
build
|
||||
gradle.properties
|
||||
|
||||
#Maven
|
||||
target
|
||||
pom.xml.*
|
||||
|
||||
#Eclipse
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
.metadata
|
||||
|
||||
#IntelliJ IDEA
|
||||
.idea
|
||||
*.iml
|
||||
/.externalNativeBuild/
|
||||
149
app/build.gradle
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
plugins {
|
||||
id 'com.android.application'
|
||||
}
|
||||
|
||||
boolean keyStoreDefined = project.hasProperty('signingStoreLocation') &&
|
||||
project.hasProperty('signingStorePassword') &&
|
||||
project.hasProperty('signingKeyAlias') &&
|
||||
project.hasProperty('signingKeyPassword')
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://jitpack.io'
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk 34
|
||||
ndkVersion '25.2.9519653'
|
||||
namespace 'org.adaway'
|
||||
|
||||
defaultConfig {
|
||||
minSdk 26
|
||||
targetSdk 33
|
||||
versionCode libs.versions.appCode.get() as int
|
||||
versionName libs.versions.appName.get()
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments = [
|
||||
"room.schemaLocation": "$projectDir/schemas".toString(),
|
||||
"room.incremental" : "true"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
/*
|
||||
* To sign release build, create file gradle.properties in ~/.gradle/ with this content:
|
||||
*
|
||||
* signingStoreLocation=/home/key.store
|
||||
* signingStorePassword=xxx
|
||||
* signingKeyAlias=alias
|
||||
* signingKeyPassword=xxx
|
||||
*/
|
||||
if (keyStoreDefined) {
|
||||
println "Found signature properties in gradle.properties. Build will be signed."
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile file(signingStoreLocation)
|
||||
storePassword signingStorePassword
|
||||
keyAlias signingKeyAlias
|
||||
keyPassword signingKeyPassword
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes.debug.signingConfig = signingConfigs.release
|
||||
buildTypes.release.signingConfig = signingConfigs.release
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
jniLibs {
|
||||
useLegacyPackaging = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
// debug {
|
||||
// shrinkResources false
|
||||
// minifyEnabled false
|
||||
// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
// }
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
// Do not abort build if lint finds errors
|
||||
lint {
|
||||
disable 'MissingTranslation'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Native modules
|
||||
implementation project(':tcpdump')
|
||||
implementation project(':webserver')
|
||||
|
||||
// AndroidX components
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.fragment:fragment:1.8.1'
|
||||
// "fragment-ktx" is not used but was added to fix the following dependency error:
|
||||
// Duplicate class androidx.lifecycle.ViewModelLazy found in modules lifecycle-viewmodel-2.5.0-runtime (androidx.lifecycle:lifecycle-viewmodel:2.5.0) and lifecycle-viewmodel-ktx-2.3.1-runtime
|
||||
implementation 'androidx.fragment:fragment-ktx:1.8.1'
|
||||
implementation 'androidx.paging:paging-runtime:3.3.0'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||
implementation 'androidx.room:room-runtime:2.6.1'
|
||||
implementation 'androidx.room:room-paging:2.6.1'
|
||||
annotationProcessor 'androidx.room:room-compiler:2.6.1'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
implementation 'androidx.work:work-runtime:2.8.1'
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
|
||||
// Collections related
|
||||
implementation libs.guava
|
||||
// Network related
|
||||
implementation libs.okhttp3.okhttp
|
||||
// Logging related
|
||||
implementation libs.timber
|
||||
if (keyStoreDefined) {
|
||||
implementation project(':sentrystub')
|
||||
} else {
|
||||
implementation platform('io.sentry:sentry-bom:7.8.0')
|
||||
implementation('io.sentry:sentry-android')
|
||||
implementation('io.sentry:sentry-android-fragment')
|
||||
implementation('io.sentry:sentry-android-timber')
|
||||
}
|
||||
|
||||
// Root related
|
||||
implementation libs.libsu
|
||||
|
||||
// VPN related
|
||||
implementation libs.bundles.pcap4j
|
||||
implementation libs.dnsjava
|
||||
implementation libs.slf4j.android
|
||||
implementation libs.okhttp.dnsoverhttps
|
||||
|
||||
// Test related
|
||||
testImplementation libs.junit
|
||||
testImplementation libs.json
|
||||
androidTestImplementation libs.bundles.androidx.test
|
||||
androidTestImplementation libs.junit
|
||||
}
|
||||
10
app/lint.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
<!-- list of issues to configure -->
|
||||
<issue id="InvalidPackage">
|
||||
<!-- Ignore dnsjava -->
|
||||
<ignore path="**/dnsjava-3.0.2*.jar" />
|
||||
<!-- Ignore pcap4j dependency -->
|
||||
<ignore path="**/jna-5.3.1.jar"/>
|
||||
</issue>
|
||||
</lint>
|
||||
38
app/proguard-rules.pro
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
-keep public class * extends android.content.ContentProvider
|
||||
|
||||
# Temporary fix for androidx preference fragement reference
|
||||
# See https://issuetracker.google.com/issues/145316223
|
||||
-keep public class org.adaway.ui.prefs.PrefsBackupRestoreFragment
|
||||
-keep public class org.adaway.ui.prefs.PrefsRootFragment
|
||||
-keep public class org.adaway.ui.prefs.PrefsUpdateFragment
|
||||
-keep public class org.adaway.ui.prefs.PrefsVpnFragment
|
||||
|
||||
-keepclassmembers class io.sentry.Sentry {
|
||||
public static final boolean STUB;
|
||||
}
|
||||
|
||||
-dontobfuscate
|
||||
|
||||
### Android Jetpack ###
|
||||
-dontwarn com.google.**
|
||||
|
||||
### Sentry ###
|
||||
-dontwarn io.sentry.**
|
||||
|
||||
### OkHttp ###
|
||||
# JSR 305 annotations are for embedding nullability information.
|
||||
-dontwarn javax.annotation.**
|
||||
# A resource is loaded with a relative path so the package of this class must be preserved.
|
||||
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
|
||||
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
|
||||
-dontwarn org.codehaus.mojo.animal_sniffer.*
|
||||
# OkHttp platform used only on JVM and when Conscrypt dependency is available.
|
||||
-dontwarn okhttp3.internal.platform.ConscryptPlatform
|
||||
# Generated rules from R8
|
||||
-dontwarn org.bouncycastle.jsse.**
|
||||
-dontwarn org.conscrypt.**
|
||||
-dontwarn org.openjsse.**
|
||||
|
||||
### dnsjava ###
|
||||
-dontwarn lombok.Generated
|
||||
-dontwarn sun.net.spi.nameservice.NameServiceDescriptor
|
||||
90
app/schemas/org.adaway.db.AppDatabase/1.json
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "5175df445bc75bbbb5ea672750d7b425",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "hosts_sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `last_modified_local` INTEGER, `last_modified_online` INTEGER, PRIMARY KEY(`url`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastLocalModification",
|
||||
"columnName": "last_modified_local",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastOnlineModification",
|
||||
"columnName": "last_modified_online",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "hosts_lists",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`host` TEXT NOT NULL, `type` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `redirection` TEXT, PRIMARY KEY(`host`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "host",
|
||||
"columnName": "host",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirection",
|
||||
"columnName": "redirection",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"5175df445bc75bbbb5ea672750d7b425\")"
|
||||
]
|
||||
}
|
||||
}
|
||||
146
app/schemas/org.adaway.db.AppDatabase/2.json
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "e9b86296a34de1d881f8530fdf2c535d",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "hosts_sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `last_modified_local` INTEGER, `last_modified_online` INTEGER)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastLocalModification",
|
||||
"columnName": "last_modified_local",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastOnlineModification",
|
||||
"columnName": "last_modified_online",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_hosts_sources_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_hosts_sources_url` ON `${TABLE_NAME}` (`url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "hosts_lists",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `host` TEXT NOT NULL, `type` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `redirection` TEXT, `source_id` INTEGER NOT NULL, FOREIGN KEY(`source_id`) REFERENCES `hosts_sources`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "host",
|
||||
"columnName": "host",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirection",
|
||||
"columnName": "redirection",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sourceId",
|
||||
"columnName": "source_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_hosts_lists_host",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_hosts_lists_host` ON `${TABLE_NAME}` (`host`)"
|
||||
},
|
||||
{
|
||||
"name": "index_hosts_lists_source_id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"source_id"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_hosts_lists_source_id` ON `${TABLE_NAME}` (`source_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "hosts_sources",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"source_id"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e9b86296a34de1d881f8530fdf2c535d')"
|
||||
]
|
||||
}
|
||||
}
|
||||
151
app/schemas/org.adaway.db.AppDatabase/3.json
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 3,
|
||||
"identityHash": "ace31b365ff79d4497319c74157538d7",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "hosts_sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `last_modified_local` INTEGER, `last_modified_online` INTEGER)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastLocalModification",
|
||||
"columnName": "last_modified_local",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastOnlineModification",
|
||||
"columnName": "last_modified_online",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_hosts_sources_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_hosts_sources_url` ON `${TABLE_NAME}` (`url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "hosts_lists",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `host` TEXT NOT NULL, `type` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `redirection` TEXT, `source_id` INTEGER NOT NULL, FOREIGN KEY(`source_id`) REFERENCES `hosts_sources`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "host",
|
||||
"columnName": "host",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirection",
|
||||
"columnName": "redirection",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sourceId",
|
||||
"columnName": "source_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_hosts_lists_host",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_hosts_lists_host` ON `${TABLE_NAME}` (`host`)"
|
||||
},
|
||||
{
|
||||
"name": "index_hosts_lists_source_id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"source_id"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_hosts_lists_source_id` ON `${TABLE_NAME}` (`source_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "hosts_sources",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"source_id"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"views": [
|
||||
{
|
||||
"viewName": "host_entries",
|
||||
"createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT `host`, `type`, `redirection` FROM `hosts_lists` WHERE `enabled` = 1 AND ((`type` = 0 AND `host` NOT LIKE (SELECT `host` FROM `hosts_lists` WHERE `enabled` = 1 and `type` = 1)) OR `type` = 2) ORDER BY `host` ASC, `type` DESC, `redirection` ASC"
|
||||
}
|
||||
],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ace31b365ff79d4497319c74157538d7')"
|
||||
]
|
||||
}
|
||||
}
|
||||
187
app/schemas/org.adaway.db.AppDatabase/4.json
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 4,
|
||||
"identityHash": "80b1c1d47fbd109a4f052817c9faf980",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "hosts_sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `last_modified_local` INTEGER, `last_modified_online` INTEGER)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "localModificationDate",
|
||||
"columnName": "last_modified_local",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "onlineModificationDate",
|
||||
"columnName": "last_modified_online",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_hosts_sources_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_hosts_sources_url` ON `${TABLE_NAME}` (`url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "hosts_lists",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `host` TEXT NOT NULL, `type` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `redirection` TEXT, `source_id` INTEGER NOT NULL, FOREIGN KEY(`source_id`) REFERENCES `hosts_sources`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "host",
|
||||
"columnName": "host",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirection",
|
||||
"columnName": "redirection",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sourceId",
|
||||
"columnName": "source_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_hosts_lists_host",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_hosts_lists_host` ON `${TABLE_NAME}` (`host`)"
|
||||
},
|
||||
{
|
||||
"name": "index_hosts_lists_source_id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"source_id"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_hosts_lists_source_id` ON `${TABLE_NAME}` (`source_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "hosts_sources",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"source_id"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "host_entries",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`host` TEXT NOT NULL, `type` INTEGER NOT NULL, `redirection` TEXT, PRIMARY KEY(`host`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "host",
|
||||
"columnName": "host",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirection",
|
||||
"columnName": "redirection",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_host_entries_host",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_host_entries_host` ON `${TABLE_NAME}` (`host`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '80b1c1d47fbd109a4f052817c9faf980')"
|
||||
]
|
||||
}
|
||||
}
|
||||
187
app/schemas/org.adaway.db.AppDatabase/5.json
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 5,
|
||||
"identityHash": "80b1c1d47fbd109a4f052817c9faf980",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "hosts_sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `last_modified_local` INTEGER, `last_modified_online` INTEGER)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "localModificationDate",
|
||||
"columnName": "last_modified_local",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "onlineModificationDate",
|
||||
"columnName": "last_modified_online",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_hosts_sources_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_hosts_sources_url` ON `${TABLE_NAME}` (`url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "hosts_lists",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `host` TEXT NOT NULL, `type` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `redirection` TEXT, `source_id` INTEGER NOT NULL, FOREIGN KEY(`source_id`) REFERENCES `hosts_sources`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "host",
|
||||
"columnName": "host",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirection",
|
||||
"columnName": "redirection",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sourceId",
|
||||
"columnName": "source_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_hosts_lists_host",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_hosts_lists_host` ON `${TABLE_NAME}` (`host`)"
|
||||
},
|
||||
{
|
||||
"name": "index_hosts_lists_source_id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"source_id"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_hosts_lists_source_id` ON `${TABLE_NAME}` (`source_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "hosts_sources",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"source_id"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "host_entries",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`host` TEXT NOT NULL, `type` INTEGER NOT NULL, `redirection` TEXT, PRIMARY KEY(`host`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "host",
|
||||
"columnName": "host",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirection",
|
||||
"columnName": "redirection",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_host_entries_host",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_host_entries_host` ON `${TABLE_NAME}` (`host`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '80b1c1d47fbd109a4f052817c9faf980')"
|
||||
]
|
||||
}
|
||||
}
|
||||
211
app/schemas/org.adaway.db.AppDatabase/6.json
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 6,
|
||||
"identityHash": "c53f309b3cbcdeda90c9f22573023ac2",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "hosts_sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `label` TEXT NOT NULL, `url` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `allowEnabled` INTEGER NOT NULL, `redirectEnabled` INTEGER NOT NULL, `last_modified_local` INTEGER, `last_modified_online` INTEGER, `size` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "label",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "allowEnabled",
|
||||
"columnName": "allowEnabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirectEnabled",
|
||||
"columnName": "redirectEnabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "localModificationDate",
|
||||
"columnName": "last_modified_local",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "onlineModificationDate",
|
||||
"columnName": "last_modified_online",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_hosts_sources_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_hosts_sources_url` ON `${TABLE_NAME}` (`url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "hosts_lists",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `host` TEXT NOT NULL, `type` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `redirection` TEXT, `source_id` INTEGER NOT NULL, FOREIGN KEY(`source_id`) REFERENCES `hosts_sources`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "host",
|
||||
"columnName": "host",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirection",
|
||||
"columnName": "redirection",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sourceId",
|
||||
"columnName": "source_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_hosts_lists_host",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_hosts_lists_host` ON `${TABLE_NAME}` (`host`)"
|
||||
},
|
||||
{
|
||||
"name": "index_hosts_lists_source_id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"source_id"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_hosts_lists_source_id` ON `${TABLE_NAME}` (`source_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "hosts_sources",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"source_id"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "host_entries",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`host` TEXT NOT NULL, `type` INTEGER NOT NULL, `redirection` TEXT, PRIMARY KEY(`host`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "host",
|
||||
"columnName": "host",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirection",
|
||||
"columnName": "redirection",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_host_entries_host",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_host_entries_host` ON `${TABLE_NAME}` (`host`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c53f309b3cbcdeda90c9f22573023ac2')"
|
||||
]
|
||||
}
|
||||
}
|
||||
221
app/schemas/org.adaway.db.AppDatabase/7.json
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 7,
|
||||
"identityHash": "dccd6211bcef97caed75ea42d7df1b32",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "hosts_sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `label` TEXT NOT NULL, `url` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `allowEnabled` INTEGER NOT NULL, `redirectEnabled` INTEGER NOT NULL, `last_modified_local` INTEGER, `last_modified_online` INTEGER, `entityTag` TEXT, `size` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "label",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "allowEnabled",
|
||||
"columnName": "allowEnabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirectEnabled",
|
||||
"columnName": "redirectEnabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "localModificationDate",
|
||||
"columnName": "last_modified_local",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "onlineModificationDate",
|
||||
"columnName": "last_modified_online",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "entityTag",
|
||||
"columnName": "entityTag",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_hosts_sources_url",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"url"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_hosts_sources_url` ON `${TABLE_NAME}` (`url`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "hosts_lists",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `host` TEXT NOT NULL, `type` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `redirection` TEXT, `source_id` INTEGER NOT NULL, FOREIGN KEY(`source_id`) REFERENCES `hosts_sources`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "host",
|
||||
"columnName": "host",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enabled",
|
||||
"columnName": "enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirection",
|
||||
"columnName": "redirection",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "sourceId",
|
||||
"columnName": "source_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_hosts_lists_host",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_hosts_lists_host` ON `${TABLE_NAME}` (`host`)"
|
||||
},
|
||||
{
|
||||
"name": "index_hosts_lists_source_id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"source_id"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_hosts_lists_source_id` ON `${TABLE_NAME}` (`source_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "hosts_sources",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"source_id"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "host_entries",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`host` TEXT NOT NULL, `type` INTEGER NOT NULL, `redirection` TEXT, PRIMARY KEY(`host`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "host",
|
||||
"columnName": "host",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "redirection",
|
||||
"columnName": "redirection",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_host_entries_host",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"host"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_host_entries_host` ON `${TABLE_NAME}` (`host`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'dccd6211bcef97caed75ea42d7df1b32')"
|
||||
]
|
||||
}
|
||||
}
|
||||
151
app/src/androidTest/java/org/adaway/db/DbTest.java
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
package org.adaway.db;
|
||||
|
||||
import static org.adaway.db.entity.HostsSource.USER_SOURCE_ID;
|
||||
import static org.adaway.db.entity.HostsSource.USER_SOURCE_URL;
|
||||
import static org.adaway.db.entity.ListType.ALLOWED;
|
||||
import static org.adaway.db.entity.ListType.BLOCKED;
|
||||
import static org.adaway.db.entity.ListType.REDIRECTED;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.room.Room;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.adaway.db.dao.HostEntryDao;
|
||||
import org.adaway.db.dao.HostListItemDao;
|
||||
import org.adaway.db.dao.HostsSourceDao;
|
||||
import org.adaway.db.entity.HostListItem;
|
||||
import org.adaway.db.entity.HostsSource;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* This class is a base class for testing database feature.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public abstract class DbTest {
|
||||
protected static final int EXTERNAL_SOURCE_ID = 2;
|
||||
@Rule
|
||||
public TestRule rule = new InstantTaskExecutorRule();
|
||||
protected AppDatabase db;
|
||||
protected HostsSourceDao hostsSourceDao;
|
||||
protected HostListItemDao hostListItemDao;
|
||||
protected HostEntryDao hostEntryDao;
|
||||
protected LiveData<Integer> blockedHostCount;
|
||||
protected LiveData<Integer> allowedHostCount;
|
||||
protected LiveData<Integer> redirectedHostCount;
|
||||
protected HostsSource externalHostSource;
|
||||
|
||||
protected static <T> T getOrAwaitValue(final LiveData<T> liveData) throws InterruptedException {
|
||||
final Object[] data = new Object[1];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Observer<T> observer = new Observer<T>() {
|
||||
@Override
|
||||
public void onChanged(@Nullable T o) {
|
||||
data[0] = o;
|
||||
latch.countDown();
|
||||
liveData.removeObserver(this);
|
||||
}
|
||||
};
|
||||
liveData.observeForever(observer);
|
||||
if (!latch.await(2, TimeUnit.SECONDS)) {
|
||||
fail("Failed to get LiveData value in time");
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (T) data[0];
|
||||
}
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
createDb();
|
||||
loadDao();
|
||||
createSources();
|
||||
}
|
||||
|
||||
protected void createDb() {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
this.db = Room.inMemoryDatabaseBuilder(context, AppDatabase.class)
|
||||
.allowMainThreadQueries()
|
||||
.build();
|
||||
}
|
||||
|
||||
protected void loadDao() {
|
||||
this.hostsSourceDao = this.db.hostsSourceDao();
|
||||
this.hostListItemDao = this.db.hostsListItemDao();
|
||||
this.hostEntryDao = this.db.hostEntryDao();
|
||||
this.blockedHostCount = this.hostListItemDao.getBlockedHostCount();
|
||||
this.allowedHostCount = this.hostListItemDao.getAllowedHostCount();
|
||||
this.redirectedHostCount = this.hostListItemDao.getRedirectHostCount();
|
||||
}
|
||||
|
||||
protected void createSources() {
|
||||
// Insert at least user source and external source to allow duplicate hosts to be inserted
|
||||
insertSource(USER_SOURCE_ID, USER_SOURCE_URL);
|
||||
insertSource(EXTERNAL_SOURCE_ID, "https://adaway.org/hosts.txt");
|
||||
this.externalHostSource = getSourceFromId(EXTERNAL_SOURCE_ID);
|
||||
}
|
||||
|
||||
@After
|
||||
public void closeDb() {
|
||||
this.db.close();
|
||||
}
|
||||
|
||||
protected void insertSource(int id, String url) {
|
||||
HostsSource source = new HostsSource();
|
||||
source.setId(id);
|
||||
source.setLabel(url);
|
||||
source.setUrl(url);
|
||||
source.setEnabled(true);
|
||||
this.hostsSourceDao.insert(source);
|
||||
}
|
||||
|
||||
protected void insertBlockedHost(String host, int sourceId) {
|
||||
HostListItem item = new HostListItem();
|
||||
item.setType(BLOCKED);
|
||||
item.setHost(host);
|
||||
item.setEnabled(true);
|
||||
item.setSourceId(sourceId);
|
||||
this.hostListItemDao.insert(item);
|
||||
}
|
||||
|
||||
protected void insertAllowedHost(String host, int sourceId) {
|
||||
HostListItem item = new HostListItem();
|
||||
item.setType(ALLOWED);
|
||||
item.setHost(host);
|
||||
item.setEnabled(true);
|
||||
item.setSourceId(sourceId);
|
||||
this.hostListItemDao.insert(item);
|
||||
}
|
||||
|
||||
protected void insertRedirectedHost(String host, String redirection, int sourceId) {
|
||||
HostListItem item = new HostListItem();
|
||||
item.setType(REDIRECTED);
|
||||
item.setHost(host);
|
||||
item.setEnabled(true);
|
||||
item.setRedirection(redirection);
|
||||
item.setSourceId(sourceId);
|
||||
this.hostListItemDao.insert(item);
|
||||
}
|
||||
|
||||
protected HostsSource getSourceFromId(int id) {
|
||||
return this.hostsSourceDao.getAll()
|
||||
.stream()
|
||||
.filter(hostsSource -> hostsSource.getId() == id)
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
90
app/src/androidTest/java/org/adaway/db/HostDbTest.java
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
package org.adaway.db;
|
||||
|
||||
import static org.adaway.db.entity.HostsSource.USER_SOURCE_ID;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.adaway.db.entity.HostEntry;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class tests {@link org.adaway.db.entity.HostListItem} database manipulations.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public class HostDbTest extends DbTest {
|
||||
@Test
|
||||
public void testEmptyByDefault() throws InterruptedException {
|
||||
assertEquals(0, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(0, getOrAwaitValue(this.allowedHostCount).intValue());
|
||||
assertEquals(0, getOrAwaitValue(this.redirectedHostCount).intValue());
|
||||
assertEquals(0, this.hostEntryDao.getAll().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertThenDeleteHosts() throws InterruptedException {
|
||||
// Insert blocked hosts
|
||||
insertBlockedHost("advertising.apple.com", USER_SOURCE_ID);
|
||||
insertBlockedHost("an.facebook.com", USER_SOURCE_ID);
|
||||
this.hostEntryDao.sync();
|
||||
// Check inserting
|
||||
assertEquals(2, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(0, getOrAwaitValue(this.allowedHostCount).intValue());
|
||||
assertEquals(0, getOrAwaitValue(this.redirectedHostCount).intValue());
|
||||
assertEquals(2, this.hostEntryDao.getAll().size());
|
||||
// Remove block hosts
|
||||
this.hostListItemDao.deleteUserFromHost("advertising.apple.com");
|
||||
this.hostEntryDao.sync();
|
||||
// Check deletion
|
||||
assertEquals(1, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(0, getOrAwaitValue(this.allowedHostCount).intValue());
|
||||
assertEquals(0, getOrAwaitValue(this.redirectedHostCount).intValue());
|
||||
assertEquals(1, this.hostEntryDao.getAll().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateBlockedHosts() throws InterruptedException {
|
||||
insertBlockedHost("advertising.apple.com", USER_SOURCE_ID);
|
||||
insertBlockedHost("advertising.apple.com", EXTERNAL_SOURCE_ID);
|
||||
this.hostEntryDao.sync();
|
||||
assertEquals(1, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(1, this.hostEntryDao.getAll().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateAllowedHosts() throws InterruptedException {
|
||||
insertAllowedHost("adaway.org", USER_SOURCE_ID);
|
||||
insertAllowedHost("adaway.org", EXTERNAL_SOURCE_ID);
|
||||
this.hostEntryDao.sync();
|
||||
assertEquals(1, getOrAwaitValue(this.allowedHostCount).intValue());
|
||||
assertEquals(0, this.hostEntryDao.getAll().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateRedirectedHosts() throws InterruptedException {
|
||||
insertRedirectedHost("github.com", "1.1.1.1", USER_SOURCE_ID);
|
||||
insertRedirectedHost("github.com", "2.2.2.2", EXTERNAL_SOURCE_ID);
|
||||
this.hostEntryDao.sync();
|
||||
assertEquals(1, getOrAwaitValue(this.redirectedHostCount).intValue());
|
||||
assertEquals(1, this.hostEntryDao.getAll().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectionPriority() throws InterruptedException {
|
||||
// Insert two redirects for the same host
|
||||
insertRedirectedHost("adaway.org", "1.1.1.1", USER_SOURCE_ID);
|
||||
insertRedirectedHost("adaway.org", "2.2.2.2", EXTERNAL_SOURCE_ID);
|
||||
this.hostEntryDao.sync();
|
||||
// Test inserted redirected hosts
|
||||
assertEquals(1, getOrAwaitValue(this.redirectedHostCount).intValue());
|
||||
// Test inserted redirect
|
||||
List<HostEntry> entries = this.hostEntryDao.getAll();
|
||||
assertEquals(1, entries.size());
|
||||
// Test user redirect is applied in priority
|
||||
HostEntry entry = this.hostEntryDao.getEntry("adaway.org");
|
||||
assertNotNull(entry);
|
||||
assertEquals("1.1.1.1", entry.getRedirection());
|
||||
}
|
||||
}
|
||||
113
app/src/androidTest/java/org/adaway/db/SourceDbTest.java
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
package org.adaway.db;
|
||||
|
||||
import static org.adaway.db.entity.HostsSource.USER_SOURCE_ID;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.adaway.db.entity.HostsSource;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class tests {@link HostsSource} database manipulations.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public class SourceDbTest extends DbTest {
|
||||
@Test
|
||||
public void testSourceCount() {
|
||||
// Test only external source is found
|
||||
List<HostsSource> sources = this.hostsSourceDao.getAll();
|
||||
assertEquals(1, sources.size());
|
||||
assertEquals("https://adaway.org/hosts.txt", sources.get(0).getUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSourceDeletion() throws InterruptedException {
|
||||
// Insert blocked hosts
|
||||
insertBlockedHost("bingads.microsoft.com", EXTERNAL_SOURCE_ID);
|
||||
insertBlockedHost("ads.yahoo.com", EXTERNAL_SOURCE_ID);
|
||||
this.hostEntryDao.sync();
|
||||
// Test inserted blocked hosts
|
||||
assertEquals(2, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(2, this.hostEntryDao.getAll().size());
|
||||
// Delete source
|
||||
this.hostsSourceDao.delete(this.externalHostSource);
|
||||
this.hostEntryDao.sync();
|
||||
List<HostsSource> sources = this.hostsSourceDao.getAll();
|
||||
assertEquals(0, sources.size());
|
||||
// Check related hosts cleaning
|
||||
assertEquals(0, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(0, this.hostEntryDao.getAll().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlockedHostsFromDisabledSource() throws InterruptedException {
|
||||
// Insert blocked hosts
|
||||
insertBlockedHost("advertising.apple.com", USER_SOURCE_ID);
|
||||
insertBlockedHost("an.facebook.com", USER_SOURCE_ID);
|
||||
insertBlockedHost("ads.google.com", USER_SOURCE_ID);
|
||||
insertBlockedHost("bingads.microsoft.com", EXTERNAL_SOURCE_ID);
|
||||
insertBlockedHost("ads.yahoo.com", EXTERNAL_SOURCE_ID);
|
||||
this.hostEntryDao.sync();
|
||||
// Test inserted blocked hosts
|
||||
assertEquals(5, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(5, this.hostEntryDao.getAll().size());
|
||||
// Disabled external source
|
||||
this.hostsSourceDao.toggleEnabled(this.externalHostSource);
|
||||
this.hostEntryDao.sync();
|
||||
assertEquals(3, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(3, this.hostEntryDao.getAll().size());
|
||||
// Re-enable external source
|
||||
this.hostsSourceDao.toggleEnabled(this.externalHostSource);
|
||||
this.hostEntryDao.sync();
|
||||
assertEquals(5, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(5, this.hostEntryDao.getAll().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllowedHostsFromDisabledSource() throws InterruptedException {
|
||||
// Insert blocked and allowed host
|
||||
insertBlockedHost("adaway.org", USER_SOURCE_ID);
|
||||
insertAllowedHost("adaway.org", EXTERNAL_SOURCE_ID);
|
||||
this.hostEntryDao.sync();
|
||||
// Test inserted blocked hosts
|
||||
assertEquals(1, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(1, getOrAwaitValue(this.allowedHostCount).intValue());
|
||||
assertEquals(0, this.hostEntryDao.getAll().size());
|
||||
// Disabled a source
|
||||
this.hostsSourceDao.toggleEnabled(this.externalHostSource);
|
||||
this.hostEntryDao.sync();
|
||||
assertEquals(1, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(0, getOrAwaitValue(this.allowedHostCount).intValue());
|
||||
assertEquals(1, this.hostEntryDao.getAll().size());
|
||||
// Re-enable a source
|
||||
this.hostsSourceDao.toggleEnabled(this.externalHostSource);
|
||||
this.hostEntryDao.sync();
|
||||
assertEquals(1, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(1, getOrAwaitValue(this.allowedHostCount).intValue());
|
||||
assertEquals(0, this.hostEntryDao.getAll().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectedHostsFromDisabledSource() throws InterruptedException {
|
||||
// Insert redirected hosts
|
||||
insertRedirectedHost("github.com", "1.1.1.1", USER_SOURCE_ID);
|
||||
insertRedirectedHost("github.com", "2.2.2.2", EXTERNAL_SOURCE_ID);
|
||||
this.hostEntryDao.sync();
|
||||
// Test inserted blocked hosts
|
||||
assertEquals(1, getOrAwaitValue(this.redirectedHostCount).intValue());
|
||||
assertEquals(1, this.hostEntryDao.getAll().size());
|
||||
// Disabled a source
|
||||
this.hostsSourceDao.toggleEnabled(this.externalHostSource);
|
||||
this.hostEntryDao.sync();
|
||||
assertEquals(1, getOrAwaitValue(this.redirectedHostCount).intValue());
|
||||
assertEquals(1, this.hostEntryDao.getAll().size());
|
||||
// Re-enable a source
|
||||
this.hostsSourceDao.toggleEnabled(this.externalHostSource);
|
||||
this.hostEntryDao.sync();
|
||||
assertEquals(1, getOrAwaitValue(this.redirectedHostCount).intValue());
|
||||
assertEquals(1, this.hostEntryDao.getAll().size());
|
||||
}
|
||||
}
|
||||
157
app/src/androidTest/java/org/adaway/db/UserListTest.java
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
package org.adaway.db;
|
||||
|
||||
import static org.adaway.db.entity.HostsSource.USER_SOURCE_ID;
|
||||
import static org.adaway.db.entity.ListType.ALLOWED;
|
||||
import static org.adaway.db.entity.ListType.BLOCKED;
|
||||
import static org.adaway.db.entity.ListType.REDIRECTED;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.adaway.db.entity.HostEntry;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* This class the user lists use case where user can freely add blocked, allowed and redirected hosts.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public class UserListTest extends DbTest {
|
||||
@Test
|
||||
public void testUserList() throws InterruptedException {
|
||||
testUserBlockedHosts();
|
||||
testUserAllowedHosts();
|
||||
testUserRedirectedHosts();
|
||||
}
|
||||
|
||||
protected void testUserBlockedHosts() throws InterruptedException {
|
||||
// Insert blocked hosts
|
||||
insertBlockedHost("advertising.apple.com", USER_SOURCE_ID);
|
||||
insertBlockedHost("an.facebook.com", USER_SOURCE_ID);
|
||||
insertBlockedHost("ads.google.com", USER_SOURCE_ID);
|
||||
insertBlockedHost("bingads.microsoft.com", USER_SOURCE_ID);
|
||||
insertBlockedHost("ads.yahoo.com", USER_SOURCE_ID);
|
||||
this.hostEntryDao.sync();
|
||||
// Test inserted blocked hosts
|
||||
assertEquals(5, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(5, this.hostEntryDao.getAll().size());
|
||||
// Test each host type
|
||||
assertEquals(BLOCKED, this.hostEntryDao.getTypeForHost("advertising.apple.com"));
|
||||
HostEntry entry = this.hostEntryDao.getEntry("advertising.apple.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("advertising.apple.com", entry.getHost());
|
||||
assertEquals(BLOCKED, entry.getType());
|
||||
assertEquals(BLOCKED, this.hostEntryDao.getTypeForHost("an.facebook.com"));
|
||||
entry = this.hostEntryDao.getEntry("an.facebook.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("an.facebook.com", entry.getHost());
|
||||
assertEquals(BLOCKED, entry.getType());
|
||||
assertEquals(BLOCKED, this.hostEntryDao.getTypeForHost("ads.google.com"));
|
||||
entry = this.hostEntryDao.getEntry("ads.google.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("ads.google.com", entry.getHost());
|
||||
assertEquals(BLOCKED, entry.getType());
|
||||
assertEquals(BLOCKED, this.hostEntryDao.getTypeForHost("bingads.microsoft.com"));
|
||||
entry = this.hostEntryDao.getEntry("bingads.microsoft.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("bingads.microsoft.com", entry.getHost());
|
||||
assertEquals(BLOCKED, entry.getType());
|
||||
assertEquals(BLOCKED, this.hostEntryDao.getTypeForHost("ads.yahoo.com"));
|
||||
entry = this.hostEntryDao.getEntry("ads.yahoo.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("ads.yahoo.com", entry.getHost());
|
||||
assertEquals(BLOCKED, entry.getType());
|
||||
}
|
||||
|
||||
protected void testUserAllowedHosts() throws InterruptedException {
|
||||
// Insert allowed hosts
|
||||
insertAllowedHost("*.google.com", USER_SOURCE_ID);
|
||||
insertAllowedHost("ads.yahoo.com", USER_SOURCE_ID);
|
||||
insertAllowedHost("adaway.org", USER_SOURCE_ID);
|
||||
this.hostEntryDao.sync();
|
||||
// Test inserted allowed hosts
|
||||
assertEquals(3, getOrAwaitValue(this.allowedHostCount).intValue());
|
||||
// Test overall list
|
||||
assertEquals(5, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(3, this.hostEntryDao.getAll().size());
|
||||
// Test each host type
|
||||
assertEquals(ALLOWED, this.hostEntryDao.getTypeForHost("adaway.org"));
|
||||
HostEntry entry = this.hostEntryDao.getEntry("adaway.org");
|
||||
assertNull(entry);
|
||||
assertEquals(BLOCKED, this.hostEntryDao.getTypeForHost("advertising.apple.com"));
|
||||
entry = this.hostEntryDao.getEntry("advertising.apple.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("advertising.apple.com", entry.getHost());
|
||||
assertEquals(BLOCKED, entry.getType());
|
||||
assertEquals(BLOCKED, this.hostEntryDao.getTypeForHost("an.facebook.com"));
|
||||
entry = this.hostEntryDao.getEntry("an.facebook.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("an.facebook.com", entry.getHost());
|
||||
assertEquals(BLOCKED, entry.getType());
|
||||
assertEquals(ALLOWED, this.hostEntryDao.getTypeForHost("ads.google.com"));
|
||||
entry = this.hostEntryDao.getEntry("ads.google.com");
|
||||
assertNull(entry);
|
||||
assertEquals(BLOCKED, this.hostEntryDao.getTypeForHost("bingads.microsoft.com"));
|
||||
entry = this.hostEntryDao.getEntry("bingads.microsoft.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("bingads.microsoft.com", entry.getHost());
|
||||
assertEquals(BLOCKED, entry.getType());
|
||||
assertEquals(ALLOWED, this.hostEntryDao.getTypeForHost("ads.yahoo.com"));
|
||||
entry = this.hostEntryDao.getEntry("ads.yahoo.com");
|
||||
assertNull(entry);
|
||||
|
||||
}
|
||||
|
||||
protected void testUserRedirectedHosts() throws InterruptedException {
|
||||
// Insert redirected hosts
|
||||
insertRedirectedHost("ads.yahoo.com", "1.2.3.4", USER_SOURCE_ID);
|
||||
insertRedirectedHost("github.com", "1.2.3.4", USER_SOURCE_ID);
|
||||
this.hostEntryDao.sync();
|
||||
// Test inserted redirected hosts
|
||||
assertEquals(2, getOrAwaitValue(this.redirectedHostCount).intValue());
|
||||
// Test overall list
|
||||
assertEquals(5, getOrAwaitValue(this.blockedHostCount).intValue());
|
||||
assertEquals(3, getOrAwaitValue(this.allowedHostCount).intValue());
|
||||
assertEquals(5, this.hostEntryDao.getAll().size()); // 3 blocked, 2 redirected
|
||||
// Test each host type
|
||||
assertEquals(ALLOWED, this.hostEntryDao.getTypeForHost("adaway.org"));
|
||||
HostEntry entry = this.hostEntryDao.getEntry("adaway.org");
|
||||
assertNull(entry);
|
||||
|
||||
assertEquals(BLOCKED, this.hostEntryDao.getTypeForHost("advertising.apple.com"));
|
||||
entry = this.hostEntryDao.getEntry("advertising.apple.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("advertising.apple.com", entry.getHost());
|
||||
assertEquals(BLOCKED, entry.getType());
|
||||
|
||||
assertEquals(BLOCKED, this.hostEntryDao.getTypeForHost("an.facebook.com"));
|
||||
entry = this.hostEntryDao.getEntry("an.facebook.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("an.facebook.com", entry.getHost());
|
||||
assertEquals(BLOCKED, entry.getType());
|
||||
|
||||
assertEquals(REDIRECTED, this.hostEntryDao.getTypeForHost("github.com"));
|
||||
entry = this.hostEntryDao.getEntry("github.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("github.com", entry.getHost());
|
||||
assertEquals(REDIRECTED, entry.getType());
|
||||
assertEquals("1.2.3.4", entry.getRedirection());
|
||||
|
||||
assertEquals(ALLOWED, this.hostEntryDao.getTypeForHost("ads.google.com"));
|
||||
entry = this.hostEntryDao.getEntry("ads.google.com");
|
||||
assertNull(entry);
|
||||
|
||||
assertEquals(BLOCKED, this.hostEntryDao.getTypeForHost("bingads.microsoft.com"));
|
||||
entry = this.hostEntryDao.getEntry("bingads.microsoft.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("bingads.microsoft.com", entry.getHost());
|
||||
assertEquals(BLOCKED, entry.getType());
|
||||
|
||||
assertEquals(REDIRECTED, this.hostEntryDao.getTypeForHost("ads.yahoo.com"));
|
||||
entry = this.hostEntryDao.getEntry("ads.yahoo.com");
|
||||
assertNotNull(entry);
|
||||
assertEquals("ads.yahoo.com", entry.getHost());
|
||||
assertEquals(REDIRECTED, entry.getType());
|
||||
assertEquals("1.2.3.4", entry.getRedirection());
|
||||
}
|
||||
}
|
||||
177
app/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony"
|
||||
android:required="false" />
|
||||
|
||||
<permission-group
|
||||
android:name="org.adaway.permission-group.API"
|
||||
android:description="@string/permission_group_api_description"
|
||||
android:label="@string/permission_group_api_label" />
|
||||
|
||||
<permission
|
||||
android:name="org.adaway.permission.SEND_COMMAND"
|
||||
android:description="@string/permission_send_command_description"
|
||||
android:label="@string/permission_send_command_label"
|
||||
android:permissionGroup="org.adaway.permission-group.API"
|
||||
android:protectionLevel="dangerous" />
|
||||
|
||||
<supports-screens
|
||||
android:anyDensity="true"
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true"
|
||||
android:xlargeScreens="true" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission
|
||||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
|
||||
<application
|
||||
android:name=".AdAwayApplication"
|
||||
android:allowBackup="true"
|
||||
android:backupAgent=".model.backup.AppBackupAgent"
|
||||
android:fullBackupOnly="false"
|
||||
android:icon="@mipmap/icon"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.AdAway"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<activity
|
||||
android:name=".ui.home.HomeActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/Theme.AdAway.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.welcome.WelcomeActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/welcome_title"
|
||||
android:parentActivityName=".ui.home.HomeActivity"
|
||||
android:theme="@style/Theme.AdAway.NoActionBar.Red" />
|
||||
<activity
|
||||
android:name=".ui.hosts.HostsSourcesActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/hosts_title"
|
||||
android:parentActivityName=".ui.home.HomeActivity" />
|
||||
<activity
|
||||
android:name=".ui.source.SourceEditActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/source_edit_title"
|
||||
android:parentActivityName=".ui.hosts.HostsSourcesActivity" />
|
||||
<activity
|
||||
android:name=".ui.log.LogActivity"
|
||||
android:label="@string/shortcut_dns_requests"
|
||||
android:parentActivityName=".ui.home.HomeActivity" />
|
||||
<activity
|
||||
android:name=".ui.lists.ListsActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/lists_title"
|
||||
android:launchMode="singleTop"
|
||||
android:parentActivityName=".ui.home.HomeActivity">
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
android:resource="@xml/list_searchable" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.help.HelpActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/menu_help"
|
||||
android:parentActivityName=".ui.home.HomeActivity" />
|
||||
<activity
|
||||
android:name=".ui.support.SupportActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/support_title"
|
||||
android:parentActivityName=".ui.home.HomeActivity"
|
||||
android:theme="@style/Theme.AdAway.NoActionBar.Red" />
|
||||
<activity
|
||||
android:name=".ui.prefs.PrefsActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/preferences_drawer_item"
|
||||
android:parentActivityName=".ui.home.HomeActivity" />
|
||||
<activity
|
||||
android:name=".ui.prefs.exclusion.PrefsVpnExcludedAppsActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/pref_vpn_exclude_user_apps_activity" />
|
||||
<activity
|
||||
android:name=".ui.update.UpdateActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/update_title"
|
||||
android:parentActivityName=".ui.home.HomeActivity"
|
||||
android:theme="@style/Theme.AdAway.NoActionBar.Red" />
|
||||
|
||||
<service
|
||||
android:name=".vpn.VpnService"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".tile.AdBlockingTileService"
|
||||
android:exported="true"
|
||||
android:icon="@drawable/logo"
|
||||
android:label="@string/app_name"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name=".broadcast.BootReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".broadcast.CommandReceiver"
|
||||
android:exported="true"
|
||||
android:permission="org.adaway.permission.SEND_COMMAND">
|
||||
<intent-filter>
|
||||
<action android:name="org.adaway.action.SEND_COMMAND" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".broadcast.UpdateReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<meta-data
|
||||
android:name="io.sentry.auto-init"
|
||||
android:value="false" />
|
||||
<meta-data
|
||||
android:name="io.sentry.dsn"
|
||||
android:value="https://8dac17b798fb45e492278a678c5ab028@o209266.ingest.us.sentry.io/1331667" />
|
||||
<meta-data android:name="io.sentry.traces.user-interaction.enable" android:value="true" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
113
app/src/main/assets/icon.svg
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 32 32"
|
||||
id="svg3167"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
sodipodi:docname="icon-lite.svg">
|
||||
<defs
|
||||
id="defs3169">
|
||||
<radialGradient
|
||||
r="17.497915"
|
||||
fy="40.636124"
|
||||
fx="33.772423"
|
||||
cy="40.636124"
|
||||
cx="33.772423"
|
||||
gradientTransform="matrix(1.161721,0,0,1.0498451,-2.6955965,978.41529)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3878"
|
||||
xlink:href="#linearGradient4263"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4263">
|
||||
<stop
|
||||
id="stop4265"
|
||||
offset="0"
|
||||
style="stop-color:#bfbfbf;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop4267"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4263"
|
||||
id="radialGradient3233"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.161721,0,0,1.0498451,-2.6955965,978.41529)"
|
||||
cx="33.772423"
|
||||
cy="40.636124"
|
||||
fx="33.772423"
|
||||
fy="40.636124"
|
||||
r="17.497915" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4263"
|
||||
id="radialGradient3238"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.61137216,0,0,0.55249588,-4.1660353,-4.330286)"
|
||||
cx="33.772423"
|
||||
cy="40.636124"
|
||||
fx="33.772423"
|
||||
fy="40.636124"
|
||||
r="17.497915" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4263"
|
||||
id="radialGradient3241"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.61137216,0,0,0.55249588,-3.9874288,-4.330286)"
|
||||
cx="33.772423"
|
||||
cy="40.636124"
|
||||
fx="33.772423"
|
||||
fy="40.636124"
|
||||
r="17.497915" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.836083"
|
||||
inkscape:cx="5.6448078"
|
||||
inkscape:cy="16.057137"
|
||||
inkscape:current-layer="svg3167"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1387"
|
||||
inkscape:window-x="1672"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata3172">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path
|
||||
style="fill:#cc0000;fill-opacity:0.11640212;stroke:#333333;stroke-width:0.46215421000000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.23529412000000000;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
d="M 9.90625 1.21875 L 1.25 9.875 L 1.21875 22.09375 L 9.875 30.75 L 22.09375 30.78125 L 30.75 22.125 L 30.78125 9.90625 L 22.125 1.25 L 9.90625 1.21875 z M 6 8.1875 C 6.6763721 8.1197446 10.285042 9.6406437 14.5625 9.65625 C 16.759542 9.791605 17.570434 10.416353 18.1875 11.0625 C 19.571912 12.366793 19.873633 13.03126 21.625 12.1875 C 22.338438 11.843849 23.228376 9.8114647 24.21875 9.59375 C 24.990847 9.481077 26.122457 10.438725 26.40625 10.71875 C 26.716053 11.02451 26.985342 11.333132 27.28125 11.46875 C 26.265273 11.52259 24.879528 13.892184 24.84375 15.90625 C 25.046345 21.533487 22.49174 21.215441 20 23.96875 C 19.205534 24.725992 18.781983 26.303089 18.78125 27.75 L 17.71875 27.71875 C 17.098386 27.73296 16.405511 26.947039 16.0625 26.40625 C 15.407551 25.314883 14.737631 27.401017 13.71875 25.6875 C 13.7659 25.120872 10.972706 25.011002 12.5625 23.6875 C 16.317406 21.029656 18.172296 20.45754 18.09375 19.65625 C 18.00095 18.709606 15.800894 19.560638 13.8125 19.46875 C 10.527778 19.317028 10.884928 17.249787 10.59375 16.28125 C 7.8361706 14.745664 12.704357 14.015508 10.5625 13.5625 C 9.5159322 13.396095 7.6703939 13.778783 7.28125 11.96875 C 7.6312051 12.401076 12.220507 11.538235 10.375 11.25 C 7.814251 10.088009 7.0930254 10.68347 6.03125 8.5 C 5.8473548 8.2768081 5.8439141 8.2031359 6 8.1875 z "
|
||||
id="path3852" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
20
app/src/main/assets/localhost-2410.crt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDTTCCAjWgAwIBAgIUR9ZJhU2vy/hB7LChIPgUuBEzqkQwDQYJKoZIhvcNAQEL
|
||||
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MTAyNzA2NTEwNVoXDTI3MTEy
|
||||
NzA2NTEwNVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAQ8AMIIBCgKCAQEAoVNvAzvXCjH07lgGTwBRMOFNuvsIBzi5rtJw8EQI1+np
|
||||
h8jZjGmTq759VoqsRSrzPSb8W4XNmGlg8gm0nRN9VJXE9IccWQmpbl61MG6zu9Ae
|
||||
/RZ8iAuydztn16/sOVQMT3Y0dXl3Fz9VtSEAMWZG9iATlVuugShLbod+Smw3mQow
|
||||
2Z6Mfg6u4vpPCLG13Hdilv+UGs19dUKlFJdsz2M/gsk0vIlyhaFc9PcyzKJG4Tk2
|
||||
XZOHEWMj7VjmEMZoUogihi9EWPLpBoi86dIWBBQfhu9HTwzl1BE33TMtYLPdKdxD
|
||||
9I9QPg59wbquiVYZ3sPVISdks4qmg4NgzBJVB/tWhQIDAQABo4GWMIGTMEIGA1Ud
|
||||
EQQ7MDmCCWxvY2FsaG9zdIIRKi5kb3VibGVjbGljay5uZXSCEyouZy5kb3VibGVj
|
||||
bGljay5uZXSHBH8AAAEwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMB
|
||||
MAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFPyh/TOOyMqOumAhxn1Jsyb09CQ3MA0G
|
||||
CSqGSIb3DQEBCwUAA4IBAQAdxRNksyLjIwVaKjmC+Yx87zKw8bSmH2mm85KJ0Y7z
|
||||
uoWLdiw5Lc7/uZoXEl4KGJEnAXb83lo2042WCn49bOhAtYv7tOBTvTGhCHCZ6p7u
|
||||
Lk+dN8eOkfKgIpKv+tyt0y+Dl+K9m0TegBqky03xf+gjfMaNctwkxxl7Ls4CR9Lz
|
||||
Bufvt1HxJkmSILmTy8ByJPG5ePN6By4YiEwg9h2Hdx7zEqAKkgHvr4Yn/c4GzDQ+
|
||||
UZZW9NCIsKkaV32UfpCSodlpc+xeayX8jxZW2GnXs31IjJd4KuNfK5g7ZuOIZN0S
|
||||
iKvTwJkScrfvLlmbCqEJ7Nu4l/XR+5EAy21YNlV+mo7D
|
||||
-----END CERTIFICATE-----
|
||||
28
app/src/main/assets/localhost-2410.key
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQChU28DO9cKMfTu
|
||||
WAZPAFEw4U26+wgHOLmu0nDwRAjX6emHyNmMaZOrvn1WiqxFKvM9Jvxbhc2YaWDy
|
||||
CbSdE31UlcT0hxxZCaluXrUwbrO70B79FnyIC7J3O2fXr+w5VAxPdjR1eXcXP1W1
|
||||
IQAxZkb2IBOVW66BKEtuh35KbDeZCjDZnox+Dq7i+k8IsbXcd2KW/5QazX11QqUU
|
||||
l2zPYz+CyTS8iXKFoVz09zLMokbhOTZdk4cRYyPtWOYQxmhSiCKGL0RY8ukGiLzp
|
||||
0hYEFB+G70dPDOXUETfdMy1gs90p3EP0j1A+Dn3Buq6JVhnew9UhJ2SziqaDg2DM
|
||||
ElUH+1aFAgMBAAECggEAErRYPjE9dP6mzc2h6Z35S+gLeZ7qZt/yU20t0AWrWtFR
|
||||
lL86Tffdub9ry9FnONvKePAguUHRvRaWuWlbqgyc7uYwgEN8C2y92sCbVGK5bxCp
|
||||
zyFAzgtBJWbbWtwYUOtIRBxJ58buAmGC/+20FoYruxSsAJixKmNwH4ARKfLTHWis
|
||||
bwzULTA4fhmvPgv+DWg7rxOTZP+7Jl5xFQnj4lXQaTt44B9DQF7ArtVjwHJWGbAN
|
||||
muc4Ij/2lzD1EFNv9hXRvPjnhdN8WuDXZ52BTNj3ZXEYYyjw6Mvy7Qtsiz4FovpT
|
||||
UvIsQgUyvkyukKX+8rQb+JAcfZQHnfXFSoZyIJ/KDwKBgQDQAY7L6o/ZwQ2FjyyN
|
||||
K0Zu4oozQh1VM6C8eGtOsa6b+1y1d3RQg0JYb4ay1KwyFAUVnDSx/dNVg8LxVbL+
|
||||
st0EAUYmyNtozAerkAAdbYLr/mDKM4pYp9OzamqJYaUVXDa70zEOe/Q86a4KKa8l
|
||||
mr6jfd2+OHhBjFfc/UWfZKWAiwKBgQDGjJXM8KvFIqkBnNqcy1NhhtUUAwzV9oye
|
||||
HsagZBiTDXeCq2FUOJRh1474az+X12PTCDHNobvhGwz+eYe8yfDmILE9RmjbtCwO
|
||||
YzTFuEyzFxG4aWAVV9/u+qWu8LQFeXRVOyztNFmIXqWaaCg0tahaVKI0dukMk+/1
|
||||
M4b/fGzXLwKBgCBxfcJUjadbMy63zC0gqNW2w/OGxmh5qwJ6jdIyaJevtyAex6ef
|
||||
MYP1sT7HaSxObxSVzqpMeuAFsyxNP6P2Zf6v7C80ePR5jmC2Dy6H3DnO7W3caCG3
|
||||
249Kc9+FuWgBgA//utEViFzP3fN72PO2lTGO+j0nNaqTp0iywF9CJYZNAoGAXo/k
|
||||
ZKgXVxuL3K3E3Lpl6uQZpZ9SRLFZBZHozcj+f0MBsWVIRKFx4iuU9zG1Ju85pu+X
|
||||
MLWf0rVcefKNuFeBeUkGwQVAuarU9MFBCA4f0YfiM69USLYCfEI6GNihFJ5kzpcR
|
||||
baPqJG3Xd3O1+myuUt9OJaiglBH9Tg4NdK7g85cCgYAYL3r9a8LosXGwtqiahX9o
|
||||
Hyu/lRrKDH6yvUN2YeHN10pyY8rkdLAZ6E6imgxHqA8n8AibhkoaGpTrBlKRgP1/
|
||||
MMjeqy3C3hYjBQdfs+Vv6YtsufhegHs84/tgDxyfkWcljeXg2GoZvPQxANNZe8QE
|
||||
JTaivHj2kiF8ZakITrXT+A==
|
||||
-----END PRIVATE KEY-----
|
||||
13
app/src/main/assets/test.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Web server test page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Web server works fine</h1>
|
||||
<img width="60%" src="icon.png" alt="AdAway logo when option is enabled" style="display: block; margin: auto;">
|
||||
<p>If you can see this page, that means the web server is working fine and its certificate is installed.</p>
|
||||
</body>
|
||||
</html>
|
||||
BIN
app/src/main/icon-web.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
76
app/src/main/java/org/adaway/AdAwayApplication.java
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package org.adaway;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import org.adaway.helper.NotificationHelper;
|
||||
import org.adaway.helper.PreferenceHelper;
|
||||
import org.adaway.model.adblocking.AdBlockMethod;
|
||||
import org.adaway.model.adblocking.AdBlockModel;
|
||||
import org.adaway.model.source.SourceModel;
|
||||
import org.adaway.model.update.UpdateModel;
|
||||
import org.adaway.util.log.ApplicationLog;
|
||||
|
||||
/**
|
||||
* This class is a custom {@link Application} for AdAway app.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public class AdAwayApplication extends Application {
|
||||
/**
|
||||
* The common source model for the whole application.
|
||||
*/
|
||||
private SourceModel sourceModel;
|
||||
/**
|
||||
* The common ad block model for the whole application.
|
||||
*/
|
||||
private AdBlockModel adBlockModel;
|
||||
/**
|
||||
* The common update model for the whole application.
|
||||
*/
|
||||
private UpdateModel updateModel;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// Delegate application creation
|
||||
super.onCreate();
|
||||
// Initialize logging
|
||||
ApplicationLog.init(this);
|
||||
// Create notification channels
|
||||
NotificationHelper.createNotificationChannels(this);
|
||||
// Create models
|
||||
this.sourceModel = new SourceModel(this);
|
||||
this.updateModel = new UpdateModel(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source model.
|
||||
*
|
||||
* @return The common source model for the whole application.
|
||||
*/
|
||||
public SourceModel getSourceModel() {
|
||||
return this.sourceModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ad block model.
|
||||
*
|
||||
* @return The common ad block model for the whole application.
|
||||
*/
|
||||
public AdBlockModel getAdBlockModel() {
|
||||
// Check cached model
|
||||
AdBlockMethod method = PreferenceHelper.getAdBlockMethod(this);
|
||||
if (this.adBlockModel == null || this.adBlockModel.getMethod() != method) {
|
||||
this.adBlockModel = AdBlockModel.build(this, method);
|
||||
}
|
||||
return this.adBlockModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the update model.
|
||||
*
|
||||
* @return Teh common update model for the whole application.
|
||||
*/
|
||||
public UpdateModel getUpdateModel() {
|
||||
return this.updateModel;
|
||||
}
|
||||
}
|
||||
64
app/src/main/java/org/adaway/broadcast/BootReceiver.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (C) 2011-2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This file is part of AdAway.
|
||||
*
|
||||
* AdAway is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AdAway is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AdAway. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.adaway.broadcast;
|
||||
|
||||
import static android.content.Intent.ACTION_BOOT_COMPLETED;
|
||||
import static org.adaway.model.adblocking.AdBlockMethod.ROOT;
|
||||
import static org.adaway.model.adblocking.AdBlockMethod.VPN;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.adaway.helper.PreferenceHelper;
|
||||
import org.adaway.model.adblocking.AdBlockMethod;
|
||||
import org.adaway.util.WebServerUtils;
|
||||
import org.adaway.vpn.VpnServiceControls;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This broadcast receiver is executed after boot.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public class BootReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
|
||||
Timber.d("BootReceiver invoked.");
|
||||
AdBlockMethod adBlockMethod = PreferenceHelper.getAdBlockMethod(context);
|
||||
// Start web server on boot if enabled in preferences
|
||||
if (adBlockMethod == ROOT && PreferenceHelper.getWebServerEnabled(context)) {
|
||||
WebServerUtils.startWebServer(context);
|
||||
}
|
||||
if (adBlockMethod == VPN && PreferenceHelper.getVpnServiceOnBoot(context)) {
|
||||
// Ensure VPN is prepared
|
||||
Intent prepareIntent = android.net.VpnService.prepare(context);
|
||||
if (prepareIntent != null) {
|
||||
context.startActivity(prepareIntent);
|
||||
}
|
||||
// Start VPN service if enabled in preferences
|
||||
VpnServiceControls.start(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
app/src/main/java/org/adaway/broadcast/Command.java
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package org.adaway.broadcast;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This enumerate lists the commands of {@link CommandReceiver}.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public enum Command {
|
||||
/**
|
||||
* Start the ad-blocking.
|
||||
*/
|
||||
START,
|
||||
/**
|
||||
* Stop the ad-blocking.
|
||||
*/
|
||||
STOP,
|
||||
/**
|
||||
* Unknown command.
|
||||
*/
|
||||
UNKNOWN;
|
||||
|
||||
private static final String INTENT_EXTRA_COMMAND = "COMMAND";
|
||||
|
||||
/**
|
||||
* Read command from intent.
|
||||
*
|
||||
* @param intent The intent to read command from.
|
||||
* @return The read intent.
|
||||
*/
|
||||
public static Command readFromIntent(Intent intent) {
|
||||
Command command = UNKNOWN;
|
||||
if (intent != null && intent.hasExtra(INTENT_EXTRA_COMMAND)) {
|
||||
String commandName = intent.getStringExtra(INTENT_EXTRA_COMMAND);
|
||||
if (commandName != null) {
|
||||
try {
|
||||
command = Command.valueOf(commandName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Timber.w("Failed to read command named %s.", commandName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append command to intent.
|
||||
*
|
||||
* @param intent The intent to append command to.
|
||||
*/
|
||||
public void appendToIntent(Intent intent) {
|
||||
intent.putExtra(INTENT_EXTRA_COMMAND, name());
|
||||
}
|
||||
}
|
||||
53
app/src/main/java/org/adaway/broadcast/CommandReceiver.java
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package org.adaway.broadcast;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.adaway.AdAwayApplication;
|
||||
import org.adaway.model.adblocking.AdBlockModel;
|
||||
import org.adaway.model.error.HostErrorException;
|
||||
import org.adaway.util.AppExecutors;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This broadcast receiver listens to commands from broadcast.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public class CommandReceiver extends BroadcastReceiver {
|
||||
/**
|
||||
* This action allows to send commands to the application. See {@link Command} for extra values.
|
||||
*/
|
||||
public static final String SEND_COMMAND_ACTION = "org.adaway.action.SEND_COMMAND";
|
||||
private static final AppExecutors EXECUTORS = AppExecutors.getInstance();
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (SEND_COMMAND_ACTION.equals(intent.getAction())) {
|
||||
AdBlockModel adBlockModel = ((AdAwayApplication) context.getApplicationContext()).getAdBlockModel();
|
||||
Command command = Command.readFromIntent(intent);
|
||||
Timber.i("CommandReceiver invoked with command %s.", command);
|
||||
EXECUTORS.diskIO().execute(() -> executeCommand(adBlockModel, command));
|
||||
}
|
||||
}
|
||||
|
||||
private void executeCommand(AdBlockModel adBlockModel, Command command) {
|
||||
try {
|
||||
switch (command) {
|
||||
case START:
|
||||
adBlockModel.apply();
|
||||
break;
|
||||
case STOP:
|
||||
adBlockModel.revert();
|
||||
break;
|
||||
case UNKNOWN:
|
||||
Timber.i("Failed to run an unsupported command.");
|
||||
break;
|
||||
}
|
||||
} catch (HostErrorException e) {
|
||||
Timber.w(e, "Failed to apply ad block command " + command + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
28
app/src/main/java/org/adaway/broadcast/UpdateReceiver.java
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package org.adaway.broadcast;
|
||||
|
||||
import static android.content.Intent.ACTION_MY_PACKAGE_REPLACED;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.adaway.AdAwayApplication;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This broadcast receiver is executed at application update.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public class UpdateReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (ACTION_MY_PACKAGE_REPLACED.equals(intent.getAction())) {
|
||||
AdAwayApplication application = (AdAwayApplication) context.getApplicationContext();
|
||||
String versionName = application.getUpdateModel().getVersionName();
|
||||
Timber.d("UpdateReceiver invoked");
|
||||
Timber.i("Application update to version %s", versionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
134
app/src/main/java/org/adaway/db/AppDatabase.java
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
package org.adaway.db;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.Database;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.RoomDatabase;
|
||||
import androidx.room.TypeConverters;
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
|
||||
import org.adaway.R;
|
||||
import org.adaway.db.converter.ListTypeConverter;
|
||||
import org.adaway.db.converter.ZonedDateTimeConverter;
|
||||
import org.adaway.db.dao.HostEntryDao;
|
||||
import org.adaway.db.dao.HostListItemDao;
|
||||
import org.adaway.db.dao.HostsSourceDao;
|
||||
import org.adaway.db.entity.HostListItem;
|
||||
import org.adaway.db.entity.HostsSource;
|
||||
import org.adaway.db.entity.HostEntry;
|
||||
import org.adaway.util.AppExecutors;
|
||||
|
||||
import static org.adaway.db.Migrations.MIGRATION_1_2;
|
||||
import static org.adaway.db.Migrations.MIGRATION_2_3;
|
||||
import static org.adaway.db.Migrations.MIGRATION_3_4;
|
||||
import static org.adaway.db.Migrations.MIGRATION_4_5;
|
||||
import static org.adaway.db.Migrations.MIGRATION_5_6;
|
||||
import static org.adaway.db.Migrations.MIGRATION_6_7;
|
||||
import static org.adaway.db.entity.HostsSource.USER_SOURCE_ID;
|
||||
import static org.adaway.db.entity.HostsSource.USER_SOURCE_URL;
|
||||
|
||||
/**
|
||||
* This class is the application database based on Room.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
@Database(entities = {HostsSource.class, HostListItem.class, HostEntry.class}, version = 7)
|
||||
@TypeConverters({ListTypeConverter.class, ZonedDateTimeConverter.class})
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
/**
|
||||
* The database singleton instance.
|
||||
*/
|
||||
private static volatile AppDatabase instance;
|
||||
|
||||
/**
|
||||
* Get the database instance.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @return The database instance.
|
||||
*/
|
||||
public static AppDatabase getInstance(Context context) {
|
||||
if (instance == null) {
|
||||
synchronized (AppDatabase.class) {
|
||||
if (instance == null) {
|
||||
instance = Room.databaseBuilder(
|
||||
context.getApplicationContext(),
|
||||
AppDatabase.class,
|
||||
"app.db"
|
||||
).addCallback(new Callback() {
|
||||
@Override
|
||||
public void onCreate(@NonNull SupportSQLiteDatabase db) {
|
||||
AppExecutors.getInstance().diskIO().execute(
|
||||
() -> AppDatabase.initialize(context, instance)
|
||||
);
|
||||
}
|
||||
}).addMigrations(
|
||||
MIGRATION_1_2,
|
||||
MIGRATION_2_3,
|
||||
MIGRATION_3_4,
|
||||
MIGRATION_4_5,
|
||||
MIGRATION_5_6,
|
||||
MIGRATION_6_7
|
||||
).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the database content.
|
||||
*/
|
||||
private static void initialize(Context context, AppDatabase database) {
|
||||
// Check if there is no hosts source
|
||||
HostsSourceDao hostsSourceDao = database.hostsSourceDao();
|
||||
if (!hostsSourceDao.getAll().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// User list
|
||||
HostsSource userSource = new HostsSource();
|
||||
userSource.setLabel(context.getString(R.string.hosts_user_source));
|
||||
userSource.setId(USER_SOURCE_ID);
|
||||
userSource.setUrl(USER_SOURCE_URL);
|
||||
userSource.setAllowEnabled(true);
|
||||
userSource.setRedirectEnabled(true);
|
||||
hostsSourceDao.insert(userSource);
|
||||
// AdAway official
|
||||
HostsSource source1 = new HostsSource();
|
||||
source1.setLabel(context.getString(R.string.hosts_adaway_source));
|
||||
source1.setUrl("https://adaway.org/hosts.txt");
|
||||
hostsSourceDao.insert(source1);
|
||||
// StevenBlack
|
||||
HostsSource source2 = new HostsSource();
|
||||
source2.setLabel(context.getString(R.string.hosts_stevenblack_source));
|
||||
source2.setUrl("https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts");
|
||||
hostsSourceDao.insert(source2);
|
||||
// Pete Lowe
|
||||
HostsSource source3 = new HostsSource();
|
||||
source3.setLabel(context.getString(R.string.hosts_peterlowe_source));
|
||||
source3.setUrl("https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext");
|
||||
hostsSourceDao.insert(source3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hosts source DAO.
|
||||
*
|
||||
* @return The hosts source DAO.
|
||||
*/
|
||||
public abstract HostsSourceDao hostsSourceDao();
|
||||
|
||||
/**
|
||||
* Get the hosts list item DAO.
|
||||
*
|
||||
* @return The hosts list item DAO.
|
||||
*/
|
||||
public abstract HostListItemDao hostsListItemDao();
|
||||
|
||||
/**
|
||||
* Get the hosts entry DAO.
|
||||
*
|
||||
* @return The hosts entry DAO.
|
||||
*/
|
||||
public abstract HostEntryDao hostEntryDao();
|
||||
}
|
||||
123
app/src/main/java/org/adaway/db/Migrations.java
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
package org.adaway.db;
|
||||
|
||||
import static org.adaway.db.entity.HostsSource.USER_SOURCE_ID;
|
||||
import static org.adaway.db.entity.HostsSource.USER_SOURCE_URL;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.migration.Migration;
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
|
||||
/**
|
||||
* This class declares database schema migrations.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
final class Migrations {
|
||||
/**
|
||||
* Private constructor of utility class.
|
||||
*/
|
||||
private Migrations() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The migration script from v1 to v2.
|
||||
*/
|
||||
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
// Add hosts sources id column and migrate data
|
||||
database.execSQL("CREATE TABLE `hosts_sources_new` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `last_modified_local` INTEGER, `last_modified_online` INTEGER)");
|
||||
database.execSQL("INSERT INTO `hosts_sources_new` (`id`, `url`, `enabled`) VALUES (" + USER_SOURCE_ID + ", '" + USER_SOURCE_URL + "', 1)");
|
||||
database.execSQL("INSERT INTO `hosts_sources_new` (`url`, `enabled`, `last_modified_local`, `last_modified_online`) SELECT `url`, `enabled`, `last_modified_local`, `last_modified_online` FROM `hosts_sources`");
|
||||
database.execSQL("DROP TABLE `hosts_sources`");
|
||||
database.execSQL("ALTER TABLE `hosts_sources_new` RENAME TO `hosts_sources`");
|
||||
// Add hosts list source id and migrate data
|
||||
database.execSQL("CREATE TABLE `hosts_lists_new` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `host` TEXT NOT NULL, `type` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `redirection` TEXT, `source_id` INTEGER NOT NULL, FOREIGN KEY(`source_id`) REFERENCES `hosts_sources`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )");
|
||||
database.execSQL("INSERT INTO `hosts_lists_new` (`host`, `type`, `enabled`, `redirection`, `source_id`) SELECT `host`, `type`, `enabled`, `redirection`, " + USER_SOURCE_ID + " FROM `hosts_lists`");
|
||||
database.execSQL("DROP TABLE `hosts_lists`");
|
||||
database.execSQL("ALTER TABLE `hosts_lists_new` RENAME TO `hosts_lists`");
|
||||
// Create index
|
||||
database.execSQL("CREATE UNIQUE INDEX `index_hosts_sources_url` ON `hosts_sources` (`url`)");
|
||||
database.execSQL("CREATE UNIQUE INDEX `index_hosts_lists_host` ON `hosts_lists` (`host`)");
|
||||
database.execSQL("CREATE INDEX `index_hosts_lists_source_id` ON `hosts_lists` (`source_id`)");
|
||||
}
|
||||
};
|
||||
/**
|
||||
* The migration script from v2 to v3.
|
||||
*/
|
||||
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE VIEW `host_entries` AS SELECT `host`, `type`, `redirection` FROM `hosts_lists` WHERE `enabled` = 1 AND ((`type` = 0 AND `host` NOT LIKE (SELECT `host` FROM `hosts_lists` WHERE `enabled` = 1 and `type` = 1)) OR `type` = 2) ORDER BY `host` ASC, `type` DESC, `redirection` ASC");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Migration script from v3 to v4.
|
||||
*/
|
||||
static final Migration MIGRATION_3_4 = new Migration(3, 4) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
// Remove unique constraint to hosts_lists.host column
|
||||
database.execSQL("DROP INDEX `index_hosts_lists_host`");
|
||||
database.execSQL("CREATE INDEX `index_hosts_lists_host` ON `hosts_lists` (`host`)");
|
||||
// Update host_entries view
|
||||
database.execSQL("DROP VIEW `host_entries`");
|
||||
database.execSQL("CREATE VIEW `host_entries` AS SELECT `host`, `type`, `redirection` FROM `hosts_lists` WHERE `enabled` = 1 AND ((`type` = 0 AND `host` NOT LIKE (SELECT `host` FROM `hosts_lists` WHERE `enabled` = 1 and `type` = 1)) OR `type` = 2) GROUP BY `host` ORDER BY `host` ASC, `type` DESC, `redirection` ASC");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Migration script from v4 to v5.
|
||||
*/
|
||||
static final Migration MIGRATION_4_5 = new Migration(4, 5) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
// Remove host_entries view
|
||||
database.execSQL("DROP VIEW `host_entries`");
|
||||
// Create new host_entries table
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS `host_entries` (`host` TEXT NOT NULL, `type` INTEGER NOT NULL, `redirection` TEXT, PRIMARY KEY(`host`))");
|
||||
database.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_host_entries_host` ON `host_entries` (`host`)");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Migration script from v5 to v6.
|
||||
*/
|
||||
static final Migration MIGRATION_5_6 = new Migration(5, 6) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
// Update hosts_sources table
|
||||
database.execSQL("ALTER TABLE `hosts_sources` ADD `label` TEXT NOT NULL DEFAULT \"\"");
|
||||
database.execSQL("ALTER TABLE `hosts_sources` ADD `allowEnabled` INTEGER NOT NULL DEFAULT 0");
|
||||
database.execSQL("ALTER TABLE `hosts_sources` ADD `redirectEnabled` INTEGER NOT NULL DEFAULT 0");
|
||||
database.execSQL("ALTER TABLE `hosts_sources` ADD `size` INTEGER NOT NULL DEFAULT 0");
|
||||
// Set default values to new source attributes
|
||||
database.execSQL("UPDATE `hosts_sources` SET `label` = `url`");
|
||||
// Update user hosts list
|
||||
database.execSQL("UPDATE `hosts_sources` SET `url` = \"content://org.adaway/user/hosts\", `allowEnabled` = 1, `redirectEnabled` = 1 WHERE `url` = \"file://app/user/hosts\"");
|
||||
// Update default hosts source label
|
||||
database.execSQL("UPDATE `hosts_sources` SET `label` = \"AdAway official hosts\" WHERE `url` = \"https://adaway.org/hosts.txt\"");
|
||||
database.execSQL("UPDATE `hosts_sources` SET `label` = \"StevenBlack Unified hosts\" WHERE `url` = \"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts\"");
|
||||
database.execSQL("UPDATE `hosts_sources` SET `label` = \"Pete Lowe blocklist hosts\" WHERE `url` = \"https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext\"");
|
||||
// Reset local date to rebuild cache
|
||||
database.execSQL("UPDATE `hosts_sources` SET `last_modified_local` = NULL");
|
||||
// Update hosts source date format
|
||||
database.execSQL("UPDATE `hosts_sources` SET `last_modified_online` = `last_modified_online` / 1000");
|
||||
// Clear previous file type hosts sources
|
||||
database.execSQL("DELETE FROM `hosts_sources` WHERE `url` LIKE \"file://%\"");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Migration script from v6 to v7.
|
||||
*/
|
||||
static final Migration MIGRATION_6_7 = new Migration(6, 7) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
// Update hosts_sources table
|
||||
database.execSQL("ALTER TABLE `hosts_sources` ADD `entityTag` TEXT DEFAULT NULL");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package org.adaway.db.converter;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
import org.adaway.db.entity.ListType;
|
||||
|
||||
/**
|
||||
* This class is a type converter for Room to support {@link ListType} type.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public final class ListTypeConverter {
|
||||
private ListTypeConverter() {
|
||||
// Prevent instantiation
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static ListType fromValue(Integer value) {
|
||||
return value == null ? null : ListType.fromValue(value);
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static Integer typeToValue(ListType type) {
|
||||
return type == null ? null : type.getValue();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package org.adaway.db.converter;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import static java.time.ZoneOffset.UTC;
|
||||
|
||||
/**
|
||||
* This class is a type converter for Room to support {@link java.time.ZonedDateTime} type.
|
||||
* It is stored as a Unix epoc timestamp.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public final class ZonedDateTimeConverter {
|
||||
private ZonedDateTimeConverter() {
|
||||
// Prevent instantiation
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static ZonedDateTime fromTimestamp(Long value) {
|
||||
return value == null ? null : ZonedDateTime.of(LocalDateTime.ofEpochSecond(value, 0, UTC), UTC);
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static Long toTimestamp(ZonedDateTime zonedDateTime) {
|
||||
return zonedDateTime == null ? null : zonedDateTime.toEpochSecond();
|
||||
}
|
||||
}
|
||||
78
app/src/main/java/org/adaway/db/dao/HostEntryDao.java
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package org.adaway.db.dao;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.Query;
|
||||
|
||||
import org.adaway.db.entity.HostEntry;
|
||||
import org.adaway.db.entity.HostListItem;
|
||||
import org.adaway.db.entity.ListType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static androidx.room.OnConflictStrategy.REPLACE;
|
||||
import static org.adaway.db.entity.ListType.REDIRECTED;
|
||||
|
||||
/**
|
||||
* This interface is the DAO for {@link HostEntry} records.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
@Dao
|
||||
public interface HostEntryDao {
|
||||
Pattern ANY_CHAR_PATTERN = Pattern.compile("\\*");
|
||||
Pattern A_CHAR_PATTERN = Pattern.compile("\\?");
|
||||
|
||||
@Query("DELETE FROM `host_entries`")
|
||||
void clear();
|
||||
|
||||
@Query("INSERT INTO `host_entries` SELECT DISTINCT `host`, `type`, `redirection` FROM `hosts_lists` WHERE `type` = 0 AND `enabled` = 1")
|
||||
void importBlocked();
|
||||
|
||||
@Query("SELECT host FROM hosts_lists WHERE type = 1 AND enabled = 1")
|
||||
List<String> getEnabledAllowedHosts();
|
||||
|
||||
@Query("DELETE FROM `host_entries` WHERE `host` LIKE :hostPattern")
|
||||
void allowHost(String hostPattern);
|
||||
|
||||
@Query("SELECT * FROM hosts_lists WHERE type = 2 AND enabled = 1 ORDER BY host ASC, source_id DESC")
|
||||
List<HostListItem> getEnabledRedirectedHosts();
|
||||
|
||||
@Insert(onConflict = REPLACE)
|
||||
void redirectHost(HostEntry redirection);
|
||||
|
||||
/**
|
||||
* Synchronize the host entries based on the current hosts lists table records.
|
||||
*/
|
||||
default void sync() {
|
||||
clear();
|
||||
importBlocked();
|
||||
for (String allowedHost : getEnabledAllowedHosts()) {
|
||||
allowedHost = ANY_CHAR_PATTERN.matcher(allowedHost).replaceAll("%");
|
||||
allowedHost = A_CHAR_PATTERN.matcher(allowedHost).replaceAll("_");
|
||||
allowHost(allowedHost);
|
||||
}
|
||||
for (HostListItem redirectedHost : getEnabledRedirectedHosts()) {
|
||||
HostEntry entry = new HostEntry();
|
||||
entry.setHost(redirectedHost.getHost());
|
||||
entry.setType(REDIRECTED);
|
||||
entry.setRedirection(redirectedHost.getRedirection());
|
||||
redirectHost(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@Query("SELECT * FROM `host_entries` ORDER BY `host`")
|
||||
List<HostEntry> getAll();
|
||||
|
||||
@Query("SELECT `type` FROM `host_entries` WHERE `host` == :host LIMIT 1")
|
||||
ListType getTypeOfHost(String host);
|
||||
|
||||
@Query("SELECT IFNULL((SELECT `type` FROM `host_entries` WHERE `host` == :host LIMIT 1), 1)")
|
||||
ListType getTypeForHost(String host);
|
||||
|
||||
@Nullable
|
||||
@Query("SELECT * FROM `host_entries` WHERE `host` == :host LIMIT 1")
|
||||
HostEntry getEntry(String host);
|
||||
}
|
||||
63
app/src/main/java/org/adaway/db/dao/HostListItemDao.java
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package org.adaway.db.dao;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.paging.PagingSource;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Update;
|
||||
|
||||
import org.adaway.db.entity.HostListItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static androidx.room.OnConflictStrategy.REPLACE;
|
||||
|
||||
/**
|
||||
* This interface is the DAO for {@link HostListItem} entities.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
@Dao
|
||||
public interface HostListItemDao {
|
||||
@Insert(onConflict = REPLACE)
|
||||
void insert(HostListItem... item);
|
||||
|
||||
@Insert(onConflict = REPLACE)
|
||||
void insert(List<HostListItem> items);
|
||||
|
||||
@Update
|
||||
void update(HostListItem item);
|
||||
|
||||
@Delete
|
||||
void delete(HostListItem item);
|
||||
|
||||
@Query("DELETE FROM hosts_lists WHERE source_id = 1 AND host = :host")
|
||||
void deleteUserFromHost(String host);
|
||||
|
||||
@Query("SELECT * FROM hosts_lists WHERE type = :type AND host LIKE :query AND ((:includeSources == 0 AND source_id == 1) || (:includeSources == 1)) GROUP BY host ORDER BY host ASC")
|
||||
PagingSource<Integer, HostListItem> loadList(int type, boolean includeSources, String query);
|
||||
|
||||
@Query("SELECT * FROM hosts_lists ORDER BY host ASC")
|
||||
List<HostListItem> getAll();
|
||||
|
||||
@Query("SELECT * FROM hosts_lists WHERE source_id = 1")
|
||||
List<HostListItem> getUserList();
|
||||
|
||||
@Query("SELECT id FROM hosts_lists WHERE host = :host AND source_id = 1 LIMIT 1")
|
||||
Optional<Integer> getHostId(String host);
|
||||
|
||||
@Query("SELECT COUNT(DISTINCT host) FROM hosts_lists WHERE type = 0 AND enabled = 1")
|
||||
LiveData<Integer> getBlockedHostCount();
|
||||
|
||||
@Query("SELECT COUNT(DISTINCT host) FROM hosts_lists WHERE type = 1 AND enabled = 1")
|
||||
LiveData<Integer> getAllowedHostCount();
|
||||
|
||||
@Query("SELECT COUNT(DISTINCT host) FROM hosts_lists WHERE type = 2 AND enabled = 1")
|
||||
LiveData<Integer> getRedirectHostCount();
|
||||
|
||||
@Query("DELETE FROM hosts_lists WHERE source_id = :sourceId")
|
||||
void clearSourceHosts(int sourceId);
|
||||
}
|
||||
80
app/src/main/java/org/adaway/db/dao/HostsSourceDao.java
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package org.adaway.db.dao;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Update;
|
||||
|
||||
import org.adaway.db.entity.HostsSource;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static androidx.room.OnConflictStrategy.IGNORE;
|
||||
|
||||
/**
|
||||
* This interface is the DAO for {@link HostsSource} entities.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
@Dao
|
||||
public interface HostsSourceDao {
|
||||
@Insert(onConflict = IGNORE)
|
||||
void insert(HostsSource source);
|
||||
|
||||
@Update
|
||||
void update(HostsSource source);
|
||||
|
||||
@Delete
|
||||
void delete(HostsSource source);
|
||||
|
||||
@Query("SELECT * FROM hosts_sources WHERE enabled = 1 AND id != 1 ORDER BY url ASC")
|
||||
List<HostsSource> getEnabled();
|
||||
|
||||
default void toggleEnabled(HostsSource source) {
|
||||
int id = source.getId();
|
||||
boolean enabled = !source.isEnabled();
|
||||
source.setEnabled(enabled);
|
||||
setSourceEnabled(id, enabled);
|
||||
setSourceItemsEnabled(id, enabled);
|
||||
}
|
||||
|
||||
@Query("UPDATE hosts_sources SET enabled = :enabled WHERE id =:id")
|
||||
void setSourceEnabled(int id, boolean enabled);
|
||||
|
||||
@Query("UPDATE hosts_lists SET enabled = :enabled WHERE source_id =:id")
|
||||
void setSourceItemsEnabled(int id, boolean enabled);
|
||||
|
||||
@Query("SELECT * FROM hosts_sources WHERE id = :id")
|
||||
Optional<HostsSource> getById(int id);
|
||||
|
||||
@Query("SELECT * FROM hosts_sources WHERE id != 1 ORDER BY label ASC")
|
||||
List<HostsSource> getAll();
|
||||
|
||||
@Query("SELECT * FROM hosts_sources WHERE id != 1 ORDER BY label ASC")
|
||||
LiveData<List<HostsSource>> loadAll();
|
||||
|
||||
@Query("UPDATE hosts_sources SET last_modified_online = :dateTime WHERE id = :id")
|
||||
void updateOnlineModificationDate(int id, ZonedDateTime dateTime);
|
||||
|
||||
@Query("UPDATE hosts_sources SET last_modified_local = :localModificationDate, last_modified_online = :onlineModificationDate WHERE id = :id")
|
||||
void updateModificationDates(int id, ZonedDateTime localModificationDate, ZonedDateTime onlineModificationDate);
|
||||
|
||||
@Query("UPDATE hosts_sources SET entityTag = :entityTag WHERE id = :id")
|
||||
void updateEntityTag(int id, String entityTag);
|
||||
|
||||
@Query("UPDATE hosts_sources SET size = (SELECT count(id) FROM hosts_lists WHERE source_id = :id) WHERE id = :id")
|
||||
void updateSize(int id);
|
||||
|
||||
@Query("SELECT count(id) FROM hosts_sources WHERE enabled = 1 AND last_modified_online > last_modified_local")
|
||||
LiveData<Integer> countOutdated();
|
||||
|
||||
@Query("SELECT count(id) FROM hosts_sources WHERE enabled = 1 AND last_modified_online <= last_modified_local")
|
||||
LiveData<Integer> countUpToDate();
|
||||
|
||||
@Query("UPDATE hosts_sources SET last_modified_local = NULL, last_modified_online = NULL, entityTag = NULL, size = 0 WHERE id = :id")
|
||||
void clearProperties(int id);
|
||||
}
|
||||
50
app/src/main/java/org/adaway/db/entity/HostEntry.java
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package org.adaway.db.entity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
/**
|
||||
* This entity represents an entry of the built hosts file.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
@Entity(
|
||||
tableName = "host_entries",
|
||||
indices = {@Index(value = "host", unique = true)}
|
||||
)
|
||||
public class HostEntry {
|
||||
@PrimaryKey
|
||||
@NonNull
|
||||
private String host;
|
||||
@NonNull
|
||||
private ListType type;
|
||||
private String redirection;
|
||||
|
||||
@NonNull
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(@NonNull String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ListType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(@NonNull ListType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getRedirection() {
|
||||
return redirection;
|
||||
}
|
||||
|
||||
public void setRedirection(String redirection) {
|
||||
this.redirection = redirection;
|
||||
}
|
||||
}
|
||||
121
app/src/main/java/org/adaway/db/entity/HostListItem.java
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package org.adaway.db.entity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.ForeignKey;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static androidx.room.ForeignKey.CASCADE;
|
||||
|
||||
/**
|
||||
* This entity represents a black, white or redirect list item.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
@Entity(
|
||||
tableName = "hosts_lists",
|
||||
indices = {
|
||||
@Index(value = "host"),
|
||||
@Index(value = "source_id")
|
||||
},
|
||||
foreignKeys = @ForeignKey(
|
||||
entity = HostsSource.class,
|
||||
parentColumns = "id",
|
||||
childColumns = "source_id",
|
||||
onUpdate = CASCADE,
|
||||
onDelete = CASCADE
|
||||
)
|
||||
)
|
||||
public class HostListItem {
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
private int id;
|
||||
@NonNull
|
||||
private String host;
|
||||
@NonNull
|
||||
private ListType type;
|
||||
private boolean enabled;
|
||||
private String redirection;
|
||||
@ColumnInfo(name = "source_id")
|
||||
private int sourceId;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(@NonNull String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ListType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(@NonNull ListType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getRedirection() {
|
||||
return redirection;
|
||||
}
|
||||
|
||||
public void setRedirection(String redirection) {
|
||||
this.redirection = redirection;
|
||||
}
|
||||
|
||||
public int getSourceId() {
|
||||
return sourceId;
|
||||
}
|
||||
|
||||
public void setSourceId(int sourceId) {
|
||||
this.sourceId = sourceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
HostListItem item = (HostListItem) o;
|
||||
|
||||
if (id != item.id) return false;
|
||||
if (enabled != item.enabled) return false;
|
||||
if (sourceId != item.sourceId) return false;
|
||||
if (!host.equals(item.host)) return false;
|
||||
if (type != item.type) return false;
|
||||
return Objects.equals(redirection, item.redirection);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = id;
|
||||
result = 31 * result + host.hashCode();
|
||||
result = 31 * result + type.hashCode();
|
||||
result = 31 * result + (enabled ? 1 : 0);
|
||||
result = 31 * result + (redirection != null ? redirection.hashCode() : 0);
|
||||
result = 31 * result + sourceId;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
187
app/src/main/java/org/adaway/db/entity/HostsSource.java
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
package org.adaway.db.entity;
|
||||
|
||||
import android.webkit.URLUtil;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.adaway.db.entity.SourceType.FILE;
|
||||
import static org.adaway.db.entity.SourceType.UNSUPPORTED;
|
||||
import static org.adaway.db.entity.SourceType.URL;
|
||||
|
||||
/**
|
||||
* This entity represents a source to get hosts list.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
@Entity(
|
||||
tableName = "hosts_sources",
|
||||
indices = {@Index(value = "url", unique = true)}
|
||||
)
|
||||
public class HostsSource {
|
||||
/**
|
||||
* The user source ID.
|
||||
*/
|
||||
public static final int USER_SOURCE_ID = 1;
|
||||
/**
|
||||
* The user source URL.
|
||||
*/
|
||||
public static final String USER_SOURCE_URL = "content://org.adaway/user/hosts";
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
private int id;
|
||||
@NonNull
|
||||
private String label;
|
||||
@NonNull
|
||||
private String url;
|
||||
private boolean enabled = true;
|
||||
private boolean allowEnabled = false;
|
||||
private boolean redirectEnabled = false;
|
||||
@ColumnInfo(name = "last_modified_local")
|
||||
private ZonedDateTime localModificationDate;
|
||||
@ColumnInfo(name = "last_modified_online")
|
||||
private ZonedDateTime onlineModificationDate;
|
||||
/**
|
||||
* The HTTP ETag (strong from, may be <code>null</code>).
|
||||
*/
|
||||
private String entityTag;
|
||||
/**
|
||||
* The number of hosts list items (<code>0</code> until synced).
|
||||
*/
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* Check whether an URL is valid for as host source.<br>
|
||||
* A valid URL is a HTTPS URL or file URL.
|
||||
*
|
||||
* @param url The URL to check.
|
||||
* @return {@code true} if the URL is valid, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isValidUrl(String url) {
|
||||
return (!"https://".equals(url) && URLUtil.isHttpsUrl(url)) || URLUtil.isContentUrl(url);
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(@NonNull String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(@NonNull String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public SourceType getType() {
|
||||
if (this.url.startsWith("https://")) {
|
||||
return URL;
|
||||
} else if (this.url.startsWith("content://")) {
|
||||
return FILE;
|
||||
} else {
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isAllowEnabled() {
|
||||
return allowEnabled;
|
||||
}
|
||||
|
||||
public void setAllowEnabled(boolean allowEnabled) {
|
||||
this.allowEnabled = allowEnabled;
|
||||
}
|
||||
|
||||
public boolean isRedirectEnabled() {
|
||||
return redirectEnabled;
|
||||
}
|
||||
|
||||
public void setRedirectEnabled(boolean redirectEnabled) {
|
||||
this.redirectEnabled = redirectEnabled;
|
||||
}
|
||||
|
||||
public ZonedDateTime getLocalModificationDate() {
|
||||
return localModificationDate;
|
||||
}
|
||||
|
||||
public void setLocalModificationDate(ZonedDateTime localModificationDate) {
|
||||
this.localModificationDate = localModificationDate;
|
||||
}
|
||||
|
||||
public ZonedDateTime getOnlineModificationDate() {
|
||||
return onlineModificationDate;
|
||||
}
|
||||
|
||||
public void setOnlineModificationDate(ZonedDateTime lastOnlineModification) {
|
||||
this.onlineModificationDate = lastOnlineModification;
|
||||
}
|
||||
|
||||
public String getEntityTag() {
|
||||
return this.entityTag;
|
||||
}
|
||||
|
||||
public void setEntityTag(String entityTag) {
|
||||
this.entityTag = entityTag;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
HostsSource that = (HostsSource) o;
|
||||
|
||||
if (id != that.id) return false;
|
||||
if (enabled != that.enabled) return false;
|
||||
if (!url.equals(that.url)) return false;
|
||||
if (!Objects.equals(localModificationDate, that.localModificationDate))
|
||||
return false;
|
||||
return Objects.equals(onlineModificationDate, that.onlineModificationDate);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = id;
|
||||
result = 31 * result + url.hashCode();
|
||||
result = 31 * result + (enabled ? 1 : 0);
|
||||
result = 31 * result + (localModificationDate != null ? localModificationDate.hashCode() : 0);
|
||||
result = 31 * result + (onlineModificationDate != null ? onlineModificationDate.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
31
app/src/main/java/org/adaway/db/entity/ListType.java
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package org.adaway.db.entity;
|
||||
|
||||
/**
|
||||
* This enumerate specifies the type of {@link HostListItem}.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public enum ListType {
|
||||
BLOCKED(0),
|
||||
ALLOWED(1),
|
||||
REDIRECTED(2);
|
||||
|
||||
private final int value;
|
||||
|
||||
ListType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ListType fromValue(int value) {
|
||||
for (ListType listType : ListType.values()) {
|
||||
if (listType.value == value) {
|
||||
return listType;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid value for list type: " + value);
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
21
app/src/main/java/org/adaway/db/entity/SourceType.java
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package org.adaway.db.entity;
|
||||
|
||||
/**
|
||||
* This enumerate specifies the type of {@link HostsSource}.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public enum SourceType {
|
||||
/**
|
||||
* The URL type represents online source to download from URL.
|
||||
*/
|
||||
URL,
|
||||
/**
|
||||
* The FILE type represents file stored source to retrieve using SAF API.
|
||||
*/
|
||||
FILE,
|
||||
/**
|
||||
* The UNSUPPORTED type represents unhandled source type.
|
||||
*/
|
||||
UNSUPPORTED
|
||||
}
|
||||
167
app/src/main/java/org/adaway/helper/NotificationHelper.java
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
package org.adaway.helper;
|
||||
|
||||
import static android.app.PendingIntent.FLAG_IMMUTABLE;
|
||||
import static android.app.PendingIntent.getActivity;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static androidx.core.app.NotificationCompat.PRIORITY_LOW;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import org.adaway.R;
|
||||
import org.adaway.ui.home.HomeActivity;
|
||||
import org.adaway.ui.update.UpdateActivity;
|
||||
|
||||
/**
|
||||
* This class is an helper class to deals with notifications.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public final class NotificationHelper {
|
||||
/**
|
||||
* The notification channel for updates.
|
||||
*/
|
||||
public static final String UPDATE_NOTIFICATION_CHANNEL = "UpdateChannel";
|
||||
/**
|
||||
* The notification channel for VPN service.
|
||||
*/
|
||||
public static final String VPN_SERVICE_NOTIFICATION_CHANNEL = "VpnServiceChannel";
|
||||
/**
|
||||
* The update hosts notification identifier.
|
||||
*/
|
||||
private static final int UPDATE_HOSTS_NOTIFICATION_ID = 10;
|
||||
/**
|
||||
* The update application notification identifier.
|
||||
*/
|
||||
private static final int UPDATE_APP_NOTIFICATION_ID = 11;
|
||||
/**
|
||||
* The VPN running service notification identifier.
|
||||
*/
|
||||
public static final int VPN_RUNNING_SERVICE_NOTIFICATION_ID = 20;
|
||||
/**
|
||||
* The VPN resume service notification identifier.
|
||||
*/
|
||||
public static final int VPN_RESUME_SERVICE_NOTIFICATION_ID = 21;
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
*/
|
||||
private NotificationHelper() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the application notification channel.
|
||||
*
|
||||
* @param context The application context.
|
||||
*/
|
||||
public static void createNotificationChannels(@NonNull Context context) {
|
||||
// Create update notification channel
|
||||
NotificationChannel updateChannel = new NotificationChannel(
|
||||
UPDATE_NOTIFICATION_CHANNEL,
|
||||
context.getString(R.string.notification_update_channel_name),
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
updateChannel.setDescription(context.getString(R.string.notification_update_channel_description));
|
||||
// Create VPN service notification channel
|
||||
NotificationChannel vpnServiceChannel = new NotificationChannel(
|
||||
VPN_SERVICE_NOTIFICATION_CHANNEL,
|
||||
context.getString(R.string.notification_vpn_channel_name),
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
updateChannel.setDescription(context.getString(R.string.notification_vpn_channel_description));
|
||||
// Register the channels with the system; you can't change the importance or other notification behaviors after this
|
||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||
if (notificationManager != null) {
|
||||
notificationManager.createNotificationChannel(updateChannel);
|
||||
notificationManager.createNotificationChannel(vpnServiceChannel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the notification about new hosts update available.
|
||||
*
|
||||
* @param context The application context.
|
||||
*/
|
||||
public static void showUpdateHostsNotification(@NonNull Context context) {
|
||||
// Get notification manager
|
||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||
if (notificationManager == null || !notificationManager.areNotificationsEnabled()) {
|
||||
return;
|
||||
}
|
||||
// Build notification
|
||||
int color = context.getColor(R.color.notification);
|
||||
String title = context.getString(R.string.notification_update_host_available_title);
|
||||
String text = context.getString(R.string.notification_update_host_available_text);
|
||||
Intent intent = new Intent(context, HomeActivity.class);
|
||||
intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
|
||||
PendingIntent pendingIntent = getActivity(context, 0, intent, FLAG_IMMUTABLE);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, UPDATE_NOTIFICATION_CHANNEL)
|
||||
.setSmallIcon(R.drawable.logo)
|
||||
.setColorized(true)
|
||||
.setColor(color)
|
||||
.setShowWhen(false)
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setPriority(PRIORITY_LOW)
|
||||
.setAutoCancel(true);
|
||||
// Notify the built notification
|
||||
notificationManager.notify(UPDATE_HOSTS_NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the notification about new application update available.
|
||||
*
|
||||
* @param context The application context.
|
||||
*/
|
||||
public static void showUpdateApplicationNotification(@NonNull Context context) {
|
||||
// Get notification manager
|
||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||
if (notificationManager == null || !notificationManager.areNotificationsEnabled()) {
|
||||
return;
|
||||
}
|
||||
// Build notification
|
||||
int color = context.getColor(R.color.notification);
|
||||
String title = context.getString(R.string.notification_update_app_available_title);
|
||||
String text = context.getString(R.string.notification_update_app_available_text);
|
||||
Intent intent = new Intent(context, UpdateActivity.class);
|
||||
intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
|
||||
PendingIntent pendingIntent = getActivity(context, 0, intent, FLAG_IMMUTABLE);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, UPDATE_NOTIFICATION_CHANNEL)
|
||||
.setSmallIcon(R.drawable.logo)
|
||||
.setColorized(true)
|
||||
.setColor(color)
|
||||
.setShowWhen(false)
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setPriority(PRIORITY_LOW)
|
||||
.setAutoCancel(true);
|
||||
// Notify the built notification
|
||||
notificationManager.notify(UPDATE_HOSTS_NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the notification about new hosts update available.
|
||||
*
|
||||
* @param context The application context.
|
||||
*/
|
||||
public static void clearUpdateNotifications(@NonNull Context context) {
|
||||
// Get notification manager
|
||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||
if (notificationManager == null) {
|
||||
return;
|
||||
}
|
||||
// Cancel the notification
|
||||
notificationManager.cancel(UPDATE_HOSTS_NOTIFICATION_ID);
|
||||
notificationManager.cancel(UPDATE_APP_NOTIFICATION_ID);
|
||||
}
|
||||
}
|
||||
361
app/src/main/java/org/adaway/helper/PreferenceHelper.java
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* Copyright (C) 2011-2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This file is part of AdAway.
|
||||
*
|
||||
* AdAway is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AdAway is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AdAway. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.adaway.helper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
import org.adaway.R;
|
||||
import org.adaway.model.adblocking.AdBlockMethod;
|
||||
import org.adaway.util.Constants;
|
||||
import org.adaway.vpn.VpnStatus;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public final class PreferenceHelper {
|
||||
private PreferenceHelper() {
|
||||
|
||||
}
|
||||
|
||||
public static int getDarkThemeMode(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
String pref = prefs.getString(
|
||||
context.getString(R.string.pref_dark_theme_mode_key),
|
||||
context.getResources().getString(R.string.pref_dark_theme_mode_def)
|
||||
);
|
||||
switch (pref) {
|
||||
case "MODE_NIGHT_NO":
|
||||
return AppCompatDelegate.MODE_NIGHT_NO;
|
||||
case "MODE_NIGHT_YES":
|
||||
return AppCompatDelegate.MODE_NIGHT_YES;
|
||||
default:
|
||||
return AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getUpdateCheck(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_update_check_key),
|
||||
context.getResources().getBoolean(R.bool.pref_update_check_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getNeverReboot(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_never_reboot_key),
|
||||
context.getResources().getBoolean(R.bool.pref_never_reboot_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static void setNeverReboot(Context context, boolean value) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putBoolean(context.getString(R.string.pref_never_reboot_key), value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static boolean getEnableIpv6(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_enable_ipv6_key),
|
||||
context.getResources().getBoolean(R.bool.pref_enable_ipv6_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getUpdateCheckAppStartup(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_update_check_app_startup_key),
|
||||
context.getResources().getBoolean(R.bool.pref_update_check_app_startup_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getUpdateCheckAppDaily(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_update_check_app_daily_key),
|
||||
context.getResources().getBoolean(R.bool.pref_update_check_app_daily_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getIncludeBetaReleases(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_update_include_beta_releases_key),
|
||||
context.getResources().getBoolean(R.bool.pref_update_include_beta_releases_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getUpdateCheckHostsDaily(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_update_check_hosts_daily_key),
|
||||
context.getResources().getBoolean(R.bool.pref_update_check_hosts_daily_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getAutomaticUpdateDaily(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_automatic_update_daily_key),
|
||||
context.getResources().getBoolean(R.bool.pref_automatic_update_daily_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getUpdateOnlyOnWifi(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_update_only_on_wifi_key),
|
||||
context.getResources().getBoolean(R.bool.pref_update_only_on_wifi_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static String getRedirectionIpv4(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getString(
|
||||
context.getString(R.string.pref_redirection_ipv4_key),
|
||||
context.getString(R.string.pref_redirection_ipv4_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static String getRedirectionIpv6(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getString(
|
||||
context.getString(R.string.pref_redirection_ipv6_key),
|
||||
context.getString(R.string.pref_redirection_ipv6_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getWebServerEnabled(Context context) {
|
||||
SharedPreferences prefs = context.getApplicationContext().getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_webserver_enabled_key),
|
||||
context.getResources().getBoolean(R.bool.pref_webserver_enabled_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getWebServerIcon(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_webserver_icon_key),
|
||||
context.getResources().getBoolean(R.bool.pref_webserver_icon_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static AdBlockMethod getAdBlockMethod(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return AdBlockMethod.fromCode(prefs.getInt(
|
||||
context.getString(R.string.pref_ad_block_method_key),
|
||||
context.getResources().getInteger(R.integer.pref_ad_block_method_key_def)
|
||||
));
|
||||
}
|
||||
|
||||
public static void setAbBlockMethod(Context context, AdBlockMethod method) {
|
||||
SharedPreferences prefs = context.getApplicationContext().getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putInt(context.getString(R.string.pref_ad_block_method_key), method.toCode());
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static VpnStatus getVpnServiceStatus(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return VpnStatus.fromCode(prefs.getInt(
|
||||
context.getString(R.string.pref_vpn_service_status_key),
|
||||
context.getResources().getInteger(R.integer.pref_vpn_service_status_def)
|
||||
));
|
||||
}
|
||||
|
||||
public static void setVpnServiceStatus(Context context, VpnStatus status) {
|
||||
SharedPreferences prefs = context.getApplicationContext().getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putInt(context.getString(R.string.pref_vpn_service_status_key), status.toCode());
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static boolean getVpnServiceOnBoot(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_vpn_service_on_boot_key),
|
||||
context.getResources().getBoolean(R.bool.pref_vpn_service_on_boot_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getVpnWatchdogEnabled(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_vpn_watchdog_enabled_key),
|
||||
context.getResources().getBoolean(R.bool.pref_vpn_watchdog_enabled_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getDebugEnabled(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_enable_debug_key),
|
||||
context.getResources().getBoolean(R.bool.pref_enable_debug_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getTelemetryEnabled(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_enable_telemetry_key),
|
||||
context.getResources().getBoolean(R.bool.pref_enable_telemetry_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static void setTelemetryEnabled(Context context, boolean enabled) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putBoolean(context.getString(R.string.pref_enable_telemetry_key), enabled);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static boolean getDisplayTelemetryConsent(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getBoolean(
|
||||
context.getString(R.string.pref_display_telemetry_consent_key),
|
||||
context.getResources().getBoolean(R.bool.pref_display_telemetry_consent_def)
|
||||
);
|
||||
}
|
||||
|
||||
public static void setDisplayTelemetryConsent(Context context, boolean displayTelemetryConsent) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putBoolean(context.getString(R.string.pref_display_telemetry_consent_key), displayTelemetryConsent);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static String getVpnExcludedSystemApps(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getString(
|
||||
context.getString(R.string.pref_vpn_excluded_system_apps_key),
|
||||
context.getString(R.string.pref_vpn_excluded_system_apps_default)
|
||||
);
|
||||
}
|
||||
|
||||
public static Set<String> getVpnExcludedApps(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
return prefs.getStringSet(
|
||||
context.getString(R.string.pref_vpn_excluded_user_apps_key),
|
||||
Collections.emptySet()
|
||||
);
|
||||
}
|
||||
|
||||
public static void setVpnExcludedApps(Context context, Set<String> excludedApplicationPackageNames) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.PREFS_NAME,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putStringSet(context.getString(R.string.pref_vpn_excluded_user_apps_key), excludedApplicationPackageNames);
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
29
app/src/main/java/org/adaway/helper/ThemeHelper.java
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package org.adaway.helper;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
/**
|
||||
* This class is a helper to apply user selected theme on the application activity.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public final class ThemeHelper {
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
*/
|
||||
private ThemeHelper() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the user selected theme.
|
||||
*
|
||||
* @param context The context to apply theme.
|
||||
*/
|
||||
public static void applyTheme(Context context) {
|
||||
AppCompatDelegate.setDefaultNightMode(PreferenceHelper.getDarkThemeMode(context));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package org.adaway.model.adblocking;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This enum represents the ad blocking methods.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public enum AdBlockMethod {
|
||||
/**
|
||||
* Not defined ad block method.
|
||||
*/
|
||||
UNDEFINED(0),
|
||||
/**
|
||||
* The system hosts file ad block method.
|
||||
*/
|
||||
ROOT(1),
|
||||
/**
|
||||
* The VPN based ad block method.
|
||||
*/
|
||||
VPN(2);
|
||||
|
||||
private int code;
|
||||
|
||||
AdBlockMethod(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static AdBlockMethod fromCode(int code) {
|
||||
return Arrays.stream(AdBlockMethod.values())
|
||||
.filter(method -> method.code == code)
|
||||
.findAny()
|
||||
.orElse(UNDEFINED);
|
||||
}
|
||||
|
||||
public int toCode() {
|
||||
return this.code;
|
||||
}
|
||||
}
|
||||
140
app/src/main/java/org/adaway/model/adblocking/AdBlockModel.java
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package org.adaway.model.adblocking;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import org.adaway.model.error.HostErrorException;
|
||||
import org.adaway.model.root.RootModel;
|
||||
import org.adaway.model.vpn.VpnModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This class is the base model for all ad block model.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public abstract class AdBlockModel {
|
||||
/**
|
||||
* The application context.
|
||||
*/
|
||||
protected final Context context;
|
||||
/**
|
||||
* The hosts installation status:
|
||||
* <ul>
|
||||
* <li>{@code null} if not defined,</li>
|
||||
* <li>{@code true} if hosts list is installed,</li>
|
||||
* <li>{@code false} if default hosts file.</li>
|
||||
* </ul>
|
||||
*/
|
||||
protected final MutableLiveData<Boolean> applied;
|
||||
/**
|
||||
* The model state.
|
||||
*/
|
||||
private final MutableLiveData<String> state;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context The application context.
|
||||
*/
|
||||
protected AdBlockModel(Context context) {
|
||||
this.context = context;
|
||||
this.state = new MutableLiveData<>();
|
||||
this.applied = new MutableLiveData<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate ad block model.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param method The ad block method to get model.
|
||||
* @return The instantiated model.
|
||||
*/
|
||||
public static AdBlockModel build(Context context, AdBlockMethod method) {
|
||||
switch (method) {
|
||||
case ROOT:
|
||||
return new RootModel(context);
|
||||
case VPN:
|
||||
return new VpnModel(context);
|
||||
default:
|
||||
return new UndefinedBlockModel(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ad block method.
|
||||
*
|
||||
* @return The ad block method of this model.
|
||||
*/
|
||||
public abstract AdBlockMethod getMethod();
|
||||
|
||||
/**
|
||||
* Checks if hosts list is applied.
|
||||
*
|
||||
* @return {@code true} if applied, {@code false} if default.
|
||||
*/
|
||||
public LiveData<Boolean> isApplied() {
|
||||
return this.applied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply hosts list.
|
||||
*
|
||||
* @throws HostErrorException If the model configuration could not be applied.
|
||||
*/
|
||||
public abstract void apply() throws HostErrorException;
|
||||
|
||||
/**
|
||||
* Revert the hosts list to the default one.
|
||||
*
|
||||
* @throws HostErrorException If the model configuration could not be revert.
|
||||
*/
|
||||
public abstract void revert() throws HostErrorException;
|
||||
|
||||
/**
|
||||
* Get the model state.
|
||||
*
|
||||
* @return The model state.
|
||||
*/
|
||||
public LiveData<String> getState() {
|
||||
return this.state;
|
||||
}
|
||||
|
||||
protected void setState(@StringRes int stateResId, Object... details) {
|
||||
String state = this.context.getString(stateResId, details);
|
||||
Timber.d(state);
|
||||
this.state.postValue(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether log are recoding or not.
|
||||
*
|
||||
* @return {@code true} if logs are recoding, {@code false} otherwise.
|
||||
*/
|
||||
public abstract boolean isRecordingLogs();
|
||||
|
||||
/**
|
||||
* Set log recoding.
|
||||
*
|
||||
* @param recording {@code true} to record logs, {@code false} otherwise.
|
||||
*/
|
||||
public abstract void setRecordingLogs(boolean recording);
|
||||
|
||||
/**
|
||||
* Get logs.
|
||||
*
|
||||
* @return The logs unique and sorted by date, older first.
|
||||
*/
|
||||
public abstract List<String> getLogs();
|
||||
|
||||
/**
|
||||
* Clear logs.
|
||||
*/
|
||||
public abstract void clearLogs();
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package org.adaway.model.adblocking;
|
||||
|
||||
import static org.adaway.model.adblocking.AdBlockMethod.UNDEFINED;
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class is a stub model when no ad block method is defined.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public class UndefinedBlockModel extends AdBlockModel {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context The application context.
|
||||
*/
|
||||
public UndefinedBlockModel(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdBlockMethod getMethod() {
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
// Unsupported operation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revert() {
|
||||
// Unsupported operation
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRecordingLogs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordingLogs(boolean recording) {
|
||||
// Unsupported operation
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getLogs() {
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearLogs() {
|
||||
// Unsupported operation
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package org.adaway.model.backup;
|
||||
|
||||
import static org.adaway.util.Constants.PREFS_NAME;
|
||||
|
||||
import android.app.backup.BackupAgentHelper;
|
||||
import android.app.backup.BackupDataInputStream;
|
||||
import android.app.backup.BackupDataOutput;
|
||||
import android.app.backup.FileBackupHelper;
|
||||
import android.app.backup.SharedPreferencesBackupHelper;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This class is a {@link android.app.backup.BackupAgent} to backup and restore application state
|
||||
* using Android Backup Service. It is based on key-value pairs backup to prevent killing the
|
||||
* application during the backup (leaving the VPN foreground service always running). It backs up
|
||||
* and restores the application preferences and the user rules.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public class AppBackupAgent extends BackupAgentHelper {
|
||||
private static final String PREFS_BACKUP_KEY = "prefs";
|
||||
private static final String RULES_BACKUP_KEY = "rules";
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
addHelper(PREFS_BACKUP_KEY, new SharedPreferencesBackupHelper(this, PREFS_NAME));
|
||||
addHelper(RULES_BACKUP_KEY, new SourceBackupHelper(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a {@link android.app.backup.BackupHelper} to backup and restore user rules.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
private static class SourceBackupHelper extends FileBackupHelper {
|
||||
private static final String RULES_FILE_NAME = "rules-backup.json";
|
||||
private final Context context;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context The application context.
|
||||
*/
|
||||
public SourceBackupHelper(Context context) {
|
||||
super(context, RULES_FILE_NAME);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) {
|
||||
try {
|
||||
BackupExporter.exportBackup(this.context, getRulesFileUri());
|
||||
super.performBackup(oldState, data, newState);
|
||||
} catch (IOException e) {
|
||||
Timber.w(e, "Failed to export rules to backup.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreEntity(BackupDataInputStream data) {
|
||||
super.restoreEntity(data);
|
||||
try {
|
||||
BackupImporter.importBackup(this.context, getRulesFileUri());
|
||||
} catch (IOException e) {
|
||||
Timber.w(e, "Failed to import rules from backup.");
|
||||
}
|
||||
}
|
||||
|
||||
private Uri getRulesFileUri() {
|
||||
File ruleFile = new File(this.context.getFilesDir(), RULES_FILE_NAME);
|
||||
return Uri.fromFile(ruleFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
141
app/src/main/java/org/adaway/model/backup/BackupExporter.java
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
package org.adaway.model.backup;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import org.adaway.R;
|
||||
import org.adaway.db.AppDatabase;
|
||||
import org.adaway.db.dao.HostListItemDao;
|
||||
import org.adaway.db.dao.HostsSourceDao;
|
||||
import org.adaway.db.entity.HostListItem;
|
||||
import org.adaway.db.entity.HostsSource;
|
||||
import org.adaway.util.AppExecutors;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.adaway.db.entity.ListType.ALLOWED;
|
||||
import static org.adaway.db.entity.ListType.BLOCKED;
|
||||
import static org.adaway.db.entity.ListType.REDIRECTED;
|
||||
import static org.adaway.model.backup.BackupFormat.ALLOWED_KEY;
|
||||
import static org.adaway.model.backup.BackupFormat.BLOCKED_KEY;
|
||||
import static org.adaway.model.backup.BackupFormat.REDIRECTED_KEY;
|
||||
import static org.adaway.model.backup.BackupFormat.SOURCES_KEY;
|
||||
import static org.adaway.model.backup.BackupFormat.hostToJson;
|
||||
import static org.adaway.model.backup.BackupFormat.sourceToJson;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This class is a helper class to export user lists and hosts sources to a backup file.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public class BackupExporter {
|
||||
private static final Executor DISK_IO_EXECUTOR = AppExecutors.getInstance().diskIO();
|
||||
private static final Executor MAIN_THREAD_EXECUTOR = AppExecutors.getInstance().mainThread();
|
||||
|
||||
private BackupExporter() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Export all user lists and hosts sources to a backup file on the external storage.
|
||||
*
|
||||
* @param context The application context.
|
||||
*/
|
||||
public static void exportToBackup(Context context, Uri backupUri) {
|
||||
DISK_IO_EXECUTOR.execute(() -> {
|
||||
boolean imported = true;
|
||||
try {
|
||||
exportBackup(context, backupUri);
|
||||
} catch (IOException e) {
|
||||
Timber.e(e, "Failed to import backup.");
|
||||
imported = false;
|
||||
}
|
||||
boolean successful = imported;
|
||||
String fileName = getFileNameFromUri(backupUri);
|
||||
MAIN_THREAD_EXECUTOR.execute(() -> notifyExportEnd(context, successful, fileName));
|
||||
});
|
||||
}
|
||||
|
||||
private static String getFileNameFromUri(Uri backupUri) {
|
||||
String path = backupUri.getPath();
|
||||
return path == null ? "" : new File(path).getName();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private static void notifyExportEnd(Context context, boolean successful, String backupUri) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(successful ? R.string.export_success : R.string.export_failed, backupUri),
|
||||
Toast.LENGTH_LONG
|
||||
).show();
|
||||
}
|
||||
|
||||
static void exportBackup(Context context, Uri backupUri) throws IOException {
|
||||
// Open writer on the export file
|
||||
try (OutputStream outputStream = context.getContentResolver().openOutputStream(backupUri);
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream))) {
|
||||
JSONObject backup = makeBackup(context);
|
||||
writer.write(backup.toString(4));
|
||||
} catch (JSONException e) {
|
||||
throw new IOException("Failed to generate backup.", e);
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Could not write file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static JSONObject makeBackup(Context context) throws JSONException {
|
||||
AppDatabase database = AppDatabase.getInstance(context);
|
||||
HostsSourceDao hostsSourceDao = database.hostsSourceDao();
|
||||
HostListItemDao hostListItemDao = database.hostsListItemDao();
|
||||
|
||||
List<HostListItem> userHosts = hostListItemDao.getUserList();
|
||||
List<HostListItem> blockedHosts = userHosts.stream()
|
||||
.filter(value -> value.getType() == BLOCKED)
|
||||
.collect(toList());
|
||||
List<HostListItem> allowedHosts = userHosts.stream()
|
||||
.filter(value -> value.getType() == ALLOWED)
|
||||
.collect(toList());
|
||||
List<HostListItem> redirectedHosts = userHosts.stream()
|
||||
.filter(value -> value.getType() == REDIRECTED)
|
||||
.collect(toList());
|
||||
|
||||
JSONObject backupObject = new JSONObject();
|
||||
backupObject.put(SOURCES_KEY, buildSourcesBackup(hostsSourceDao.getAll()));
|
||||
backupObject.put(BLOCKED_KEY, buildListBackup(blockedHosts));
|
||||
backupObject.put(ALLOWED_KEY, buildListBackup(allowedHosts));
|
||||
backupObject.put(REDIRECTED_KEY, buildListBackup(redirectedHosts));
|
||||
|
||||
return backupObject;
|
||||
}
|
||||
|
||||
private static JSONArray buildSourcesBackup(List<HostsSource> sources) throws JSONException {
|
||||
JSONArray sourceArray = new JSONArray();
|
||||
for (HostsSource source : sources) {
|
||||
sourceArray.put(sourceToJson(source));
|
||||
}
|
||||
return sourceArray;
|
||||
}
|
||||
|
||||
private static JSONArray buildListBackup(List<HostListItem> hosts) throws JSONException {
|
||||
JSONArray listArray = new JSONArray();
|
||||
for (HostListItem host : hosts) {
|
||||
listArray.put(hostToJson(host));
|
||||
}
|
||||
return listArray;
|
||||
}
|
||||
}
|
||||
84
app/src/main/java/org/adaway/model/backup/BackupFormat.java
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package org.adaway.model.backup;
|
||||
|
||||
import org.adaway.db.entity.HostListItem;
|
||||
import org.adaway.db.entity.HostsSource;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import static org.adaway.db.entity.HostsSource.USER_SOURCE_ID;
|
||||
|
||||
/**
|
||||
* This class defines user lists and hosts sources file format.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
final class BackupFormat {
|
||||
/*
|
||||
* Source backup format.
|
||||
*/
|
||||
static final String SOURCES_KEY = "sources";
|
||||
static final String SOURCE_LABEL_ATTRIBUTE = "label";
|
||||
static final String SOURCE_URL_ATTRIBUTE = "url";
|
||||
static final String SOURCE_ENABLED_ATTRIBUTE = "enabled";
|
||||
static final String SOURCE_ALLOW_ATTRIBUTE = "allow";
|
||||
static final String SOURCE_REDIRECT_ATTRIBUTE = "redirect";
|
||||
/*
|
||||
* User source backup format.
|
||||
*/
|
||||
static final String BLOCKED_KEY = "blocked";
|
||||
static final String ALLOWED_KEY = "allowed";
|
||||
static final String REDIRECTED_KEY = "redirected";
|
||||
static final String ENABLED_ATTRIBUTE = "enabled";
|
||||
static final String HOST_ATTRIBUTE = "host";
|
||||
static final String REDIRECT_ATTRIBUTE = "redirect";
|
||||
|
||||
BackupFormat() {
|
||||
|
||||
}
|
||||
|
||||
static JSONObject sourceToJson(HostsSource source) throws JSONException {
|
||||
JSONObject sourceObject = new JSONObject();
|
||||
sourceObject.put(SOURCE_LABEL_ATTRIBUTE, source.getLabel());
|
||||
sourceObject.put(SOURCE_URL_ATTRIBUTE, source.getUrl());
|
||||
sourceObject.put(SOURCE_ENABLED_ATTRIBUTE, source.isEnabled());
|
||||
sourceObject.put(SOURCE_ALLOW_ATTRIBUTE, source.isAllowEnabled());
|
||||
sourceObject.put(SOURCE_REDIRECT_ATTRIBUTE, source.isRedirectEnabled());
|
||||
return sourceObject;
|
||||
}
|
||||
|
||||
static HostsSource sourceFromJson(JSONObject sourceObject) throws JSONException {
|
||||
HostsSource source = new HostsSource();
|
||||
source.setLabel(sourceObject.getString(SOURCE_LABEL_ATTRIBUTE));
|
||||
String url = sourceObject.getString(SOURCE_URL_ATTRIBUTE);
|
||||
if (!HostsSource.isValidUrl(url)) {
|
||||
throw new JSONException("Invalid source URL: "+url);
|
||||
}
|
||||
source.setUrl(url);
|
||||
source.setEnabled(sourceObject.getBoolean(SOURCE_ENABLED_ATTRIBUTE));
|
||||
source.setAllowEnabled(sourceObject.getBoolean(SOURCE_ALLOW_ATTRIBUTE));
|
||||
source.setRedirectEnabled(sourceObject.getBoolean(SOURCE_REDIRECT_ATTRIBUTE));
|
||||
return source;
|
||||
}
|
||||
|
||||
static JSONObject hostToJson(HostListItem host) throws JSONException {
|
||||
JSONObject hostObject = new JSONObject();
|
||||
hostObject.put(HOST_ATTRIBUTE, host.getHost());
|
||||
String redirection = host.getRedirection();
|
||||
if (redirection != null && !redirection.isEmpty()) {
|
||||
hostObject.put(REDIRECT_ATTRIBUTE, redirection);
|
||||
}
|
||||
hostObject.put(ENABLED_ATTRIBUTE, host.isEnabled());
|
||||
return hostObject;
|
||||
}
|
||||
|
||||
static HostListItem hostFromJson(JSONObject hostObject) throws JSONException {
|
||||
HostListItem host = new HostListItem();
|
||||
host.setHost(hostObject.getString(HOST_ATTRIBUTE));
|
||||
if (hostObject.has(REDIRECT_ATTRIBUTE)) {
|
||||
host.setRedirection(hostObject.getString(REDIRECT_ATTRIBUTE));
|
||||
}
|
||||
host.setEnabled(hostObject.getBoolean(ENABLED_ATTRIBUTE));
|
||||
host.setSourceId(USER_SOURCE_ID);
|
||||
return host;
|
||||
}
|
||||
}
|
||||
136
app/src/main/java/org/adaway/model/backup/BackupImporter.java
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
package org.adaway.model.backup;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import org.adaway.R;
|
||||
import org.adaway.db.AppDatabase;
|
||||
import org.adaway.db.dao.HostListItemDao;
|
||||
import org.adaway.db.dao.HostsSourceDao;
|
||||
import org.adaway.db.entity.HostListItem;
|
||||
import org.adaway.db.entity.ListType;
|
||||
import org.adaway.util.AppExecutors;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static org.adaway.db.entity.ListType.ALLOWED;
|
||||
import static org.adaway.db.entity.ListType.BLOCKED;
|
||||
import static org.adaway.db.entity.ListType.REDIRECTED;
|
||||
import static org.adaway.model.backup.BackupFormat.ALLOWED_KEY;
|
||||
import static org.adaway.model.backup.BackupFormat.BLOCKED_KEY;
|
||||
import static org.adaway.model.backup.BackupFormat.REDIRECTED_KEY;
|
||||
import static org.adaway.model.backup.BackupFormat.SOURCES_KEY;
|
||||
import static org.adaway.model.backup.BackupFormat.hostFromJson;
|
||||
import static org.adaway.model.backup.BackupFormat.sourceFromJson;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This class is a helper class to import user lists and hosts sources to a backup file.<br>
|
||||
* Importing a file source will no restore read access from Storage Access Framework.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public final class BackupImporter {
|
||||
|
||||
private BackupImporter() {
|
||||
|
||||
}
|
||||
|
||||
private static final Executor DISK_IO_EXECUTOR = AppExecutors.getInstance().diskIO();
|
||||
private static final Executor MAIN_THREAD_EXECUTOR = AppExecutors.getInstance().mainThread();
|
||||
|
||||
/**
|
||||
* Import a backup file.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param backupUri The URI of a backup file.
|
||||
*/
|
||||
@UiThread
|
||||
public static void importFromBackup(Context context, Uri backupUri) {
|
||||
DISK_IO_EXECUTOR.execute(() -> {
|
||||
boolean imported = true;
|
||||
try {
|
||||
importBackup(context, backupUri);
|
||||
} catch (IOException e) {
|
||||
Timber.e(e, "Failed to import backup.");
|
||||
imported = false;
|
||||
}
|
||||
boolean successful = imported;
|
||||
MAIN_THREAD_EXECUTOR.execute(() -> notifyImportEnd(context, successful));
|
||||
});
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private static void notifyImportEnd(Context context, boolean successful) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(successful ? R.string.import_success : R.string.import_failed),
|
||||
Toast.LENGTH_LONG
|
||||
).show();
|
||||
}
|
||||
|
||||
static void importBackup(Context context, Uri backupUri) throws IOException {
|
||||
try (InputStream inputStream = context.getContentResolver().openInputStream(backupUri);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
||||
StringBuilder contentBuilder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
contentBuilder.append(line);
|
||||
}
|
||||
JSONObject backupObject = new JSONObject(contentBuilder.toString());
|
||||
importBackup(context, backupObject);
|
||||
} catch (JSONException exception) {
|
||||
throw new IOException("Failed to parse backup file.", exception);
|
||||
} catch (FileNotFoundException exception) {
|
||||
throw new IOException("Failed to find backup file.", exception);
|
||||
} catch (IOException exception) {
|
||||
throw new IOException("Failed to read backup file.", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static void importBackup(Context context, JSONObject backupObject) throws JSONException {
|
||||
AppDatabase database = AppDatabase.getInstance(context);
|
||||
HostsSourceDao hostsSourceDao = database.hostsSourceDao();
|
||||
HostListItemDao hostListItemDao = database.hostsListItemDao();
|
||||
|
||||
importSourceBackup(hostsSourceDao, backupObject.getJSONArray(SOURCES_KEY));
|
||||
importListBackup(hostListItemDao, BLOCKED, backupObject.getJSONArray(BLOCKED_KEY));
|
||||
importListBackup(hostListItemDao, ALLOWED, backupObject.getJSONArray(ALLOWED_KEY));
|
||||
importListBackup(hostListItemDao, REDIRECTED, backupObject.getJSONArray(REDIRECTED_KEY));
|
||||
}
|
||||
|
||||
private static void importSourceBackup(HostsSourceDao hostsSourceDao, JSONArray sources) throws JSONException {
|
||||
for (int index = 0; index < sources.length(); index++) {
|
||||
JSONObject sourceObject = sources.getJSONObject(index);
|
||||
hostsSourceDao.insert(sourceFromJson(sourceObject));
|
||||
}
|
||||
}
|
||||
|
||||
private static void importListBackup(HostListItemDao hostListItemDao, ListType type, JSONArray hosts) throws JSONException {
|
||||
for (int index = 0; index < hosts.length(); index++) {
|
||||
JSONObject hostObject = hosts.getJSONObject(index);
|
||||
HostListItem host = hostFromJson(hostObject);
|
||||
host.setType(type);
|
||||
Optional<Integer> id = hostListItemDao.getHostId(host.getHost());
|
||||
if (id.isPresent()) {
|
||||
host.setId(id.get());
|
||||
hostListItemDao.update(host);
|
||||
} else {
|
||||
hostListItemDao.insert(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
app/src/main/java/org/adaway/model/error/HostError.java
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package org.adaway.model.error;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import org.adaway.R;
|
||||
|
||||
/**
|
||||
* This enumeration represents the hosts error case.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public enum HostError {
|
||||
// Source model errors
|
||||
NO_CONNECTION(R.string.error_no_connection_message, R.string.error_no_connection_details),
|
||||
DOWNLOAD_FAILED(R.string.error_download_failed_message, R.string.error_no_connection_details),
|
||||
// Host install model errors
|
||||
PRIVATE_FILE_FAILED(R.string.error_private_file_failed_message, R.string.error_private_file_failed_details),
|
||||
NOT_ENOUGH_SPACE(R.string.error_not_enough_space_message, R.string.error_not_enough_space_details),
|
||||
COPY_FAIL(R.string.error_copy_failed_message, R.string.error_copy_failed_details),
|
||||
REVERT_FAIL(R.string.error_revert_failed_message, R.string.error_revert_failed_details),
|
||||
// VPN model error
|
||||
ENABLE_VPN_FAIL(R.string.error_enable_vpn_failed_message, R.string.error_enable_vpn_failed_details),
|
||||
DISABLE_VPN_FAIL(R.string.error_disable_vpn_failed_message, R.string.error_disable_vpn_failed_details);
|
||||
|
||||
@StringRes
|
||||
private final int messageKey;
|
||||
@StringRes
|
||||
private final int detailsKey;
|
||||
|
||||
HostError(int messageKey, int detailsKey) {
|
||||
this.messageKey = messageKey;
|
||||
this.detailsKey = detailsKey;
|
||||
}
|
||||
|
||||
@StringRes
|
||||
public int getMessageKey() {
|
||||
return this.messageKey;
|
||||
}
|
||||
|
||||
@StringRes
|
||||
public int getDetailsKey() {
|
||||
return this.detailsKey;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package org.adaway.model.error;
|
||||
|
||||
/**
|
||||
* This class in an {@link Exception} thrown on hosts error.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public class HostErrorException extends Exception {
|
||||
/**
|
||||
* The exception error type.
|
||||
*/
|
||||
private final HostError error;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param error The exception error type.
|
||||
*/
|
||||
public HostErrorException(HostError error) {
|
||||
super("An host error " + error.name() + " occurred");
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param error The exception error type.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public HostErrorException(HostError error, Throwable cause) {
|
||||
super("An host error " + error.name() + " occurred", cause);
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error type.
|
||||
*
|
||||
* @return The exception error type
|
||||
*/
|
||||
public HostError getError() {
|
||||
return this.error;
|
||||
}
|
||||
}
|
||||
61
app/src/main/java/org/adaway/model/git/GistHostsSource.java
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package org.adaway.model.git;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This class is an utility class to get information from GitHub gist hosting.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
class GistHostsSource extends GitHostsJsonApiSource {
|
||||
/**
|
||||
* The gist identifier.
|
||||
*/
|
||||
private final String gistIdentifier;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param url The hosts file URL hosted on GitHub gist.
|
||||
* @throws MalformedURLException If the URl is not a gist URL.
|
||||
*/
|
||||
GistHostsSource(String url) throws MalformedURLException {
|
||||
// Check URL path
|
||||
URL parsedUrl = new URL(url);
|
||||
String path = parsedUrl.getPath();
|
||||
String[] pathParts = path.split("/");
|
||||
if (pathParts.length < 2) {
|
||||
throw new MalformedURLException("The GitHub gist URL " + url + " is not valid.");
|
||||
}
|
||||
// Extract gist identifier from path
|
||||
this.gistIdentifier = pathParts[2];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCommitApiUrl() {
|
||||
return "https://api.github.com/gists/" + this.gistIdentifier;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ZonedDateTime parseJsonBody(String body) throws JSONException {
|
||||
JSONObject gistObject = new JSONObject(body);
|
||||
String dateString = gistObject.getString("updated_at");
|
||||
ZonedDateTime date = null;
|
||||
try {
|
||||
date = ZonedDateTime.parse(dateString);
|
||||
} catch (DateTimeParseException exception) {
|
||||
Timber.w(exception, "Failed to parse commit date: " + dateString + ".");
|
||||
}
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package org.adaway.model.git;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This class is an utility class to get information from Git hosted hosts sources from JSON API.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public abstract class GitHostsJsonApiSource extends GitHostsSource {
|
||||
@Override
|
||||
@Nullable
|
||||
public ZonedDateTime getLastUpdate() {
|
||||
return getLastUpdateFromApi(getCommitApiUrl());
|
||||
}
|
||||
|
||||
protected abstract String getCommitApiUrl();
|
||||
|
||||
@Nullable
|
||||
protected ZonedDateTime getLastUpdateFromApi(String commitApiUrl) {
|
||||
// Create client and request
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
Request request = new Request.Builder().url(commitApiUrl).build();
|
||||
try (Response response = client.newCall(request).execute();
|
||||
ResponseBody body = response.body()) {
|
||||
if (response.isSuccessful()) {
|
||||
return parseJsonBody(body.string());
|
||||
}
|
||||
} catch (UnknownHostException | SocketTimeoutException exception) {
|
||||
Timber.i(exception, "Unable to reach API backend.");
|
||||
} catch (IOException | JSONException exception) {
|
||||
Timber.e(exception, "Unable to get commits from API.");
|
||||
}
|
||||
// Return failed
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract ZonedDateTime parseJsonBody(String body) throws JSONException;
|
||||
}
|
||||
65
app/src/main/java/org/adaway/model/git/GitHostsSource.java
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package org.adaway.model.git;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* This class is an utility class to get information from Git hosted hosts sources.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public abstract class GitHostsSource {
|
||||
/**
|
||||
* The GitHub repository URL.
|
||||
*/
|
||||
private static final String GITHUB_REPO_URL = "https://raw.githubusercontent.com/";
|
||||
/**
|
||||
* The GitHub gist URL.
|
||||
*/
|
||||
private static final String GITHUB_GIST_URL = "https://gist.githubusercontent.com";
|
||||
/**
|
||||
* The GitLab URL.
|
||||
*/
|
||||
private static final String GITLAB_URL = "https://gitlab.com/";
|
||||
|
||||
/**
|
||||
* Check if a hosts file url is hosted on Git hosting.
|
||||
*
|
||||
* @param url The url to check.
|
||||
* @return {@code true} if the hosts file is hosted on Git hosting, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isHostedOnGit(String url) {
|
||||
return url.startsWith(GITHUB_REPO_URL) ||
|
||||
url.startsWith(GITHUB_GIST_URL) ||
|
||||
url.startsWith(GITLAB_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the GitHub hosts source.
|
||||
*
|
||||
* @param url The URL to get source from.
|
||||
* @return The GitHub hosts source.
|
||||
* @throws MalformedURLException If the URL is not a GitHub URL or not a supported GitHub URL.
|
||||
*/
|
||||
public static GitHostsSource getSource(String url) throws MalformedURLException {
|
||||
if (url.startsWith(GITHUB_REPO_URL)) {
|
||||
return new GitHubHostsSource(url);
|
||||
} else if (url.startsWith(GITHUB_GIST_URL)) {
|
||||
return new GistHostsSource(url);
|
||||
} else if (url.startsWith(GITLAB_URL)) {
|
||||
return new GitLabHostsSource(url);
|
||||
} else {
|
||||
throw new MalformedURLException("URL is not a supported Git hosting URL");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last update of the hosts file.
|
||||
*
|
||||
* @return The last update date, {@code null} if the date could not be retrieved.
|
||||
*/
|
||||
@Nullable
|
||||
public abstract ZonedDateTime getLastUpdate();
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package org.adaway.model.git;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This class is an utility class to get information from GitHub repository hosting.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
class GitHubHostsSource extends GitHostsJsonApiSource {
|
||||
/**
|
||||
* The GitHub owner name.
|
||||
*/
|
||||
private final String owner;
|
||||
/**
|
||||
* The GitHub repository name.
|
||||
*/
|
||||
private final String repo;
|
||||
/**
|
||||
* The GitHub blob (hosts file) path.
|
||||
*/
|
||||
private final String blobPath;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param url The hosts file URL hosted on GitHub.
|
||||
* @throws MalformedURLException If the URl is not a GitHub URL.
|
||||
*/
|
||||
GitHubHostsSource(String url) throws MalformedURLException {
|
||||
// Check URL path
|
||||
URL parsedUrl = new URL(url);
|
||||
String path = parsedUrl.getPath();
|
||||
String[] pathParts = path.split("/");
|
||||
if (pathParts.length < 5) {
|
||||
throw new MalformedURLException("The GitHub user content URL " + url + " is not valid.");
|
||||
}
|
||||
// Extract components from path
|
||||
this.owner = pathParts[1];
|
||||
this.repo = pathParts[2];
|
||||
this.blobPath = Arrays.stream(pathParts)
|
||||
.skip(4)
|
||||
.collect(joining("/"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCommitApiUrl() {
|
||||
return "https://api.github.com/repos/" + this.owner + "/" + this.repo +
|
||||
"/commits?per_page=1&path=" + this.blobPath;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ZonedDateTime parseJsonBody(String body) throws JSONException {
|
||||
JSONArray commitArray = new JSONArray(body);
|
||||
int nbrOfCommits = commitArray.length();
|
||||
ZonedDateTime date = null;
|
||||
for (int i = 0; i < nbrOfCommits && date == null; i++) {
|
||||
JSONObject commitItemObject = commitArray.getJSONObject(i);
|
||||
JSONObject commitObject = commitItemObject.getJSONObject("commit");
|
||||
JSONObject committerObject = commitObject.getJSONObject("committer");
|
||||
String dateString = committerObject.getString("date");
|
||||
try {
|
||||
date = ZonedDateTime.parse(dateString);
|
||||
} catch (DateTimeParseException exception) {
|
||||
Timber.w(exception, "Failed to parse commit date: " + dateString + ".");
|
||||
}
|
||||
}
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package org.adaway.model.git;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This class is an utility class to get information from GitLab hosts source hosting.
|
||||
*
|
||||
* @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
|
||||
*/
|
||||
public class GitLabHostsSource extends GitHostsJsonApiSource {
|
||||
/**
|
||||
* The GitHub owner name.
|
||||
*/
|
||||
private final String owner;
|
||||
/**
|
||||
* The GitHub repository name.
|
||||
*/
|
||||
private final String repo;
|
||||
/**
|
||||
* The GitLab reference name.
|
||||
*/
|
||||
private final String ref;
|
||||
/**
|
||||
* The GitLab (hosts) file path.
|
||||
*/
|
||||
private final String path;
|
||||
|
||||
GitLabHostsSource(String url) throws MalformedURLException {
|
||||
// Check URL path
|
||||
URL parsedUrl = new URL(url);
|
||||
String path = parsedUrl.getPath();
|
||||
String[] pathParts = path.split("/");
|
||||
if (pathParts.length < 5) {
|
||||
throw new MalformedURLException("The GitLab user content URL " + url + " is not valid.");
|
||||
}
|
||||
// Extract components from path
|
||||
this.owner = pathParts[1];
|
||||
this.repo = pathParts[2];
|
||||
this.ref = pathParts[4];
|
||||
this.path = Arrays.stream(pathParts)
|
||||
.skip(5)
|
||||
.collect(joining("/"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCommitApiUrl() {
|
||||
return "https://gitlab.com/api/v4/projects/" + this.owner + "%2F" + this.repo
|
||||
+ "/repository/commits?path=" + this.path + "&ref_name=" + this.ref;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ZonedDateTime parseJsonBody(String body) throws JSONException {
|
||||
JSONArray commitArray = new JSONArray(body);
|
||||
int nbrOfCommits = commitArray.length();
|
||||
ZonedDateTime date = null;
|
||||
for (int i = 0; i < nbrOfCommits && date == null; i++) {
|
||||
JSONObject commitItemObject = commitArray.getJSONObject(i);
|
||||
String dateString = commitItemObject.getString("committed_date");
|
||||
try {
|
||||
date = ZonedDateTime.parse(dateString);
|
||||
} catch (DateTimeParseException exception) {
|
||||
Timber.w(exception, "Failed to parse commit date: " + dateString + ".");
|
||||
}
|
||||
}
|
||||
return date;
|
||||
}
|
||||
}
|
||||