├── .github
├── ISSUE_TEMPLATE
│ └── bug-report-only.md
└── workflows
│ └── buid-app-workflow.yaml
├── .gitignore
├── .mainframer
├── config
├── ignore
└── localignore
├── .travis.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTORS.md
├── Contributing.md
├── FAQs.md
├── LICENSE.md
├── PRIVACY.md
├── README.md
├── SCREENSHOTS.md
├── ad-filter
├── .gitignore
├── README.md
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
├── scriptlets_src
│ ├── .gitignore
│ ├── README.md
│ ├── gulpfile.js
│ ├── package.json
│ └── scriptlets.js
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── io
│ │ └── github
│ │ └── edsuns
│ │ └── adfilter
│ │ ├── AdFilter.kt
│ │ ├── CustomFilter.kt
│ │ ├── Filter.kt
│ │ ├── FilterResult.kt
│ │ ├── FilterViewModel.kt
│ │ ├── impl
│ │ ├── AdFilterImpl.kt
│ │ ├── BinaryDataStore.kt
│ │ ├── Constants.kt
│ │ ├── CustomFilterImpl.kt
│ │ ├── Detector.kt
│ │ ├── FilterDataLoader.kt
│ │ ├── FilterSharedPreferences.kt
│ │ └── FilterViewModelImpl.kt
│ │ ├── script
│ │ ├── ElementHiding.kt
│ │ ├── ScriptInjection.kt
│ │ └── Scriptlet.kt
│ │ ├── util
│ │ ├── Checksum.kt
│ │ ├── HashUtilities.kt
│ │ ├── None.kt
│ │ └── RuleIterator.kt
│ │ └── workers
│ │ ├── DownloadWorker.kt
│ │ └── InstallationWorker.kt
│ └── js
│ ├── element_hiding.js
│ ├── elemhide_blocked.js
│ ├── extended-css.min.js
│ ├── inject.js
│ ├── scriptlets.min.js
│ └── scriptlets_inject.js
├── adblock-client
├── .gitignore
├── CMakeLists.txt
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── resources
│ │ └── binary
│ │ ├── easylist_sample
│ │ └── easyprivacy_sample
│ └── main
│ ├── AndroidManifest.xml
│ ├── cpp
│ ├── adblockclient-lib.cpp
│ └── third-party
│ │ ├── ad-block
│ │ ├── ad_block_client.cc
│ │ ├── ad_block_client.h
│ │ ├── bad_fingerprint.h
│ │ ├── bad_fingerprints.h
│ │ ├── base.h
│ │ ├── context_domain.cc
│ │ ├── context_domain.h
│ │ ├── cosmetic_filter.cc
│ │ ├── cosmetic_filter.h
│ │ ├── filter.cc
│ │ ├── filter.h
│ │ ├── hash_map.h
│ │ ├── linked_list.h
│ │ ├── no_fingerprint_domain.cc
│ │ ├── no_fingerprint_domain.h
│ │ ├── protocol.cc
│ │ └── protocol.h
│ │ ├── bloom-filter-cpp
│ │ ├── BloomFilter.cpp
│ │ ├── BloomFilter.h
│ │ └── base.h
│ │ └── hashset-cpp
│ │ ├── base.h
│ │ ├── hashFn.cc
│ │ ├── hashFn.h
│ │ ├── hash_item.h
│ │ ├── hash_set.cc
│ │ └── hash_set.h
│ └── java
│ └── io
│ └── github
│ └── edsuns
│ └── adblockclient
│ ├── AdBlockClient.kt
│ ├── Client.kt
│ ├── MatchResult.kt
│ └── ResourceType.kt
├── app
├── build.gradle.kts
├── lint-baseline.xml
├── proguard-rules.txt
├── schemas
│ ├── de.baumann.browser.database.AppDatabase
│ │ └── 2.json
│ └── info.plateaukao.einkbro.database.AppDatabase
│ │ ├── 2.json
│ │ ├── 3.json
│ │ ├── 4.json
│ │ ├── 5.json
│ │ └── 6.json
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ ├── MozReadability.js
│ ├── chat.html
│ ├── eVoiceList.json
│ ├── fix_scrolling.js
│ ├── get_selected_text_with_context.js
│ ├── highlight.css
│ ├── process_text_nodes.js
│ ├── readerview.css
│ ├── select_paragraph.js
│ ├── select_sentence.js
│ ├── text_node_monitor.js
│ ├── text_selection_change.js
│ ├── text_selection_highlight.js
│ ├── translate_by_paragraph.js
│ ├── translate_processing.html
│ ├── translated_image.html
│ ├── verticalReaderview.css
│ └── video_auto_fullscreen.js
│ ├── java
│ └── info
│ │ └── plateaukao
│ │ └── einkbro
│ │ ├── EinkBroApplication.kt
│ │ ├── activity
│ │ ├── AdBlockSettingActivity.kt
│ │ ├── BrowserActivity.kt
│ │ ├── DataListActivity.kt
│ │ ├── DictActivity.kt
│ │ ├── EpubReaderActivity.kt
│ │ ├── ExtraBrowserActivity.kt
│ │ ├── GptActionsActivity.kt
│ │ ├── GptQueryListActivity.kt
│ │ ├── HighlightsActivity.kt
│ │ ├── KeyHandler.kt
│ │ ├── SettingActivity.kt
│ │ └── ToolbarConfigActivity.kt
│ │ ├── browser
│ │ ├── AlbumController.kt
│ │ ├── BaseWebConfig.kt
│ │ ├── BrowserContainer.kt
│ │ ├── BrowserController.kt
│ │ ├── ChatWebInterface.kt
│ │ ├── DataItemInterface.kt
│ │ ├── EBClickHandler.kt
│ │ ├── EBDownloadListener.kt
│ │ ├── EBWebChromeClient.kt
│ │ ├── JsWebInterface.kt
│ │ ├── LongPressGestureListener.kt
│ │ ├── NinjaWebViewClient.kt
│ │ └── WebContentPostProcessor.kt
│ │ ├── caption
│ │ ├── DualCaptionProcessor.kt
│ │ └── TimedText.kt
│ │ ├── database
│ │ ├── Bookmark.kt
│ │ ├── BookmarkDao.kt
│ │ ├── ChatGptQuery.kt
│ │ ├── ChatGptQueryDao.kt
│ │ ├── DomainConfiguration.kt
│ │ ├── DomainConfigurationDao.kt
│ │ ├── FaviconInfo.kt
│ │ ├── Highlight.kt
│ │ ├── History.kt
│ │ ├── Record.kt
│ │ ├── RecordDb.kt
│ │ └── RecordHelper.kt
│ │ ├── epub
│ │ ├── BookTextMapper.kt
│ │ ├── Epub.kt
│ │ ├── EpubCoverParser.kt
│ │ ├── EpubFileInfo.kt
│ │ ├── EpubManager.kt
│ │ ├── EpubParser.kt
│ │ ├── EpubReaderListener.kt
│ │ ├── EpubReaderView.kt
│ │ ├── EpubXMLFileParser.kt
│ │ ├── HelperExtensions.kt
│ │ └── SelectedTextInfo.kt
│ │ ├── pocket
│ │ └── PocketNetwork.kt
│ │ ├── preference
│ │ ├── AlbumInfo.kt
│ │ ├── ChatGPTActionInfo.kt
│ │ ├── ConfigManager.kt
│ │ ├── CustomFontInfo.kt
│ │ ├── RecentBookmark.kt
│ │ ├── SplitSearchItemInfo.kt
│ │ └── TouchAreaType.kt
│ │ ├── search
│ │ └── SplitSearchListType.kt
│ │ ├── service
│ │ ├── ClearService.kt
│ │ ├── OpenAiRepository.kt
│ │ ├── TranslateRepository.kt
│ │ ├── TtsManager.kt
│ │ └── data
│ │ │ └── GeminiData.kt
│ │ ├── setting
│ │ ├── SettingComposeData.kt
│ │ └── SettingComposeUi.kt
│ │ ├── task
│ │ └── SaveScreenshotTask.kt
│ │ ├── tts
│ │ ├── ByteArrayMediaSource.kt
│ │ ├── CustomMediaPlayer.kt
│ │ ├── ETts.kt
│ │ └── entity
│ │ │ ├── VoiceItem.kt
│ │ │ └── VoiceTag.kt
│ │ ├── unit
│ │ ├── BackupUnit.kt
│ │ ├── BrowserUnit.kt
│ │ ├── HelperUnit.kt
│ │ ├── IntentUnit.kt
│ │ ├── LocaleManager.kt
│ │ ├── RecordUnit.kt
│ │ ├── ShareUtil.kt
│ │ └── ViewUnit.kt
│ │ ├── util
│ │ ├── Constants.kt
│ │ ├── CustomExceptionHandler.kt
│ │ ├── DebugT.kt
│ │ ├── KeyHandler.kt
│ │ ├── KeyHandlingManager.kt
│ │ ├── PdfDocumentAdapter.kt
│ │ └── TranslationLanguage.kt
│ │ ├── view
│ │ ├── Album.kt
│ │ ├── EBToast.kt
│ │ ├── EBWebView.kt
│ │ ├── GuestureType.kt
│ │ ├── MultitouchListener.kt
│ │ ├── SwipeTouchListener.kt
│ │ ├── TwoPaneLayout.kt
│ │ ├── compose
│ │ │ ├── AutoCompleteTextField.kt
│ │ │ ├── BrowseHistoryList.kt
│ │ │ ├── HistoryAndTabs.kt
│ │ │ ├── MyTheme.kt
│ │ │ ├── SearchBar.kt
│ │ │ ├── SelectableItem.kt
│ │ │ ├── Shapes.kt
│ │ │ └── Toolbar.kt
│ │ ├── data
│ │ │ └── MenuInfo.kt
│ │ ├── dialog
│ │ │ ├── BookmarkEditDialog.kt
│ │ │ ├── DialogManager.kt
│ │ │ ├── ListSettingDialog.kt
│ │ │ ├── PrinterDocumentPaperSizeDialog.kt
│ │ │ ├── ReceiveDataDialog.kt
│ │ │ ├── SendLinkDialog.kt
│ │ │ ├── ShortcutEditDialog.kt
│ │ │ ├── TextInputDialog.kt
│ │ │ ├── TranslationLanguageDialog.kt
│ │ │ ├── TtsLanguageDialog.kt
│ │ │ └── compose
│ │ │ │ ├── ActionModeView.kt
│ │ │ │ ├── AuthenticationDialogFragment.kt
│ │ │ │ ├── BookmarkContextMenuDlgFragment.kt
│ │ │ │ ├── BookmarksDialogFragment.kt
│ │ │ │ ├── ComposeDialogFragment.kt
│ │ │ │ ├── ContextMenuDialogFragment.kt
│ │ │ │ ├── DraggableComposeDialogFragment.kt
│ │ │ │ ├── ETtsVoiceDialogFragment.kt
│ │ │ │ ├── FastToggleDialogFragment.kt
│ │ │ │ ├── FontBoldnessDialogFragment.kt
│ │ │ │ ├── FontDialogFragment.kt
│ │ │ │ ├── HighlightStyleDialogFragment.kt
│ │ │ │ ├── LanguageSettingDialogFragment.kt
│ │ │ │ ├── MenuDialogFragment.kt
│ │ │ │ ├── ReaderFontDialogFragment.kt
│ │ │ │ ├── ShowEditGptActionDialogFragment.kt
│ │ │ │ ├── ToolbarConfigDialogFragment.kt
│ │ │ │ ├── TouchAreaDialogFragment.kt
│ │ │ │ ├── TranslateDialogFragment.kt
│ │ │ │ ├── TranslationConfigDlgFragment.kt
│ │ │ │ └── TtsSettingDialogFragment.kt
│ │ ├── handlers
│ │ │ ├── GestureHandler.kt
│ │ │ ├── MenuActionHandler.kt
│ │ │ └── ToolbarActionHandler.kt
│ │ ├── toolbaricons
│ │ │ └── ToolbarAction.kt
│ │ └── viewControllers
│ │ │ ├── ComposeToolbarViewController.kt
│ │ │ ├── FabImageViewController.kt
│ │ │ ├── OverviewDialogController.kt
│ │ │ ├── TouchAreaViewController.kt
│ │ │ └── TwoPaneController.kt
│ │ └── viewmodel
│ │ ├── ActionModeMenuViewModel.kt
│ │ ├── AlbumViewModel.kt
│ │ ├── BookmarkViewModel.kt
│ │ ├── ExternalSearchViewModel.kt
│ │ ├── GptQueryViewModel.kt
│ │ ├── HighlightViewModel.kt
│ │ ├── PocketViewModel.kt
│ │ ├── RemoteConnViewModel.kt
│ │ ├── SplitSearchViewModel.kt
│ │ ├── TranslationViewModel.kt
│ │ └── TtsViewModel.kt
│ └── res
│ ├── drawable-hdpi
│ ├── qc_bookmarks.webp
│ └── qc_history.webp
│ ├── drawable-mdpi
│ ├── qc_bookmarks.webp
│ └── qc_history.webp
│ ├── drawable-xhdpi
│ ├── qc_bookmarks.webp
│ └── qc_history.webp
│ ├── drawable-xxhdpi
│ ├── qc_bookmarks.webp
│ └── qc_history.webp
│ ├── drawable-xxxhdpi
│ ├── qc_bookmarks.webp
│ └── qc_history.webp
│ ├── drawable
│ ├── background_transparent_with_border.xml
│ ├── background_with_border.xml
│ ├── background_with_border_margin.xml
│ ├── button_border_bg.xml
│ ├── button_border_bg_dash.xml
│ ├── gesture_tap.xml
│ ├── ic_add_folder.xml
│ ├── ic_black_font_off.xml
│ ├── ic_black_font_on.xml
│ ├── ic_bold_font.xml
│ ├── ic_bold_font_active.xml
│ ├── ic_book.xml
│ ├── ic_chat_gpt.xml
│ ├── ic_copy.xml
│ ├── ic_data.xml
│ ├── ic_drag.xml
│ ├── ic_earth.xml
│ ├── ic_folder.xml
│ ├── ic_font_decrease.xml
│ ├── ic_font_increase.xml
│ ├── ic_gemini.xml
│ ├── ic_highlight.xml
│ ├── ic_highlight_color.xml
│ ├── ic_history.xml
│ ├── ic_history_activated.xml
│ ├── ic_home.xml
│ ├── ic_incognito.xml
│ ├── ic_link_here.xml
│ ├── ic_minimize.xml
│ ├── ic_ollama.xml
│ ├── ic_page_count.xml
│ ├── ic_papago.xml
│ ├── ic_paragraph.xml
│ ├── ic_paste.xml
│ ├── ic_phone.xml
│ ├── ic_pocket.xml
│ ├── ic_reader.xml
│ ├── ic_receive.xml
│ ├── ic_remove.xml
│ ├── ic_remove_all_tabs.xml
│ ├── ic_reselect.xml
│ ├── ic_rotate.xml
│ ├── ic_send.xml
│ ├── ic_sort.xml
│ ├── ic_split_screen.xml
│ ├── ic_split_screen_vertical.xml
│ ├── ic_stop.xml
│ ├── ic_sync.xml
│ ├── ic_sync_scroll.xml
│ ├── ic_tab_plus_activated.xml
│ ├── ic_toolbar.xml
│ ├── ic_touch_direction_down.xml
│ ├── ic_touch_direction_left.xml
│ ├── ic_touch_direction_right.xml
│ ├── ic_touch_direction_up.xml
│ ├── ic_touch_disabled.xml
│ ├── ic_touch_enabled.xml
│ ├── ic_touch_left.xml
│ ├── ic_touch_left_right.xml
│ ├── ic_touch_long.xml
│ ├── ic_touch_middle_left_right.xml
│ ├── ic_touch_right.xml
│ ├── ic_translate.xml
│ ├── ic_translate_google.xml
│ ├── ic_tts.xml
│ ├── ic_underscore.xml
│ ├── ic_voice_off.xml
│ ├── ic_white_background.xml
│ ├── ic_white_background_active.xml
│ ├── icon_arrow_down_gest.xml
│ ├── icon_arrow_left_gest.xml
│ ├── icon_arrow_right_gest.xml
│ ├── icon_arrow_up_gest.xml
│ ├── icon_backup.xml
│ ├── icon_bookmark.xml
│ ├── icon_close.xml
│ ├── icon_copyright.xml
│ ├── icon_delete.xml
│ ├── icon_desktop.xml
│ ├── icon_desktop_activate.xml
│ ├── icon_dots.xml
│ ├── icon_earth.xml
│ ├── icon_edit.xml
│ ├── icon_exit.xml
│ ├── icon_export.xml
│ ├── icon_info.xml
│ ├── icon_list.xml
│ ├── icon_menu_save.xml
│ ├── icon_menu_share.xml
│ ├── icon_overflow.xml
│ ├── icon_plus.xml
│ ├── icon_refresh.xml
│ ├── icon_search.xml
│ ├── icon_tab_plus.xml
│ ├── icon_tab_unselected.xml
│ ├── icon_ui.xml
│ ├── item_bg_selector.xml
│ ├── roundcorner.xml
│ ├── selected.xml
│ ├── selected_border_bg.xml
│ ├── top_border_bg.xml
│ ├── touch_area_border.xml
│ └── unselected.xml
│ ├── layout-v26
│ └── dialog_edit_extension.xml
│ ├── layout
│ ├── activity_dict.xml
│ ├── activity_main.xml
│ ├── activity_main_content.xml
│ ├── dialog_action.xml
│ ├── dialog_composable.xml
│ ├── dialog_edit_bookmark.xml
│ ├── dialog_edit_extension.xml
│ ├── dialog_edit_shortcut.xml
│ ├── dialog_saved_epub_list.xml
│ ├── list_item_epub_file.xml
│ ├── translation_page_index.xml
│ ├── translation_panel.xml
│ └── two_pane_layout.xml
│ ├── menu
│ ├── menu_clear.xml
│ └── menu_ereader.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.webp
│ ├── ic_launcher_foreground.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-mdpi
│ ├── ic_launcher.webp
│ ├── ic_launcher_foreground.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-xhdpi
│ ├── ic_launcher.webp
│ ├── ic_launcher_foreground.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.webp
│ ├── ic_launcher_foreground.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.webp
│ ├── ic_launcher_foreground.webp
│ └── ic_launcher_round.webp
│ ├── styles.xml
│ ├── values-af
│ └── strings.xml
│ ├── values-ar
│ └── strings.xml
│ ├── values-ca
│ └── strings.xml
│ ├── values-cs
│ └── strings.xml
│ ├── values-da
│ └── strings.xml
│ ├── values-de
│ └── strings.xml
│ ├── values-el
│ └── strings.xml
│ ├── values-es
│ └── strings.xml
│ ├── values-fi
│ └── strings.xml
│ ├── values-fr
│ └── strings.xml
│ ├── values-he
│ └── strings.xml
│ ├── values-hu
│ └── strings.xml
│ ├── values-in
│ └── strings.xml
│ ├── values-it
│ └── strings.xml
│ ├── values-ja
│ └── strings.xml
│ ├── values-ko
│ └── strings.xml
│ ├── values-night
│ ├── strings.xml
│ └── styles.xml
│ ├── values-nl
│ └── strings.xml
│ ├── values-no
│ └── strings.xml
│ ├── values-pl
│ └── strings.xml
│ ├── values-pt
│ └── strings.xml
│ ├── values-ro
│ └── strings.xml
│ ├── values-ru
│ └── strings.xml
│ ├── values-sat
│ └── strings.xml
│ ├── values-sr
│ └── strings.xml
│ ├── values-sv
│ └── strings.xml
│ ├── values-tr
│ └── strings.xml
│ ├── values-uk
│ └── strings.xml
│ ├── values-vi
│ └── strings.xml
│ ├── values-zh-rCN
│ └── strings.xml
│ ├── values-zh-rTW
│ └── strings.xml
│ ├── values
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimens.xml
│ ├── ic_launcher_background.xml
│ ├── strings.xml
│ └── styles.xml
│ ├── xml-v25
│ └── shortcuts.xml
│ └── xml
│ ├── backup_descriptor.xml
│ ├── filepaths.xml
│ ├── network_security_config.xml
│ └── searchable.xml
├── build.gradle.kts
├── crowdin.yml
├── fastlane
└── metadata
│ └── android
│ └── en-US
│ ├── changelogs
│ └── 853.txt
│ ├── full_description.txt
│ ├── images
│ ├── featureGraphic.png
│ ├── icon.png
│ └── phoneScreenshots
│ │ ├── 1_1.png
│ │ ├── 1_3.png
│ │ ├── 1_4.png
│ │ ├── 1_5.png
│ │ ├── 1_6.png
│ │ └── 7.png
│ ├── short_description.txt
│ └── title.txt
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── ic_launcher-playstore.png
├── mainframer.sh
├── settings.gradle
└── tools
├── screenshots.yml
└── settings_arch.excalidraw
/.github/ISSUE_TEMPLATE/bug-report-only.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report Only
3 | about: Report a bug here. If you want to suggest a feature, please go to Discussions
4 | tab
5 | title: ''
6 | labels: ''
7 | assignees: plateaukao
8 |
9 | ---
10 |
11 | Please write necessary information as described below. If information is not enough, I may not have time to look into it.
12 |
13 | **What device and app version are you using**
14 |
15 | - Device:
16 | - OS:
17 | - EinkBro Version [e.g. 9.7.0. You can find the version number in Settings.]
18 |
19 | **Describe the bug**
20 |
21 | A clear and concise description of what the bug is.
22 |
23 | **To Reproduce**
24 | Steps to reproduce the behavior.
25 |
26 | Example description:
27 | 1. Go to '...'
28 | 2. Click on '....'
29 | 3. Scroll down to '....'
30 | 4. See error
31 |
32 | ** What's the behavior on following similar browsers **
33 | * Via Browser
34 | * xBrowser
35 |
36 | **Do not report that it works in CHROME, FIREFOX, or EDGE, BRAVE. That does not help. Since these are full-fledge browsers**
37 |
38 | **Expected behavior**
39 |
40 | A clear and concise description of what you expected to happen.
41 |
42 | **Screenshots**
43 |
44 | If applicable, add screenshots to help explain your problem.
45 |
46 | **Additional context**
47 |
48 | Add any other context about the problem here.
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | # files for the dex VM
3 | # Java class files
4 | # generated files
5 | gen/
6 |
7 | .kotlin/
8 |
9 | # Local configuration file (sdk path, etc)
10 | local.properties
11 |
12 | # Eclipse project files
13 | # Android Studio
14 | .idea/
15 | .gradle
16 | build
17 | *.iml
18 | app/release
19 | .DS_Store
20 | .idea/gradle.xml
21 | tools/*.png
22 |
23 | .externalNativeBuild
24 | .cxx/
25 | adblock-client/.cxx
26 |
--------------------------------------------------------------------------------
/.mainframer/config:
--------------------------------------------------------------------------------
1 | remote_machine=m1
2 |
--------------------------------------------------------------------------------
/.mainframer/ignore:
--------------------------------------------------------------------------------
1 | .gradle
2 |
3 | .git/*
4 | .gitmodules
5 |
6 | /.idea
7 | /.mainframer
8 | /mainframer.sh
9 |
--------------------------------------------------------------------------------
/.mainframer/localignore:
--------------------------------------------------------------------------------
1 | build/*
2 | app/build/*
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | android:
3 | components:
4 | - tools
5 | - platform-tools
6 | - tools
7 |
8 | before_install:
9 | - yes | sdkmanager "platforms;android-27" "build-tools;27.0.3"
10 |
11 | before_cache:
12 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
13 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
14 | cache:
15 | directories:
16 | - $HOME/.gradle/caches/
17 | - $HOME/.gradle/wrapper/
18 | - $HOME/.android/build-cache
19 | script:
20 | - ./gradlew build
21 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | This program is free software: you can redistribute it and/or modify
2 | it under the terms of the GNU General Public License as published by
3 | the Free Software Foundation, either version 3 of the License, or
4 | (at your option) any later version.
5 |
6 | This program is distributed in the hope that it will be useful,
7 | but WITHOUT ANY WARRANTY; without even the implied warranty of
8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 | GNU General Public License for more details.
10 |
11 | You should have received a copy of the GNU General Public License
12 | along with this program. If not, see .
13 |
14 | **Used open source libraries:**
15 |
16 | "Android Observable ScrollView"
17 | https://github.com/ksoichiro/Android-ObservableScrollView
18 |
19 | "Android Onboarder"
20 | https://github.com/chyrta/AndroidOnboarder
21 |
22 | "Glide"
23 | https://github.com/bumptech/glide
24 |
25 | "MAH Encryptor Lib"
26 | https://github.com/hummatli/MAHEncryptorLib
27 |
28 | "Material About Library"
29 | https://github.com/daniel-stoneuk/material-about-library
30 |
31 | "Material Design Icons"
32 | https://github.com/Templarian/MaterialDesign
--------------------------------------------------------------------------------
/SCREENSHOTS.md:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 
4 | 
5 | 
6 | 
7 | 
--------------------------------------------------------------------------------
/ad-filter/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ad-filter/README.md:
--------------------------------------------------------------------------------
1 | # Files Introduction
2 |
3 | ## About JavaScript source code files in `src/main/js/`
4 |
5 | - `extended-css.min.js` is from https://github.com/AdguardTeam/ExtendedCss/releases
6 | - `scriptlets.min.js` is generated from `scriptlets_src` folder
7 |
--------------------------------------------------------------------------------
/ad-filter/consumer-rules.pro:
--------------------------------------------------------------------------------
1 | # ProGuard rules for Kotlin Serialization
2 |
3 | -keepattributes *Annotation*, InnerClasses
4 | -dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations
5 |
6 | # kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
7 | -keepclassmembers class kotlinx.serialization.json.** {
8 | *** Companion;
9 | }
10 | -keepclasseswithmembers class kotlinx.serialization.json.** {
11 | kotlinx.serialization.KSerializer serializer(...);
12 | }
13 |
14 | # Change here com.yourcompany.yourpackage
15 | -keep,includedescriptorclasses class io.github.edsuns.adfilter.**$$serializer { *; } # <-- change package name to your app's
16 | -keepclassmembers class io.github.edsuns.adfilter.** { # <-- change package name to your app's
17 | *** Companion;
18 | }
19 | -keepclasseswithmembers class io.github.edsuns.adfilter.** { # <-- change package name to your app's
20 | kotlinx.serialization.KSerializer serializer(...);
21 | }
--------------------------------------------------------------------------------
/ad-filter/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ad-filter/scriptlets_src/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json
--------------------------------------------------------------------------------
/ad-filter/scriptlets_src/README.md:
--------------------------------------------------------------------------------
1 | # Scriptlets for AdblockAndroid
2 |
3 | `scriptlets.js` is from https://github.com/AdguardTeam/Scriptlets/blob/master/dist/scriptlets.js
4 |
5 |
6 | ### Build
7 |
8 | Run `gulp` to generate output files into `../src/main/js/`.
9 |
--------------------------------------------------------------------------------
/ad-filter/scriptlets_src/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp');
2 | const fs = require('fs');
3 |
4 | // uglify-es
5 | const uglifyjs = require('uglify-es');
6 | const composer = require('gulp-uglify/composer');
7 | const pump = require('pump');
8 | const uglifyES = composer(uglifyjs, console);
9 |
10 | const file = 'scriptlets.js';
11 | const dist = '../src/main/js/';
12 | const minFile = dist + 'scriptlets.min.js'
13 |
14 | gulp.task('uglify', function (cb) {
15 | var options = {
16 | toplevel: true,
17 | compress: {
18 | inline: false,
19 | keep_fargs: true,
20 | keep_classnames: true,
21 | keep_fnames: true
22 | },
23 | mangle: false
24 | };
25 |
26 | pump([
27 | gulp.src(file),
28 | uglifyES(options),
29 | gulp.dest(dist)
30 | ],
31 | cb
32 | );
33 | });
34 |
35 | gulp.task('rename', function (done) {
36 | const distFile = dist + file;
37 | fs.rename(distFile, minFile, done);
38 | });
39 |
40 | gulp.task('default', gulp.series('uglify', 'rename'));
--------------------------------------------------------------------------------
/ad-filter/scriptlets_src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Scriptlets for AdblockAndroid",
3 | "version": "0.0.1",
4 | "description": "Uglify scriptlets.js for AdblockAndroid",
5 | "main": "none",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/Edsuns/AdblockAndroid.git"
12 | },
13 | "keywords": [
14 | "scriptlets"
15 | ],
16 | "author": "Edsuns@qq.com",
17 | "license": "UNLICENSED",
18 | "bugs": {
19 | "url": "https://github.com/Edsuns/AdblockAndroid/issues"
20 | },
21 | "homepage": "https://github.com/Edsuns/AdblockAndroid#readme",
22 | "devDependencies": {
23 | "gulp": "^4.0.2",
24 | "gulp-uglify": "^3.0.2",
25 | "pump": "^3.0.0",
26 | "uglify-es": "^3.3.9",
27 | "split-file": "^2.2.1"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ad-filter/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ad-filter/src/main/java/io/github/edsuns/adfilter/CustomFilter.kt:
--------------------------------------------------------------------------------
1 | package io.github.edsuns.adfilter
2 |
3 | /**
4 | * Created by Edsuns@qq.com on 2021/1/24.
5 | */
6 | interface CustomFilter : Iterator {
7 | /**
8 | * Get specified line of rule.
9 | */
10 | fun get(line: Int): String
11 |
12 | /**
13 | * @return true if custom contains the [rule]
14 | */
15 | fun contains(rule: String): Boolean
16 |
17 | /**
18 | * Add a new rule.
19 | */
20 | fun append(rule: String)
21 |
22 | /**
23 | * @return true if [rule] is a comment (starts with `! `)
24 | */
25 | fun isComment(rule: String): Boolean
26 |
27 | /**
28 | * Make the existing rule as a comment.
29 | */
30 | fun comment(rule: String)
31 |
32 | /**
33 | * Make the existing comment as a rule.
34 | */
35 | fun uncomment(rule: String)
36 |
37 | /**
38 | * Remove specified rule from custom filter.
39 | */
40 | fun remove(rule: String)
41 |
42 | /**
43 | * @return the number of rules in custom filter
44 | */
45 | fun size(): Int
46 |
47 | /**
48 | * Save the changes.
49 | */
50 | fun flush()
51 | }
--------------------------------------------------------------------------------
/ad-filter/src/main/java/io/github/edsuns/adfilter/Filter.kt:
--------------------------------------------------------------------------------
1 | package io.github.edsuns.adfilter
2 |
3 | import io.github.edsuns.adfilter.util.sha1
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Created by Edsuns@qq.com on 2021/1/1.
8 | */
9 | @Serializable
10 | data class Filter(
11 | val url: String,
12 | val name: String = "",
13 | val isEnabled: Boolean = false,
14 | val downloadState: DownloadState = DownloadState.NONE,
15 | val updateTime: Long = -1L,
16 | val filtersCount: Int = 0,
17 | val checksum: String = "",
18 | ) {
19 | val id by lazy { url.sha1 }
20 | fun hasDownloaded() = updateTime > 0
21 | }
22 |
23 | enum class DownloadState {
24 | ENQUEUED, DOWNLOADING, INSTALLING, SUCCESS, FAILED, CANCELLED, NONE;
25 |
26 | val isRunning
27 | get() = when (this) {
28 | ENQUEUED, DOWNLOADING, INSTALLING -> true
29 | else -> false
30 | }
31 | }
--------------------------------------------------------------------------------
/ad-filter/src/main/java/io/github/edsuns/adfilter/FilterResult.kt:
--------------------------------------------------------------------------------
1 | package io.github.edsuns.adfilter
2 |
3 | import android.webkit.WebResourceResponse
4 |
5 | /**
6 | * Created by Edsuns@qq.com on 2021/1/24.
7 | */
8 | data class FilterResult(
9 | val rule: String?,
10 | val resourceUrl: String,
11 | val resourceResponse: WebResourceResponse?,
12 | val shouldBlock: Boolean = rule != null
13 | )
14 |
--------------------------------------------------------------------------------
/ad-filter/src/main/java/io/github/edsuns/adfilter/impl/BinaryDataStore.kt:
--------------------------------------------------------------------------------
1 | package io.github.edsuns.adfilter.impl
2 |
3 | import timber.log.Timber
4 | import java.io.File
5 |
6 | /**
7 | * Created by Edsuns@qq.com on 2020/10/24.
8 | */
9 | internal class BinaryDataStore(private val dir: File) {
10 |
11 | init {
12 | if (!dir.exists() && !dir.mkdirs()) {
13 | Timber.v("BinaryDataStore: failed to create store dirs")
14 | }
15 | }
16 |
17 | fun hasData(name: String): Boolean = File(dir, name).exists()
18 |
19 | fun loadData(name: String): ByteArray =
20 | File(dir, name).readBytes()
21 |
22 | fun saveData(name: String, byteArray: ByteArray) {
23 | File(dir, name).writeBytes(byteArray)
24 | }
25 |
26 | fun clearData(name: String) {
27 | File(dir, name).delete()
28 | }
29 | }
--------------------------------------------------------------------------------
/ad-filter/src/main/java/io/github/edsuns/adfilter/impl/Constants.kt:
--------------------------------------------------------------------------------
1 | package io.github.edsuns.adfilter.impl
2 |
3 | /**
4 | * Created by Edsuns@qq.com on 2021/1/1.
5 | */
6 | internal object Constants {
7 | const val FILE_STORE_DIR = "filter_data"
8 | const val KEY_FILTER_ID = "KEY_FILTER_ID"
9 | const val KEY_DOWNLOAD_URL = "KEY_DOWNLOAD_URL"
10 | const val KEY_DOWNLOADED_DATA = "KEY_DOWNLOADED_DATA"
11 | const val TAG_INSTALLATION = "TAG_INSTALLATION"
12 | const val KEY_FILTER_NAME = "KEY_FILTER_NAME"
13 | const val KEY_FILTERS_COUNT = "KEY_FILTERS_COUNT"
14 | const val KEY_RAW_CHECKSUM = "KEY_RAW_SHA_256"
15 | const val KEY_ALREADY_UP_TO_DATE = "KEY_ALREADY_UP_TO_DATE"
16 | const val KEY_CHECK_LICENSE = "KEY_CHECK_LICENSE"
17 | }
--------------------------------------------------------------------------------
/ad-filter/src/main/java/io/github/edsuns/adfilter/impl/CustomFilterImpl.kt:
--------------------------------------------------------------------------------
1 | package io.github.edsuns.adfilter.impl
2 |
3 | import io.github.edsuns.adfilter.CustomFilter
4 | import io.github.edsuns.adfilter.util.RuleIterator
5 |
6 | /**
7 | * Created by Edsuns@qq.com on 2021/7/29.
8 | */
9 | internal class CustomFilterImpl constructor(
10 | private val filterDataLoader: FilterDataLoader,
11 | data: String? = null
12 | ) : CustomFilter, RuleIterator(data) {
13 |
14 | override fun flush() {
15 | val blacklistStr = dataBuilder.toString()
16 | if (blacklistStr.isNotBlank()) {
17 | val rawData = blacklistStr.toByteArray()
18 | filterDataLoader.loadCustomFilter(rawData)
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/ad-filter/src/main/java/io/github/edsuns/adfilter/impl/FilterSharedPreferences.kt:
--------------------------------------------------------------------------------
1 | package io.github.edsuns.adfilter.impl
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import androidx.core.content.edit
6 | import kotlinx.serialization.decodeFromString
7 | import kotlinx.serialization.encodeToString
8 | import kotlinx.serialization.json.Json
9 |
10 | /**
11 | * Created by Edsuns@qq.com on 2021/1/1.
12 | */
13 | internal class FilterSharedPreferences(private val context: Context) {
14 |
15 | val hasInstallation: Boolean
16 | get() = preferences.contains(KEY_FILTER_MAP)
17 |
18 | var isEnabled: Boolean
19 | get() = preferences.getBoolean(KEY_ENABLED, true)
20 | set(value) = preferences.edit { putBoolean(KEY_ENABLED, value) }
21 |
22 | var filterMap: String
23 | get() = preferences.getString(KEY_FILTER_MAP, "{}")!!
24 | set(value) = preferences.edit(commit = true) { putString(KEY_FILTER_MAP, value) }
25 |
26 | var downloadFilterIdMap: HashMap
27 | get() = Json.decodeFromString(preferences.getString(KEY_DOWNLOAD_FILTER_ID_MAP, "{}")!!)
28 | set(value) = preferences.edit {
29 | putString(
30 | KEY_DOWNLOAD_FILTER_ID_MAP,
31 | Json.encodeToString(value)
32 | )
33 | }
34 |
35 | private val preferences: SharedPreferences
36 | get() = context.getSharedPreferences(FILENAME, Context.MODE_PRIVATE)
37 |
38 | companion object {
39 | private const val FILENAME = "io.github.edsuns.filter"
40 | private const val KEY_FILTER_MAP = "filter_map"
41 | private const val KEY_ENABLED = "filter_enabled"
42 | private const val KEY_DOWNLOAD_FILTER_ID_MAP = "download_filter_id_map"
43 | }
44 | }
--------------------------------------------------------------------------------
/ad-filter/src/main/java/io/github/edsuns/adfilter/util/Checksum.kt:
--------------------------------------------------------------------------------
1 | package io.github.edsuns.adfilter.util
2 |
3 | import android.util.Base64
4 |
5 | /**
6 | * Created by Edsuns@qq.com on 2021/1/7.
7 | *
8 | * Reference: [validateChecksum.py](https://hg.adblockplus.org/adblockplus/file/tip/validateChecksum.py)
9 | */
10 | class Checksum(val filter: String) {
11 | // here don't use '$' as the end of a line
12 | private val checksumRegexp = Regex(
13 | "^\\s*!\\s*checksum[\\s\\-:]+([\\w+/=]+).*\\r?\\n",
14 | setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE)
15 | )
16 |
17 | val checksumIn: String? by lazy { extractChecksum(filter) }
18 | val checksumCalc: String by lazy { calculateChecksum(filter) }
19 |
20 | fun validate() = validate(checksumIn, checksumCalc)
21 |
22 | fun validate(checksumIn: String?) = validate(checksumIn, checksumCalc)
23 |
24 | private fun validate(checksumIn: String?, checksumCalc: String): Boolean =
25 | checksumIn == null || checksumIn == checksumCalc
26 |
27 | private fun extractChecksum(data: String): String? =
28 | checksumRegexp.find(data)?.groupValues?.get(1)
29 |
30 | private fun calculateChecksum(data: String): String =
31 | Base64.encodeToString(
32 | md5(normalize(data).toByteArray()),
33 | Base64.NO_PADDING or Base64.NO_WRAP
34 | )
35 |
36 | private fun normalize(data: String): String {
37 | var normalize = data.replace("\r", "")
38 | normalize = Regex("\n+").replace(normalize, "\n")
39 | normalize = checksumRegexp.replaceFirst(normalize, "")
40 | return normalize
41 | }
42 | }
--------------------------------------------------------------------------------
/ad-filter/src/main/java/io/github/edsuns/adfilter/util/HashUtilities.kt:
--------------------------------------------------------------------------------
1 | package io.github.edsuns.adfilter.util
2 |
3 | import java.math.BigInteger
4 | import java.security.MessageDigest
5 |
6 | val ByteArray.sha256: String
7 | get() = sha("SHA-256", this)
8 |
9 |
10 | fun ByteArray.verifySha256(sha256: String): Boolean {
11 | return this.sha256 == sha256
12 | }
13 |
14 | val String.sha1: String
15 | get() = sha("SHA-1", this.toByteArray())
16 |
17 | fun String.verifySha1(sha1: String): Boolean {
18 | return this.sha1 == sha1
19 | }
20 |
21 | private fun sha(algorithm: String, bytes: ByteArray): String {
22 | val digest = hash(algorithm, bytes)
23 | return String.format("%0" + digest.size * 2 + "x", BigInteger(1, digest))
24 | }
25 |
26 | fun md5(bytes: ByteArray): ByteArray {
27 | return hash("MD5", bytes)
28 | }
29 |
30 | private fun hash(algorithm: String, bytes: ByteArray): ByteArray {
31 | val md = MessageDigest.getInstance(algorithm)
32 | return md.digest(bytes)
33 | }
--------------------------------------------------------------------------------
/ad-filter/src/main/java/io/github/edsuns/adfilter/util/None.kt:
--------------------------------------------------------------------------------
1 | package io.github.edsuns.adfilter.util
2 |
3 | /**
4 | * Created by Edsuns@qq.com on 2021/3/31.
5 | */
6 | sealed class None {
7 | object Value : None()
8 | }
9 |
--------------------------------------------------------------------------------
/ad-filter/src/main/js/element_hiding.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | {{DEBUG}} console.log('element hiding started on ' + document.location.href);
3 |
4 | // hide by injecting CSS
5 | var styleSheet = {{BRIDGE}}.getStyleSheet(document.location.href);
6 | if (styleSheet.length) {
7 | {{DEBUG}} console.log('stylesheet length: ' + styleSheet.length);
8 | // Why `html` here? Because the css at the end of `html` usually has a higher priority.
9 | var html = document.getElementsByTagName('html')[0];
10 | var style = document.createElement('style');
11 | html.appendChild(style);
12 | style.textContent = styleSheet;
13 | {{DEBUG}} console.log('finished injecting stylesheet');
14 | } else {
15 | {{DEBUG}} console.log('stylesheet is empty, skipped');
16 | }
17 |
18 | // hide by ExtendedCss
19 | try {
20 | var css = {{BRIDGE}}.getExtendedCssStyleSheet(document.location.href);
21 | {{DEBUG}} console.log(`ExtendedCss rules(length: ${css.length}) injecting for ${document.location.href}`);
22 | if (css.length > 0) {
23 | var extendedCss = new ExtendedCss({ styleSheet: css });
24 | extendedCss.apply();
25 | }
26 | {{DEBUG}} console.log(`ExtendedCss rules success for ${document.location.href}`);
27 | } catch (err) {
28 | {{DEBUG}} console.log(`ExtendedCss rules failed '${css}' for ${document.location.href} by ${err}`);
29 | throw err;
30 | }
31 |
32 | {{DEBUG}} console.log('element hiding finished');
33 | })();
--------------------------------------------------------------------------------
/ad-filter/src/main/js/scriptlets_inject.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var scriptletArray = JSON.parse({{BRIDGE}}.getScriptlets(document.location.href));
3 | for (let item of scriptletArray) {
4 | let script = scriptlets.invoke({
5 | name: item[0],
6 | args: item.slice(1)
7 | });
8 | {{DEBUG}} !script && console.log(`invalid scriptlets: ${JSON.stringify(item)}`);
9 | try {
10 | // don't use eval() here, it may be blocked by scriptlets
11 | new Function(script)();
12 | } catch (err) {
13 | {{DEBUG}} console.log('scriptlets went wrong: ' + err);
14 | throw err;
15 | }
16 | }
17 | {{DEBUG}} console.log(`applied ${scriptletArray.length} scriptlets for ${document.location.href}`);
18 | })();
--------------------------------------------------------------------------------
/adblock-client/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | .cxx
3 |
--------------------------------------------------------------------------------
/adblock-client/consumer-rules.pro:
--------------------------------------------------------------------------------
1 | # keep function, which will be called by native lib
2 | -keepclassmembers class io.github.edsuns.adblockclient.MatchResult { (boolean,java.lang.String,java.lang.String); }
3 |
--------------------------------------------------------------------------------
/adblock-client/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/adblock-client/src/androidTest/resources/binary/easylist_sample:
--------------------------------------------------------------------------------
1 | [Adblock Plus 2.0]
2 | ! Version: 201711011335
3 | ! Title: EasyList
4 | ! Last modified: 01 Nov 2017 13:35 UTC
5 | ! Expires: 4 days (update frequency)
6 | ! Homepage: https://easylist.to/
7 | ! Licence: https://easylist.to/pages/licence.html
8 | !
9 | ! Please report any unblocked adverts or problems
10 | ! in the forums (https://forums.lanik.us/)
11 | ! or via e-mail (easylist.subscription@gmail.com).
12 | ! GitHub issues: https://github.com/easylist/easylist/issues
13 | ! GitHub pull requests: https://github.com/easylist/easylist/pulls
14 | !
15 | ! -----------------------General advert blocking filters-----------------------!
16 | ! *** easylist:easylist/easylist_general_block.txt ***
17 | ###videoad
18 | ###videoads
19 | example.com##.video_ads
20 | example.com#@##videoads
21 | sub.example.com#@##videoad
22 | ||imasdk.googleapis.com^$third-party
23 | /:[0-9]{4,6}\/[a-z]{2}\/[a-z]-[0-9]{2,4}-[0-9]{2,3}-[0-9]\.html/$subdocument
24 | ||exception-rule.com^
25 | @@||exception-rule.com/a/b/info
26 |
--------------------------------------------------------------------------------
/adblock-client/src/androidTest/resources/binary/easyprivacy_sample:
--------------------------------------------------------------------------------
1 | [Adblock Plus 1.1]
2 | ! Version: 201711011335
3 | ! Title: EasyPrivacy
4 | ! Last modified: 01 Nov 2017 13:35 UTC
5 | ! Expires: 4 days (update frequency)
6 | ! Homepage: https://easylist.to/
7 | ! Licence: https://easylist.to/pages/licence.html
8 | !
9 | ! Please report any unblocked tracking or problems
10 | ! in the forums (https://forums.lanik.us/)
11 | ! or via e-mail (easylist.subscription@gmail.com).
12 | !
13 | ! -----------------General tracking systems-----------------!
14 | ! *** easylist:easyprivacy/easyprivacy_general.txt ***
15 | /TagCommander.cfc?
16 | /tagcommander/*
17 | ||tagcommander.com^$third-party
18 | ||tagcommander.laredoute.
19 | @@||tagcommander.com^*/tc_$script
20 | @@||cdscdn.com/Js/external/tagcommander/tc_nav.js$domain=cdiscount.com
21 |
--------------------------------------------------------------------------------
/adblock-client/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/adblock-client/src/main/cpp/third-party/ad-block/bad_fingerprints.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | const char *badSubstrings[] = {"http", "www"};
4 |
5 | // BadFingerprints exclusion is not reliable and appreciable for performance optimization.
6 | // Disable it temporarily.
7 | #ifndef ENABLE_BadFingerprints_Exclusion
8 | const char *badFingerprints[] = {};
9 | #else
10 | const char *badFingerprints[] = {};
11 | #endif
12 |
--------------------------------------------------------------------------------
/adblock-client/src/main/cpp/third-party/ad-block/base.h:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /**
7 | * Modified by Edsuns@qq.com.
8 | * Origin: https://github.com/brave/ad-block
9 | */
10 |
11 | #ifndef BASE_H_
12 | #define BASE_H_
13 |
14 | #if !defined(nullptr) && !defined(_MSC_VER)
15 | #define nullptr 0
16 | #endif
17 |
18 | #include
19 |
20 | #if defined(_MSC_VER) && _MSC_VER < 1900
21 | #include
22 | #include
23 | #define snprintf c99_snprintf
24 | #define vsnprintf c99_vsnprintf
25 | inline int c99_vsnprintf(char *outBuf, size_t size,
26 | const char *format, va_list ap) {
27 | int count = -1;
28 | if (size != 0) {
29 | count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
30 | }
31 | if (count == -1) {
32 | count = _vscprintf(format, ap);
33 | }
34 | return count;
35 | }
36 |
37 | inline int c99_snprintf(char *outBuf, size_t size,
38 | const char *format, ...) {
39 | int count;
40 | va_list ap;
41 | va_start(ap, format);
42 | count = c99_vsnprintf(outBuf, size, format, ap);
43 | va_end(ap);
44 | return count;
45 | }
46 | #endif
47 |
48 | #endif // BASE_H_
49 |
--------------------------------------------------------------------------------
/adblock-client/src/main/cpp/third-party/ad-block/context_domain.cc:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /**
7 | * Modified by Edsuns@qq.com.
8 | * Origin: https://github.com/brave/ad-block
9 | */
10 |
11 | #include "./context_domain.h"
12 | #include "../hashset-cpp/hashFn.h"
13 |
14 | static HashFn context_domain_hash(19);
15 |
16 | uint64_t ContextDomain::GetHash() const {
17 | return context_domain_hash(start_, len_);
18 | }
19 |
--------------------------------------------------------------------------------
/adblock-client/src/main/cpp/third-party/ad-block/cosmetic_filter.cc:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /**
7 | * Modified by Edsuns@qq.com.
8 | * Origin: https://github.com/brave/ad-block
9 | */
10 |
11 | #include "./cosmetic_filter.h"
12 | #include "../hashset-cpp/hashFn.h"
13 |
14 | static HashFn fn(19);
15 |
16 | uint64_t CosmeticFilter::hash() const {
17 | return fn(data, static_cast(strlen(data)));
18 | }
19 |
--------------------------------------------------------------------------------
/adblock-client/src/main/cpp/third-party/ad-block/no_fingerprint_domain.h:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /**
7 | * Modified by Edsuns@qq.com.
8 | * Origin: https://github.com/brave/ad-block
9 | */
10 |
11 | #ifndef NO_FINGERPRINT_DOMAIN_H_
12 | #define NO_FINGERPRINT_DOMAIN_H_
13 |
14 | #include "./base.h"
15 |
16 | class NoFingerprintDomain {
17 | public:
18 | NoFingerprintDomain();
19 |
20 | NoFingerprintDomain(const NoFingerprintDomain &other);
21 |
22 | NoFingerprintDomain(const char *data, int dataLen);
23 |
24 | ~NoFingerprintDomain();
25 |
26 | uint64_t hash() const;
27 |
28 | uint64_t GetHash() const {
29 | return hash();
30 | }
31 |
32 | uint32_t Serialize(char *buffer);
33 |
34 | uint32_t Deserialize(char *buffer, uint32_t bufferSize);
35 |
36 | // Nothing needs to be updated when being added multiple times
37 | void Update(const NoFingerprintDomain &) {}
38 |
39 | bool operator==(const NoFingerprintDomain &rhs) const;
40 |
41 | private:
42 | // Holds true if the data should not free memory because for example it
43 | // was loaded from a large buffer somewhere else via the serialize and
44 | // deserialize functions.
45 | bool borrowed_data;
46 | char *data;
47 | int dataLen;
48 | };
49 |
50 | #endif // NO_FINGERPRINT_DOMAIN_H_
51 |
--------------------------------------------------------------------------------
/adblock-client/src/main/cpp/third-party/ad-block/protocol.h:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /**
7 | * Modified by Edsuns@qq.com.
8 | * Origin: https://github.com/brave/ad-block
9 | */
10 |
11 | #ifndef PROTOCOL_H_
12 | #define PROTOCOL_H_
13 |
14 | // Checks whether the URL can be blocked by ABP, based on its protocol.
15 | //
16 | // We only apply ABP rules against certain protocols (http, https, ws, wss).
17 | // This function checks to see if the given url is of one of these protocol.
18 | // For the purposes of this function, blob indicators are ignored (e.g.
19 | // "blob:http://" is treated the same as "http://").
20 | bool isBlockableProtocol(const char *url, int urlLen);
21 |
22 | #endif // PROTOCOL_H_
23 |
--------------------------------------------------------------------------------
/adblock-client/src/main/cpp/third-party/bloom-filter-cpp/base.h:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /**
7 | * Modified by Edsuns@qq.com.
8 | * Origin: https://github.com/brave/ad-block
9 | */
10 |
11 | #ifndef BASE_H_
12 | #define BASE_H_
13 |
14 | #if !defined(nullptr) && !defined(_MSC_VER)
15 | #define nullptr 0
16 | #endif
17 |
18 | #include
19 |
20 | #endif // BASE_H_
21 |
--------------------------------------------------------------------------------
/adblock-client/src/main/cpp/third-party/hashset-cpp/base.h:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /**
7 | * Modified by Edsuns@qq.com.
8 | * Origin: https://github.com/brave/ad-block
9 | */
10 |
11 | #ifndef BASE_H_
12 | #define BASE_H_
13 |
14 | #if !defined(nullptr) && !defined(_MSC_VER)
15 | #define nullptr 0
16 | #endif
17 |
18 | #if defined(_MSC_VER) && _MSC_VER < 1900
19 | #include
20 | #define snprintf c99_snprintf
21 | #define vsnprintf c99_vsnprintf
22 | inline int c99_vsnprintf(char *outBuf, size_t size,
23 | const char *format, va_list ap) {
24 | int count = -1;
25 | if (size != 0) {
26 | count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
27 | }
28 | if (count == -1) {
29 | count = _vscprintf(format, ap);
30 | }
31 | return count;
32 | }
33 |
34 | inline int c99_snprintf(char *outBuf, size_t size,
35 | const char *format, ...) {
36 | int count;
37 | va_list ap;
38 | va_start(ap, format);
39 | count = c99_vsnprintf(outBuf, size, format, ap);
40 | va_end(ap);
41 | return count;
42 | }
43 | #endif
44 |
45 | #endif // BASE_H_
46 |
--------------------------------------------------------------------------------
/adblock-client/src/main/cpp/third-party/hashset-cpp/hashFn.cc:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /**
7 | * Modified by Edsuns@qq.com.
8 | * Origin: https://github.com/brave/ad-block
9 | */
10 |
11 | #include "./hashFn.h"
12 |
13 | uint64_t HashFn::operator()(const char *input, int len,
14 | unsigned char lastCharCode, uint64_t lastHash) {
15 | // See the abracadabra example:
16 | // https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm
17 | return (lastHash - lastCharCode *
18 | customPow(&precomputedPowers, precompute, p, len - 1)) *
19 | p + input[len - 1];
20 | }
21 |
22 | uint64_t HashFn::operator()(const char *input, int len) {
23 | uint64_t total = 0;
24 | for (int i = 0; i < len; i++) {
25 | total += input[i] *
26 | customPow(&precomputedPowers, precompute, p, len - i - 1);
27 | }
28 | return total;
29 | }
30 |
--------------------------------------------------------------------------------
/adblock-client/src/main/cpp/third-party/hashset-cpp/hash_item.h:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /**
7 | * Modified by Edsuns@qq.com.
8 | * Origin: https://github.com/brave/ad-block
9 | */
10 |
11 | #ifndef HASH_ITEM_H_
12 | #define HASH_ITEM_H_
13 |
14 | #include "./base.h"
15 |
16 | template
17 | class HashItem {
18 | public:
19 | HashItem() : next_(nullptr), hash_item_storage_(nullptr) {
20 | }
21 |
22 | ~HashItem() {
23 | if (hash_item_storage_) {
24 | delete hash_item_storage_;
25 | }
26 | }
27 |
28 | HashItem *next_;
29 | T *hash_item_storage_;
30 | };
31 |
32 | #endif // HASH_ITEM_H_
33 |
--------------------------------------------------------------------------------
/adblock-client/src/main/cpp/third-party/hashset-cpp/hash_set.cc:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
2 | * This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | #include "./hash_set.h"
7 |
--------------------------------------------------------------------------------
/adblock-client/src/main/java/io/github/edsuns/adblockclient/Client.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 DuckDuckGo
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.github.edsuns.adblockclient
18 |
19 | /**
20 | * Modified by Edsuns@qq.com.
21 | *
22 | * Description: In `duckduckgo/Android`, `tag/5.38.1` is the last version that has the implement of AdBlockClient.
23 | *
24 | * Reference: [github.com/duckduckgo/Android/releases/tag/5.38.1](https://github.com/duckduckgo/Android/releases/tag/5.38.1)
25 | */
26 | interface Client {
27 |
28 | val id: String
29 |
30 | var isGenericElementHidingEnabled: Boolean
31 |
32 | fun matches(url: String, documentUrl: String, resourceType: ResourceType): MatchResult
33 |
34 | fun getElementHidingSelectors(url: String): String?
35 |
36 | fun getExtendedCssSelectors(url: String): Array?
37 |
38 | fun getCssRules(url: String): Array?
39 |
40 | fun getScriptlets(url: String): Array?
41 |
42 | }
--------------------------------------------------------------------------------
/adblock-client/src/main/java/io/github/edsuns/adblockclient/MatchResult.kt:
--------------------------------------------------------------------------------
1 | package io.github.edsuns.adblockclient
2 |
3 | /**
4 | * Created by Edsuns@qq.com on 2021/1/23.
5 | */
6 | data class MatchResult(
7 | /**
8 | * true if has matched rule and no matched exception rule
9 | */
10 | val shouldBlock: Boolean,
11 |
12 | /**
13 | * rule text is "-" if loading processed data without preserveRules enabled
14 | */
15 | val matchedRule: String?,
16 |
17 | /**
18 | * rule text is "-" if loading processed data without preserveRules enabled
19 | */
20 | val matchedExceptionRule: String?
21 | )
22 |
23 | val MatchResult.hasException: Boolean get() = matchedExceptionRule != null
24 |
--------------------------------------------------------------------------------
/app/proguard-rules.txt:
--------------------------------------------------------------------------------
1 | -dontwarn org.xmlpull.v1.**
2 | -keep class org.xmlpull.v1.** {*;}
3 |
4 | -dontwarn org.kxml2.io.**
5 | -dontwarn android.content.res.**
6 | -dontwarn org.slf4j.impl.StaticLoggerBinder
7 |
8 | -keep class org.xmlpull.** { *; }
9 | -keepclassmembers class org.xmlpull.** { *; }
10 |
11 | -assumenosideeffects class android.util.Log {
12 | public static boolean isLoggable(java.lang.String, int);
13 | public static int v(...);
14 | public static int d(...);
15 | public static int i(...);
16 | }
17 |
18 | -keepnames class <1>$$serializer {static <1>$$serializer INSTANCE;}
--------------------------------------------------------------------------------
/app/src/main/assets/fix_scrolling.js:
--------------------------------------------------------------------------------
1 | function findScrollableParent(element) {
2 | if (!element) {
3 | return document.scrollingElement || document.documentElement;
4 | }
5 |
6 | if (element.scrollHeight > element.clientHeight) {
7 | const overflowY = window.getComputedStyle(element).overflowY;
8 | if (overflowY !== 'visible' && overflowY !== 'hidden') {
9 | return element;
10 | }
11 | }
12 |
13 | return findScrollableParent(element.parentElement);
14 | }
15 |
16 | function scrollPage(direction) {
17 | const scrollable = findScrollableParent(document.activeElement);
18 | const scrollAmount = direction * scrollable.clientHeight * (1 - %s) - %s;
19 | scrollable.scrollBy({
20 | top: scrollAmount,
21 | left: 0,
22 | behavior: 'auto'
23 | });
24 | }
25 |
26 | window.addEventListener('keydown', function(e) {
27 | if (e.key === 'PageDown' || e.keyCode === 34) {
28 | e.preventDefault();
29 | scrollPage(1);
30 | } else if (e.key === 'PageUp' || e.keyCode === 33) {
31 | e.preventDefault();
32 | scrollPage(-1);
33 | }
34 | }, true);
35 |
--------------------------------------------------------------------------------
/app/src/main/assets/get_selected_text_with_context.js:
--------------------------------------------------------------------------------
1 | let contextLength = 120;
2 | let selection = window.getSelection();
3 | if (selection.rangeCount === 0) return "";
4 |
5 | let range = selection.getRangeAt(0);
6 | let startContainer = range.startContainer;
7 | let endContainer = range.endContainer;
8 |
9 | // Handle the case where the selected text spans multiple nodes
10 | if (startContainer !== endContainer) {
11 | return ""; // For simplicity, not handling multi-node selections here
12 | }
13 |
14 | let textContent = startContainer.textContent;
15 | let startOffset = range.startOffset;
16 | let endOffset = range.endOffset;
17 |
18 | // Extend previousContext to the previous ".", "。", "?", or "!"
19 | let contextStartPos = startOffset;
20 | while (contextStartPos > 0 && ![".", "。", "?", "!"].includes(textContent[contextStartPos - 1])) {
21 | contextStartPos--;
22 | if (startOffset - contextStartPos > contextLength) {
23 | break;
24 | }
25 | }
26 |
27 | // Extend nextContext to the next ".", "?", or "。"
28 | let contextEndPos = endOffset;
29 | while (contextEndPos < textContent.length && ![".", "?", "。"].includes(textContent[contextEndPos])) {
30 | contextEndPos++;
31 | if (contextEndPos - endOffset > contextLength) {
32 | break;
33 | }
34 | }
35 |
36 | let previousContext = textContent.substring(contextStartPos, startOffset);
37 | let nextContext = textContent.substring(endOffset, contextEndPos+1);
38 |
39 | let selectedText = selection.toString();
40 | return previousContext + "<<" + selectedText + ">>" + nextContext;
41 |
--------------------------------------------------------------------------------
/app/src/main/assets/highlight.css:
--------------------------------------------------------------------------------
1 | /* highlight.js */
2 |
3 | .highlight_underline {
4 | text-decoration: underline !important;
5 | text-underline-offset: 3px;
6 | display: inline;
7 | }
8 |
9 | .highlight_yellow {
10 | background-color: yellow !important;
11 | display: inline;
12 | }
13 |
14 | .highlight_green {
15 | background-color: lightgreen !important;
16 | display: inline;
17 | }
18 |
19 | .highlight_blue {
20 | background-color: lightblue !important;
21 | display: inline;
22 | }
23 |
24 | .highlight_pink {
25 | background-color: pink !important;
26 | display: inline;
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/assets/select_paragraph.js:
--------------------------------------------------------------------------------
1 | let selection = window.getSelection();
2 | if (selection.rangeCount === 0) return;
3 |
4 | let range = selection.getRangeAt(0);
5 | let startContainer = range.startContainer;
6 | let endContainer = range.endContainer;
7 |
8 | // Check if the selection is within a single text node
9 | if (startContainer !== endContainer || startContainer.nodeType !== Node.TEXT_NODE) {
10 | return;
11 | }
12 |
13 | let textContent = startContainer.textContent;
14 | let startOffset = range.startOffset;
15 | let endOffset = range.endOffset;
16 |
17 | let paragraphStart = startOffset;
18 | let paragraphEnd = endOffset;
19 |
20 | // Move the start of the range to the start of the paragraph (i.e., look for newline or start of the node)
21 | while (paragraphStart > 0 && textContent[paragraphStart - 1] !== '\n') {
22 | paragraphStart--;
23 | }
24 |
25 | // Move the end of the range to the end of the paragraph (i.e., look for newline or end of the node)
26 | while (paragraphEnd < textContent.length && textContent[paragraphEnd] !== '\n') {
27 | paragraphEnd++;
28 | }
29 |
30 | // Set the range to the paragraph boundaries
31 | range.setStart(startContainer, paragraphStart);
32 | range.setEnd(startContainer, paragraphEnd);
33 |
34 | // Clear previous selection and set the new one
35 | selection.removeAllRanges();
36 | selection.addRange(range);
37 |
--------------------------------------------------------------------------------
/app/src/main/assets/select_sentence.js:
--------------------------------------------------------------------------------
1 | let selection = window.getSelection();
2 | if (selection.rangeCount === 0) return;
3 |
4 | let range = selection.getRangeAt(0);
5 | let startContainer = range.startContainer;
6 | let endContainer = range.endContainer;
7 |
8 | if (startContainer !== endContainer || startContainer.nodeType !== Node.TEXT_NODE) {
9 | // Only handle cases where the selection is within a single text node
10 | return;
11 | }
12 |
13 | let textContent = startContainer.textContent;
14 | let startOffset = range.startOffset;
15 | let endOffset = range.endOffset;
16 |
17 | let sentenceStart = startOffset;
18 | let sentenceEnd = endOffset;
19 |
20 | // Move the start of the range to the start of the sentence
21 | while (sentenceStart > 0 && ![".", "?", "。", "!"].includes(textContent[sentenceStart - 1])) {
22 | sentenceStart--;
23 | }
24 |
25 | // Move the end of the range to the end of the sentence
26 | while (sentenceEnd < textContent.length && ![".", "?", "。", "!"].includes(textContent[sentenceEnd])) {
27 | sentenceEnd++;
28 | }
29 |
30 | // Set the range to the sentence boundaries
31 | range.setStart(startContainer, sentenceStart);
32 | range.setEnd(startContainer, sentenceEnd);
33 |
34 | // Clear previous selection and set the new one
35 | selection.removeAllRanges();
36 | selection.addRange(range);
37 |
--------------------------------------------------------------------------------
/app/src/main/assets/text_node_monitor.js:
--------------------------------------------------------------------------------
1 | function myCallback(elementId, responseString) {
2 | //console.log("Element ID:", elementId, "Response string:", responseString);
3 | node = document.getElementById(elementId).nextElementSibling;
4 | node.textContent = responseString;
5 | node.classList.add("translated");
6 | }
7 |
8 | // Create a new IntersectionObserver object
9 | observer = new IntersectionObserver((entries) => {
10 | entries.forEach((entry) => {
11 | // Check if the target node is currently visible
12 | if (entry.isIntersecting) {
13 | //console.log('Node is visible:', entry.target.textContent);
14 | const nextNode = entry.target.nextElementSibling;
15 | //nextNode.textContent = result;
16 | if (nextNode && nextNode.textContent === "") {
17 | androidApp.getTranslation(entry.target.textContent, entry.target.id, "myCallback");
18 | }
19 | } else {
20 | // The target node is not visible
21 | //console.log('Node is not visible');
22 | }
23 | });
24 | });
25 |
26 | // Select all elements with class name 'to-translate'
27 | targetNodes = document.querySelectorAll('.to-translate');
28 |
29 | // Loop through each target node and start observing it
30 | targetNodes.forEach((targetNode) => {
31 | observer.observe(targetNode);
32 | });
33 |
--------------------------------------------------------------------------------
/app/src/main/assets/text_selection_change.js:
--------------------------------------------------------------------------------
1 | var selectedText = "";
2 | function getSelectionPositionInWebView() {
3 | let selection = window.getSelection();
4 |
5 | if (selection) {
6 | let range = selection.getRangeAt(0);
7 | let startNode = range.startContainer;
8 | let startOffset = range.startOffset;
9 | let endNode = range.endContainer;
10 | let endOffset = range.endOffset;
11 |
12 | let start = getRectInWebView(startNode, startOffset);
13 | let end = getRectInWebView(endNode, endOffset);
14 |
15 | // Send anchor position to Android
16 | if (selection.toString() != selectedText) {
17 | selectedText = selection.toString();
18 | if (selectedText.length > 0) {
19 | androidApp.getAnchorPosition(start.left, start.top, end.right, end.bottom);
20 | }
21 | }
22 | }
23 | }
24 |
25 | function getRectInWebView(node, offset) {
26 | let range = document.createRange();
27 | range.setStart(node, offset);
28 | range.setEnd(node, offset);
29 | let rect = range.getBoundingClientRect();
30 |
31 | return rect;
32 | }
33 |
34 | // Call the function to get selection position
35 | document.addEventListener("selectionchange", function() {
36 | getSelectionPositionInWebView();
37 | });
38 |
--------------------------------------------------------------------------------
/app/src/main/assets/translate_processing.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Processing...
5 |
20 |
21 |
22 | %s
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/assets/translated_image.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/assets/video_auto_fullscreen.js:
--------------------------------------------------------------------------------
1 | var element = document.querySelector("video");
2 | element.addEventListener("playing", function() {
3 | if (element.requestFullscreen) {
4 | element.requestFullscreen();
5 | } else if (element.webkitRequestFullscreen) {
6 | element.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
7 | }
8 | }, false);
9 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/activity/ExtraBrowserActivity.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.activity
2 |
3 | class ExtraBrowserActivity : BrowserActivity() {
4 | override var shouldRunClearService: Boolean = false
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/browser/AlbumController.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.browser
2 |
3 | import info.plateaukao.einkbro.view.Album
4 |
5 | interface AlbumController {
6 | val album: Album
7 | var albumTitle: String
8 | val albumUrl: String
9 | var initAlbumUrl: String
10 |
11 | var isTranslatePage: Boolean
12 |
13 | var isAIPage: Boolean
14 |
15 | fun activate()
16 | fun deactivate()
17 |
18 | fun pauseWebView()
19 | fun resumeWebView()
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/browser/BrowserContainer.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.browser
2 |
3 | import info.plateaukao.einkbro.view.EBWebView
4 | import java.util.*
5 |
6 | class BrowserContainer {
7 | private val list: MutableList = LinkedList()
8 | operator fun get(index: Int): AlbumController {
9 | return list[index]
10 | }
11 |
12 | fun add(controller: AlbumController) = list.add(controller)
13 |
14 | fun add(controller: AlbumController, index: Int) = list.add(index, controller)
15 |
16 | fun remove(controller: AlbumController) {
17 | (controller as EBWebView).destroy()
18 | list.remove(controller)
19 | }
20 |
21 | fun indexOf(controller: AlbumController?): Int = list.indexOf(controller)
22 |
23 | fun list(): List = list
24 |
25 | fun size(): Int = list.size
26 |
27 | fun isEmpty(): Boolean = list.isEmpty()
28 |
29 | fun clear() {
30 | for (albumController in list) {
31 | (albumController as EBWebView).destroy()
32 | }
33 | list.clear()
34 | }
35 |
36 | fun pauseAll() = list.forEach { it.pauseWebView() }
37 |
38 | fun resumeAll() = list.forEach { it.resumeWebView() }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/browser/DataItemInterface.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.browser
2 |
3 | interface DomainInterface {
4 | fun getDomains(): List
5 | fun addDomain(domain: String)
6 | fun deleteDomain(domain: String)
7 | fun deleteAllDomains()
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/browser/EBClickHandler.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.browser
2 |
3 | import android.os.Handler
4 | import android.os.Message
5 | import android.view.MotionEvent
6 | import info.plateaukao.einkbro.view.EBWebView
7 |
8 | class EBClickHandler(private val webView: EBWebView) : Handler() {
9 |
10 | var currentMotionEvent: MotionEvent? = null
11 |
12 | override fun handleMessage(message: Message) {
13 | super.handleMessage(message)
14 | webView.browserController?.onLongPress(message, currentMotionEvent)
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/browser/EBDownloadListener.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.browser
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.webkit.DownloadListener
6 | import info.plateaukao.einkbro.unit.BrowserUnit.download
7 |
8 | class EBDownloadListener(private val context: Context) : DownloadListener {
9 | override fun onDownloadStart(
10 | url: String,
11 | userAgent: String,
12 | contentDisposition: String,
13 | mimeType: String,
14 | contentLength: Long
15 | ) {
16 | if (context is Activity) {
17 | download(context, url, contentDisposition, mimeType)
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/browser/LongPressGestureListener.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.browser
2 |
3 | import android.view.GestureDetector.SimpleOnGestureListener
4 | import android.view.MotionEvent
5 | import info.plateaukao.einkbro.view.EBWebView
6 |
7 | class LongPressGestureListener(private var webView: EBWebView) : SimpleOnGestureListener() {
8 | private var longPress = true
9 |
10 |
11 | override fun onContextClick(e: MotionEvent): Boolean {
12 | return super.onContextClick(e)
13 | }
14 |
15 | override fun onLongPress(e: MotionEvent) {
16 | if (longPress) {
17 | webView.onLongPress(e)
18 | }
19 | }
20 |
21 | override fun onDoubleTapEvent(e: MotionEvent): Boolean {
22 | longPress = false
23 | return false
24 | }
25 |
26 | override fun onShowPress(e: MotionEvent) {
27 | longPress = true
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/caption/TimedText.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.caption
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class TimedText(
8 | @SerialName("wireMagic") val wireMagic: String,
9 | @SerialName("pens") val pens: List,
10 | @SerialName("wsWinStyles") val wsWinStyles: List,
11 | @SerialName("wpWinPositions") val wpWinPositions: List,
12 | @SerialName("events") val events: MutableList
13 | )
14 |
15 | @Serializable
16 | class Pen
17 |
18 | @Serializable
19 | data class WsWinStyle(
20 | @SerialName("mhModeHint") var mhModeHint: Int? = null,
21 | @SerialName("juJustifCode") val juJustifCode: Int? = null,
22 | @SerialName("sdScrollDir") var sdScrollDir: Int? = null
23 | )
24 |
25 | @Serializable
26 | data class WpWinPosition(
27 | @SerialName("apPoint") val apPoint: Int? = null,
28 | @SerialName("ahHorPos") val ahHorPos: Int? = null,
29 | @SerialName("avVerPos") val avVerPos: Int? = null,
30 | @SerialName("rcRows") val rcRows: Int? = null,
31 | @SerialName("ccCols") val ccCols: Int? = null
32 | )
33 |
34 | @Serializable
35 | data class Event(
36 | @SerialName("tStartMs") val tStartMs: Long = 0,
37 | @SerialName("dDurationMs") val dDurationMs: Long = 0,
38 | @SerialName("id") val id: Int = 0,
39 | @SerialName("wpWinPosId") val wpWinPosId: Int? = null,
40 | @SerialName("wsWinStyleId") val wsWinStyleId: Int? = null,
41 | @SerialName("wWinId") val wWinId: Int? = 1,
42 | @SerialName("segs") val segs: MutableList? = mutableListOf()
43 | )
44 |
45 | @Serializable
46 | data class Segment(
47 | @SerialName("utf8") var utf8: String,
48 | @SerialName("acAsrConf") val acAsrConf: Int = 0
49 | )
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/database/Bookmark.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.database
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity(tableName = "bookmarks")
7 | data class Bookmark(
8 | var title: String,
9 | var url: String,
10 | val isDirectory: Boolean = false,
11 | var parent: Int = 0,
12 | var order: Int = 0,
13 | ) {
14 | @PrimaryKey (autoGenerate = true)
15 | var id: Int = 0
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/database/ChatGptQuery.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.database
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 |
7 | @Entity(tableName = "chat_gpt_query")
8 | data class ChatGptQuery(
9 | var date: Long,
10 | var url: String,
11 | var model: String,
12 | var selectedText: String,
13 | var result: String,
14 | ) {
15 | @PrimaryKey(autoGenerate = true)
16 | var id: Int = 0
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/database/ChatGptQueryDao.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.database
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Delete
5 | import androidx.room.Insert
6 | import androidx.room.OnConflictStrategy
7 | import androidx.room.Query
8 | import kotlinx.coroutines.flow.Flow
9 |
10 | @Dao
11 | interface ChatGptQueryDao {
12 | @Query("SELECT * FROM chat_gpt_query ORDER BY date DESC")
13 | fun getAllChatGptQueries(): Flow>
14 |
15 | @Query("SELECT * FROM chat_gpt_query ORDER BY date DESC")
16 | suspend fun getAllChatGptQueriesAsync(): List
17 |
18 | @Query("SELECT * FROM chat_gpt_query WHERE id = :id")
19 | suspend fun getChatGptQueryById(id: Int): ChatGptQuery
20 |
21 | @Delete
22 | suspend fun deleteChatGptQuery(chatGptQuery: ChatGptQuery)
23 |
24 | @Insert(onConflict = OnConflictStrategy.REPLACE)
25 | suspend fun addChatGptQuery(chatGptQuery: ChatGptQuery)
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/database/DomainConfiguration.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.database
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 | import kotlinx.serialization.Serializable
6 |
7 | @Entity(tableName = "domain_configuration")
8 | data class DomainConfiguration(
9 | @PrimaryKey
10 | var domain: String,
11 | var configuration: String,
12 | )
13 |
14 | @Serializable
15 | data class DomainConfigurationData(
16 | val domain: String,
17 | var shouldFixScroll: Boolean = false,
18 | var shouldSendPageNavKey: Boolean = false,
19 | var shouldTranslateSite: Boolean = false,
20 | var shouldUseWhiteBackground: Boolean = false,
21 | var shouldInvertColor: Boolean = false
22 | )
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/database/DomainConfigurationDao.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.database
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.Query
6 | import androidx.room.Update
7 |
8 | @Dao
9 | interface DomainConfigurationDao {
10 | @Query("SELECT * FROM domain_configuration")
11 | suspend fun getAllDomainConfigurations(): List
12 |
13 | @Query("SELECT * FROM domain_configuration WHERE domain = :domain")
14 | suspend fun getDomainConfiguration(domain: String): DomainConfiguration?
15 |
16 | @Insert(onConflict = androidx.room.OnConflictStrategy.REPLACE)
17 | suspend fun addDomainConfiguration(domainConfiguration: DomainConfiguration)
18 |
19 | @Update
20 | suspend fun updateDomainConfiguration(domainConfiguration: DomainConfiguration)
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/database/FaviconInfo.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.database
2 |
3 | import android.graphics.Bitmap
4 | import android.graphics.BitmapFactory
5 | import androidx.room.ColumnInfo
6 | import androidx.room.Entity
7 | import androidx.room.PrimaryKey
8 |
9 |
10 | @Entity(tableName = "favicons")
11 | data class FaviconInfo(
12 | @PrimaryKey
13 | val domain: String,
14 | @ColumnInfo(typeAffinity = ColumnInfo.BLOB)
15 | var icon: ByteArray?
16 | ) {
17 | fun getBitmap(): Bitmap? {
18 | val icon = icon ?: return null
19 | return BitmapFactory.decodeByteArray(icon, 0, icon.size)
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/database/Highlight.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.database
2 |
3 | import androidx.room.Entity
4 | import androidx.room.Index
5 | import androidx.room.PrimaryKey
6 |
7 | @Entity(tableName = "highlights",
8 | indices = [Index(value = ["articleId"], unique = false)],
9 | foreignKeys = [
10 | androidx.room.ForeignKey(
11 | entity = Article::class,
12 | parentColumns = ["id"],
13 | childColumns = ["articleId"],
14 | onDelete = androidx.room.ForeignKey.CASCADE
15 | )
16 | ]
17 | )
18 |
19 | data class Highlight(
20 | var articleId: Int,
21 | var content: String,
22 | ) {
23 | @PrimaryKey (autoGenerate = true)
24 | var id: Int = 0
25 | }
26 |
27 | @Entity(tableName = "articles")
28 | data class Article(
29 | var title: String,
30 | var url: String,
31 | var date: Long,
32 | var tags: String,
33 | ) {
34 | @PrimaryKey (autoGenerate = true)
35 | var id: Int = 0
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/database/History.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.database
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity(tableName = "history")
7 | data class History(
8 | var title: String,
9 | var url: String,
10 | var time: Int
11 | ) {
12 | @PrimaryKey(autoGenerate = true)
13 | var id: Int = 0
14 |
15 | override fun toString(): String {
16 | return title
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/database/Record.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.database
2 |
3 | data class Record(val title: String?, val url: String, val time: Long, val type: RecordType = RecordType.History) {
4 | override fun equals(other: Any?): Boolean = when (other) {
5 | is Record -> this.title == other.title && this.url == other.url
6 | else -> false
7 | }
8 |
9 | override fun hashCode(): Int = this.title.hashCode() + this.url.hashCode()
10 | }
11 |
12 | enum class RecordType { Bookmark, History }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/database/RecordHelper.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.database
2 |
3 | import android.content.Context
4 | import android.database.sqlite.SQLiteDatabase
5 | import android.database.sqlite.SQLiteOpenHelper
6 | import info.plateaukao.einkbro.unit.RecordUnit
7 |
8 | class RecordHelper(
9 | context: Context
10 | ) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
11 | override fun onCreate(database: SQLiteDatabase) {
12 | database.execSQL(RecordUnit.CREATE_HISTORY)
13 | database.execSQL(RecordUnit.CREATE_WHITELIST)
14 | database.execSQL(RecordUnit.CREATE_JAVASCRIPT)
15 | database.execSQL(RecordUnit.CREATE_COOKIE)
16 | }
17 |
18 | // UPGRADE ATTENTION!!!
19 | override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {}
20 |
21 | companion object {
22 | private const val DATABASE_NAME = "Ninja4.db"
23 | private const val DATABASE_VERSION = 1
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/epub/Epub.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("ArrayInDataClass")
2 |
3 | package info.plateaukao.einkbro.epub
4 |
5 | @Suppress("ArrayInDataClass")
6 | data class EpubFile(
7 | val absPath: String,
8 | val data: ByteArray,
9 | )
10 |
11 | @Suppress("ArrayInDataClass")
12 | data class EpubBook(
13 | val fileName: String,
14 | val title: String,
15 | val author: String?,
16 | val description: String?,
17 | val coverImage: Image?,
18 | val pageProgressDirection: PageProgressDirection?,
19 | val chapters: List,
20 | val images: List,
21 | val toc: List = emptyList(),
22 | val rootPath: String = "",
23 | ) {
24 | data class Chapter(
25 | val absPath: String,
26 | val title: String,
27 | val parts: List,
28 | )
29 |
30 | data class ChapterPart(
31 | val absPath: String,
32 | val body: String,
33 | )
34 |
35 | data class Image(
36 | val absPath: String,
37 | val mediaType: String,
38 | val image: ByteArray
39 | )
40 |
41 | data class ToCEntry(
42 | val chapterTitle: String,
43 | val chapterLink: String
44 | )
45 | }
46 | data class ManifestItem(
47 | val id: String,
48 | val absPath: String,
49 | val mediaType: String,
50 | val properties: String
51 | )
52 |
53 | enum class PageProgressDirection { LTR, RTL }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/epub/EpubFileInfo.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.epub
2 |
3 | data class EpubFileInfo(
4 | val title: String,
5 | val uri: String
6 | ) {
7 | fun toPrefString(): String = "$title$SEPARATOR$uri"
8 |
9 | companion object {
10 | private const val SEPARATOR = ":$:"
11 | fun fromString(fileString: String): EpubFileInfo {
12 | val terms = fileString.split(SEPARATOR)
13 | return EpubFileInfo(terms[0], terms[1])
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/epub/EpubReaderListener.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.epub
2 |
3 | interface EpubReaderListener {
4 | fun onPageChangeListener(ChapterNumber: Int, PageNumber: Int, ProgressStart: Float, ProgressEnd: Float)
5 | fun onChapterChangeListener(ChapterNumber: Int)
6 | fun onTextSelectionModeChangeListener(mode: Boolean?)
7 | fun onLinkClicked(url: String?)
8 | fun onBookStartReached()
9 | fun onBookEndReached()
10 | fun onSingleTap()
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/epub/HelperExtensions.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.epub
2 |
3 | import org.w3c.dom.Document
4 | import org.w3c.dom.Element
5 | import org.w3c.dom.Node
6 | import org.w3c.dom.NodeList
7 | import org.xml.sax.InputSource
8 | import java.io.InputStream
9 | import java.net.URLDecoder
10 | import java.util.zip.ZipInputStream
11 | import javax.xml.parsers.DocumentBuilderFactory
12 |
13 |
14 | internal val NodeList.elements get() = (0..length).asSequence().mapNotNull { item(it) as? Element }
15 | internal val Node.childElements get() = childNodes.elements
16 | internal fun Document.selectFirstTag(tag: String): Node? = getElementsByTagName(tag).item(0)
17 | internal fun Node.selectFirstChildTag(tag: String) = childElements.find { it.tagName == tag }
18 | internal fun Node.selectChildTag(tag: String) = childElements.filter { it.tagName == tag }
19 | internal fun Node.selectChildTags(tag1: String, tag2: String) = childElements.filter { it.tagName == tag1 || it.tagName == tag2 }
20 | internal fun Node.getAttributeValue(attribute: String): String? =
21 | attributes?.getNamedItem(attribute)?.textContent
22 |
23 | internal fun parseXMLFile(inputSteam: InputStream): Document? =
24 | DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputSteam)
25 |
26 | internal fun parseXMLFile(byteArray: ByteArray): Document? = parseXMLFile(byteArray.inputStream())
27 | @Suppress("unused")
28 | internal fun parseXMLText(text: String): Document? = text.reader().runCatching {
29 | DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(InputSource(this))
30 | }.getOrNull()
31 |
32 | internal val String.decodedURL: String get() = URLDecoder.decode(this, "UTF-8")
33 | internal fun String.asFileName(): String = this.replace("/", "_")
34 |
35 | internal fun ZipInputStream.entries() = generateSequence { nextEntry }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/preference/AlbumInfo.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.preference
2 |
3 | data class AlbumInfo(
4 | val title: String,
5 | val url: String
6 | ) {
7 | fun toSerializedString(): String = "$title::$url"
8 | }
9 |
10 | fun String.toAlbumInfo(): AlbumInfo? {
11 | val segments = this.split("::", limit = 2)
12 | if (segments.size != 2) return null
13 | return AlbumInfo(segments[0], segments[1])
14 | }
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/preference/ChatGPTActionInfo.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.preference
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class ChatGPTActionInfo (
7 | val name: String = "ChatGPT",
8 | val systemMessage: String = "",
9 | val userMessage: String = "",
10 | val actionType: GptActionType = GptActionType.Default,
11 | val model: String = "",
12 | val display: GptActionDisplay = GptActionDisplay.Popup,
13 | )
14 |
15 | enum class GptActionType {
16 | Default,
17 | OpenAi,
18 | SelfHosted,
19 | Gemini
20 | }
21 |
22 | enum class GptActionDisplay {
23 | Popup,
24 | NewTab,
25 | SplitScreen,
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/preference/CustomFontInfo.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.preference
2 |
3 | data class CustomFontInfo(
4 | val name: String,
5 | val url: String
6 | ) {
7 | fun toSerializedString(): String = "$name::$url"
8 | }
9 |
10 | fun String.toCustomFontInfo(): CustomFontInfo? {
11 | val segments = this.split("::", limit = 2)
12 | if (segments.size != 2) return null
13 | return CustomFontInfo(segments[0], segments[1])
14 | }
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/preference/RecentBookmark.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.preference
2 |
3 |
4 | data class RecentBookmark(val name: String, val url: String, var count: Int) {
5 | fun toSerializedString(): String = "$name::$url::$count"
6 | }
7 |
8 | fun String.toRecentBookmark(): RecentBookmark? {
9 | val segments = this.split("::", limit = 3)
10 | if (segments.size != 3) return null
11 | return RecentBookmark(segments[0], segments[1], segments[2].toInt())
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/preference/SplitSearchItemInfo.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.preference
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class SplitSearchItemInfo(
7 | val title: String,
8 | val stringPattern: String,
9 | val enabled: Boolean
10 | )
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/preference/TouchAreaType.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.preference
2 |
3 | enum class TouchAreaType {
4 | BottomLeftRight, Left, Right, LongLeftRight, MiddleLeftRight, Long,
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/service/ClearService.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.service
2 |
3 | import android.app.Service
4 | import android.content.Intent
5 | import android.os.IBinder
6 | import info.plateaukao.einkbro.preference.ConfigManager
7 | import info.plateaukao.einkbro.unit.BrowserUnit.clearCache
8 | import info.plateaukao.einkbro.unit.BrowserUnit.clearCookie
9 | import info.plateaukao.einkbro.unit.BrowserUnit.clearHistory
10 | import info.plateaukao.einkbro.unit.BrowserUnit.clearIndexedDB
11 | import org.koin.core.component.KoinComponent
12 | import org.koin.core.component.inject
13 | import kotlin.system.exitProcess
14 |
15 | class ClearService : Service(), KoinComponent {
16 | private val config: ConfigManager by inject()
17 |
18 | override fun onBind(intent: Intent): IBinder? {
19 | return null
20 | }
21 |
22 | override fun onDestroy() {
23 | exitProcess(0) // For remove all WebView thread
24 | }
25 |
26 | override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
27 | clear()
28 | stopSelf()
29 | return START_STICKY
30 | }
31 |
32 | private fun clear() {
33 | if (config.clearCache) clearCache(this)
34 | if (config.clearCookies) clearCookie()
35 | if (config.clearHistory) clearHistory(this)
36 | if (config.clearIndexedDB) clearIndexedDB(this)
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/service/data/GeminiData.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.service.data
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class ContentPart(val text: String)
7 |
8 | @Serializable
9 | data class Content(val parts: List)
10 |
11 | @Serializable
12 | data class SafetySetting(val category: String, val threshold: String)
13 |
14 | @Serializable
15 | data class RequestData(
16 | val contents: List,
17 | val safety_settings: List
18 | )
19 |
20 | @Serializable
21 | data class ResponseData(val candidates: List)
22 |
23 | @Serializable
24 | data class Candidate(val content: Content)
25 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/tts/ByteArrayMediaSource.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.tts
2 |
3 | import android.media.MediaDataSource
4 | import java.io.IOException
5 |
6 | class ByteArrayMediaDataSource(private val data: ByteArray) : MediaDataSource() {
7 |
8 | override fun close() {
9 | // Nothing to close
10 | }
11 |
12 | @Throws(IOException::class)
13 | override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
14 | val length = data.size
15 | if (position >= length) {
16 | return -1 // Indicates end of data
17 | }
18 | val remaining = length - position.toInt()
19 | val count = if (size > remaining) remaining else size
20 | System.arraycopy(data, position.toInt(), buffer, offset, count)
21 | return count
22 | }
23 |
24 | override fun getSize(): Long {
25 | return data.size.toLong()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/tts/CustomMediaPlayer.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.tts
2 |
3 | import android.media.MediaPlayer
4 |
5 | class CustomMediaPlayer : MediaPlayer() {
6 |
7 | private var onResetListener: () -> Unit = {}
8 |
9 | fun setOnResetListener(listener: () -> Unit) {
10 | this.onResetListener = listener
11 | }
12 |
13 | override fun reset() {
14 | super.reset()
15 | onResetListener()
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/tts/entity/VoiceTag.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.tts.entity
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class VoiceTag(
8 | @SerialName("ContentCategories")
9 | val contentCategories: List,
10 | @SerialName("VoicePersonalities")
11 | val voicePersonalities: List,
12 | )
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/unit/LocaleManager.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.unit
2 |
3 | import android.content.Context
4 | import android.content.res.Configuration
5 | import android.os.Build
6 | import android.os.LocaleList
7 | import androidx.annotation.RequiresApi
8 | import java.util.Locale
9 |
10 | object LocaleManager {
11 |
12 | fun setLocale(context: Context, languageCode: String): Context {
13 | val locale = Locale.forLanguageTag(languageCode)
14 | Locale.setDefault(locale)
15 | val resources = context.resources
16 | val configuration = Configuration(resources.configuration)
17 | configuration.setLocale(locale)
18 |
19 | // For Android N and above, use `setLocales`
20 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
21 | configuration.setLocales(LocaleList(locale))
22 | }
23 |
24 | return context.createConfigurationContext(configuration)
25 | }
26 |
27 | @RequiresApi(Build.VERSION_CODES.N)
28 | fun updateResources(context: Context, languageCode: String): Context {
29 | val locale = Locale(languageCode)
30 | Locale.setDefault(locale)
31 | val resources = context.resources
32 | val configuration = Configuration(resources.configuration)
33 | configuration.setLocale(locale)
34 | configuration.setLocales(LocaleList(locale))
35 | return context.createConfigurationContext(configuration)
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/unit/RecordUnit.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.unit
2 |
3 | object RecordUnit {
4 | const val TABLE_HISTORY = "HISTORY"
5 | const val TABLE_WHITELIST = "WHITELIST"
6 | const val TABLE_JAVASCRIPT = "JAVASCRIPT"
7 | const val TABLE_COOKIE = "COOKIE"
8 | const val COLUMN_TITLE = "TITLE"
9 | const val COLUMN_URL = "URL"
10 | const val COLUMN_TIME = "TIME"
11 | const val COLUMN_DOMAIN = "DOMAIN"
12 | const val CREATE_HISTORY = ("CREATE TABLE "
13 | + TABLE_HISTORY
14 | + " ("
15 | + " " + COLUMN_TITLE + " text,"
16 | + " " + COLUMN_URL + " text,"
17 | + " " + COLUMN_TIME + " integer"
18 | + ")")
19 | const val CREATE_WHITELIST = ("CREATE TABLE "
20 | + TABLE_WHITELIST
21 | + " ("
22 | + " " + COLUMN_DOMAIN + " text"
23 | + ")")
24 | const val CREATE_JAVASCRIPT = ("CREATE TABLE "
25 | + TABLE_JAVASCRIPT
26 | + " ("
27 | + " " + COLUMN_DOMAIN + " text"
28 | + ")")
29 | const val CREATE_COOKIE = ("CREATE TABLE "
30 | + TABLE_COOKIE
31 | + " ("
32 | + " " + COLUMN_DOMAIN + " text"
33 | + ")")
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/util/CustomExceptionHandler.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.util
2 |
3 | import android.os.Environment
4 | import java.io.File
5 |
6 | class CustomExceptionHandler(private val defaultHandler: Thread.UncaughtExceptionHandler) :
7 | Thread.UncaughtExceptionHandler {
8 | override fun uncaughtException(thread: Thread, throwable: Throwable) {
9 | val tempFile = File(
10 | Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + "/crash_log.txt"
11 | )
12 | if (tempFile.exists()) {
13 | tempFile.delete()
14 | }
15 | tempFile.createNewFile()
16 | tempFile.appendText(throwable.stackTraceToString())
17 |
18 | defaultHandler.uncaughtException(thread, throwable)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/util/DebugT.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.util
2 |
3 | import android.util.Log
4 | import info.plateaukao.einkbro.BuildConfig
5 |
6 | class DebugT(private val tag: String) {
7 | private val startTimeMillis = System.currentTimeMillis()
8 | init {
9 | if (BuildConfig.DEBUG) {
10 | Log.d(TAG, "[debugT][$tag]: start")
11 | }
12 | }
13 |
14 | fun printPath(intermediateTag: String) {
15 | if (BuildConfig.DEBUG) {
16 | val newTime = System.currentTimeMillis()
17 | Log.d(TAG, "[debugT][$tag][$intermediateTag]: ${(newTime - startTimeMillis)}")
18 | }
19 | }
20 |
21 | fun printTime() {
22 | if (BuildConfig.DEBUG) {
23 | val newTime = System.currentTimeMillis()
24 | Log.d(TAG, "[debugT][$tag]: ${(newTime - startTimeMillis)}")
25 | }
26 | }
27 |
28 | companion object {
29 | private const val TAG = "debugT"
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/util/KeyHandler.kt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/util/KeyHandlingManager.kt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/Album.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view
2 |
3 | import android.graphics.Bitmap
4 | import androidx.compose.runtime.getValue
5 | import androidx.compose.runtime.mutableStateOf
6 | import androidx.compose.runtime.setValue
7 | import info.plateaukao.einkbro.browser.AlbumController
8 | import info.plateaukao.einkbro.browser.BrowserController
9 |
10 | data class Album(
11 | private val albumController: AlbumController,
12 | private var browserController: BrowserController?
13 | ) {
14 | var isLoaded = false
15 |
16 | var isTranslatePage = false
17 |
18 | var albumTitle: String by mutableStateOf("")
19 |
20 | var bitmap: Bitmap? by mutableStateOf(null)
21 |
22 | var isActivated = false
23 |
24 | fun showOrJumpToTop() {
25 | val controller = browserController ?: return
26 | if (controller.isCurrentAlbum(albumController)) {
27 | if (controller.isAtTop()) {
28 | controller.refreshAction()
29 | } else {
30 | controller.jumpToTop()
31 | }
32 | } else {
33 | controller.showAlbum(albumController)
34 | }
35 | }
36 |
37 | fun remove(showHomePage: Boolean = false) {
38 | browserController?.removeAlbum(albumController, showHomePage)
39 | }
40 |
41 | fun getUrl(): String = albumController.albumUrl
42 |
43 | fun setAlbumCover(bitmap: Bitmap?) {
44 | this.bitmap = bitmap
45 | }
46 |
47 | fun activate() {
48 | isActivated = true
49 | }
50 |
51 | fun deactivate() {
52 | isActivated = false
53 | }
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/EBToast.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view
2 |
3 | import android.content.Context
4 | import android.widget.Toast
5 |
6 | object EBToast {
7 | @JvmStatic
8 | fun show(context: Context?, stringResId: Int) {
9 | Toast.makeText(context, stringResId, Toast.LENGTH_SHORT).show()
10 | }
11 |
12 | @JvmStatic
13 | fun show(context: Context?, text: String?) {
14 | Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
15 | }
16 |
17 | @JvmStatic
18 | fun showShort(context: Context?, stringResId: Int) {
19 | Toast.makeText(context, stringResId, Toast.LENGTH_SHORT).show()
20 | }
21 |
22 | @JvmStatic
23 | fun showShort(context: Context?, text: String?) {
24 | Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/GuestureType.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view
2 |
3 | import info.plateaukao.einkbro.R
4 |
5 | enum class GestureType(val value: String, val resId: Int) {
6 | NothingHappen("01", R.string.nothing),
7 | Forward("02", R.string.forward_in_history),
8 | Backward("03", R.string.back_in_history),
9 | ScrollToTop("04", R.string.scroll_to_top),
10 | ScrollToBottom("05", R.string.scroll_to_bottom),
11 | ToLeftTab("06", R.string.switch_to_left_tab),
12 | ToRightTab("07", R.string.switch_to_right_tab),
13 | Overview("08", R.string.show_overview),
14 | OpenNewTab("09", R.string.open_new_tab),
15 | CloseTab("10", R.string.close_tab),
16 | PageUp("11", R.string.page_up),
17 | PageDown("12", R.string.page_down),
18 | Bookmark("13", R.string.bookmarks),
19 | Back("14", R.string.back),
20 | Fullscreen("15", R.string.fullscreen),
21 | Refresh("16", R.string.refresh),
22 | Menu("17", R.string.menu),
23 | TouchPagination("18", R.string.toggle_touch_turn_page),
24 | KeyPageUp("19", R.string.key_page_up),
25 | KeyPageDown("20", R.string.key_page_down),
26 | KeyLeft("21", R.string.key_left),
27 | KeyRight("22", R.string.key_right),
28 | ;
29 |
30 | companion object {
31 | fun from(value: String): GestureType =
32 | values().firstOrNull { it.value == value } ?: NothingHappen
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/compose/MyTheme.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view.compose
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.material.MaterialTheme
6 | import androidx.compose.material.darkColors
7 | import androidx.compose.material.lightColors
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.Color
11 | import androidx.compose.ui.unit.dp
12 |
13 | @Composable
14 | fun MyTheme(
15 | darkTheme: Boolean = isSystemInDarkTheme(),
16 | content: @Composable () -> Unit
17 | ) {
18 | MaterialTheme(
19 | colors = if (darkTheme) DarkColors else LightColors,
20 | content = content,
21 | )
22 | }
23 |
24 | val NormalTextModifier = Modifier.padding(6.dp)
25 |
26 | private val DarkColors = darkColors(
27 | primary = Color.Black,
28 | onPrimary = Color.Gray,
29 | secondary = Color.Gray,
30 | onSecondary = Color.White,
31 | surface = Color.Black,
32 | onSurface = Color.Gray,
33 | background = Color.Black,
34 | onBackground = Color.Gray,
35 | )
36 | private val LightColors = lightColors(
37 | primary = Color.Black,
38 | onPrimary = Color.White,
39 | surface = Color.White,
40 | onSurface = Color.Black,
41 | background = Color.White,
42 | onBackground = Color.Black,
43 | )
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/compose/SelectableItem.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view.compose
2 |
3 | import androidx.compose.foundation.border
4 | import androidx.compose.foundation.clickable
5 | import androidx.compose.foundation.interaction.MutableInteractionSource
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.foundation.shape.RoundedCornerShape
8 | import androidx.compose.material.MaterialTheme
9 | import androidx.compose.material.Text
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.runtime.remember
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.text.style.TextAlign
14 | import androidx.compose.ui.text.style.TextOverflow
15 | import androidx.compose.ui.unit.dp
16 |
17 | @Composable
18 | fun SelectableText(
19 | modifier: Modifier,
20 | selected: Boolean,
21 | text: String,
22 | isEnabled: Boolean = true,
23 | textAlign: TextAlign = TextAlign.Start,
24 | onClick: () -> Unit,
25 | ) {
26 | val interactionSource = remember { MutableInteractionSource() }
27 | val borderWidth = if (selected) 1.dp else -1.dp
28 | Text(
29 | text = text,
30 | color = MaterialTheme.colors.onBackground.copy(alpha = if (isEnabled) 1f else 0.5f),
31 | style = MaterialTheme.typography.button,
32 | maxLines = 1,
33 | overflow = TextOverflow.Ellipsis,
34 | textAlign = textAlign,
35 | modifier = modifier
36 | .border(borderWidth, MaterialTheme.colors.onBackground, RoundedCornerShape(7.dp))
37 | .padding(horizontal = 6.dp, vertical = 6.dp)
38 | .clickable(
39 | indication = null,
40 | interactionSource = interactionSource,
41 | ) {
42 | if (isEnabled) onClick()
43 | }
44 | )
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/data/MenuInfo.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view.data
2 |
3 | import android.content.Intent
4 | import android.content.pm.PackageManager
5 | import android.content.pm.ResolveInfo
6 | import android.graphics.drawable.Drawable
7 | import androidx.compose.ui.graphics.vector.ImageVector
8 |
9 | data class MenuInfo(
10 | val title: String,
11 | val drawable: Drawable? = null, // for data from resolveInfo
12 | val imageVector: ImageVector? = null, // for other locally created data
13 | val intent: Intent? = null,
14 | val closeMenu: Boolean = true,
15 | val action: (() -> Unit)? = null,
16 | val longClickAction: (() -> Unit)? = null,
17 | )
18 |
19 | fun ResolveInfo.toMenuInfo(pm: PackageManager): MenuInfo {
20 | val title = loadLabel(pm).toString()
21 | val icon = loadIcon(pm)
22 | val intent = Intent(Intent.ACTION_PROCESS_TEXT).apply {
23 | type = "text/plain"
24 | setClassName(activityInfo.packageName, activityInfo.name)
25 | }
26 | return MenuInfo(title, icon, null, intent)
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/dialog/PrinterDocumentPaperSizeDialog.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view.dialog
2 |
3 | import android.content.Context
4 | import androidx.appcompat.app.AlertDialog
5 | import info.plateaukao.einkbro.R
6 | import info.plateaukao.einkbro.preference.ConfigManager
7 | import info.plateaukao.einkbro.preference.PaperSize
8 | import org.koin.core.component.KoinComponent
9 | import org.koin.core.component.inject
10 |
11 | class PrinterDocumentPaperSizeDialog(val context: Context) : KoinComponent {
12 | private val config: ConfigManager by inject()
13 |
14 | fun show() {
15 | val paperSizes = PaperSize.values().map { it.sizeString }.toTypedArray()
16 |
17 | AlertDialog.Builder(context, R.style.TouchAreaDialog).apply {
18 | setTitle("Choose the default paper size")
19 | setSingleChoiceItems(paperSizes, config.pdfPaperSize.ordinal) { dialog, selectedIndex ->
20 | config.pdfPaperSize = PaperSize.values()[selectedIndex]
21 | dialog.dismiss()
22 | }
23 | }.create().show()
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/dialog/ReceiveDataDialog.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view.dialog
2 |
3 | import android.content.Context
4 | import android.widget.ProgressBar
5 | import androidx.appcompat.app.AlertDialog
6 | import androidx.lifecycle.LifecycleCoroutineScope
7 | import info.plateaukao.einkbro.R
8 | import info.plateaukao.einkbro.unit.ShareUtil
9 | import kotlinx.coroutines.Dispatchers
10 | import kotlinx.coroutines.launch
11 |
12 | class ReceiveDataDialog(
13 | private val context: Context,
14 | private val lifecycleCoroutineScope: LifecycleCoroutineScope,
15 | ) {
16 | fun show(afterAction: (String) -> Unit) {
17 | val dialog = AlertDialog.Builder(context, R.style.TouchAreaDialog).apply {
18 | setView(ProgressBar(context))
19 | setPositiveButton(R.string.done) { _, _ -> ShareUtil.stopBroadcast() }
20 | setTitle(R.string.menu_receive)
21 | setOnDismissListener {
22 | afterAction("")
23 | }
24 | }.show()
25 |
26 | ShareUtil.startReceiving(lifecycleCoroutineScope) {
27 | lifecycleCoroutineScope.launch(Dispatchers.Main) {
28 | dialog.dismiss()
29 | afterAction(it)
30 | }
31 | ShareUtil.stopBroadcast()
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/dialog/SendLinkDialog.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view.dialog
2 |
3 | import android.content.Context
4 | import android.widget.ProgressBar
5 | import androidx.appcompat.app.AlertDialog
6 | import androidx.lifecycle.LifecycleCoroutineScope
7 | import info.plateaukao.einkbro.R
8 | import info.plateaukao.einkbro.unit.ShareUtil
9 |
10 | class SendLinkDialog(
11 | private val context: Context,
12 | private val lifecycleCoroutineScope: LifecycleCoroutineScope,
13 | ) {
14 | fun show(url: String) {
15 | ShareUtil.startBroadcastingUrl(lifecycleCoroutineScope, url)
16 |
17 | AlertDialog.Builder(context, R.style.TouchAreaDialog).apply {
18 | setView(ProgressBar(context))
19 | setNeutralButton(R.string.done) { _, _ -> ShareUtil.stopBroadcast() }
20 | setTitle(R.string.menu_send_link)
21 | setOnDismissListener { ShareUtil.stopBroadcast() }
22 | }.show()
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/dialog/ShortcutEditDialog.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view.dialog
2 |
3 | import android.app.Activity
4 | import android.graphics.Bitmap
5 | import android.os.Build
6 | import android.view.LayoutInflater
7 | import androidx.annotation.RequiresApi
8 | import info.plateaukao.einkbro.databinding.DialogEditShortcutBinding
9 | import info.plateaukao.einkbro.unit.HelperUnit
10 |
11 | class ShortcutEditDialog(
12 | private val activity: Activity,
13 | private val title: String,
14 | private val url: String,
15 | private val bitmap: Bitmap?,
16 | private val okAction: () -> Unit,
17 | private val cancelAction: () -> Unit,
18 | ) {
19 | private val dialogManager: DialogManager = DialogManager(activity)
20 |
21 | fun show() {
22 | val binding = DialogEditShortcutBinding.inflate(LayoutInflater.from(activity))
23 | binding.title.setText(title)
24 | binding.url.setText(url)
25 |
26 | dialogManager.showOkCancelDialog(
27 | title = activity.getString(info.plateaukao.einkbro.R.string.menu_sc),
28 | view = binding.root,
29 | okAction = {
30 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
31 | createShortcut(binding)
32 | }
33 | },
34 | cancelAction = { cancelAction.invoke() }
35 | )
36 | }
37 |
38 | @RequiresApi(Build.VERSION_CODES.O)
39 | private fun createShortcut(binding: DialogEditShortcutBinding) {
40 | HelperUnit.createShortcut(
41 | activity,
42 | binding.title.text.toString().trim { it <= ' ' },
43 | binding.url.text.toString().trim { it <= ' ' },
44 | bitmap
45 | )
46 | okAction.invoke()
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/dialog/TextInputDialog.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view.dialog
2 |
3 | import android.app.AlertDialog
4 | import android.content.Context
5 |
6 | import android.widget.EditText
7 | import info.plateaukao.einkbro.R
8 | import kotlin.coroutines.resume
9 | import kotlin.coroutines.suspendCoroutine
10 |
11 |
12 | class TextInputDialog(
13 | private val context: Context,
14 | private val title: String,
15 | private val message: String,
16 | private val defaultText: String = "",
17 | ) {
18 | suspend fun show() = suspendCoroutine { continuation ->
19 | val editText = EditText(context).apply {
20 | setText(defaultText)
21 | }
22 |
23 | AlertDialog.Builder(context, R.style.TouchAreaDialog)
24 | .setTitle(title)
25 | .setMessage(message)
26 | .setView(editText)
27 | .setCancelable(true)
28 | .setPositiveButton(android.R.string.ok) { dialog, _ ->
29 | dialog.dismiss()
30 | val text = editText.text.toString()
31 | continuation.resume(text)
32 | }
33 | .setNegativeButton(android.R.string.cancel) { dialog, _ ->
34 | dialog.dismiss()
35 | continuation.resume(null)
36 | }.show()
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/dialog/TtsLanguageDialog.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view.dialog
2 |
3 | import android.content.Context
4 | import androidx.appcompat.app.AlertDialog
5 | import info.plateaukao.einkbro.R
6 | import info.plateaukao.einkbro.preference.ConfigManager
7 | import org.koin.core.component.KoinComponent
8 | import org.koin.core.component.inject
9 | import java.util.Locale
10 |
11 | class TtsLanguageDialog(val context: Context) : KoinComponent {
12 | private val config: ConfigManager by inject()
13 |
14 | fun show(locales: List) {
15 | val availableLocales = locales.sortedBy { it.displayName }
16 | val availableLocalDisplayNames = availableLocales.map { it.displayName }.toTypedArray()
17 |
18 | AlertDialog.Builder(context, R.style.TouchAreaDialog).apply {
19 | setTitle("Read in Which Language")
20 | setSingleChoiceItems(
21 | availableLocalDisplayNames, availableLocales.indexOf(config.ttsLocale)
22 | ) { dialog, selectedIndex ->
23 | val locale = availableLocales[selectedIndex]
24 | config.ttsLocale = locale
25 | dialog.dismiss()
26 | }
27 | }.create().show()
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/view/dialog/compose/ShowEditGptActionDialogFragment.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.view.dialog.compose
2 |
3 | import info.plateaukao.einkbro.activity.GptActionDialog
4 | import info.plateaukao.einkbro.preference.ChatGPTActionInfo
5 | import info.plateaukao.einkbro.preference.GptActionType
6 | import info.plateaukao.einkbro.view.compose.MyTheme
7 |
8 | class ShowEditGptActionDialogFragment(
9 | private val editActionIndex: Int = -1,
10 | ) : ComposeDialogFragment() {
11 | override fun setupComposeView() {
12 | var actionList = config.gptActionList
13 | composeView.setContent {
14 | MyTheme {
15 | GptActionDialog(
16 | editActionIndex,
17 | if (editActionIndex >= 0) actionList[editActionIndex] else createDefaultGptAction(),
18 | config.getGptTypeModelMap(),
19 | okAction = { modifiedAction ->
20 | actionList = actionList.toMutableList().apply {
21 | if (editActionIndex >= 0) set(editActionIndex, modifiedAction)
22 | else add(modifiedAction)
23 | }
24 | config.gptActionList = actionList
25 | dismiss()
26 | },
27 | dismissAction = { dismiss() }
28 | )
29 | }
30 | }
31 | }
32 |
33 | private fun createDefaultGptAction(): ChatGPTActionInfo {
34 | return ChatGPTActionInfo(
35 | "",
36 | "",
37 | "",
38 | GptActionType.Default,
39 | config.getDefaultActionModel()
40 | )
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/viewmodel/AlbumViewModel.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.viewmodel
2 |
3 | import androidx.compose.runtime.mutableIntStateOf
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.lifecycle.ViewModel
6 | import info.plateaukao.einkbro.view.Album
7 |
8 | class AlbumViewModel: ViewModel() {
9 | val albums = mutableStateOf(listOf())
10 | val focusIndex = mutableIntStateOf(0)
11 |
12 | fun addAlbum(album: Album, index: Int) {
13 | albums.value = albums.value.toMutableList().apply { add(index, album) }.toList()
14 | }
15 |
16 | fun removeAlbum(album: Album) {
17 | albums.value = albums.value.toMutableList().apply {
18 | remove(album)
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/viewmodel/ExternalSearchViewModel.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import info.plateaukao.einkbro.preference.ConfigManager
5 | import info.plateaukao.einkbro.preference.SplitSearchItemInfo
6 | import kotlinx.coroutines.flow.MutableStateFlow
7 | import kotlinx.coroutines.flow.StateFlow
8 | import kotlinx.coroutines.flow.asStateFlow
9 | import org.koin.core.component.KoinComponent
10 | import org.koin.core.component.inject
11 | import java.net.URLEncoder
12 |
13 | class ExternalSearchViewModel: ViewModel(), KoinComponent {
14 | private val config: ConfigManager by inject()
15 | val searchActions = config.splitSearchItemInfoList + SplitSearchItemInfo("external", config.customProcessTextUrl, true)
16 | var currentSearchAction = searchActions.last()
17 | private var currentSearchText = ""
18 |
19 | private val _showButton = MutableStateFlow(false)
20 | val showButton: StateFlow = _showButton.asStateFlow()
21 |
22 | fun setButtonVisibility(isVisible: Boolean) {
23 | _showButton.value = isVisible
24 | }
25 |
26 | fun generateSearchUrl(
27 | searchText: String = currentSearchText,
28 | splitSearchItemInfo: SplitSearchItemInfo = currentSearchAction,
29 | ): String {
30 | currentSearchText = searchText
31 | return if (splitSearchItemInfo.stringPattern.contains("%s"))
32 | splitSearchItemInfo.stringPattern.format(URLEncoder.encode(searchText, "UTF-8"))
33 | else "${splitSearchItemInfo.stringPattern}$searchText"
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/info/plateaukao/einkbro/viewmodel/SplitSearchViewModel.kt:
--------------------------------------------------------------------------------
1 | package info.plateaukao.einkbro.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import java.net.URLEncoder
5 |
6 | class SplitSearchViewModel : ViewModel() {
7 | var state: ActionModeMenuState.SplitSearch? = null
8 |
9 | fun reset() {
10 | state = null
11 | }
12 |
13 | fun getUrl(text: String): String {
14 | val stringFormat = state?.stringFormat ?: return ""
15 | if (stringFormat.contains("=")) {
16 | val url = stringFormat.format(text, "UTF-8")
17 | return url.split("=")[0] + "=" +
18 | URLEncoder.encode(url.split("=")[1], "UTF-8")
19 | } else {
20 | return state?.stringFormat?.format(URLEncoder.encode(text, "UTF-8")).orEmpty()
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/qc_bookmarks.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/drawable-hdpi/qc_bookmarks.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/qc_history.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/drawable-hdpi/qc_history.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/qc_bookmarks.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/drawable-mdpi/qc_bookmarks.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/qc_history.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/drawable-mdpi/qc_history.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/qc_bookmarks.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/drawable-xhdpi/qc_bookmarks.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/qc_history.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/drawable-xhdpi/qc_history.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/qc_bookmarks.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/drawable-xxhdpi/qc_bookmarks.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/qc_history.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/drawable-xxhdpi/qc_history.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/qc_bookmarks.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/drawable-xxxhdpi/qc_bookmarks.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/qc_history.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/drawable-xxxhdpi/qc_history.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_transparent_with_border.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
6 |
7 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_with_border.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
6 |
7 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_with_border_margin.xml:
--------------------------------------------------------------------------------
1 |
6 |
8 |
9 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_border_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
8 |
9 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_border_bg_dash.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
8 |
9 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/gesture_tap.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add_folder.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_black_font_off.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_bold_font.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_bold_font_active.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_book.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_copy.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_data.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_drag.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_folder.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_font_decrease.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_font_increase.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_gemini.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_highlight.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_highlight_color.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_history.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_history_activated.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_home.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_incognito.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_link_here.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_minimize.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_page_count.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_paragraph.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_paste.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_phone.xml:
--------------------------------------------------------------------------------
1 |
4 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_pocket.xml:
--------------------------------------------------------------------------------
1 |
7 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_reader.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_receive.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_remove.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_remove_all_tabs.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_reselect.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_rotate.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_send.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sort.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_split_screen.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_split_screen_vertical.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_stop.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sync.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sync_scroll.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_tab_plus_activated.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_toolbar.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_direction_down.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_direction_left.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_direction_right.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_direction_up.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_disabled.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_enabled.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_left.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
9 |
12 |
15 |
18 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_left_right.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
9 |
12 |
15 |
18 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_long.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
9 |
12 |
15 |
18 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_middle_left_right.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
9 |
12 |
15 |
18 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_right.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
9 |
12 |
15 |
18 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_translate.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_tts.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_underscore.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_voice_off.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_white_background.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_white_background_active.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_arrow_down_gest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_arrow_left_gest.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_arrow_right_gest.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_arrow_up_gest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_backup.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_bookmark.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_close.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_copyright.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_delete.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_desktop.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_desktop_activate.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_dots.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_earth.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_export.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_menu_save.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_menu_share.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_overflow.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_plus.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_refresh.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_tab_plus.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_tab_unselected.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_ui.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/item_bg_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/roundcorner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
8 |
9 |
10 |
11 |
15 |
16 |
17 | -
22 |
23 |
24 |
25 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/selected.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 | -
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/selected_border_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
6 |
7 |
8 |
11 |
12 |
13 | -
18 |
20 |
21 |
22 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/top_border_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/touch_area_border.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
7 |
8 |
13 |
14 |
15 | -
21 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/unselected.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_dict.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_composable.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_edit_extension.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
15 |
16 |
25 |
26 |
27 |
28 |
31 |
32 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_saved_epub_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_item_epub_file.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
16 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/translation_page_index.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/two_pane_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_clear.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_ereader.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 |
13 |
18 |
19 |
25 |
29 |
30 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Two sides
4 | ChatGPT action definition
5 | Define different prompts for different ChatGPT actions.
6 | ChatGPT Actions
7 | Select text
8 | Show icons on action menus
9 | Show/hide icons on action menus to make it simpler.
10 | Use OpenAI TTS Api
11 | Use OpenAI API to read web content
12 | Highlight
13 | Highlights
14 | Articles
15 | Highlights
16 | Invert color
17 | Highlight Style
18 | Choose a style for text highlight
19 | Underline
20 | Yellow
21 | Green
22 | Blue
23 | Pink
24 | Update to Latest Release
25 | Something wrong saving epub. Please try again later.
26 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #AAAAAA
6 |
7 | @android:color/transparent
8 | #fafafa
9 | #E0E0E0
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 56dp
6 | 16dp
7 | 12dp
8 | 8dp
9 | 50dp
10 | 50dp
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_descriptor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/filepaths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/searchable.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | gradlePluginPortal()
7 | }
8 | dependencies {
9 | classpath("com.android.tools.build:gradle:8.7.1")
10 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0")
11 | classpath("org.jetbrains.kotlin:kotlin-serialization:2.0.0")
12 | classpath("com.google.devtools.ksp:symbol-processing-gradle-plugin:2.0.0-1.0.21")
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | google()
19 | mavenCentral()
20 | maven { url = uri("https://jitpack.io") }
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/crowdin.yml:
--------------------------------------------------------------------------------
1 | files:
2 | - source: /app/src/**/res/values/strings*.xml
3 | translation: /%original_path%-%two_letters_code%/%original_file_name%
4 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/853.txt:
--------------------------------------------------------------------------------
1 | * optimize Reader mode
2 | * put more icons on menu settings
3 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/full_description.txt:
--------------------------------------------------------------------------------
1 | It's a lightweight browser to provide powerful features designed for Eink devices.
2 |
3 | E-Ink specific features
4 | * pageUp / pageDown / Back button on toolbar
5 | * Physical volume keys for pageUp/pageDown
6 | * Tapping on screen left/right edge for pageUp/Down (finger button on toolbar)
7 | * Desktop mode feature
8 | * All icons in high contrast colors
9 | * Tab count count toolbar
10 | * Font size change button
11 | * Bold font button
12 | * Reader mode
13 | * Vertical Read mode
14 |
15 | Basic UI/Handling:
16 | * optimized for one hand handling (toolbar at bottom)
17 | * tab control (switch, open, close unlimited tabs)
18 | * full material design
19 | * fullscreen browsing (optional)
20 | * navigation button in fullscreen mode
21 | * fast toggle for most important settings
22 | * advanced gesture control for toolbar and navigation button
23 |
24 | Some nice extra features:
25 | * small size
26 | * search on site
27 | * open links in background
28 | * Websearch (from marked text context menu)
29 | * screenshots of the whole website
30 | * share/save as PDF
31 | * open links in other apps (for example YouTube)
32 |
33 | Permissions that may be used
34 | * INTERNET: it's a browser, so it's expected.
35 | * READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE: for saving web page as PDF or epub file, and for downloading files.
36 | * INSTALL_SHORTCUT: to install web shortcuts on Home.
37 | * ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION: when using some navigation web site, or some web service that needs location, these permissions are needed.
38 |
39 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/featureGraphic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/fastlane/metadata/android/en-US/images/featureGraphic.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/fastlane/metadata/android/en-US/images/icon.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/1_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/fastlane/metadata/android/en-US/images/phoneScreenshots/1_1.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/1_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/fastlane/metadata/android/en-US/images/phoneScreenshots/1_3.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/1_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/fastlane/metadata/android/en-US/images/phoneScreenshots/1_4.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/1_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/fastlane/metadata/android/en-US/images/phoneScreenshots/1_5.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/1_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/fastlane/metadata/android/en-US/images/phoneScreenshots/1_6.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/short_description.txt:
--------------------------------------------------------------------------------
1 | lightweight, fast, but powerful browser designed for Eink devices.
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/title.txt:
--------------------------------------------------------------------------------
1 | EinkBro
2 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## For more details on how to configure your build environment visit
2 | # http://www.gradle.org/docs/current/userguide/build_environment.html
3 | #
4 | # Specifies the JVM arguments used for the daemon process.
5 | # The setting is particularly useful for tweaking memory settings.
6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
7 | org.gradle.jvmargs=-Xmx16240m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
8 | #
9 | # When configured, Gradle will run in incubating parallel mode.
10 | # This option should only be used with decoupled projects. More details, visit
11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
12 | # org.gradle.parallel=true
13 | #Sun Oct 31 01:03:15 CST 2021
14 | org.gradle.parallel=true
15 | org.gradle.daemon=true
16 | android.useAndroidX=true
17 | org.gradle.caching=true
18 | org.gradle.unsafe.configuration-cache=true
19 | org.gradle.unsafe.configuration-cache-problems=warn
20 | kotlin.build.report.output=file
21 | org.gradle.configureondemand=true
22 | kotlin.incremental=true
23 | android.enableR8.fullMode=true
24 |
25 |
26 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | accompanistDrawablepainter = "0.31.0-alpha"
3 | jsoup = "1.17.2"
4 | kotlin = "2.0.0"
5 | foundationAndroid = "1.6.8"
6 | kotlinxSerializationJson = "1.6.3"
7 | kxml2 = "2.3.0"
8 | material = "1.12.0"
9 | mediationTestSuite = "3.0.0"
10 | okhttp = "4.12.0"
11 | okhttpSse = "4.11.0"
12 | reorderable = "2.3.3"
13 | slf4jApi = "1.7.36"
14 | timber = "4.7.1"
15 | [libraries]
16 | accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanistDrawablepainter" }
17 | androidx-foundation-android = { group = "androidx.compose.foundation", name = "foundation-android", version.ref = "foundationAndroid" }
18 | jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
19 | kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
20 | kxml2 = { module = "net.sf.kxml:kxml2", version.ref = "kxml2" }
21 | material = { module = "com.google.android.material:material", version.ref = "material" }
22 | mediation-test-suite = { group = "com.google.android.ads", name = "mediation-test-suite", version.ref = "mediationTestSuite" }
23 | okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
24 | okhttp-sse = { group = "com.squareup.okhttp3", name = "okhttp-sse", version.ref = "okhttpSse" }
25 | reorderable = { group = "sh.calvin.reorderable", name = "reorderable", version.ref = "reorderable" }
26 | slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4jApi" }
27 | timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" }
28 |
29 | [plugins]
30 | org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
31 |
32 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
33 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Oct 12 18:12:01 MSK 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plateaukao/einkbro/fcd4323ff86b021e80184bdff1a0ea0bff23b78a/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':adblock-client'
2 | include ':ad-filter'
3 | include ':app'
4 |
--------------------------------------------------------------------------------
/tools/screenshots.yml:
--------------------------------------------------------------------------------
1 | appId: info.plateaukao.einkbro
2 | ---
3 | - launchApp
4 | - extendedWaitUntil:
5 | visible: "EinkBro"
6 | timeout: 4000
7 | - takeScreenshot: MainScreen
8 | # Settings button on toolbar
9 | - tapOn:
10 | id: "settings"
11 | - takeScreenshot: SettingsMenu
12 |
13 | - tapOn: "Settings"
14 | - takeScreenshot: SettingsScreen
15 |
16 | - tapOn: "UI"
17 | - waitForAnimationToEnd
18 | - takeScreenshot: UISettingsScreen
19 | - back
20 | - tapOn: "Toolbar"
21 | - takeScreenshot: ToolbarSettingsScreen
22 | - back
23 | - tapOn: "Behavior"
24 | - takeScreenshot: BehaviorSettingsScreen
25 | - back
26 | - tapOn: "Gestures"
27 | - takeScreenshot: GesturesSettingsScreen
28 | - back
29 | - tapOn: "Backup"
30 | - takeScreenshot: BackupSettingsScreen
31 | - back
32 | - tapOn: "Start Control"
33 | - takeScreenshot: StartControlSettingsScreen
34 | - back
35 | - tapOn: "Data Control"
36 | - takeScreenshot: DataControlSettingsScreen
37 | - back
38 | - tapOn: "Search"
39 | - takeScreenshot: SearchSettingsScreen
40 | - back
41 | - tapOn: "Misc"
42 | - takeScreenshot: MiscSettingScreen
43 | - tapOn: "Printed pdf paper size"
44 | - back
45 | - tapOn: "Custom user agent"
46 | - back
47 | - tapOn: "Edit Home Url"
48 | - back
49 | - back
50 | - tapOn:
51 | text: "About EinkBro.*"
52 | - takeScreenshot: AboutSettingsScreen
53 | - back
54 | - back
55 |
--------------------------------------------------------------------------------