├── .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 | ![1](https://github.com/scoute-dich/browser/blob/master/Screenshots/1.png?raw=true) 2 | ![2](https://github.com/scoute-dich/browser/blob/master/Screenshots/2.png?raw=true) 3 | ![3](https://github.com/scoute-dich/browser/blob/master/Screenshots/3.png?raw=true) 4 | ![4](https://github.com/scoute-dich/browser/blob/master/Screenshots/4.png?raw=true) 5 | ![5](https://github.com/scoute-dich/browser/blob/master/Screenshots/5.png?raw=true) 6 | ![6](https://github.com/scoute-dich/browser/blob/master/Screenshots/6.png?raw=true) 7 | ![7](https://github.com/scoute-dich/browser/blob/master/Screenshots/7.png?raw=true) -------------------------------------------------------------------------------- /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 | Translated Image 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 | 5 | 6 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_ereader.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 10 | 11 | 15 | 16 | 20 | 21 | 25 | 26 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------