Compare commits

..

1222 Commits

Author SHA1 Message Date
Syer10 7e211ee9a1 Release v2.1.1867
CI Publish / Validate Gradle Wrapper (push) Failing after 2m5s
CI Publish / Build Jar (push) Has been skipped
CI Publish / jlink (linux-x64, ubuntu-latest) (push) Failing after 2s
CI Publish / jlink (macOS-arm64, macos-14) (push) Has been cancelled
CI Publish / jlink (macOS-x64, macos-13) (push) Has been cancelled
CI Publish / jlink (windows-x64, windows-latest) (push) Has been cancelled
CI Publish / Make linux-assets release (push) Has been cancelled
CI Publish / Make appimage release (push) Has been cancelled
CI Publish / Make debian-all release (push) Has been cancelled
CI Publish / Make linux-x64 release (push) Has been cancelled
CI Publish / Make macOS-arm64 release (push) Has been cancelled
CI Publish / Make macOS-x64 release (push) Has been cancelled
CI Publish / Make windows-x64 release (push) Has been cancelled
CI Publish / release (push) Has been cancelled
2025-07-31 13:56:03 -04:00
Mitchell Syer 8e8883ba37 Update electron (#1556) 2025-07-31 12:01:33 -04:00
schroda 02c4398e48 Fix handling of too long page image urls migration (#1552)
* Delete duplicated chapter page rows by index and chapter

In case duplicated rows based on the condition for the updated unique constraint existed, the new constraint could not be added and caused the migration to fail

* Drop UC_PAGE only if it exists
2025-07-29 18:00:10 -04:00
schroda ad7a8dd7dc Fix/page download conversion reduce logs (#1545)
* Cleanup chapter page conversion

* Reduce chapter page conversion logging
2025-07-25 19:42:06 -04:00
schroda e3338211d6 Handle too long page image urls (#1544)
Attempted fix of 3ff29aa38a might not work, because there is no guarantee that the extension supports retrieving a specific page.
2025-07-25 19:42:00 -04:00
Mitchell Syer 7cab4b9229 Simplify secondary config parse (#1540)
* Add backslash escaping

* Use parseMap instead
2025-07-21 22:20:58 -04:00
Mitchell Syer ac5f1a0d93 Add enabled preference setting (#1539)
* Add enabled preference setting

* Don't change preference if its not enabled
2025-07-21 15:13:17 -04:00
Mitchell Syer 798b9d0c98 Fix cookies when domain is null (#1538) 2025-07-21 15:13:04 -04:00
Chiru-Dey 3ff29aa38a snowmtl extension error fix: dynamic retrieval (#1531)
* dynamic retrieval

* ktlint errors fixed

* reinstated comments
2025-07-21 15:12:57 -04:00
renovate[bot] f8c2b9ffb0 Update dependency adoptium/temurin21-binaries to jdk-21.0.8+9 (#1529)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 15:12:49 -04:00
Syer10 888bb8897a Update AppImageTool download location
Not very impressed by the random name change that breaks existing scripts, no warning given.
2025-07-20 17:26:15 -04:00
schroda 192136e66c Change "download conversion compression level" type to Double (#1535)
https://opensource.expediagroup.com/graphql-kotlin/docs/schema-generator/writing-schemas/scalars/#primitive-types
2025-07-20 17:00:00 -04:00
schroda 5057a57f7f Properly bind track privately (#1534)
In case the track was read from the TrackSearchTable the private status was never applied
2025-07-20 16:59:48 -04:00
renovate[bot] d81a4e0b7f Update dependency io.mockk:mockk to v1.14.5 (#1527)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-16 21:53:05 -04:00
Mitchell Syer c63a06730f Fix downloads on errors when converting image (#1526)
* Fix downloads on errors when converting image

* Lint

* Simplify it

* No need for return

* Simplified
2025-07-16 21:52:54 -04:00
Mitchell Syer bef326d2d7 Fix paths in system properties (#1528)
* Fix paths in system properties

* Remove uneeded parse

* Cleanup name
2025-07-16 21:52:34 -04:00
renovate[bot] b8e85422f0 Update polyglot to v24.2.2 (#1523)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-15 15:38:29 -04:00
Constantin Piber d050bfdc68 Localize WebView and Login pages (#1522)
* Localize WebView and Login pages

* Switch to JTE for page rendering

* Lint

* Add gradle task dependency

* JTE -> KTE

* ShouldRunAfter

* I guess we must

---------

Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2025-07-15 15:38:20 -04:00
schroda 3bac176bf6 Prevent UnsupportedOperationException in DownloadManager (#1521)
CopyOnWriteArraySet does not support the usage of "removeAll" with a predicate
2025-07-15 09:32:11 -04:00
Syer10 7c506a42ae Install libfuse2 when creating AppImage 2025-07-14 18:59:50 -04:00
KamaleiZestri 2c436e2027 Add AppImage bundle (#1519)
* Add AppImage build

* add appimage package variable name

* Add workflows

---------

Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2025-07-14 18:51:09 -04:00
Constantin Piber df0078b725 [#1496] Image conversion (#1505)
* [#1496] First conversion attempt

* [#1496] Configurable conversion

* Fix: allow nested configs (map)

* [#1496] Support explicit `none` conversion

* Use MimeUtils for provided download

* [1496] Support image conversion on load for downloaded images

* Lint

* [#1496] Support conversion on fresh download as well

Previous commit was only for already downloaded images, now also for
fresh and cached

* [#1496] Refactor: Move where conversion for download happens

* Rewrite config handling, improve custom types

* Lint

* Add format to pages mutation

* Lint

* Standardize url encode

* Lint

* Config: Allow additional conversion parameters

* Implement conversion quality parameter

* Lint

* Implement a conversion util to allow fallback readers

* Add downloadConversions to api and backup, fix updateValue issues

* Lint

* Minor cleanup

* Update libs.versions.toml

---------

Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2025-07-14 17:51:18 -04:00
schroda 09c950a890 Fix/gql download subscription errors spamming emits (#1518)
* Remove immediate download notification for latest gql subscription

There is a problem where too many immediate updates can cause the client to lag out (e.g., in case it has to update the queue in the cache based on the updates).
This happens in case e.g., a source is broken and all its downloads error out basically immediately.
With each errored out download, a new one starts, which causes an immediate notification to the clients.

* Determine downloader status from active state of downloader jobs

In case the downloader is active but all downloads are erroring out immediately, no download will have the DOWNLOADING status.
This then would result in the downloader status to constantly be STOPPED.

* Prevent multiple update for the same downloads

It was possible that multiple updates got added for the same download.
This caused issues with the graphql apollo client, because it wasn't able to correctly update the client cache.

* Set download error state only after reaching max retries

In case the max retries haven't been reached yet, the download will be retried and thus setting and emitting the error state will cause weird looking ui updates.
2025-07-14 17:50:03 -04:00
schroda e7e76ed68d Prevent duplicated meta entries in database (#1517)
fixes #1513
2025-07-14 17:49:27 -04:00
schroda 06c1eeb995 Add missing transaction context to manga category update (#1516)
fixes #1510
2025-07-14 17:49:03 -04:00
renovate[bot] d545d852c5 Update dependency com.android.tools.build:apksig to v8.11.1 (#1511)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 17:48:37 -04:00
renovate[bot] 3486e8dcf3 Update dependency com.typesafe:config to v1.4.4 (#1509)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 17:48:27 -04:00
renovate[bot] 1956b700fc Update dependency com.github.usefulness:webp-imageio to v0.10.2 (#1507)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 17:48:21 -04:00
renovate[bot] 64ad9af344 Update okhttp monorepo to v5.1.0 (#1506)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 17:48:14 -04:00
renovate[bot] 55fec0b82c Update plugin ktlint to v13 (#1504)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 17:48:06 -04:00
Syer10 8323ef5f41 [skip ci] Winget now uses Suwayomi.Suwayomi-Server 2025-07-08 18:26:35 -04:00
Syer10 8dbab23de7 [skip ci] Manual release only 2025-07-07 23:21:29 -04:00
Syer10 825d232613 [skip ci] Manually input version 2025-07-07 23:14:51 -04:00
Syer10 d797a03502 Modify Winget 2025-07-07 23:05:33 -04:00
Mitchell Syer 0ef6d74514 Add auth to log protection (#1501) 2025-07-06 14:01:37 -04:00
Constantin Piber 6234e897a8 [#1497] WebView: Localstorage (#1500)
* [#1497] WebView: Localstorage

* WebView: Transition to our own header/postData system

This is also what is recommended by most other posts, I haven't seen the
context used anywhere, and `KCEFResourceRequestHandler` seems to just
bypass a lot of CEF

* Lint
2025-07-06 12:09:31 -04:00
renovate[bot] fe121f59b0 Update okhttp monorepo to v5.0.0 (#1493)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-06 12:09:16 -04:00
renovate[bot] 90cf5fcdec Update dependency com.squareup.okio:okio to v3.15.0 (#1488)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-06 12:09:00 -04:00
renovate[bot] 8b5782a5b6 Update dependency gradle to v8.14.3 (#1494)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-06 12:08:44 -04:00
Constantin Piber 68a131dbeb [#1349] Basic Cookie Authentication (#1498)
* [#1349] Stub basic cookie authentication

* [#1349] Basic login page

Also adjusts WebView header color and shadow to match WebUI. WebUI uses
a background-image gradient to change the perceived color, which was not
noticed originally.

* [#1349] Handle login post

* [#1349] Redirect to previous URL

* [#1349] Return a basic 401 for api endpoints

Instead of redirecting to a visual login page, API should just indicate
the bad state

* Use more appropriate 303 redirect

* Update server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>

* Update server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>

* Lint

* Transition to AuthMode enum with migration path

* Make basicAuthEnabled auto property, Lint

* ConfigManager: Make sure to re-parse the config after migration

* basicAuth{Username,Password} -> auth{Username,Password}

* Lint

* Update server settings backup model

* Update comment

* Minor cleanup

* Improve backup legacy settings fix

* Lint

* Simplify config value migration

---------

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2025-07-06 12:08:29 -04:00
Mitchell Syer 1411c02e18 [skip ci] Update CONTRIBUTING.md 2025-07-02 23:38:22 -04:00
Constantin Piber 81fe3f0108 Stop dumping cookies in the console (#1490) 2025-07-02 13:35:56 -04:00
Constantin Piber c15cf23168 Kcef: Disable SHM (#1489)
In Docker, `/dev/shm` is restricted, so Chromium dies of OOM

See also https://stackoverflow.com/a/56941767/7508309
2025-07-02 13:23:21 -04:00
Constantin Piber a79dc580a5 Browser Webview (#1486)
* WebView: Add initial controller

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>

* WebView: Prepare page

* WebView: Basic HTML setup

* WebView: Improve navigation

* WebView: Refactor message class deserialization

* WebView: Refactor event message serialization

* WebView: Handle click events

* WebView: Fix events after refactor

* WebView: Fix normalizing of URLs

* WebView: HTML remove navigation buttons

* WebView: Handle more events

* WebView: Handle document change in events

* WebView: Refactor to send mutation events

* WebView: More mouse events

* WebView: Include bubbles, cancelable in event

Those seem to be important

* WebView: Attempt to support nested iframe

* WebView: Handle long titles

* WebView: Avoid setting invalid url

* WebView: Send mousemove

* WebView: Start switch to canvas-based render

* WebView: Send on every render

* WebView: Dynamic size

* WebView: Keyboard events

* WebView: Handle mouse events in CEF

This is important because JS can't click into iFrames, meaning the
previous solution doesn't work for captchas

* WebView: Cleanup

* WebView: Cleanup 2

* WebView: Document title

* WebView: Also send title on address change

* WebView: Load and flush cookies from store

* WebView: remove outdated TODOs

* Offline WebView: Load cookies from store

* Cleanup

* Add KcefCookieManager, need to figure out how to inject it

* ktLintFormat

* Fix a few cookie bugs

* Fix Webview on Windows

* Minor cleanup

* WebView: Remove /tmp image write, lint

* Remove custom cookie manager

* Multiple cookie fixes

* Minor fix

* Minor cleanup and add support for MacOS meta key

* Get enter working

* WebView HTML: Make responsive for mobile pages

* WebView: Translate touch events to mouse scroll

* WebView: Overlay an actual input to allow typing on mobile

Browsers will only show the keyboard if an input is focused. This also
removes the `tabstop` hack.

* WebView: Protect against occasional NullPointerException

* WebView: Use float for clientX/Y

* WebView: Fix ChromeAndroid being a pain

* Simplify enter fix

* NetworkHelper: Fix cache

* Improve CookieStore url matching, fix another cookie conversion issue

* Move distinctBy

* WebView: Mouse direction toggle

* Remove accidentally copied comment

---------

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2025-07-01 17:28:41 -04:00
renovate[bot] 8a62c6295d Update moko to v0.25.0 (#1487)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-01 17:28:28 -04:00
renovate[bot] 88e77e1547 Update okhttp monorepo to v5.0.0-alpha.17 (#1485)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-01 17:28:06 -04:00
Weblate (bot) 534619bc1a Weblate translations (#1484)
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/ja/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/pl/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/pt/
Translation: Suwayomi/Suwayomi-Server

Co-authored-by: 9811pc <9811.main@gmail.com>
Co-authored-by: N'Num Yutthaphon Inchaiya <yutthaphon30667@gmail.com>
Co-authored-by: Psico <psikenji@users.noreply.hosted.weblate.org>
Co-authored-by: Syer10 <Mitchellptbo@gmail.com>
Co-authored-by: UnknownSkyrimPasserby <f7022961@opayq.com>
2025-07-01 17:27:56 -04:00
Constantin Piber ae904753f7 systemd: use startup script, X server (#1482)
* systemd: use startup script

* script: Start X server using `xvfb-run` if DISPLAY is not set
2025-06-28 17:04:13 -04:00
Mitchell Syer 8c4a2cb529 Add chapter lastReadAt to backups as BackupHistory (#1477)
* Add chapter lastReadAt to backups as BackupHistory

* MaxOrNull
2025-06-28 17:04:04 -04:00
renovate[bot] 16d4893480 Update dependency com.squareup.okio:okio to v3.14.0 (#1483)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 17:03:55 -04:00
renovate[bot] a7446e2f4c Update dependency com.github.usefulness:webp-imageio to v0.10.1 (#1480)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 17:03:47 -04:00
renovate[bot] 9dcae193a9 Update serialization to v1.9.0 (#1479)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 17:03:36 -04:00
renovate[bot] 36fac0f3f4 Update dependency com.android.tools.build:apksig to v8.11.0 (#1473)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 17:03:26 -04:00
renovate[bot] 24a4c176c0 Update plugin buildconfig to v5.6.7 (#1469)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 17:03:17 -04:00
renovate[bot] 9c7f50e91e Update kotlin monorepo to v2.2.0 (#1466)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 17:03:07 -04:00
renovate[bot] e52bc255f3 Update dependency org.jsoup:jsoup to v1.21.1 (#1465)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 17:02:58 -04:00
Weblate (bot) ae4c9887d8 Translations update from Hosted Weblate (#1471)
* Weblate translations

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: N'Num Yutthaphon Inchaiya <yutthaphon30667@gmail.com>
Co-authored-by: Psico <psikenji@users.noreply.hosted.weblate.org>
Co-authored-by: UnknownSkyrimPasserby <f7022961@opayq.com>
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/pl/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/pt/
Translation: Suwayomi/Suwayomi-Server

* Deleted translation using Weblate (Thai)

* Deleted translation using Weblate (Portuguese (Portugal))

* Update languages.json

---------

Co-authored-by: N'Num Yutthaphon Inchaiya <yutthaphon30667@gmail.com>
Co-authored-by: Psico <psikenji@users.noreply.hosted.weblate.org>
Co-authored-by: UnknownSkyrimPasserby <f7022961@opayq.com>
Co-authored-by: Syer10 <Mitchellptbo@gmail.com>
Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2025-06-28 17:02:46 -04:00
Mitchell Syer 52201e2488 Add private to trackrecords filter (#1468)
* Add private to trackrecords filter

* Remove sourceMapping from base

* Format
2025-06-26 22:04:26 -04:00
renovate[bot] c3e2b0e002 Update dependency io.mockk:mockk to v1.14.4 (#1464)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-22 20:19:08 -04:00
renovate[bot] 709915bf59 Update dependency io.javalin:javalin to v6.7.0 (#1460)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-22 20:17:52 -04:00
renovate[bot] 9d7ec6fd60 Update dependency io.mockk:mockk to v1.14.3 (#1462)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-22 20:17:46 -04:00
Mitchell Syer ee9de376a3 Fix new private parameter in tracking backup (#1463)
* Fix new private parameter in tracking backup

* Cleanup uneeded models
2025-06-22 20:17:31 -04:00
Mitchell Syer b54dc6f967 Fix Tracking DisplayScore (#1461) 2025-06-22 15:53:25 -04:00
Mitchell Syer abea85d831 Update Tracking Backend (#1457)
* Update Tracking Library

* Update Bangumi

* Update Anilist

* Update MangaUpdates

* Update MAL

* Add private to bind track

* Use null

* Remove old nullable

* Remove custom implementation of supportsTrackDeletion

* Add private to updateTrack

* Some descriptions

* Another description
2025-06-22 10:38:22 -04:00
Constantin Piber 972137c035 Paint: Support Typeface (#1459)
* Paint: Support Typeface

* Paint: Undo textSize transform
2025-06-22 10:38:09 -04:00
Mitchell Syer 1cdef5e0ee Use Kotlin AppDirs (#1453) 2025-06-21 12:02:14 -04:00
Constantin Piber bd7ea64b02 Add an abort handler and preload it on Linux (#1456)
Some native code (CEF) may cause SIGTRAP to be sent on fatal errors,
which brings down the entire server. Instead only kill the thread and
attempt to continue.
2025-06-21 12:02:05 -04:00
Constantin Piber 20c850c10b Implement Bitmap.copy, text layouting (#1455)
* Bitmap: Use provided config

* Bitmap: implement copy

* Bitmap: Simplify getPixels

This also fixes a bug where the returned data may not be in the correct
format

Android getPixels():
> The returned colors are non-premultiplied ARGB values in the sRGB color space.
BufferedImage getRGB():
> Returns an array of integer pixels in the default RGB color model (TYPE_INT_ARGB) and default sRGB color space

* Stub TextPaint and Paint

* Paint: Implement some required functions

* Stub StaticLayout and Layout

* Implement some Paint support

* Draw Bounds

* WebP write support

* First text rendering

* Paint: Fix text size, font metrics

* Paint: Fix not copying new properties

Fixes font size in draw

* Canvas: Stroke add cap/join for better aliasing

Otherwise we get bad artifacts on sharp corners

Based on https://stackoverflow.com/a/35222059/

* Remove logs

* Canvas: Implement other drawText methods

* Bitmap: support erase

* Layout: Fix text direction

Should be LTR, otherwise 0 is read, which is automatically interpreted
as RTL without explicit check

* Bitmap: scale to destination rectangle

* Canvas: drawBitmap with just x/y

* Bitmap: Convert image on JPEG export to RGB

JPEG does not support alpha, so will throw "bogus color space"

* Switch to newer fork
2025-06-21 12:01:56 -04:00
Constantin Piber 0b021e6c42 Increase WebView compatibility (#1451)
* LoadData: Use regular load but intercept request

The method we used before, `createBrowserWithHtml`, is implemented by
KCEF. This method creates a `file://` url and adds handlers for that.
Instead, use regular `createBrowser` and intercept the request later on.
This has the effect of creating the page with the correct origin, while
still setting the requested HTML instead of live data. This is important
for scripts due to CORS.

Also fixes a mistake in the ResourceRequestHandler, where (a) the status
was not set, resulting in ABORT, (b) the return value of `readResponse`
was correct (`false` too early) and (c) the callback was unnecessarily
called on the MainLoop.
Based on https://stackoverflow.com/a/52423252/

* Convince the compiler we're doing it right

Invoking "public final" methods would fail. Not sure why this only
happens for some extensions, but it does. We need to tell the compiler
we're sure we have access to it, for some reason...

* JS: Invoke result handler on the loop

Some extensions call WebView methods on the result, so this should be on
the same loop as the WebView itself

* JS: Await arguments

* Fix using wrong URL property for errors
2025-06-20 12:21:25 -04:00
renovate[bot] 0d109cdd4f Update graphqlkotlin to v8.8.1 (#1450)
* Update graphqlkotlin to v8.8.1

* Remove manual graphql-java-core update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2025-06-20 12:21:13 -04:00
Syer10 1dab9e1a7d Fix database migration 2025-06-15 17:30:24 -04:00
Syer10 593b01819d Update locales 2025-06-15 17:16:51 -04:00
renovate[bot] 029aa9c01b Update plugin buildconfig to v5.6.6 (#1449)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-15 17:15:26 -04:00
schroda 149b549d8d Handle chapter marked as downloaded without downloaded files (#1448)
In case a chapter was marked as downloaded but did not have any downloaded files, an uncaught exception was thrown
2025-06-15 17:15:10 -04:00
renovate[bot] 786635010e Update dependency com.squareup.okio:okio to v3.13.0 (#1447)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-15 17:14:57 -04:00
Weblate (bot) d7fe170067 Weblate translations (#1445)
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/de/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/ta/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/zh_Hans/
Translation: Suwayomi/Suwayomi-Server

Co-authored-by: Constantin Piber <cp.piber@gmail.com>
Co-authored-by: Poesty Li <poesty7450@gmail.com>
Co-authored-by: தமிழ்நேரம் <anishprabu.t@gmail.com>
2025-06-15 17:14:44 -04:00
renovate[bot] e3d4be9a5a Update dependency org.bouncycastle:bcprov-jdk18on to v1.81 (#1443)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-15 17:14:29 -04:00
schroda 4086a73727 Feature/backup suwayomi data (#1430)
* Export meta data

* Import meta data

* Add missing "opdsUseBinaryFileSize" setting to gql

* Export server settings

* Import server settings

* Streamline server config enum handling

* Use "restore amount" in backup import progress
2025-06-15 17:14:13 -04:00
schroda 483e3a760f Increase chapter scanlator column max length (#1425) 2025-06-15 17:13:57 -04:00
renovate[bot] 2757f881dc Update plugin ktlint to v12.3.0 (#1403)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-15 17:13:39 -04:00
renovate[bot] e2aff0ece7 Update dependency com.pinterest.ktlint:ktlint-cli to v1.6.0 (#1402)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-15 17:13:26 -04:00
Mitchell Syer 327526330f [skip ci] Update README.md 2025-06-12 11:59:41 -04:00
renovate[bot] ea976a4d0f Update dependency io.insert-koin:koin-core to v4.1.0 (#1442)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 11:51:48 -04:00
renovate[bot] 728ada5e70 Update okhttp monorepo to v5.0.0-alpha.16 (#1441)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 11:51:35 -04:00
renovate[bot] c091ac4d67 Update xmlserialization to v0.91.1 (#1400)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 11:50:52 -04:00
schroda ee4c852f1b Always update manga thumbnail on fetch (#1429)
It's possible that the cover changed, but the url is still the same.
In that case the cover never gets updated unless the downloaded/cached file gets deleted
2025-06-12 11:50:18 -04:00
schroda 1a5d334f6c Delete thumbnails during backup import (#1428)
Was accidentally removed with 633ea97848
2025-06-12 11:49:34 -04:00
schroda 7d72ff3514 Fix extracting "startDate" (#1427)
Value of json object was never properly accessed.
Start date can have different formats (yyyy-MM, yyyy-MM-dd) so it's not feasible to format it
2025-06-12 11:49:14 -04:00
schroda e224e91100 Dequeue downloads of removed chapters (#1426)
Otherwise, graphql errors will be caused because the chapters won't be found and are unexpectedly null.

fixes #1358
2025-06-12 11:48:27 -04:00
schroda 7c5edd1b73 Realign chapter number recognition with mihon (#1424)
https://github.com/mihonapp/mihon/commit/6a80305d6c572da6c08c0c69f5c25ff26ecf7383
2025-06-12 11:47:30 -04:00
Weblate (bot) 2621415f7c Weblate translations (#1422)
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/es/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/ja/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/pt/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/vi/
Translation: Suwayomi/Suwayomi-Server

Co-authored-by: Nguyễn Trung Đức <vaicato16@gmail.com>
Co-authored-by: Syer10 <Mitchellptbo@gmail.com>
Co-authored-by: Zereef <rafael.v.veloso@proton.me>
Co-authored-by: marimo <nekomiminimoe@gmail.com>
2025-06-12 11:46:39 -04:00
renovate[bot] a3184c46b6 Update dependency com.android.tools.build:apksig to v8.10.1 (#1419)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 11:45:36 -04:00
renovate[bot] 1575ffa6ae Migrate config renovate.json (#1420)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 11:45:24 -04:00
renovate[bot] ee27da3de6 Update dependency com.github.Suwayomi:exposed-migrations to v3.8.0 (#1421)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 11:45:12 -04:00
renovate[bot] 83a7224f2d Update exposed to v0.61.0 (#1291)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 11:44:58 -04:00
schroda ecea2ecdf5 Fix/initial scheduling of global update (#1416)
* Fix setting initial global update delay

In case no update has run yet, and the "last automated update" defaulted to 0, the calculation always resulted in a multiple of the interval.

This resulted for e.g., interval 24, to always be scheduled at 00:00.
For e.g., interval 6, it was always one of the following times: 00:00, 06:00, 12:00, 18:00; depending on the current system time.

* Delete the existing "last automated update time"

So that the update will be triggered based on the time the server got started again after the update.

* Extract migrations into separate functions

* Cleanup migration execution logic
2025-06-12 11:44:38 -04:00
renovate[bot] f04060b31b Update graphqlkotlin to v8.8.0 (#1365)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 11:43:56 -04:00
renovate[bot] d411d1966a Update dependency com.graphql-java:graphql-java to v22.4 (#1401)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 11:43:25 -04:00
AwkwardPeak7 507bf07104 implement setSummaryOn and setSummaryOff in TwoStatePreference (#1431) 2025-06-12 11:41:31 -04:00
Constantin Piber 09061a38bc Negation missing in SystemProperties (#1433) 2025-06-12 11:41:08 -04:00
renovate[bot] 611a7db2e1 Update dependency gradle to v8.14.2 (#1435)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 11:40:46 -04:00
David Brochero a5cf428ce5 doc: Add Neko integration instructions (#1440)
* doc: Add Neko integration instructions

* Update README.md

---------

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2025-06-12 11:40:30 -04:00
renovate[bot] 31f06a2d43 Update dependency com.squareup.okio:okio to v3.12.0 (#1418)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 11:39:38 -04:00
schroda 1d7a60b630 Automatically truncate required varchar columns (#1423) 2025-06-12 11:39:19 -04:00
Constantin Piber a2fadbe513 Implement WebView via Playwright (#1434)
* Implement Android's Looper

Looper handles thread messaging. This is used by extensions when they
want to enqueue actions e.g. for sleeping while WebView does someting

* Stub WebView

* Continue stubbing ViewGroup for WebView

* Implement WebView via Playwright

* Lint

* Implement request interception

Supports Yidan

* Support WebChromeClient

For Bokugen

* Fix onPageStarted

* Make Playwright configurable

* Subscribe to config changes

* Fix exposing of functions

* Support data urls

* Looper: Fix infinite sleep

* Looper: Avoid killing the loop on exception

Just log it and continue

* Pump playwright's message queue periodically

https://playwright.dev/java/docs/multithreading#pagewaitfortimeout-vs-threadsleep

* Update server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SettingsType.kt

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>

* Stub a KCef WebViewProvider

* Initial Kcef Webview implementation

Still buggy, on the second call it just seems to fall over

* Format, restructure to create browser on load

This is much more consistent, before we would sometimes see errors from
about:blank, which block the actual page

* Implement some small useful properties

* Move inline objects to class

* Handle requests in Kcef

* Move Playwright implementation

* Document Playwright settings, fix deprecated warnings

* Inject default user agent from NetworkHelper

* Move playwright to libs.versions.toml

* Lint

* Fix missing imports after lint

* Update server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>

* Fix default user agent set/get

Use System.getProperty instead of SystemProperties.get

* Configurable WebView provider implementation

* Simplify Playwright settings init

* Minor cleanup and improvements

* Remove playwright WebView impl

* Document WebView for Linux

---------

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2025-06-12 11:38:54 -04:00
Mitchell Syer dee61e191c [ci skip] Add Translation into to README 2025-05-27 16:26:23 -04:00
renovate[bot] 32b6461c6a Update dependency io.javalin:javalin to v6.6.0 (#1364)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-26 20:46:48 -04:00
renovate[bot] 93fff42693 Update dependency gradle to v8.14.1 (#1398)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-26 20:46:35 -04:00
Zeedif 61f429896c feat(opds): implement full internationalization and refactor feed gen… (#1405)
* feat(opds): implement full internationalization and refactor feed generation

This commit introduces a comprehensive internationalization (i18n) framework
and significantly refactors the OPDS v1.2 implementation for improved
robustness, spec compliance, and localization.

Key changes:

Internationalization (`i18n`):
- Introduces `LocalizationService` to manage translations:
    - Loads localized strings from JSON files (e.g., `en.json`, `es.json`)
      stored in a new `i18n` data directory.
    - Default `en.json` and `es.json` files are bundled and copied from
      resources on first run if not present.
    - Supports template resolution with `$t()` cross-references, locale
      fallbacks (to "en" by default), and argument interpolation ({{placeholder}}).
- `ServerSetup` now initializes the `i18n` directory and `LocalizationService`.

OPDS Refactor & Enhancements:
- Replaces the previous `Opds.kt` and `OpdsDataClass.kt` with a new
  `OpdsFeedBuilder.kt` and a set of more granular, spec-aligned XML
  models (e.g., `OpdsFeedXml`, `OpdsEntryXml`, `OpdsLinkXml`).
- Integrates `LocalizationService` throughout all OPDS feeds:
    - All user-facing text (feed titles, entry titles, summaries,
      link titles, facet labels for sorting/filtering) is now localized.
    - Adds a `lang` query parameter to all OPDS endpoints to allow
      clients to request a specific UI language.
    - Uses the `Accept-Language` header as a fallback for language detection.
- The OpenSearch description (`/search` endpoint) is now localized and
  its template URL includes the determined language.
- Centralizes OPDS constants (namespaces, link relations, media types)
  in `OpdsConstants.kt`.
- Adds utility classes `OpdsDateUtil.kt`, `OpdsStringUtil.kt`, and
  `OpdsXmlUtil.kt` for common OPDS tasks.
- `MangaDataClass` now includes `sourceLang` to provide the content
  language of the manga in OPDS entries (`<dc:language>`).
- Updates OpenAPI documentation for OPDS endpoints with more detail
  and includes the new `lang` parameter.

Configuration:
- Adds `useBinaryFileSizes` server configuration option. File sizes in
  OPDS feeds now respect this setting (e.g., MiB vs MB), utilized via
  `OpdsStringUtil.formatFileSizeForOpds`.

This major refactor addresses the request for internationalization
originally mentioned in PR #1257 ("it would be great if messages were
adapted based on the user's language settings"). It builds upon the
foundational OPDS work in #1257 and subsequent enhancements in #1262,
#1263, #1278, and #1392, providing a more stable and extensible
OPDS implementation. Features like localized facet titles from #1392
are now fully integrated with the i18n system.

This resolves long-standing requests for better OPDS support (e.g., issue #769)
by making feeds more user-friendly, accessible, and standards-compliant,
also improving the robustness of features requested in #1390 (resolved by #1392)
and addressing underlying data needs for issues like #1265 (related to #1277, #1278).

* fix(opds): revert MIME type to application/xml for browser compatibility

* fix(opds): use chapter index for metadata feed and correct link relation

- Change `getChapterMetadataFeed` to use `chapterIndexFromPath` (sourceOrder)
  instead of `chapterIdFromPath` for fetching chapter data, ensuring
  consistency with how chapters are identified in manga feeds.
- Add error handling for cases where manga or chapter by index is not found.
- Correct OPDS link relation for chapter detail/fetch link in non-metadata
  chapter entries from `alternate` to `subsection` as per OPDS spec
  for navigation to more specific content or views.

* Use Moko-Resources

* Format

* Forgot the Languages.json

* refactor(opds)!: restructure OPDS feeds and introduce data repositories

This commit significantly refactors the OPDS v1.2 implementation by introducing dedicated repository classes for data fetching and by restructuring the feed generation logic for clarity and maintainability. The `chapterId` path parameter for chapter metadata feeds has been changed to `chapterIndex` (sourceOrder) to align with how chapters are identified in manga feeds.

BREAKING CHANGE: The OPDS endpoint for chapter metadata has changed from `/api/opds/v1.2/manga/{mangaId}/chapter/{chapterId}/fetch` to `/api/opds/v1.2/manga/{mangaId}/chapter/{chapterIndex}/fetch`. Clients will need to update to use the chapter's source order (index) instead of its database ID.

Key changes:
- Introduced `MangaRepository`, `ChapterRepository`, and `NavigationRepository` to encapsulate database queries and data transformation logic for OPDS feeds.
- Moved data fetching logic from `OpdsFeedBuilder` to these new repositories.
- `OpdsFeedBuilder` now primarily focuses on constructing the XML feed structure using DTOs provided by the repositories.
- Renamed `OpdsMangaAcqEntry.thumbnailUrl` to `rawThumbnailUrl` for clarity.
- Added various DTOs (e.g., `OpdsRootNavEntry`, `OpdsMangaDetails`, `OpdsChapterListAcqEntry`) to define clear data contracts between repositories and the feed builder.
- Simplified `OpdsV1Controller` by reorganizing feed endpoints into logical groups (Main Navigation, Filtered Acquisition, Item-Specific).
- Updated `OpdsAPI` to reflect the path parameter change for chapter metadata (`chapterIndex` instead of `chapterId`).
- Added `slugify()` utility to `OpdsStringUtil` for creating URL-friendly genre IDs.
- Standardized localization keys for root feed entry descriptions to use `*.entryContent` instead of `*.description`.
- Added `server.generated.BuildConfig` (likely from build process).

* style(opds): apply ktlint fixes

* Delete server/bin

* refactor(i18n): remove custom LocalizationService initialization

* refactor(i18n): remove unused imports from ServerSetup

* refactor(model): remove sourceLang from MangaDataClass

* refactor(opds): rename OPDS binary file size config property

- Rename `useBinaryFileSizes` to `opdsUseBinaryFileSizes` in code and config
- Update related condition check in formatFileSizeForOpds

BREAKING CHANGE: Existing server configurations using `server.useBinaryFileSizes` need to migrate to `server.opdsUseBinaryFileSizes`

* refactor(opds): improve OPDS endpoint structure and documentation

- Restructure endpoint paths for better resource hierarchy
- Add descriptive comments for each feed type and purpose
- Rename `/fetch` endpoint to `/metadata` for clarity
- Standardize feed naming conventions in route definitions

BREAKING CHANGE: Existing OPDS client integrations using old endpoint paths (`/manga/{mangaId}` and `/chapter/{chapterIndex}/fetch`) require updates to new paths (`/manga/{mangaId}/chapters` and `/chapter/{chapterIndex}/metadata`)

* fix(opds): Apply review suggestions for localization and comments

* Fix

* fix(opds): Update chapter links to include 'chapters' and 'metadata' in URLs

---------

Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2025-05-26 20:46:14 -04:00
schroda a9e03837a3 Exclude web manifest file from requiring authentication (#1414) 2025-05-26 20:46:02 -04:00
schroda 89421946af Properly deschedule active tasks (#1413) 2025-05-26 20:45:48 -04:00
Mitchell Syer 218af8ea54 Update Gradle Wrapper Validation (#1412)
* Update Gradle Wrapper Validation

* Use v4
2025-05-26 20:45:31 -04:00
Edge At Zero d0f79ca473 Fix: Validate zipEntry directories during extension asset decompression (#1407) 2025-05-26 20:45:20 -04:00
Mitchell Syer ec870759cf Add highest numbered chapter function in MangaType (#1397)
* Add highest numbered chapter function in MangaType

* Fix name
2025-05-22 19:58:09 -04:00
Shirish 0405a535c7 Feat: Adds OPDS Chapter Filtering/Ordering (#1392)
* Adds server level configs for OPDS

* PR comments

* Refactor server-reference.conf (itemsPerPage range)

* Coerce itemsPerPage (10, 5000) and default invalid sort orders to DESC

* Coerce itemsPerPage (10, 5000) and default invalid sort orders to DESC

* Change opdsChapterSortOrder type to Enum(SortOrder)

* Fix serialization of SortOrderEnum & Add `opdsShowOnlyDownloadedChapters` config
2025-05-22 19:57:55 -04:00
renovate[bot] 814e4ba744 Update kotlin monorepo to v2.1.21 (#1383)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-22 19:57:38 -04:00
renovate[bot] 5621c1ab58 Update dependency com.android.tools.build:apksig to v8.10.0 (#1376)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-22 19:57:25 -04:00
renovate[bot] f1fd8bc446 Update dependency io.mockk:mockk to v1.14.2 (#1371)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-22 19:57:11 -04:00
renovate[bot] 60fdd6cda9 Update dependency org.jsoup:jsoup to v1.20.1 (#1369)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-22 19:56:57 -04:00
renovate[bot] 3332363a10 Update plugin buildconfig to v5.6.5 (#1368)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-22 19:56:44 -04:00
Mitchell Syer 538bd3f126 Improve Downloads Handling (#1387)
* Improve Downloads Handling

* Update known pagecount for downloaded chapters

* Get fresh data for downloadReady

* Format

* Assume downloaded if first page is found

* Filter out ComicInfoFile
2025-05-16 15:57:53 -04:00
Mitchell Syer 336f985894 Fix Downloaded pages with no cached pages from source (#1386) 2025-05-16 12:45:39 -04:00
BrutuZ ba6687355e Ignore hidden folders/archives for Local Source chapter list (#1377) 2025-05-16 12:45:31 -04:00
Mitchell Syer 983980d8da Add Alternatives to deb package (#1375) 2025-05-06 16:39:43 -04:00
renovate[bot] 82d4a401fd Update dependency gradle to v8.14 (#1363)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 19:41:21 -04:00
renovate[bot] 76e9f42734 Update dependency com.squareup.okio:okio to v3.11.0 (#1362)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 19:37:00 -04:00
renovate[bot] 0c0035370a Update polyglot to v24.2.1 (#1360)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 19:34:24 -04:00
renovate[bot] a3ac136b3b Update dependency adoptium/temurin21-binaries to jdk-21.0.7+6 (#1359)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 19:34:11 -04:00
renovate[bot] ed1509b54f Update dependency io.mockk:mockk to v1.14.0 (#1341)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 19:33:58 -04:00
renovate[bot] 1d0dcd097c Update kotlinx-coroutines monorepo to v1.10.2 (#1337)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 19:33:45 -04:00
schroda 785c0469ac Fix/m0045 prevent duplicated chapter pages migration (#1361)
* Fix "imageUrl" column name in migration

* Rename column "imageUrl" to "IMAGE_URL" of table "Page"
2025-04-27 19:33:15 -04:00
renovate[bot] 7594ae5fa5 Update xmlserialization to v0.91.0 (#1331)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 18:56:42 -04:00
renovate[bot] 6b4e08fdd1 Update serialization to v1.8.1 (#1330)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 18:56:19 -04:00
renovate[bot] 65435341f3 Update dependency io.github.oshai:kotlin-logging-jvm to v7.0.7 (#1329)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 18:56:12 -04:00
renovate[bot] 9bc9f963b7 Update dependency io.insert-koin:koin-core to v4.0.4 (#1326)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 18:55:58 -04:00
renovate[bot] a27501371f Update plugin buildconfig to v5.6.3 (#1322)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 18:55:43 -04:00
renovate[bot] 9fafebc8e7 Update dependency com.android.tools.build:apksig to v8.9.2 (#1321)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 18:55:07 -04:00
schroda 59d2151c92 Prevent duplicated chapter pages (#1353)
In case "ChapterForDownload#asDownloadReady" was called in quick succession, the page list got inserted twice.

This caused problems with getting the images from the rest endpoint, because they are selected by sorting them by asc index and selecting the page by using the provided index as an offset.

This, however, only works as long as there are no duplicates, otherwise, page indexes 1, 2; 3, 4; 5, 6; ... will just return the same page.
2025-04-27 18:54:54 -04:00
schroda 1cc2a05f90 [ci skip] Update outdated install instructions in README (#1356)
* Update outdated install instructions in README

* Update README.md
2025-04-27 18:15:38 -04:00
schroda 8aea6f5473 [ci skip] Update feature list in README (#1355) 2025-04-27 18:14:53 -04:00
Syer10 22df7e3074 Release v2.0.1727
CI Publish / Validate Gradle Wrapper (push) Successful in 2m14s
CI Publish / jlink (linux-x64, ubuntu-latest) (push) Failing after 1m6s
CI Publish / Build Jar (push) Failing after 2m8s
CI Publish / jlink (macOS-arm64, macos-14) (push) Has been cancelled
CI Publish / jlink (macOS-x64, macos-13) (push) Has been cancelled
CI Publish / jlink (windows-x64, windows-latest) (push) Has been cancelled
CI Publish / Make debian-all release (push) Has been cancelled
CI Publish / Make linux-assets release (push) Has been cancelled
CI Publish / Make linux-x64 release (push) Has been cancelled
CI Publish / Make macOS-arm64 release (push) Has been cancelled
CI Publish / Make macOS-x64 release (push) Has been cancelled
CI Publish / Make windows-x64 release (push) Has been cancelled
CI Publish / release (push) Has been cancelled
2025-04-21 13:32:42 -04:00
schroda 1d5323a477 [skip ci] Add link to discord in issue templates (#1347) 2025-04-16 18:10:37 -04:00
KAAAsS f8d73819ea [Feature] Support Bangumi Tracker (#1343)
* feat: Support Bangumi Tracker

Credits: Andreas, AntsyLich, Caleb Morris, Gauthier, MCAxiaz, MajorTanya, NarwhalHorns, arkon, fei long, jmir1, mutsumi, stevenyomi

* Use Suwayomi api keys

---------

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2025-04-12 19:34:04 -04:00
Syer10 cbe26b7291 Chmod in build script 2025-04-08 12:48:09 -04:00
Syer10 93477f60c2 Fix release name 2025-04-08 12:36:42 -04:00
Syer10 9feebbfe17 Use tar for MacOS 2025-04-08 12:34:24 -04:00
Mitchell Syer 6e365491a9 Add permissions to jspawnhelper (#1339) 2025-04-08 12:16:37 -04:00
Mitchell Syer 2e58658129 Fix MacOS builds (#1338) 2025-04-08 11:59:38 -04:00
schroda 256c564b91 Fix release version extraction from jar name (#1335)
Broke due to the changes made in 3167d8aa15
2025-04-06 15:53:04 -04:00
schroda 96b50f52ec Ensure webui "channel" is always of corresponding enum (#1334)
The "channel" was just the string from the config file, which will never equal the enum unless via case-insensitive comparison
2025-04-06 15:10:07 -04:00
schroda 3167d8aa15 Fix/startup jvm error after installation update via msi (#1229)
* Remove existing installations with msi installer

* Remove unused x86 wxs file

* Uninstall old msi versions with different upgrade code

* Progress but error 2721 happens on install

* Remove added uninstall previous version wxs stuff

* Use revision as patch number

MSI only uninstalls previous versions in case the version number changed (it only checks the first three numbers (major, minor, patch)).
Thus, to prevent each preview install to result in it getting registered as a new "app" and for it to uninstall the old versions, we have to change the version on each release.

* Deprecate "BuildConfig.REVISION"

* Remove outdated env vars

---------

Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2025-04-06 15:09:56 -04:00
schroda 78fd09c728 Prevent IndexOutOfBoundsException in "libraryUpdate" subscription (#1320) 2025-03-22 22:50:25 -04:00
schroda 4c5598cedf Feature/graphql log execution exceptions (#1319)
* Log exceptions during graphql execution

Exceptions got swallowed by graphql

* Add stack trace to error in graphql response

Depending on the exceptions error message, the error in the response might be quite useless (e.g. "Stub!" error in android classes)
2025-03-22 19:35:16 -04:00
schroda c3347d94ab Feature/use GitHub issue yml format (#1314)
* Update github issue templates to yml format

* Remove "issue moderator" workflow

* Require more client info for "bug issues"

- client name
- client version
2025-03-22 19:35:08 -04:00
Mitchell Syer 7ca4aa75a8 Fix checkbox preference title nullability (#1313) 2025-03-22 19:35:02 -04:00
schroda 226fad5594 Remove "default" category from backups (#1307)
Restoring a suwayomi backup in mihon created a category named "Default"
2025-03-22 19:34:57 -04:00
schroda d0ee1ba5af Align kitsu icon with icons of other trackers (#1303)
The used icon for kitsu has a transparent background while all other tracker icons have a background color
2025-03-22 19:34:50 -04:00
schroda 439e0c8284 Emit only updater job changes instead of full status (#1302)
The update subscription emitted the full update status, which, depending on how big the status was, took forever because the graphql subscription does not support data loader batching, causing it to run into the n+1 problem
2025-03-22 19:34:43 -04:00
renovate[bot] 7d079a8728 Update kotlin monorepo to v2.1.20 (#1315)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-22 19:34:30 -04:00
renovate[bot] 945a52653e Update graphqlkotlin to v8.4.0 (#1311)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-22 19:34:23 -04:00
renovate[bot] bdafc86990 Update polyglot to v24.2.0 (#1310)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-22 19:34:13 -04:00
renovate[bot] 57d425ab9f Update dependency ch.qos.logback:logback-classic to v1.5.18 (#1309)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-22 19:34:06 -04:00
renovate[bot] 395ac8e944 Update dependency com.ibm.icu:icu4j to v77 (#1305)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-22 19:34:00 -04:00
renovate[bot] 2f801e7571 Update plugin buildconfig to v5.5.4 (#1304)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-22 19:33:53 -04:00
renovate[bot] d7636045fe Update dependency io.javalin:javalin to v6.5.0 (#1301)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-22 19:33:46 -04:00
renovate[bot] b745f10870 Update dependency org.jsoup:jsoup to v1.19.1 (#1292)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-22 19:33:38 -04:00
renovate[bot] d76849942c Update plugin buildconfig to v5.5.2 (#1299)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 11:31:38 -05:00
renovate[bot] b7a8a3ffe8 Update dependency io.github.oshai:kotlin-logging-jvm to v7.0.5 (#1298)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 11:31:26 -05:00
Shirish 95d9293fe0 Add support for opds-pse for undownloaded chapters (#1278)
* Add OPDS page streaming for undownloaded chapters

* Add [D] in chapter title prefix when isDownloaded

* Removed Chapter.isDownloaded check in query for other opds endpoints

* Add chapter progression tracking for streaming and refactor code

* dd  Unicode for chapters with 0 pages [post pageRefresh]

* Add Library Updates feed and remove redundant metadata fetching for OPDS chapters and manga

* Address PR comments & add chapter markAsRead for cbzDownload

* Address PR comment/s

* Rem. markAsRead for chapter download

* Rem. markAsRead for chapter download

---------

Co-authored-by: ShowY <showypro@gmail.com>
2025-03-08 11:31:07 -05:00
Constantin Piber 3be165a551 Initial import of Kitsu tracker (#1297)
* Initial import of Kitsu tracker

Based on Mihon 6c6ea84509cc1bd859c880bebbc69067a241b358 because its
successor 9f99f03 relies on incompatible changes

* Kitsu: Avoid stupid long/int cast
2025-03-08 11:30:59 -05:00
renovate[bot] cb498e2128 Update dependency org.slf4j:slf4j-api to v2.0.17 (#1284)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 11:30:35 -05:00
renovate[bot] 973f4d66e2 Update jackson monorepo to v2.18.3 (#1289)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 11:30:02 -05:00
renovate[bot] 2c80672f6e Update plugin ktlint to v12.2.0 (#1288)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 11:29:55 -05:00
renovate[bot] 2599813ef1 Update dependency io.mockk:mockk to v1.13.17 (#1287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 11:29:48 -05:00
renovate[bot] 86f849a185 Update dependency net.harawata:appdirs to v1.4.0 (#1286)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 11:29:39 -05:00
renovate[bot] 875f1f1506 Update dependency com.android.tools.build:apksig to v8.9.0 (#1285)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 11:29:22 -05:00
renovate[bot] e418375963 Update dependency ch.qos.logback:logback-classic to v1.5.17 (#1283)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 11:29:07 -05:00
renovate[bot] d528fc7f9e Update dependency gradle to v8.13 (#1282)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 11:28:59 -05:00
schroda 633ea97848 Feature/optimize backup import (#1270)
* Optimize restoring manga chapters

* Streamline restoring manga data

* Optimize restoring manga trackers

* Simplify passing manga category restore data

* Properly prevent mangas from getting added to default category

76595233fc never actually worked...

* Extract logic to add manga to categories from gql mutation

* Optimize restoring manga categories

* Optimize restoring categories
2025-02-16 13:00:26 -05:00
schroda 36cb899b91 Prevent chapter lastReadPage coerceIn error (#1272)
coerceIn throws an error in case the max value is less than the min value ("Cannot coerce value to an empty range: maximum <max> is less than minimum <min>")

Regression from c8bd39b4bf
2025-02-14 23:05:04 -05:00
schroda c4d849d6a3 Fix invalid chapter download state in database (#1271)
* Fix invalid chapter download state in database

Should have been added with 37f57c0c55

* Improve "fix chapter invalid download state" migrations
2025-02-14 21:02:10 -05:00
schroda c8bd39b4bf Prevent negative lastPageRead values (#1267)
Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2025-02-14 19:06:54 -05:00
schroda 733ba16af2 Fix/backup with duplicated chapters failure (#1269)
* Extract logic to restore manga chapters into function

* Extract logic to restore manga categories into function

* Extract logic to restore manga trackers into function

* Handle duplicated chapters in backup

In case a backup contained duplicated chapters for a manga, the manga failed to restore since the ChapterTable has a unique constraint to prevent multiple chapters with the same "url" and "mangaId"
2025-02-14 19:02:02 -05:00
schroda 37f57c0c55 Prevent marking chapter as downloaded without pages (#1268)
The Downloader marked chapters without any pages incorrectly as downloaded.
2025-02-14 19:01:46 -05:00
renovate[bot] 013dbd79b4 Update dependency com.android.tools.build:apksig to v8.8.1 (#1264)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 19:01:37 -05:00
Zeedif f76d0b3258 Remove redundant code and add next/prev links (#1263) 2025-02-10 20:34:24 -05:00
Zeedif c2f7cdd72e Refactoring OPDS API for a more versatile root, allowing selection of manga listing by: all, source, genre, category, language, status. (#1262)
* Añadiendo algunos cambios iniciales para probar OPDS

* Add suport to OPDS v1.2

* Added support for OPDS-PSE and reorganized controllers

* Rename chapterIndex to chapterId in the API and controller, and update descriptions in OPDS

* Refactor OPDS to use formatted timestamps and proxy thumbnail URLs

* Refactor OPDS to use formatted timestamps and proxy thumbnail URLs

* Update Manga API to download chapters cbz using only chapterId and improve chapter download query

* Optimize OPDS queries

* Update Manga API to download chapters cbz using only chapterId and improve chapter download query

* Optimize OPDS queries

* Use SourceDataClass to map sources and optimize thumbnail URL retrieval

* Kotlin lint errors in ChapterDownloadHelper and Opds

* Kotlin lint errors in ChapterDownloadHelper and Opds

* Refactor OPDS API endpoints and rename OpdsController to OpdsV1Controller

* Translate OpdsV1Controller comments to English and remove unused imports

* Translate comments in OpdsAPI.kt to English

* Add SearchCriteria class and update OpdsV1Controller

* Remove spanish comments

* Refactor search handling in OpdsV1Controller and update search feed endpoint

* Fix search
2025-02-09 16:03:18 -05:00
schroda 01c37cb0ba Ignore authentication for preflight requests (#1261)
Cors preflight requests never include credentials (https://fetch.spec.whatwg.org/#cors-protocol-and-credentials), thus, they always failed due to being unauthorized
2025-02-08 11:53:32 -05:00
renovate[bot] 0dd0af1b84 Update dependency io.github.oshai:kotlin-logging-jvm to v7.0.4 (#1260)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-08 11:53:21 -05:00
Zeedif 26aa684300 Add support for OPDS v1.2 to browse stored CBZ files (#1257)
* Añadiendo algunos cambios iniciales para probar OPDS

* Add suport to OPDS v1.2

* Added support for OPDS-PSE and reorganized controllers

* Rename chapterIndex to chapterId in the API and controller, and update descriptions in OPDS

* Refactor OPDS to use formatted timestamps and proxy thumbnail URLs

* Refactor OPDS to use formatted timestamps and proxy thumbnail URLs

* Update Manga API to download chapters cbz using only chapterId and improve chapter download query

* Optimize OPDS queries

* Update Manga API to download chapters cbz using only chapterId and improve chapter download query

* Optimize OPDS queries

* Use SourceDataClass to map sources and optimize thumbnail URL retrieval

* Kotlin lint errors in ChapterDownloadHelper and Opds

* Kotlin lint errors in ChapterDownloadHelper and Opds
2025-02-08 11:53:06 -05:00
renovate[bot] 9669cdfb76 Update kotlin monorepo to v2.1.10 (#1251)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-08 11:52:54 -05:00
renovate[bot] 2c5e5e283e Update dependency com.github.Suwayomi:exposed-migrations to v3.7.0 (#1248)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-08 11:52:43 -05:00
renovate[bot] 303921c6ea Update dependency gradle to v8.12.1 (#1247)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-08 11:52:31 -05:00
renovate[bot] 593291a60f Update exposed to v0.59.0 (#1228)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-08 11:52:18 -05:00
Mitchell Syer 3af8e395bd Check if file exists (#1246) 2025-01-23 09:38:52 -05:00
Mitchell Syer 0b192cfa52 Normalize Paths (#1245)
* Normalize Paths

* Formatting

* Different format
2025-01-23 09:36:10 -05:00
Mitchell Syer fb8f20f31a Fix MacOS .command file with new JRE (#1243) 2025-01-22 15:49:22 -05:00
renovate[bot] e53386cf72 Update dependency adoptium/temurin21-binaries to jdk-21.0.6+7 (#1241)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 13:33:30 -05:00
renovate[bot] b0e9b9b307 Update polyglot to v24.1.2 (#1240)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 13:33:16 -05:00
renovate[bot] 789678a45d Update dependency io.insert-koin:koin-core to v4.0.2 (#1239)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 13:33:04 -05:00
renovate[bot] 4ad01d3451 Update graphqlkotlin to v8.3.0 (#1235)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 13:32:49 -05:00
renovate[bot] 6a87daa0b3 Update dependency org.bouncycastle:bcprov-jdk18on to v1.80 (#1231)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 13:32:35 -05:00
renovate[bot] aebef87076 Update dependency io.github.reactivecircus.cache4k:cache4k to v0.14.0 (#1230)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 13:32:13 -05:00
Mitchell Syer 32ff58598f Merge Service Files during build to fix GraalJS (#1242) 2025-01-22 13:32:01 -05:00
renovate[bot] 2d56cbe227 Update serialization to v1.8.0 (#1220)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-12 14:50:49 -05:00
renovate[bot] fbef5f592b Update dependency com.android.tools.build:apksig to v8.8.0 (#1227)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-12 14:47:47 -05:00
renovate[bot] 090af36f5e Update dependency com.squareup.okio:okio to v3.10.2 (#1223)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-12 14:47:37 -05:00
renovate[bot] 885f27fcf1 Update dependency net.harawata:appdirs to v1.3.0 (#1219)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-12 14:47:09 -05:00
renovate[bot] 8dc4eaf77a Update dependency io.insert-koin:koin-core to v4.0.1 (#1213)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-12 14:46:58 -05:00
renovate[bot] a55bef08a8 Update kotlinx-coroutines monorepo to v1.10.1 (#1212)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-12 14:46:32 -05:00
renovate[bot] 0dc3089739 Update dependency gradle to v8.12 (#1211)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-12 14:46:22 -05:00
renovate[bot] 789ef0d783 Update dependency io.mockk:mockk to v1.13.16 (#1210)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-12 14:46:11 -05:00
renovate[bot] 092db1106d Update dependency ch.qos.logback:logback-classic to v1.5.16 (#1209)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-12 14:46:01 -05:00
renovate[bot] 5f4b5bc570 Update dependency io.javalin:javalin to v6.4.0 (#1205)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-12 14:45:36 -05:00
Mitchell Syer 32581fcd5a [WIP] Customize JRE (#1177)
* Customize JRE

* Fix build push

* Run test

* Where is jre

* Try this

* Fix debain-all and linux-assets

* Stop ref-master for test

* Revert "Stop ref-master for test"

This reverts commit 8e34a12247087eff643676ef0ac692df4c2700ff.

* Revert "Run test"

This reverts commit dad629aaff2cf5c270b7fffeb98dfb9e3d1c93e5.
2025-01-12 14:45:22 -05:00
robo b14d28c406 new icons (#1222)
Co-authored-by: Robonau <30987265+Robonau@users.noreply.github>
2025-01-09 09:02:37 -05:00
schroda 1d1535dc55 Send dequeue download mutation response (#1218)
Response was never sent due to incorrect updates filter condition
2025-01-01 21:26:01 -05:00
schroda de942440e3 Handle missing track search results on bind (#1196)
It's possible that a manga is bound to a tracker while there is no search result.
This happens when e.g. restoring a backup which includes track bindings for which there was never a tracker search.

In that case when trying to e.g. copy the binding to another manga, the mutation would fail due to not finding a search result.
These cases can be handled by additionally checking the TrackRecordTable to get the necessary track info.
2024-12-10 19:50:16 -05:00
renovate[bot] b58fc39cf1 Update dependency io.github.oshai:kotlin-logging-jvm to v7.0.3 (#1191)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-07 23:56:51 -05:00
Mitchell Syer 2e3af25dd4 Fix usage of deprecated functions (#1192)
* Fix usage of deprecated functions

* lint

* Lint

* Another
2024-12-07 23:56:42 -05:00
schroda 1d541a30ae Feature/update to exposed v0.57.0 (#1150)
* Update to exposed-migrations v3.5.0

* Update to kotlin-logging v7.0.0

* Update to exposed v0.46.0

* Update to exposed v0.47.0

* Update to exposed v0.55.0

* Update to exposed v0.56.0

* Update to exposed v0.57.0
2024-12-07 23:49:11 -05:00
renovate[bot] f926714544 Update dependency com.pinterest.ktlint:ktlint-cli to v1.5.0 (#1182)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-07 14:28:09 -05:00
renovate[bot] f68849d3a5 Update dependency com.russhwolf:multiplatform-settings-jvm to v1.3.0 (#1176)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-07 14:27:54 -05:00
renovate[bot] 2111232f42 Update kotlin monorepo to v2.1.0 (#1170)
* Update kotlin monorepo to v2.1.0

* Fix build warnings

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2024-12-07 14:27:47 -05:00
Mitchell Syer 088552bf56 Fix Deprecation Warnings (#1187) 2024-12-07 14:13:06 -05:00
Mitchell Syer 3eabbc9770 Manually update GraphQL-Java to fix subscription data loaders (#1186) 2024-12-07 14:12:56 -05:00
renovate[bot] 8e3b8df497 Update dependency com.android.tools.build:apksig to v8.7.3 (#1179)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-07 14:12:25 -05:00
renovate[bot] 06a5aaaa72 Update dependency com.fasterxml.jackson.core:jackson-databind to v2.18.2 (#1175)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-07 14:12:00 -05:00
renovate[bot] 97c4f14094 Update dependency org.jsoup:jsoup to v1.18.3 (#1169)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-07 14:11:43 -05:00
renovate[bot] 1fa7f18235 Update plugin ktlint to v12.1.2 (#1166)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-07 14:11:33 -05:00
renovate[bot] b309d2fd4a Update dependency gradle to v8.11.1 (#1161)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-07 14:11:22 -05:00
are-are-are 372b56bb1b add manga description (#1165) 2024-11-23 18:22:21 -05:00
schroda 3325a36cae Allow cors with credentials (#1163)
"anyHost" is not allowed in combination with "Access-Control-Allow-Credentials" (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#sect2).
At least the default webUI always includes credentials which causes a cors policy violation
2024-11-22 20:00:25 -05:00
schroda 38673bbff4 Handle missing credentials as being invalid (#1164)
In case the credentials were missing the basic authentication was just bypassed
2024-11-22 20:00:16 -05:00
Mitchell Syer fb51834153 Fix RealUrl (#1162) 2024-11-20 22:57:42 -05:00
renovate[bot] 3a932a1e8a [skip ci] Update plugin buildconfig to v5.5.1 (#1157)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-19 17:09:04 -05:00
schroda 53c61bcb17 Serve webui on all unmatched routes (#1156)
with "/root" only "http:localhost:4567" opened the webui all other endpoints resulted in "Endpoint GET /endpoint not found"
2024-11-17 21:11:40 -05:00
schroda 6951b4b20d Remove "grapqhl log level" setting (#1155)
internal logging was removed with graphql-java v22.0
2024-11-17 21:11:26 -05:00
Mitchell Syer 746f9f1a11 [WIP] Switch to GraalJS Engine (#793)
* Switch to GraalJS Engine

* Update Polygot
2024-11-17 15:24:08 -05:00
renovate[bot] 9a7344ccbe Update dependency ch.qos.logback:logback-classic to v1.5.12 (#1151)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-17 15:07:59 -05:00
renovate[bot] ab2fb8747f Update jackson monorepo to v2.18.1 (#1148)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-17 15:04:28 -05:00
renovate[bot] 9cd8cb3d54 Update dependency io.javalin:javalin to v6 (#1152)
* Update dependency io.javalin:javalin to v6

* Simple compile fixes

* Simple compile fixes pass 2

* Add results to futures

* Setup jetty server and api routes

* Setup Cors

* Setup basic auth

* Documentation stubs

* Replace chapter mutex cache

* Fix compile

* Disable Jetty Logging

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2024-11-17 15:00:53 -05:00
renovate[bot] ba1c2845b6 chore(deps): update plugin buildconfig to v5 (#1135)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-17 12:50:42 -05:00
renovate[bot] 065aa19e9e Update graphqlkotlin to v8 (major) (#1143)
* Update graphqlkotlin to v8

* Go back to JsonMapper

* Add context to data loaders

* Compile fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2024-11-17 12:50:33 -05:00
Mitchell Syer 4c2a05c3a6 Update Java to 21 (#1149)
* Update Java to 21

* Update Readme
2024-11-17 12:17:39 -05:00
Syer10 fd45c0740c [skip ci] Update Renovate config 2024-11-16 13:00:09 -05:00
Syer10 e44bf920fa [skip ci] Update Renovate config 2024-11-16 12:47:53 -05:00
renovate[bot] 8a327b2dff [skip ci] chore(config): migrate config renovate.json (#1144)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-16 12:41:15 -05:00
Syer10 6ece7e2596 Update Renovate config 2024-11-16 12:37:02 -05:00
renovate[bot] 2e2ce98be3 fix(deps): update dependency com.pinterest.ktlint:ktlint-cli to v1.4.1 (#1126)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 21:39:15 -05:00
renovate[bot] fe4c2392db chore(deps): update plugin ktlint to v12 (#1136)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 21:31:04 -05:00
schroda 320a0971b4 Fix/gql download subscription (#1137)
* Properly set download update type on exceptions

* Always send FINISHED download update to client for deprecated subscription

By the time the status was sent to the client, the finished download item was already removed from the queue, causing the client to never get the latest status, thus, having an outdated cache

Regression introduced with 168b76cb0c
2024-11-15 21:30:09 -05:00
renovate[bot] bfb70b6a05 fix(deps): update dependency com.ibm.icu:icu4j to v76 (#1140)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 21:29:28 -05:00
renovate[bot] a68af62748 fix(deps): update twelvemonkeys to v3.12.0 (#1133)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 21:29:11 -05:00
renovate[bot] 52bd5ce5cc fix(deps): update kotlinx-coroutines monorepo to v1.9.0 (#1132)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 21:28:44 -05:00
renovate[bot] b93d486348 fix(deps): update dependency org.bouncycastle:bcprov-jdk18on to v1.79 (#1127)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 21:28:30 -05:00
renovate[bot] d193c58e5f fix(deps): update dependency androidx.annotation:annotation to v1.9.1 (#1121)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 21:28:15 -05:00
renovate[bot] 6ac2a61793 Update serialization to v1.7.3 (#1119)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 18:09:02 -05:00
renovate[bot] a45c6f2197 Update kotlin monorepo to v2.0.21 (#1117)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 18:08:53 -05:00
renovate[bot] 71d639bf19 Update dependency io.mockk:mockk to v1.13.13 (#1116)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 18:08:41 -05:00
Mitchell Syer 9a51472726 Update Titles from the Source (#1115)
* Update Titles from the source

* Properly keep null fields
2024-11-14 18:08:31 -05:00
Mitchell Syer 0670f298cd Switch from Kodein to Koin (#1112)
* Switch from Kodein to Koin

* Ktlint
2024-11-14 18:08:19 -05:00
schroda aa1e98544b Fix/invalid server settings gql mutation request (#1092)
* Validate setting values on mutation

* Handle invalid negative setting values

* Ensure at least one source is downloading at all times

* Prevent possible IllegalArgumentException

The "serverConfig.maxSourcesInParallel" value could have changed after the if-condition
2024-11-14 18:08:07 -05:00
renovate[bot] fa4607e232 Update dependency gradle to v8.11 (#1091)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 18:07:55 -05:00
renovate[bot] cb46420c09 Update dependency com.android.tools.build:apksig to v8 (#1076)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 18:07:42 -05:00
renovate[bot] d88014fa90 Update xmlserialization (#1075)
* Update xmlserialization

* Fix XML update

* Use Jvm Serialization

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2024-11-14 18:07:29 -05:00
schroda 168b76cb0c Feature/graphql download queue subscription send only updates (#1011)
* Emit only download changes instead of full status

The download subscription emitted the full download status, which, depending on how big the queue was, took forever because the graphql subscription does not support data loader batching, causing it to run into the n+1 problem

* Rename "DownloadManager#status" to "DownloadManager#updates"

* Add initial queue to download subscription type

Adds the current queue at the time of sending the initial message.
This field is null for all following messages after the initial one

* Optionally limit and omit download updates

To prevent the n+1 dataloader issue, the max number of updates included in the download subscription can be limited.
This way, the problem will be circumvented and instead, the latest download status should be (re-)fetched via the download status query, which does not run into this problem.

* Formatting
2024-11-14 18:07:14 -05:00
schroda f5680c6d69 Switch to Koin from Injekt (#1109)
replace "com.github.inorichi.injekt" with "com.github.null2264:injekt-koin"
2024-11-09 11:32:51 -05:00
Mitchell Syer 654a3cc7ed Use Backup.serializer() (#1088) 2024-09-16 21:18:01 -04:00
Mitchell Syer 841cdc474f Remove Broken Sources and Broken History (#1084) 2024-09-15 00:19:47 -04:00
schroda 0adbea3a43 Remove manga artist, author length limit (#1080) 2024-09-15 00:10:23 -04:00
schroda e12bada052 Use correct sync id (#1079) 2024-09-15 00:10:08 -04:00
renovate[bot] 68dbefc46f Update dependency gradle to v8.10.1 (#1072)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-14 09:15:15 -04:00
renovate[bot] 9d71e9b177 Update twelvemonkeys to v3.11.0 (#1069)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-14 09:15:06 -04:00
renovate[bot] 5b5801c2cf Update dependency com.squareup.okio:okio to v3.9.1 (#1071)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-14 09:07:53 -04:00
Antoine Aflalo df1cc2b8e9 fix(flaresolverr): fix cookie expiry for flaresolverr (#1070)
Cookie expiry is returned in seconds, the persistent cache expect it in miliseconds. Cookie is always considered as expired
2024-09-14 09:04:49 -04:00
renovate[bot] 18d399b3f7 Update settings to v1.2.0 (#1068)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-14 09:04:22 -04:00
renovate[bot] e9687fd182 Update serialization to v1.7.2 (#1067)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-14 09:04:13 -04:00
renovate[bot] 79137a074c Update plugin download to v5.6.0 (#1066)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-14 09:04:04 -04:00
renovate[bot] dae55ca386 Update graphqlkotlin to v6.8.5 (#1064)
* Update graphqlkotlin to v6.8.5

* Replace Jackson with Kotlinx.Serialization where possible

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2024-09-14 09:03:53 -04:00
renovate[bot] b7f040d89a Update dependency org.jsoup:jsoup to v1.18.1 (#1060)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-14 09:03:44 -04:00
renovate[bot] dc69df9f4f Update dependency com.squareup.okio:okio to v3.9.0 (#1056)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 21:37:36 -04:00
Syer10 6c1fbfa63b [skip ci] Formatting 2024-09-03 21:37:18 -04:00
renovate[bot] e968a2195a [skip ci] Update dependency com.pinterest.ktlint:ktlint-cli to v1.3.1 (#1055)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 21:36:34 -04:00
Syer10 000bcea181 [skip ci] Update SystemTray 2024-09-03 21:27:02 -04:00
renovate[bot] 8edf508453 [skip ci] Update dependency org.apache.commons:commons-compress to v1.27.1 (#1058)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 21:23:45 -04:00
renovate[bot] bc9cc50130 [skip ci] Update dependency org.bouncycastle:bcprov-jdk18on to v1.78.1 (#1059)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 21:23:26 -04:00
renovate[bot] 71091d88fc [skip ci] Update dependency com.android.tools.build:apksig to v7.4.2 (#1049)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 21:17:59 -04:00
renovate[bot] 70c1d7e21f [skip ci] Update dependency io.github.config4k:config4k to v0.7.0 (#1057)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 21:17:35 -04:00
renovate[bot] c07920978e Update tachiyomiorg/issue-moderator-action action to v2 (#1032)
* Update tachiyomiorg/issue-moderator-action action to v2

* Update issue_moderator.yml

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2024-09-02 21:47:42 -04:00
renovate[bot] 954b2919ac [skip ci] Update plugin ktlint to v11.6.1 (#1043)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-02 21:44:01 -04:00
renovate[bot] c630f731ed [skip ci] Update dependency androidx.annotation:annotation to v1.8.2 (#1046)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-02 21:43:47 -04:00
renovate[bot] aaefa7f74e [skip ci] Update coroutines to v1.8.1 (#1045)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-02 21:43:19 -04:00
renovate[bot] 2ec6b471f1 [skip ci] Update dependency gradle to v8.10 (#1047)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-02 21:43:02 -04:00
Syer10 a5c5ab68d2 [skip ci] Ktlint 2024-09-02 21:28:59 -04:00
Syer10 fb045c501a Update Kotlin to 2.0.20 2024-09-02 21:25:01 -04:00
renovate[bot] 6a6e411492 [skip ci] Update dependency net.harawata:appdirs to v1.2.2 (#1033)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-02 21:15:22 -04:00
renovate[bot] 76aac330fc [skip ci] Update dependency org.slf4j:slf4j-api to v2.0.16 (#1034)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-02 21:14:59 -04:00
renovate[bot] 07bdf31f66 [skip ci] Update okhttp monorepo to v5.0.0-alpha.14 (#1039)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-02 21:14:43 -04:00
renovate[bot] fe14928af6 [skip ci] Update rhino to v1.7.15 (#1044)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-02 21:13:38 -04:00
renovate[bot] ad0c1033a4 [skip ci] Update GitHub Artifact Actions to v4 (#1040)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 22:06:41 -04:00
Mitchell Syer 0c2448fb99 Update gradle build action (#1035) 2024-09-01 21:57:02 -04:00
renovate[bot] cedda145a5 Update gradle/gradle-build-action action to v3 (#1029)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 20:02:46 -04:00
renovate[bot] ee73187f1a [skip ci] Update gradle/wrapper-validation-action action to v3 (#1030)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 20:02:17 -04:00
renovate[bot] 89f91d6800 [skip ci] Update actions/checkout action to v4 (#1028)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 20:02:05 -04:00
renovate[bot] 6714827694 [skip ci] Update softprops/action-gh-release action to v2 (#1031)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 20:01:54 -04:00
renovate[bot] aad73f7d19 [skip ci] Update dependency io.mockk:mockk to v1.13.12 (#1026)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 19:29:54 -04:00
renovate[bot] c5985de1c3 [skip ci] Update dependency com.typesafe:config to v1.4.3 (#1025)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 19:29:41 -04:00
renovate[bot] 86a5b0879a Add renovate.json (#1024)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 19:24:12 -04:00
schroda 414972d545 Feature/update log file rotation (#1023)
* Keep up to 31 log files

On average one log file per day gets created, thus, increasing to 31 files will store log files for one month

* Decrease total log files size to 100mb

* Make log appender settings configurable
2024-08-31 18:55:26 -04:00
Antoine Aflalo 9a74ae5844 feat(comicinfo): add date fields to comic info (#1021)
* feat(comicinfo): add date fields to comic info

This will be parsed by Komga, Kavita etc ... and any other library management to also have the date of the chapter.

* refactor: improve code readability

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>

---------

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2024-08-31 18:55:13 -04:00
Antoine Aflalo 301980ab14 fix(flaresolverr): support possible rewrite flaresolverr (#1020)
This PR:
https://github.com/FlareSolverr/FlareSolverr/pull/1300

Solve a lot of issue with not solving challenge, however, the cookie don't have path, httpOnly, secure and sameSite.

By making them optional that should work for both version of flaresolverr.
2024-08-31 18:55:02 -04:00
schroda 5dced82e5a Fix/missed automated task execution failure crashes server on startup (#1019)
* Catch automated backup task errors

* Catch automated udpate task errors

* Catch automated webui update task errors
2024-08-31 18:54:51 -04:00
schroda 9a1e4df408 Fix/server startup blocked by synchronous tasks (#1018)
* Launch missed auto backup task in background

* Launch missed auto global update task in background

* Launch missed auto webui update check task in background
2024-08-31 18:54:41 -04:00
schroda 5b08b81239 Initialize manga on add to library (#1016)
In case a manga gets added to the library which has not been initialized yet, it should be tried to initialize it.
Since it's not an error to have uninitialized manga in the library, this can be done in the background via the updater and the client receives the updated data via the update subscription.
2024-08-31 18:54:30 -04:00
schroda 7fac538ba3 Initialize uninitialized manga during global update (#1015)
They were only initialized in case the setting to refresh manga metadata during an update was enabled.
However, this should always be done for uninitialized manga, regardless of the setting.

06bfc33e72 prevents uninitialized manga from getting filtered out, however, it did not ensure to initialize the manga
2024-08-31 18:54:18 -04:00
schroda ef6be74ec2 Fix/chapter downloaded check (#1012)
* Properly check for first page in cbz files

The download check for cbz files only checked if the archive existed but didn't check for the first page

* Streamline getImageImpl of ChapterDownloadProviders

* Exclude comic info file from page list

In case the download folder did not contain any page files, only the comic info file existed, which caused the download check to incorrectly detect the first page

* Add logging to ChapterForDownload#asDownloadReady
2024-08-31 18:54:06 -04:00
schroda 9f49587245 [skip ci] Update readme (#1008)
* Move JUI and Sorayomi to inactive clients

* Update client descriptions

* Remove outdated mihon sync info

* Update feature list

* Move Docker installation info to top of list

* Move feature list higher in readme

* Rename feature list section to "Features"

* Separate inactive/abonded clients
2024-08-18 17:17:44 -04:00
schroda b7b733f351 [skip ci] Feature/update readme (#1005)
* Replace tachiyomi with mihon

* Update "syncing with mihon" readme section

* Update graphql section in CONTRIBUTING.md
2024-08-17 20:10:47 -04:00
schroda 06bfc33e72 Always update uninitialized manga in global update (#997)
Manga can be added to the library while they have not been initialized yet.
In this case, depending on the manga exclusion setting, they will never be updated automatically unless they get refreshed once manually.
2024-07-28 15:58:23 -04:00
schroda fbcd55d6c5 Add "hasDuplicatedChapters" field to gql MangaType (#995) 2024-07-28 15:58:12 -04:00
schroda 2484b5f14b Fix/downloaded chapters with lost page count (#994)
* Persist page count during chapter list update

In case a downloaded chapter gets deleted during a chapter list update, the download status was tried to be preserved.
However, in case the status could be preserved, the page count was lost and thus, the chapter now was marked as downloaded with a page count of -1.

* Mark downloaded chapters without page count as not downloaded
2024-07-28 15:58:01 -04:00
schroda 6982659658 Fix/inserting duplicated chapters into database (#991)
* Prevent adding duplicated chapters into the db

it's possible that the source returns a list containing chapters with the same url
once such duplicated chapters have been added, they aren't being removed anymore as long as there is
a chapter with the same url in the fetched chapter list, even if the duplicated chapter itself
does not exist anymore on the source

* Drop duplicated chapters from database table

* Add unique constraint to chapter table

This is to completely prevent duplicated chapters from being added to the database.
Since once a duplicated chapter has been added to the database, it does not get removed anymore as long as a chapter with the same url is included in the requested source chapter list
2024-07-28 15:57:50 -04:00
AeonLucid 9e006166a8 Add setting to use the flaresolverr response (#990) 2024-07-28 15:57:40 -04:00
AeonLucid 25a62e33a1 Fix PersistentCookieStore for domains with an underscore (#989)
* Fix PersistentCookieStore for domains with an underscore

* Missed one uri
2024-07-28 15:57:30 -04:00
AeonLucid d05ed0a56c Fix SOCKS5 authentication by setting a default Authenticator (#988)
* Fix SOCKS5 authentication by setting a default Authenticator

* Fix lint
2024-07-28 15:57:17 -04:00
schroda eaffb2755c Cleanup only created auto backup files (#984)
The automated backup cleanup just deleted every file (recursively in subfolders as well) in the set folder in case it was older than the set backup ttl.
This made it impossible to save the automated backups into a folder with different files.
2024-06-29 11:44:03 -04:00
schroda e0fcae2ae3 Handle deprecated gql sort again (#983) 2024-06-28 09:21:32 -04:00
schroda af9ad61174 Feature/gql simpilify filtering for multiple values (#960)
* Remove code duplication

* Remove unnecessary functions

* Simplify filtering for multiple values in queries

Makes it easier to filter for multiple values at ones without having to nest filters with multiple "and".

e.g.

```gql
query MyQuery {
 mangas(
  filter: {genre: {includesInsensitive: "action"}, and: {genre: {includesInsensitive: "adventure"}, and: { ... }}}
 ) {
  nodes {
   id
  }
 }
}
```

can be simplified to

```gql
query MyQuery {
 mangas(
  filter: {genre: {includesInsensitive: ["action", "adventure", ...]}}
 ) {
  nodes {
   id
  }
 }
}
```

* Add filter for matching "any" value in list

Makes it easier to filter for entries that match any value without having to nest filters with multiple "or".

e.g.

```gql
query MyQuery {
 mangas(
  filter: {genre: {includesInsensitiveAny: ["action", "adventure", ...]}}
 ) {
  nodes {
   id
  }
 }
}
```

instead of

```gql
query MyQuery {
 mangas(
  filter: {genre: {includesInsensitive: "action", or: {genre: includesInsensitive: "adventure", or: {...}}}}
 ) {
  nodes {
   id
  }
 }
}
```

* Add util function to apply "andWhere/All/Any"
2024-06-27 20:34:29 -04:00
schroda 7c54ad54fc Sort gql queries by multiple columns (#963) 2024-06-27 20:34:21 -04:00
schroda aee9f1032c Exit early in case of empty chapter id list (#980)
In case the list was empty, the batch update failed with a NoSuchElement exception
2024-06-27 20:34:12 -04:00
robo f7d0605e0a [skip ci] Remove docker commands (#972)
* [skip ci] Remove docker commands

* mention compose file
2024-06-16 15:44:01 -04:00
Syer10 f40dcafb43 Release v1.1.1
CI Publish / Validate Gradle Wrapper (push) Successful in 11s
CI Publish / Build Jar (push) Failing after 8s
CI Publish / Make debian-all release (push) Has been skipped
CI Publish / Make linux-assets release (push) Has been skipped
CI Publish / Make linux-x64 release (push) Has been skipped
CI Publish / Make macOS-arm64 release (push) Has been skipped
CI Publish / Make macOS-x64 release (push) Has been skipped
CI Publish / Make windows-x64 release (push) Has been skipped
CI Publish / Make windows-x86 release (push) Has been skipped
CI Publish / release (push) Has been skipped
2024-06-15 13:15:00 -04:00
schroda d9cb54b285 Compare webUI version with bundled webUI version (#969)
* Compare webUI version with bundled webUI version

The bundled webUI version was incorrectly compared with the minimum server version.
This worked until the latest release, because the bundled webUI version had a lower revision number than the server revision, however, with the latest release, it is now higher, resulting in no compatible webUI version to be found

* Consider bundled webUI version only for default flavor

* Add "isDefault" util function to WebUIFlavor
2024-06-15 13:05:39 -04:00
schroda f738a162d3 Support for "STABLEPREVIEW" webUI version (#970)
Makes it possible to release new stable webUI versions without having to update the mapping file.
2024-06-15 13:05:28 -04:00
Syer10 fda4cd6783 Release v1.1.0
CI Publish / Validate Gradle Wrapper (push) Successful in 13s
CI Publish / Build Jar (push) Failing after 7s
CI Publish / Make debian-all release (push) Has been skipped
CI Publish / Make linux-assets release (push) Has been skipped
CI Publish / Make linux-x64 release (push) Has been skipped
CI Publish / Make macOS-arm64 release (push) Has been skipped
CI Publish / Make macOS-x64 release (push) Has been skipped
CI Publish / Make windows-x64 release (push) Has been skipped
CI Publish / Make windows-x86 release (push) Has been skipped
CI Publish / release (push) Has been skipped
2024-06-14 21:41:17 -04:00
schroda ecd1604e25 Update metadata in source browse only if new data is not null (#962)
Browsing a source loads only a minimal representation of a manga which does not include some metadata.
This metadata is only loaded when the specific manga gets fetched.

Thus, when the extra metadata of a manga was already loaded, it got removed when browsing the source and a page response included this manga
2024-06-09 12:08:21 -04:00
Mitchell Syer 0f061900af Fix browse source (#961) 2024-06-09 11:23:54 -04:00
Rat Cornu c47f5ea85e [skip ci] doc: add NixOS installation (#959) 2024-06-08 10:48:43 -04:00
Mitchell Syer 306eb0e3c7 Update manga info when browsing if not in library (#958)
* Update manga info when browsing if not in library

* Cleanup
2024-06-06 21:17:17 -04:00
schroda e64025ded8 Correctly set name of logger (#956) 2024-06-02 20:33:32 -04:00
schroda c1fe2da636 Fix/failing thumbnail requests with http 410 (#955)
* Refresh thumbnail url on 410 error

* Refresh thumbnail url on 301 error
2024-06-02 20:33:25 -04:00
schroda ff23f58a4f Support partial mutation responses (#954)
In case e.g. a mutation was made which looked like this

myMutation {
  mutationA { ... }
  mutationB { ... }
  mutationC { ... }
}

and mutation A and B succeeded while mutation C failed, the response only included the error of C and the successful mutation data response of A and B was missing
2024-06-02 20:33:17 -04:00
schroda fc2f5ffdf9 Fix/failing track progress update for logged out trackers (#953)
* Refresh track record only when logged in

In case one tracker was logged out, the refresh failed with an unauthenticated error and caused the other trackers to not get updated

* Prevent chapter track update from failing due to failure of other tracker

* Change level of log to "info"
2024-06-01 12:22:25 -04:00
schroda 6dd9ed7fb0 Fix/prevent importing unsupported trackers from backup II (#945)
* Properly prevent importing unsupported trackers from backup

Missed the early return in case no tracker record exists in the database in 2f362abb91be875e943b1364eb86d70a4144dd6f...

* Remove incorrect non null assertion

Prevented unbinding track records of unsupported trackers
2024-05-07 09:30:45 -04:00
schroda 2f362abb91 Prevent importing unsupported tracker from backup (#944)
* Prevent importing unsupported tracker from backup

This will lead to graphql field validation errors (non null declared field is null) once the track records get used, since they will point to trackers that do not exist

* Delete track records of unsupporter trackers

* Always return all track records of manga

Was already partially changed in 7df5f1c4c4 but this occurrence was missed
2024-05-06 09:29:34 -04:00
FumoVite 96807a64cf [skip ci] Update README.md (#941)
fixed "inctive"
2024-05-05 13:24:31 -04:00
schroda 7df5f1c4c4 Feature/backup tracking (#940)
* Include tracking in validation of backup

* Always return track records

Not clear why an empty list should be returned in case no trackers are logged in

* Include tracking in backup creation

* Restore tracking from backup
2024-05-05 13:24:16 -04:00
schroda cf1ede9cf7 Update lastPageRead on chapter update (#939)
Broken with 729385588a3d8e06ec8be38865a12c47e88f6bcb...
2024-04-28 10:35:33 -04:00
schroda 729385588a Prevent greater last page read than page count (#938)
In case multiple chapters are getting updated, the last page read might be higher than the available pages of a chapter
2024-04-28 00:34:40 -04:00
schroda 668d5cf8f0 Prevent IndexOutOfBoundsException when removing duplicated chapters (#935)
In case the "new" chapters consisted only of re-uploads an out of bound exception was thrown
2024-04-27 20:33:30 -04:00
schroda 72b1b5b0f9 Exit track progress update early in case new chapter is same as current local (#937)
Prevents unnecessary requests
2024-04-27 20:33:19 -04:00
schroda fbf726c174 Use "AsyncExecutionStrategy" for mutations (#932)
Batching only works with "AsyncExecutionStrategy" and by default mutations use "SerialExecutionStrategy"
2024-04-15 17:49:33 -04:00
schroda c441eed847 Exclude duplicated chapters from auto download limit (#923)
In case the new chapters include duplicates from different scanlators, they would be included in the limit causing the auto download to potentially only download duplicated chapters while there might be more non duplicated chapters to download.

Instead, the limit should only consider unique chapters and then should include all duplicates of the chapters that should get downloaded
2024-04-06 23:07:55 -04:00
schroda e8e83ed49c Remove duplicated mangas from gql "mangas" query (#924) 2024-04-06 22:53:56 -04:00
schroda cdc21b067c Fix/recognition of already downloaded chapters (#922)
* Remove overrides of "ChapterFilesProvider::downloadImpl"

* Check final download folder for existing page on download

Downloads were changed to get downloaded to the system temp folder instead to directly into the final download folder.

This broke the check for existing pages, because now only the temp folder was checked instead of both the temp and the final download folder.

Regression introduced with 1c9a139006

* Properly check for already existing downloaded pages

The previous check was always false because the file ending of the page file is unknown and thus, missing from the created file path

* Cleanup cache download folder
2024-04-06 22:53:49 -04:00
schroda 48e19f7914 Feature/auto download of new chapters improve handling of unhandable reuploads (#921)
* Update test/server-reference file

* Properly handle re-uploaded chapters in auto download of new chapters

In case of unhandable re-uploaded chapters (different chapter numbers) they potentially would have prevented auto downloads due being considered as unread.

Additionally, they would not have been considered to get downloaded due to not having a higher chapter number than the previous latest existing chapter before the chapter list fetch.

* Add option to ignore re-uploads for auto downloads

* Extract check for manga category download inclusion

* Extract logic to get new chapter ids to download

* Simplify manga category download inclusion check

In case the DEFAULT category does not exist, someone messed with the database and it is basically corrupted
2024-04-06 22:53:36 -04:00
schroda 89dd570b30 Add mutation to fetch the latest track data from the tracker (#920)
* Add mutation to fetch the latest track data from the tracker

* Update Track.kt

---------

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2024-03-31 13:25:58 -04:00
schroda 16474d4328 Feature/tracking gql add option to delete remote binding on tracker (#919)
* Extract unbinding track into function

* Introduce new unbind mutation

* Add option to delete track binding on track service

---------

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2024-03-31 13:22:13 -04:00
schroda 9db612bf03 Move trigger for track progress update to client (#918)
Triggering the progress update on server side does not work because the client needs to get the mutation result, otherwise, the clients cache will get outdated
2024-03-31 13:20:37 -04:00
schroda 7d92dbc5c0 Fix/tracking progress update in case local chapter is smaller than remote (#917)
* Update lastReadChapter on bind in case it's greater than remote

* Update lastReadChapter on chapter read in case it's greater than remote

* [Logging] Improve logs
2024-03-31 13:20:27 -04:00
schroda a9efca8687 Add chapter bookmark count field to MangaType (#912) 2024-03-31 13:20:19 -04:00
schroda dbfea5d02b Update inLibraryAt timestamp when adding manga to library (#911) 2024-03-31 13:20:08 -04:00
schroda a6b05c4a27 Feature/refresh outdated thumbnail url on fetch failure (#910)
* Extract thumbnail url fresh into function

* Remove incorrect non-null assertion

According to the typing there is no guarantee that fetching a manga from the source provides a thumbnail url

* Refresh manga thumbnail url on 404 error

* Refresh manga thumbnail url on unreachable origin cloudflare errors
2024-03-31 13:19:58 -04:00
schroda 6d539d3404 Fix/update subscription clear data loader cache (#908)
* Set updater running flag to false only at the end of the update

For clearing the data loader cache properly, the update status subscription requires the update to be running.

For the last completed manga update the flag was immediately set to false which prevented the dataloader cache from getting cleared, returning outdated data for the last updated manga

* Correctly clear the "MangaForIdsDataLoader" cache

The cache keys for this dataloader are lists of manga ids.
Thus, it is not possible to clear only the cached data of the provided manga id and instead each cache entry that includes the manga id has to be cleared

* Ensure that manga dataloader caches gets cleared during global update

The "StateFlow" drops value updates in case the collector is too slow, which was the case for the "UpdateSubscription".

This caused the dataloader cache to not get properly cleared because the running state of the update was already set to false.
2024-03-31 13:19:49 -04:00
Mitchell Syer b2aff1efc9 Fix MAL after restarting the server (#903)
* Fix MAL after restarting the server

* Cleanup MAL interceptor

* Fix

* Cleanup Anilist interceptor

* Use IOException

* Make Anilist private

* Lint
2024-03-16 23:36:45 -04:00
schroda 8a20a1ef50 Add first unread chapter field to MangaType (#900) 2024-03-10 19:01:03 -04:00
schroda 33cbfa9751 Fix/electron launch error not logged (#895)
* Log "Browser::openInBrowser" errors

The error was never written to the log file.
It was only visible in the console

* Remove "printStackTrace" usage with logs
2024-03-10 19:00:54 -04:00
schroda b95a8d44d4 Always fetch thumbnail of manga from local source (#898)
The local manga thumbnail got "downloaded" to thumbnail download folder of in library manga.
Since the "thumbnail url" of a local source manga never changes, the "downloaded" manga thumbnail never got updated

Regression introduced with f2dd67d87f
2024-03-10 19:00:44 -04:00
Syer10 54df9d634a Fix release
CI Publish / Validate Gradle Wrapper (push) Successful in 17s
CI Publish / Build Jar (push) Failing after 16s
CI Publish / Make debian-all release (push) Has been skipped
CI Publish / Make linux-assets release (push) Has been skipped
CI Publish / Make linux-x64 release (push) Has been skipped
CI Publish / Make macOS-arm64 release (push) Has been skipped
CI Publish / Make macOS-x64 release (push) Has been skipped
CI Publish / Make windows-x64 release (push) Has been skipped
CI Publish / Make windows-x86 release (push) Has been skipped
CI Publish / release (push) Has been skipped
2024-02-23 13:35:08 -05:00
Syer10 4eb9a696ff Fix release 2024-02-23 13:29:57 -05:00
Syer10 7b4fb4682b Add ChangeLog 2024-02-23 12:56:46 -05:00
Syer10 da4275530d v1.0.0 2024-02-23 12:13:42 -05:00
Mitchell Syer 1c417e909a Support Comic Info creation on download (#887)
* Support Comic Info creation on download

* Update Json and add Protobuf
2024-02-22 14:29:30 -05:00
Aria Moradi fc53d69f82 Add auth and version support to socks proxy (#883)
* Add auth support to socsk proxy

* better logging

* fix lint issue

* implement fixes and version

* add to test reference too
2024-02-19 11:06:39 -05:00
Mitchell Syer dda86cdb93 Seperate out migrations to allow run-once migrations (#882)
* Seperate out migrations to allow run-once migrations

* Previous

* Move migrations to a new file
2024-02-19 11:06:31 -05:00
Mitchell Syer 525a974e3a Start Server after routes are defined (#881)
* Start Server after routes are defined

* Separate events
2024-02-19 11:06:20 -05:00
Mitchell Syer b18c155e22 Fix Downloader Memory Leak (#880) 2024-02-19 11:06:13 -05:00
Mitchell Syer 07e011092a Support Token Expiry properly (#878)
* Support token expiry properly

* Small fix

* Lint

* Use newer fixes for expiry

* Lint
2024-02-19 11:06:00 -05:00
Aria Moradi 6803ac0611 move qtui to inactive list as it hasen't had commits in 2 years 2024-02-19 15:21:51 +03:30
Mitchell Syer af0dde5ae8 Add Source Meta (#875) 2024-02-17 11:24:01 -05:00
Mitchell Syer ea6edaecc4 Fix local source being accidentally removed (#874) 2024-02-17 11:23:53 -05:00
schroda eb2054bd5e Add VUI as a webUI flavor (#873) 2024-02-17 11:23:45 -05:00
schroda b277b3e3af Add thumbnail fetch timestamp to the gql manga type (#872) 2024-02-17 11:23:35 -05:00
schroda 9dc3a4e6ee Use correct name for scores data loader (#870) 2024-02-17 11:23:25 -05:00
schroda 6fbd2f1079 Feature/remove download ahead logic (#867)
* Remove download ahead logic

Unnecessary on server side, should just be done by the client

* Rename "autoDownloadAheadLimit" to "autoDownloadNewChaptersLimit"

* Deprecate the old field

* Update Stable WebUI

* Update Stable WebUI

---------

Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2024-02-17 11:23:13 -05:00
schroda 9edbc7f1d7 Feature/support different webui flavors (#863)
* Run functions for specific webui flavor

* Set default flavor of WebUIFlavor enum

* Consider flavor of served webUI when checking for update

In case the flavor was changed and the served webui files are still for the previous flavor, the update check could incorrectly detect no update

* Skip validation during initial setup

In case initial setup is triggered because of an invalid local webUI, doing a validation again is unnecessary

* Handle changed flavor on startup
2024-02-17 11:23:01 -05:00
schroda 8aa75be0d3 Cleanup gql subscription session state correctly (#859)
In case a socket got disconnected, the session state of the subscriptions did not get correctly cleaned up.
The active operations did get closed but not removed and thus, when the client tried to reconnect, the server incorrectly detected an active subscription for an operation and immediately terminated the subscription.
2024-02-01 21:34:11 -05:00
Chance Zibolski dc124fb15c Make flaresolverr session options configurable (#854)
Signed-off-by: Chance Zibolski <chance.zibolski@gmail.com>
2024-01-24 21:11:53 -05:00
Chance Zibolski 9109d1ca3e Use a session with flaresolverr (#853)
Signed-off-by: Chance Zibolski <chance.zibolski@gmail.com>
2024-01-24 20:44:45 -05:00
schroda 02296f1d1c Change flaresolverr settings to be non optional (#852)
The settings are not optional in the ServerConfig, thus, they should also not be optional in the returned settings type
2024-01-24 20:02:36 -05:00
Mitchell Syer 63e1082b97 Minor fixes for FlareSolverr (#851)
* Minor fixes for FlareSolverr

* Weird crash but ok
2024-01-24 17:49:51 -05:00
schroda 285f228660 Gracefully shutdown server in case webUI can't be setup (#850) 2024-01-24 17:49:42 -05:00
schroda c18cf069b1 Prevent invalid webUI from stopping the server (#849)
In case there is no internet connection, it is not possible to verify the webUI files, leading to the server to fail from starting up.
Instead, the existing webUI should just be used
2024-01-24 17:49:28 -05:00
schroda fc64f47589 Fix/excessive logging (#848)
* Remove log of mangas to update

This logged the full manga data objects in the list with information that is not needed (e.g. description of a manga).
Once a manga gets updated via the updater, it gets logged, which should be enough

* Include manga id in updater log

* Use "toString" to log mangas

* Change "HttpLoggingInterceptor" level to "BASIC"

Was unintentionally merged with d658e07583
2024-01-24 17:49:16 -05:00
Mitchell Syer 562b940d91 Remove dot before cookie (#845) 2024-01-23 19:21:33 -05:00
Mitchell Syer d658e07583 Implement FlareSolverr (#844)
* Implement FlareSolverr

* Oops
2024-01-23 18:48:55 -05:00
Mitchell Syer 9121a6341c Fix Tracker Status and Scores (#843) 2024-01-23 18:48:47 -05:00
Mitchell Syer 4bec027f11 Change Track.bind to use trackerId + remoteId (#842) 2024-01-22 21:35:56 -05:00
Mitchell Syer b9053e3057 Fix graphql tracking (#840) 2024-01-21 20:04:24 -05:00
Mitchell Syer 0621138478 Improve Tracker Icons Implementation (#836)
* Improve tracker icons implementation

* Fix description
2024-01-21 14:37:51 -05:00
Mitchell Syer ce42e89e25 Add MangaUpdates (#834) 2024-01-21 11:45:34 -05:00
Mitchell Syer 46e1e4c043 Table for Track Searches (#833)
* Table for Track Searches

* Lint
2024-01-20 23:12:18 -05:00
Andrei Paunescu 621468a183 Apply natural sort to local manga pages in Directory format (#826) 2024-01-20 19:42:08 -05:00
schroda d8876cf96a Add mutex to "updateExtensionDatabase" (#829)
If called in quick succession it is possible that duplicated extensions get inserted to the database, because it has not yet been updated by the first call
2024-01-20 19:42:01 -05:00
Chance Zibolski 57d5bc6480 Add support for configuring which categories are downloaded automatically (#832)
* Rename IncludeInUpdate class to IncludeOrExclude

Signed-off-by: Chance Zibolski <chance.zibolski@gmail.com>

* Add support for configuring which categories are downloaded automatically

If a manga has no configured categories, behavior remains the same and
the automatic download functionality will download new chapters without
consulting the category includeInDownload configuration.

Signed-off-by: Chance Zibolski <chance.zibolski@gmail.com>

---------

Signed-off-by: Chance Zibolski <chance.zibolski@gmail.com>
2024-01-20 19:41:47 -05:00
Mitchell Syer f224918f33 Create bin folder (#822) 2024-01-13 11:48:15 -05:00
Mitchell Syer 7b290dc465 Update User Agent (#821) 2024-01-12 23:48:33 -05:00
Mitchell Syer b1412dda34 Update Java 8 (#820) 2024-01-12 23:46:57 -05:00
Mitchell Syer 28e4ac8dcb Remove Playwright (#643) 2024-01-12 23:46:47 -05:00
robo 79eeb6d703 [skip ci] add VUI to README.md (#819) 2024-01-12 23:46:37 -05:00
Mitchell Syer 0d0e735d0e Fix brotli (#818) 2024-01-11 23:09:31 -05:00
Mitchell Syer d994502d06 Update Electron (#817) 2024-01-11 23:09:22 -05:00
schroda dfbd7a65ae [skip ci] Correct wrong tracker oauth example url (#814) 2024-01-10 21:06:06 -05:00
schroda f99f94c8d7 Enable tracking (#813)
* Enable tracking

* Add documentation
2024-01-10 20:31:56 -05:00
schroda 41c643496a Add more chapter fields to MangaType (#812)
- last read
- latest read (latest fetched chapter that has been read)
- latest fetched
- latest uploaded
2024-01-10 20:31:47 -05:00
Mitchell Syer e5476f8a01 Extension repo fixes and improvements (#811) 2024-01-09 19:42:53 -05:00
Mitchell Syer 188fb188ce Set Mac Launcher Executable (#810) 2024-01-09 19:40:42 -05:00
schroda c852592b34 Prevent adding duplicated extensions to the db table (#808)
* Prevent adding duplicated extensions to the db table

There is a possibility that multiple extension repos have been added which contain the same extensions.
In case these extensions have not been added to the db table yet, they would all get added to the db, which would create duplicated extensions

* Use the extension with the latest version from all repos

In case multiple repos have the same extension, use the extension with the latest version
2024-01-09 19:40:31 -05:00
schroda 3a1e0c5a63 Remove extension obsolete flag when updating db after extension list fetch (#807)
In case an extension got marked as obsolete and was found again in a set repo, the obsolete flag has to be removed
2024-01-09 19:40:16 -05:00
schroda 6376972130 Remove caching of extensions for gql mutation (#806)
The client should use the extension query to get "cached" extensions and the mutation to update the extensions
2024-01-09 19:38:21 -05:00
Mitchell Syer c70c860a82 Create Client IDs (#804)
* Create Client IDs

* Cleanup imports
2024-01-07 15:36:23 -05:00
Tachimanga 5a178ada74 add trackers support (#720)
* add trackers support

* Cleanup Tracker Code

* Add GraphQL support for Tracking

* Fix lint and deprecation errors

* remove password from logs

* Fixes after merge

* Disable tracking for now

* More disabled

---------

Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2024-01-07 15:07:41 -05:00
Mitchell Syer 230427e758 Support Custom Repos (#803)
* Support custom repos

* Fix migration

* Make extension after update optional
2024-01-05 19:14:09 -05:00
Mitchell Syer abf1af41a3 Update bundled webui (#802) 2024-01-05 15:22:04 -05:00
Mitchell Syer 61e2548bb7 Deb fixes (#801) 2024-01-05 14:51:23 -05:00
Mitchell Syer f739c54292 Rename the project (#795) 2024-01-05 14:12:35 -05:00
brianmakesthings 3ed84de320 [skip ci] Add API info (#798) 2023-12-25 23:51:53 -05:00
schroda 621b4c0946 Correctly calculate the first chapter to download index (#796)
Subtracting 1 from the first chapter to download index caused an additional chapter to get downloaded (e.g. limit 4 would download 5 chapters)

Regression was introduced with 05bf4f5525
2023-12-23 16:23:06 -05:00
schroda 11be969101 Fix/download subscription returning outdated data for finished downloads (#794)
* Return latest data for finished downloads

In case a download has finished, the cache of the data loader has to be cleared to be able to get the latest data, otherwise, the returned chapter will still be marked as not downloaded

* Correctly clear manga data loader caches
2023-12-16 12:59:17 -05:00
schroda ea958cd8f7 Correctly emit the current status immediately (#792)
For finished downloads the immediate emission did not work because the emission was done async and by the time the state got updated with the new status, the finished download was already removed from the queue.
Thus, the new state was missing the finished download.
2023-12-16 11:26:48 -05:00
Mitchell Syer 56048dcdb0 Update Github Actions (#788)
* Update github actions

* Replace set-output
2023-12-08 19:50:12 -05:00
schroda fb545947ec Feature/gql improve webui update status (#783)
* Remove "updateAvailable" from webui update info

Doesn't add anything

* Extract status creation into function

* Optionally emit status immediately

Otherwise, some emissions can get lost due to the 1s sample period

* Rename "STOPPED" state to "IDLE"

* Reset webui update status

Currently, the update status never gets reset.
Thus, it will be "FINISHED" or "ERROR" until the next update gets triggered.
Due to this, the client won't know that the update result was already handled and will handle it again the next time it gets the update status.

To prevent this, the client has to be able to tell the server that it has handled the update result and that it can be resetted
2023-12-08 19:17:25 -05:00
schroda df57070b70 Make sure to always send finished chapter downloads with the download status (#782)
Due to not immediately sending the status, the finished chapters were already removed from the queue by the time the status was actually send to the client.
This caused the client to never receive a status with the chapters downloaded flag to be true, resulting in the client to not know that a chapter is downloaded
2023-12-08 19:16:52 -05:00
Mitchell Syer 9b27d7ee23 Improve Http Client Configuration (#786)
* Improve Http Client Configuration

* Lint
2023-12-08 19:16:38 -05:00
Mitchell Syer a2d3fa6e1d Use new Tachiyomi backup filename format (#787)
* Use new Tachiyomi backup filename format

* Lint

* Get Backup Filename in more places

* Delete BackupFull
2023-12-08 19:16:25 -05:00
schroda 94b670eb81 Fix/gql about webui query same response type as webui update info (#781)
* Use a new type for the webui about info query

Using the same type for this and the webui update queries/mutations caused apollo to save it as the same data in the cache, overwriting the "about info"

* Use a new type for the webui about check query

To prevent similar issues (cc3bf5f34a8afebadd306d037db1a10088ef9334) with the "update check" and the "update progress" payloads

* Throw update check error when calling it via the query

Otherwise, the error is never raised to the frontend

* Set "ERROR" state in case the update check failed on WebUI update trigger
2023-11-26 17:17:28 -05:00
Mitchell Syer d65ed6ced7 Fix Bundler Script (#780) 2023-11-25 21:42:25 -05:00
schroda db50eb7526 Disable download ahead limit by default (#778)
Currently, it causes the download ahead while reading logic in the WebUI to be enabled by default, which should be disabled by default
2023-11-25 11:53:37 -05:00
schroda d21b2018cb Add mutation to clear the cached images (#775) 2023-11-25 11:53:29 -05:00
schroda 9110c07ed9 Correctly select enum webui flavor via "ui name" (#772)
The selection always returned the default value fallback due to incorrectly using the enums value name instead of the "uiName" of the enum value
2023-11-19 19:22:26 -05:00
schroda 2298e71279 Feature/gql about webui query (#773)
* Rename "about" query to "aboutServer"

* Add "about" query for webUI
2023-11-19 19:22:16 -05:00
schroda 909bd76e08 Cleanup parent folders when deleting downloaded chapters (#776)
Currently, the download folder never gets cleaned up which leads to having a lot of empty folders (sources, mangas folders)
2023-11-19 19:22:06 -05:00
Mitchell Syer 50cd0c4e10 Fix Queries Containing % (#766) 2023-11-07 18:11:38 -05:00
Mitchell Syer 460fc235e3 Add Cache-Control to Extension Icons (#765) 2023-11-07 18:11:30 -05:00
schroda c38a3d9eba Update served webUI after update (#764)
The served file gets cached and thus, it won't reflect the latest version of the file.
This was a problem after the webUI got updated, since now the served "index.html" was outdated and pointed to files that didn't exist anymore.
2023-11-07 18:11:21 -05:00
schroda b303291e94 Always get the latest commit count for jar name (#763)
When adding commits or switching between branches the "shadowJar" gradle task always used the same (outdated) commit count which created jars with confusing names
2023-11-05 21:16:55 -05:00
schroda 7993da038e Fix/initial auto backup never triggered in case server was not running (#762)
* Trigger initial auto backup in case server was not running

In case the server was not started (stopped, system shutdown - not in hibernation) during the scheduled auto backup time, the auto backup never got triggered.

* Update server util preferences
2023-11-05 21:16:48 -05:00
schroda 05bf4f5525 Fix/auto download new chapters initial fetch (#761)
* Fix automatic chapter download for initial chapter list fetch

The initial fetch wasn't correctly detected which caused chapters to get downloaded.
Using index based numbers also caused the first chapter to not get downloaded due to it being omitted in the "subList" call which excludes the "toIndex".

* [Logging] Update logs
2023-11-05 21:16:40 -05:00
Mitchell Syer db36896f92 Fix chapter duplicates if its a different url but same chapter list size (#759) 2023-11-05 10:52:10 -05:00
Mitchell Syer 16dbad8bdf Fix path to Preference file if it contains a invalid path character (#750)
* Fix path to shared preference files if it contains a invalid character

* Lint
2023-11-04 18:10:58 -04:00
schroda 8a4c717d24 Check for all downloaded pages during a chapter download (#752)
In case a chapter is marked as not downloaded, but the download folder exists already, the chapter did not get downloaded again.
 This could cause issues in case the previous download failed or has missing pages.
Instead of only checking if the folder exists, each page should be checked individually

This was previously done and was incorrectly changed with 1c9a139006.
2023-11-04 18:10:06 -04:00
Mitchell Syer 442a290966 Improve Extensions List (#753)
* Use new extension icon path

* Improve Extension list performance
2023-11-04 18:09:55 -04:00
Mitchell Syer 0785f4d0f5 Chapter Fetch Improvements (#754)
* Chapter fetch improvements

* Update previous date uploads

* Lint

* Fix backup inserts

* Remove extra maxSeenUploadDate

* Port downloaded over

* Make sure to set isDownloaded on all inserts
2023-11-04 18:09:40 -04:00
schroda 21e325af9c Correctly handle download of new chapters of not started entries (#755)
The function incorrectly exited early in case no latest read chapter was found.
This rendered disabling the setting "excludeEntryWithUnreadChapters" useless.
2023-11-04 18:09:32 -04:00
schroda 3e9d29ea7f Remove username and password from config log (#756) 2023-11-04 18:09:24 -04:00
schroda 4324373e61 Fix/chapter list fetch updating and inserting chapters into database (#749)
* Keep initial fetch date of existing chapters on a list fetch

The fetch at date should not get updated for existing chapters.
Updating this field makes it impossible to detect which chapters were actually newly fetched.
To get the last fetched timestamp of the chapters, the "chaptersLastFetchedAt" field of the manga should be used

got changed in 6d33d72663

* Get real chapter url safely

In case this causes an exception, it should not cause the whole list fetch to fail

was removed in 6d33d72663
2023-11-01 20:24:34 -04:00
schroda 673053d291 Migrate preferences only if necessary (#748)
Currently, the server tries to migrate the preference on every startup, even if the migration was already done.
This can lead to an unhandled exception, if the write permission to the system preference was revoked.
In case the migration has already happened, these permissions should not be required
2023-11-01 09:19:57 -04:00
schroda 5b3975f886 Only batch update in case list is not empty (#747)
Apparently "BatchUpdateStatement" can't handle an empty data set
2023-11-01 09:19:40 -04:00
schroda 7ed8f43859 Fix/backup import failure not resetting status (#746)
* Reset backup status to idle in case of an exception

* Rename "performRestore" function

* Set backup status to failure on exception

Makes it possible to detect if the restore failed or not after the first status was received

* Set backup status to success on completion

Since the status is not provided over a subscription, but over a query that should be pulled, it is not really easily detectable if a restore finished or not, since both states will be indicated by "idle"

* Correctly wait for first new status when triggering backup import

The status is only "Idle" in case no backup import has ever run.
Once the first backup process finished it is either "Failure" or "Success"

* Rename "ProtoBackupImport::restore" function

* Add id to restore process

Makes it possible to differentiate between backup restore processes.
2023-10-31 21:21:11 -04:00
schroda dcbb1c0dd1 Handle backups with categories having default category name (#745)
We do not allow any category to have the default categories name ("default" case-insensitive).
In case the backup includes a category with this name, it won't be matched to any category in our database and also won't insert a new category.
This will then lead to an exception, causing the backup to fail.
2023-10-30 19:47:33 -04:00
schroda 5d4d417f3e Extract downloaded webUI zip in temp folder for validation (#744)
Could cause issues due to not having permission to create the folder to extract the zip into
2023-10-30 19:47:23 -04:00
schroda 5943c6a2c6 Feature/improve browsing source performance (#743)
* Improve browse source database operations

* Reuse "insertOrGet" for "processEntries"
2023-10-30 19:47:13 -04:00
Alexandre Journet 6d33d72663 #733: Improve perfs on getChapterList with onlineFetch (Less databases calls) (#737)
* improve(#733): less databases calls on getChapterList with onlineFetch

* improve(#733): fixes (delete with ids), tried batch update but not successfull

* improve(#733): fixes (batch update)

* improve(#733): clean imports

* improve(#733): fixes SChapter to ChapterDataClass,

* improve(#733): re-added recognize chap number

---------

Co-authored-by: Alexandre JOURNET <alexandre.journet@axopen.com>
2023-10-30 19:47:03 -04:00
schroda 9d2b098837 Fix/updater update stuck in running status after failure (#731)
* Move running check to update function

* Move updating update status to process function

* Fail all source updates in case of update channel failure

In case the channel failed due to an exception, the update for the source failed completely.
This however was never handled and the pending updates for the source were never set to failed.
Due to this, the global updates running state was always true

* Remove completed update channel from available channels

* Always log specific update job failure
2023-10-30 19:46:54 -04:00
schroda 17bc2d2331 Fetch mangas during the update (#729)
* Optionally fetch mangas during the update

The update only fetched the chapter list of a manga but never the manga itself.
Thus, e.g. unless the manga got online fetched via the ui, it would never get recognized if it is completed or not.
This would e.g. prevent the update setting, to not update completed mangas, from working as intended

* Make settings required
2023-10-30 19:46:43 -04:00
Mitchell Syer 1c192b8db6 Fix Updater (#742) 2023-10-29 12:17:20 -04:00
schroda 76595233fc Prevent mangas from being added to the default category (#741)
Mangas are not supposed to be mapped to the default category in the database.
In case this happens, the category query won't be able to correctly select mangas in the default category
2023-10-29 11:02:31 -04:00
schroda 6531b80998 Delete outdated thumbnails when inserting mangas into database (#739)
In case the database got deleted without deleting cached/downloaded thumbnails, the next time a manga gets inserted, it's possible that a thumbnail was already downloaded for its id.
This then causes mangas to be displayed with incorrect thumbnails due to using the outdated cached/downloaded thumbnails
2023-10-29 11:02:23 -04:00
schroda 05707e29d7 Add missing settings to gql (#738)
* Add missing settings to gql

* Cleanup updating settings
2023-10-29 11:02:13 -04:00
schroda 616ed4637d Handle disabled download ahead limit for new chapters auto download (#734)
In case download ahead is disabled, all new chapters should get downloaded.
Due to incorrectly calculating the index of the first new chapter to download, no new chapter was downloaded at all
2023-10-29 11:02:02 -04:00
schroda 912c340a01 Fix update subscription returning stale data (#727)
In case a manga was already loaded via the data loader, the cached data will get used.
Due to this, the update status did not return the updated manga data, but instead, stale data
2023-10-29 11:01:55 -04:00
Mitchell Syer 583a2f0fad Migrate to XML Settings from Preferences (#722)
* Migrate to XML Settings from Preferences

* Lint
2023-10-29 11:01:46 -04:00
schroda 60015bc041 Return source for preference mutation (#728) 2023-10-26 18:15:29 -04:00
Mitchell Syer 029f445d0a Revert Dex2Jar (#721)
* Revert attempts to fix Dex2Jar

* Revert to v64 of Dex2Jar
2023-10-16 20:13:06 -04:00
schroda 150416b578 Fix/default log level (#719)
* Set default log level to INFO

Default log level was accidentally changed to ERROR for the base logger in 56deea9fb3

* Reduce graphql log level to WARN

Otherwise, thrown exceptions are swallowed by graphql and never logged besides a very brief error in the graphql response
2023-10-16 09:02:56 -04:00
schroda 6684576de1 Fix more missing functions (#718)
2cf9a407e8
2023-10-16 09:02:01 -04:00
Mitchell Syer 289acc9296 Fix Kavita (#716) 2023-10-15 21:58:38 -04:00
Mitchell Syer 2cf9a407e8 Fix MangaDex and Other Sources (#715) 2023-10-15 21:50:30 -04:00
Mitchell Syer 682c364647 Address Build Warnings and Cleanup (#707)
* Address build warnings and cleanup

* Actual name of who defined the protocol

* Remove uneeded detekt supression

* GraphQL Before-After cleanup

* Lint

* Cleanup unused functions

* Fix some discrepancies with the 1.5 source api and fix lang exception
2023-10-15 20:16:30 -04:00
schroda e70730e9a8 Query for mangas in specific categories (#712) 2023-10-15 20:14:59 -04:00
schroda 0ba6c88d69 Fix/graphql mangas query genre based filtering (#713)
* Filter mangas based on each genre of the genre condition

Genres are stored as a comma separated string in the database.
Thus, unless the correct string was passed in the condition, no manga would match the condition.

* Query mangas based on genre filter
2023-10-15 20:14:20 -04:00
schroda c56bdea1e2 Do not log ping messages (#709) 2023-10-15 20:14:10 -04:00
schroda a449a01a24 Fix/web interface manager get latest compatible version (#706)
* Correctly check for none PREVIEW channel latest compatible version

The only working channel was the PREVIEW channel, since any other channel would have fetched the actual version of the preview and used this as the potential latest compatible version.
This was caused due to incorrectly checking if the preview version should be ignored.

* Remove PREVIEW version constant

* Consider versions of different channels

In case the current server version isn't compatible with the latest version of the selected webUI channel, versions of other channel should be considered depending on the selected channel.

E.g. PREVIEW is the latest available version and thus, any version of another channel is also compatible with the PREVIEW channel

* Restrict min compatible version to the bundled version

The oldest compatible version for a server is the bundled version, thus, any version that is older than the bundled one should not be considered compatible
2023-10-07 20:20:09 -04:00
Mitchell Syer 849acfca3d Switch to a new Ktlint Formatter (#705)
* Switch to new Ktlint plugin

* Add ktlintCheck to PR builds

* Run formatter

* Put ktlint version in libs toml

* Fix lint

* Use Zip4Java from libs.toml
2023-10-06 23:38:39 -04:00
schroda 3cd3cb0186 Fix/graphql subscriptions logging (#704)
* Only log operationMessage in case gql logging is enabled

* Always log message type and operation name
2023-10-04 22:02:46 -04:00
Mitchell Syer feead100f2 Update dependencies (#701) 2023-10-04 22:02:28 -04:00
Mitchell Syer a9987e6ab0 Support more image types (#700) 2023-10-04 22:02:20 -04:00
schroda ef0a6f54b8 Feature/auto download ahead (#681)
* Add "download ahead" mutation

Checks if the specified number of unread chapters, that should be downloaded, are available.
In case not enough chapters are downloaded, the number of missing unread chapters will get downloaded

* Optionally pass the latest read chapter id of a manga

In case a chapter will get marked as read, which also triggered the download ahead call, it's possible, that by the time the download ahead logic gets triggered, the chapter hasn't been marked as read yet.
This could then cause this chapter to be included in the chapters to get downloaded.
By providing the chapter id, this chapter will be used as the latest read chapter instead, and thus, not be included inn the chapters to download.
2023-10-04 22:02:10 -04:00
Mitchell Syer c8865ad185 Implement Non-Final 1.5 Extensions API (#699)
* Implement non-final 1.5 extensions API

* Bump lib version max

* Add visibility to preferences

* Add preference visibility
2023-10-04 22:01:45 -04:00
schroda 354968fba7 Update version "name" and "code" when installing external extension (#698)
In case a newer version of the extension is installed  and the extension gets manually downgraded, the version in db is still the one of the newer version.
This will prevent detection of available updates, since it won't get recognized, that an older version is currently installed.
2023-10-02 17:46:46 -04:00
schroda f985ed2131 Order chapters to download by manga and source order (#697)
Chapters were added to the queue by database index order.
In case a chapters of different mangas got added to the queue, downloads got mingled instead of being group inserted per manga.
Also sort manga chapters by source order, to make sure, that, in case chapters of a manga are, for some reason, not in the correct order in the database, they will still get downloaded in the order of the source.
2023-10-02 17:46:38 -04:00
schroda be2628875f Correctly select results using cursors while sorting (#696)
When using cursors for pagination while sorting, the sort order was inverted (desc -> asc, asc -> desc).
However, this was then not considered when selecting results based on the cursor.
For before/after results where always selected via greater/less.
Due to inverting the sort order, this also needs to be inverted depending on the sort order (desc or asc).
2023-10-02 17:46:26 -04:00
Alessandro Schwaiger 9430c8c580 [skip ci] Added new Tachidesk-VaadinUI Client (#695) 2023-10-01 17:16:35 -04:00
Mitchell Syer ea2cf5d4ff Fix File Upload (#694)
* Fix File Upload

* Use operations instead of operation

* Fix tests
2023-09-30 21:55:42 -04:00
He Zhu 3b36974d84 Fixed Bitmap missing method when using Baozi Manhua extensions. (#687) 2023-09-23 16:26:20 -04:00
MangaCrushTeam 41fea1d2a0 remove @Synchronized in CloudflareInterceptor.kt for performance (#688) 2023-09-23 16:26:07 -04:00
schroda d81fafc9f6 Correctly detect initial fetch of chapters (#689)
Since the number of chapters gets converted to be index based, 1 available chapter would result in 0.

Due to this, in case a manga had exactly one chapter before updating the chapters, it was incorrectly detected as the initial fetch and the new chapters did not get automatically downloaded.
2023-09-23 16:25:59 -04:00
schroda 0a73177996 Update graphqlkotlin to v6.5.6 (#685) 2023-09-16 13:07:50 -04:00
schroda c9423ef425 Send every download status change to the subscriber (#684)
Flow::stateIn has "Strong equality-based conflation" (see documentation).
Thus, it omits every value in case it's equal to the previous one.
Since the DownloadManger::getStatus function returns a status with a queue, that contains all current "DownloadChapters" by reference, the equality check was always true.
Thus, progress changes of downloads were never sent to subscribers.
Subscriber were only notified about finished downloads (size of queue changed) or downloader status changes
2023-09-16 13:07:43 -04:00
schroda 7086055ec3 Handle finished downloads that weren't removed from the queue (#683)
In case a download was finished, but the downloader got stopped before it was able to remove the finished download from the queue, the downloader got stuck in an endless loop of starting and pausing downloads.

This was caused by selecting the next chapter to download and then recognizing in "Downloader::step", that there is another chapter to download before the current one in the queue.
However, since this recognized chapter is already downloaded, the downloader selected the next queued chapter again.
It was then stuck in this loop until the finished chapter was manually removed from the queue.
2023-09-16 13:07:35 -04:00
schroda 553b35d218 Feature/improve automatic chapter downloads (#680)
* Rename "newChapters" to "updatedChapterList"

* Do not auto download new chapters of entries with unread chapters

Makes it possible to prevent unnecessary chapter downloads in case the entry hasn't yet been caught up

* Optionally limit auto new chapter downloads

* Prevent downloading new chapters for mangas not in the library
2023-09-16 13:07:24 -04:00
schroda c910026308 Do not reset already loaded config when updating config file (#679)
In case the user config file has to be updated, the file needs to get reset.
While doing the reset, the already loaded internal state of the config got also reset, but was never updated again.
Due to this, the internal state of the config was the default config reference until the next server startup

Regression introduced with a31446557d.
2023-09-05 20:57:59 -04:00
schroda 35be9f14e4 Return correct latest compatible webUI version (#677)
The function always returned the PREVIEW version as the latest compatible version.
This was caused by incorrectly selecting the version from the json object, which resulted in the version to be wrapped in '"'.
2023-09-03 18:11:06 -04:00
schroda abcbec9c2a Fix/downloader not creating folder or cbz file (#676)
* Create manga download dir in case it's missing for cbz downloads

The directory, in which the cbz file should have been saved in, was never created.

* Correctly copy chapter download to final download location

"renameTo" does not include the content of a directory.
Thus, it just created an empty chapter folder int the final download directory
2023-09-03 18:10:54 -04:00
schroda ff6f5d7e89 Add more fields to the manga graphql type (#675)
These are information that are necessary for nearly all manga requests.
They could be selected via the categories mutation, but this only works for a single manga.
It is not possible to select this information for lists of mangas without having to request all chapters for every manga in the list.
2023-09-03 18:10:49 -04:00
schroda 56deea9fb3 Feature/graphql logging (#674)
* Set graphql logs to error level

Set log level for loggers with names
 - ExecutionStrategy (spams logs with "... completing field ...")
 - notprivacysafe (logs every received request up to 4 times (received, parse, validate, execute))

* Extract logic to get logger for name into function

* Add function to set log level for a logger

* Add settings to enable graphql debug logging
2023-09-03 18:10:43 -04:00
schroda 1c9a139006 Always return "ArchiveProvider" in case "downloadAsCbz" is enabled (#671)
* Move chapter download logic to base class

* Do not reuse "FolderProvider" in "ArchiveProviders" download function

Due to reusing the "FolderProvider" to download a chapter as a cbz file, a normal chapter download folder was created.
In case the download was aborted before the cbz file got created and the folder deleted, the next time the chapter got downloaded, the wrong "FileProvider" was selected, causing the chapter not to get downloaded as a cbz file.
2023-08-28 19:25:50 -04:00
Mitchell Syer 4d89c324b9 Fix Oracle JRE Extension Install (#670)
* Minor cleanup

* Fix Oracle JRE Write issue
2023-08-27 22:39:05 -04:00
schroda a76ce03911 Throw error instead of returning null (#666)
In case e.g. no manga exists for the passed id, the query returned null.
This makes it harder to have a "streamlined" error handling in the client, since these types of queries need a special handling.
2023-08-27 22:38:52 -04:00
schroda 9ee3f46ff0 Feature/graphql chapter pages mutation handle downloaded chapters (#665)
* Update chapter page refresh logic with logic from "ChapterMutation"

* Rename function to "getChapterDownloadReadyByIndex"

* Update "ChapterForDownload" to work with only "chapterId" being passed

* Return database chapter page list in case chapter is downloaded

In case the chapter is downloaded, fetching the chapter pages info should not be needed.
It should also currently break reading downloaded chapters while being offline, since the page request will always fail, since there is no internet connection
2023-08-27 22:38:33 -04:00
schroda 3343007cf8 Add mutation to install external extension (#667) 2023-08-26 22:19:51 -04:00
schroda c42d314b76 Move source download dirs to new download subfolder (#660)
Should have been added with f2dd67d87f
2023-08-20 14:32:53 -04:00
Mitchell Syer 8db6c2153e Fix some settings not being applied properly (#661)
* Fix some settings not being applied properly

* Update ProtoBackupExport.kt

* Update Updater.kt

* Revert "Update ProtoBackupExport.kt"

This reverts commit 41deaee2449ff28bb4ba4eb90959b68d4e82764d.

* Revert "Update Updater.kt"

This reverts commit 2678792cf6a354de8de4a19b2b74fbad72c1379a.
2023-08-20 14:32:25 -04:00
schroda 5baf54335b Feature/updater provide more info about update (#657)
* Provide last global update timestamp

* Provide skipped mangas in update status

* Extract update status logic into function

* Rename update "statusMap" to "mangaStatusMap"

* Provide info about categories in update status
2023-08-15 17:51:21 -04:00
schroda d9019b8f46 Correctly emit changed values (#656)
"SharedFlow::emit" blocked the flow due not being called in a new coroutine.
2023-08-12 14:06:38 -04:00
schroda a31446557d Feature/graphql server settings (#629)
* Add "uiName" to WebUI enum

* Add "Custom" WebUI to enum

* Rename "WebUI" enum to "WebUIFlavor"

* Add "WebUIInterface" enum

* Add query for server settings

* Add mutation for server settings

* Add mutation to reset the server settings

* Only update the config in case the value changed

In case the value of the config is already the same as the new value of the state flow, it is not necessary to update the config file
2023-08-12 12:03:25 -04:00
schroda 321fbe22dd Feature/listen to server config value changes (#617)
* Make server config value changes subscribable

* Make server config value changes subscribable - Update usage

* Add util functions to listen to server config value changes

* Listen to server config value changes - Auto backups

* Listen to server config value changes - Auto global update

* Listen to server config value changes - WebUI auto updates

* Listen to server config value changes - Javalin update ip and port

* Listen to server config value changes - Update socks proxy

* Listen to server config value changes - Update debug log level

* Listen to server config value changes - Update system tray icon

* Update config values one at a time

In case settings are changed in quick succession it's possible that each setting update reverts the change of the previous changed setting because the internal config hasn't been updated yet.

E.g.
1. settingA changed
2. settingB changed
3. settingA updates config file
4. settingB updates config file (internal config hasn't been updated yet with change from settingA)
5. settingA updates internal config (settingA updated)
6. settingB updates internal config (settingB updated, settingA outdated)

now settingA is unchanged because settingB reverted its change while updating the config with its new value

* Always add log interceptor to OkHttpClient

In case debug logs are disabled then the KotlinLogging log level will be set to level > debug and thus, these logs won't get logged

* Rename "maxParallelUpdateRequests" to "maxSourcesInParallel"

* Use server setting "maxSourcesInParallel" for downloads

* Listen to server config value changes - downloads

* Always use latest server settings - Browser

* Always use latest server settings - folders

* [Test] Fix type error
2023-08-12 11:47:41 -04:00
schroda 01ab912bd9 Remove unnecessary "downloadNewChapters" call in "fetchChapters" mutation (#652)
Gets already called by "Chapter::fetchChapterList", thus, this is unnecessary.
Additionally, "chapters.toList()" and "chapters.map()" have to be called in a transaction block, which they are not, and thus, cause an unhandled exception, breaking the mutation
2023-08-12 11:15:06 -04:00
schroda 557bad60bc Prevent last page read to be greater than max page count (#655)
There were cases where the last page read was greater than the max page count of a chapter.
This is not possible and is just invalid data, that is saved in the database, possible leading to other errors down the line.

This could happen in case the chapter was loaded at some point with e.g. 18 pages and after some time got fetched again from the source, now with fewer pages than before e.g. 15.
If the chapters last page was already read by that time, the last read page would have been 18, while the chapter now has only 15 pages.
2023-08-12 11:14:58 -04:00
schroda f2dd67d87f Feature/decouple thumbnail downloads and cache (#581)
* Rename "DownloadedFilesProvider" to "ChaptersFilesProvider"

* Move files into sub packages

* Further abstract "DownloadedFilesProvider"

* Rename "getCachedImageResponse" to "getImageResponse"

* Extract getting cached image response into new function

* Decouple thumbnail cache and download

* Download and delete permanent thumbnails

When adding/removing manga from/to the library make sure the permanent thumbnail files will get handled properly

* Move thumbnail cache to actual temp folder

* Rename "mangaDownloadsRoot" to "downloadRoot"

* Move manga downloads into "mangas" subfolder

* Clear downloaded thumbnail
2023-08-12 11:14:43 -04:00
Mitchell Syer b8b92c8d69 Suspend setupBundledWebUI() (#650) 2023-08-09 20:52:53 -04:00
schroda 74ff112e7a Feature/graphql web UI (#649)
* Add "server" to "checkForUpdate" logic names

* Use "webUIRoot" as default path for "getLocalVersion"

* Use local version as default version for "isUpdateAvailable"

* Return the version with the webUI update check

* Update WebinterfaceManager to be async

* Add query, mutation and subscription for webUI update

* Catch error and return default error value for missing local WebUI version
2023-08-09 20:46:48 -04:00
schroda 684bb1875c Fix/webinterfacemanager update to bundled webui (#648)
* Catch error when updating to bundled webUI

In case the bundled webUI is missing, the webUI setup threw an error and made the server startup fail.
Since a local webUI exists the error should be ignored, since it's only a try to update to a newer webUI version.

* Extract logic to setup bundled webUI version

* Update to bundled webUI try to download missing bundled webUI
2023-08-09 20:46:33 -04:00
schroda f6fec2424c Fix/extracting assets from apks (#644)
* Get rid of multiple static "assets/" usage

* Correctly add new zip entry

The name of the entry has to be a "/" separated path, otherwise, the files can't be found.
2023-08-06 23:21:31 -04:00
schroda 2889029b70 Fix/downloader manager persisting queue (#639)
* Extract reorder logic into function

* Save download queue everytime a download was finished

The download queue was never saved after a download was finished.
This caused finished download to be restored on a server start, which caused unnecessary "downloads" which most of the time would just finish immediately since the pages were still in the cache

* Wait for download queue save process to be finished

Since multiple downloaders could be finished at the same time, the download queue should be saved synchronously

* Remove unnecessary download queue save trigger

This gets called everytime a downloader finished downloading all chapters of its source.
Since the queue is now saved everytime a download is finished, this is trigger is not needed anymore
2023-08-06 23:21:21 -04:00
Mitchell Syer b56b4fa813 Update Local Source to latest Tachiyomi (#637)
* Update Local Source to latest Tachiyomi

* More formatting

* Enable zip64
2023-08-06 23:20:55 -04:00
schroda 00bc055d69 Fix/load extension log load failure (#641)
* Log extension load failure

In case the extension couldn't be loaded the error was never logged, making it impossible to analyse what was going on

* Log exception in "GetCatalogueSource:: getCatalogueSourceOrNull"

In case "GetCatalogueSource::getCatalogueSource" threw an error, this was never logged here
2023-08-05 20:10:12 -04:00
schroda 6fd291c7e3 Fetch downloaded chapters page again in case the stored file can't be retrieved (#640)
In case the file could not be retrieved, the page retrieve just failed and wasn't triggered again.
In case of the downloader, the chapter download just kept failing 3 times and was aborted
2023-08-05 20:10:02 -04:00
schroda dbdb787076 Restore download queue async (#638)
The download queue was blocking the main thread, thus, slowing down the startup.
In case the stored queue was huge, this could take multiple seconds
2023-08-05 20:09:38 -04:00
Mitchell Syer fc788a718d Add 128 px icon (#636) 2023-08-05 20:09:27 -04:00
Mitchell Syer e093fe6a06 Add CookieManager implementation (#635)
* Add CookieManager implementation

* Remove Syncronized

* Rename CookieStore
2023-08-05 20:09:12 -04:00
Mitchell Syer cdce368042 Fix Graphql-WS errors and Improve Downloader Subscription (#634)
* Fix errors in graphql-ws

* Send download messages more often
2023-08-04 22:48:41 -04:00
Mitchell Syer 689847d864 Update dependencies (#611) 2023-08-04 22:48:24 -04:00
Mitchell Syer 3675580d87 Add Subscriptions to GraphiQL and Update (#631)
* Add subscription url

* Update Graphiql and dependencies
2023-08-03 22:28:37 -04:00
Mitchell Syer 92f494d0fe Implement Graphql-WS Subscriptions (#630)
* Implement graphql-ws subscriptions

* Fix subscription payload issue

* Close session directly

* Improve id handling
2023-08-03 22:28:28 -04:00
Mitchell Syer 06d7a6d892 Info Queries (#627) 2023-08-03 18:09:11 -04:00
Mitchell Syer e2754200af Use Tachidesk-Launcher (#618)
* Use the Launcher

* Test launcher

* a

* Revert "a"

This reverts commit eb8667e4397c20dae7a7dfdf26058f5aff76fff8.

* Move launcher

* Test launcher 2

* Update dex2jar

* Fixes

* Use regular java with deb install

* Improve linux installs

* Revert "Test launcher 2"

This reverts commit 265825808fd82616223e4a919718ea87a7eeff43.

* Revert "Test launcher"

This reverts commit 7ff83c7ab954bcab6c0b039701041b639a489382.
2023-08-03 18:09:04 -04:00
Mitchell Syer cdb083ff48 Downloader Queries and Mutations (#610)
* Add downloader GraphQL endpoints

* Fix names

* DeleteDownloadedChapter(s)

* DequeueChapterDownload(s)
2023-08-03 18:08:47 -04:00
Mitchell Syer c3fb08d634 Library Update Queries and Mutations (#609)
* Add library update GraphQL endpoints

* No need for data classes

* UpdateLibraryManga
2023-08-03 18:08:35 -04:00
schroda 78a167aacf Fix/webui setup failure in case bundled webui is missing (#625)
* Rename functions

* Require version to be passed to "downloadVersion"

Makes it possible to download different versions than the latest compatible one with retry functionality

* Fallback to downloading bundled webUI in case it's missing

In case no download was possible and the fallback to the bundled version also failed due to it not existing, try to download the version of the bundled version as a last resort.

* Handle exception of "getLatestCompatibleVersion"

* Move validation of download to actual download function

* Extract retry logic into function

* Retry every fetch up to 3 times

* Log full exception and change log level
2023-07-30 10:29:40 -04:00
schroda 5a913fdfbb Make path to local source changeable (#626) 2023-07-30 10:29:09 -04:00
schroda f0a190e8d2 Update to bundled webUI version if necessary (#619)
In case on the startup no webUI update was available but the bundled version of the server is newer than the current used version, then the bundled version should be used.

This could be the case in case a new server version was installed and no compatible webUI version is available
2023-07-29 20:26:04 -04:00
schroda a2715fb851 Feature/webui update download failure do not immediately fallback to bundled version (#620)
* Return actual version for "PREVIEW" in "getLatestCompatibleVersion"

In case "PREVIEW" is the latest available version, the function should immediately fetch the actual webUI version that is currently the latest released version.

Thus, the function always returns a valid version and the preview version has not to be considered anymore at other places in the code

* Ignore download failure in case local webUI version is valid

In case the download failed e.g. due to internet connection issues, the server should only fall back to another version in case the local version is invalid or missing
2023-07-29 20:25:47 -04:00
Mitchell Syer 47e5b03f45 Fix some manga filters (#624) 2023-07-29 20:25:27 -04:00
schroda 251141a5c3 Fix/downloader (#622)
* Change log level of download error

* Change type of sourceId in Downloader

Unclear why it was converted to Long since it just got converted back to String anyway when it was used in the Downloader

* Only stop downloads from source of the Downloader

The downloader just changed the state of all downloads, ignoring if they are from the source the Downloader is for or not

* Remove unnecessary DownloadManager::start calls

In case chapters were added to the queue the DownloadManager will start itself

* Extract download filtering into property

* Improve Downloader logging

* Notify clients only in case Downloader was started

In case nothing was done there is nothing to notify about

* Do not start Downloaders for failed downloads

In case there were failed chapter downloads in the queue the DownloadManager still created a Downloader and started it.
This Downloader would than immediately call "onComplete", since there is no available download, which then would refresh the Downloaders again which created an infinite loop until the failed download got removed from the queue

* Retry download in case it failed it gets re-added to the queue

In case a failed downloaded that was still in the queue was tried to get added to the queue again, nothing happened.
Instead of doing nothing, the download should get retried.
Thus, it also provides the logic to easily retry a failed download by just "adding" the chapter to the queue again.
Currently, to retry a failed download, the download has to be removed from the queue and then get re-added.

* Rename function "unqueue" to "dequeue"

* Move "dequeue" function

* Extract dequeue logic into function

* Improve DownloadManager logging

* Override "toString" of DownloadChapter
2023-07-29 20:25:12 -04:00
schroda 6ac8f4c45d Use mathematical modulo implementation for calculations (#616)
See documentation (%/rem, mod) for differences.

Example for "issue" that occurred:
mathematical: -4 % 6 = 2 (expected)
kotlin: -4 % 6 = -4 (unexpected)
2023-07-26 19:28:13 -04:00
schroda 7ebefa7c42 Fix/updater scheduling auto updates (#615)
* Trigger missed auto global update immediately on server start

In case the last execution was missed, it was never immediately scheduled.
Thus, it had to be waited for the next scheduled execution to be executed.

* Schedule auto global updates at a later point during the startup

In case a global update was triggered immediately, the server setup wasn't far enough causing an error due to trying to use things (e.g. database) that weren't initialized yet
2023-07-25 20:33:57 -04:00
schroda 9e4c90f220 Always update the last webUI update check timestamp (#614) 2023-07-25 20:33:46 -04:00
schroda 50f988641b Fix/ha scheduler rescheduling ha tasks (#613)
* Correctly set the "firstExecutionTime" of a "HATask"

In case an initial delay is used for "Timer::scheduleAtFixedRate" (e.g. when rescheduling) then the "firstExecutionTime" of the "HATask" was incorrect, since it considered the first execution to be based on the actual interval.
This caused
 - calculations for execution times (e.g. "timeToNextExecution", "nextExecutionTime") to be incorrect
 - the ordering of the "scheduledTasks" queue to be incorrect

* Add logging

* Do not modify queue during forEach loop

Caused a "ConcurrentModificationException" and broke the system suspension detection due to the unhandled exception canceling the task

* Log all uncaught exceptions

In case an exception is uncaught/unhandled, it only gets logged in the console, but is not included in the log file.

E.g. the "HAScheduler::scheduleHibernateCheckerTask" task caused an unhandled "ConcurrentModificationException" which caused the task to get dropped.
In the log files this error could not be seen and thus, analysing the issue of the suspension detection to stop working was not possible via the logs

* Schedule "HATask" immediately when its last execution was missed

The missed execution was never triggered

* Calculate the "HATask" "last execution time" correctly

When scheduling a task for the first time, the "first execution time" is in the future.
This time is used for by all functions calculating times for this task (e.g. next/last execution time).

In case the first execution didn't happen yet and the current time, would have been an "execution time" based on the interval, the "hibernation detection" would trigger for this task, since it would think that the last execution was missed, due to the "last execution" being in the future.
To prevent this, it has to be made sure, that the "last execution time" is in the past.
2023-07-25 20:33:36 -04:00
schroda e53b9d4790 Fix/ha scheduler not triggering missed executions due to not meeting the threshold (#612)
* Check correctly if task threshold was met

It was incorrectly considered to be met in case the remaining time till the next execution was less than the threshold.
Instead, it has to be greater, since that would mean, that the next execution is taking long enough to not be triggering a double execution

Thus, the current logic is not, as intended, preventing possible double executions and instead is making sure to only execute missed tasks in case it will lead to double executions...

* Always trigger missed executions

The idea to have a threshold to prevent double executions in case the next scheduled execution isn't too far in the future doesn't really work with big intervals (e.g. in the days range).
For such cases, multiple days left for the next executions could be considered to cause double executions.

Decreasing the threshold doesn't really work since then it wouldn't really work for low intervals.
Instead, it makes more sense to just allow possible double executions and to just live with it.
In case it would be a problem for a specific task, the task should handle this issue itself.
2023-07-23 12:40:22 -04:00
schroda 027805c4d5 Preserve download queue through server restarts (#599) 2023-07-22 11:42:48 -04:00
schroda c02496c4f0 Fix/updater automated update max interval of 23 hours (#606)
* Rename schedule functions

* Introduce Base task for "HATask"

* Support kotlin Timer repeated interval in HAScheduler

It's not possible to schedule a task via cron expression to run every x hours in case the set hours are greater than 23.
To be able to do this and still keep the functionality provided by the "HAScheduler" it has to also support repeated tasks scheduled via the default Timer

* Support global update interval greater 23 hours

* Use "globalUpdateInterval" to disable auto updates

Gets rid of an unnecessary setting
2023-07-22 11:41:52 -04:00
schroda 2a83f290a5 Use "backupInterval" to disable auto backups (#608)
Gets rid of unnecessary setting
2023-07-22 11:41:21 -04:00
schroda d4f9b0b1bc Feature/log to file (#607)
* Setup "logback" to write to file

To be able to dynamically set the log file save location, logback has to be setup via code instead of a config file

* Log OkHttp via logback

Otherwise, the logs would only get written to the console and thus, not be included in the log file

* Init logback

Has to be done after the config was loaded, otherwise, the root directory would be unknown.
Moved the log of the loaded config to the "applicationSetup" since otherwise, the log would not be included in the log file
2023-07-21 19:53:41 -04:00
schroda 2452b03a49 Schedule automated update only once per hour (#605)
The update was scheduled to run every minute of the set hour.
But it should only run once in the set hour.
2023-07-21 19:52:12 -04:00
schroda 2ce423b6cb Correctly check if a new version is available for the preview channel (#604)
The actual version of the preview was never loaded and compared to the local version.
Instead, for the preview channel it was incorrectly decided that a new version is available on every update check
2023-07-21 19:51:51 -04:00
schroda e9206158b8 Feature/move server frontend mapping to the frontend (#591)
* Convert "WebInterfaceManager" to singleton

* Move server webUI mapping to the webUI

* Extract logic into functions

* Retry failed download

* Validate downloaded webUI

* Automatically check for webUI updates

* Add logic to support different webUIs

* Update logs

* Close ZipFile after extracting it
2023-07-20 20:48:27 -04:00
schroda 8690e918dd Feature/automatically download new chapters (#596)
* Automatically download new chapters

* Log queued downloads

* Add function to get number of manga chapters
2023-07-20 17:47:46 -04:00
schroda c1d702a51c Feature/improve automated backup (#597)
* Add option to disable cleanup of backups

* Ensure the minimum TTL of backups to 1 day

* Schedule the automated backup on a specific time of the day

* Introduce scheduler that takes system hibernation time into account

In case the system was hibernating/suspended scheduled task that should have been executed during that time would not get triggered and thus, miss an execution.

To prevent this, this new scheduler periodically checks if the system was suspended and in case it was, triggers any task that missed its last execution

* Use new scheduler
2023-07-20 17:47:30 -04:00
schroda 0338ac3810 Extract assets from apk file (#602)
Some extension require some assets to work properly.
Currently, the extracted jar file does not contain these assets, thus, these extensions wouldn't work
2023-07-20 17:47:08 -04:00
schroda 526fef85e4 Feature/global update trigger automatically (#593)
* Move "addCategoriesToUpdateQueue" to "Updater"

* Automatically trigger the global update
2023-07-10 13:14:14 +03:30
schroda 49f2d8588a Feature/automated backups (#595)
* Automatically create backups

* Cleanup automated backups

* Extract backup filename creation into function
2023-07-10 13:13:53 +03:30
schroda 9a80992aec Correctly read resource in build jar and dev mode (#594)
The server reference config file was only able to be read while in dev mode.
Using the build jar, the content of the file was empty, since in the build jar resources aren't actual files anymore, instead they are streams.
This caused the user config content to be replaced with an empty string.
2023-07-04 04:07:49 +03:30
Mitchell Syer 32d0890dba Proxy thumbnail urls (#589) 2023-07-02 19:18:44 +03:30
schroda b4d37f9ba2 Make sure "UserConfig" is up-to-date (#590)
Currently, the "UserConfig" was created in case it was missing.
But in case settings changed (added/removed), an already existing "UserConfig" never reflected these changes and thus, was out of date
2023-07-02 19:18:08 +03:30
Mitchell Syer 5372ef8f0c Manga for Source data loader (#588) 2023-07-02 19:16:04 +03:30
Mitchell Syer a11b654c3d Backup creation and restore gql endpoints (#587) 2023-07-02 19:15:44 +03:30
schroda 1a9a0b3394 Exclude "default" category from reordering (#586)
* Exclude "default" category from reordering

Due to the "default" category having been added to the database, the index based approach to reorder the categories didn't work anymore.
In case one tried to move a category to or from pos 1, the default category was selected due to being at index 0

* Normalize categories after reordering

Makes sure that the ordering is correct.
E.g. "default" category should always be at position 0
2023-07-01 21:23:10 +03:30
schroda 890920a57b Freeze graphql playground scripts to working versions (#585)
The latest versions might include breaking changes
2023-07-01 21:17:41 +03:30
Mitchell Syer 7fe7de5fdf Fix fetchSourceManga filtering 2023-07-01 13:28:15 -04:00
Mitchell Syer b9b115d0ea Rewrite filter and preference mutations (#577) 2023-06-24 19:58:11 +03:30
schroda 08af195f11 Fix graphql/plugin-explorer urls (#584) 2023-06-24 19:56:29 +03:30
schroda 71cde729fc Delete tmp files on request failure (#582)
There is a possibility that a partially downloaded file remains in case of an error.
In that case, the next time the image gets requested the existing file would be handled as a successfully cached image.
2023-06-21 17:32:58 +03:30
schroda 077f0a03f6 Update "dex2jar" to v61 (#583) 2023-06-21 17:21:16 +03:30
Mitchell Syer 812eb8001b Add fetch chapter pages (#576) 2023-06-10 21:42:42 +03:30
schroda b59af683ac Do not count mangas as part of categories that aren't in the library (#574)
Otherwise, the returned "size" of a property doesn't match the actual manga list, since that list only includes mangas from the library.
2023-06-09 18:27:16 +03:30
schroda 561d680e78 Exclude mangas with specific state from global update (#537) 2023-06-09 16:03:10 +03:30
Mitchell Syer 7c3eff2ba7 Complete source mutations (#567) 2023-06-05 16:49:03 +03:30
Mitchell Syer 300c0a8f35 Category Mutations (#566)
* Complete Category mutations

* Remove TODO
2023-06-05 16:48:57 +03:30
schroda 51bfdc0947 Feature/make config settings changeable during runtime (#545)
* Add logic to update config during runtime

* Update ConfigModule to always use the latest config

* Make ServerConfig settings re-assignable
2023-06-05 16:48:18 +03:30
Aria Moradi a64566c0f3 fill in the cover according to spec (#571) 2023-06-05 16:18:03 +03:30
Aria Moradi dbb9a80ea6 use commons-compress everywhere (#570) 2023-06-05 16:16:27 +03:30
Aria Moradi e930c54246 improve zip parsing (#569) 2023-06-05 14:31:14 +03:30
Mitchell Syer dfff047cbf Fix cascade migration (#565) 2023-05-29 17:29:54 -04:00
Mitchell Syer 44fb2b02bc Fix global meta delete (#564) 2023-05-29 23:41:39 +03:30
Mitchell Syer 6a7efafd9f Improve database column references and default category handling (#563)
* Improve default category handling and add cascade to references where possible

* Minor fix for default category

* Make the default category always first in the normalization
2023-05-28 04:11:27 +03:30
Mitchell Syer 241abc3956 Add items that are related to the deleted meta (#562) 2023-05-27 21:39:47 +03:30
Mitchell Syer 1e82c879bf Add default category to the database (#561)
* Add default category to the database

* Fix getCategorySize
2023-05-27 03:20:21 +03:30
Mitchell Syer a81d01d2e3 Don't use data fetchers in mutations (#559) 2023-05-27 03:09:31 +03:30
Mitchell Syer 2230796504 Extension mutations (#560) 2023-05-27 03:09:17 +03:30
Mitchell Syer 458ca7c7cf Fix update chapters (#557) 2023-05-26 13:45:25 +03:30
Mitchell Syer 3f91663ecf Rewrite meta and add meta mutations (#556) 2023-05-26 13:45:16 +03:30
Mitchell Syer 04a671382a Improve GQL Playground (#558) 2023-05-26 13:44:18 +03:30
Mitchell Syer 945ec818e5 Remove category filter (#551) 2023-05-24 14:01:21 +03:30
Mitchell Syer ff7ac8a785 Fetch Manga and Chapters in GQL (#555) 2023-05-24 14:01:07 +03:30
Mitchell Syer 603105e2ea Fix StringFilter (#554) 2023-05-24 14:00:54 +03:30
Mitchell Syer 5475567b48 Cleanup download type (#553) 2023-05-24 14:00:29 +03:30
Mitchell Syer 2aec0adb08 Category mangas (#552) 2023-05-24 14:00:06 +03:30
Mitchell Syer 54fc3761bf Put graphql under api (#549) 2023-05-17 03:58:00 +03:30
Mitchell Syer 99e1912bfe Fix manga/source and manga/chapters for graphql (#548) 2023-05-17 03:57:20 +03:30
Aria Moradi ecc1cabafd Merge pull request #547 from Suwayomi/graphql
add graphql
2023-05-13 23:00:13 +03:30
Aria Moradi 1a5b847b23 Update README.md 2023-05-01 19:16:32 +03:30
Aria Moradi d3409e7133 Update README.md 2023-05-01 19:09:59 +03:30
Aria Moradi 4e553e3eb3 better description about the Tachiyomi extension 2023-05-01 18:44:28 +03:30
Syer10 4577bbc572 More mutations 2023-04-28 21:56:25 -04:00
Syer10 da8ca23496 Start working on mutations 2023-04-28 21:29:06 -04:00
Syer10 988853be63 Seems like this should return null if it errors 2023-04-28 21:28:18 -04:00
schroda cde5dc5bfa Update "dex2jar" to v60 (#538) 2023-04-10 11:44:22 +03:30
Syer10 b617250eff Delete updates query since the chapters query can now mimic it 2023-04-08 22:55:56 -04:00
Syer10 313da99536 Add in library filter for chapters 2023-04-08 22:54:56 -04:00
Syer10 442e245216 Update TODO 2023-04-08 22:37:51 -04:00
Syer10 050ab17019 Complete SourceQuery 2023-04-08 21:05:05 -04:00
Syer10 c80f488a13 Complete ChapterQuery 2023-04-08 20:40:18 -04:00
Syer10 cf73804c71 Complete ExtensionQuery 2023-04-08 20:39:38 -04:00
Syer10 a90e5d13ea Complete MetaQuery 2023-04-08 15:47:10 -04:00
Syer10 891fb0b479 Simplify keyset pagination 2023-04-08 15:27:18 -04:00
Syer10 58a623d44d Fix keyset pagination for non-unique order by modes 2023-04-08 13:37:09 -04:00
Syer10 0e84b8a154 Lint 2023-04-08 13:36:28 -04:00
Syer10 a4dfcf80e4 Implement manga status filter 2023-04-07 21:30:20 -04:00
Syer10 d8567eadb2 Simplify queries 2023-04-07 21:10:38 -04:00
Syer10 0b88207ad5 Fix empty results errors 2023-04-07 00:02:00 -04:00
Syer10 671466a737 Complete CategoryQuery 2023-04-06 21:53:30 -04:00
Syer10 84881a0d52 Complete MangaQuery 2023-04-04 21:02:29 -04:00
Syer10 a589049cc7 Move things around and introduce Cursor type 2023-04-04 21:02:07 -04:00
Syer10 17877e0f17 Fix case insensitive 2023-04-03 22:12:38 -04:00
Syer10 1ed9bef2a1 Fix the playground explorer and add a updated default query 2023-04-03 22:07:10 -04:00
Syer10 a6dddf311c Basically finish MangaQuery, only paging left 2023-04-03 22:04:46 -04:00
Syer10 e8c2bad187 Handle missing objects in graphql 2023-04-02 20:39:56 -04:00
Syer10 52bda2c080 Start working on graphql paging 2023-04-02 20:15:09 -04:00
Syer10 607919f40f Implement more query parameters 2023-04-02 17:33:19 -04:00
Syer10 d830638ee6 Use actual MangaStatus enum 2023-04-02 16:47:00 -04:00
Syer10 106bda2097 Proper conversion Scalar for Long to String and back 2023-04-02 16:30:03 -04:00
Syer10 7debb27374 Might not need a updates query 2023-03-31 23:11:18 -04:00
Syer10 05b5a7f598 Add updates 2023-03-31 23:03:26 -04:00
Syer10 3bbda7ba54 More todos 2023-03-31 22:36:01 -04:00
Syer10 9312f5fd14 Add global meta 2023-03-31 22:30:14 -04:00
Syer10 399eb07e35 Fix imports 2023-03-31 22:21:09 -04:00
Syer10 eb197ebcee Switch database logger to SLF4J 2023-03-31 22:19:13 -04:00
Syer10 4c30d8ab05 Some TODOs with ideas 2023-03-31 22:00:01 -04:00
Syer10 3a67ddf0f6 Add Extensions to Graphql 2023-03-31 20:58:18 -04:00
Syer10 6541c7b5b7 Serialize Long as String in graphql 2023-03-31 20:30:24 -04:00
Syer10 37f41ade43 Directly use the database for sources in graphql 2023-03-31 20:29:55 -04:00
Syer10 007d20d417 Add Sources to Graphql 2023-03-31 19:44:21 -04:00
Syer10 00370a81fa Minor cleanup 2023-03-31 19:10:04 -04:00
Syer10 d4599c3331 Use Graphiql with the Explorer plugin for the query builder 2023-03-31 19:09:32 -04:00
Syer10 bce76bbcf3 Use Kotlin Coroutines Flow instead of Project reactor 2023-03-30 18:28:56 -04:00
Valter Martinek 847a5fe71b Subscriptions! 2023-03-30 17:05:41 -04:00
Valter Martinek e2fa003239 Rewrite graphql controller execute as function without docs 2023-03-30 17:04:01 -04:00
Valter Martinek 0c555e88d3 Update graphql-playground endpoint 2023-03-30 17:04:01 -04:00
Valter Martinek bf7f1a04b3 Add categories to graphql 2023-03-30 17:04:01 -04:00
Valter Martinek 623172af6d Add mutation for updating chapters 2023-03-30 17:04:01 -04:00
Valter Martinek 4fb689d9e4 Add chapter and manga meta field 2023-03-30 17:04:01 -04:00
Valter Martinek 6054c489c6 Add graphql playground 2023-03-30 17:04:01 -04:00
Valter Martinek 21719f4408 Add basic graphql implementation with manga and chapters loading with data loaders 2023-03-30 17:03:56 -04:00
Aria Moradi f2a650ba02 fix typo 2023-03-27 21:27:10 +03:30
Aria Moradi 871c28b1ea cleanup notes 2023-03-27 21:26:37 +03:30
schroda d3aa32147a Add logic to only update specific categories (#520)
Makes it possible to only update specific categories.

In case a manga is in an excluded category it will be excluded even if it is also in an included category.
2023-03-27 20:15:46 +03:30
schroda 9a50f2e408 Notify clients even if no manga gets updated (#531)
In case no manga gets updated and no update job was running before, the client would never receive an info about its update request
2023-03-27 17:11:19 +03:30
schroda dcde4947e8 Emit update to clients after adding all mangas to the queue (#521)
Emitting updates before all the mangas were added to the queue could lead to e.g. wrong progress calculation.
2023-03-25 22:08:42 +03:30
schroda 5b61bdc3a8 add size field to Category data class (#519)
Makes it possible to display the size of a category to the user
2023-03-25 22:07:50 +03:30
schroda ec1d65f4c3 update library grouped by source (#511)
* Update mangas grouped by source

* Limit parallel update requests
2023-03-10 11:03:09 +03:30
akabhirav a0081dec07 fix manga unread and download count (#509) 2023-02-23 22:04:03 +03:30
akabhirav 783787e514 Send last read chapter in Mangas in Category API (#507)
* Send last read chapter with manga

* optimize query

* introduce new field for better performance
2023-02-21 02:47:45 +03:30
akabhirav ac99dd55a2 Fix random page sent when manga is downloaded (#508) 2023-02-21 02:40:38 +03:30
Mitchell Syer c56f984952 Fix SharedPreferences.Editor.clear and SharedPreferences.Editor.remove (#505)
* Fix SharedPreferences.Editor.clear and SharedPreferences.Editor.remove

* UNCHECKED_CAST

* Support removing Set<String>

* Typo

* Remove unneeded OptIn
2023-02-19 07:54:24 +03:30
Aria Moradi 9269ca726e It's not us, I swear ;;; 2023-02-16 10:57:26 +03:30
DattatreyaReddy Panta eca3205dcf Update winget.yml (#500) 2023-02-14 15:02:41 +03:30
akabhirav 13f5486d0b Fix CBZ download bug for newly added mangas in Library (#499) 2023-02-13 19:17:14 +03:30
Aria Moradi d4e71274f9 update changelog 2023-02-12 23:33:06 +03:30
Aria Moradi 4cc96de806 v0.7.0
CI Publish / Validate Gradle Wrapper (push) Successful in 12s
CI Publish / Build Jar (push) Failing after 5s
CI Publish / Make debian-all release (push) Has been skipped
CI Publish / Make linux-assets release (push) Has been skipped
CI Publish / Make linux-x64 release (push) Has been skipped
CI Publish / Make macOS-arm64 release (push) Has been skipped
CI Publish / Make macOS-x64 release (push) Has been skipped
CI Publish / Make windows-x64 release (push) Has been skipped
CI Publish / Make windows-x86 release (push) Has been skipped
CI Publish / release (push) Has been skipped
2023-02-12 23:08:05 +03:30
Aria Moradi d27ef12039 stop using depricated API 2023-02-12 23:03:45 +03:30
Aria Moradi f3c2ee4c40 re-order config options 2023-02-12 22:50:06 +03:30
akabhirav 555f73b478 Download as CBZ (#490)
* Download as CBZ

* Better error handling for zips (code review changes)
2023-02-12 22:45:58 +03:30
akabhirav 544bf2ea21 fix Page index issues for some providers (#491) 2023-02-12 18:34:30 +03:30
Aria Moradi 54bbb5e384 rethink image cache (#498) 2023-02-12 18:33:36 +03:30
akabhirav b10062c73d Decouple Cache and Download behaviour (#493)
* Separate cache dir from download dir

* Move downloader logic outside of caching/image download logic

* remove unnecessary method duplication

* moved download logic inside download provider

* optimize and handle partial downloads

* made code review changes
2023-02-12 18:26:26 +03:30
Aria Moradi a027d6df1b disable playwright for v0.6.7 2023-02-12 14:35:11 +03:30
Mitchell Syer 926a53a4b0 add support for Extensions Lib 1.4 (#496)
* Support extensions lib 1.4

* Fix build

* Support UpdateStrategy

* Update extension lib min/max to match Tachiyomi

* Use HttpSource.getMangaUrl and add Chapter.realUrl
2023-02-12 05:49:32 +03:30
Mitchell Syer 406cb46170 Fix logging and update system try (#488)
- Dorkbox SystemTray now automatically adds its shutdown hook, and removed the manual function
2023-02-05 22:21:35 +03:30
akabhirav acc58dc892 Fixe Dex2Jar and dorkbox dependency issues (#487)
Co-authored-by: akxer <>
2023-02-05 18:43:00 +03:30
Aria Moradi 55894c22a4 upgrade dorkbox stuff 2023-01-15 12:46:50 +03:30
Aria Moradi 476b10b862 update gradle version 2023-01-13 13:05:50 +03:30
Aria Moradi 3cbbe446ab fix ambiguous reference issue on JDK 13+ 2023-01-11 15:46:02 +03:30
Mitchell Syer 4cf7512ee0 Improve Playwright handling (#479)
* Improve playwright

* Move DriverJar.java and Chromium.kt
2023-01-08 01:17:53 +03:30
Mitchell Syer ee8ec460a1 Improve Gradle Configuration (#478)
* Improve gradle configuration

* Formatting fix

Co-authored-by: Aria Moradi <aria.moradi007@gmail.com>

* Improve asm version lock description

Co-authored-by: Aria Moradi <aria.moradi007@gmail.com>

* Improve image decoder description

Co-authored-by: Aria Moradi <aria.moradi007@gmail.com>

Co-authored-by: Aria Moradi <aria.moradi007@gmail.com>
2023-01-07 20:07:53 +03:30
Aria Moradi deecab3cca fix typo 2023-01-03 13:39:15 +03:30
Aria Moradi d2f5c1a195 link to Tachiyomi section 2023-01-03 13:38:20 +03:30
Aria Moradi dba77e26a3 Clarify and Update 2023-01-03 13:30:58 +03:30
Aria Moradi fa48bafbc6 Clarify and Update 2023-01-03 13:28:54 +03:30
Aria Moradi 73c48694c7 remove possibly misleading sentence 2023-01-03 13:24:42 +03:30
Aria Moradi 0ff89d039b fix CategoryMetaTable reference to CategoryTable (#473) 2023-01-03 13:19:44 +03:30
Aria Moradi 7a7081ee13 Update CategoryMetaTable.kt 2023-01-02 18:23:01 +03:30
Aria Moradi 874aaf4e93 fix when playwright fails on providing a UA 2022-12-28 17:14:12 +03:30
Mitchell Syer ebf076d9f6 Use extension list fallback if extensions fail to fetch (#469) 2022-12-25 11:15:37 +03:30
Mitchell Syer 073a041d4c Add better manga thumbnail handling (#465) 2022-12-23 00:43:36 +03:30
Mahor 96a9b4dabd Fix debian release (#463)
* Update debhelper-compat to 13

* Re-enable debian release

* Re-enable debian release
2022-12-16 00:21:42 +03:30
Aria Moradi 8e4cdf2386 disable deb release 2022-12-11 20:05:42 +03:30
Mitchell Syer ab4d925a5a Get Playwright working (#462)
* Get Playwright working with ShadowJar

* Set system driver implementation

* Minor cleanup

* Fix run gradle task and re-add some removed code

* No need to get the FS if it already exists

* use java implementation

Co-authored-by: Aria Moradi <aria.moradi007@gmail.com>
2022-12-09 21:47:26 +03:30
Zero d9c6f52e21 Basic android.graphics Rect and Canvas implementation (#461)
Some extensions use more Canvas methods, but they don't
really seem to get that far yet, all the others I was
able to test seem to work now.
2022-12-06 07:48:40 +03:30
Zero 0a748cd53b implementation of android.graphics.BitmapFactory (#460)
Only what was needed is implemented, compression method is still untested.
2022-12-05 19:21:16 +03:30
Aria Moradi 07314ef018 get default User Agent from WebView (#457)
* get default User Agent from WebView

* make sure to close browser

Co-authored-by: Mitchell Syer <Mitchellptbo@gmail.com>

Co-authored-by: Mitchell Syer <Mitchellptbo@gmail.com>
2022-12-04 21:21:19 +03:30
Aria Moradi 5eaebf678f fix regex 2022-12-04 13:03:23 +03:30
Aria Moradi 80fbfa60de better description 2022-12-04 12:59:33 +03:30
Aria Moradi fbbcc9e9b6 update issue mod 2022-12-04 12:50:03 +03:30
Aria Moradi f47dc6b9de WebView based cloudflare interceptor (#456)
* WebView based cloudflare interceptor

ported https://github.com/vvanglro/cf-clearance to kotlin

* code clean up

* Forgot to commit these

* Get ResolveWithWebView working
1. Make sure to .use all closeable resources
2. Use 10 seconds instead of 1 second for waiting for cloudflare(this was the most probable issue)
3. Use Extension UA when possible
4. Minor cleanup of logging

* rewrite and refactor

Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2022-12-04 12:08:54 +03:30
Aria Moradi 5f8e74f017 fix Changelog typos 2022-11-26 20:45:51 +03:30
Aria Moradi 8c1ca0ac7e add Chagelog TL;DR 2022-11-26 20:37:09 +03:30
Aria Moradi 9018de3c4c v0.6.6
CI Publish / Validate Gradle Wrapper (push) Successful in 11s
CI Publish / Build Jar (push) Failing after 5s
CI Publish / Make debian-all release (push) Has been skipped
CI Publish / Make linux-assets release (push) Has been skipped
CI Publish / Make linux-x64 release (push) Has been skipped
CI Publish / Make macOS-arm64 release (push) Has been skipped
CI Publish / Make macOS-x64 release (push) Has been skipped
CI Publish / Make windows-x64 release (push) Has been skipped
CI Publish / Make windows-x86 release (push) Has been skipped
CI Publish / release (push) Has been skipped
2022-11-26 20:29:51 +03:30
Valter Martinek e7cb88c757 Download queue missing update fix (#450)
* Add immediate updates to download queue manager for updates that always needs to be delivered

* Update server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt

Co-authored-by: Mitchell Syer <Mitchellptbo@gmail.com>

* Revert change to make sure that data in status sent to client are actual

* Reduce number of immediate updates to clients

Co-authored-by: Mitchell Syer <Mitchellptbo@gmail.com>
2022-11-16 20:03:51 +03:30
Valter Martinek d6127d6811 Add batch endpoint for removing downloads from download queue (#452) 2022-11-16 20:01:48 +03:30
Aria Moradi 67e09e2e1d make chapters endpoint more unifrom 2022-11-15 15:46:02 +03:30
Valter Martinek 8fbc24c751 Batch editing and deleting any chapter (#449)
* Add new endpoint for batch editing any chapter

* Add option to batch editing chapters to delete chapter (remove downloaded content)

* Rename the endpoint to match single manga batch endpoint

* Do not return early, in case there are other changes

* PR changes
2022-11-15 14:19:20 +03:30
Valter Martinek c0948209be Fix docs for /server/check-updates (#447) 2022-11-11 16:21:29 +03:30
Valter Martinek 7237161d52 Fix settings/check-update endpoint (#445) 2022-11-10 21:32:27 +03:30
Aria Moradi 94c2e21e2b Future proofing 2022-11-10 04:36:56 +03:30
Aria Moradi 65067e6e01 changes needed for tachiyomi tracker 2022-11-10 02:13:20 +03:30
Valter Martinek 39490ce7ba Add batch chapter update endpoint (#442) 2022-11-09 20:43:29 +03:30
Mitchell Syer 2f3f47c745 Set source preference doc fix (#441) 2022-11-08 10:32:45 +03:30
Mitchell Syer 2195c3df76 Downloader Rewrite (#437)
* Downloader rewrite
- Rewrite downloader to use coroutines instead of a thread
- Remove unused Page functions
- Add page progress
- Add ProgressResponseBody
- Add support for canceling a download in the middle of downloading
- Fix clear download queue

* Minor fix

* Minor improvements
- notifyAllClients now launches in another thread and only sends new data every second
- Better handling of download queue checker in step()
- Minor improvements and fixes

* Reorder downloads

* Download in parallel by source

* Remove TODO
2022-11-08 04:39:26 +03:30
Aria Moradi 119b9db6b4 refactor deprecated api 2022-11-07 22:50:20 +03:30
Aria Moradi fcbc598732 Revert H2 database to v1 2022-11-07 22:50:20 +03:30
Aria Moradi e850049e8e add category and global meta (#438) 2022-11-07 21:04:34 +03:30
Aria Moradi 907adea73f Migrate to H2 v2 2022-11-07 14:10:33 +03:30
Valter Martinek 2ac5c1362c add batch download api (#436)
* Add POST /downloads endpoint for creating multiple

* Fix review notes

* Add chapter id to API endpoints

* Rewrite batch chapter download to use chapter id instead of mangaId+chapterIndex combination

* Change EnqueueInput format to be more futureproof

* Change endpoint path

* Change endpoint path
2022-11-07 01:02:18 +03:30
Mitchell Syer 8b20e2b48f Add request body to documentation (#435) 2022-11-06 23:19:11 +03:30
Valter Martinek c2a9820fc1 POST variant for /{sourceId}/search endpoint (#434)
* Add POST variant for `/{sourceId}/search` endpoint which handles body data as list of FilterChanges

* Revert changes to existing endpoint and create new route and change the interface

* Update doc

* Rename api endpoint
2022-11-05 20:48:20 +03:30
Valter Martinek a9e5bc0c95 Pre-load meta entries for all chapters for optimization (#432)
Load meta entries for all chapters in one query to prevent N+1 queries
2022-10-30 20:18:27 +03:30
Valter Martinek 0fa2834d25 add MangaTable.lastFetchedAt and ChapterTable.chaptersLastFetchedAt (#431)
* Add lastFetchedAt and chaptersLastFetchedAt columns to manga

* Update lastFetchedAt columns when data are fetched from source

* Add age and chaptersAge fields to MangaDataClass

* Replace two migrations with single migration
2022-10-30 20:16:23 +03:30
Valter Martinek 23f0876c00 Add cache control header to manga page response (#430) 2022-10-29 22:19:19 +03:30
Anurag 6d88d90659 Fix: Error handling for popular/latest api if pageNum was supplied as zero (#424)
* fix: handle and throw proper error if pageNum is zero for popular/latest api, fixes #75

* chore: replace if-else with kotlin require which throws IllegalArgumentException and add comment

* fix: remove comment as exception message is enough
2022-10-28 14:34:22 +03:30
Mitchell Syer a3c366c360 Lint (#423) 2022-10-22 15:38:14 +03:30
Mitchell Syer 3bef07eeab Update dependencies (#422)
* Update dependencies and lint files

* Revert lint
2022-10-22 03:33:07 +03:30
Aria Moradi d029e65b8e include list of mangas missing source in restore report (#421) 2022-10-20 00:20:39 +03:30
Aria Moradi f305ac6905 remove BuildConfig as extensions now use AppInfo 2022-10-19 23:08:08 +03:30
Aria Moradi 4d4a46d2a5 move Tachiyomi's BuildConfig to kotlin dir 2022-10-19 22:44:00 +03:30
Aria Moradi 8218f2f830 ktlint 2022-10-19 16:22:07 +03:30
like b1bf901eac replace quickjs with Mozilla Rhino (#415)
* replace quickjs with jdk 8 default js engine

* replace quickjs with rhino engine and translate type for read comic online extension

* move quick js to AndroidCompat

* fix commicabc long type cast exception
2022-10-12 14:03:49 +03:30
Mitchell Syer 06eff55210 Updater cleanup and improvements (#416) 2022-10-11 19:57:15 +03:30
Mitchell Syer 71730fddad Documentation cleanup (#417) 2022-10-11 12:54:45 +03:30
Mitchell Syer f2d1c6e3cb Fix downloader memory leak (#418) 2022-10-11 12:52:10 +03:30
Marco Ebbinghaus 7ae837ca3c Remove support for Sorayomi web interface (#414)
fixes #392
2022-10-07 22:26:26 +03:30
Vedant b10908df5e Update winget.yml (#410) 2022-10-02 15:07:16 +03:30
Mahor 4dd4d38d5b Revert back to correct way of handling jre_dir (#408) 2022-09-28 22:44:14 +03:30
Mahor 447c286b56 Add libc++-dev (#405)
Use java8-runtime-headless virtual package which is a superset of default-jre-headless
2022-09-25 18:19:37 +03:30
Aria Moradi c71898ece9 Update Changelog
CI Publish / Validate Gradle Wrapper (push) Successful in 17s
CI Publish / Build Jar (push) Failing after 5s
CI Publish / Make debian-all release (push) Has been skipped
CI Publish / Make linux-assets release (push) Has been skipped
CI Publish / Make linux-x64 release (push) Has been skipped
CI Publish / Make macOS-arm64 release (push) Has been skipped
CI Publish / Make macOS-x64 release (push) Has been skipped
CI Publish / Make windows-x64 release (push) Has been skipped
CI Publish / Make windows-x86 release (push) Has been skipped
CI Publish / release (push) Has been skipped
2022-09-18 09:20:21 +04:30
Aria Moradi 9473e88ea9 bump version 2022-09-18 09:14:44 +04:30
Mahor d7663ed56e Fix deb package (#397) 2022-08-29 21:59:23 +04:30
voltrare da7569e2f5 fix jre path(#396)
use `mv -T` @mahor1221
2022-08-27 16:01:05 +04:30
Vedant d989940a4d Update winget.yml (#393) 2022-08-21 15:29:18 +04:30
Aria Moradi bd6a86b135 fix more broken stuff 2022-08-19 00:26:03 +04:30
Aria Moradi b38eb11503 fix more broken stuff 2022-08-19 00:25:37 +04:30
Aria Moradi 7aef32c13d fix more broken stuff 2022-08-19 00:24:40 +04:30
Aria Moradi fab64b147c fix broken links 2022-08-19 00:21:23 +04:30
Aria Moradi cc5a63205c v0.6.4
CI Publish / Validate Gradle Wrapper (push) Successful in 18s
CI Publish / Build Jar (push) Failing after 47s
CI Publish / Make debian-all release (push) Has been skipped
CI Publish / Make linux-assets release (push) Has been skipped
CI Publish / Make linux-x64 release (push) Has been skipped
CI Publish / Make macOS-arm64 release (push) Has been skipped
CI Publish / Make macOS-x64 release (push) Has been skipped
CI Publish / Make windows-x64 release (push) Has been skipped
CI Publish / Make windows-x86 release (push) Has been skipped
CI Publish / release (push) Has been skipped
2022-08-19 00:16:55 +04:30
Aria Moradi 814166a884 Fix mistakes from #384 (#385) 2022-08-10 19:14:16 +04:30
Aria Moradi 55240d9e9b Rename every instance of Tachidesk jar to Tachdidesk-Server.jar (#384) 2022-08-10 18:40:16 +04:30
Mahor 9dc598150f Replace linux-all with linux-assets (#381)
* Move linux package specific files to scripts/resources/pkg

* Replace linux-all with linux-assets

* Fix linux-x64 launchers issue

* Remove -e
2022-08-10 18:20:07 +04:30
Mahor 21c087e273 Tidy up bundler script (#380)
* Tidy up bundler script

* Update paths

* Remove -e

* Revert "Remove -e"

This reverts commit 1e29293dd0148c8aa692004f36b29d7abd9ca0f0.
2022-08-10 18:10:45 +04:30
Mitchell Syer bdf3a7014f Improve DocumentationDsl, bugfix default values and add queryParams (#378)
* Improve DocumentationDsl, bugfix default values and add queryParams

Adding `queryParams<Int>("mangaId[]")` would allow something like this: `http://127.0.0.1:4567/api/v1/download/manga?mangaId[]=1&mangaId[]=2`

* Remove extra comma

* Make QueryParams not nullable and use default value if empty

* Allow nullable again
2022-07-30 17:59:27 +04:30
Mahor dfea6e9b1b Update gradle action (#372)
* Update gradle action

* Update actions in build_pull_request.yml
2022-07-04 23:23:31 -04:00
Mahor 50eef1190e Run workflow jobs toghether (#371)
* Run scripts in parallel

* Re enable deb package builds
2022-07-04 10:06:30 -04:00
Mahor ed180121ff Refactor scripts (#370)
* Rename debian to deb

* Merge scripts into one

* Add error handler

* Disable wine installation to change electron icon due to error

* Replace debuild with dpkg-buildpackage

* Update workflows with new script

* Fix path
2022-07-02 15:42:08 -04:00
Vedant bdb0ad89d4 Publish to Windows Package Managar (WinGet) (#369)
* Update publish.yml

* Create winget.yml

* Update winget.yml

* Update publish.yml

* Update winget.yml
2022-06-30 08:29:23 +04:30
Mahor 7195a30d55 Add linux-all.tar.gz & systemd service (#366)
* Update my email address

* Add systemd configs to debian package

* Add systemd configs

* Tidy up

* Add linux-all.tar.gz

* Rename Tachidesk.jar to tachidesk-server.jar

* Fix typo

* Fix typo
2022-06-16 17:58:31 +04:30
Mitchell Syer 5b0426a94c Docs improvements (#359)
* Use Array since Javalin OpenAPI requires it to read the list generics

* Use custom Pager class for documentation
2022-05-21 14:42:10 +04:30
Mitchell Syer a6d012abd9 Fix documentation errors (#358) 2022-05-20 15:54:06 +04:30
Aria Moradi 86f0b3f29f fix WebUI release name
CI Publish / Validate Gradle Wrapper (push) Successful in 13s
CI Publish / Build artifacts and release (push) Failing after 15s
2022-05-06 20:36:42 +04:30
Aria Moradi 85e3aa34ac bump WebUI 2022-05-06 20:19:11 +04:30
Aria Moradi 5bbc1dedef fix formatting by kotlinter 2022-05-06 17:52:16 +04:30
Aria Moradi 39b468ef06 fix copymanga (#354) 2022-05-06 17:45:05 +04:30
Mitchell Syer fe17176b31 document all endpoints (#350)
* Document all endpoints

* Forgot about global endpoints
2022-04-27 16:01:39 +04:30
abhijeetChawla 84f701c4ab add ChapterCount to manga object in categoryMangas endpoint (#349)
* adds ChapterCount to the Manga returned when accessing the array of Manga is a category

* removed a conflicting expresssion
2022-04-24 13:13:35 +04:30
Mitchell Syer 047f8c176f document manga endpoints (#348) 2022-04-24 13:08:33 +04:30
Mitchell Syer d82e79b680 Add displayValues json field for select filter (#347) 2022-04-24 13:06:19 +04:30
Aria Moradi 320d1ae9d8 add support for alternative web interfaces (#342)
* add support for alternative web interfaces

* fix naming

* won't bundle sorayomi zip

* clean diff
2022-04-16 21:09:36 +04:30
Aria Moradi a8892143a2 fix Applications dir dependency (#344) 2022-04-16 20:58:12 +04:30
Aria Moradi 50f4532406 add support for changing downloads dir (#343) 2022-04-16 20:20:57 +04:30
Fidel Selva 844454053d handle solid RAR archives (#339)
* Upgrade junrar version to 7.5.0 and set unrar.extractor.thread-keep-alive-seconds to 30 (default is 5)

* #338 Read whole archive in case RAR file is solid (it is, it can't be decompressed at an arbitrary location).
2022-04-16 18:24:03 +04:30
Mitchell Syer db5c5ed534 Save categories when manga is unfavorited (#335)
Fixes non-library manga with categories in backups
2022-04-08 06:10:39 +04:30
Aria Moradi a26b8ecca0 v0.6.3 2022-04-07 15:54:42 +04:30
Aria Moradi 5a32ccfa7a fix auth not actually blocking requests (#333) 2022-04-06 21:30:38 +04:30
Mitchell Syer f51818b157 Add QuickJS, replaces Duktape for Extensions Lib 1.3 (#331) 2022-04-02 19:43:45 +04:30
Mitchell Syer 31a624db51 Add last bit of code needed for Extensions Lib 1.3 (#330) 2022-04-02 05:02:26 +04:30
DattatreyaReddy Panta f045b18762 update description for Tachidesk-Sorayomi (#326)
* added Tachidesk-Flutter to readme

* Updated Description for Tachidesk-Sorayomi
2022-03-27 16:41:35 +04:30
Mitchell Syer f5006cac7d Add thumbnail support for stub sources (#320) 2022-03-22 15:51:58 +04:30
Mitchell Syer 152b193ad5 Improve source handling, fix errors with uninitialized mangas in broken sources (#319) 2022-03-22 15:51:07 +04:30
Mitchell Syer a27af0b642 Fix sources list of one source throws an exception (#308) 2022-03-20 19:24:09 +03:30
Aria Moradi 44ffed3f7c add support for tachiyomi extensions Lib 1.3 (#316)
* closes #315

* provide real values

* add support for tachiyomi extensions lib 1.3
2022-03-19 02:36:42 +03:30
Aria Moradi fa035ad9be fix meta update changing all keys (#314) 2022-03-18 00:14:22 +03:30
Mahor 186ace4343 Update README.md (#305)
* Update README.md

* Update README.md again
2022-03-05 09:38:20 +03:30
Aria Moradi 8fb1a0bb1f fix filterlist bugs (#306) 2022-03-05 01:13:48 +03:30
Aria Moradi 05513bf8b9 support array filter changes (#304)
* support array filter changes

* typo

* better formating
2022-03-05 00:06:55 +03:30
Aria Moradi 858784857e v0.6.2
CI Publish / Validate Gradle Wrapper (push) Successful in 12s
CI Publish / Build artifacts and release (push) Failing after 15s
2022-03-04 19:03:23 +03:30
Mahor 291a23949a Refactor debian-packager.sh, rename launcher scripts (#303)
* Improve windows-bundler.sh

* Overhaul debian-packager.sh. Rename base package name to tachidesk-server

* Add -electron-launcher-debian.sh
2022-03-03 13:26:56 +03:30
Aria Moradi 454de23844 fix wrong release name
CI Publish / Validate Gradle Wrapper (push) Successful in 12s
CI Publish / Build artifacts and release (push) Failing after 15s
2022-02-19 05:38:04 +03:30
Aria Moradi 1176092fc6 v0.6.1 2022-02-19 05:29:46 +03:30
Aria Moradi 0fc2c57395 fix mentions 2022-02-19 05:27:34 +03:30
Aria Moradi 0b292f8d74 update WebUI and CHANGELOG 2022-02-19 05:17:56 +03:30
Aria Moradi 4bbe51331f remove gson (#295)
* remove gson

* also remove kotson

* fix build
2022-02-19 05:12:20 +03:30
Aria Moradi c289786dfa auto-remove duplicate chapters (#294)
* auto-remove duplicate chapters

* Apply suggestions from code review

Co-authored-by: Mitchell Syer <Mitchellptbo@gmail.com>

* Update Chapter.kt

Co-authored-by: Mitchell Syer <Mitchellptbo@gmail.com>
2022-02-19 04:44:17 +03:30
Aria Moradi 9a9752a6a1 no online fetch on backup (#293)
* no online fetch on backup

* Revert "no online fetch on backup"

This reverts commit 09af582b1a3ef77ca62167bc01f7dcc9643329c9.

* alternative solution
2022-02-19 02:57:03 +03:30
DattatreyaReddy Panta c43b688f8e add Tachidesk-Flutter to readme (#292) 2022-02-01 21:52:58 +03:30
Mahor a6272d5221 Improve windows-bundler.sh (#290) 2022-01-22 14:10:10 +03:30
Mahor b440a58d95 Fix build_push.yml Hopefully (#289) 2022-01-18 11:01:39 +03:30
Mahor ec1589aa2d fix Debian package errors (#288)
* Fix lintian errors

Fix lintian errors
Short description in debian/changelog to hide lintian changelog warnings
Use launcher scripts that run java and electron from /usr/bin

* Update changelog version

* Rename laucnher scripts. Put electron installaion guide in electron-launcher-standalone.sh

* Seperate debian packager codes from unix-bundler.sh

* chmod +x debian-packager.sh

* Fix mistakes

* Fix mistakes

* Add missing '!' to shebang

* Change faviconlogo.png to tachidesk.png

* Change faviconlogo.png to tachidesk.png
2022-01-17 18:41:58 +03:30
Mahor 8d48e56fa0 Automated debian package building (#287)
* Automate debian package building

Move wxs files to scripts/resources/msi/
Define icon's path inside script instead of hardcoding it in wxs

* Revert back Windows script improvments

* Remove copyright year. Use uppercase for first letter of foruzesh

* Seperate deb from tar code with defining new debian-x64 arch

* Add ./unix-bundler.sh debian-x64

* Fix mistake

* Remove unneeded change of license
2022-01-16 08:27:59 +03:30
Mahor df7938037e Automated MSI package building (#277)
* Create msi package instead of zipping stuff

* Keep the zip release, Install wixl inside windows-bundler.sh

* Fix mistakes

* Seprate mv command

* Automate versioning

* Fix syntax mistake

* Fix mistake
2022-01-12 23:48:43 +03:30
Mitchell Syer c908ee2d49 Allow app compilation on Java 18+ (#286) 2022-01-09 22:30:10 +03:30
Mitchell Syer b714abddae Handlers must return a result (#282) 2022-01-07 17:03:35 +03:30
Mitchell Syer 63ca189907 Update Gradle and Dependencies (#281) 2022-01-07 17:03:01 +03:30
Aria Moradi 6130fb9ba2 update dex2jar 2021-12-13 17:16:19 +03:30
Aria Moradi f0edc676fc fix compile erorr 2021-12-13 17:16:03 +03:30
Aria Moradi b68fdb0772 ignore non image files (#269)
* ignore non image files

* Update server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt

Co-authored-by: Mitchell Syer <Mitchellptbo@gmail.com>

Co-authored-by: Mitchell Syer <Mitchellptbo@gmail.com>
2021-12-03 20:53:18 +03:30
Sascha Hahne 76c7bdd604 Add Route to stop and reset the updater (#260) 2021-12-01 23:18:32 +03:30
Mitchell Syer 205101568e Improve documentation with Http codes (#261)
* Improve documentation dsl with Http codes

* Fix plaintext requiring T
2021-12-01 23:17:41 +03:30
Aria Moradi efff68c49b refactor getChapter (#268)
* refactor

* better naming

* fix copytight notice
2021-12-01 22:39:19 +03:30
Aria Moradi f74f60bb1d Update README.md 2021-11-30 11:18:44 +03:30
Aria Moradi 63ea28a620 Update README.md 2021-11-29 20:22:43 +03:30
Aria Moradi 2c6d043277 bump version
CI Publish / Validate Gradle Wrapper (push) Successful in 12s
CI Publish / Build artifacts and release (push) Failing after 15s
2021-11-29 19:24:07 +03:30
Aria Moradi 9a226f7b64 update 2021-11-29 19:14:40 +03:30
Aria Moradi 3b73a0fd72 empty searchTerm support (#259)
* linter run

* convert search params to queryParams
2021-11-29 18:42:53 +03:30
Aria Moradi 2478aa77cd add support for MultiSelectListPreference (#258)
* add support for MultiSelectListPreference

* Update AndroidCompat/src/main/java/xyz/nulldev/androidcompat/io/sharedprefs/JavaSharedPreferences.kt

Co-authored-by: Mitchell Syer <Mitchellptbo@gmail.com>

* don't convert to list

* fix by @Syer10

Co-authored-by: Mitchell Syer <Mitchellptbo@gmail.com>
2021-11-28 23:11:56 +03:30
Aria Moradi a41068dbc9 migrate application directories (#255) 2021-11-28 19:56:06 +03:30
Aria Moradi 5e47b7ae6b Update README.md 2021-11-16 21:37:44 +03:30
Aria Moradi debf45a7d5 Update README.md 2021-11-16 21:09:16 +03:30
Aria Moradi e7041e8c8c Fix first page not being detected correctly (#253)
* fix first page not being recognized correctly

* fix first page not being recognized correctly
2021-11-15 23:49:02 +03:30
Aria Moradi bd960992bc cleanup directory names (#251) 2021-11-15 23:40:49 +03:30
Aria Moradi 0c5f6b432c update 2021-11-15 12:22:49 +03:30
Aria Moradi 49232edbd5 update 2021-11-15 12:06:04 +03:30
Mitchell Syer b02884f58d Add a Kotlin DSL for endpoint documentation (#249) 2021-11-14 18:16:39 +03:30
Aria Moradi 845b588426 Mention the existence of Mahor's Tachidesk-GTK 2021-11-13 13:51:20 +03:30
Aria Moradi 3a97d7c8be update 2021-11-13 13:27:17 +03:30
Sascha Hahne 2cb2ded2d9 Implement Update of Library/Category (#235)
* Implement Update Controller tests

* Basic Threading and notify

* WIP

* Reworked using coroutines

* Use Map for JobSummary Tracking

* Change Tests

* Clean up

* Changes based on review

* Rethrow cancellationexception

* Clean up

* Fix Merge Error

* Actually handle messages

* Clean up

* Remove useless annotation
2021-11-10 22:38:41 +03:30
Aria Moradi 14e02bee6c update 2021-11-10 12:17:26 +03:30
Aria Moradi 30f7cdc1ba add pagination to recentChapters (#246)
* add pagination to recentChapters

* Use kotlin native library

Co-authored-by: Mitchell Syer <Mitchellptbo@gmail.com>

Co-authored-by: Mitchell Syer <Mitchellptbo@gmail.com>
2021-11-08 18:45:52 +03:30
Mitchell Syer 420d14fc37 Fix Manga Meta, add Manga Meta test (#245)
* Fix Manga Meta, add Manga Meta test

* Tweak assertion strings
2021-11-08 03:08:22 +03:30
Aria Moradi 3d7953d977 add manga data to download queue object (#244)
* add manga data to download queue object

* fix lint erro
2021-11-07 21:32:57 +03:30
Aria Moradi 35238b3da1 stop supporting zero based image storage (#242)
* stop supporting zero based image storage, closes #210

* add test
2021-11-07 21:27:11 +03:30
Aria Moradi 446f4283e0 Update CONTRIBUTING.md 2021-11-02 15:58:30 +03:30
Aria Moradi 8062dd3709 convert request type 2021-11-02 12:14:44 +03:30
Aria Moradi 41c8fde8c5 ignore build artifacts generated by teting 2021-11-02 12:13:33 +03:30
Aria Moradi d90b986d19 implement Source Filters 2021-11-02 04:15:08 +03:30
Aria Moradi 64ea8416b2 refactor 2021-11-01 23:46:46 +03:30
Aria Moradi 100a4c9d35 refactor 2021-11-01 18:02:06 +03:30
Aria Moradi 4ef6dec89a cleanup 2021-11-01 02:10:51 +03:30
Aria Moradi a14cdc48bd fix credit 2021-11-01 02:09:14 +03:30
Aria Moradi 8a4ddbc6df update 2021-11-01 02:05:02 +03:30
Aria Moradi ee33acc561 allow injecting Sources 2021-10-31 18:05:55 +03:30
Aria Moradi 5fe69becf3 improve tests 2021-10-31 17:31:46 +03:30
Aria Moradi d460d3ccdf change windows bundle names 2021-10-31 14:06:59 +03:30
Sascha Hahne 0ee74943e8 Fix category reorder Endpoint. Added Test for Category Reorder (#232)
* Fix category reorder Endpoint. Added Test for Category Reorder

* Remove streams and use kotlin filtering
2021-10-30 14:46:22 +03:30
Aria Moradi 82837e38d2 update 2021-10-30 14:25:23 +03:30
Sascha Hahne 91df90d760 Fix broken test (#231) 2021-10-29 19:40:20 +03:30
Mitchell Syer 1ee37da720 Fix unread and download counts casing (#230) 2021-10-28 19:11:10 +03:30
Aria Moradi 6f8fc5b69d Update README.md 2021-10-28 12:35:24 +03:30
Sascha Hahne be1918c769 add Cache Header to Thumbnail Response for improved library performance (#228) 2021-10-26 21:46:38 +03:30
Sascha Hahne 0057b35a0a Expose unread and download count of Manga in category api (#227)
* #224 Created view for unread and download badges

* #224 Basic test structure

* Created test and cleaned up a bit

* Move counts to MangaDataClass and delete MangaViewDataClass

* Readded trailing space

* Removed SQL view and calculate with joins now
2021-10-25 13:41:07 +03:30
Sascha Hahne d12974702a Fix tests (#226) 2021-10-24 23:19:27 +03:30
Aria Moradi 921c41689d update 2021-10-24 23:08:56 +03:30
Aria Moradi 6389899507 remove anime support 2021-10-24 22:58:25 +03:30
Aria Moradi 92ede2a2b3 Update README.md 2021-10-24 17:28:02 +03:30
Aria Moradi 826a63ed71 Update README.md 2021-10-24 16:57:06 +03:30
Aria Moradi d1576a2a72 Update README.md 2021-10-24 14:27:12 +03:30
Aria Moradi 95f218d704 Update README.md 2021-10-23 20:36:45 +03:30
Aria Moradi 315d3a0ac0 Update CONTRIBUTING.md 2021-10-23 20:04:10 +03:30
Aria Moradi 954818cef2 Update CONTRIBUTING.md 2021-10-23 20:02:42 +03:30
Aria Moradi 5a95ca9b1b Update CONTRIBUTING.md 2021-10-23 20:00:35 +03:30
Aria Moradi c1e6f4c26e better cleaning algorithm 2021-10-23 19:27:50 +03:30
Aria Moradi 7c603258fb Update README.md 2021-10-21 14:32:58 +03:30
Mahor fcbc582686 Update README.md (#223)
Add PPA's description for Ubuntu-based distributions
2021-10-20 12:15:46 +03:30
Aria Moradi 9c4906b90b cleanup 2021-10-20 08:34:07 +03:30
Aria Moradi d35e31e02d bump version
CI Publish / Validate Gradle Wrapper (push) Successful in 16s
CI Publish / Build artifacts and release (push) Failing after 15s
2021-10-18 14:41:01 +03:30
Aria Moradi 6c4ca36c09 update WebUI 2021-10-18 14:37:13 +03:30
Aria Moradi 70355dc505 update 2021-10-11 00:21:10 +03:30
Aria Moradi 20aeaf2a05 update 2021-10-11 00:18:49 +03:30
Mitchell Syer 1f13e1d08b Use a custom task to run electron (#220) 2021-10-11 00:16:32 +03:30
Aria Moradi eb9d35c123 code cleanup 2021-10-11 00:15:15 +03:30
Mitchell Syer 45808cd530 Support using a CatalogueSource instead of only HttpSources (#219) 2021-10-11 00:01:04 +03:30
Aria Moradi fd715a3f92 fix workflow 2021-10-10 23:52:35 +03:30
Aria Moradi e3b32367a7 update 2021-10-10 23:51:21 +03:30
Aria Moradi bf9554a746 update 2021-10-10 23:32:19 +03:30
Aria Moradi b9f8ca1488 update 2021-10-10 23:01:58 +03:30
Aria Moradi e8c4159678 use correct conversion units 2021-10-10 21:31:38 +03:30
Aria Moradi e57e71629e update 2021-10-10 21:05:24 +03:30
Aria Moradi 2bfd9d24a4 remove isNsfw annotation detection 2021-10-10 21:04:28 +03:30
Aria Moradi b18b8fe22f update 2021-10-10 20:58:10 +03:30
Aria Moradi b154ff2f9d fix export chapter ordering, include new props in backup 2021-10-10 20:53:50 +03:30
Aria Moradi e9b764b63c update 2021-10-10 12:27:50 +03:30
Aria Moradi 7216b97d92 mimic Tachyomi's behaviour more closely, fixes ReadComicOnline (EN) 2021-10-10 12:18:21 +03:30
Aria Moradi 0e9d93b194 prepare CHANGELOG 2021-10-06 21:18:21 +03:30
Aria Moradi 2cbee62f0a update docs 2021-09-28 18:06:20 +03:30
Aria Moradi 379e9da5fe clenup 2021-09-28 18:00:24 +03:30
Aria Moradi ae7caa4901 merge 2021-09-28 17:57:55 +03:30
Aria Moradi cd8b4c9dd7 convert android.jar lib to a maven repo 2021-09-28 17:55:50 +03:30
Aria Moradi 60cd61dfd2 Update README.md 2021-09-28 03:42:20 +03:30
Aria Moradi 5a6637d9fc Update README.md 2021-09-28 03:39:45 +03:30
Aria Moradi dca7ed23f5 Update README.md 2021-09-28 03:36:06 +03:30
Aria Moradi 8cb5791f3b Update README.md 2021-09-28 03:34:14 +03:30
Aria Moradi 9b67f2c58f Update CHANGELOG.md 2021-09-28 03:24:57 +03:30
Aria Moradi 3815810d4f Update for release 2021-09-28 01:11:24 +03:30
Aria Moradi 819ceba17d bump version
CI Publish / Validate Gradle Wrapper (push) Successful in 12s
CI Publish / Build artifacts and release (push) Failing after 16s
2021-09-28 00:52:49 +03:30
Aria Moradi 0aa0d62e03 update changelog file and it's template 2021-09-28 00:51:05 +03:30
Aria Moradi b3e2a35880 update WebUI 2021-09-28 00:50:33 +03:30
Aria Moradi 15ec20c65d fix sorting 2021-09-27 20:27:40 +03:30
Aria Moradi d4d6d7e12f add recentChapters endpoint 2021-09-27 18:27:05 +03:30
Aria Moradi 2e7a4f1421 remove no longer relevant comment 2021-09-27 14:44:48 +03:30
Aria Moradi ab8a52faf3 rename ChapterTable.chapterIndex to ChapterTable.sourceOrder 2021-09-27 14:36:06 +03:30
Aria Moradi bd465559fb Update README.md 2021-09-26 23:48:29 +03:30
Aria Moradi 13ec45a95c aftermath of adding kotlinter to all modules 2021-09-25 04:34:02 +03:30
Mitchell Syer 13b034875b Workaround StdLib issue and add KtLint to all modules (#206)
* Workaround buildconfig kotlin stdlib issue

* Add KtLint to all modules
2021-09-25 04:31:03 +03:30
Aria Moradi bb701fb088 fix macOS-arm64 java path 2021-09-24 14:06:19 +03:30
Aria Moradi b367414865 changes 2021-09-24 13:56:26 +03:30
Aria Moradi 4b00eec608 update CHANGELOG 2021-09-19 18:01:13 +04:30
Aria Moradi 5e11b51152 update CHANGELOG 2021-09-19 17:59:37 +04:30
Aria Moradi 9fb43b996e CHANGELOG update 2021-09-19 17:39:28 +04:30
Aria Moradi bc2072e81f bump version
CI Publish / Validate Gradle Wrapper (push) Successful in 13s
CI Publish / Build artifacts and release (push) Failing after 15s
2021-09-19 17:36:46 +04:30
Aria Moradi f36bc3f643 update WebUI 2021-09-19 17:34:18 +04:30
Aria Moradi f7901ad843 fix windows paths 2021-09-19 16:43:16 +04:30
Aria Moradi 3771030ed6 closes #202 2021-09-19 14:24:13 +04:30
Aria Moradi 57197e58b5 fix Task path 2021-09-19 14:14:42 +04:30
Aria Moradi ac601399ac update WebUI 2021-09-19 14:14:21 +04:30
Aria Moradi 6a0e221153 fix compile 2021-09-19 01:01:20 +04:30
Aria Moradi 6a949fc851 Minor cleanup 2021-09-19 00:59:04 +04:30
Aria Moradi f1a077dc2f update CHANGELOG 2021-09-18 22:09:34 +04:30
Mitchell Syer f20962b02b Gradle Updates (#199)
* Cleanup and update gradle, update dependencies

* Duplicate Jsoup
2021-09-18 22:07:19 +04:30
Mitchell Syer 77e057f244 Update BytecodeEditor to use Java NIO Paths (#200) 2021-09-18 21:57:15 +04:30
Aria Moradi 2c5114c770 bump version
CI Publish / Validate Gradle Wrapper (push) Successful in 13s
CI Publish / Build artifacts and release (push) Failing after 16s
2021-09-18 21:07:38 +04:30
Aria Moradi a30895a199 update WebUI 2021-09-18 21:03:01 +04:30
Aria Moradi 6d46d4b3da closes #131 2021-09-18 20:30:56 +04:30
Aria Moradi 006efbbb77 add ChapterRecognition from tachiyomi, closes #10 2021-09-18 19:40:44 +04:30
Aria Moradi 52334087ad add support for Archive chapters to Local source 2021-09-18 19:14:06 +04:30
Aria Moradi ea8fb2c70a add comment 2021-09-18 16:11:25 +04:30
Aria Moradi 531d148718 don't save Local source chapters into disk again! 2021-09-18 14:53:59 +04:30
Aria Moradi d83ddea323 better URLs 2021-09-18 14:49:38 +04:30
Aria Moradi b5dea34090 update WebUI 2021-09-18 02:39:15 +04:30
Aria Moradi 7d8e3202b5 lint 2021-09-18 02:28:52 +04:30
Aria Moradi d956e0af4b add Local Source (#196) 2021-09-18 02:20:30 +04:30
Aria Moradi 9ad70990b5 update WebUI 2021-09-18 02:19:43 +04:30
Aria Moradi 82d711f077 fix typo 2021-09-18 02:19:38 +04:30
Aria Moradi 5fc28ef711 handle when title is changing properly 2021-09-18 02:10:35 +04:30
Aria Moradi 1f3dc682e2 migrate to kotlinx.json 2021-09-18 01:43:10 +04:30
Aria Moradi cce7768246 clean up imports 2021-09-18 01:03:06 +04:30
Aria Moradi 01172b0664 better exception messages 2021-09-18 01:00:42 +04:30
Aria Moradi 1ca11fdd34 add Local Source 2021-09-18 00:47:50 +04:30
Aria Moradi 52a064ae45 lint 2021-09-17 16:32:14 +04:30
Aria Moradi 6c62ddf927 depricate zero based chapters 2021-09-17 16:31:47 +04:30
Aria Moradi 47e04c08d0 fix lint complain 2021-09-17 15:17:18 +04:30
Aria Moradi d1601874b4 [SKIP CI] update CHANGELOG.md 2021-09-17 01:31:13 +04:30
Aria Moradi 6bc19af041 add ability to delete downloaded chapters 2021-09-17 01:29:42 +04:30
Aria Moradi 4e72a3886f actaully make sure the chapter exists 2021-09-17 00:38:15 +04:30
Aria Moradi 8e6b219eea update CHANGELOG.md 2021-09-14 04:18:19 +04:30
Aria Moradi bd638251e4 add BasicAuth support 2021-09-14 04:17:01 +04:30
Aria Moradi 0173d5e4b3 migrate to Javalin 4 2021-09-14 03:23:00 +04:30
Aria Moradi 4f364e134b remove expand char limit on MangaTable columns 2021-09-13 20:57:41 +04:30
Aria Moradi 94d2519717 add CHANGELOG for the next release 2021-09-13 20:14:02 +04:30
Aria Moradi 6a11d2e357 bump version
CI Publish / Validate Gradle Wrapper (push) Successful in 11s
CI Publish / Build artifacts and release (push) Failing after 16s
2021-09-13 19:32:05 +04:30
Aria Moradi 0a9e0bc9e4 update WebUI 2021-09-13 19:31:07 +04:30
Aria Moradi 5914e367d1 better CHANGELOG 2021-09-13 19:29:54 +04:30
Mitchell Syer aeaed888d4 Update Proto models to match Tachi preview (#192) 2021-09-12 04:04:48 +04:30
Aria Moradi a0f054b005 CHANGELOG.md 2021-09-11 19:47:06 +04:30
Aria Moradi 4498e9d444 beter handling of uninstalling Extensions 2021-09-11 16:26:04 +04:30
Aria Moradi 43e0763fef fix a bug where if two sources return the same URL, a false duplicate might be detected 2021-09-11 14:47:03 +04:30
Aria Moradi a519c8a482 better PR links 2021-09-11 03:18:36 +04:30
Aria Moradi 19bc595a2a update CHANGELOG.md 2021-09-11 03:13:26 +04:30
Aria Moradi db07825c58 remove println instance 2021-09-11 03:09:26 +04:30
Aria Moradi b199e3bf0e fix installing apk with weird name 2021-09-11 01:21:22 +04:30
Aria Moradi 1f9ea0891e add some logging 2021-09-11 00:48:17 +04:30
Aria Moradi 0a7aa48f1e fix apk log and apk name 2021-09-11 00:14:52 +04:30
Aria Moradi 4b65b7da6c rm dummy 2021-09-10 21:42:52 +04:30
Mosei 183a7dac4b Create docker_build_stable (#184)
* Create docker_build_stable

Add docker build workflow

* Rename docker_build_stable to docker_build_stable.yml

* Update docker_build_stable.yml

* Update docker_build_stable.yml
2021-09-10 21:42:31 +04:30
Aria Moradi 6726f008c1 add dummy file to trigger workflow 2021-09-10 21:22:28 +04:30
Mosei 89cf0c140f Update publish.yml (#190) 2021-09-10 20:49:57 +04:30
Mosei 504025ce80 Update build_push.yml (#191) 2021-09-10 20:49:33 +04:30
Aria Moradi fee9e914f1 fix exposed crash 2021-09-10 00:45:00 +04:30
Aria Moradi 76efa71c68 update WebUI 2021-09-09 23:05:58 +04:30
Aria Moradi 26e61959ae update WebUI 2021-09-09 16:46:19 +04:30
Aria Moradi 9a8956ef9d update dependencies 2021-09-09 16:41:41 +04:30
Aria Moradi 10d3ffc2f6 better UX 2021-09-09 06:05:09 +04:30
Aria Moradi 090399f61d add support for installing external APK 2021-09-09 05:22:45 +04:30
Aria Moradi ae7d975a92 run won't get stuck now 2021-09-09 05:15:51 +04:30
Aria Moradi 55ec6bcafe add TODO for changing later 2021-09-09 05:15:08 +04:30
Aria Moradi f0566d15af fix compiler warning 2021-09-08 20:27:29 +04:30
Aria Moradi a730b692bc update WebUI 2021-09-08 20:24:22 +04:30
Aria Moradi 826d767423 [SKIP CI] update CHANGELOG.md 2021-09-08 20:23:45 +04:30
Aria Moradi 6d227c7fcd Fix typo 2021-09-06 22:23:26 +04:30
Aria Moradi 7d9d97840e Update README.md 2021-09-06 14:41:00 +04:30
Mosei 110ded45a0 Update README.md (#189)
Remove depreciated arbuilder’s docker link
2021-09-05 21:16:25 +04:30
Aria Moradi 7872b593c7 Credit to @Syer10 2021-09-05 17:50:45 +04:30
Aria Moradi 90be30bddb add support for the new gnere type in WebUI 2021-09-05 01:40:34 +04:30
Mitchell Syer a298c61dab Change type of Manga.genre to a List<String> (#188) 2021-09-05 01:36:52 +04:30
Aria Moradi eb416c45bd remove not-code changes from CHANGELOG 2021-09-05 00:54:01 +04:30
Aria Moradi b05b817aeb Update README.md 2021-09-03 18:52:06 +04:30
Aria Moradi 666602283a update
CI Publish / Validate Gradle Wrapper (push) Successful in 12s
CI Publish / Build artifacts and release (push) Failing after 15s
2021-09-03 07:59:41 +04:30
Aria Moradi ac040a4bae fix oopsies n'shit 2021-09-03 07:57:53 +04:30
Aria Moradi b4982c8f22 update 2021-09-03 07:55:15 +04:30
Aria Moradi ce3ad92095 remove node module cache, won't need it anymore 2021-09-03 07:54:07 +04:30
Aria Moradi 8e1ac8698c bump version 2021-09-03 07:48:31 +04:30
Aria Moradi b60a39c7cb update 2021-09-03 07:46:11 +04:30
Aria Moradi 3b3e8ba4c8 add new unix bundles to release 2021-09-03 07:45:11 +04:30
Aria Moradi e387f6d3be update 2021-09-03 07:44:00 +04:30
Aria Moradi 799d469cb6 bump electron version 2021-09-03 07:43:13 +04:30
Aria Moradi a54a596fa7 fix macOS bundler 2021-09-03 07:39:28 +04:30
Aria Moradi 92d73d0285 add linux and macOS bundler script 2021-09-03 06:50:42 +04:30
Aria Moradi acb752e4e8 update jre 2021-09-03 05:43:33 +04:30
Aria Moradi 9e377abba6 update CHANGELOG 2021-09-03 04:55:01 +04:30
Aria Moradi 04552c0923 update WebUI 2021-09-03 04:52:34 +04:30
Aria Moradi 2e1fb85b73 [skip ci] update changelog 2021-09-03 04:49:38 +04:30
Aria Moradi 50db32d9b4 this is dumb 2021-09-03 04:48:32 +04:30
Aria Moradi 28743d953e [skip ci] r857 2021-09-03 04:41:00 +04:30
Aria Moradi b109d26aa7 fixed typo 2021-09-03 04:39:45 +04:30
Aria Moradi 7fd21bd06d [skip ci] r855 2021-09-02 17:45:52 +04:30
Aria Moradi 12e0ffb466 text cleanup 2021-09-02 17:43:43 +04:30
Aria Moradi 9259341df8 missed this changelog 2021-09-02 17:31:30 +04:30
Aria Moradi 8e8aca7e7b Update CONTRIBUTING.md 2021-09-02 17:29:26 +04:30
Aria Moradi 7f0bcd987b Update CONTRIBUTING.md 2021-09-02 17:24:32 +04:30
Aria Moradi ef21de95cb add continues changelog file 2021-09-02 17:20:53 +04:30
Aria Moradi ca3246de02 bump version 2021-09-02 15:21:27 +04:30
Aria Moradi f0940b7926 bump version
CI Publish / Validate Gradle Wrapper (push) Successful in 15s
CI Publish / Build artifacts and release (push) Failing after 17s
2021-08-31 17:43:23 +04:30
Aria Moradi 0066e0b901 suppress warnings 2021-08-31 17:33:29 +04:30
Aria Moradi 9771f566b0 better comments 2021-08-30 02:48:10 +04:30
Aria Moradi 38ad4c6dec refactor 2021-08-30 02:44:35 +04:30
Aria Moradi 37cf80a188 code cleanup 2021-08-30 02:38:15 +04:30
Aria Moradi c86ee53f66 resolve compiler warnings 2021-08-29 22:25:43 +04:30
Aria Moradi c2cea7e797 can serialize Search Filters 2021-08-29 22:19:44 +04:30
Aria Moradi a8ef6cdd4f change category re-order url 2021-08-29 21:52:23 +04:30
Aria Moradi 53d157fee8 update 2021-08-29 21:47:09 +04:30
Aria Moradi c2e07b13f6 fix categories not being normalized 2021-08-29 20:09:17 +04:30
Aria Moradi 2e8cc48311 update WebUI 2021-08-29 02:11:11 +04:30
Aria Moradi f6f811eb77 update WebUI 2021-08-29 01:59:36 +04:30
Aria Moradi ac5528fb15 add when the statement was true 2021-08-27 04:49:53 +04:30
Aria Moradi 940d2b7862 bump version 2021-08-26 22:31:06 +04:30
Aria Moradi 835fe3dad3 sorround with try, catch as it might throw an exception 2021-08-26 22:24:54 +04:30
Aria Moradi dfaecc08c5 add realUrl to Manga, reperesents open in WebView URL 2021-08-26 22:11:51 +04:30
Aria Moradi 87f5e9b847 fix migration number 2021-08-26 22:10:51 +04:30
Aria Moradi 3d3939e808 better logs 2021-08-26 22:10:27 +04:30
Aria Moradi 90822e3858 merge manga data while restoring backup 2021-08-26 16:28:45 +04:30
Aria Moradi 14eec47e9c correct value for inLibrary 2021-08-26 01:34:56 +04:30
Aria Moradi 15ed3fcc69 actual fix for source order 2021-08-26 01:31:59 +04:30
Aria Moradi fd8fa9f3ef fix chapter restore order 2021-08-26 01:28:42 +04:30
Aria Moradi b81075f4a7 fix docker builds faling? 2021-08-24 22:23:39 +04:30
Aria Moradi f11a52e8e1 we don't need that feild anymore 2021-08-24 22:23:00 +04:30
Aria Moradi 9c007483d4 better method of detemining if a source is Nsfw 2021-08-24 02:44:13 +04:30
Aria Moradi ff4e818e4c add some comments 2021-08-23 21:48:27 +04:30
Aria Moradi 45a50ca0c1 add isNsfw to SourceDataClass 2021-08-23 21:46:28 +04:30
Aria Moradi 65d9021c37 close response 2021-08-23 06:10:31 +04:30
Aria Moradi 66481a0391 NPE fix suggested by @syer10 2021-08-23 06:05:04 +04:30
Aria Moradi a14a82bc9a fix oppsie, sync dependencies with tachiyomi 2021-08-23 05:27:39 +04:30
Aria Moradi 756c57a16e also intercept on 403 2021-08-23 04:56:27 +04:30
Aria Moradi 8b19e34dc5 Update README.md 2021-08-23 04:38:32 +04:30
Aria Moradi 50083019ee add copyright notices 2021-08-23 04:37:30 +04:30
Aria Moradi 155272e638 add new keys 2021-08-23 04:28:07 +04:30
Aria Moradi 08443ceb3d remove comment 2021-08-23 04:20:04 +04:30
Aria Moradi c215696f04 have a lighter log level 2021-08-23 04:17:40 +04:30
Aria Moradi 5ca42bf9b6 make it compile 2021-08-23 04:02:55 +04:30
Aria Moradi 3272b9dec5 add CloudflareInterceptor from TachiWeb-Server 2021-08-23 03:45:10 +04:30
Aria Moradi 2ebd5da4aa bump kotlinter version 2021-08-22 19:00:33 +04:30
Aria Moradi 34f024ace2 migrate dex2jar dependency to @ThexXTURBOXx version 2021-08-21 16:36:34 +04:30
Aria Moradi b31f2d50f6 No more legacy backup 2021-08-21 06:39:12 +04:30
Aria Moradi da44d3b2b4 bump to v0.4.7
CI Publish / Validate Gradle Wrapper (push) Successful in 12s
CI Publish / Build artifacts and release (push) Failing after 17s
2021-08-21 06:30:45 +04:30
Aria Moradi 99ec2aca6a update WebUI 2021-08-21 06:27:14 +04:30
Aria Moradi 6c278604ec got rid of legacy backups 2021-08-21 06:23:58 +04:30
Aria Moradi 1e094a467a not TODO 2021-08-21 06:13:55 +04:30
Aria Moradi 978ccfeeba the true commit 2021-08-21 06:12:22 +04:30
Aria Moradi e93d66d8a1 add backup validation endpoints 2021-08-21 06:08:17 +04:30
Aria Moradi c29a749833 proto export support 2021-08-21 05:48:05 +04:30
Aria Moradi b08d5d1261 all forms of Default are illegal 2021-08-21 05:10:09 +04:30
Aria Moradi 9b129789e9 all forms of Default are illegal 2021-08-21 05:05:01 +04:30
Aria Moradi a76a6d2798 creating a categort named Default is illegal 2021-08-21 03:58:46 +04:30
Aria Moradi 086a760378 update WebUI 2021-08-21 03:55:22 +04:30
Aria Moradi f78c8d4fd8 Default is now a category, no more library 2021-08-21 03:54:16 +04:30
Aria Moradi 7b91489997 better print 2021-08-21 01:20:18 +04:30
Aria Moradi 36a8980c95 TODO block no longer relevant 2021-08-21 01:11:47 +04:30
Aria Moradi 7c65640cb7 include extra chapter data in restore 2021-08-21 00:37:50 +04:30
Aria Moradi d70e68495a restoring with clean db and not installed extensions work 2021-08-21 00:18:03 +04:30
Aria Moradi 2586202772 better comments 2021-08-19 21:11:53 +04:30
Aria Moradi b5f771368a put back dex2jar where it should be 2021-08-19 03:15:35 +04:30
Aria Moradi 0c28320ce3 better debug launcher 2021-08-19 02:54:36 +04:30
Aria Moradi c8b4fbc36b new observation 2021-08-19 02:06:48 +04:30
Aria Moradi e9b07849fe move dex2jar to server, lint 2021-08-19 01:47:26 +04:30
Aria Moradi 409260af6f Merge pull request #176 from Suwayomi/protobuf
protobuf backup support
2021-08-19 00:47:32 +04:30
Aria Moradi d3d53d1a4e initial support for portobuf backup 2021-08-19 00:46:45 +04:30
Aria Moradi e2db191f70 consolidate the external backup api 2021-08-18 23:34:39 +04:30
Aria Moradi d61816734d add all proto backup classes we need 2021-08-18 22:58:56 +04:30
Aria Moradi f4dad8058f Merge branch 'master' into protobuf 2021-08-18 21:51:12 +04:30
Aria Moradi 70bdb375c3 Update README.md 2021-08-18 19:10:46 +04:30
Aria Moradi e724ab0a29 Update README.md 2021-08-18 19:10:16 +04:30
Aria Moradi 7d0ee2ac11 Update README.md 2021-08-18 06:17:04 +04:30
Aria Moradi 59b7e852e2 Update README.md 2021-08-18 06:15:44 +04:30
Aria Moradi b2eb1a391d Update README.md 2021-08-18 05:31:34 +04:30
Aria Moradi 9b3aee98d3 Update CONTRIBUTING.md 2021-08-18 05:30:19 +04:30
Aria Moradi 0476f4144c Update CONTRIBUTING.md 2021-08-18 05:29:24 +04:30
Aria Moradi ed77f45fae Update CONTRIBUTING.md 2021-08-18 05:28:26 +04:30
Aria Moradi 0cd529e746 Update CONTRIBUTING.md 2021-08-18 05:26:55 +04:30
Aria Moradi 5969048318 Update README.md 2021-08-18 05:19:42 +04:30
Aria Moradi d1a7f8baa0 Update README.md 2021-08-18 05:04:22 +04:30
Aria Moradi 18dc936002 Update README.md (#177)
* Update README.md

* Update README.md
2021-08-18 04:50:10 +04:30
Aria Moradi b4b7b5d572 bump to v0.4.6
CI Publish / Validate Gradle Wrapper (push) Successful in 12s
CI Publish / Build artifacts and release (push) Failing after 17s
2021-08-18 04:29:14 +04:30
Aria Moradi 291c2e692d clean up build.gradle files, move constants to buildSrc 2021-08-18 04:24:58 +04:30
Aria Moradi 8a9a4f21b1 remove some stuff we don't use 2021-08-18 04:06:13 +04:30
Aria Moradi cd31332b39 better download progress 2021-08-18 03:55:52 +04:30
Aria Moradi cc8d2162a0 fix compile issue 2021-08-18 02:59:07 +04:30
Aria Moradi e6313cdc67 yeet improvments from jui 2021-08-18 01:21:17 +04:30
Aria Moradi 5af64892e7 Merge branch 'master' into protobuf 2021-08-18 00:28:42 +04:30
Aria Moradi a5578a7ac7 fix compile warnings 2021-08-17 23:54:02 +04:30
Aria Moradi fcdda6406e update dependencies 2021-08-17 23:53:41 +04:30
Aria Moradi 9bdd9f8aa6 better endpoint urls based on suggestion from @mgn-norm 2021-08-17 21:45:19 +04:30
Aria Moradi f3856f051b protobuf backup endpoints 2021-08-17 20:09:31 +04:30
Aria Moradi d3a6662c60 make it compile 2021-08-15 03:16:13 +04:30
Aria Moradi 5474eddf84 fix some inconsitencies 2021-08-15 02:41:23 +04:30
Aria Moradi b666cd47d4 fix shouldOverwrite 2021-08-15 00:25:08 +04:30
Aria Moradi 8a986383fe fixes #175, better webUI download task 2021-08-14 17:10:41 +04:30
Aria Moradi 9fa17f617e add anime seach functionality 2021-08-11 08:47:07 +04:30
Aria Moradi e46e165704 bump to v0.4.5
CI Publish / Validate Gradle Wrapper (push) Successful in 12s
CI Publish / Build artifacts and release (push) Failing after 17s
2021-08-11 00:39:08 +04:30
Aria Moradi a6af423fb4 Merge branch 'master' of github.com:Suwayomi/Tachidesk-Server 2021-08-10 09:42:41 +04:30
Aria Moradi 3397e694c0 sync anime lib implementation with 12 (#133)
* sync anime lib implementation with 11

* fix wrong api

* delete unused classes

* adapt to lib 12

* add LICENSE for eu.kanade.tachiyomi

* changes for lib 12

* update to lib 12

* update webUI
2021-08-10 09:42:14 +04:30
Aria Moradi 77ff82505e fix wrong api 2021-08-10 04:01:08 +04:30
Aria Moradi c3f2838270 fix name tag generator 2021-08-10 03:40:49 +04:30
Aria Moradi aed7f205b6 update webUI 2021-08-10 03:39:36 +04:30
Aria Moradi 95b3587f7a update webUI 2021-08-10 02:04:35 +04:30
Aria Moradi a4baeb995a refactor endpoints to the new styles 2021-08-09 23:18:41 +04:30
Aria Moradi 032ab54206 fix tag generator 2021-08-09 23:18:06 +04:30
Aria Moradi 1f9c1eb1c0 only open browser when appropriate 2021-08-09 07:15:41 +04:30
Aria Moradi a213e568ba update property strings 2021-08-09 06:59:16 +04:30
Aria Moradi 7aeaeb4b86 move copyright notice to it's place 2021-08-09 06:49:12 +04:30
Aria Moradi 81aef4b8fa remove un-used files 2021-08-09 06:48:02 +04:30
Aria Moradi 31f0b6a16c ability to override server.conf with java -D arguments 2021-08-09 06:45:49 +04:30
Aria Moradi 44e3a682fd update Project name 2021-08-09 01:27:09 +04:30
Aria Moradi 70402a6d3a update webUI to r20
CI Publish / Validate Gradle Wrapper (push) Successful in 13s
CI Publish / Build artifacts and release (push) Failing after 17s
2021-08-08 14:18:46 +04:30
Aria Moradi 5c4143224a Bump to v0.4.4 2021-08-08 07:11:03 +04:30
Aria Moradi f943e924f7 Update build_push.yml 2021-08-08 07:00:12 +04:30
Aria Moradi 7d2f542f8a bundle WebUI for stable builds 2021-08-08 06:18:00 +04:30
Aria Moradi fb1f88e971 lint 2021-08-08 05:36:54 +04:30
Aria Moradi f566f13423 launch parameter for Syer 2021-08-08 05:32:14 +04:30
Aria Moradi 0f88baf1c1 download webUI on demand 2021-08-08 05:31:58 +04:30
Aria Moradi a04cbcd814 add webUIEnabled config 2021-08-08 01:11:21 +04:30
Aria Moradi a17d6a2ea4 clean up the after effects of bringing back webUI 2021-08-07 22:52:44 +04:30
Aria Moradi 08f49e0ac4 Revert "changes for the headless Tachidesk"
This reverts commit e575aaf4fb.
2021-08-07 22:37:27 +04:30
Aria Moradi 7787dd1ecc better comments 2021-08-07 22:18:05 +04:30
Aria Moradi 3d0765d4ab posibly fixes #119 2021-08-07 22:01:38 +04:30
Aria Moradi b51651ace4 refactor to fancy migration classes 2021-08-07 21:58:36 +04:30
Aria Moradi 568fa56d59 add suppress unused class warning 2021-08-07 17:15:01 +04:30
Aria Moradi 50dee9251c rename preview repo 2021-08-06 07:22:11 +04:30
Aria Moradi e575aaf4fb changes for the headless Tachidesk 2021-08-06 07:15:38 +04:30
Aria Moradi bdd5caae1a kick webUI out of Tachidesk 2021-08-06 04:55:03 +04:30
Aria Moradi 3af7de3460 partial implementation for Extenstion Preferences 2021-08-06 04:53:53 +04:30
Aria Moradi caa219f8d6 remove not used types 2021-08-06 03:44:54 +04:30
Aria Moradi afabaccf1d implement data store for extension prefs 2021-08-06 03:37:09 +04:30
Aria Moradi 53cc73701c update exposed version 2021-08-05 23:53:45 +04:30
Aria Moradi c69b954ffd move migration lib outside of Tachidesk 2021-08-05 22:53:31 +04:30
Aria Moradi d05c447fe4 refactor and comment 2021-08-05 20:43:08 +04:30
Aria Moradi 5810a24cb0 about api url is changed 2021-08-05 17:12:53 +04:30
Syer10 e04c6a9f4d Update Android jar to API 30 (#172) 2021-08-05 15:13:57 +04:30
Aria Moradi e3a9f7af42 it's named Stable 2021-08-05 06:42:28 +04:30
Aria Moradi 5eb58a73ee check app update api closes #72 2021-08-05 06:39:09 +04:30
Aria Moradi 68ad1f72ce new about url, fix typo 2021-08-05 05:49:18 +04:30
Aria Moradi 704a52d943 separate Global API 2021-08-05 05:40:48 +04:30
Aria Moradi 67ec9ccc4e mark methods as @JsonIgnore to avoid Jackson serializing them 2021-08-05 03:31:32 +04:30
Aria Moradi c7112ec67f mark methods as @JsonIgnore to avoid Jackson serializing them 2021-08-05 03:28:21 +04:30
Aria Moradi 1e1e2034eb Merge branch 'master' of github.com:Suwayomi/Tachidesk 2021-08-04 15:36:44 +04:30
Aria Moradi 5f316c6f44 rename to value 2021-08-04 15:36:29 +04:30
KraXen72 0c39718f82 the plural of Manga *is* Mangas don't let anyone tell you otherwise (#168) 2021-08-04 02:03:50 +04:30
Aria Moradi 8a5ac4a0af add better comments 2021-08-01 18:12:49 +04:30
Syer10 492d5f5e84 Fix list preference crashing on set (#166) 2021-08-01 17:13:50 +04:30
Syer10 5a3621fe39 Override getDefaultValueType with values based on the preference (#167) 2021-08-01 17:13:29 +04:30
Syer10 fb862e23e5 Fix main thread scheduler, fixes Komga and LANraragi(not the preferences though) (#165) 2021-07-31 19:43:50 +04:30
Aria Moradi 3d69348301 add comment about which types should be supported 2021-07-31 08:35:56 +04:30
Aria Moradi 30787846a2 sync androidx.preference method signatures with extensions-lib 2021-07-31 08:28:45 +04:30
Aria Moradi 345ca27f85 support ListPreference 2021-07-31 07:59:05 +04:30
Aria Moradi b7a6d6cae8 fix HentaiHand preferences 2021-07-31 07:41:19 +04:30
Aria Moradi dadb686514 refine extension preferences API 2021-07-31 07:24:45 +04:30
Aria Moradi 75f635a28b add configure button to source card 2021-07-31 05:06:46 +04:30
Aria Moradi f2bd5b8149 add text to EditTextPreference 2021-07-31 05:04:30 +04:30
Aria Moradi 6ed4c79ca4 merge missing commit from #163 2021-07-31 04:50:43 +04:30
Aria Moradi 333f954919 hide anime menu stuff 2021-07-31 04:14:48 +04:30
Aria Moradi 2494d0821d ConfigurableExtension(PreferenceScreen) support (#163)
* initial PreferenceScreen support, works with 'NeoXXX Scans' (pt-br)

* convert EditTextPreference to json successfully

* commit what I've got

* bring back the old SharedPreferences for CustomContext, implement Toast

* put back syer's implementation
2021-07-31 03:53:28 +04:30
Aria Moradi e349d0cef3 add pereference change 2021-07-31 03:50:41 +04:30
Aria Moradi eddad2ba89 Merge branch 'preference-screen' of https://github.com/Syer10/Tachidesk into preference-screen 2021-07-31 02:50:20 +04:30
Aria Moradi bfaf88afd6 put back syer's implementation 2021-07-31 02:49:15 +04:30
Syer10 cd59aed8c7 Fix Invalid Type exception 2021-07-30 17:56:12 -04:00
Aria Moradi f18ca5811f bring back the old SharedPreferences for CustomContext, implement Toast 2021-07-31 02:10:48 +04:30
Aria Moradi 1ed9bcf7c8 commit what I've got 2021-07-31 00:41:09 +04:30
Aria Moradi 29a79ab079 dont print manifestXml 2021-07-30 19:00:58 +04:30
Aria Moradi 7c03c73419 convert EditTextPreference to json successfully 2021-07-30 18:48:07 +04:30
Aria Moradi 74f3b9b609 merge master into preference-screen 2021-07-30 16:55:14 +04:30
Aria Moradi a3953d530e make it compile 2021-07-30 16:52:06 +04:30
Aria Moradi 2280e8c725 initial PreferenceScreen support, works with 'NeoXXX Scans' (pt-br) 2021-07-30 15:05:21 +04:30
Syer10 b327df732c Comments, comments, and comments!! And future proofing (#162) 2021-07-30 05:56:13 +04:30
Aria Moradi 21d7cf5d6a prepare for PreferenceScreen support, remove some old depricated android libs 2021-07-30 00:46:45 +04:30
Aria Moradi 5b64bdc5b7 add copyright notices to Syer10's previous PR 2021-07-29 23:26:23 +04:30
Aria Moradi a3a25b6263 move replace classes 2021-07-29 22:59:03 +04:30
Aria Moradi c7611c8024 log data root dir 2021-07-29 22:36:39 +04:30
Aria Moradi f08170504c Merge branch 'master' of github.com:Suwayomi/Tachidesk 2021-07-29 22:35:29 +04:30
Syer10 863dccb5ea Modify extension bytecode to fix SimpleDateFormat cannot parse errors (#149)
- Fixes sources like NHentai
- Fixes mass testing
- Cleans up a bit of the build.gradle.kts's
- Fix Tsuki by setting a http.agent system property
2021-07-29 22:21:25 +04:30
Syer10 fc8bb10ca3 Modify extension bytecode to fix SimpleDateFormat cannot parse errors 2021-06-26 12:51:54 -04:00
Syer10 d06c3586fd Fix sources that require the singleton Json object (#147) 2021-06-24 11:37:26 +04:30
Syer10 395989b528 Improve process time for getAndroid.ps1 (#146) 2021-06-23 00:15:39 +04:30
Aria Moradi a325440f24 bump to v0.4.3
CI Publish / Validate Gradle Wrapper (push) Successful in 10s
CI Publish / Build artifacts and release (push) Failing after 22s
2021-06-17 00:28:19 +04:30
Aria Moradi 14072bb5a0 fix manga extensions not loading 2021-06-17 00:26:34 +04:30
Aria Moradi 7fc33ba8db fix cache 2021-06-06 03:19:03 +04:30
Aria Moradi 47e51b6615 fix naming 2021-06-06 03:13:09 +04:30
Aria Moradi 857562eaff add build flexiblity for Equinox 2021-06-06 02:48:26 +04:30
Aria Moradi bace854b50 rm dummy 2021-06-06 02:05:47 +04:30
Aria Moradi e4a404472d Merge branch 'master' of github.com:Suwayomi/Tachidesk 2021-06-06 02:05:26 +04:30
Aria Moradi 7bfa215b4c change old paths 2021-06-06 01:52:05 +04:30
Aria Moradi ab7af4b80b fix typo 2021-06-06 01:35:39 +04:30
Aria Moradi 2c7ebd8ece prepare for integration with Equinox 2021-06-06 01:34:49 +04:30
Syer10 c96da79058 Fix MacOS crashing on launch (#132) 2021-06-05 22:36:36 +04:30
Aria Moradi 8f09ebacf5 dummy file to trigger gh actions 2021-06-04 23:10:40 +04:30
Aria Moradi e21f3b9c75 closes #130 2021-06-04 21:51:48 +04:30
Aria Moradi 37eeef06e2 correct spelling 2021-06-04 16:34:19 +04:30
Aria Moradi b7fe56687c Bump styfle/cancel-workflow-action from 0.5.0 to 0.9.0 2021-06-04 13:35:20 +04:30
Aria Moradi 60565729ca lint by linter 2021-06-04 13:35:07 +04:30
Aria Moradi 36f4e1c340 move all packages to 'suwayomi.tachidesk' 2021-06-04 13:08:20 +04:30
Aria Moradi abc2a5214b Merge branch 'master' of github.com:Suwayomi/Tachidesk 2021-06-04 10:43:39 +04:30
Aria Moradi a29010e0d7 fix chached image returning file type with extra . 2021-06-04 10:43:03 +04:30
arbuilder db99ab66ae Update build_push.yml (#124)
* Update build_push.yml

docker workflow for preview build

* Update build_push.yml

remove cd master

* Update build_push.yml

Change access token
2021-06-01 13:27:57 +04:30
arbuilder 84cc73c149 Update publish.yml (#123)
* Update publish.yml

add docker build workflow

* Update publish.yml

Remove cd master

* Update publish.yml

Change access token
2021-06-01 13:25:38 +04:30
Aria Moradi 10a29cab33 Merge branch 'master' of github.com:Suwayomi/Tachidesk 2021-05-30 04:56:59 +04:30
Aria Moradi 849e2f103a [SKIP CI] download chapters for real now 2021-05-30 04:24:21 +04:30
Syer10 6c22fe193a Add meta info for clients to store custom data in (#113)
* Add meta info for clients to store custom data in

* PR comments

* Really update migration
2021-05-30 04:18:08 +04:30
Syer10 e69dbbf418 Working shared preferences (#112)
* Working shared preferences

* Remove unneeded prefs dir

* Todo
2021-05-30 04:05:56 +04:30
806 changed files with 75189 additions and 34181 deletions
+11
View File
@@ -0,0 +1,11 @@
[*.{kt,kts}]
indent_size=4
insert_final_newline=true
ij_kotlin_allow_trailing_comma=true
ij_kotlin_allow_trailing_comma_on_call_site=true
ij_kotlin_name_count_to_use_star_import=2147483647
ij_kotlin_name_count_to_use_star_import_for_members=2147483647
ktlint_standard_discouraged-comment-location=disabled
ktlint_standard_if-else-wrapping=disabled
ktlint_standard_no-consecutive-comments=disabled
+4 -1
View File
@@ -25,4 +25,7 @@
*.pyc binary
*.swp binary
*.pdf binary
*.exe binary
*.exe binary
*.avif binary
*.heif binary
*.jxl binary
-44
View File
@@ -1,44 +0,0 @@
---
name: "🐞 Bug report"
title: "[Bug] <short description>"
about: "Report a bug"
labels: "bug"
---
**PLEASE READ THIS**
I acknowledge that:
- I have updated to the latest version of the app.
- I have tried the troubleshooting guide described in `README.md`
- If this is a request for adding/changing an extension it should be brought up to Tachiyomi: https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose
- If this is an issue with some extension not working properly, It does work inside Tachiyomi as intended.
- I have searched the existing issues and this is a new ticket **NOT** a duplicate or related to another open issue
- I will fill out the title and the information in this template
Note that the issue will be automatically closed if you do not fill out the title or requested information.
**DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT**
---
## Device information
- Tachidesk version: (Example: v0.2.3-r255-win32)
- Server Operating System: (Example: Ubuntu 20.04)
- Server Desktop Environment: N/A or (Example: Gnome 40)
- Server JVM version: bundled with win32 or (Example: Java 8 Update 281 or OpenJDK 8u281)
- Client Operating System: <usually the same as above Server Operating System>
- Client Web Browser: (Example: Google Chrome 89.0.4389.82)
## Steps to reproduce
1. First Step
2. Second Step
### Expected behavior
Describe what should have happened. Remove this line after you are done.
### Actual behavior
Describe what happens instead. Remove this line after you are done.
## Other details
Describe additional details If necessary. Remove this line after you are done.
+144
View File
@@ -0,0 +1,144 @@
name: 🐞 Bug report
description: Report a bug in Suwayomi-Server
labels: [bug]
body:
- type: textarea
id: reproduce-steps
attributes:
label: Steps to reproduce
description: Provide an example of the issue.
placeholder: |
Example:
1. First step
2. Second step
3. Issue here
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected behavior
description: Explain what you should expect to happen.
placeholder: |
Example: "This should happen..."
validations:
required: true
- type: textarea
id: actual-behavior
attributes:
label: Actual behavior
description: Explain what actually happens.
placeholder: |
Example: "This happened instead..."
validations:
required: true
- type: input
id: suwayomi-server-version
attributes:
label: Suwayomi-Server version
description: You can find your Suwayomi-Server version in **More → About**.
placeholder: |
Example: "v2.1.1867"
validations:
required: true
- type: input
id: server-os
attributes:
label: Server operating system
description: The operating system on which Suwayomi-Server is running on
placeholder: |
Example: "Windows 11 Pro 24H2 | Ubuntu 24.04.2 LTS"
validations:
required: true
- type: input
id: server-desktop-environment
attributes:
label: Server Desktop Environment
description:
placeholder: |
Example: "Gnome 40"
validations:
required: false
- type: input
id: server-jvm-version
attributes:
label: Server JVM version
description: The java version used to run Suwayomi-Server
placeholder: |
Example: "openjdk 21.0.5 2024-10-15 LTS"
validations:
required: true
- type: input
id: client-name
attributes:
label: Used client name
description:
placeholder: |
Example: "Suwayomi-WebUI"
validations:
required: true
- type: input
id: client-version
attributes:
label: Client version
description:
placeholder: |
Example: "v1.2.3"
validations:
required: true
- type: input
id: client-browser
attributes:
label: Used web browser
description: The browser which is used to open Suwayomi-WebUI
placeholder: |
Example: "Chrome 134.0.6998.118 (64-Bit) | FireFox 136.0.2 (64-Bit) | Electron v35.0.2"
validations:
required: true
- type: input
id: client-os
attributes:
label: Client operating system
description: The system on which the Suwayomi-WebUI is running on
placeholder: |
Example: "Windows 11 Pro 24H2 | Ubuntu 24.04.2 LTS"
validations:
required: true
- type: textarea
id: other-details
attributes:
label: Other details
description: The more information that gets provided the better, especially via videos and images
placeholder: |
Additional details and attachments.
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
description: Read this carefully, we will close and ignore your issue if you skimmed through this.
options:
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open or closed issue.
required: true
- label: I have written a short but informative title (ideally less than ~100 characters).
required: true
- label: I have tried the troubleshooting guide described in [README.md](https://github.com/Suwayomi/Suwayomi-Server?tab=readme-ov-file#troubleshooting-and-support)
required: true
- label: I have updated to the **[latest version](https://github.com/suwayomi/suwayomi-server/releases/latest)**.
required: true
- label: I have filled out all of the requested information in this form, including specific version numbers.
required: true
- label: I understand that **Suwayomi does not have or fix any extensions**, and I **will not receive help** for any issues related to sources or extensions.
required: true
+4
View File
@@ -1 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: ☎️ Support
url: https://discord.gg/DDZdqZWaHA
about: Join our discord to get help for anything that is not a bug or a feature request
-29
View File
@@ -1,29 +0,0 @@
---
name: "🌟 Feature request"
title: "[Feature Request] <short description>"
about: "Suggest a feature to improve the project"
labels: "enhancement"
---
**PLEASE READ THIS**
I acknowledge that:
- I have updated to the latest version of the app.
- I have tried the troubleshooting guide described in `README.md`
- If this is a request for adding/changing an extension it should be brought up to Tachiyomi: https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose
- If this is an issue with some extension not working properly, It does work in Tachiyomi application as intended.
- I have searched the existing issues and this is a new ticket **NOT** a duplicate or related to another open issue
- I will fill out the title and the information in this template
Note that the issue will be automatically closed if you do not fill out the title or requested information.
**DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT**
---
## What feature should be added to Tachidesk?
Explain What the feature is and how it should work in detail. Remove this line after you are done.
## Why/Project's Benefit/Existing Problem
Explain why this should be added. Remove this line after you are done.
@@ -0,0 +1,37 @@
name: 🌟 Feature request
description: Suggest a feature to improve Suwayomi-Server
labels: [enhancement]
body:
- type: textarea
id: feature-description
attributes:
label: Describe your suggested feature
description: How can Suwayomi-Server be improved?
placeholder: |
Example:
"It should work like this..."
validations:
required: true
- type: textarea
id: other-details
attributes:
label: Other details
placeholder: |
Additional details and attachments.
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
description: Read this carefully, we will close and ignore your issue if you skimmed through this.
options:
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open or closed issue.
required: true
- label: I have written a short but informative title (ideally less than ~100 characters).
required: true
- label: I have updated to the **[latest version](https://github.com/suwayomi/suwayomi-server/releases/latest)**.
required: true
- label: I have filled out all of the requested information in this form, including specific version numbers.
required: true
+17 -32
View File
@@ -3,6 +3,10 @@ name: CI Pull Request
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check_wrapper:
name: Validate Gradle Wrapper
@@ -10,34 +14,32 @@ jobs:
steps:
- name: Clone repo
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
uses: gradle/actions/wrapper-validation@v4
build:
name: Build pull request
needs: check_wrapper
if: "!startsWith(github.event.head_commit.message, '[SKIP CI]')"
runs-on: ubuntu-latest
steps:
- name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.5.0
with:
access_token: ${{ github.token }}
- name: Checkout pull request
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
path: master
fetch-depth: 0
- name: Set up JDK 1.8
uses: actions/setup-java@v1
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 1.8
java-version: 21
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Copy CI gradle.properties
run: |
@@ -45,24 +47,7 @@ jobs:
mkdir -p ~/.gradle
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
- name: Download android.jar
run: |
cd master
curl https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
- name: Build Jar
working-directory: master
run: ./gradlew ktlintCheck :server:shadowJar --stacktrace
- name: Cache node_modules
uses: actions/cache@v2
with:
path: |
**/react/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/react/yarn.lock') }}
- name: Build and copy webUI, Build Jar
uses: eskatos/gradle-command-action@v1
with:
build-root-directory: master
wrapper-directory: master
arguments: :webUI:copyBuild :server:shadowJar --stacktrace
wrapper-cache-enabled: true
dependencies-cache-enabled: true
configuration-cache-enabled: true
+190 -70
View File
@@ -5,41 +5,41 @@ on:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check_wrapper:
name: Validate Gradle Wrapper
runs-on: ubuntu-latest
steps:
- name: Clone repo
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
uses: gradle/actions/wrapper-validation@v4
build:
name: Build artifacts and deploy preview
name: Build Jar
needs: check_wrapper
if: "!startsWith(github.event.head_commit.message, '[SKIP CI]')"
runs-on: ubuntu-latest
steps:
- name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.5.0
with:
access_token: ${{ github.token }}
- name: Checkout master branch
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
ref: master
path: master
fetch-depth: 0
- name: Set up JDK 1.8
uses: actions/setup-java@v1
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 1.8
java-version: 21
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Copy CI gradle.properties
run: |
@@ -47,62 +47,181 @@ jobs:
mkdir -p ~/.gradle
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
- name: Download android.jar
run: |
cd master
curl https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
- name: Cache node_modules
uses: actions/cache@v2
with:
path: |
**/react/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/react/yarn.lock') }}
- name: Build and copy webUI, Build Jar
uses: eskatos/gradle-command-action@v1
- name: Build Jar
env:
TachideskBuildType: "Preview"
with:
build-root-directory: master
wrapper-directory: master
arguments: :webUI:copyBuild :server:shadowJar --stacktrace
wrapper-cache-enabled: true
dependencies-cache-enabled: true
configuration-cache-enabled: true
ProductBuildType: "Preview"
working-directory: master
run: ./gradlew :server:shadowJar --stacktrace
# - name: Mock Build and copy webUI, Build Jar
# run: |
# mkdir -p master/server/build
# cd master/server/build
# echo "test" > Tachidesk-v0.3.8-r583.jar
- name: Upload Jar
uses: actions/upload-artifact@v4
with:
name: jar
path: master/server/build/*.jar
if-no-files-found: error
- name: Upload icons
uses: actions/upload-artifact@v4
with:
name: icon
path: master/server/src/main/resources/icon
if-no-files-found: error
- name: Tar scripts dir to maintain file permissions
run: tar -cvzf scripts.tar.gz -C master/ scripts/
- name: Upload scripts.tar.gz
uses: actions/upload-artifact@v4
with:
name: scripts
path: scripts.tar.gz
if-no-files-found: error
jlink:
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
name: linux-x64
- os: windows-latest
name: windows-x64
- os: macos-14
name: macOS-arm64
- os: macos-13
name: macOS-x64
os: [ubuntu-latest, windows-latest, macos-14, macos-13]
steps:
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 21
distribution: 'temurin'
- name: Package JDK
run: jlink --add-modules java.base,java.compiler,java.datatransfer,java.desktop,java.instrument,java.logging,java.management,java.naming,java.prefs,java.scripting,java.se,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,jdk.attach,jdk.crypto.ec,jdk.jdi,jdk.management,jdk.net,jdk.random,jdk.unsupported,jdk.unsupported.desktop,jdk.zipfs --output suwa --strip-debug --no-man-pages --no-header-files --compress=2
- name: Upload JRE package
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.name }}-jre
path: suwa
bundle:
strategy:
fail-fast: false
matrix:
include:
- name: debian-all
jre: linux-x64
- name: appimage
jre: linux-x64
- name: linux-assets
jre: linux-assets
- name: linux-x64
jre: linux-x64
- name: macOS-x64
jre: macOS-x64
- name: macOS-arm64
jre: macOS-arm64
- name: windows-x64
jre: windows-x64
name: [debian-all, appimage, linux-assets, linux-x64, macOS-x64, macOS-arm64, windows-x64]
name: Make ${{ matrix.name }} release
needs: [build, jlink]
runs-on: ubuntu-latest
steps:
- name: Download Jar
uses: actions/download-artifact@v4
with:
name: jar
path: server/build
- name: Download JRE
uses: actions/download-artifact@v4
if: matrix.name != 'linux-assets' && matrix.name != 'debian-all'
with:
name: ${{ matrix.jre }}-jre
path: jre
- name: Download icons
uses: actions/download-artifact@v4
with:
name: icon
path: server/src/main/resources/icon
- name: Download scripts.tar.gz
uses: actions/download-artifact@v4
with:
name: scripts
- name: Make ${{ matrix.name }} release
run: |
mkdir upload
tar -xvpf scripts.tar.gz
scripts/bundler.sh -o upload/ ${{ matrix.name }}
- name: Upload ${{ matrix.name }} release
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.name }}
path: upload/*
if-no-files-found: error
release:
needs: bundle
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: jar
path: release
- uses: actions/download-artifact@v4
with:
name: debian-all
path: release
- uses: actions/download-artifact@v4
with:
name: appimage
path: release
- uses: actions/download-artifact@v4
with:
name: linux-assets
path: release
- uses: actions/download-artifact@v4
with:
name: linux-x64
path: release
- uses: actions/download-artifact@v4
with:
name: macOS-x64
path: release
- uses: actions/download-artifact@v4
with:
name: macOS-arm64
path: release
- uses: actions/download-artifact@v4
with:
name: windows-x64
path: release
- name: Checkout Preview branch
uses: actions/checkout@v4
with:
repository: "Suwayomi/Suwayomi-Server-preview"
ref: main
path: preview
token: ${{ secrets.DEPLOY_PREVIEW_TOKEN }}
- name: Generate Tag Name
id: GenTagName
run: |
cd master/server/build
genTag=$(ls *.jar | sed -e's/Tachidesk-\|.jar//g')
cd release
genTag=$(ls *.jar | sed -e's/Suwayomi-Server-\|.jar//g')
echo "$genTag"
echo "::set-output name=value::$genTag"
- name: make windows packages
run: |
cd master/scripts
./windows-bundler.sh win32
./windows-bundler.sh win64
# - name: Mock make windows packages
# run: |
# cd master/server/build
# echo test > Tachidesk-v0.3.8-r580-win32.zip
- name: Checkout preview branch
uses: actions/checkout@v2
with:
repository: 'Suwayomi/Tachidesk-preview'
ref: main
path: preview
token: ${{ secrets.DEPLOY_PREVIEW_TOKEN }}
echo "value=$genTag" >> $GITHUB_OUTPUT
- name: Create Tag
run: |
@@ -111,7 +230,8 @@ jobs:
cd preview
echo "{ \"latest\": \"$TAG\" }" > index.json
git add index.json
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.email \
"github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
git commit -m "Updated to $TAG"
git push origin main
@@ -120,10 +240,10 @@ jobs:
git push origin $TAG
- name: Upload Preview Release
uses: ncipollo/release-action@v1
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.DEPLOY_PREVIEW_TOKEN }}
artifacts: "master/server/build/*.jar,master/server/build/*.zip"
owner: "Suwayomi"
repo: "Tachidesk-preview"
tag: ${{ steps.GenTagName.outputs.value }}
repository: "Suwayomi/Suwayomi-Server-preview"
tag_name: ${{ steps.GenTagName.outputs.value }}
files: release/*
+15
View File
@@ -0,0 +1,15 @@
name: Docker Build Stable
on:
release:
types: [published]
jobs:
build_publish_docker_container:
runs-on: ubuntu-latest
steps:
- name: run docker build and publish script
run: |
curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token ${{ secrets.DEPLOY_PREVIEW_TOKEN }}" -d '{"ref":"main", "inputs":{"tachidesk_release_type": "stable"}}' https://api.github.com/repos/suwayomi/docker-tachidesk/actions/workflows/build_container_images.yml/dispatches
-37
View File
@@ -1,37 +0,0 @@
name: Issue closer
on:
issues:
types: [opened, edited, reopened]
jobs:
autoclose:
runs-on: ubuntu-latest
steps:
- name: Autoclose issues
uses: arkon/issue-closer-action@v3.0
with:
repo-token: ${{ github.token }}
rules: |
[
{
"type": "title",
"regex": ".*<short description>*",
"message": "You did not fill out the description in the title"
},
{
"type": "body",
"regex": ".*DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT.*",
"message": "The acknowledgment section was not removed"
},
{
"type": "body",
"regex": "(Tachidesk version|Server Operating System|Server Desktop Environment|Server JVM version|Client Operating System|Client Web Browser):.*(\\(Example:|<usually).*",
"message": "The requested information was not filled out"
},
{
"type": "body",
"regex": ".*Remove this line after you are done.*",
"message": "The lines requesting to be removed were not removed."
}
]
+182 -48
View File
@@ -1,87 +1,221 @@
name: CI Publish
on:
workflow_dispatch:
push:
tags:
- 'v*'
- "v*"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check_wrapper:
name: Validate Gradle Wrapper
runs-on: ubuntu-latest
steps:
- name: Clone repo
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
uses: gradle/actions/wrapper-validation@v4
build:
name: Build artifacts and release
name: Build Jar
needs: check_wrapper
runs-on: ubuntu-latest
steps:
- name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.5.0
with:
access_token: ${{ github.token }}
- name: Checkout ${{ github.ref }}
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
path: master
fetch-depth: 0
- name: Set up JDK 1.8
uses: actions/setup-java@v1
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 1.8
java-version: 21
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Copy CI gradle.properties
run: |
cd master
mkdir -p ~/.gradle
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
- name: Download android.jar
run: |
cd master
curl https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
- name: Cache node_modules
uses: actions/cache@v2
with:
path: |
**/react/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/react/yarn.lock') }}
cp .github/runner-files/ci-gradle.properties \
~/.gradle/gradle.properties
- name: Build and copy webUI, Build Jar
uses: eskatos/gradle-command-action@v1
env:
TachideskBuildType: "Stable"
with:
build-root-directory: master
wrapper-directory: master
arguments: :webUI:copyBuild :server:shadowJar --stacktrace
wrapper-cache-enabled: true
dependencies-cache-enabled: true
configuration-cache-enabled: true
ProductBuildType: "Stable"
working-directory: master
run: ./gradlew :server:downloadWebUI :server:shadowJar --stacktrace
- name: make windows packages
- name: Upload Jar
uses: actions/upload-artifact@v4
with:
name: jar
path: master/server/build/*.jar
if-no-files-found: error
- name: Upload icons
uses: actions/upload-artifact@v4
with:
name: icon
path: master/server/src/main/resources/icon
if-no-files-found: error
- name: Tar scripts dir to maintain file permissions
run: tar -cvzf scripts.tar.gz -C master/ scripts/
- name: Upload scripts.tar.gz
uses: actions/upload-artifact@v4
with:
name: scripts
path: scripts.tar.gz
if-no-files-found: error
jlink:
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
name: linux-x64
- os: windows-latest
name: windows-x64
- os: macos-14
name: macOS-arm64
- os: macos-13
name: macOS-x64
os: [ubuntu-latest, windows-latest, macos-14, macos-13]
steps:
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 21
distribution: 'temurin'
- name: Package JDK
run: jlink --add-modules java.base,java.compiler,java.datatransfer,java.desktop,java.instrument,java.logging,java.management,java.naming,java.prefs,java.scripting,java.se,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,jdk.attach,jdk.crypto.ec,jdk.jdi,jdk.management,jdk.net,jdk.random,jdk.unsupported,jdk.unsupported.desktop,jdk.zipfs --output suwa --strip-debug --no-man-pages --no-header-files --compress=2
- name: Upload JDK package
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.name }}-jre
path: suwa
bundle:
strategy:
fail-fast: false
matrix:
include:
- name: debian-all
jre: linux-x64
- name: appimage
jre: linux-x64
- name: linux-assets
jre: linux-assets
- name: linux-x64
jre: linux-x64
- name: macOS-x64
jre: macOS-x64
- name: macOS-arm64
jre: macOS-arm64
- name: windows-x64
jre: windows-x64
name: [debian-all, appimage, linux-assets, linux-x64, macOS-x64, macOS-arm64, windows-x64]
name: Make ${{ matrix.name }} release
needs: [build, jlink]
runs-on: ubuntu-latest
steps:
- name: Download Jar
uses: actions/download-artifact@v4
with:
name: jar
path: server/build
- name: Download JRE
uses: actions/download-artifact@v4
if: matrix.name != 'linux-assets' && matrix.name != 'debian-all'
with:
name: ${{ matrix.jre }}-jre
path: jre
- name: Download icons
uses: actions/download-artifact@v4
with:
name: icon
path: server/src/main/resources/icon
- name: Download scripts.tar.gz
uses: actions/download-artifact@v4
with:
name: scripts
- name: Make ${{ matrix.name }} release
run: |
cd master/scripts
./windows-bundler.sh win32
./windows-bundler.sh win64
mkdir upload/
tar -xvpf scripts.tar.gz
scripts/bundler.sh -o upload/ ${{ matrix.name }}
- name: Upload Release
uses: xresloader/upload-to-github-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload ${{ matrix.name }} files
uses: actions/upload-artifact@v4
with:
file: "master/server/build/*.jar;master/server/build/*.zip"
tags: true
name: ${{ matrix.name }}
path: upload/*
if-no-files-found: error
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: bundle
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: jar
path: release
- uses: actions/download-artifact@v4
with:
name: debian-all
path: release
- uses: actions/download-artifact@v4
with:
name: appimage
path: release
- uses: actions/download-artifact@v4
with:
name: linux-assets
path: release
- uses: actions/download-artifact@v4
with:
name: linux-x64
path: release
- uses: actions/download-artifact@v4
with:
name: macOS-x64
path: release
- uses: actions/download-artifact@v4
with:
name: macOS-arm64
path: release
- uses: actions/download-artifact@v4
with:
name: windows-x64
path: release
- name: Generate checksums
run: cd release && sha256sum * > Checksums.sha256
- name: Release
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.DEPLOY_RELEASE_TOKEN }}
draft: true
verbose: true
files: release/*
+19
View File
@@ -0,0 +1,19 @@
name: Publish to WinGet
on:
workflow_dispatch:
inputs:
version:
description: Version
required: false
jobs:
publish:
runs-on: windows-latest # action can only be run on windows
steps:
- uses: vedantmgoyal2009/winget-releaser@v2
with:
identifier: Suwayomi.Suwayomi-Server
installers-regex: '.*x64.msi$'
token: ${{ secrets.WINGET_PUBLISH_PAT }}
version: ${{ inputs.version || github.ref_name }}
release-tag: ${{ inputs.version || github.ref_name }}
+15 -8
View File
@@ -1,16 +1,23 @@
# Ignore Gradle project-specific cache directory
# Ignore project-specific local files and dirs
.gradle
.idea
gradle.properties
.fleet
# But we need these
!.idea/runConfigurations
.kotlin
# Ignore Gradle build output directory
build
server/out
AndroidCompat/out
server/src/main/resources/react
server/tmp/
server/tachiserver-data/
# WebUI is either to be downloaded on-demand or is a dynamic build asset
server/src/main/resources/WebUI.zip
# bundle asset downlaods
OpenJDK*.zip
electron-*.zip
rcedit-*
# bundling stage downlaoded assets
scripts/OpenJDK*
scripts/zulu*
scripts/electron-*
scripts/rcedit-*
scripts/resources/*.so
+22 -3
View File
@@ -1,4 +1,23 @@
plugins {
id(
libs.plugins.kotlin.jvm
.get()
.pluginId,
)
id(
libs.plugins.kotlin.serialization
.get()
.pluginId,
)
id(
libs.plugins.ktlint
.get()
.pluginId,
)
}
dependencies {
// Config API, moved to the global build.gradle
// implementation("com.typesafe:config:1.4.0")
}
// Shared
implementation(libs.bundles.shared)
testImplementation(libs.bundles.sharedTest)
}
@@ -1,18 +1,20 @@
package xyz.nulldev.ts.config
import net.harawata.appdirs.AppDirsFactory
/*
* Copyright (C) Contributors to the Suwayomi project
*
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import ca.gosyer.appdirs.AppDirs
const val CONFIG_PREFIX = "suwayomi.tachidesk.config"
val ApplicationRootDir: String
get(): String {
return System.getProperty(
"suwayomi.server.rootDir",
AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)
"$CONFIG_PREFIX.server.rootDir",
AppDirs { appName = "Tachidesk" }.getUserDataDir(),
)
}
}
@@ -1,12 +0,0 @@
package xyz.nulldev.ts.config
import org.kodein.di.DI
import org.kodein.di.bind
import org.kodein.di.singleton
class ConfigKodeinModule {
fun create() = DI.Module("ConfigManager") {
//Config module
bind<ConfigManager>() with singleton { GlobalConfigManager }
}
}
@@ -10,22 +10,32 @@ package xyz.nulldev.ts.config
import ch.qos.logback.classic.Level
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigRenderOptions
import mu.KotlinLogging
import com.typesafe.config.ConfigObject
import com.typesafe.config.ConfigValue
import com.typesafe.config.parser.ConfigDocument
import com.typesafe.config.parser.ConfigDocumentFactory
import io.github.config4k.toConfig
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.io.File
/**
* Manages app config.
*/
open class ConfigManager {
val logger = KotlinLogging.logger {}
private val generatedModules = mutableMapOf<Class<out ConfigModule>, ConfigModule>()
val config by lazy { loadConfigs() }
private val userConfigFile = File(ApplicationRootDir, "server.conf")
private var internalConfig = loadConfigs()
val config: Config
get() = internalConfig
//Public read-only view of modules
// Public read-only view of modules
val loadedModules: Map<Class<out ConfigModule>, ConfigModule>
get() = generatedModules
val logger = KotlinLogging.logger {}
private val mutex = Mutex()
/**
* Get a config module
@@ -35,30 +45,35 @@ open class ConfigManager {
/**
* Get a config module (Java API)
*/
@Suppress("UNCHECKED_CAST")
fun <T : ConfigModule> module(type: Class<T>): T = loadedModules[type] as T
private fun getUserConfig(): Config =
userConfigFile.let {
ConfigFactory.parseFile(it)
}
/**
* Load configs
*/
fun loadConfigs(): Config {
//Load reference configs
// Load reference configs
val compatConfig = ConfigFactory.parseResources("compat-reference.conf")
val serverConfig = ConfigFactory.parseResources("server-reference.conf")
val baseConfig =
ConfigFactory.parseMap(
mapOf(
"ts.server.rootDir" to ApplicationRootDir
)
)
ConfigFactory.parseMap(
mapOf(
// override AndroidCompat's rootDir
"androidcompat.rootDir" to "$ApplicationRootDir/android-compat",
),
)
//Load user config
val userConfig =
File(ApplicationRootDir, "server.conf").let {
ConfigFactory.parseFile(it)
}
// Load user config
val userConfig = getUserConfig()
val config = ConfigFactory.empty()
val config =
ConfigFactory
.empty()
.withFallback(baseConfig)
.withFallback(userConfig)
.withFallback(compatConfig)
@@ -67,11 +82,7 @@ open class ConfigManager {
// set log level early
if (debugLogsEnabled(config)) {
setLogLevel(Level.DEBUG)
}
logger.debug {
"Loaded config:\n" + config.root().render(ConfigRenderOptions.concise().setFormatted(true))
setLogLevelFor(BASE_LOGGER_NAME, Level.DEBUG)
}
return config
@@ -86,6 +97,84 @@ open class ConfigManager {
registerModule(it)
}
}
private fun updateUserConfigFile(
path: String,
value: ConfigValue,
) {
val userConfigDoc = ConfigDocumentFactory.parseFile(userConfigFile)
val updatedConfigDoc = userConfigDoc.withValue(path, value)
val newFileContent = updatedConfigDoc.render()
userConfigFile.writeText(newFileContent)
}
suspend fun updateValue(
path: String,
value: Any,
) {
mutex.withLock {
val actualValue = if (value is Enum<*>) value.name else value
val configValue = actualValue.toConfig("internal").getValue("internal")
updateUserConfigFile(path, configValue)
internalConfig = internalConfig.withValue(path, configValue)
}
}
fun resetUserConfig(updateInternalConfig: Boolean = true): ConfigDocument {
val serverConfigFileContent = this::class.java.getResource("/server-reference.conf")?.readText()
val serverConfigDoc = ConfigDocumentFactory.parseString(serverConfigFileContent)
userConfigFile.writeText(serverConfigDoc.render())
if (updateInternalConfig) {
getUserConfig().entrySet().forEach { internalConfig = internalConfig.withValue(it.key, it.value) }
}
return serverConfigDoc
}
/**
* Makes sure the "UserConfig" is up-to-date.
*
* - adds missing settings
* - removes outdated settings
*/
fun updateUserConfig(migrate: ConfigDocument.(Config) -> ConfigDocument) {
val serverConfig = ConfigFactory.parseResources("server-reference.conf")
val userConfig = getUserConfig()
// NOTE: if more than 1 dot is included, that's a nested setting, which we need to filter out here
val refKeys =
serverConfig.root().entries.flatMap {
(it.value as? ConfigObject)?.entries?.map { e -> "${it.key}.${e.key}" }.orEmpty()
}
val hasMissingSettings = refKeys.any { !userConfig.hasPath(it) }
val hasOutdatedSettings = userConfig.entrySet().any { !refKeys.contains(it.key) && it.key.count { c -> c == '.' } <= 1 }
val isUserConfigOutdated = hasMissingSettings || hasOutdatedSettings
if (!isUserConfigOutdated) {
return
}
logger.debug {
"user config is out of date, updating... (missingSettings= $hasMissingSettings, outdatedSettings= $hasOutdatedSettings"
}
var newUserConfigDoc: ConfigDocument = resetUserConfig(false)
userConfig
.entrySet()
.filter {
serverConfig.hasPath(
it.key,
) ||
it.key.count { c -> c == '.' } > 1
}.forEach { newUserConfigDoc = newUserConfigDoc.withValue(it.key, it.value) }
newUserConfigDoc =
migrate(newUserConfigDoc, internalConfig)
userConfigFile.writeText(newUserConfigDoc.render())
getUserConfig().entrySet().forEach { internalConfig = internalConfig.withValue(it.key, it.value) }
}
}
object GlobalConfigManager : ConfigManager()
@@ -0,0 +1,9 @@
package xyz.nulldev.ts.config
import org.koin.core.module.Module
import org.koin.dsl.module
fun configManagerModule(): Module =
module {
single<ConfigManager> { GlobalConfigManager }
}
@@ -1,8 +1,76 @@
package xyz.nulldev.ts.config
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import com.typesafe.config.Config
import com.typesafe.config.ConfigException
import com.typesafe.config.ConfigFactory
import io.github.config4k.ClassContainer
import io.github.config4k.TypeReference
import io.github.config4k.getValue
import io.github.config4k.readers.SelectReader
import kotlin.reflect.KProperty
/**
* Abstract config module.
*/
abstract class ConfigModule(config: Config)
@Suppress("UNUSED_PARAMETER")
abstract class ConfigModule(
getConfig: () -> Config,
)
/**
* Abstract jvm-commandline-argument-overridable config module.
*/
abstract class SystemPropertyOverridableConfigModule(
getConfig: () -> Config,
val moduleName: String,
) : ConfigModule(getConfig) {
val overridableConfig = SystemPropertyOverrideDelegate(getConfig, moduleName)
}
/** Defines a config property that is overridable with jvm `-D` commandline arguments prefixed with [CONFIG_PREFIX] */
class SystemPropertyOverrideDelegate(
val getConfig: () -> Config,
val moduleName: String,
) {
inline operator fun <R, reified T> getValue(
thisRef: R,
property: KProperty<*>,
): T {
val config = getConfig()
val systemProperty =
System.getProperty("$CONFIG_PREFIX.$moduleName.${property.name}")
if (systemProperty == null) {
val configValue: T = config.getValue(thisRef, property)
return configValue
}
val systemPropertyConfig =
try {
ConfigFactory.parseString("internal=$systemProperty")
} catch (_: ConfigException) {
ConfigFactory.parseMap(mapOf("internal" to systemProperty))
}
val genericType = object : TypeReference<T>() {}.genericType()
val clazz = ClassContainer(T::class, genericType)
val reader = SelectReader.getReader(clazz)
val path = property.name
val result = reader(systemPropertyConfig, "internal")
return try {
result as T
} catch (e: Exception) {
throw result
?.let { e }
?: ConfigException.BadPath(path, "take a look at your config")
}
}
}
@@ -8,13 +8,132 @@ package xyz.nulldev.ts.config
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.LoggerContext
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy
import ch.qos.logback.core.util.FileSize
import com.typesafe.config.Config
import mu.KotlinLogging
import io.github.oshai.kotlinlogging.DelegatingKLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import org.slf4j.Logger
import org.slf4j.LoggerFactory
fun setLogLevel(level: Level) {
(KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).underlyingLogger as ch.qos.logback.classic.Logger).level = level
private fun fileSizeValueOfOrDefault(
fileSizeStr: String,
default: String,
): FileSize =
try {
FileSize.valueOf(fileSizeStr)
} catch (e: IllegalArgumentException) {
FileSize.valueOf(default)
}
private const val FILE_APPENDER_NAME = "SuwayomiDefaultAppender"
private fun createRollingFileAppender(
logContext: LoggerContext,
logDirPath: String,
maxFiles: Int,
maxFileSize: String,
maxTotalSize: String,
): RollingFileAppender<ILoggingEvent> {
val logFilename = "application"
val logEncoder =
PatternLayoutEncoder().apply {
pattern = "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n"
context = logContext
start()
}
val appender =
RollingFileAppender<ILoggingEvent>().apply {
name = FILE_APPENDER_NAME
context = logContext
encoder = logEncoder
file = "$logDirPath/$logFilename.log"
}
val rollingPolicy =
SizeAndTimeBasedRollingPolicy<ILoggingEvent>().apply {
context = logContext
setParent(appender)
fileNamePattern = "$logDirPath/${logFilename}_%d{yyyy-MM-dd}_%i.log.gz"
maxHistory = maxFiles.coerceAtLeast(0)
setMaxFileSize(fileSizeValueOfOrDefault(maxFileSize, "10mb"))
setTotalSizeCap(fileSizeValueOfOrDefault(maxTotalSize, "100mb"))
start()
}
appender.rollingPolicy = rollingPolicy
appender.start()
return appender
}
fun debugLogsEnabled(config: Config)
= System.getProperty("suwayomi.server.debugLogsEnabled", config.getString("server.debugLogsEnabled")).toBoolean()
private fun getBaseLogger(): ch.qos.logback.classic.Logger =
((KotlinLogging.logger(Logger.ROOT_LOGGER_NAME) as DelegatingKLogger<*>).underlyingLogger as ch.qos.logback.classic.Logger)
private fun getLogger(name: String): ch.qos.logback.classic.Logger {
val context = LoggerFactory.getILoggerFactory() as LoggerContext
return context.getLogger(name)
}
fun initLoggerConfig(
appRootPath: String,
maxFiles: Int,
maxFileSize: String,
maxTotalSize: String,
) {
val context = LoggerFactory.getILoggerFactory() as LoggerContext
val logger = getBaseLogger()
// logback logs to the console by default (at least when adding a console appender logs in the console are duplicated)
logger.addAppender(createRollingFileAppender(context, "$appRootPath/logs", maxFiles, maxFileSize, maxTotalSize))
// set "kotlin exposed" log level
setLogLevelFor("Exposed", Level.ERROR)
}
fun updateFileAppender(
maxFiles: Int,
maxFileSize: String,
maxTotalSize: String,
) {
val logger = getBaseLogger()
val appender = logger.getAppender(FILE_APPENDER_NAME) as RollingFileAppender<*>? ?: return
val rollingPolicy = appender.rollingPolicy as SizeAndTimeBasedRollingPolicy<*>
rollingPolicy.apply {
maxHistory = maxFiles
setMaxFileSize(FileSize.valueOf(maxFileSize))
setTotalSizeCap(FileSize.valueOf(maxTotalSize))
rollingPolicy.stop()
appender.stop()
rollingPolicy.start()
appender.start()
}
}
const val BASE_LOGGER_NAME = "_BaseLogger"
fun setLogLevelFor(
name: String,
level: Level,
) {
val logger =
if (name == BASE_LOGGER_NAME) {
getBaseLogger()
} else {
getLogger(name)
}
logger.level = level
}
fun debugLogsEnabled(config: Config) =
System.getProperty("suwayomi.tachidesk.config.server.debugLogsEnabled", config.getString("server.debugLogsEnabled")).toBoolean()
@@ -2,5 +2,6 @@ package xyz.nulldev.ts.config.util
import com.typesafe.config.Config
operator fun Config.get(key: String) = getString(key)
operator fun Config.get(key: String) =
getString(key)
?: throw IllegalStateException("Could not find value for config entry: $key!")
@@ -1 +0,0 @@
xyz.nulldev.ts.api.v2.java.impl.ServerAPIImpl
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

File diff suppressed because one or more lines are too long
@@ -1,22 +0,0 @@
[
{
"label": "Sync",
"icon": "import_export",
"type": "nested",
"prefs": []
},
{
"label": "Server",
"icon": "dns",
"type": "nested",
"prefs": [
{
"label": "Password authentication",
"type": "text-password",
"default": "",
"key": "pref_ts_server_password",
"hint": "Enter a password"
}
]
}
]
+36 -47
View File
@@ -1,62 +1,51 @@
plugins {
application
}
repositories {
mavenCentral()
maven {
url = uri("https://jitpack.io")
}
maven {
url = uri("https://maven.google.com")
}
id(
libs.plugins.kotlin.jvm
.get()
.pluginId,
)
id(
libs.plugins.kotlin.serialization
.get()
.pluginId,
)
id(
libs.plugins.ktlint
.get()
.pluginId,
)
}
dependencies {
// Shared
implementation(libs.bundles.shared)
testImplementation(libs.bundles.sharedTest)
// Android stub library
implementation(fileTree("lib/"))
// Android JAR libs
// compileOnly( fileTree(dir: new File(rootProject.rootDir, "libs/other"), include: "*.jar")
// JSON
compileOnly( "com.google.code.gson:gson:2.8.6")
// Javassist
compileOnly( "org.javassist:javassist:3.27.0-GA")
implementation(libs.android.stubs)
// XML
compileOnly( group= "xmlpull", name= "xmlpull", version= "1.1.3.1")
compileOnly(libs.xmlpull)
// Config API
implementation(project(":AndroidCompat:Config"))
implementation(projects.androidCompat.config)
// APK sig verifier
compileOnly("com.android.tools.build:apksig:4.2.0-alpha13")
compileOnly(libs.apksig)
// AndroidX annotations
compileOnly( "androidx.annotation:annotation:1.2.0-alpha01")
compileOnly(libs.android.annotations)
// substitute for duktape-android
// 'org.mozilla:rhino' includes some code that we don't need so use 'org.mozilla:rhino-runtime' instead
implementation("org.mozilla:rhino-runtime:1.7.13")
// 'org.mozilla:rhino-engine' provides the same interface as 'javax.script' a.k.a Nashorn
implementation("org.mozilla:rhino-engine:1.7.13")
// substitute for duktape-android/quickjs
implementation(libs.bundles.polyglot)
// Kotlin wrapper around Java Preferences, makes certain things easier
implementation(libs.bundles.settings)
// Android version of SimpleDateFormat
implementation(libs.icu4j)
// OpenJDK lacks native JPEG encoder and native WEBP decoder
implementation(libs.bundles.twelvemonkeys)
implementation(libs.imageio.webp)
}
//def fatJarTask = tasks.getByPath(':AndroidCompat:JVMPatch:fatJar')
//
//// Copy JVM core patches
//task copyJVMPatches(type: Copy) {
// from fatJarTask.outputs.files
// into 'src/main/resources/patches'
//}
//
//compileOnly(Java.dependsOn gradle.includedBuild('dex2jar').task(':dex-translator:assemble')
//compileOnly(Java.dependsOn copyJVMPatches
//copyJVMPatches.dependsOn fatJarTask
//
+15 -7
View File
@@ -15,7 +15,7 @@ Write-Output "Getting required Android.jar..."
Remove-Item -Recurse -Force "tmp" -ErrorAction SilentlyContinue | Out-Null
New-Item -ItemType Directory -Force -Path "tmp" | Out-Null
$androidEncoded = (Invoke-WebRequest -Uri "https://android.googlesource.com/platform/prebuilts/sdk/+/3b8a524d25fa6c3d795afb1eece3f24870c60988/27/public/android.jar?format=TEXT" -UseBasicParsing).content
$androidEncoded = (Invoke-WebRequest -Uri "https://android.googlesource.com/platform/prebuilts/sdk/+/6cd31be5e4e25901aadf838120d71a79b46d9add/30/public/android.jar?format=TEXT" -UseBasicParsing).content
$android_jar = (Get-Location).Path + "\tmp\android.jar"
@@ -24,7 +24,7 @@ $android_jar = (Get-Location).Path + "\tmp\android.jar"
# We need to remove any stub classes that we have implementations for
Write-Output "Patching JAR..."
function Remove-Files-Zip($zipfile, $path)
function Remove-Files-Zip($zipfile, $paths)
{
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression') | Out-Null
@@ -32,7 +32,18 @@ function Remove-Files-Zip($zipfile, $path)
$mode = [IO.Compression.ZipArchiveMode]::Update
$zip = New-Object IO.Compression.ZipArchive($stream, $mode)
($zip.Entries | Where-Object { $_.FullName -like $path }) | ForEach-Object { Write-Output "Deleting: $($_.FullName)"; $_.Delete() }
if ($paths.getType().Name -eq "Object[]")
{
$paths | ForEach-Object {
$path = $_
($zip.Entries | Where-Object { $_.FullName -like $path }) | ForEach-Object { Write-Output "Deleting: $($_.FullName)"; $_.Delete() }
}
}
else
{
($zip.Entries | Where-Object { $_.FullName -like $paths }) | ForEach-Object { Write-Output "Deleting: $($_.FullName)"; $_.Delete() }
}
$zip.Dispose()
$stream.Close()
@@ -78,10 +89,7 @@ function Dedupe($path)
$classes = Get-ChildItem . *.* -Recurse | Where-Object { !$_.PSIsContainer }
$classes | ForEach-Object {
"Processing class: $($_.FullName)"
Remove-Files-Zip $android_jar "$($_.Name).class" | Out-Null
Remove-Files-Zip $android_jar "$($_.Name)$*.class" | Out-Null
Remove-Files-Zip $android_jar "$($_.Name)Kt.class" | Out-Null
Remove-Files-Zip $android_jar "$($_.Name)Kt$*.class" | Out-Null
Remove-Files-Zip $android_jar ("$($_.Name).class","$($_.Name)$*.class","$($_.Name)Kt.class","$($_.Name)Kt$*.class") | Out-Null
}
Pop-Location
}
+2 -2
View File
@@ -13,7 +13,7 @@ do
which $dep >/dev/null 2>&1 || { echo >&2 "Error: This script needs $dep installed."; abort=yes; }
done
if [ $abort = yes ]; then
if [ "$abort" = yes ]; then
echo "Some of the dependencies didn't exist. Aborting."
exit 1
fi
@@ -30,7 +30,7 @@ rm -rf "tmp"
mkdir -p "tmp"
pushd "tmp"
curl "https://android.googlesource.com/platform/prebuilts/sdk/+/3b8a524d25fa6c3d795afb1eece3f24870c60988/27/public/android.jar?format=TEXT" | base64 --decode > android.jar
curl "https://android.googlesource.com/platform/prebuilts/sdk/+/6cd31be5e4e25901aadf838120d71a79b46d9add/30/public/android.jar?format=TEXT" | base64 --decode > android.jar
# We need to remove any stub classes that we have implementations for
echo "Patching JAR..."
-1
View File
@@ -1 +0,0 @@
android.jar
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
/**
* <p>Denotes that the annotated element represents a packed color
* long. If applied to a long array, every element in the array
* represents a color long. For more information on how colors
* are packed in a long, please refer to the documentation of
* the {@link android.graphics.Color} class.</p>
*
* <p>Example:</p>
*
* <pre>{@code
* public void setFillColor(@ColorLong long color);
* }</pre>
*
* @see android.graphics.Color
*
* @hide
*/
@Retention(SOURCE)
@Target({PARAMETER,METHOD,LOCAL_VARIABLE,FIELD})
public @interface ColorLong {
}
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
/**
* <p>Denotes that the annotated element represents a half-precision floating point
* value. Such values are stored in short data types and can be manipulated with
* the {@link android.util.Half} class. If applied to an array of short, every
* element in the array represents a half-precision float.</p>
*
* <p>Example:</p>
*
* <pre>{@code
* public abstract void setPosition(@HalfFloat short x, @HalfFloat short y, @HalfFloat short z);
* }</pre>
*
* @see android.util.Half
* @see android.util.Half#toHalf(float)
* @see android.util.Half#toFloat(short)
*
* @hide
*/
@Retention(SOURCE)
@Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD})
public @interface HalfFloat {
}
@@ -25,7 +25,7 @@ import android.os.IBinder;
import android.util.Log;
import kotlin.NotImplementedError;
import xyz.nulldev.androidcompat.service.ServiceSupport;
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -299,7 +299,7 @@ import java.lang.annotation.RetentionPolicy;
*/
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
private static final ServiceSupport serviceSupport = KodeinGlobalHelper.instance(ServiceSupport.class);
private static final ServiceSupport serviceSupport = KoinGlobalHelper.instance(ServiceSupport.class);
private static final String TAG = "Service";
/**
@@ -328,7 +328,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
public Service() {
//==================[THIS LINE MODIFIED FROM ANDROID SOURCE!]==================
//Service must be initialized with a base context!
super(KodeinGlobalHelper.instance(Context.class));
super(KoinGlobalHelper.instance(Context.class));
}
/** Return the application that owns this service. */
public final Application getApplication() {
@@ -0,0 +1,267 @@
package android.graphics;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
public final class Bitmap {
private final int width;
private final int height;
private final BufferedImage image;
public Bitmap(BufferedImage image) {
this.image = image;
this.width = image.getWidth();
this.height = image.getHeight();
}
public BufferedImage getImage() {
return image;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public enum CompressFormat {
JPEG (0),
PNG (1),
WEBP (2),
WEBP_LOSSY (3),
WEBP_LOSSLESS (4);
CompressFormat(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
public enum Config {
ALPHA_8(1),
RGB_565(3),
ARGB_4444(4),
ARGB_8888(5),
RGBA_F16(6),
HARDWARE(7),
RGBA_1010102(8);
final int nativeInt;
private static final Config[] sConfigs = {
null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE, RGBA_1010102
};
Config(int ni) {
this.nativeInt = ni;
}
static Config nativeToConfig(int ni) {
return sConfigs[ni];
}
}
private static int configToBufferedImageType(Config config) {
switch (config) {
case ALPHA_8:
return BufferedImage.TYPE_BYTE_GRAY;
case RGB_565:
return BufferedImage.TYPE_USHORT_565_RGB;
case ARGB_8888:
return BufferedImage.TYPE_INT_ARGB;
default:
throw new UnsupportedOperationException("Bitmap.Config(" + config + ") not supported");
}
}
/**
* Common code for checking that x and y are >= 0
*
* @param x x coordinate to ensure is >= 0
* @param y y coordinate to ensure is >= 0
*/
private static void checkXYSign(int x, int y) {
if (x < 0) {
throw new IllegalArgumentException("x must be >= 0");
}
if (y < 0) {
throw new IllegalArgumentException("y must be >= 0");
}
}
/**
* Common code for checking that width and height are > 0
*
* @param width width to ensure is > 0
* @param height height to ensure is > 0
*/
private static void checkWidthHeight(int width, int height) {
if (width <= 0) {
throw new IllegalArgumentException("width must be > 0");
}
if (height <= 0) {
throw new IllegalArgumentException("height must be > 0");
}
}
public static Bitmap createBitmap(int width, int height, Config config) {
BufferedImage image = new BufferedImage(width, height, configToBufferedImageType(config));
return new Bitmap(image);
}
public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height) {
checkXYSign(x, y);
checkWidthHeight(width, height);
if (x + width > source.getWidth()) {
throw new IllegalArgumentException("x + width must be <= bitmap.width()");
}
if (y + height > source.getHeight()) {
throw new IllegalArgumentException("y + height must be <= bitmap.height()");
}
// Android will make a copy when creating a sub image,
// so we do the same here
BufferedImage subImage = source.image.getSubimage(x, y, width, height);
BufferedImage newImage = new BufferedImage(subImage.getWidth(), subImage.getHeight(), subImage.getType());
newImage.setData(subImage.getData());
return new Bitmap(newImage);
}
public boolean compress(CompressFormat format, int quality, OutputStream stream) {
if (stream == null) {
throw new NullPointerException();
}
if (quality < 0 || quality > 100) {
throw new IllegalArgumentException("quality must be 0..100");
}
float qualityFloat = ((float) quality) / 100;
String formatString;
if (format == Bitmap.CompressFormat.PNG) {
formatString = "png";
} else if (format == Bitmap.CompressFormat.JPEG) {
formatString = "jpg";
} else if (format == Bitmap.CompressFormat.WEBP || format == Bitmap.CompressFormat.WEBP_LOSSY) {
formatString = "webp";
} else {
throw new IllegalArgumentException("unsupported compression format! " + format);
}
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(formatString);
if (!writers.hasNext()) {
throw new IllegalStateException("no image writers found for this format!");
}
ImageWriter writer = writers.next();
ImageOutputStream ios;
try {
ios = ImageIO.createImageOutputStream(stream);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
writer.setOutput(ios);
BufferedImage img = image;
ImageWriteParam param = writer.getDefaultWriteParam();
if ("jpg".equals(formatString)) {
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(qualityFloat);
img = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
img.getGraphics().drawImage(image, 0, 0, null);
}
try {
writer.write(null, new IIOImage(img, null, null), param);
ios.close();
writer.dispose();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return true;
}
public Bitmap copy(Config config, boolean isMutable) {
Bitmap ret = createBitmap(width, height, config);
ret.image.getGraphics().drawImage(image, 0, 0, null);
return ret;
}
/**
* Shared code to check for illegal arguments passed to getPixels()
* or setPixels()
*
* @param x left edge of the area of pixels to access
* @param y top edge of the area of pixels to access
* @param width width of the area of pixels to access
* @param height height of the area of pixels to access
* @param offset offset into pixels[] array
* @param stride number of elements in pixels[] between each logical row
* @param pixels array to hold the area of pixels being accessed
*/
private void checkPixelsAccess(int x, int y, int width, int height,
int offset, int stride, int[] pixels) {
checkXYSign(x, y);
if (width < 0) {
throw new IllegalArgumentException("width must be >= 0");
}
if (height < 0) {
throw new IllegalArgumentException("height must be >= 0");
}
if (x + width > getWidth()) {
throw new IllegalArgumentException(
"x + width must be <= bitmap.width()");
}
if (y + height > getHeight()) {
throw new IllegalArgumentException(
"y + height must be <= bitmap.height()");
}
if (Math.abs(stride) < width) {
throw new IllegalArgumentException("abs(stride) must be >= width");
}
int lastScanline = offset + (height - 1) * stride;
int length = pixels.length;
if (offset < 0 || (offset + width > length)
|| lastScanline < 0
|| (lastScanline + width > length)) {
throw new ArrayIndexOutOfBoundsException();
}
}
public void getPixels(@ColorInt int[] pixels, int offset, int stride,
int x, int y, int width, int height) {
checkPixelsAccess(x, y, width, height, offset, stride, pixels);
image.getRGB(x, y, width, height, pixels, offset, stride);
}
public void eraseColor(int c) {
java.awt.Color color = Color.valueOf(c).toJavaColor();
Graphics2D graphics = image.createGraphics();
graphics.setColor(color);
graphics.fillRect(0, 0, width, height);
graphics.dispose();
}
public void recycle() {
// do nothing
}
}
@@ -0,0 +1,51 @@
package android.graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
public class BitmapFactory {
public static Bitmap decodeStream(InputStream inputStream) {
Bitmap bitmap = null;
try {
ImageInputStream imageInputStream = ImageIO.createImageInputStream(inputStream);
Iterator<ImageReader> imageReaders = ImageIO.getImageReaders(imageInputStream);
if (!imageReaders.hasNext()) {
throw new IllegalArgumentException("no reader for image");
}
ImageReader imageReader = imageReaders.next();
imageReader.setInput(imageInputStream);
BufferedImage image = imageReader.read(0, imageReader.getDefaultReadParam());
bitmap = new Bitmap(image);
imageReader.dispose();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return bitmap;
}
public static Bitmap decodeByteArray(byte[] data, int offset, int length) {
Bitmap bitmap = null;
ByteArrayInputStream byteArrayStream = new ByteArrayInputStream(data);
try {
BufferedImage image = ImageIO.read(byteArrayStream);
bitmap = new Bitmap(image);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return bitmap;
}
}
@@ -0,0 +1,184 @@
package android.graphics;
import android.annotation.NonNull;
import android.util.Log;
import java.awt.BasicStroke;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
public final class Canvas {
private BufferedImage canvasImage;
private Graphics2D canvas;
private List<AffineTransform> transformStack = new ArrayList<AffineTransform>();
private static final String TAG = "Canvas";
public Canvas(Bitmap bitmap) {
canvasImage = bitmap.getImage();
canvas = canvasImage.createGraphics();
}
public void drawBitmap(Bitmap sourceBitmap, Rect src, Rect dst, Paint paint) {
BufferedImage sourceImage = sourceBitmap.getImage();
BufferedImage sourceImageCropped = sourceImage.getSubimage(src.left, src.top, src.getWidth(), src.getHeight());
canvas.drawImage(sourceImageCropped, dst.left, dst.top, dst.getWidth(), dst.getHeight(), null);
}
public void drawBitmap(Bitmap sourceBitmap, float left, float top, Paint paint) {
BufferedImage sourceImage = sourceBitmap.getImage();
canvas.drawImage(sourceImage, null, (int) left, (int) top);
}
public void drawText(@NonNull char[] text, int index, int count, float x, float y,
@NonNull Paint paint) {
drawText(new String(text, index, count), x, y, paint);
}
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
applyPaint(paint);
GlyphVector glyphVector = paint.getFont().createGlyphVector(canvas.getFontRenderContext(), text);
Shape textShape = glyphVector.getOutline();
switch (paint.getStyle()) {
case Paint.Style.FILL:
canvas.drawString(text, x, y);
break;
case Paint.Style.STROKE:
save();
translate(x, y);
canvas.draw(textShape);
restore();
break;
case Paint.Style.FILL_AND_STROKE:
save();
translate(x, y);
canvas.draw(textShape);
canvas.fill(textShape);
restore();
break;
}
}
public void drawText(@NonNull String text, int start, int end, float x, float y,
@NonNull Paint paint) {
drawText(text.substring(start, end), x, y, paint);
}
public void drawText(@NonNull CharSequence text, int start, int end, float x, float y,
@NonNull Paint paint) {
String str = text.subSequence(start, end).toString();
drawText(str, x, y, paint);
}
public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
throw new RuntimeException("Stub!");
}
public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
@NonNull Paint paint) {
throw new RuntimeException("Stub!");
}
public void drawPath(@NonNull Path path, @NonNull Paint paint) {
throw new RuntimeException("Stub!");
}
public void translate(float dx, float dy) {
if (dx == 0.0f && dy == 0.0f) return;
// TODO: check this, should translations stack?
canvas.translate(dx, dy);
}
public void scale(float sx, float sy) {
if (sx == 1.0f && sy == 1.0f) return;
canvas.scale(sx, sy);
}
public final void scale(float sx, float sy, float px, float py) {
if (sx == 1.0f && sy == 1.0f) return;
translate(px, py);
scale(sx, sy);
translate(-px, -py);
}
public void rotate(float degrees) {
if (degrees == 0.0f) return;
canvas.rotate(degrees);
}
public final void rotate(float degrees, float px, float py) {
if (degrees == 0.0f) return;
canvas.rotate(degrees, px, py);
}
public int getSaveCount() {
return transformStack.size();
}
public int save() {
transformStack.add(canvas.getTransform());
return getSaveCount();
}
public void restoreToCount(int saveCount) {
if (saveCount < 1) {
throw new IllegalArgumentException(
"Underflow in restoreToCount - more restores than saves");
}
if (saveCount > getSaveCount()) {
throw new IllegalArgumentException("Overflow in restoreToCount");
}
AffineTransform ts = transformStack.get(saveCount - 1);
canvas.setTransform(ts);
while (transformStack.size() >= saveCount) {
transformStack.remove(transformStack.size() - 1);
}
}
public void restore() {
restoreToCount(getSaveCount());
}
public boolean getClipBounds(@NonNull Rect bounds) {
Rectangle r = canvas.getClipBounds();
if (r == null) {
bounds.left = 0;
bounds.top = 0;
bounds.right = canvasImage.getWidth();
bounds.bottom = canvasImage.getHeight();
return true;
}
bounds.left = r.x;
bounds.top = r.y;
bounds.right = r.x + r.width;
bounds.bottom = r.y + r.height;
return r.width != 0 && r.height != 0;
}
private void applyPaint(Paint paint) {
canvas.setFont(paint.getFont());
java.awt.Color color = Color.valueOf(paint.getColorLong()).toJavaColor();
canvas.setColor(color);
canvas.setStroke(new BasicStroke(paint.getStrokeWidth(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
if (paint.isAntiAlias()) {
canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
} else {
canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
}
if (paint.isDither()) {
canvas.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
} else {
canvas.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
}
// TODO: use more from paint?
}
}
@@ -0,0 +1,578 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.graphics;
import android.annotation.ColorInt;
import android.annotation.ColorLong;
import android.annotation.HalfFloat;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
import android.util.Half;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.function.DoubleUnaryOperator;
public class Color {
@ColorInt public static final int BLACK = 0xFF000000;
@ColorInt public static final int DKGRAY = 0xFF444444;
@ColorInt public static final int GRAY = 0xFF888888;
@ColorInt public static final int LTGRAY = 0xFFCCCCCC;
@ColorInt public static final int WHITE = 0xFFFFFFFF;
@ColorInt public static final int RED = 0xFFFF0000;
@ColorInt public static final int GREEN = 0xFF00FF00;
@ColorInt public static final int BLUE = 0xFF0000FF;
@ColorInt public static final int YELLOW = 0xFFFFFF00;
@ColorInt public static final int CYAN = 0xFF00FFFF;
@ColorInt public static final int MAGENTA = 0xFFFF00FF;
@ColorInt public static final int TRANSPARENT = 0;
@NonNull
@Size(min = 4, max = 5)
private final float[] mComponents;
@NonNull
private final ColorSpace mColorSpace;
public Color() {
// This constructor is required for compatibility with previous APIs
mComponents = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };
mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
}
private Color(float r, float g, float b, float a) {
this(r, g, b, a, ColorSpace.get(ColorSpace.Named.SRGB));
}
private Color(float r, float g, float b, float a, @NonNull ColorSpace colorSpace) {
mComponents = new float[] { r, g, b, a };
mColorSpace = colorSpace;
}
private Color(@Size(min = 4, max = 5) float[] components, @NonNull ColorSpace colorSpace) {
mComponents = components;
mColorSpace = colorSpace;
}
public java.awt.Color toJavaColor() {
return new java.awt.Color(red(), green(), blue(), alpha());
}
@NonNull
public ColorSpace getColorSpace() {
return mColorSpace;
}
public ColorSpace.Model getModel() {
return mColorSpace.getModel();
}
public boolean isWideGamut() {
return getColorSpace().isWideGamut();
}
public boolean isSrgb() {
return getColorSpace().isSrgb();
}
@IntRange(from = 4, to = 5)
public int getComponentCount() {
return mColorSpace.getComponentCount() + 1;
}
@ColorLong
public long pack() {
return pack(mComponents[0], mComponents[1], mComponents[2], mComponents[3], mColorSpace);
}
@NonNull
public Color convert(@NonNull ColorSpace colorSpace) {
ColorSpace.Connector connector = ColorSpace.connect(mColorSpace, colorSpace);
float[] color = new float[] {
mComponents[0], mComponents[1], mComponents[2], mComponents[3]
};
connector.transform(color);
return new Color(color, colorSpace);
}
@ColorInt
public int toArgb() {
if (mColorSpace.isSrgb()) {
return ((int) (mComponents[3] * 255.0f + 0.5f) << 24) |
((int) (mComponents[0] * 255.0f + 0.5f) << 16) |
((int) (mComponents[1] * 255.0f + 0.5f) << 8) |
(int) (mComponents[2] * 255.0f + 0.5f);
}
float[] color = new float[] {
mComponents[0], mComponents[1], mComponents[2], mComponents[3]
};
// The transformation saturates the output
ColorSpace.connect(mColorSpace).transform(color);
return ((int) (color[3] * 255.0f + 0.5f) << 24) |
((int) (color[0] * 255.0f + 0.5f) << 16) |
((int) (color[1] * 255.0f + 0.5f) << 8) |
(int) (color[2] * 255.0f + 0.5f);
}
public float red() {
return mComponents[0];
}
public float green() {
return mComponents[1];
}
public float blue() {
return mComponents[2];
}
public float alpha() {
return mComponents[mComponents.length - 1];
}
@NonNull
@Size(min = 4, max = 5)
public float[] getComponents() {
return Arrays.copyOf(mComponents, mComponents.length);
}
@NonNull
@Size(min = 4)
public float[] getComponents(@Nullable @Size(min = 4) float[] components) {
if (components == null) {
return Arrays.copyOf(mComponents, mComponents.length);
}
if (components.length < mComponents.length) {
throw new IllegalArgumentException("The specified array's length must be at "
+ "least " + mComponents.length);
}
System.arraycopy(mComponents, 0, components, 0, mComponents.length);
return components;
}
public float getComponent(@IntRange(from = 0, to = 4) int component) {
return mComponents[component];
}
public float luminance() {
if (mColorSpace.getModel() != ColorSpace.Model.RGB) {
throw new IllegalArgumentException("The specified color must be encoded in an RGB " +
"color space. The supplied color space is " + mColorSpace.getModel());
}
DoubleUnaryOperator eotf = ((ColorSpace.Rgb) mColorSpace).getEotf();
double r = eotf.applyAsDouble(mComponents[0]);
double g = eotf.applyAsDouble(mComponents[1]);
double b = eotf.applyAsDouble(mComponents[2]);
return saturate((float) ((0.2126 * r) + (0.7152 * g) + (0.0722 * b)));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Color color = (Color) o;
//noinspection SimplifiableIfStatement
if (!Arrays.equals(mComponents, color.mComponents)) return false;
return mColorSpace.equals(color.mColorSpace);
}
@Override
public int hashCode() {
int result = Arrays.hashCode(mComponents);
result = 31 * result + mColorSpace.hashCode();
return result;
}
@Override
@NonNull
public String toString() {
StringBuilder b = new StringBuilder("Color(");
for (float c : mComponents) {
b.append(c).append(", ");
}
b.append(mColorSpace.getName());
b.append(')');
return b.toString();
}
@NonNull
public static ColorSpace colorSpace(@ColorLong long color) {
return ColorSpace.get((int) (color & 0x3fL));
}
public static float red(@ColorLong long color) {
if ((color & 0x3fL) == 0L) return ((color >> 48) & 0xff) / 255.0f;
return Half.toFloat((short) ((color >> 48) & 0xffff));
}
public static float green(@ColorLong long color) {
if ((color & 0x3fL) == 0L) return ((color >> 40) & 0xff) / 255.0f;
return Half.toFloat((short) ((color >> 32) & 0xffff));
}
public static float blue(@ColorLong long color) {
if ((color & 0x3fL) == 0L) return ((color >> 32) & 0xff) / 255.0f;
return Half.toFloat((short) ((color >> 16) & 0xffff));
}
public static float alpha(@ColorLong long color) {
if ((color & 0x3fL) == 0L) return ((color >> 56) & 0xff) / 255.0f;
return ((color >> 6) & 0x3ff) / 1023.0f;
}
public static boolean isSrgb(@ColorLong long color) {
return colorSpace(color).isSrgb();
}
public static boolean isWideGamut(@ColorLong long color) {
return colorSpace(color).isWideGamut();
}
public static boolean isInColorSpace(@ColorLong long color, @NonNull ColorSpace colorSpace) {
return (int) (color & 0x3fL) == colorSpace.getId();
}
@ColorInt
public static int toArgb(@ColorLong long color) {
if ((color & 0x3fL) == 0L) return (int) (color >> 32);
float r = red(color);
float g = green(color);
float b = blue(color);
float a = alpha(color);
// The transformation saturates the output
float[] c = ColorSpace.connect(colorSpace(color)).transform(r, g, b);
return ((int) (a * 255.0f + 0.5f) << 24) |
((int) (c[0] * 255.0f + 0.5f) << 16) |
((int) (c[1] * 255.0f + 0.5f) << 8) |
(int) (c[2] * 255.0f + 0.5f);
}
@NonNull
public static Color valueOf(@ColorInt int color) {
float r = ((color >> 16) & 0xff) / 255.0f;
float g = ((color >> 8) & 0xff) / 255.0f;
float b = ((color ) & 0xff) / 255.0f;
float a = ((color >> 24) & 0xff) / 255.0f;
return new Color(r, g, b, a, ColorSpace.get(ColorSpace.Named.SRGB));
}
@NonNull
public static Color valueOf(@ColorLong long color) {
return new Color(red(color), green(color), blue(color), alpha(color), colorSpace(color));
}
@NonNull
public static Color valueOf(float r, float g, float b) {
return new Color(r, g, b, 1.0f);
}
@NonNull
public static Color valueOf(float r, float g, float b, float a) {
return new Color(saturate(r), saturate(g), saturate(b), saturate(a));
}
@NonNull
public static Color valueOf(float r, float g, float b, float a, @NonNull ColorSpace colorSpace) {
if (colorSpace.getComponentCount() > 3) {
throw new IllegalArgumentException("The specified color space must use a color model " +
"with at most 3 color components");
}
return new Color(r, g, b, a, colorSpace);
}
@NonNull
public static Color valueOf(@NonNull @Size(min = 4, max = 5) float[] components,
@NonNull ColorSpace colorSpace) {
if (components.length < colorSpace.getComponentCount() + 1) {
throw new IllegalArgumentException("Received a component array of length " +
components.length + " but the color model requires " +
(colorSpace.getComponentCount() + 1) + " (including alpha)");
}
return new Color(Arrays.copyOf(components, colorSpace.getComponentCount() + 1), colorSpace);
}
@ColorLong
public static long pack(@ColorInt int color) {
return (color & 0xffffffffL) << 32;
}
@ColorLong
public static long pack(float red, float green, float blue) {
return pack(red, green, blue, 1.0f, ColorSpace.get(ColorSpace.Named.SRGB));
}
@ColorLong
public static long pack(float red, float green, float blue, float alpha) {
return pack(red, green, blue, alpha, ColorSpace.get(ColorSpace.Named.SRGB));
}
@ColorLong
public static long pack(float red, float green, float blue, float alpha,
@NonNull ColorSpace colorSpace) {
if (colorSpace.isSrgb()) {
int argb =
((int) (alpha * 255.0f + 0.5f) << 24) |
((int) (red * 255.0f + 0.5f) << 16) |
((int) (green * 255.0f + 0.5f) << 8) |
(int) (blue * 255.0f + 0.5f);
return (argb & 0xffffffffL) << 32;
}
int id = colorSpace.getId();
if (id == ColorSpace.MIN_ID) {
throw new IllegalArgumentException(
"Unknown color space, please use a color space returned by ColorSpace.get()");
}
if (colorSpace.getComponentCount() > 3) {
throw new IllegalArgumentException(
"The color space must use a color model with at most 3 components");
}
@HalfFloat short r = Half.toHalf(red);
@HalfFloat short g = Half.toHalf(green);
@HalfFloat short b = Half.toHalf(blue);
int a = (int) (Math.max(0.0f, Math.min(alpha, 1.0f)) * 1023.0f + 0.5f);
// Suppress sign extension
return (r & 0xffffL) << 48 |
(g & 0xffffL) << 32 |
(b & 0xffffL) << 16 |
(a & 0x3ffL ) << 6 |
id & 0x3fL;
}
@ColorLong
public static long convert(@ColorInt int color, @NonNull ColorSpace colorSpace) {
float r = ((color >> 16) & 0xff) / 255.0f;
float g = ((color >> 8) & 0xff) / 255.0f;
float b = ((color ) & 0xff) / 255.0f;
float a = ((color >> 24) & 0xff) / 255.0f;
ColorSpace source = ColorSpace.get(ColorSpace.Named.SRGB);
return convert(r, g, b, a, source, colorSpace);
}
@ColorLong
public static long convert(@ColorLong long color, @NonNull ColorSpace colorSpace) {
float r = red(color);
float g = green(color);
float b = blue(color);
float a = alpha(color);
ColorSpace source = colorSpace(color);
return convert(r, g, b, a, source, colorSpace);
}
@ColorLong
public static long convert(float r, float g, float b, float a,
@NonNull ColorSpace source, @NonNull ColorSpace destination) {
float[] c = ColorSpace.connect(source, destination).transform(r, g, b);
return pack(c[0], c[1], c[2], a, destination);
}
@ColorLong
public static long convert(@ColorLong long color, @NonNull ColorSpace.Connector connector) {
float r = red(color);
float g = green(color);
float b = blue(color);
float a = alpha(color);
return convert(r, g, b, a, connector);
}
@ColorLong
public static long convert(float r, float g, float b, float a,
@NonNull ColorSpace.Connector connector) {
float[] c = connector.transform(r, g, b);
return pack(c[0], c[1], c[2], a, connector.getDestination());
}
public static float luminance(@ColorLong long color) {
ColorSpace colorSpace = colorSpace(color);
if (colorSpace.getModel() != ColorSpace.Model.RGB) {
throw new IllegalArgumentException("The specified color must be encoded in an RGB " +
"color space. The supplied color space is " + colorSpace.getModel());
}
DoubleUnaryOperator eotf = ((ColorSpace.Rgb) colorSpace).getEotf();
double r = eotf.applyAsDouble(red(color));
double g = eotf.applyAsDouble(green(color));
double b = eotf.applyAsDouble(blue(color));
return saturate((float) ((0.2126 * r) + (0.7152 * g) + (0.0722 * b)));
}
private static float saturate(float v) {
return v <= 0.0f ? 0.0f : (v >= 1.0f ? 1.0f : v);
}
@IntRange(from = 0, to = 255)
public static int alpha(int color) {
return color >>> 24;
}
@IntRange(from = 0, to = 255)
public static int red(int color) {
return (color >> 16) & 0xFF;
}
@IntRange(from = 0, to = 255)
public static int green(int color) {
return (color >> 8) & 0xFF;
}
@IntRange(from = 0, to = 255)
public static int blue(int color) {
return color & 0xFF;
}
@ColorInt
public static int rgb(
@IntRange(from = 0, to = 255) int red,
@IntRange(from = 0, to = 255) int green,
@IntRange(from = 0, to = 255) int blue) {
return 0xff000000 | (red << 16) | (green << 8) | blue;
}
@ColorInt
public static int rgb(float red, float green, float blue) {
return 0xff000000 |
((int) (red * 255.0f + 0.5f) << 16) |
((int) (green * 255.0f + 0.5f) << 8) |
(int) (blue * 255.0f + 0.5f);
}
@ColorInt
public static int argb(
@IntRange(from = 0, to = 255) int alpha,
@IntRange(from = 0, to = 255) int red,
@IntRange(from = 0, to = 255) int green,
@IntRange(from = 0, to = 255) int blue) {
return (alpha << 24) | (red << 16) | (green << 8) | blue;
}
@ColorInt
public static int argb(float alpha, float red, float green, float blue) {
return ((int) (alpha * 255.0f + 0.5f) << 24) |
((int) (red * 255.0f + 0.5f) << 16) |
((int) (green * 255.0f + 0.5f) << 8) |
(int) (blue * 255.0f + 0.5f);
}
public static float luminance(@ColorInt int color) {
ColorSpace.Rgb cs = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
DoubleUnaryOperator eotf = cs.getEotf();
double r = eotf.applyAsDouble(red(color) / 255.0);
double g = eotf.applyAsDouble(green(color) / 255.0);
double b = eotf.applyAsDouble(blue(color) / 255.0);
return (float) ((0.2126 * r) + (0.7152 * g) + (0.0722 * b));
}
@ColorInt
public static int parseColor(@Size(min=1) String colorString) {
if (colorString.charAt(0) == '#') {
// Use a long to avoid rollovers on #ffXXXXXX
long color = Long.parseLong(colorString.substring(1), 16);
if (colorString.length() == 7) {
// Set the alpha value
color |= 0x00000000ff000000;
} else if (colorString.length() != 9) {
throw new IllegalArgumentException("Unknown color");
}
return (int)color;
} else {
Integer color = sColorNameMap.get(colorString.toLowerCase(Locale.ROOT));
if (color != null) {
return color;
}
}
throw new IllegalArgumentException("Unknown color");
}
public static void RGBToHSV(
@IntRange(from = 0, to = 255) int red,
@IntRange(from = 0, to = 255) int green,
@IntRange(from = 0, to = 255) int blue, @Size(3) float hsv[]) {
if (hsv.length < 3) {
throw new RuntimeException("3 components required for hsv");
}
nativeRGBToHSV(red, green, blue, hsv);
}
public static void colorToHSV(@ColorInt int color, @Size(3) float hsv[]) {
RGBToHSV((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, hsv);
}
@ColorInt
public static int HSVToColor(@Size(3) float hsv[]) {
return HSVToColor(0xFF, hsv);
}
@ColorInt
public static int HSVToColor(@IntRange(from = 0, to = 255) int alpha, @Size(3) float hsv[]) {
if (hsv.length < 3) {
throw new RuntimeException("3 components required for hsv");
}
return nativeHSVToColor(alpha, hsv);
}
private static native void nativeRGBToHSV(int red, int greed, int blue, float hsv[]);
private static native int nativeHSVToColor(int alpha, float hsv[]);
private static final HashMap<String, Integer> sColorNameMap;
static {
sColorNameMap = new HashMap<>();
sColorNameMap.put("black", BLACK);
sColorNameMap.put("darkgray", DKGRAY);
sColorNameMap.put("gray", GRAY);
sColorNameMap.put("lightgray", LTGRAY);
sColorNameMap.put("white", WHITE);
sColorNameMap.put("red", RED);
sColorNameMap.put("green", GREEN);
sColorNameMap.put("blue", BLUE);
sColorNameMap.put("yellow", YELLOW);
sColorNameMap.put("cyan", CYAN);
sColorNameMap.put("magenta", MAGENTA);
sColorNameMap.put("aqua", 0xFF00FFFF);
sColorNameMap.put("fuchsia", 0xFFFF00FF);
sColorNameMap.put("darkgrey", DKGRAY);
sColorNameMap.put("grey", GRAY);
sColorNameMap.put("lightgrey", LTGRAY);
sColorNameMap.put("lime", 0xFF00FF00);
sColorNameMap.put("maroon", 0xFF800000);
sColorNameMap.put("navy", 0xFF000080);
sColorNameMap.put("olive", 0xFF808000);
sColorNameMap.put("purple", 0xFF800080);
sColorNameMap.put("silver", 0xFFC0C0C0);
sColorNameMap.put("teal", 0xFF008080);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,122 @@
package android.graphics;
import android.os.Parcelable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.os.Parcel;
public final class Rect {
public int left;
public int top;
public int right;
public int bottom;
private static final class UnflattenHelper {
private static final Pattern FLATTENED_PATTERN = Pattern.compile(
"(-?\\d+) (-?\\d+) (-?\\d+) (-?\\d+)");
static Matcher getMatcher(String str) {
return FLATTENED_PATTERN.matcher(str);
}
}
public Rect() {
}
public Rect(int left, int top, int right, int bottom) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public Rect(Rect r) {
if (r == null) {
this.left = 0;
this.top = 0;
this.right = 0;
this.bottom = 0;
} else {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
}
public final int getWidth() {
return right - left;
}
public final int getHeight() {
return bottom - top;
}
public static Rect unflattenFromString(String str) {
if (str.isEmpty()) {
return null;
}
Matcher matcher = UnflattenHelper.getMatcher(str);
if (!matcher.matches()) {
return null;
}
return new Rect(Integer.parseInt(matcher.group(1)),
Integer.parseInt(matcher.group(2)),
Integer.parseInt(matcher.group(3)),
Integer.parseInt(matcher.group(4)));
}
public String toShortString() {
return toShortString(new StringBuilder(32));
}
public String toShortString(StringBuilder sb) {
sb.setLength(0);
sb.append('['); sb.append(left); sb.append(',');
sb.append(top); sb.append("]["); sb.append(right);
sb.append(','); sb.append(bottom); sb.append(']');
return sb.toString();
}
public String flattenToString() {
StringBuilder sb = new StringBuilder(32);
sb.append(left);
sb.append(' ');
sb.append(top);
sb.append(' ');
sb.append(right);
sb.append(' ');
sb.append(bottom);
return sb.toString();
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {
@Override
public Rect createFromParcel(Parcel in) {
Rect r = new Rect();
r.readFromParcel(in);
return r;
}
@Override
public Rect[] newArray(int size) {
return new Rect[size];
}
};
public void readFromParcel(Parcel in) {
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
}
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.graphics;
import com.android.internal.util.ArrayUtils;
/**
* @hide
*/
public class TemporaryBuffer {
public static char[] obtain(int len) {
char[] buf;
synchronized (TemporaryBuffer.class) {
buf = sTemp;
sTemp = null;
}
if (buf == null || buf.length < len) {
buf = ArrayUtils.newUnpaddedCharArray(len);
}
return buf;
}
public static void recycle(char[] temp) {
if (temp.length > 1000) return;
synchronized (TemporaryBuffer.class) {
sTemp = temp;
}
}
private static char[] sTemp = null;
}
@@ -0,0 +1,327 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.graphics;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.res.AssetManager;
import android.util.Log;
import com.android.internal.util.Preconditions;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.font.TextAttribute;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import android.annotation.NonNull;
public class Typeface {
private static String TAG = "Typeface";
/** @hide */
public static final boolean ENABLE_LAZY_TYPEFACE_INITIALIZATION = true;
/** The default NORMAL typeface object */
public static final Typeface DEFAULT;
public static final Typeface DEFAULT_BOLD;
/** The NORMAL style of the default sans serif typeface. */
public static final Typeface SANS_SERIF;
/** The NORMAL style of the default serif typeface. */
public static final Typeface SERIF;
/** The NORMAL style of the default monospace typeface. */
public static final Typeface MONOSPACE;
public @interface Style {}
// Style
public static final int NORMAL = 0;
public static final int BOLD = 1;
public static final int ITALIC = 2;
public static final int BOLD_ITALIC = 3;
/** @hide */ public static final int STYLE_MASK = 0x03;
public static final String DEFAULT_FAMILY = "sans-serif";
private final Font mFont;
/** Returns the typeface's weight value */
public int getWeight() {
Map<TextAttribute, Object> atts = (Map<TextAttribute, Object>) mFont.getAttributes();
Object weight = atts.getOrDefault(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR);
if (weight instanceof Float) {
float w = ((Float) weight).floatValue();
// undo the transformation
return (int) ((w - TextAttribute.WEIGHT_REGULAR) / (TextAttribute.WEIGHT_BOLD - TextAttribute.WEIGHT_REGULAR) * (Builder.BOLD_WEIGHT - Builder.NORMAL_WEIGHT) + Builder.NORMAL_WEIGHT);
}
return Builder.NORMAL_WEIGHT;
}
public float getJavaWeight() {
Map<TextAttribute, Object> atts = (Map<TextAttribute, Object>) mFont.getAttributes();
Object weight = atts.getOrDefault(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR);
if (weight instanceof Float) {
return ((Float) weight).floatValue();
}
return TextAttribute.WEIGHT_REGULAR;
}
/** Returns the typeface's intrinsic style attributes */
public @Style int getStyle() {
if (isBold() && isItalic()) return BOLD_ITALIC;
if (isBold()) return BOLD;
if (isItalic()) return ITALIC;
return NORMAL;
}
/** Returns true if getStyle() has the BOLD bit set. */
public final boolean isBold() {
return mFont.isBold();
}
/** Returns true if getStyle() has the ITALIC bit set. */
public final boolean isItalic() {
return mFont.isItalic();
}
public final @Nullable String getSystemFontFamilyName() {
return mFont.getFamily();
}
public static Typeface findFromCache(AssetManager mgr, String path) {
throw new RuntimeException("Stub!");
}
public static final class Builder {
/** @hide */
public static final int NORMAL_WEIGHT = 400;
/** @hide */
public static final int BOLD_WEIGHT = 700;
private final String mPath;
private Font mFont;
private int mStyle;
private Map<TextAttribute, Object> mAttributes;
private String mFallbackFamilyName;
public Builder(@NonNull File path) {
mFont = loadFont(path);
Log.v(TAG, "Font loaded from " + path.toURI());
mPath = null;
mStyle = 0;
mAttributes = new HashMap<TextAttribute, Object>();
}
public Builder(@NonNull FileDescriptor fd) {
throw new RuntimeException("Stub!");
}
public Builder(@NonNull String path) {
mFont = loadFont(new File(path));
mPath = path;
mStyle = 0;
mAttributes = new HashMap<TextAttribute, Object>();
}
public Builder(@NonNull AssetManager assetManager, @NonNull String path) {
throw new RuntimeException("Stub!");
}
public Builder(@NonNull AssetManager assetManager, @NonNull String path, boolean isAsset,
int cookie) {
throw new RuntimeException("Stub!");
}
public Builder setWeight(int weight) {
// java font weight does not follow typical weight distribution
// In Java, regular weight is at 1.0 and bold at 2.0, compared to 400 and 700 in TTF
// Typical range is 0 to 1000
float jWeight = (weight - NORMAL_WEIGHT) / (BOLD_WEIGHT - NORMAL_WEIGHT) * (TextAttribute.WEIGHT_BOLD - TextAttribute.WEIGHT_REGULAR) + TextAttribute.WEIGHT_REGULAR;
mAttributes.put(TextAttribute.WEIGHT, jWeight);
return this;
}
public Builder setItalic(boolean italic) {
if (italic) {
mStyle |= Font.ITALIC;
} else {
mStyle &= ~Font.ITALIC;
}
return this;
}
public Builder setTtcIndex(int ttcIndex) {
throw new RuntimeException("Stub!");
}
public Builder setFontVariationSettings(@Nullable String variationSettings) {
throw new RuntimeException("Stub!");
}
public Builder setFallback(@Nullable String familyName) {
mFallbackFamilyName = familyName;
return this;
}
private Typeface resolveFallbackTypeface() {
if (mFallbackFamilyName == null) {
return null;
}
return new Typeface(new Font(mFallbackFamilyName, mStyle, 12).deriveFont(mAttributes));
}
public Typeface build() {
if (mFont == null) return resolveFallbackTypeface();
return new Typeface(mFont.deriveFont(mStyle).deriveFont(mAttributes));
}
private Font loadFont(File fontFile) {
try {
return Font.createFont(Font.TRUETYPE_FONT, fontFile);
} catch (FontFormatException ex) {
Log.v(TAG, "Failed to create font as TTF", ex);
} catch (IOException ex) {
Log.v(TAG, "Failed to create font as TTF", ex);
throw new RuntimeException(ex);
}
try {
return Font.createFont(Font.TYPE1_FONT, fontFile);
} catch (FontFormatException ex) {
Log.v(TAG, "Failed to create font as T1", ex);
} catch (IOException ex) {
Log.v(TAG, "Failed to create font as T1", ex);
throw new RuntimeException(ex);
}
return null;
}
}
public static Typeface create(String familyName, @Style int style) {
return create(getSystemDefaultTypeface(familyName), style);
}
public static Typeface create(Typeface family, @Style int style) {
if ((style & ~STYLE_MASK) != 0) {
style = NORMAL;
}
if (family == null) {
family = getSystemDefaultTypeface(DEFAULT_FAMILY);
}
throw new RuntimeException("Stub!");
}
public static @NonNull Typeface create(@Nullable Typeface family,
int weight, boolean italic) {
Preconditions.checkArgumentInRange(weight, 0, 1000, "weight");
if (family == null) {
family = getSystemDefaultTypeface(DEFAULT_FAMILY);
}
return createWeightStyle(family, weight, italic);
}
private static @NonNull Typeface createWeightStyle(@NonNull Typeface base,
int weight, boolean italic) {
throw new RuntimeException("Stub!");
}
public static Typeface defaultFromStyle(@Style int style) {
throw new RuntimeException("Stub!");
}
public static Typeface createFromAsset(AssetManager mgr, String path) {
Preconditions.checkNotNull(path); // for backward compatibility
Preconditions.checkNotNull(mgr);
Typeface typeface = new Builder(mgr, path).build();
if (typeface != null) return typeface;
// check if the file exists, and throw an exception for backward compatibility
try (InputStream inputStream = mgr.open(path)) {
} catch (IOException e) {
throw new RuntimeException("Font asset not found " + path);
}
return Typeface.DEFAULT;
}
public Typeface(Font fnt) {
mFont = fnt;
}
public static Typeface createFromFile(@Nullable File file) {
// For the compatibility reasons, leaving possible NPE here.
// See android.graphics.cts.TypefaceTest#testCreateFromFileByFileReferenceNull
Typeface typeface = new Builder(file).build();
if (typeface != null) return typeface;
// check if the file exists, and throw an exception for backward compatibility
if (!file.exists()) {
throw new RuntimeException("Font asset not found " + file.getAbsolutePath());
}
return Typeface.DEFAULT;
}
public static Typeface createFromFile(@Nullable String path) {
Preconditions.checkNotNull(path); // for backward compatibility
return createFromFile(new File(path));
}
private static Typeface getSystemDefaultTypeface(@NonNull String familyName) {
return new Typeface(new Font(familyName, 0, 12));
}
public Font getFont() {
return mFont;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Typeface typeface = (Typeface) o;
return typeface.mFont.equals(mFont);
}
@Override
public int hashCode() {
return mFont.hashCode();
}
static {
DEFAULT = new Typeface(new Font(null, 0, 12));
DEFAULT_BOLD = new Typeface(new Font(null, Font.BOLD, 12));
SANS_SERIF = new Typeface(new Font(Font.SANS_SERIF, 0, 12));
SERIF = new Typeface(new Font(Font.SERIF, 0, 12));
MONOSPACE = new Typeface(new Font(Font.MONOSPACED, 0, 12));
}
}
@@ -1,7 +1,7 @@
package android.os;
import xyz.nulldev.androidcompat.io.AndroidFiles;
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
import java.io.File;
@@ -9,7 +9,7 @@ import java.io.File;
* Android compatibility layer for files
*/
public class Environment {
private static AndroidFiles androidFiles = KodeinGlobalHelper.instance(AndroidFiles.class);
private static AndroidFiles androidFiles = KoinGlobalHelper.instance(AndroidFiles.class);
public static String DIRECTORY_ALARMS = getHomeDirectory("Alarms").getAbsolutePath();
public static String DIRECTORY_DCIM = getHomeDirectory("DCIM").getAbsolutePath();
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
/* //device/java/android/android/app/IActivityPendingResult.aidl
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
package android.os;
import android.os.Message;
/** @hide */
/* oneway */ interface IMessenger {
void send(/* in */ Message msg);
}
@@ -0,0 +1,575 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Log;
import android.util.Printer;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import java.util.Objects;
/**
* Class used to run a message loop for a thread. Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
* <p>Most interaction with a message loop is through the
* {@link Handler} class.
*
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler(Looper.myLooper()) {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }</pre>
*/
public final class Looper {
/*
* API Implementation Note:
*
* This class contains the code required to set up and manage an event loop
* based on MessageQueue. APIs that affect the state of the queue should be
* defined on MessageQueue or Handler rather than on Looper itself. For example,
* idle handlers and sync barriers are defined on the queue whereas preparing the
* thread, looping, and quitting are defined on the looper.
*/
private static final String TAG = "Looper";
private static class NoImagePreloadHolder {
// Enable/Disable verbose logging with a system prop. e.g.
// adb shell 'setprop log.looper.slow.verbose false && stop && start'
private static final boolean sVerboseLogging =
SystemProperties.getBoolean("log.looper.slow.verbose", false);
}
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
private static Observer sObserver;
final MessageQueue mQueue;
final Thread mThread;
private boolean mInLoop;
private Printer mLogging;
private long mTraceTag;
/**
* If set, the looper will show a warning log if a message dispatch takes longer than this.
*/
private long mSlowDispatchThresholdMs;
/**
* If set, the looper will show a warning log if a message delivery (actual delivery time -
* post time) takes longer than this.
*/
private long mSlowDeliveryThresholdMs;
/**
* True if a message delivery takes longer than {@link #mSlowDeliveryThresholdMs}.
*/
private boolean mSlowDeliveryDetected;
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. See also: {@link #prepare()}
*
* @deprecated The main looper for your application is created by the Android environment,
* so you should never need to call this function yourself.
*/
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
/**
* Force the application's main looper to the given value. The main looper is typically
* configured automatically by the OS, so this capability is only intended to enable testing.
*
* @hide
*/
public static void setMainLooperForTest(@NonNull Looper looper) {
synchronized (Looper.class) {
sMainLooper = Objects.requireNonNull(looper);
}
}
/**
* Clear the application's main looper to be undefined. The main looper is typically
* configured automatically by the OS, so this capability is only intended to enable testing.
*
* @hide
*/
public static void clearMainLooperForTest() {
synchronized (Looper.class) {
sMainLooper = null;
}
}
/**
* Set the transaction observer for all Loopers in this process.
*
* @hide
*/
public static void setObserver(@Nullable Observer observer) {
sObserver = observer;
}
/**
* Poll and deliver single message, return true if the outer loop should continue.
*/
@SuppressWarnings({"UnusedTokenOfOriginalCallingIdentity",
"ClearIdentityCallNotFollowedByTryFinally"})
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
final boolean hasOverride = thresholdOverride >= 0;
if (hasOverride) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0 || hasOverride)
&& (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0 || hasOverride);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
Log.e(TAG, "Loop handler threw", exception);
// throw exception;
} finally {
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
ThreadLocalWorkSource.restore(origWorkSource);
}
if (logSlowDelivery) {
boolean slow = false;
if (!me.mSlowDeliveryDetected || NoImagePreloadHolder.sVerboseLogging) {
slow = showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart,
"delivery", msg);
}
if (me.mSlowDeliveryDetected) {
if (!slow && (dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
me.mSlowDeliveryDetected = false;
}
} else if (slow) {
// A slow delivery is detected, suppressing further logs unless verbose logging
// is enabled.
me.mSlowDeliveryDetected = true;
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
// final long newIdent = Binder.clearCallingIdentity();
// if (ident != newIdent) {
// Log.wtf(TAG, "Thread identity changed from 0x"
// + Long.toHexString(ident) + " to 0x"
// + Long.toHexString(newIdent) + " while dispatching to "
// + msg.target.getClass().getName() + " "
// + msg.callback + " what=" + msg.what);
// }
msg.recycleUnchecked();
return true;
}
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
@SuppressWarnings({"UnusedTokenOfOriginalCallingIdentity",
"ClearIdentityCallNotFollowedByTryFinally",
"ResultOfClearIdentityCallNotStoredInVariable"})
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
// Binder.clearCallingIdentity();
// final long ident = Binder.clearCallingIdentity();
final long ident = 0;
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride = getThresholdOverride();
me.mSlowDeliveryDetected = false;
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
private static int getThresholdOverride() {
return -1;
// // Allow overriding the threshold for all processes' main looper with a system prop.
// // e.g. adb shell 'setprop log.looper.any.main.slow 1 && stop && start'
// if (myLooper() == getMainLooper()) {
// final int globalOverride = SystemProperties.getInt("log.looper.any.main.slow", -1);
// if (globalOverride >= 0) {
// return globalOverride;
// }
// }
// // Allow overriding the threshold for all threads within a process with a system prop.
// // e.g. adb shell 'setprop log.looper.1000.any.slow 1 && stop && start'
// final int processOverride = SystemProperties.getInt("log.looper."
// + Process.myUid() + ".any.slow", -1);
// if (processOverride >= 0) {
// return processOverride;
// }
// return SystemProperties.getInt("log.looper."
// + Process.myUid() + "."
// + Thread.currentThread().getName()
// + ".slow", -1);
}
private static int getThresholdOverride$ravenwood() {
return -1;
}
private static int getThreadGroup() {
int threadGroup = Process.THREAD_GROUP_DEFAULT;
if (!Process.isIsolated()) {
threadGroup = Process.getProcessGroup(Process.myTid());
}
return threadGroup;
}
private static String threadGroupToString(int threadGroup) {
switch (threadGroup) {
case Process.THREAD_GROUP_SYSTEM:
return "SYSTEM";
case Process.THREAD_GROUP_AUDIO_APP:
return "AUDIO_APP";
case Process.THREAD_GROUP_AUDIO_SYS:
return "AUDIO_SYS";
case Process.THREAD_GROUP_TOP_APP:
return "TOP_APP";
case Process.THREAD_GROUP_RT_APP:
return "RT_APP";
default:
return "UNKNOWN";
}
}
private static boolean showSlowLog(long threshold, long measureStart, long measureEnd,
String what, Message msg) {
final long actualTime = measureEnd - measureStart;
if (actualTime < threshold) {
return false;
}
String name = /* Process.myProcessName() */ "Stub!";
String threadGroup = threadGroupToString(getThreadGroup());
boolean isMain = myLooper() == getMainLooper();
// For slow delivery, the current message isn't really important, but log it anyway.
Slog.w(TAG, "Slow " + what + " took " + actualTime + "ms "
+ Thread.currentThread().getName() + " app=" + name
+ " main=" + isMain + " group=" + threadGroup
+ " h=" + msg.target.getClass().getName() + " c=" + msg.callback
+ " m=" + msg.what);
return true;
}
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
/**
* Return the {@link MessageQueue} object associated with the current
* thread. This must be called from a thread running a Looper, or a
* NullPointerException will be thrown.
*/
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
/**
* Returns true if the current thread is this looper's thread.
*/
public boolean isCurrentThread() {
return Thread.currentThread() == mThread;
}
/**
* Control logging of messages as they are processed by this Looper. If
* enabled, a log message will be written to <var>printer</var>
* at the beginning and ending of each message dispatch, identifying the
* target Handler and message contents.
*
* @param printer A Printer object that will receive log messages, or
* null to disable message logging.
*/
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
/** {@hide} */
public void setTraceTag(long traceTag) {
mTraceTag = traceTag;
}
/**
* Set a thresholds for slow dispatch/delivery log.
* {@hide}
*/
public void setSlowLogThresholdMs(long slowDispatchThresholdMs, long slowDeliveryThresholdMs) {
mSlowDispatchThresholdMs = slowDispatchThresholdMs;
mSlowDeliveryThresholdMs = slowDeliveryThresholdMs;
}
/**
* Quits the looper.
* <p>
* Causes the {@link #loop} method to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @see #quitSafely
*/
public void quit() {
mQueue.quit(false);
}
/**
* Quits the looper safely.
* <p>
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p>
*/
public void quitSafely() {
mQueue.quit(true);
}
/**
* Gets the Thread associated with this Looper.
*
* @return The looper's thread.
*/
public @NonNull Thread getThread() {
return mThread;
}
/**
* Gets this looper's message queue.
*
* @return The looper's message queue.
*/
public @NonNull MessageQueue getQueue() {
return mQueue;
}
/**
* Dumps the state of the looper for debugging purposes.
*
* @param pw A printer to receive the contents of the dump.
* @param prefix A prefix to prepend to each line which is printed.
*/
public void dump(@NonNull Printer pw, @NonNull String prefix) {
throw new RuntimeException("Stub!");
}
/**
* Dumps the state of the looper for debugging purposes.
*
* @param pw A printer to receive the contents of the dump.
* @param prefix A prefix to prepend to each line which is printed.
* @param handler Only dump messages for this Handler.
* @hide
*/
public void dump(@NonNull Printer pw, @NonNull String prefix, Handler handler) {
throw new RuntimeException("Stub!");
}
/** @hide */
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
throw new RuntimeException("Stub!");
}
@Override
public String toString() {
return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
+ ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
}
/** {@hide} */
public interface Observer {
/**
* Called right before a message is dispatched.
*
* <p> The token type is not specified to allow the implementation to specify its own type.
*
* @return a token used for collecting telemetry when dispatching a single message.
* The token token must be passed back exactly once to either
* {@link Observer#messageDispatched} or {@link Observer#dispatchingThrewException}
* and must not be reused again.
*
*/
Object messageDispatchStarting();
/**
* Called when a message was processed by a Handler.
*
* @param token Token obtained by previously calling
* {@link Observer#messageDispatchStarting} on the same Observer instance.
* @param msg The message that was dispatched.
*/
void messageDispatched(Object token, Message msg);
/**
* Called when an exception was thrown while processing a message.
*
* @param token Token obtained by previously calling
* {@link Observer#messageDispatchStarting} on the same Observer instance.
* @param msg The message that was dispatched and caused an exception.
* @param exception The exception that was thrown.
*/
void dispatchingThrewException(Object token, Message msg, Exception exception);
}
}
@@ -0,0 +1,630 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
import android.annotation.Nullable;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
/**
*
* Defines a message containing a description and arbitrary data object that can be
* sent to a {@link Handler}. This object contains two extra int fields and an
* extra object field that allow you to not do allocations in many cases.
*
* <p class="note">While the constructor of Message is public, the best way to get
* one of these is to call {@link #obtain Message.obtain()} or one of the
* {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
* them from a pool of recycled objects.</p>
*/
public final class Message implements Parcelable {
/**
* User-defined message code so that the recipient can identify
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
*
* If not specified, this value is 0.
* Use values other than 0 to indicate custom message codes.
*/
public int what;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg1;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg2;
/**
* An arbitrary object to send to the recipient. When using
* {@link Messenger} to send the message across processes this can only
* be non-null if it contains a Parcelable of a framework class (not one
* implemented by the application). For other data transfer use
* {@link #setData}.
*
* <p>Note that Parcelable objects here are not supported prior to
* the {@link android.os.Build.VERSION_CODES#FROYO} release.
*/
public Object obj;
/**
* Optional Messenger where replies to this message can be sent. The
* semantics of exactly how this is used are up to the sender and
* receiver.
*/
public Messenger replyTo;
/**
* Indicates that the uid is not set;
*
* @hide Only for use within the system server.
*/
public static final int UID_NONE = -1;
/**
* Optional field indicating the uid that sent the message. This is
* only valid for messages posted by a {@link Messenger}; otherwise,
* it will be -1.
*/
public int sendingUid = UID_NONE;
/**
* Optional field indicating the uid that caused this message to be enqueued.
*
* @hide Only for use within the system server.
*/
public int workSourceUid = UID_NONE;
/** If set message is in use.
* This flag is set when the message is enqueued and remains set while it
* is delivered and afterwards when it is recycled. The flag is only cleared
* when a new message is created or obtained since that is the only time that
* applications are allowed to modify the contents of the message.
*
* It is an error to attempt to enqueue or recycle a message that is already in use.
*/
/*package*/ static final int FLAG_IN_USE = 1 << 0;
/** If set message is asynchronous */
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
/** Flags to clear in the copyFrom method */
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
/*package*/ int flags;
/**
* The targeted delivery time of this message. The time-base is
* {@link SystemClock#uptimeMillis}.
* @hide Only for use within the tests.
*/
public long when;
/** @hide */
@SuppressWarnings("unused")
public long mInsertSeq;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
/** @hide */
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
/**
* Same as {@link #obtain()}, but copies the values of an existing
* message (including its target) into the new one.
* @param orig Original message to copy.
* @return A Message object from the global pool.
*/
public static Message obtain(Message orig) {
Message m = obtain();
m.what = orig.what;
m.arg1 = orig.arg1;
m.arg2 = orig.arg2;
m.obj = orig.obj;
m.replyTo = orig.replyTo;
m.sendingUid = orig.sendingUid;
m.workSourceUid = orig.workSourceUid;
if (orig.data != null) {
m.data = new Bundle(orig.data);
}
m.target = orig.target;
m.callback = orig.callback;
return m;
}
/**
* Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
* @param h Handler to assign to the returned Message object's <em>target</em> member.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
/**
* Same as {@link #obtain(Handler)}, but assigns a callback Runnable on
* the Message that is returned.
* @param h Handler to assign to the returned Message object's <em>target</em> member.
* @param callback Runnable that will execute when the message is handled.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
/**
* Same as {@link #obtain()}, but sets the values for both <em>target</em> and
* <em>what</em> members on the Message.
* @param h Value to assign to the <em>target</em> member.
* @param what Value to assign to the <em>what</em> member.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what) {
Message m = obtain();
m.target = h;
m.what = what;
return m;
}
/**
* Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, and <em>obj</em>
* members.
* @param h The <em>target</em> value to set.
* @param what The <em>what</em> value to set.
* @param obj The <em>object</em> method to set.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.obj = obj;
return m;
}
/**
* Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
* <em>arg1</em>, and <em>arg2</em> members.
*
* @param h The <em>target</em> value to set.
* @param what The <em>what</em> value to set.
* @param arg1 The <em>arg1</em> value to set.
* @param arg2 The <em>arg2</em> value to set.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what, int arg1, int arg2) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
return m;
}
/**
* Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
* <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
*
* @param h The <em>target</em> value to set.
* @param what The <em>what</em> value to set.
* @param arg1 The <em>arg1</em> value to set.
* @param arg2 The <em>arg2</em> value to set.
* @param obj The <em>obj</em> value to set.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
/** @hide */
public static void updateCheckRecycle(int targetSdkVersion) {
if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
gCheckRecycle = false;
}
}
/**
* Return a Message instance to the global pool.
* <p>
* You MUST NOT touch the Message after calling this function because it has
* effectively been freed. It is an error to recycle a message that is currently
* enqueued or that is in the process of being delivered to a Handler.
* </p>
*/
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
/**
* Make this message like o. Performs a shallow copy of the data field.
* Does not copy the linked list fields, nor the timestamp or
* target/callback of the original message.
*/
public void copyFrom(Message o) {
this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
this.what = o.what;
this.arg1 = o.arg1;
this.arg2 = o.arg2;
this.obj = o.obj;
this.replyTo = o.replyTo;
this.sendingUid = o.sendingUid;
this.workSourceUid = o.workSourceUid;
if (o.data != null) {
this.data = (Bundle) o.data.clone();
} else {
this.data = null;
}
}
/**
* Return the targeted delivery time of this message, in milliseconds.
*/
public long getWhen() {
return when;
}
public void setTarget(Handler target) {
this.target = target;
}
/**
* Retrieve the {@link android.os.Handler Handler} implementation that
* will receive this message. The object must implement
* {@link android.os.Handler#handleMessage(android.os.Message)
* Handler.handleMessage()}. Each Handler has its own name-space for
* message codes, so you do not need to
* worry about yours conflicting with other handlers.
*/
public Handler getTarget() {
return target;
}
/**
* Retrieve callback object that will execute when this message is handled.
* This object must implement Runnable. This is called by
* the <em>target</em> {@link Handler} that is receiving this Message to
* dispatch it. If
* not set, the message will be dispatched to the receiving Handler's
* {@link Handler#handleMessage(Message)}.
*/
public Runnable getCallback() {
return callback;
}
/** @hide */
public Message setCallback(Runnable r) {
callback = r;
return this;
}
/**
* Obtains a Bundle of arbitrary data associated with this
* event, lazily creating it if necessary. Set this value by calling
* {@link #setData(Bundle)}. Note that when transferring data across
* processes via {@link Messenger}, you will need to set your ClassLoader
* on the Bundle via {@link Bundle#setClassLoader(ClassLoader)
* Bundle.setClassLoader()} so that it can instantiate your objects when
* you retrieve them.
* @see #peekData()
* @see #setData(Bundle)
*/
public Bundle getData() {
if (data == null) {
data = new Bundle();
}
return data;
}
/**
* Like getData(), but does not lazily create the Bundle. A null
* is returned if the Bundle does not already exist. See
* {@link #getData} for further information on this.
* @see #getData()
* @see #setData(Bundle)
*/
@Nullable
public Bundle peekData() {
return data;
}
/**
* Sets a Bundle of arbitrary data values. Use arg1 and arg2 members
* as a lower cost way to send a few simple integer values, if you can.
* @see #getData()
* @see #peekData()
*/
public void setData(Bundle data) {
this.data = data;
}
/**
* Chainable setter for {@link #what}
*
* @hide
*/
public Message setWhat(int what) {
this.what = what;
return this;
}
/**
* Sends this Message to the Handler specified by {@link #getTarget}.
* Throws a null pointer exception if this field has not been set.
*/
public void sendToTarget() {
target.sendMessage(this);
}
/**
* Returns true if the message is asynchronous, meaning that it is not
* subject to {@link Looper} synchronization barriers.
*
* @return True if the message is asynchronous.
*
* @see #setAsynchronous(boolean)
*/
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
/**
* Sets whether the message is asynchronous, meaning that it is not
* subject to {@link Looper} synchronization barriers.
* <p>
* Certain operations, such as view invalidation, may introduce synchronization
* barriers into the {@link Looper}'s message queue to prevent subsequent messages
* from being delivered until some condition is met. In the case of view invalidation,
* messages which are posted after a call to {@link android.view.View#invalidate}
* are suspended by means of a synchronization barrier until the next frame is
* ready to be drawn. The synchronization barrier ensures that the invalidation
* request is completely handled before resuming.
* </p><p>
* Asynchronous messages are exempt from synchronization barriers. They typically
* represent interrupts, input events, and other signals that must be handled independently
* even while other work has been suspended.
* </p><p>
* Note that asynchronous messages may be delivered out of order with respect to
* synchronous messages although they are always delivered in order among themselves.
* If the relative order of these messages matters then they probably should not be
* asynchronous in the first place. Use with caution.
* </p>
*
* @param async True if the message is asynchronous.
*
* @see #isAsynchronous()
*/
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
/*package*/ boolean isInUse() {
return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}
/*package*/ void markInUse() {
flags |= FLAG_IN_USE;
}
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {
}
@Override
public String toString() {
return toString(SystemClock.uptimeMillis());
}
String toString(long now) {
StringBuilder b = new StringBuilder();
b.append("{ when=");
TimeUtils.formatDuration(when - now, b);
if (target != null) {
if (callback != null) {
b.append(" callback=");
b.append(callback.getClass().getName());
} else {
b.append(" what=");
b.append(what);
}
if (arg1 != 0) {
b.append(" arg1=");
b.append(arg1);
}
if (arg2 != 0) {
b.append(" arg2=");
b.append(arg2);
}
if (obj != null) {
b.append(" obj=");
b.append(obj);
}
b.append(" target=");
b.append(target.getClass().getName());
} else {
b.append(" barrier=");
b.append(arg1);
}
b.append(" }");
return b.toString();
}
public static final @android.annotation.NonNull Parcelable.Creator<Message> CREATOR
= new Parcelable.Creator<Message>() {
public Message createFromParcel(Parcel source) {
Message msg = Message.obtain();
msg.readFromParcel(source);
return msg;
}
public Message[] newArray(int size) {
return new Message[size];
}
};
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
if (callback != null) {
throw new RuntimeException(
"Can't marshal callbacks across processes.");
}
dest.writeInt(what);
dest.writeInt(arg1);
dest.writeInt(arg2);
if (obj != null) {
try {
Parcelable p = (Parcelable)obj;
dest.writeInt(1);
dest.writeParcelable(p, flags);
} catch (ClassCastException e) {
throw new RuntimeException(
"Can't marshal non-Parcelable objects across processes.");
}
} else {
dest.writeInt(0);
}
dest.writeLong(when);
dest.writeBundle(data);
Messenger.writeMessengerOrNullToParcel(replyTo, dest);
dest.writeInt(sendingUid);
dest.writeInt(workSourceUid);
}
private void readFromParcel(Parcel source) {
what = source.readInt();
arg1 = source.readInt();
arg2 = source.readInt();
if (source.readInt() != 0) {
obj = source.readParcelable(getClass().getClassLoader());
}
when = source.readLong();
data = source.readBundle();
replyTo = Messenger.readMessengerOrNullFromParcel(source);
sendingUid = source.readInt();
workSourceUid = source.readInt();
}
}
File diff suppressed because it is too large Load Diff
@@ -42,19 +42,19 @@ public class SystemProperties {
return native_get(key);
}
private static int native_get_int(String key, int def) {
if(configModule.hasProperty(key))
if(!configModule.hasProperty(key))
return def;
else
return configModule.getIntProperty(key);
}
private static long native_get_long(String key, long def) {
if(configModule.hasProperty(key))
if(!configModule.hasProperty(key))
return def;
else
return configModule.getLongProperty(key);
}
private static boolean native_get_boolean(String key, boolean def) {
if(configModule.hasProperty(key))
if(!configModule.hasProperty(key))
return def;
else
return configModule.getBooleanProperty(key);
@@ -123,4 +123,4 @@ public class SystemProperties {
}
native_set(key, val);
}
}
}
@@ -0,0 +1,106 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
/**
* Tracks who triggered the work currently executed on this thread.
*
* <p>ThreadLocalWorkSource is automatically updated inside system server for incoming/outgoing
* binder calls and messages posted to handler threads.
*
* <p>ThreadLocalWorkSource can also be set manually if needed to refine the WorkSource.
*
* <p>Example:
* <ul>
* <li>Bluetooth process calls {@link PowerManager#isInteractive()} API on behalf of app foo.
* <li>ThreadLocalWorkSource will be automatically set to the UID of foo.
* <li>Any code on the thread handling {@link PowerManagerService#isInteractive()} can call
* {@link ThreadLocalWorkSource#getUid()} to blame any resource used to handle this call.
* <li>If a message is posted from the binder thread, the code handling the message can also call
* {@link ThreadLocalWorkSource#getUid()} and it will return the UID of foo since the work source is
* automatically propagated.
* </ul>
*
* @hide Only for use within system server.
*/
public final class ThreadLocalWorkSource {
public static final int UID_NONE = Message.UID_NONE;
private static final ThreadLocal<int []> sWorkSourceUid =
ThreadLocal.withInitial(() -> new int[] {UID_NONE});
/**
* Returns the UID to blame for the code currently executed on this thread.
*
* <p>This UID is set automatically by common frameworks (e.g. Binder and Handler frameworks)
* and automatically propagated inside system server.
* <p>It can also be set manually using {@link #setUid(int)}.
*/
public static int getUid() {
return sWorkSourceUid.get()[0];
}
/**
* Sets the UID to blame for the code currently executed on this thread.
*
* <p>Inside system server, this UID will be automatically propagated.
* <p>It will be used to attribute future resources used on this thread (e.g. binder
* transactions or processing handler messages) and on any other threads the UID is propagated
* to.
*
* @return a token that can be used to restore the state.
*/
public static long setUid(int uid) {
final long token = getToken();
sWorkSourceUid.get()[0] = uid;
return token;
}
/**
* Restores the state using the provided token.
*/
public static void restore(long token) {
sWorkSourceUid.get()[0] = parseUidFromToken(token);
}
/**
* Clears the stored work source uid.
*
* <p>This method should be used when we do not know who to blame. If the UID to blame is the
* UID of the current process, it is better to attribute the work to the current process
* explicitly instead of clearing the work source:
*
* <pre>
* ThreadLocalWorkSource.setUid(Process.myUid());
* </pre>
*
* @return a token that can be used to restore the state.
*/
public static long clear() {
return setUid(UID_NONE);
}
private static int parseUidFromToken(long token) {
return (int) token;
}
private static long getToken() {
return sWorkSourceUid.get()[0];
}
private ThreadLocalWorkSource() {
}
}
@@ -0,0 +1,168 @@
package android.os.shadows;
// package org.robolectric.res.android;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A unique id per object registry. Used to emulate android platform behavior of storing a long
* which represents a pointer to an object.
*/
public class NativeObjRegistry<T> {
private static final int INITIAL_ID = 1;
private final String name;
private final boolean debug;
private final HashMap<Long, T> nativeObjToIdMap = new HashMap<Long, T>();
private final Map<Long, DebugInfo> idToDebugInfoMap;
private long nextId = INITIAL_ID;
public NativeObjRegistry(Class<T> theClass) {
this(theClass, false);
}
public NativeObjRegistry(Class<T> theClass, boolean debug) {
this(theClass.getSimpleName(), debug);
}
public NativeObjRegistry(String name) {
this(name, false);
}
public NativeObjRegistry(String name, boolean debug) {
this.name = name;
this.debug = debug;
this.idToDebugInfoMap = debug ? new HashMap<>() : null;
}
private Long getNativeObjectId(T o) {
for(Map.Entry<Long, T> entry : nativeObjToIdMap.entrySet()) {
if (o == entry.getValue())
return entry.getKey();
}
return null;
}
/**
* Register and assign a new unique native id for given object (representing a C memory pointer).
*
* @throws IllegalStateException if the object was previously registered
*/
public synchronized long register(T o) {
if (o == null)
throw new IllegalStateException("Object must not be null");
Long nativeId = getNativeObjectId(o);
if (nativeId != null) {
if (debug) {
DebugInfo debugInfo = idToDebugInfoMap.get(nativeId);
if (debugInfo != null) {
System.out.printf(
"NativeObjRegistry %s: register %d -> %s already registered:%n", name, nativeId, o);
debugInfo.registrationTrace.printStackTrace(System.out);
}
}
throw new IllegalStateException("Object was previously registered with id " + nativeId);
}
nativeId = nextId;
if (debug) {
System.out.printf("NativeObjRegistry %s: register %d -> %s%n", name, nativeId, o);
idToDebugInfoMap.put(nativeId, new DebugInfo(new Trace()));
}
nativeObjToIdMap.put(nativeId, o);
nextId++;
return nativeId;
}
/**
* Unregister an object previously registered with {@link #register(Object)}.
*
* @param nativeId the unique id (representing a C memory pointer) of the object to unregister.
* @throws IllegalStateException if the object was never registered, or was previously
* unregistered.
*/
public synchronized T unregister(long nativeId) {
T o = nativeObjToIdMap.remove(nativeId);
if (debug) {
System.out.printf("NativeObjRegistry %s: unregister %d -> %s%n", name, nativeId, o);
new RuntimeException("unregister debug").printStackTrace(System.out);
}
if (o == null) {
if (debug) {
DebugInfo debugInfo = idToDebugInfoMap.get(nativeId);
debugInfo.unregistrationTraces.add(new Trace());
if (debugInfo.unregistrationTraces.size() > 1) {
System.out.format("NativeObjRegistry %s: Too many unregistrations:%n", name);
for (Trace unregistration : debugInfo.unregistrationTraces) {
unregistration.printStackTrace(System.out);
}
}
}
throw new IllegalStateException(
nativeId + " has already been removed (or was never registered)");
}
return o;
}
/** Retrieve the native object for given id. Throws if object with that id cannot be found */
public synchronized T getNativeObject(long nativeId) {
T object = nativeObjToIdMap.get(nativeId);
if (object != null) {
return object;
} else {
throw new NullPointerException(
String.format(
"Could not find object with nativeId: %d. Currently registered ids: %s",
nativeId, nativeObjToIdMap.keySet()));
}
}
/**
* Updates the native object for the given id.
*
* @throws IllegalStateException if no object was registered with the given id before
*/
public synchronized void update(long nativeId, T o) {
T previous = nativeObjToIdMap.get(nativeId);
if (previous == null) {
throw new IllegalStateException("Native id " + nativeId + " was never registered");
}
if (debug) {
System.out.printf("NativeObjRegistry %s: update %d -> %s%n", name, nativeId, o);
idToDebugInfoMap.put(nativeId, new DebugInfo(new Trace()));
}
nativeObjToIdMap.put(nativeId, o);
}
/**
* Similar to {@link #getNativeObject(long)} but returns null if object with given id cannot be
* found.
*/
public synchronized T peekNativeObject(long nativeId) {
return nativeObjToIdMap.get(nativeId);
}
/** WARNING -- dangerous! Call {@link #unregister(long)} instead! */
public synchronized void clear() {
nextId = INITIAL_ID;
nativeObjToIdMap.clear();
}
private static class DebugInfo {
final Trace registrationTrace;
final List<Trace> unregistrationTraces = new ArrayList<>();
public DebugInfo(Trace trace) {
registrationTrace = trace;
}
}
private static class Trace extends Throwable {
private Trace() {}
}
}
@@ -0,0 +1,73 @@
package android.os.shadows;
// package org.robolectric.shadows;
// and badly gutted
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.os.MessageQueue.IdleHandler;
import android.os.SystemClock;
import android.util.Log;
import java.time.Duration;
import java.util.ArrayList;
/**
* The shadow {@link} MessageQueue} for {@link LooperMode.Mode.PAUSED}
*
* <p>This class should not be referenced directly. Use {@link ShadowMessageQueue} instead.
*/
@SuppressWarnings("SynchronizeOnNonFinalField")
public class ShadowPausedMessageQueue {
// just use this class as the native object
private static NativeObjRegistry<ShadowPausedMessageQueue> nativeQueueRegistry =
new NativeObjRegistry<ShadowPausedMessageQueue>(ShadowPausedMessageQueue.class);
private boolean isPolling = false;
private Exception uncaughtException = null;
// shadow constructor instead of nativeInit because nativeInit signature has changed across SDK
// versions
public static long nativeInit() {
return nativeQueueRegistry.register(new ShadowPausedMessageQueue());
}
public static void nativeDestroy(long ptr) {
nativeQueueRegistry.unregister(ptr);
}
public static void nativePollOnce(long ptr, int timeoutMillis) {
ShadowPausedMessageQueue obj = nativeQueueRegistry.getNativeObject(ptr);
obj.nativePollOnce(timeoutMillis);
}
public void nativePollOnce(int timeoutMillis) {
if (timeoutMillis == 0) {
return;
}
synchronized (this) {
isPolling = true;
try {
if (timeoutMillis < 0) {
this.wait();
} else {
this.wait(timeoutMillis);
}
} catch (InterruptedException e) {
// ignore
}
isPolling = false;
}
}
public static void nativeWake(long ptr) {
ShadowPausedMessageQueue obj = nativeQueueRegistry.getNativeObject(ptr);
synchronized (obj) {
obj.notifyAll();
}
}
public static boolean nativeIsPolling(long ptr) {
return nativeQueueRegistry.getNativeObject(ptr).isPolling;
}
}
@@ -9,8 +9,10 @@ import android.content.Context
class PreferenceManager {
companion object {
@JvmStatic
fun getDefaultSharedPreferences(context: Context)
= context.getSharedPreferences(context.applicationInfo.packageName,
Context.MODE_PRIVATE)!!
fun getDefaultSharedPreferences(context: Context) =
context.getSharedPreferences(
context.applicationInfo.packageName,
Context.MODE_PRIVATE,
)!!
}
}
@@ -1,291 +0,0 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.support.v4.content;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.StatFs;
import android.support.v4.os.EnvironmentCompat;
import java.io.File;
/**
* Helper for accessing features in {@link android.content.Context}
* introduced after API level 4 in a backwards compatible fashion.
*/
public class ContextCompat {
/**
* Start a set of activities as a synthesized task stack, if able.
*
* <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for
* app navigation using the back key changed. The back key's behavior is local
* to the current task and does not capture navigation across different tasks.
* Navigating across tasks and easily reaching the previous task is accomplished
* through the "recents" UI, accessible through the software-provided Recents key
* on the navigation or system bar. On devices with the older hardware button configuration
* the recents UI can be accessed with a long press on the Home key.</p>
*
* <p>When crossing from one task stack to another post-Android 3.0,
* the application should synthesize a back stack/history for the new task so that
* the user may navigate out of the new task and back to the Launcher by repeated
* presses of the back key. Back key presses should not navigate across task stacks.</p>
*
* <p>startActivities provides a mechanism for constructing a synthetic task stack of
* multiple activities. If the underlying API is not available on the system this method
* will return false.</p>
*
* @param context Start activities using this activity as the starting context
* @param intents Array of intents defining the activities that will be started. The element
* length-1 will correspond to the top activity on the resulting task stack.
* @return true if the underlying API was available and the call was successful, false otherwise
*/
public static boolean startActivities(Context context, Intent[] intents) {
return startActivities(context, intents, null);
}
/**
* Start a set of activities as a synthesized task stack, if able.
*
* <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for
* app navigation using the back key changed. The back key's behavior is local
* to the current task and does not capture navigation across different tasks.
* Navigating across tasks and easily reaching the previous task is accomplished
* through the "recents" UI, accessible through the software-provided Recents key
* on the navigation or system bar. On devices with the older hardware button configuration
* the recents UI can be accessed with a long press on the Home key.</p>
*
* <p>When crossing from one task stack to another post-Android 3.0,
* the application should synthesize a back stack/history for the new task so that
* the user may navigate out of the new task and back to the Launcher by repeated
* presses of the back key. Back key presses should not navigate across task stacks.</p>
*
* <p>startActivities provides a mechanism for constructing a synthetic task stack of
* multiple activities. If the underlying API is not available on the system this method
* will return false.</p>
*
* @param context Start activities using this activity as the starting context
* @param intents Array of intents defining the activities that will be started. The element
* length-1 will correspond to the top activity on the resulting task stack.
* @param options Additional options for how the Activity should be started.
* See {@link android.content.Context#startActivity(Intent, Bundle)
* @return true if the underlying API was available and the call was successful, false otherwise
*/
public static boolean startActivities(Context context, Intent[] intents,
Bundle options) {
context.startActivities(intents, options);
return true;
}
/**
* Returns absolute paths to application-specific directories on all
* external storage devices where the application's OBB files (if there are
* any) can be found. Note if the application does not have any OBB files,
* these directories may not exist.
* <p>
* This is like {@link Context#getFilesDir()} in that these files will be
* deleted when the application is uninstalled, however there are some
* important differences:
* <ul>
* <li>External files are not always available: they will disappear if the
* user mounts the external storage on a computer or removes it.
* <li>There is no security enforced with these files.
* </ul>
* <p>
* External storage devices returned here are considered a permanent part of
* the device, including both emulated external storage and physical media
* slots, such as SD cards in a battery compartment. The returned paths do
* not include transient devices, such as USB flash drives.
* <p>
* An application may store data on any or all of the returned devices. For
* example, an app may choose to store large files on the device with the
* most available space, as measured by {@link StatFs}.
* <p>
* Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
* are required to write to the returned paths; they're always accessible to
* the calling app. Before then,
* {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to
* write. Write access outside of these paths on secondary external storage
* devices is not available. To request external storage access in a
* backwards compatible way, consider using {@code android:maxSdkVersion}
* like this:
*
* <pre class="prettyprint">&lt;uses-permission
* android:name="android.permission.WRITE_EXTERNAL_STORAGE"
* android:maxSdkVersion="18" /&gt;</pre>
* <p>
* The first path returned is the same as {@link Context#getObbDir()}.
* Returned paths may be {@code null} if a storage device is unavailable.
*
* @see Context#getObbDir()
* @see EnvironmentCompat#getStorageState(File)
*/
public static File[] getObbDirs(Context context) {
return context.getObbDirs();
}
/**
* Returns absolute paths to application-specific directories on all
* external storage devices where the application can place persistent files
* it owns. These files are internal to the application, and not typically
* visible to the user as media.
* <p>
* This is like {@link Context#getFilesDir()} in that these files will be
* deleted when the application is uninstalled, however there are some
* important differences:
* <ul>
* <li>External files are not always available: they will disappear if the
* user mounts the external storage on a computer or removes it.
* <li>There is no security enforced with these files.
* </ul>
* <p>
* External storage devices returned here are considered a permanent part of
* the device, including both emulated external storage and physical media
* slots, such as SD cards in a battery compartment. The returned paths do
* not include transient devices, such as USB flash drives.
* <p>
* An application may store data on any or all of the returned devices. For
* example, an app may choose to store large files on the device with the
* most available space, as measured by {@link StatFs}.
* <p>
* Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
* are required to write to the returned paths; they're always accessible to
* the calling app. Before then,
* {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to
* write. Write access outside of these paths on secondary external storage
* devices is not available. To request external storage access in a
* backwards compatible way, consider using {@code android:maxSdkVersion}
* like this:
*
* <pre class="prettyprint">&lt;uses-permission
* android:name="android.permission.WRITE_EXTERNAL_STORAGE"
* android:maxSdkVersion="18" /&gt;</pre>
* <p>
* The first path returned is the same as
* {@link Context#getExternalFilesDir(String)}. Returned paths may be
* {@code null} if a storage device is unavailable.
*
* @see Context#getExternalFilesDir(String)
* @see EnvironmentCompat#getStorageState(File)
*/
public static File[] getExternalFilesDirs(Context context, String type) {
return context.getExternalFilesDirs(type);
}
/**
* Returns absolute paths to application-specific directories on all
* external storage devices where the application can place cache files it
* owns. These files are internal to the application, and not typically
* visible to the user as media.
* <p>
* This is like {@link Context#getCacheDir()} in that these files will be
* deleted when the application is uninstalled, however there are some
* important differences:
* <ul>
* <li>External files are not always available: they will disappear if the
* user mounts the external storage on a computer or removes it.
* <li>There is no security enforced with these files.
* </ul>
* <p>
* External storage devices returned here are considered a permanent part of
* the device, including both emulated external storage and physical media
* slots, such as SD cards in a battery compartment. The returned paths do
* not include transient devices, such as USB flash drives.
* <p>
* An application may store data on any or all of the returned devices. For
* example, an app may choose to store large files on the device with the
* most available space, as measured by {@link StatFs}.
* <p>
* Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
* are required to write to the returned paths; they're always accessible to
* the calling app. Before then,
* {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to
* write. Write access outside of these paths on secondary external storage
* devices is not available. To request external storage access in a
* backwards compatible way, consider using {@code android:maxSdkVersion}
* like this:
*
* <pre class="prettyprint">&lt;uses-permission
* android:name="android.permission.WRITE_EXTERNAL_STORAGE"
* android:maxSdkVersion="18" /&gt;</pre>
* <p>
* The first path returned is the same as
* {@link Context#getExternalCacheDir()}. Returned paths may be {@code null}
* if a storage device is unavailable.
*
* @see Context#getExternalCacheDir()
* @see EnvironmentCompat#getStorageState(File)
*/
public static File[] getExternalCacheDirs(Context context) {
return context.getExternalCacheDirs();
}
/**
* Return a drawable object associated with a particular resource ID.
* <p>
* Starting in {@link android.os.Build.VERSION_CODES#LOLLIPOP}, the returned
* drawable will be styled for the specified Context's theme.
*
* @param id The desired resource identifier, as generated by the aapt tool.
* This integer encodes the package, type, and resource entry.
* The value 0 is an invalid identifier.
* @return Drawable An object that can be used to draw this resource.
*/
public static final Drawable getDrawable(Context context, int id) {
return context.getDrawable(id);
}
/**
* Returns the absolute path to the directory on the filesystem similar to
* {@link Context#getFilesDir()}. The difference is that files placed under this
* directory will be excluded from automatic backup to remote storage on
* devices running {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later. See
* {@link android.app.backup.BackupAgent BackupAgent} for a full discussion
* of the automatic backup mechanism in Android.
*
* <p>No permissions are required to read or write to the returned path, since this
* path is internal storage.
*
* @return The path of the directory holding application files that will not be
* automatically backed up to remote storage.
*
* @see android.content.Context.getFilesDir
*/
public final File getNoBackupFilesDir(Context context) {
return context.getNoBackupFilesDir();
}
/**
* Returns the absolute path to the application specific cache directory on
* the filesystem designed for storing cached code. On devices running
* {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later, the system will delete
* any files stored in this location both when your specific application is
* upgraded, and when the entire platform is upgraded.
* <p>
* This location is optimal for storing compiled or optimized code generated
* by your application at runtime.
* <p>
* Apps require no extra permissions to read or write to the returned path,
* since this path lives in their private storage.
*
* @return The path of the directory holding application code cache files.
*/
public final File getCodeCacheDir(Context context) {
return context.getCodeCacheDir();
}
}
@@ -1,53 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.support.v4.os;
import android.os.Environment;
import java.io.File;
/**
* Helper for accessing features in {@link Environment} introduced after API
* level 4 in a backwards compatible fashion.
*/
public class EnvironmentCompat {
/**
* Unknown storage state, such as when a path isn't backed by known storage
* media.
*
* @see #getStorageState(File)
*/
public static final String MEDIA_UNKNOWN = "unknown";
/**
* Returns the current state of the storage device that provides the given
* path.
*
* @return one of {@link #MEDIA_UNKNOWN}, {@link Environment#MEDIA_REMOVED},
* {@link Environment#MEDIA_UNMOUNTED},
* {@link Environment#MEDIA_CHECKING},
* {@link Environment#MEDIA_NOFS},
* {@link Environment#MEDIA_MOUNTED},
* {@link Environment#MEDIA_MOUNTED_READ_ONLY},
* {@link Environment#MEDIA_SHARED},
* {@link Environment#MEDIA_BAD_REMOVAL}, or
* {@link Environment#MEDIA_UNMOUNTABLE}.
*/
public static String getStorageState(File path) {
return Environment.getStorageState(path);
}
}
@@ -1,193 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.support.v7.preference;
import android.support.annotation.Nullable;
import java.util.Set;
/**
* A data store interface to be implemented and provided to the Preferences framework. This can be
* used to replace the default {@link android.content.SharedPreferences}, if needed.
*
* <p>In most cases you want to use {@link android.content.SharedPreferences} as it is automatically
* backed up and migrated to new devices. However, providing custom data store to preferences can be
* useful if your app stores its preferences in a local db, cloud or they are device specific like
* "Developer settings". It might be also useful when you want to use the preferences UI but
* the data are not supposed to be stored at all because they are valid per session only.
*
* <p>Once a put method is called it is full responsibility of the data store implementation to
* safely store the given values. Time expensive operations need to be done in the background to
* prevent from blocking the UI. You also need to have a plan on how to serialize the data in case
* the activity holding this object gets destroyed.
*
* <p>By default, all "put" methods throw {@link UnsupportedOperationException}.
*/
public abstract class PreferenceDataStore {
/**
* Sets a {@link String} value to the data store.
*
* <p>Once the value is set the data store is responsible for holding it.
*
* @param key the name of the preference to modify
* @param value the new value for the preference
* @see #getString(String, String)
*/
public void putString(String key, @Nullable String value) {
throw new UnsupportedOperationException("Not implemented on this data store");
}
/**
* Sets a set of Strings to the data store.
*
* <p>Once the value is set the data store is responsible for holding it.
*
* @param key the name of the preference to modify
* @param values the set of new values for the preference
* @see #getStringSet(String, Set<String>)
*/
public void putStringSet(String key, @Nullable Set<String> values) {
throw new UnsupportedOperationException("Not implemented on this data store");
}
/**
* Sets an {@link Integer} value to the data store.
*
* <p>Once the value is set the data store is responsible for holding it.
*
* @param key the name of the preference to modify
* @param value the new value for the preference
* @see #getInt(String, int)
*/
public void putInt(String key, int value) {
throw new UnsupportedOperationException("Not implemented on this data store");
}
/**
* Sets a {@link Long} value to the data store.
*
* <p>Once the value is set the data store is responsible for holding it.
*
* @param key the name of the preference to modify
* @param value the new value for the preference
* @see #getLong(String, long)
*/
public void putLong(String key, long value) {
throw new UnsupportedOperationException("Not implemented on this data store");
}
/**
* Sets a {@link Float} value to the data store.
*
* <p>Once the value is set the data store is responsible for holding it.
*
* @param key the name of the preference to modify
* @param value the new value for the preference
* @see #getFloat(String, float)
*/
public void putFloat(String key, float value) {
throw new UnsupportedOperationException("Not implemented on this data store");
}
/**
* Sets a {@link Boolean} value to the data store.
*
* <p>Once the value is set the data store is responsible for holding it.
*
* @param key the name of the preference to modify
* @param value the new value for the preference
* @see #getBoolean(String, boolean)
*/
public void putBoolean(String key, boolean value) {
throw new UnsupportedOperationException("Not implemented on this data store");
}
/**
* Retrieves a {@link String} value from the data store.
*
* @param key the name of the preference to retrieve
* @param defValue value to return if this preference does not exist in the storage
* @return the value from the data store or the default return value
* @see #putString(String, String)
*/
@Nullable
public String getString(String key, @Nullable String defValue) {
return defValue;
}
/**
* Retrieves a set of Strings from the data store.
*
* @param key the name of the preference to retrieve
* @param defValues values to return if this preference does not exist in the storage
* @return the values from the data store or the default return values
* @see #putStringSet(String, Set<String>)
*/
@Nullable
public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
return defValues;
}
/**
* Retrieves an {@link Integer} value from the data store.
*
* @param key the name of the preference to retrieve
* @param defValue value to return if this preference does not exist in the storage
* @return the value from the data store or the default return value
* @see #putInt(String, int)
*/
public int getInt(String key, int defValue) {
return defValue;
}
/**
* Retrieves a {@link Long} value from the data store.
*
* @param key the name of the preference to retrieve
* @param defValue value to return if this preference does not exist in the storage
* @return the value from the data store or the default return value
* @see #putLong(String, long)
*/
public long getLong(String key, long defValue) {
return defValue;
}
/**
* Retrieves a {@link Float} value from the data store.
*
* @param key the name of the preference to retrieve
* @param defValue value to return if this preference does not exist in the storage
* @return the value from the data store or the default return value
* @see #putFloat(String, float)
*/
public float getFloat(String key, float defValue) {
return defValue;
}
/**
* Retrieves a {@link Boolean} value from the data store.
*
* @param key the name of the preference to retrieve
* @param defValue value to return if this preference does not exist in the storage
* @return the value from the data store or the default return value
* @see #getBoolean(String, boolean)
*/
public boolean getBoolean(String key, boolean defValue) {
return defValue;
}
}
@@ -1,4 +0,0 @@
package android.support.v7.preference;
public class PreferenceScreen {
}
@@ -4,7 +4,7 @@ package android.text;
import android.graphics.drawable.Drawable;
import org.jetbrains.annotations.NotNull;
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;
import org.jsoup.safety.Safelist;
import org.xml.sax.XMLReader;
/**
@@ -18,7 +18,7 @@ import org.xml.sax.XMLReader;
public class Html {
public static Spanned fromHtml(String source) {
return new FakeSpanned(Jsoup.clean(source, Whitelist.none()));
return new FakeSpanned(Jsoup.clean(source, Safelist.none()));
}
public static Spanned fromHtml(String source, Html.ImageGetter imageGetter, Html.TagHandler tagHandler) {
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,631 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.text;
import android.annotation.ColorInt;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.Log;
import java.awt.RenderingHints;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedString;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
public class StaticLayout extends Layout {
/*
* The break iteration is done in native code. The protocol for using the native code is as
* follows.
*
* First, call nInit to setup native line breaker object. Then, for each paragraph, do the
* following:
*
* - Create MeasuredParagraph by MeasuredParagraph.buildForStaticLayout which measures in
* native.
* - Run LineBreaker.computeLineBreaks() to obtain line breaks for the paragraph.
*
* After all paragraphs, call finish() to release expensive buffers.
*/
static final String TAG = "StaticLayout";
public final static class Builder {
private Builder() {}
@NonNull
public static Builder obtain(@NonNull CharSequence source, @IntRange(from = 0) int start,
@IntRange(from = 0) int end, @NonNull TextPaint paint,
@IntRange(from = 0) int width) {
Builder b = new Builder();
// set default initial values
b.mText = source;
b.mStart = start;
b.mEnd = end;
b.mPaint = paint;
b.mWidth = width;
b.mAlignment = Alignment.ALIGN_NORMAL;
b.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
b.mSpacingMult = DEFAULT_LINESPACING_MULTIPLIER;
b.mSpacingAdd = DEFAULT_LINESPACING_ADDITION;
b.mIncludePad = true;
b.mFallbackLineSpacing = false;
b.mEllipsizedWidth = width;
b.mEllipsize = null;
b.mMaxLines = Integer.MAX_VALUE;
b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
b.mMinimumFontMetrics = null;
return b;
}
// release any expensive state
/* package */ void finish() {
mText = null;
mPaint = null;
mLeftIndents = null;
mRightIndents = null;
mMinimumFontMetrics = null;
}
public Builder setText(CharSequence source) {
return setText(source, 0, source.length());
}
@NonNull
public Builder setText(@NonNull CharSequence source, int start, int end) {
mText = source;
mStart = start;
mEnd = end;
return this;
}
@NonNull
public Builder setPaint(@NonNull TextPaint paint) {
mPaint = paint;
return this;
}
@NonNull
public Builder setWidth(@IntRange(from = 0) int width) {
mWidth = width;
if (mEllipsize == null) {
mEllipsizedWidth = width;
}
return this;
}
@NonNull
public Builder setAlignment(@NonNull Alignment alignment) {
mAlignment = alignment;
return this;
}
@NonNull
public Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
mTextDir = textDir;
return this;
}
@NonNull
public Builder setLineSpacing(float spacingAdd, float spacingMult) {
mSpacingAdd = spacingAdd;
mSpacingMult = spacingMult;
return this;
}
@NonNull
public Builder setIncludePad(boolean includePad) {
mIncludePad = includePad;
return this;
}
@NonNull
public Builder setUseLineSpacingFromFallbacks(boolean useLineSpacingFromFallbacks) {
mFallbackLineSpacing = useLineSpacingFromFallbacks;
return this;
}
@NonNull
public Builder setEllipsizedWidth(@IntRange(from = 0) int ellipsizedWidth) {
mEllipsizedWidth = ellipsizedWidth;
return this;
}
@NonNull
public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) {
mEllipsize = ellipsize;
return this;
}
@NonNull
public Builder setMaxLines(@IntRange(from = 0) int maxLines) {
mMaxLines = maxLines;
return this;
}
@NonNull
public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
mBreakStrategy = breakStrategy;
return this;
}
@NonNull
public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) {
mHyphenationFrequency = hyphenationFrequency;
return this;
}
@NonNull
public Builder setIndents(@Nullable int[] leftIndents, @Nullable int[] rightIndents) {
mLeftIndents = leftIndents;
mRightIndents = rightIndents;
return this;
}
@NonNull
public Builder setJustificationMode(@JustificationMode int justificationMode) {
mJustificationMode = justificationMode;
return this;
}
@NonNull
/* package */ Builder setAddLastLineLineSpacing(boolean value) {
mAddLastLineLineSpacing = value;
return this;
}
@SuppressLint("MissingGetterMatchingBuilder") // The base class `Layout` has a getter.
@NonNull
public Builder setUseBoundsForWidth(boolean useBoundsForWidth) {
mUseBoundsForWidth = useBoundsForWidth;
return this;
}
@NonNull
// The corresponding getter is getShiftDrawingOffsetForStartOverhang()
@SuppressLint("MissingGetterMatchingBuilder")
public Builder setShiftDrawingOffsetForStartOverhang(
boolean shiftDrawingOffsetForStartOverhang) {
mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang;
return this;
}
public Builder setCalculateBounds(boolean value) {
mCalculateBounds = value;
return this;
}
@NonNull
public Builder setMinimumFontMetrics(@Nullable Paint.FontMetrics minimumFontMetrics) {
mMinimumFontMetrics = minimumFontMetrics;
return this;
}
@NonNull
public StaticLayout build() {
StaticLayout result = new StaticLayout(this, mIncludePad, mEllipsize != null
? COLUMNS_ELLIPSIZE : COLUMNS_NORMAL);
return result;
}
private CharSequence mText;
private int mStart;
private int mEnd;
private TextPaint mPaint;
private int mWidth;
private Alignment mAlignment;
private TextDirectionHeuristic mTextDir;
private float mSpacingMult;
private float mSpacingAdd;
private boolean mIncludePad;
private boolean mFallbackLineSpacing;
private int mEllipsizedWidth;
private TextUtils.TruncateAt mEllipsize;
private int mMaxLines;
private int mBreakStrategy;
private int mHyphenationFrequency;
@Nullable private int[] mLeftIndents;
@Nullable private int[] mRightIndents;
private int mJustificationMode;
private boolean mAddLastLineLineSpacing;
private boolean mUseBoundsForWidth;
private boolean mShiftDrawingOffsetForStartOverhang;
private boolean mCalculateBounds;
@Nullable private Paint.FontMetrics mMinimumFontMetrics;
private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
}
private StaticLayout() {
super(
null, // text
null, // paint
0, // width
null, // alignment
null, // textDir
1, // spacing multiplier
0, // spacing amount
false, // include font padding
false, // fallback line spacing
0, // ellipsized width
null, // ellipsize
1, // maxLines
BREAK_STRATEGY_SIMPLE,
HYPHENATION_FREQUENCY_NONE,
null, // leftIndents
null, // rightIndents
JUSTIFICATION_MODE_NONE,
false, // useBoundsForWidth
false, // shiftDrawingOffsetForStartOverhang
null // minimumFontMetrics
);
mColumns = COLUMNS_ELLIPSIZE;
mLineDirections = new Directions[2];
mLines = new int[2 * mColumns];
}
@Deprecated
public StaticLayout(CharSequence source, TextPaint paint,
int width,
Alignment align, float spacingmult, float spacingadd,
boolean includepad) {
this(source, 0, source.length(), paint, width, align,
spacingmult, spacingadd, includepad);
}
@Deprecated
public StaticLayout(CharSequence source, int bufstart, int bufend,
TextPaint paint, int outerwidth,
Alignment align,
float spacingmult, float spacingadd,
boolean includepad) {
this(source, bufstart, bufend, paint, outerwidth, align,
spacingmult, spacingadd, includepad, null, 0);
}
@Deprecated
public StaticLayout(CharSequence source, int bufstart, int bufend,
TextPaint paint, int outerwidth,
Alignment align,
float spacingmult, float spacingadd,
boolean includepad,
TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
this(source, bufstart, bufend, paint, outerwidth, align,
TextDirectionHeuristics.FIRSTSTRONG_LTR,
spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, Integer.MAX_VALUE);
}
@Deprecated
public StaticLayout(CharSequence source, int bufstart, int bufend,
TextPaint paint, int outerwidth,
Alignment align, TextDirectionHeuristic textDir,
float spacingmult, float spacingadd,
boolean includepad,
TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) {
this(Builder.obtain(source, bufstart, bufend, paint, outerwidth)
.setAlignment(align)
.setTextDirection(textDir)
.setLineSpacing(spacingadd, spacingmult)
.setIncludePad(includepad)
.setEllipsize(ellipsize)
.setEllipsizedWidth(ellipsizedWidth)
.setMaxLines(maxLines), includepad,
ellipsize != null ? COLUMNS_ELLIPSIZE : COLUMNS_NORMAL);
}
private StaticLayout(Builder b, boolean trackPadding, int columnSize) {
super((b.mEllipsize == null) ? b.mText : (b.mText instanceof Spanned)
? new SpannedEllipsizer(b.mText) : new Ellipsizer(b.mText),
b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd,
b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize,
b.mMaxLines, b.mBreakStrategy, b.mHyphenationFrequency, b.mLeftIndents,
b.mRightIndents, b.mJustificationMode, b.mUseBoundsForWidth,
b.mShiftDrawingOffsetForStartOverhang, b.mMinimumFontMetrics);
mColumns = columnSize;
if (b.mEllipsize != null) {
Ellipsizer e = (Ellipsizer) getText();
e.mLayout = this;
e.mWidth = b.mEllipsizedWidth;
e.mMethod = b.mEllipsize;
throw new UnsupportedOperationException("Ellipsis not supported");
}
mLineDirections = new Directions[2];
mLines = new int[2 * mColumns];
mMaximumVisibleLineCount = b.mMaxLines;
mLeftIndents = b.mLeftIndents;
mRightIndents = b.mRightIndents;
String str = b.mText.subSequence(b.mStart, b.mEnd).toString();
AttributedString text = new AttributedString(str);
text.addAttribute(TextAttribute.FONT, getPaint().getFont());
FontRenderContext frc = new FontRenderContext(getPaint().getFont().getTransform(), RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT, RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT);
LineBreakMeasurer measurer = new LineBreakMeasurer(text.getIterator(), frc);
// TODO: directions
float y = 0;
while (measurer.getPosition() < str.length()) {
int off = mLineCount * mColumns;
int want = off + mColumns + TOP;
if (want >= mLines.length) {
final int[] grow = ArrayUtils.newUnpaddedIntArray(GrowingArrayUtils.growSize(want));
System.arraycopy(mLines, 0, grow, 0, mLines.length);
mLines = grow;
}
int pos = measurer.getPosition();
TextLayout l = measurer.nextLayout(getWidth());
mLines[off + START] = pos;
mLines[off + TOP] = (int) y;
mLines[off + DESCENT] = (int) (l.getDescent() + l.getLeading());
mLines[off + EXTRA] = (int) l.getLeading();
mLines[off + DIR] |= Layout.DIR_LEFT_TO_RIGHT << DIR_SHIFT;
y += l.getAscent();
y += l.getDescent() + l.getLeading();
mLines[off + mColumns + START] = measurer.getPosition();
mLines[off + mColumns + TOP] = (int) y;
mLineCount += 1;
}
}
// Override the base class so we can directly access our members,
// rather than relying on member functions.
// The logic mirrors that of Layout.getLineForVertical
// FIXME: It may be faster to do a linear search for layouts without many lines.
@Override
public int getLineForVertical(int vertical) {
int high = mLineCount;
int low = -1;
int guess;
int[] lines = mLines;
while (high - low > 1) {
guess = (high + low) >> 1;
if (lines[mColumns * guess + TOP] > vertical){
high = guess;
} else {
low = guess;
}
}
if (low < 0) {
return 0;
} else {
return low;
}
}
@Override
public int getLineCount() {
return mLineCount;
}
@Override
public int getLineTop(int line) {
return mLines[mColumns * line + TOP];
}
@Override
public int getLineExtra(int line) {
return mLines[mColumns * line + EXTRA];
}
@Override
public int getLineDescent(int line) {
return mLines[mColumns * line + DESCENT];
}
@Override
public int getLineStart(int line) {
return mLines[mColumns * line + START] & START_MASK;
}
@Override
public int getParagraphDirection(int line) {
return mLines[mColumns * line + DIR] >> DIR_SHIFT;
}
@Override
public boolean getLineContainsTab(int line) {
return (mLines[mColumns * line + TAB] & TAB_MASK) != 0;
}
@Override
public final Directions getLineDirections(int line) {
if (line > getLineCount()) {
throw new ArrayIndexOutOfBoundsException();
}
return new Directions(null);
// return mLineDirections[line];
}
@Override
public int getTopPadding() {
return mTopPadding;
}
@Override
public int getBottomPadding() {
return mBottomPadding;
}
// To store into single int field, pack the pair of start and end hyphen edit.
static int packHyphenEdit(
@Paint.StartHyphenEdit int start, @Paint.EndHyphenEdit int end) {
return start << START_HYPHEN_BITS_SHIFT | end;
}
static int unpackStartHyphenEdit(int packedHyphenEdit) {
return (packedHyphenEdit & START_HYPHEN_MASK) >> START_HYPHEN_BITS_SHIFT;
}
static int unpackEndHyphenEdit(int packedHyphenEdit) {
return packedHyphenEdit & END_HYPHEN_MASK;
}
@Override
public @Paint.StartHyphenEdit int getStartHyphenEdit(int lineNumber) {
return unpackStartHyphenEdit(mLines[mColumns * lineNumber + HYPHEN] & HYPHEN_MASK);
}
@Override
public @Paint.EndHyphenEdit int getEndHyphenEdit(int lineNumber) {
return unpackEndHyphenEdit(mLines[mColumns * lineNumber + HYPHEN] & HYPHEN_MASK);
}
@Override
public int getIndentAdjust(int line, Alignment align) {
if (align == Alignment.ALIGN_LEFT) {
if (mLeftIndents == null) {
return 0;
} else {
return mLeftIndents[Math.min(line, mLeftIndents.length - 1)];
}
} else if (align == Alignment.ALIGN_RIGHT) {
if (mRightIndents == null) {
return 0;
} else {
return -mRightIndents[Math.min(line, mRightIndents.length - 1)];
}
} else if (align == Alignment.ALIGN_CENTER) {
int left = 0;
if (mLeftIndents != null) {
left = mLeftIndents[Math.min(line, mLeftIndents.length - 1)];
}
int right = 0;
if (mRightIndents != null) {
right = mRightIndents[Math.min(line, mRightIndents.length - 1)];
}
return (left - right) >> 1;
} else {
throw new AssertionError("unhandled alignment " + align);
}
}
@Override
public int getEllipsisCount(int line) {
if (mColumns < COLUMNS_ELLIPSIZE) {
return 0;
}
return mLines[mColumns * line + ELLIPSIS_COUNT];
}
@Override
public int getEllipsisStart(int line) {
if (mColumns < COLUMNS_ELLIPSIZE) {
return 0;
}
return mLines[mColumns * line + ELLIPSIS_START];
}
@Override
@NonNull
public RectF computeDrawingBoundingBox() {
// Cache the drawing bounds result because it does not change after created.
if (mDrawingBounds == null) {
mDrawingBounds = super.computeDrawingBoundingBox();
}
return mDrawingBounds;
}
@Override
public int getHeight(boolean cap) {
if (cap && mLineCount > mMaximumVisibleLineCount && mMaxLineHeight == -1
&& Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "maxLineHeight should not be -1. "
+ " maxLines:" + mMaximumVisibleLineCount
+ " lineCount:" + mLineCount);
}
return cap && mLineCount > mMaximumVisibleLineCount && mMaxLineHeight != -1
? mMaxLineHeight : super.getHeight();
}
private int mLineCount;
private int mTopPadding, mBottomPadding;
private int mColumns;
private RectF mDrawingBounds = null; // lazy calculation.
private boolean mEllipsized;
private int mMaxLineHeight = DEFAULT_MAX_LINE_HEIGHT;
private static final int COLUMNS_NORMAL = 5;
private static final int COLUMNS_ELLIPSIZE = 7;
private static final int START = 0;
private static final int DIR = START;
private static final int TAB = START;
private static final int TOP = 1;
private static final int DESCENT = 2;
private static final int EXTRA = 3;
private static final int HYPHEN = 4;
private static final int ELLIPSIS_START = 5;
private static final int ELLIPSIS_COUNT = 6;
private int[] mLines;
private Directions[] mLineDirections;
private int mMaximumVisibleLineCount = Integer.MAX_VALUE;
private static final int START_MASK = 0x1FFFFFFF;
private static final int DIR_SHIFT = 30;
private static final int TAB_MASK = 0x20000000;
private static final int HYPHEN_MASK = 0xFF;
private static final int START_HYPHEN_BITS_SHIFT = 3;
private static final int START_HYPHEN_MASK = 0x18; // 0b11000
private static final int END_HYPHEN_MASK = 0x7; // 0b00111
private static final float TAB_INCREMENT = 20; // same as Layout, but that's private
private static final char CHAR_NEW_LINE = '\n';
private static final double EXTRA_ROUNDING = 0.5;
private static final int DEFAULT_MAX_LINE_HEIGHT = -1;
// Unused, here because of gray list private API accesses.
/*package*/ static class LineBreaks {
private static final int INITIAL_SIZE = 16;
public int[] breaks = new int[INITIAL_SIZE];
public float[] widths = new float[INITIAL_SIZE];
public float[] ascents = new float[INITIAL_SIZE];
public float[] descents = new float[INITIAL_SIZE];
public int[] flags = new int[INITIAL_SIZE]; // hasTab
// breaks, widths, and flags should all have the same length
}
@Nullable private int[] mLeftIndents;
@Nullable private int[] mRightIndents;
}
@@ -0,0 +1,178 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.text;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.Layout.Directions;
import android.text.Layout.TabStops;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
public class TextLine {
private TextPaint mPaint;
private CharSequence mText;
private int mStart;
private int mLen;
private int mDir;
private Directions mDirections;
private boolean mHasTabs;
private TabStops mTabs;
private char[] mChars;
private boolean mCharsValid;
private Spanned mSpanned;
private PrecomputedText mComputed;
private RectF mTmpRectForMeasure;
private RectF mTmpRectForPaintAPI;
private Rect mTmpRectForPrecompute;
public static final class LineInfo {
private int mClusterCount;
public int getClusterCount() {
return mClusterCount;
}
public void setClusterCount(int clusterCount) {
mClusterCount = clusterCount;
}
};
public float getAddedWordSpacingInPx() {
throw new RuntimeException("Stub!");
}
public float getAddedLetterSpacingInPx() {
throw new RuntimeException("Stub!");
}
public boolean isJustifying() {
throw new RuntimeException("Stub!");
}
/** Not allowed to access. If it's for memory leak workaround, it was already fixed M. */
private static final TextLine[] sCached = new TextLine[3];
public static TextLine obtain() {
TextLine tl;
synchronized (sCached) {
for (int i = sCached.length; --i >= 0;) {
if (sCached[i] != null) {
tl = sCached[i];
sCached[i] = null;
return tl;
}
}
}
tl = new TextLine();
return tl;
}
public static TextLine recycle(TextLine tl) {
synchronized(sCached) {
for (int i = 0; i < sCached.length; ++i) {
if (sCached[i] == null) {
sCached[i] = tl;
break;
}
}
}
return null;
}
public void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
Directions directions, boolean hasTabs, TabStops tabStops,
int ellipsisStart, int ellipsisEnd, boolean useFallbackLineSpacing) {
mPaint = paint;
mText = text;
mStart = start;
mLen = limit - start;
mDir = dir;
mDirections = directions;
if (mDirections == null) {
throw new IllegalArgumentException("Directions cannot be null");
}
mHasTabs = hasTabs;
mSpanned = null;
if (text instanceof Spanned) {
mSpanned = (Spanned) text;
}
mComputed = null;
if (text instanceof PrecomputedText) {
// Here, no need to check line break strategy or hyphenation frequency since there is no
// line break concept here.
mComputed = (PrecomputedText) text;
if (!mComputed.getParams().getTextPaint().equalsForTextMeasurement(paint)) {
mComputed = null;
}
}
mTabs = tabStops;
}
public void justify(@Layout.JustificationMode int justificationMode, float justifyWidth) {
throw new RuntimeException("Stub!");
}
public static int calculateRunFlag(int bidiRunIndex, int bidiRunCount, int lineDirection) {
throw new RuntimeException("Stub!");
}
public static int resolveRunFlagForSubSequence(int runFlag, boolean isRtlRun, int runStart,
int runEnd, int spanStart, int spanEnd) {
throw new RuntimeException("Stub!");
}
public float metrics(FontMetricsInt fmi, @Nullable RectF drawBounds, boolean returnDrawWidth,
@Nullable LineInfo lineInfo) {
FontRenderContext frc = new FontRenderContext(null, RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT, RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT);
return (float) mPaint.getFont().getStringBounds(mText.toString(), mStart, mStart + mLen, frc).getWidth();
}
public float measure(@IntRange(from = 0) int offset, boolean trailing,
@NonNull FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable LineInfo lineInfo) {
throw new RuntimeException("Stub!");
}
public void measureAllBounds(@NonNull float[] bounds, @Nullable float[] advances) {
throw new RuntimeException("Stub!");
}
public float[] measureAllOffsets(boolean[] trailing, FontMetricsInt fmi) {
throw new RuntimeException("Stub!");
}
// Note: keep this in sync with Minikin LineBreaker::isLineEndSpace()
public static boolean isLineEndSpace(char ch) {
return ch == ' ' || ch == '\t' || ch == 0x1680
|| (0x2000 <= ch && ch <= 0x200A && ch != 0x2007)
|| ch == 0x205F || ch == 0x3000;
}
void draw(Canvas c, float x, int top, int y, int bottom) {
c.drawText(mText, mStart, mStart + mLen, x, y, mPaint);
}
}
@@ -0,0 +1,74 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.text;
import android.annotation.ColorInt;
import android.graphics.Paint;
public class TextPaint extends Paint {
// Special value 0 means no background paint
@ColorInt
public int bgColor;
public int baselineShift;
@ColorInt
public int linkColor;
public int[] drawableState;
public float density = 1.0f;
@ColorInt
public int underlineColor = 0;
public float underlineThickness;
public TextPaint() {
super();
}
public TextPaint(int flags) {
super(flags);
}
public TextPaint(Paint p) {
super(p);
}
public void set(TextPaint tp) {
super.set(tp);
bgColor = tp.bgColor;
baselineShift = tp.baselineShift;
linkColor = tp.linkColor;
drawableState = tp.drawableState;
density = tp.density;
underlineColor = tp.underlineColor;
underlineThickness = tp.underlineThickness;
}
public void setUnderlineText(int color, float thickness) {
underlineColor = color;
underlineThickness = thickness;
}
@Override
public float getUnderlineThickness() {
if (underlineColor != 0) { // Return custom thickness only if underline color is set.
return underlineThickness;
} else {
return super.getUnderlineThickness();
}
}
}
@@ -136,4 +136,4 @@ public final class Log {
first.append(msg);
return first.toString();
}
}
}
@@ -0,0 +1,270 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.util;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Build;
/**
* API for sending log output to the {@link Log#LOG_ID_SYSTEM} buffer.
*
* <p>Should be used by system components. Use {@code adb logcat --buffer=system} to fetch the logs.
*
* @see Log
* @hide
*/
public final class Slog {
private Slog() {
}
/**
* Logs {@code msg} at {@link Log#VERBOSE} level.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
*
* @see Log#v(String, String)
*/
public static int v(@Nullable String tag, @NonNull String msg) {
return Log.println(Log.VERBOSE, tag, msg);
}
/**
* Logs {@code msg} at {@link Log#VERBOSE} level, attaching stack trace of the {@code tr} to
* the end of the log statement.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
* @param tr an exception to log.
*
* @see Log#v(String, String, Throwable)
*/
public static int v(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) {
return Log.println(Log.VERBOSE, tag,
msg + '\n' + Log.getStackTraceString(tr));
}
/**
* Logs {@code msg} at {@link Log#DEBUG} level.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
*
* @see Log#d(String, String)
*/
public static int d(@Nullable String tag, @NonNull String msg) {
return Log.println(Log.DEBUG, tag, msg);
}
/**
* Logs {@code msg} at {@link Log#DEBUG} level, attaching stack trace of the {@code tr} to
* the end of the log statement.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
* @param tr an exception to log.
*
* @see Log#d(String, String, Throwable)
*/
public static int d(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) {
return Log.println(Log.DEBUG, tag,
msg + '\n' + Log.getStackTraceString(tr));
}
/**
* Logs {@code msg} at {@link Log#INFO} level.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
*
* @see Log#i(String, String)
*/
public static int i(@Nullable String tag, @NonNull String msg) {
return Log.println(Log.INFO, tag, msg);
}
/**
* Logs {@code msg} at {@link Log#INFO} level, attaching stack trace of the {@code tr} to
* the end of the log statement.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
* @param tr an exception to log.
*
* @see Log#i(String, String, Throwable)
*/
public static int i(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) {
return Log.println(Log.INFO, tag,
msg + '\n' + Log.getStackTraceString(tr));
}
/**
* Logs {@code msg} at {@link Log#WARN} level.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
*
* @see Log#w(String, String)
*/
public static int w(@Nullable String tag, @NonNull String msg) {
return Log.println(Log.WARN, tag, msg);
}
/**
* Logs {@code msg} at {@link Log#WARN} level, attaching stack trace of the {@code tr} to
* the end of the log statement.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
* @param tr an exception to log.
*
* @see Log#w(String, String, Throwable)
*/
public static int w(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) {
return Log.println(Log.WARN, tag,
msg + '\n' + Log.getStackTraceString(tr));
}
/**
* Logs stack trace of {@code tr} at {@link Log#WARN} level.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param tr an exception to log.
*
* @see Log#w(String, Throwable)
*/
public static int w(@Nullable String tag, @Nullable Throwable tr) {
return Log.println(Log.WARN, tag, Log.getStackTraceString(tr));
}
/**
* Logs {@code msg} at {@link Log#ERROR} level.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
*
* @see Log#e(String, String)
*/
public static int e(@Nullable String tag, @NonNull String msg) {
return Log.println(Log.ERROR, tag, msg);
}
/**
* Logs {@code msg} at {@link Log#ERROR} level, attaching stack trace of the {@code tr} to
* the end of the log statement.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
* @param tr an exception to log.
*
* @see Log#e(String, String, Throwable)
*/
public static int e(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) {
return Log.println(Log.ERROR, tag,
msg + '\n' + Log.getStackTraceString(tr));
}
/**
* Logs a condition that should never happen.
*
* <p>
* Similar to {@link Log#wtf(String, String)}, but will never cause the caller to crash, and
* will always be handled asynchronously. Primarily to be used by the system server.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
*
* @see Log#wtf(String, String)
*/
public static int wtf(@Nullable String tag, @NonNull String msg) {
return Log.wtf(tag, msg, null);
}
/**
* Logs a condition that should never happen, attaching the full call stack to the log.
*
* <p>
* Similar to {@link Log#wtfStack(String, String)}, but will never cause the caller to crash,
* and will always be handled asynchronously. Primarily to be used by the system server.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
*
* @see Log#wtfStack(String, String)
*/
public static int wtfStack(@Nullable String tag, @NonNull String msg) {
return Log.wtf(tag, msg, null);
}
/**
* Logs a condition that should never happen, attaching stack trace of the {@code tr} to the
* end of the log statement.
*
* <p>
* Similar to {@link Log#wtf(String, Throwable)}, but will never cause the caller to crash,
* and will always be handled asynchronously. Primarily to be used by the system server.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param tr an exception to log.
*
* @see Log#wtf(String, Throwable)
*/
public static int wtf(@Nullable String tag, @Nullable Throwable tr) {
return Log.wtf(tag, tr.getMessage(), tr);
}
/**
* Logs a condition that should never happen, attaching stack trace of the {@code tr} to the
* end of the log statement.
*
* <p>
* Similar to {@link Log#wtf(String, String, Throwable)}, but will never cause the caller to
* crash, and will always be handled asynchronously. Primarily to be used by the system server.
*
* @param tag identifies the source of a log message. It usually represents system service,
* e.g. {@code PackageManager}.
* @param msg the message to log.
* @param tr an exception to log.
*
* @see Log#wtf(String, String, Throwable)
*/
public static int wtf(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) {
return Log.wtf(tag, msg, tr);
}
/** @hide */
public static int println(int priority, @Nullable String tag, @NonNull String msg) {
return Log.println(priority, tag, msg);
}
}
@@ -0,0 +1,371 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.util;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Build;
import android.os.SystemClock;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* A class containing utility methods related to time zones.
*/
public class TimeUtils {
/** @hide */ public TimeUtils() {}
/** {@hide} */
private static final SimpleDateFormat sLoggingFormat =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/** @hide */
public static final SimpleDateFormat sDumpDateFormat =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
/**
* This timestamp is used in TimeUtils methods and by the SettingsUI to filter time zones
* to only "effective" ones in a country. It is compared against the notUsedAfter metadata that
* Android records for some time zones.
*
* <p>What is notUsedAfter?</p>
* Android chooses to avoid making users choose between functionally identical time zones at the
* expense of not being able to represent local times in the past.
*
* notUsedAfter exists because some time zones can "merge" with other time zones after a given
* point in time (i.e. they change to have identical transitions, offsets, display names, etc.).
* From the notUsedAfter time, the zone will express the same local time as the one it merged
* with.
*
* <p>Why hardcoded?</p>
* Rather than using System.currentTimeMillis(), a timestamp known to be in the recent past is
* used to ensure consistent behavior across devices and time, and avoid assumptions that the
* system clock on a device is currently set correctly. The fixed value should be updated
* occasionally, but it doesn't have to be very often as effective time zones for a country
* don't change very often.
*
* @hide
*/
public static final Instant MIN_USE_DATE_OF_TIMEZONE =
Instant.ofEpochMilli(1546300800000L); // 1/1/2019 00:00 UTC
/** @hide Field length that can hold 999 days of time */
public static final int HUNDRED_DAY_FIELD_LEN = 19;
private static final int SECONDS_PER_MINUTE = 60;
private static final int SECONDS_PER_HOUR = 60 * 60;
private static final int SECONDS_PER_DAY = 24 * 60 * 60;
/** @hide */
public static final long NANOS_PER_MS = 1000000;
private static final Object sFormatSync = new Object();
private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10];
private static char[] sTmpFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10];
static private int accumField(int amt, int suffix, boolean always, int zeropad) {
if (amt > 999) {
int num = 0;
while (amt != 0) {
num++;
amt /= 10;
}
return num + suffix;
} else {
if (amt > 99 || (always && zeropad >= 3)) {
return 3+suffix;
}
if (amt > 9 || (always && zeropad >= 2)) {
return 2+suffix;
}
if (always || amt > 0) {
return 1+suffix;
}
}
return 0;
}
static private int printFieldLocked(char[] formatStr, int amt, char suffix, int pos,
boolean always, int zeropad) {
if (always || amt > 0) {
final int startPos = pos;
if (amt > 999) {
int tmp = 0;
while (amt != 0 && tmp < sTmpFormatStr.length) {
int dig = amt % 10;
sTmpFormatStr[tmp] = (char)(dig + '0');
tmp++;
amt /= 10;
}
tmp--;
while (tmp >= 0) {
formatStr[pos] = sTmpFormatStr[tmp];
pos++;
tmp--;
}
} else {
if ((always && zeropad >= 3) || amt > 99) {
int dig = amt/100;
formatStr[pos] = (char)(dig + '0');
pos++;
amt -= (dig*100);
}
if ((always && zeropad >= 2) || amt > 9 || startPos != pos) {
int dig = amt/10;
formatStr[pos] = (char)(dig + '0');
pos++;
amt -= (dig*10);
}
formatStr[pos] = (char)(amt + '0');
pos++;
}
formatStr[pos] = suffix;
pos++;
}
return pos;
}
private static int formatDurationLocked(long duration, int fieldLen) {
if (sFormatStr.length < fieldLen) {
sFormatStr = new char[fieldLen];
}
char[] formatStr = sFormatStr;
if (duration == 0) {
int pos = 0;
fieldLen -= 1;
while (pos < fieldLen) {
formatStr[pos++] = ' ';
}
formatStr[pos] = '0';
return pos+1;
}
char prefix;
if (duration > 0) {
prefix = '+';
} else {
prefix = '-';
duration = -duration;
}
int millis = (int)(duration%1000);
int seconds = (int) Math.floor(duration / 1000);
int days = 0, hours = 0, minutes = 0;
if (seconds >= SECONDS_PER_DAY) {
days = seconds / SECONDS_PER_DAY;
seconds -= days * SECONDS_PER_DAY;
}
if (seconds >= SECONDS_PER_HOUR) {
hours = seconds / SECONDS_PER_HOUR;
seconds -= hours * SECONDS_PER_HOUR;
}
if (seconds >= SECONDS_PER_MINUTE) {
minutes = seconds / SECONDS_PER_MINUTE;
seconds -= minutes * SECONDS_PER_MINUTE;
}
int pos = 0;
if (fieldLen != 0) {
int myLen = accumField(days, 1, false, 0);
myLen += accumField(hours, 1, myLen > 0, 2);
myLen += accumField(minutes, 1, myLen > 0, 2);
myLen += accumField(seconds, 1, myLen > 0, 2);
myLen += accumField(millis, 2, true, myLen > 0 ? 3 : 0) + 1;
while (myLen < fieldLen) {
formatStr[pos] = ' ';
pos++;
myLen++;
}
}
formatStr[pos] = prefix;
pos++;
int start = pos;
boolean zeropad = fieldLen != 0;
pos = printFieldLocked(formatStr, days, 'd', pos, false, 0);
pos = printFieldLocked(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0);
pos = printFieldLocked(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0);
pos = printFieldLocked(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0);
pos = printFieldLocked(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0);
formatStr[pos] = 's';
return pos + 1;
}
/** @hide Just for debugging; not internationalized. */
public static void formatDuration(long duration, StringBuilder builder) {
synchronized (sFormatSync) {
int len = formatDurationLocked(duration, 0);
builder.append(sFormatStr, 0, len);
}
}
/** @hide Just for debugging; not internationalized. */
public static void formatDuration(long duration, StringBuilder builder, int fieldLen) {
synchronized (sFormatSync) {
int len = formatDurationLocked(duration, fieldLen);
builder.append(sFormatStr, 0, len);
}
}
/** @hide Just for debugging; not internationalized. */
public static void formatDuration(long duration, PrintWriter pw, int fieldLen) {
synchronized (sFormatSync) {
int len = formatDurationLocked(duration, fieldLen);
pw.print(new String(sFormatStr, 0, len));
}
}
/** @hide Just for debugging; not internationalized. */
@TestApi
public static String formatDuration(long duration) {
synchronized (sFormatSync) {
int len = formatDurationLocked(duration, 0);
return new String(sFormatStr, 0, len);
}
}
/** @hide Just for debugging; not internationalized. */
public static void formatDuration(long duration, PrintWriter pw) {
formatDuration(duration, pw, 0);
}
/** @hide Just for debugging; not internationalized. */
public static void formatDuration(long time, long now, StringBuilder sb) {
if (time == 0) {
sb.append("--");
return;
}
formatDuration(time-now, sb, 0);
}
/** @hide Just for debugging; not internationalized. */
public static void formatDuration(long time, long now, PrintWriter pw) {
if (time == 0) {
pw.print("--");
return;
}
formatDuration(time-now, pw, 0);
}
/** @hide Just for debugging; not internationalized. */
public static String formatUptime(long time) {
return formatTime(time, SystemClock.uptimeMillis());
}
/** @hide Just for debugging; not internationalized. */
public static String formatRealtime(long time) {
return formatTime(time, SystemClock.elapsedRealtime());
}
/** @hide Just for debugging; not internationalized. */
public static String formatTime(long time, long referenceTime) {
long diff = time - referenceTime;
if (diff > 0) {
return time + " (in " + diff + " ms)";
}
if (diff < 0) {
return time + " (" + -diff + " ms ago)";
}
return time + " (now)";
}
/**
* Convert a System.currentTimeMillis() value to a time of day value like
* that printed in logs. MM-DD HH:MM:SS.MMM
*
* @param millis since the epoch (1/1/1970)
* @return String representation of the time.
* @hide
*/
public static String logTimeOfDay(long millis) {
Calendar c = Calendar.getInstance();
if (millis >= 0) {
c.setTimeInMillis(millis);
return String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c);
} else {
return Long.toString(millis);
}
}
/** {@hide} */
public static String formatForLogging(long millis) {
if (millis <= 0) {
return "unknown";
} else {
return sLoggingFormat.format(new Date(millis));
}
}
/**
* Dump a currentTimeMillis style timestamp for dumpsys.
*
* @hide
*/
public static void dumpTime(PrintWriter pw, long time) {
pw.print(sDumpDateFormat.format(new Date(time)));
}
/**
* This method is used to find if a clock time is inclusively between two other clock times
* @param reference The time of the day we want check if it is between start and end
* @param start The start time reference
* @param end The end time
* @return true if the reference time is between the two clock times, and false otherwise.
*/
public static boolean isTimeBetween(@NonNull LocalTime reference,
@NonNull LocalTime start,
@NonNull LocalTime end) {
// ////////E----+-----S////////
if ((reference.isBefore(start) && reference.isAfter(end)
// -----+----S//////////E------
|| (reference.isBefore(end) && reference.isBefore(start) && start.isBefore(end))
// ---------S//////////E---+---
|| (reference.isAfter(end) && reference.isAfter(start)) && start.isBefore(end))) {
return false;
} else {
return true;
}
}
/**
* Dump a currentTimeMillis style timestamp for dumpsys, with the delta time from now.
*
* @hide
*/
public static void dumpTimeWithDelta(PrintWriter pw, long time, long now) {
pw.print(sDumpDateFormat.format(new Date(time)));
if (time == now) {
pw.print(" (now)");
} else {
pw.print(" (");
TimeUtils.formatDuration(time, now, pw);
pw.print(")");
}
}}
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
import android.annotation.NonNull;
/**
* Supplier for custom trace messages.
*
* @hide
*/
public interface TraceNameSupplier {
/**
* Gets the name used for trace messages.
*/
@NonNull String getTraceName();
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,247 @@
package android.webkit;
import android.annotation.Nullable;
import xyz.nulldev.androidcompat.webkit.CookieManagerImpl;
public abstract class CookieManager {
/**
* @deprecated This class should not be constructed by applications, use {@link #getInstance}
* instead to fetch the singleton instance.
*/
// TODO(ntfschr): mark this as @SystemApi after a year.
@Deprecated
public CookieManager() {}
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("doesn't implement Cloneable");
}
private static CookieManager INSTANCE = null;
private static final Object lock = new Object();
/**
* Gets the singleton CookieManager instance.
*
* @return the singleton CookieManager instance
*/
public static CookieManager getInstance() {
if (INSTANCE != null) {
return INSTANCE;
} else {
synchronized (lock) {
if (INSTANCE == null) {
INSTANCE = new CookieManagerImpl();
}
return INSTANCE;
}
}
}
/**
* Sets whether the application's {@link WebView} instances should send and
* accept cookies.
* By default this is set to {@code true} and the WebView accepts cookies.
* <p>
* When this is {@code true}
* {@link CookieManager#setAcceptThirdPartyCookies setAcceptThirdPartyCookies} and
* {@link CookieManager#setAcceptFileSchemeCookies setAcceptFileSchemeCookies}
* can be used to control the policy for those specific types of cookie.
*
* @param accept whether {@link WebView} instances should send and accept
* cookies
*/
public abstract void setAcceptCookie(boolean accept);
/**
* Gets whether the application's {@link WebView} instances send and accept
* cookies.
*
* @return {@code true} if {@link WebView} instances send and accept cookies
*/
public abstract boolean acceptCookie();
/**
* Sets whether the {@link WebView} should allow third party cookies to be set.
* Allowing third party cookies is a per WebView policy and can be set
* differently on different WebView instances.
* <p>
* Apps that target {@link android.os.Build.VERSION_CODES#KITKAT} or below
* default to allowing third party cookies. Apps targeting
* {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later default to disallowing
* third party cookies.
*
* @param webview the {@link WebView} instance to set the cookie policy on
* @param accept whether the {@link WebView} instance should accept
* third party cookies
*/
public abstract void setAcceptThirdPartyCookies(WebView webview, boolean accept);
/**
* Gets whether the {@link WebView} should allow third party cookies to be set.
*
* @param webview the {@link WebView} instance to get the cookie policy for
* @return {@code true} if the {@link WebView} accepts third party cookies
*/
public abstract boolean acceptThirdPartyCookies(WebView webview);
/**
* Sets a single cookie (key-value pair) for the given URL. Any existing cookie with the same
* host, path and name will be replaced with the new cookie. The cookie being set
* will be ignored if it is expired. To set multiple cookies, your application should invoke
* this method multiple times.
*
* <p>The {@code value} parameter must follow the format of the {@code Set-Cookie} HTTP
* response header defined by
* <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>.
* This is a key-value pair of the form {@code "key=value"}, optionally followed by a list of
* cookie attributes delimited with semicolons (ex. {@code "key=value; Max-Age=123"}). Please
* consult the RFC specification for a list of valid attributes.
*
* <p class="note"><b>Note:</b> if specifying a {@code value} containing the {@code "Secure"}
* attribute, {@code url} must use the {@code "https://"} scheme.
*
* @param url the URL for which the cookie is to be set
* @param value the cookie as a string, using the format of the 'Set-Cookie'
* HTTP response header
*/
public abstract void setCookie(String url, String value);
/**
* Sets a single cookie (key-value pair) for the given URL. Any existing cookie with the same
* host, path and name will be replaced with the new cookie. The cookie being set
* will be ignored if it is expired. To set multiple cookies, your application should invoke
* this method multiple times.
*
* <p>The {@code value} parameter must follow the format of the {@code Set-Cookie} HTTP
* response header defined by
* <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>.
* This is a key-value pair of the form {@code "key=value"}, optionally followed by a list of
* cookie attributes delimited with semicolons (ex. {@code "key=value; Max-Age=123"}). Please
* consult the RFC specification for a list of valid attributes.
*
* <p>This method is asynchronous. If a {@link ValueCallback} is provided,
* {@link ValueCallback#onReceiveValue} will be called on the current
* thread's {@link android.os.Looper} once the operation is complete.
* The value provided to the callback indicates whether the cookie was set successfully.
* You can pass {@code null} as the callback if you don't need to know when the operation
* completes or whether it succeeded, and in this case it is safe to call the method from a
* thread without a Looper.
*
* <p class="note"><b>Note:</b> if specifying a {@code value} containing the {@code "Secure"}
* attribute, {@code url} must use the {@code "https://"} scheme.
*
* @param url the URL for which the cookie is to be set
* @param value the cookie as a string, using the format of the 'Set-Cookie'
* HTTP response header
* @param callback a callback to be executed when the cookie has been set
*/
public abstract void setCookie(String url, String value, @Nullable ValueCallback<Boolean>
callback);
/**
* Gets all the cookies for the given URL. This may return multiple key-value pairs if multiple
* cookies are associated with this URL, in which case each cookie will be delimited by {@code
* "; "} characters (semicolon followed by a space). Each key-value pair will be of the form
* {@code "key=value"}.
*
* @param url the URL for which the cookies are requested
* @return value the cookies as a string, using the format of the 'Cookie'
* HTTP request header
*/
public abstract String getCookie(String url);
/**
* Removes all session cookies, which are cookies without an expiration
* date.
* @deprecated use {@link #removeSessionCookies(ValueCallback)} instead.
*/
@Deprecated
public abstract void removeSessionCookie();
/**
* Removes all session cookies, which are cookies without an expiration
* date.
* <p>
* This method is asynchronous.
* If a {@link ValueCallback} is provided,
* {@link ValueCallback#onReceiveValue(Object)} will be called on the current
* thread's {@link android.os.Looper} once the operation is complete.
* The value provided to the callback indicates whether any cookies were removed.
* You can pass {@code null} as the callback if you don't need to know when the operation
* completes or whether any cookie were removed, and in this case it is safe to call the
* method from a thread without a Looper.
* @param callback a callback which is executed when the session cookies have been removed
*/
public abstract void removeSessionCookies(@Nullable ValueCallback<Boolean> callback);
/**
* Removes all cookies.
* @deprecated Use {@link #removeAllCookies(ValueCallback)} instead.
*/
@Deprecated
public abstract void removeAllCookie();
/**
* Removes all cookies.
* <p>
* This method is asynchronous.
* If a {@link ValueCallback} is provided,
* {@link ValueCallback#onReceiveValue(Object)} will be called on the current
* thread's {@link android.os.Looper} once the operation is complete.
* The value provided to the callback indicates whether any cookies were removed.
* You can pass {@code null} as the callback if you don't need to know when the operation
* completes or whether any cookies were removed, and in this case it is safe to call the
* method from a thread without a Looper.
* @param callback a callback which is executed when the cookies have been removed
*/
public abstract void removeAllCookies(@Nullable ValueCallback<Boolean> callback);
/**
* Gets whether there are stored cookies.
*
* @return {@code true} if there are stored cookies
*/
public abstract boolean hasCookies();
/**
* Removes all expired cookies.
* @deprecated The WebView handles removing expired cookies automatically.
*/
@Deprecated
public abstract void removeExpiredCookie();
/**
* Ensures all cookies currently accessible through the getCookie API are
* written to persistent storage.
* This call will block the caller until it is done and may perform I/O.
*/
public abstract void flush();
/**
* Gets whether the application's {@link WebView} instances send and accept
* cookies for file scheme URLs.
*
* @return {@code true} if {@link WebView} instances send and accept cookies for
* file scheme URLs
*/
// Static for backward compatibility.
public static boolean allowFileSchemeCookies() {
return getInstance().allowFileSchemeCookiesImpl();
}
public abstract boolean allowFileSchemeCookiesImpl();
/**
* Sets whether the application's {@link WebView} instances should send and accept cookies for
* file scheme URLs.
* <p>
* Use of cookies with file scheme URLs is potentially insecure and turned off by default. All
* {@code file://} URLs share all their cookies, which may lead to leaking private app cookies
* (ex. any malicious file can access cookies previously set by other (trusted) files).
* <p class="note">
* Loading content via {@code file://} URLs is generally discouraged. See the note in
* {@link WebSettings#setAllowFileAccess}.
* Using <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader.html">
* androidx.webkit.WebViewAssetLoader</a> to load files over {@code http(s)://} URLs allows
* the standard web security model to be used for setting and sharing cookies for local files.
* <p>
* Note that calls to this method will have no effect if made after calling other
* {@link CookieManager} APIs.
*
* @deprecated This setting is not secure, please use
* <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader.html">
* androidx.webkit.WebViewAssetLoader</a> instead.
*/
// Static for backward compatibility.
@Deprecated
public static void setAcceptFileSchemeCookies(boolean accept) {
getInstance().setAcceptFileSchemeCookiesImpl(accept);
}
public abstract void setAcceptFileSchemeCookiesImpl(boolean accept);
}
@@ -0,0 +1,205 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.webkit;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Message;
import android.view.View;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class WebChromeClient {
public void onProgressChanged(WebView view, int newProgress) {}
public void onReceivedTitle(WebView view, String title) {}
public void onReceivedIcon(WebView view, Bitmap icon) {}
public void onReceivedTouchIconUrl(WebView view, String url,
boolean precomposed) {}
public interface CustomViewCallback {
public void onCustomViewHidden();
}
public void onShowCustomView(View view, CustomViewCallback callback) {};
@Deprecated
public void onShowCustomView(View view, int requestedOrientation,
CustomViewCallback callback) {};
public void onHideCustomView() {}
public boolean onCreateWindow(WebView view, boolean isDialog,
boolean isUserGesture, Message resultMsg) {
return false;
}
public void onRequestFocus(WebView view) {}
public void onCloseWindow(WebView window) {}
public boolean onJsAlert(WebView view, String url, String message,
JsResult result) {
return false;
}
public boolean onJsConfirm(WebView view, String url, String message,
JsResult result) {
return false;
}
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result) {
return false;
}
public boolean onJsBeforeUnload(WebView view, String url, String message,
JsResult result) {
return false;
}
@Deprecated
public void onExceededDatabaseQuota(String url, String databaseIdentifier,
long quota, long estimatedDatabaseSize, long totalQuota,
WebStorage.QuotaUpdater quotaUpdater) {
// This default implementation passes the current quota back to WebCore.
// WebCore will interpret this that new quota was declined.
quotaUpdater.updateQuota(quota);
}
@Deprecated
public void onReachedMaxAppCacheSize(long requiredStorage, long quota,
WebStorage.QuotaUpdater quotaUpdater) {
quotaUpdater.updateQuota(quota);
}
public void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback) {}
public void onGeolocationPermissionsHidePrompt() {}
public void onPermissionRequest(PermissionRequest request) {
request.deny();
}
public void onPermissionRequestCanceled(PermissionRequest request) {}
// This method was only called when using the JSC javascript engine. V8 became
// the default JS engine with Froyo and support for building with JSC was
// removed in b/5495373. V8 does not have a mechanism for making a callback such
// as this.
@Deprecated
public boolean onJsTimeout() {
return true;
}
@Deprecated
public void onConsoleMessage(String message, int lineNumber, String sourceID) { }
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
// Call the old version of this function for backwards compatability.
onConsoleMessage(consoleMessage.message(), consoleMessage.lineNumber(),
consoleMessage.sourceId());
return false;
}
@Nullable
public Bitmap getDefaultVideoPoster() {
return null;
}
@Nullable
public View getVideoLoadingProgressView() {
return null;
}
/** Obtains a list of all visited history items, used for link coloring
*/
public void getVisitedHistory(ValueCallback<String[]> callback) {
}
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
return false;
}
public static abstract class FileChooserParams {
@SystemApi
public static final long ENABLE_FILE_SYSTEM_ACCESS = 364980165L;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
public @interface Mode {}
/** Open single file. Requires that the file exists before allowing the user to pick it. */
public static final int MODE_OPEN = 0;
/** Like Open but allows multiple files to be selected. */
public static final int MODE_OPEN_MULTIPLE = 1;
/** Like Open but allows a folder to be selected. */
public static final int MODE_OPEN_FOLDER = 2;
/** Allows picking a nonexistent file and saving it. */
public static final int MODE_SAVE = 3;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionMode {}
/** File or directory should be opened for reading only. */
public static final int PERMISSION_MODE_READ = 0;
/** File or directory should be opened for read and write. */
public static final int PERMISSION_MODE_READ_WRITE = 1;
@Nullable
public static Uri[] parseResult(int resultCode, Intent data) {
throw new RuntimeException("Stub!");
}
@Mode
public abstract int getMode();
public abstract String[] getAcceptTypes();
public abstract boolean isCaptureEnabled();
@Nullable
public abstract CharSequence getTitle();
@Nullable
public abstract String getFilenameHint();
@PermissionMode
public int getPermissionMode() {
return PERMISSION_MODE_READ;
}
public abstract Intent createIntent();
}
@SystemApi
@Deprecated
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
uploadFile.onReceiveValue(null);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,609 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.webkit;
import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Message;
import android.view.InputEvent;
import android.view.KeyEvent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class WebViewClient {
/**
* Give the host application a chance to take control when a URL is about to be loaded in the
* current WebView. If a WebViewClient is not provided, by default WebView will ask Activity
* Manager to choose the proper handler for the URL. If a WebViewClient is provided, returning
* {@code true} causes the current WebView to abort loading the URL, while returning
* {@code false} causes the WebView to continue loading the URL as usual.
*
* <p class="note"><b>Note:</b> Do not call {@link WebView#loadUrl(String)} with the same
* URL and then return {@code true}. This unnecessarily cancels the current load and starts a
* new load with the same URL. The correct way to continue loading a given URL is to simply
* return {@code false}, without calling {@link WebView#loadUrl(String)}.
*
* <p class="note"><b>Note:</b> This method is not called for POST requests.
*
* <p class="note"><b>Note:</b> This method may be called for subframes and with non-HTTP(S)
* schemes; calling {@link WebView#loadUrl(String)} with such a URL will fail.
*
* @param view The WebView that is initiating the callback.
* @param url The URL to be loaded.
* @return {@code true} to cancel the current load, otherwise return {@code false}.
* @deprecated Use {@link #shouldOverrideUrlLoading(WebView, WebResourceRequest)
* shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead.
*/
@Deprecated
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
/**
* Give the host application a chance to take control when a URL is about to be loaded in the
* current WebView. If a WebViewClient is not provided, by default WebView will ask Activity
* Manager to choose the proper handler for the URL. If a WebViewClient is provided, returning
* {@code true} causes the current WebView to abort loading the URL, while returning
* {@code false} causes the WebView to continue loading the URL as usual.
*
* <p>This callback is not called for all page navigations. In particular, this is not called
* for navigations which the app initiated with {@code loadUrl()}: this callback would not serve
* a purpose in this case, because the app already knows about the navigation. This callback
* lets the app know about navigations initiated by the web page (such as navigations initiated
* by JavaScript code), by the user (such as when the user taps on a link), or by an HTTP
* redirect (ex. if {@code loadUrl("foo.com")} redirects to {@code "bar.com"} because of HTTP
* 301).
*
* <p class="note"><b>Note:</b> Do not call {@link WebView#loadUrl(String)} with the request's
* URL and then return {@code true}. This unnecessarily cancels the current load and starts a
* new load with the same URL. The correct way to continue loading a given URL is to simply
* return {@code false}, without calling {@link WebView#loadUrl(String)}.
*
* <p class="note"><b>Note:</b> This method is not called for POST requests.
*
* <p class="note"><b>Note:</b> This method may be called for subframes and with non-HTTP(S)
* schemes; calling {@link WebView#loadUrl(String)} with such a URL will fail.
*
* @param view The WebView that is initiating the callback.
* @param request Object containing the details of the request.
* @return {@code true} to cancel the current load, otherwise return {@code false}.
*/
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return shouldOverrideUrlLoading(view, request.getUrl().toString());
}
/**
* Notify the host application that a page has started loading. This method
* is called once for each main frame load so a page with iframes or
* framesets will call onPageStarted one time for the main frame. This also
* means that onPageStarted will not be called when the contents of an
* embedded frame changes, i.e. clicking a link whose target is an iframe,
* it will also not be called for fragment navigations (navigations to
* #fragment_id).
*
* @param view The WebView that is initiating the callback.
* @param url The url to be loaded.
* @param favicon The favicon for this page if it already exists in the
* database.
*/
public void onPageStarted(WebView view, String url, Bitmap favicon) {
}
/**
* Notify the host application that a page has finished loading. This method
* is called only for main frame. Receiving an {@code onPageFinished()} callback does not
* guarantee that the next frame drawn by WebView will reflect the state of the DOM at this
* point. In order to be notified that the current DOM state is ready to be rendered, request a
* visual state callback with {@link WebView#postVisualStateCallback} and wait for the supplied
* callback to be triggered.
*
* @param view The WebView that is initiating the callback.
* @param url The url of the page.
*/
public void onPageFinished(WebView view, String url) {
}
/**
* Notify the host application that the WebView will load the resource
* specified by the given url.
*
* @param view The WebView that is initiating the callback.
* @param url The url of the resource the WebView will load.
*/
public void onLoadResource(WebView view, String url) {
}
/**
* Notify the host application that {@link android.webkit.WebView} content left over from
* previous page navigations will no longer be drawn.
*
* <p>This callback can be used to determine the point at which it is safe to make a recycled
* {@link android.webkit.WebView} visible, ensuring that no stale content is shown. It is called
* at the earliest point at which it can be guaranteed that {@link WebView#onDraw} will no
* longer draw any content from previous navigations. The next draw will display either the
* {@link WebView#setBackgroundColor background color} of the {@link WebView}, or some of the
* contents of the newly loaded page.
*
* <p>This method is called when the body of the HTTP response has started loading, is reflected
* in the DOM, and will be visible in subsequent draws. This callback occurs early in the
* document loading process, and as such you should expect that linked resources (for example,
* CSS and images) may not be available.
*
* <p>For more fine-grained notification of visual state updates, see {@link
* WebView#postVisualStateCallback}.
*
* <p>Please note that all the conditions and recommendations applicable to
* {@link WebView#postVisualStateCallback} also apply to this API.
*
* <p>This callback is only called for main frame navigations.
*
* @param view The {@link android.webkit.WebView} for which the navigation occurred.
* @param url The URL corresponding to the page navigation that triggered this callback.
*/
public void onPageCommitVisible(WebView view, String url) {
}
/**
* Notify the host application of a resource request and allow the
* application to return the data. If the return value is {@code null}, the WebView
* will continue to load the resource as usual. Otherwise, the return
* response and data will be used.
*
* <p>This callback is invoked for a variety of URL schemes (e.g., {@code http(s):}, {@code
* data:}, {@code file:}, etc.), not only those schemes which send requests over the network.
* This is not called for {@code javascript:} URLs, {@code blob:} URLs, or for assets accessed
* via {@code file:///android_asset/} or {@code file:///android_res/} URLs.
*
* <p>In the case of redirects, this is only called for the initial resource URL, not any
* subsequent redirect URLs.
*
* <p class="note"><b>Note:</b> This method is called on a thread
* other than the UI thread so clients should exercise caution
* when accessing private data or the view system.
*
* <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
* Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
* to skip Safe Browsing checks for that host or dismiss the warning in {@link
* #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
* @param url The raw url of the resource.
* @return A {@link android.webkit.WebResourceResponse} containing the
* response information or {@code null} if the WebView should load the
* resource itself.
* @deprecated Use {@link #shouldInterceptRequest(WebView, WebResourceRequest)
* shouldInterceptRequest(WebView, WebResourceRequest)} instead.
*/
@Deprecated
@Nullable
public WebResourceResponse shouldInterceptRequest(WebView view,
String url) {
return null;
}
/**
* Notify the host application of a resource request and allow the
* application to return the data. If the return value is {@code null}, the WebView
* will continue to load the resource as usual. Otherwise, the return
* response and data will be used.
*
* <p>This callback is invoked for a variety of URL schemes (e.g., {@code http(s):}, {@code
* data:}, {@code file:}, etc.), not only those schemes which send requests over the network.
* This is not called for {@code javascript:} URLs, {@code blob:} URLs, or for assets accessed
* via {@code file:///android_asset/} or {@code file:///android_res/} URLs.
*
* <p>In the case of redirects, this is only called for the initial resource URL, not any
* subsequent redirect URLs.
*
* <p class="note"><b>Note:</b> This method is called on a thread
* other than the UI thread so clients should exercise caution
* when accessing private data or the view system.
*
* <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
* Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
* to skip Safe Browsing checks for that host or dismiss the warning in {@link
* #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
* @param request Object containing the details of the request.
* @return A {@link android.webkit.WebResourceResponse} containing the
* response information or {@code null} if the WebView should load the
* resource itself.
*/
@Nullable
public WebResourceResponse shouldInterceptRequest(WebView view,
WebResourceRequest request) {
return shouldInterceptRequest(view, request.getUrl().toString());
}
/**
* Notify the host application that there have been an excessive number of
* HTTP redirects. As the host application if it would like to continue
* trying to load the resource. The default behavior is to send the cancel
* message.
*
* @param view The WebView that is initiating the callback.
* @param cancelMsg The message to send if the host wants to cancel
* @param continueMsg The message to send if the host wants to continue
* @deprecated This method is no longer called. When the WebView encounters
* a redirect loop, it will cancel the load.
*/
@Deprecated
public void onTooManyRedirects(WebView view, Message cancelMsg,
Message continueMsg) {
cancelMsg.sendToTarget();
}
// These ints must match up to the hidden values in EventHandler.
/** Generic error */
public static final int ERROR_UNKNOWN = -1;
/** Server or proxy hostname lookup failed */
public static final int ERROR_HOST_LOOKUP = -2;
/** Unsupported authentication scheme (not basic or digest) */
public static final int ERROR_UNSUPPORTED_AUTH_SCHEME = -3;
/** User authentication failed on server */
public static final int ERROR_AUTHENTICATION = -4;
/** User authentication failed on proxy */
public static final int ERROR_PROXY_AUTHENTICATION = -5;
/** Failed to connect to the server */
public static final int ERROR_CONNECT = -6;
/** Failed to read or write to the server */
public static final int ERROR_IO = -7;
/** Connection timed out */
public static final int ERROR_TIMEOUT = -8;
/** Too many redirects */
public static final int ERROR_REDIRECT_LOOP = -9;
/** Unsupported URI scheme */
public static final int ERROR_UNSUPPORTED_SCHEME = -10;
/** Failed to perform SSL handshake */
public static final int ERROR_FAILED_SSL_HANDSHAKE = -11;
/** Malformed URL */
public static final int ERROR_BAD_URL = -12;
/** Generic file error */
public static final int ERROR_FILE = -13;
/** File not found */
public static final int ERROR_FILE_NOT_FOUND = -14;
/** Too many requests during this load */
public static final int ERROR_TOO_MANY_REQUESTS = -15;
/** Resource load was canceled by Safe Browsing */
public static final int ERROR_UNSAFE_RESOURCE = -16;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
public @interface SafeBrowsingThreat {}
/** The resource was blocked for an unknown reason. */
public static final int SAFE_BROWSING_THREAT_UNKNOWN = 0;
/** The resource was blocked because it contains malware. */
public static final int SAFE_BROWSING_THREAT_MALWARE = 1;
/** The resource was blocked because it contains deceptive content. */
public static final int SAFE_BROWSING_THREAT_PHISHING = 2;
/** The resource was blocked because it contains unwanted software. */
public static final int SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE = 3;
/**
* The resource was blocked because it may trick the user into a billing agreement.
*
* <p>This constant is only used when targetSdkVersion is at least {@link
* android.os.Build.VERSION_CODES#Q}. Otherwise, {@link #SAFE_BROWSING_THREAT_UNKNOWN} is used
* instead.
*/
public static final int SAFE_BROWSING_THREAT_BILLING = 4;
/**
* Report an error to the host application. These errors are unrecoverable
* (i.e. the main resource is unavailable). The {@code errorCode} parameter
* corresponds to one of the {@code ERROR_*} constants.
* @param view The WebView that is initiating the callback.
* @param errorCode The error code corresponding to an ERROR_* value.
* @param description A String describing the error.
* @param failingUrl The url that failed to load.
* @deprecated Use {@link #onReceivedError(WebView, WebResourceRequest, WebResourceError)
* onReceivedError(WebView, WebResourceRequest, WebResourceError)} instead.
*/
@Deprecated
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
}
/**
* Report web resource loading error to the host application. These errors usually indicate
* inability to connect to the server. Note that unlike the deprecated version of the callback,
* the new version will be called for any resource (iframe, image, etc.), not just for the main
* page. Thus, it is recommended to perform minimum required work in this callback.
* @param view The WebView that is initiating the callback.
* @param request The originating request.
* @param error Information about the error occurred.
*/
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
if (request.isForMainFrame()) {
onReceivedError(view,
error.getErrorCode(), error.getDescription().toString(),
request.getUrl().toString());
}
}
/**
* Notify the host application that an HTTP error has been received from the server while
* loading a resource. HTTP errors have status codes &gt;= 400. This callback will be called
* for any resource (iframe, image, etc.), not just for the main page. Thus, it is recommended
* to perform minimum required work in this callback. Note that the content of the server
* response may not be provided within the {@code errorResponse} parameter.
* @param view The WebView that is initiating the callback.
* @param request The originating request.
* @param errorResponse Information about the error occurred.
*/
public void onReceivedHttpError(
WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
}
/**
* As the host application if the browser should resend data as the
* requested page was a result of a POST. The default is to not resend the
* data.
*
* @param view The WebView that is initiating the callback.
* @param dontResend The message to send if the browser should not resend
* @param resend The message to send if the browser should resend data
*/
public void onFormResubmission(WebView view, Message dontResend,
Message resend) {
dontResend.sendToTarget();
}
/**
* Notify the host application to update its visited links database.
*
* @param view The WebView that is initiating the callback.
* @param url The url being visited.
* @param isReload {@code true} if this url is being reloaded.
*/
public void doUpdateVisitedHistory(WebView view, String url,
boolean isReload) {
}
/**
* Notifies the host application that an SSL error occurred while loading a
* resource. The host application must call either
* {@link SslErrorHandler#cancel()} or {@link SslErrorHandler#proceed()}.
*
* <p class="warning"><b>Warning:</b> Application overrides of this method
* can be used to display custom error pages or to silently log issues, but
* the host application should always call {@code SslErrorHandler#cancel()}
* and never proceed past errors.
*
* <p class="note"><b>Note:</b> Do not prompt the user about SSL errors.
* Users are unlikely to be able to make an informed security decision, and
* {@code WebView} does not provide a UI for showing the details of the
* error in a meaningful way.
*
* <p>The decision to call {@code proceed()} or {@code cancel()} may be
* retained to facilitate responses to future SSL errors. The default
* behavior is to cancel the resource loading process.
*
* <p>This API is called only for recoverable SSL certificate errors. For
* non-recoverable errors (such as when the server fails the client), the
* {@code WebView} calls {@link #onReceivedError(WebView,
* WebResourceRequest, WebResourceError) onReceivedError(WebView,
* WebResourceRequest, WebResourceError)} with the
* {@link #ERROR_FAILED_SSL_HANDSHAKE} argument.
*
* @param view {@code WebView} that initiated the callback.
* @param handler {@link SslErrorHandler} that handles the user's response.
* @param error SSL error object.
*/
public void onReceivedSslError(WebView view, SslErrorHandler handler,
SslError error) {
handler.cancel();
}
/**
* Notify the host application to handle a SSL client certificate request. The host application
* is responsible for showing the UI if desired and providing the keys. There are three ways to
* respond: {@link ClientCertRequest#proceed}, {@link ClientCertRequest#cancel}, or {@link
* ClientCertRequest#ignore}. Webview stores the response in memory (for the life of the
* application) if {@link ClientCertRequest#proceed} or {@link ClientCertRequest#cancel} is
* called and does not call {@code onReceivedClientCertRequest()} again for the same host and
* port pair. Webview does not store the response if {@link ClientCertRequest#ignore}
* is called. Note that, multiple layers in chromium network stack might be
* caching the responses, so the behavior for ignore is only a best case
* effort.
*
* This method is called on the UI thread. During the callback, the
* connection is suspended.
*
* For most use cases, the application program should implement the
* {@link android.security.KeyChainAliasCallback} interface and pass it to
* {@link android.security.KeyChain#choosePrivateKeyAlias} to start an
* activity for the user to choose the proper alias. The keychain activity will
* provide the alias through the callback method in the implemented interface. Next
* the application should create an async task to call
* {@link android.security.KeyChain#getPrivateKey} to receive the key.
*
* An example implementation of client certificates can be seen at
* <A href="https://android.googlesource.com/platform/packages/apps/Browser/+/android-5.1.1_r1/src/com/android/browser/Tab.java">
* AOSP Browser</a>
*
* The default behavior is to cancel, returning no client certificate.
*
* @param view The WebView that is initiating the callback
* @param request An instance of a {@link ClientCertRequest}
*
*/
public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
request.cancel();
}
/**
* Notifies the host application that the WebView received an HTTP
* authentication request. The host application can use the supplied
* {@link HttpAuthHandler} to set the WebView's response to the request.
* The default behavior is to cancel the request.
*
* <p class="note"><b>Note:</b> The supplied HttpAuthHandler must be used on
* the UI thread.
*
* @param view the WebView that is initiating the callback
* @param handler the HttpAuthHandler used to set the WebView's response
* @param host the host requiring authentication
* @param realm the realm for which authentication is required
* @see WebView#getHttpAuthUsernamePassword
*/
public void onReceivedHttpAuthRequest(WebView view,
HttpAuthHandler handler, String host, String realm) {
handler.cancel();
}
/**
* Give the host application a chance to handle the key event synchronously.
* e.g. menu shortcut key events need to be filtered this way. If return
* true, WebView will not handle the key event. If return {@code false}, WebView
* will always handle the key event, so none of the super in the view chain
* will see the key event. The default behavior returns {@code false}.
*
* @param view The WebView that is initiating the callback.
* @param event The key event.
* @return {@code true} if the host application wants to handle the key event
* itself, otherwise return {@code false}
*/
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
return false;
}
/**
* Notify the host application that a key was not handled by the WebView.
* Except system keys, WebView always consumes the keys in the normal flow
* or if {@link #shouldOverrideKeyEvent} returns {@code true}. This is called asynchronously
* from where the key is dispatched. It gives the host application a chance
* to handle the unhandled key events.
*
* @param view The WebView that is initiating the callback.
* @param event The key event.
*/
public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
onUnhandledInputEventInternal(view, event);
}
/**
* Notify the host application that a input event was not handled by the WebView.
* Except system keys, WebView always consumes input events in the normal flow
* or if {@link #shouldOverrideKeyEvent} returns {@code true}. This is called asynchronously
* from where the event is dispatched. It gives the host application a chance
* to handle the unhandled input events.
*
* Note that if the event is a {@link android.view.MotionEvent}, then it's lifetime is only
* that of the function call. If the WebViewClient wishes to use the event beyond that, then it
* <i>must</i> create a copy of the event.
*
* It is the responsibility of overriders of this method to call
* {@link #onUnhandledKeyEvent(WebView, KeyEvent)}
* when appropriate if they wish to continue receiving events through it.
*
* @param view The WebView that is initiating the callback.
* @param event The input event.
* @removed
*/
public void onUnhandledInputEvent(WebView view, InputEvent event) {
if (event instanceof KeyEvent) {
onUnhandledKeyEvent(view, (KeyEvent) event);
return;
}
onUnhandledInputEventInternal(view, event);
}
private void onUnhandledInputEventInternal(WebView view, InputEvent event) {
throw new RuntimeException("Stub!");
}
/**
* Notify the host application that the scale applied to the WebView has
* changed.
*
* @param view The WebView that is initiating the callback.
* @param oldScale The old scale factor
* @param newScale The new scale factor
*/
public void onScaleChanged(WebView view, float oldScale, float newScale) {
}
/**
* Notify the host application that a request to automatically log in the
* user has been processed.
* @param view The WebView requesting the login.
* @param realm The account realm used to look up accounts.
* @param account An optional account. If not {@code null}, the account should be
* checked against accounts on the device. If it is a valid
* account, it should be used to log in the user.
* @param args Authenticator specific arguments used to log in the user.
*/
public void onReceivedLoginRequest(WebView view, String realm,
@Nullable String account, String args) {
}
/**
* Notify host application that the given WebView's render process has exited.
*
* Multiple WebView instances may be associated with a single render process;
* onRenderProcessGone will be called for each WebView that was affected.
* The application's implementation of this callback should only attempt to
* clean up the specific WebView given as a parameter, and should not assume
* that other WebView instances are affected.
*
* The given WebView can't be used, and should be removed from the view hierarchy,
* all references to it should be cleaned up, e.g any references in the Activity
* or other classes saved using {@link android.view.View#findViewById} and similar calls, etc.
*
* To cause an render process crash for test purpose, the application can
* call {@code loadUrl("chrome://crash")} on the WebView. Note that multiple WebView
* instances may be affected if they share a render process, not just the
* specific WebView which loaded chrome://crash.
*
* @param view The WebView which needs to be cleaned up.
* @param detail the reason why it exited.
* @return {@code true} if the host application handled the situation that process has
* exited, otherwise, application will crash if render process crashed,
* or be killed if render process was killed by the system.
*/
public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
return false;
}
/**
* Notify the host application that a loading URL has been flagged by Safe Browsing.
*
* The application must invoke the callback to indicate the preferred response. The default
* behavior is to show an interstitial to the user, with the reporting checkbox visible.
*
* If the application needs to show its own custom interstitial UI, the callback can be invoked
* asynchronously with {@link SafeBrowsingResponse#backToSafety} or {@link
* SafeBrowsingResponse#proceed}, depending on user response.
*
* @param view The WebView that hit the malicious resource.
* @param request Object containing the details of the request.
* @param threatType The reason the resource was caught by Safe Browsing, corresponding to a
* {@code SAFE_BROWSING_THREAT_*} value.
* @param callback Applications must invoke one of the callback methods.
*/
public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
@SafeBrowsingThreat int threatType, SafeBrowsingResponse callback) {
callback.showInterstitial(/* allowReporting */ true);
}
}
@@ -0,0 +1,541 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.webkit;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Picture;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.net.http.SslCertificate;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.print.PrintDocumentAdapter;
import android.util.LongSparseArray;
import android.util.SparseArray;
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.textclassifier.TextClassifier;
import android.webkit.WebView.HitTestResult;
import android.webkit.WebView.PictureListener;
import android.webkit.WebView.VisualStateCallback;
import java.io.BufferedWriter;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* WebView backend provider interface: this interface is the abstract backend to a WebView
* instance; each WebView object is bound to exactly one WebViewProvider object which implements
* the runtime behavior of that WebView.
*
* All methods must behave as per their namesake in {@link WebView}, unless otherwise noted.
*
* @hide Not part of the public API; only required by system implementors.
*/
@SystemApi
public interface WebViewProvider {
//-------------------------------------------------------------------------
// Main interface for backend provider of the WebView class.
//-------------------------------------------------------------------------
/**
* Initialize this WebViewProvider instance. Called after the WebView has fully constructed.
* @param javaScriptInterfaces is a Map of interface names, as keys, and
* object implementing those interfaces, as values.
* @param privateBrowsing If {@code true} the web view will be initialized in private /
* incognito mode.
*/
public void init(Map<String, Object> javaScriptInterfaces,
boolean privateBrowsing);
// Deprecated - should never be called
public void setHorizontalScrollbarOverlay(boolean overlay);
// Deprecated - should never be called
public void setVerticalScrollbarOverlay(boolean overlay);
// Deprecated - should never be called
public boolean overlayHorizontalScrollbar();
// Deprecated - should never be called
public boolean overlayVerticalScrollbar();
public int getVisibleTitleHeight();
public SslCertificate getCertificate();
public void setCertificate(SslCertificate certificate);
public void savePassword(String host, String username, String password);
public void setHttpAuthUsernamePassword(String host, String realm,
String username, String password);
public String[] getHttpAuthUsernamePassword(String host, String realm);
/**
* See {@link WebView#destroy()}.
* As well as releasing the internal state and resources held by the implementation,
* the provider should null all references it holds on the WebView proxy class, and ensure
* no further method calls are made to it.
*/
public void destroy();
public void setNetworkAvailable(boolean networkUp);
public WebBackForwardList saveState(Bundle outState);
public boolean savePicture(Bundle b, final File dest);
public boolean restorePicture(Bundle b, File src);
public WebBackForwardList restoreState(Bundle inState);
public void loadUrl(String url, Map<String, String> additionalHttpHeaders);
public void loadUrl(String url);
public void postUrl(String url, byte[] postData);
public void loadData(String data, String mimeType, String encoding);
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl);
public void evaluateJavaScript(String script, ValueCallback<String> resultCallback);
public void saveWebArchive(String filename);
public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback);
public void stopLoading();
public void reload();
public boolean canGoBack();
public void goBack();
public boolean canGoForward();
public void goForward();
public boolean canGoBackOrForward(int steps);
public void goBackOrForward(int steps);
public boolean isPrivateBrowsingEnabled();
public boolean pageUp(boolean top);
public boolean pageDown(boolean bottom);
public void insertVisualStateCallback(long requestId, VisualStateCallback callback);
public void clearView();
public Picture capturePicture();
public PrintDocumentAdapter createPrintDocumentAdapter(String documentName);
public float getScale();
public void setInitialScale(int scaleInPercent);
public void invokeZoomPicker();
public HitTestResult getHitTestResult();
public void requestFocusNodeHref(Message hrefMsg);
public void requestImageRef(Message msg);
public String getUrl();
public String getOriginalUrl();
public String getTitle();
public Bitmap getFavicon();
public String getTouchIconUrl();
public int getProgress();
public int getContentHeight();
public int getContentWidth();
public void pauseTimers();
public void resumeTimers();
public void onPause();
public void onResume();
public boolean isPaused();
public void freeMemory();
public void clearCache(boolean includeDiskFiles);
public void clearFormData();
public void clearHistory();
public void clearSslPreferences();
public WebBackForwardList copyBackForwardList();
public void setFindListener(WebView.FindListener listener);
public void findNext(boolean forward);
public int findAll(String find);
public void findAllAsync(String find);
public boolean showFindDialog(String text, boolean showIme);
public void clearMatches();
public void documentHasImages(Message response);
public void setWebViewClient(WebViewClient client);
public WebViewClient getWebViewClient();
@Nullable
public WebViewRenderProcess getWebViewRenderProcess();
public void setWebViewRenderProcessClient(
@Nullable Executor executor,
@Nullable WebViewRenderProcessClient client);
@Nullable
public WebViewRenderProcessClient getWebViewRenderProcessClient();
public void setDownloadListener(DownloadListener listener);
public void setWebChromeClient(WebChromeClient client);
public WebChromeClient getWebChromeClient();
public void setPictureListener(PictureListener listener);
public void addJavascriptInterface(Object obj, String interfaceName);
public void removeJavascriptInterface(String interfaceName);
public WebMessagePort[] createWebMessageChannel();
public void postMessageToMainFrame(WebMessage message, Uri targetOrigin);
public WebSettings getSettings();
public void setMapTrackballToArrowKeys(boolean setMap);
public void flingScroll(int vx, int vy);
public View getZoomControls();
public boolean canZoomIn();
public boolean canZoomOut();
public boolean zoomBy(float zoomFactor);
public boolean zoomIn();
public boolean zoomOut();
public void dumpViewHierarchyWithProperties(BufferedWriter out, int level);
public View findHierarchyView(String className, int hashCode);
public void setRendererPriorityPolicy(int rendererRequestedPriority, boolean waivedWhenNotVisible);
public int getRendererRequestedPriority();
public boolean getRendererPriorityWaivedWhenNotVisible();
@SuppressWarnings("unused")
public default void setTextClassifier(@Nullable TextClassifier textClassifier) {}
@NonNull
public default TextClassifier getTextClassifier() { return TextClassifier.NO_OP; }
//-------------------------------------------------------------------------
// Provider internal methods
//-------------------------------------------------------------------------
/**
* @return the ViewDelegate implementation. This provides the functionality to back all of
* the name-sake functions from the View and ViewGroup base classes of WebView.
*/
/* package */ ViewDelegate getViewDelegate();
/**
* @return a ScrollDelegate implementation. Normally this would be same object as is
* returned by getViewDelegate().
*/
/* package */ ScrollDelegate getScrollDelegate();
/**
* Only used by FindActionModeCallback to inform providers that the find dialog has
* been dismissed.
*/
public void notifyFindDialogDismissed();
//-------------------------------------------------------------------------
// View / ViewGroup delegation methods
//-------------------------------------------------------------------------
/**
* Provides mechanism for the name-sake methods declared in View and ViewGroup to be delegated
* into the WebViewProvider instance.
* NOTE: For many of these methods, the WebView will provide a super.Foo() call before or after
* making the call into the provider instance. This is done for convenience in the common case
* of maintaining backward compatibility. For remaining super class calls (e.g. where the
* provider may need to only conditionally make the call based on some internal state) see the
* {@link WebView.PrivateAccess} callback class.
*/
// TODO: See if the pattern of the super-class calls can be rationalized at all, and document
// the remainder on the methods below.
interface ViewDelegate {
public boolean shouldDelayChildPressedState();
public void onProvideVirtualStructure(android.view.ViewStructure structure);
default void onProvideAutofillVirtualStructure(
@SuppressWarnings("unused") android.view.ViewStructure structure,
@SuppressWarnings("unused") int flags) {
}
default void autofill(@SuppressWarnings("unused") SparseArray<AutofillValue> values) {
}
default boolean isVisibleToUserForAutofill(@SuppressWarnings("unused") int virtualId) {
return true; // true is the default value returned by View.isVisibleToUserForAutofill()
}
default void onProvideContentCaptureStructure(
@NonNull @SuppressWarnings("unused") android.view.ViewStructure structure,
@SuppressWarnings("unused") int flags) {
}
// @SuppressLint("NullableCollection")
// default void onCreateVirtualViewTranslationRequests(
// @NonNull @SuppressWarnings("unused") long[] virtualIds,
// @NonNull @SuppressWarnings("unused") @DataFormat int[] supportedFormats,
// @NonNull @SuppressWarnings("unused")
// Consumer<ViewTranslationRequest> requestsCollector) {
// }
// default void onVirtualViewTranslationResponses(
// @NonNull @SuppressWarnings("unused")
// LongSparseArray<ViewTranslationResponse> response) {
// }
// default void dispatchCreateViewTranslationRequest(
// @NonNull @SuppressWarnings("unused") Map<AutofillId, long[]> viewIds,
// @NonNull @SuppressWarnings("unused") @DataFormat int[] supportedFormats,
// @Nullable @SuppressWarnings("unused") TranslationCapability capability,
// @NonNull @SuppressWarnings("unused") List<ViewTranslationRequest> requests) {
// }
public AccessibilityNodeProvider getAccessibilityNodeProvider();
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
public void onInitializeAccessibilityEvent(AccessibilityEvent event);
public boolean performAccessibilityAction(int action, Bundle arguments);
public void setOverScrollMode(int mode);
public void setScrollBarStyle(int style);
public void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar, int l, int t,
int r, int b);
public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY);
public void onWindowVisibilityChanged(int visibility);
public void onDraw(Canvas canvas);
public void setLayoutParams(LayoutParams layoutParams);
public boolean performLongClick();
public void onConfigurationChanged(Configuration newConfig);
public InputConnection onCreateInputConnection(EditorInfo outAttrs);
public boolean onDragEvent(DragEvent event);
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event);
public boolean onKeyDown(int keyCode, KeyEvent event);
public boolean onKeyUp(int keyCode, KeyEvent event);
public void onAttachedToWindow();
public void onDetachedFromWindow();
public default void onMovedToDisplay(int displayId, Configuration config) {}
public void onVisibilityChanged(View changedView, int visibility);
public void onWindowFocusChanged(boolean hasWindowFocus);
public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect);
public boolean setFrame(int left, int top, int right, int bottom);
public void onSizeChanged(int w, int h, int ow, int oh);
public void onScrollChanged(int l, int t, int oldl, int oldt);
public boolean dispatchKeyEvent(KeyEvent event);
public boolean onTouchEvent(MotionEvent ev);
public boolean onHoverEvent(MotionEvent event);
public boolean onGenericMotionEvent(MotionEvent event);
public boolean onTrackballEvent(MotionEvent ev);
public boolean requestFocus(int direction, Rect previouslyFocusedRect);
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec);
public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate);
public void setBackgroundColor(int color);
public void setLayerType(int layerType, Paint paint);
public void preDispatchDraw(Canvas canvas);
public void onStartTemporaryDetach();
public void onFinishTemporaryDetach();
public void onActivityResult(int requestCode, int resultCode, Intent data);
public Handler getHandler(Handler originalHandler);
public View findFocus(View originalFocusedView);
@SuppressWarnings("unused")
default boolean onCheckIsTextEditor() {
return false;
}
/**
* @see View#onApplyWindowInsets(WindowInsets).
*
* <p>This is the entry point for the WebView implementation to override. It returns
* {@code null} when the WebView implementation hasn't implemented the WindowInsets support
* on S yet. In this case, the {@link View#onApplyWindowInsets()} super method will be
* called instead.
*
* @param insets Insets to apply
* @return The supplied insets with any applied insets consumed.
*/
@SuppressWarnings("unused")
@Nullable
default WindowInsets onApplyWindowInsets(@Nullable WindowInsets insets) {
return null;
}
/**
* @hide Only used by WebView.
*/
@SuppressWarnings("unused")
@Nullable
default PointerIcon onResolvePointerIcon(@NonNull MotionEvent event, int pointerIndex) {
return null;
}
}
interface ScrollDelegate {
// These methods are declared protected in the ViewGroup base class. This interface
// exists to promote them to public so they may be called by the WebView proxy class.
// TODO: Combine into ViewDelegate?
/**
* See {@link android.webkit.WebView#computeHorizontalScrollRange}
*/
public int computeHorizontalScrollRange();
/**
* See {@link android.webkit.WebView#computeHorizontalScrollOffset}
*/
public int computeHorizontalScrollOffset();
/**
* See {@link android.webkit.WebView#computeVerticalScrollRange}
*/
public int computeVerticalScrollRange();
/**
* See {@link android.webkit.WebView#computeVerticalScrollOffset}
*/
public int computeVerticalScrollOffset();
/**
* See {@link android.webkit.WebView#computeVerticalScrollExtent}
*/
public int computeVerticalScrollExtent();
/**
* See {@link android.webkit.WebView#computeScroll}
*/
public void computeScroll();
}
}
@@ -0,0 +1,108 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RemoteViews.RemoteView;
@Deprecated
@RemoteView
public class AbsoluteLayout extends ViewGroup {
public AbsoluteLayout(Context context) {
this(context, null);
}
public AbsoluteLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AbsoluteLayout(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public AbsoluteLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
throw new RuntimeException("Stub!");
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0);
}
@Override
protected void onLayout(boolean changed, int l, int t,
int r, int b) {
throw new RuntimeException("Stub!");
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new AbsoluteLayout.LayoutParams(getContext(), attrs);
}
// Override to allow type-checking of LayoutParams.
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof AbsoluteLayout.LayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
@Override
public boolean shouldDelayChildPressedState() {
return false;
}
public static class LayoutParams extends ViewGroup.LayoutParams {
public int x;
public int y;
public LayoutParams(int width, int height, int x, int y) {
super(width, height);
this.x = x;
this.y = y;
}
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
throw new RuntimeException("Stub!");
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
public String debug(String output) {
return output + "Absolute.LayoutParams={width="
+ String.valueOf(width) + ", height=" + String.valueOf(height)
+ " x=" + x + " y=" + y + "}";
}
}
}
@@ -0,0 +1,40 @@
package android.widget;
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
public class EditText {
public EditText(android.content.Context context) { throw new RuntimeException("Stub!"); }
public EditText(android.content.Context context, android.util.AttributeSet attrs) { throw new RuntimeException("Stub!"); }
public EditText(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr) { throw new RuntimeException("Stub!"); }
public EditText(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes) { throw new RuntimeException("Stub!"); }
public boolean getFreezesText() { throw new RuntimeException("Stub!"); }
protected boolean getDefaultEditable() { throw new RuntimeException("Stub!"); }
protected android.text.method.MovementMethod getDefaultMovementMethod() { throw new RuntimeException("Stub!"); }
public android.text.Editable getText() { throw new RuntimeException("Stub!"); }
public void setText(java.lang.CharSequence text, android.widget.TextView.BufferType type) { throw new RuntimeException("Stub!"); }
public void setSelection(int start, int stop) { throw new RuntimeException("Stub!"); }
public void setSelection(int index) { throw new RuntimeException("Stub!"); }
public void selectAll() { throw new RuntimeException("Stub!"); }
public void extendSelection(int index) { throw new RuntimeException("Stub!"); }
public void setEllipsize(android.text.TextUtils.TruncateAt ellipsis) { throw new RuntimeException("Stub!"); }
public java.lang.CharSequence getAccessibilityClassName() { throw new RuntimeException("Stub!"); }
}
@@ -0,0 +1,91 @@
package android.widget;
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
public class Toast {
public static final int LENGTH_LONG = 1;
public static final int LENGTH_SHORT = 0;
private CharSequence text;
private Toast(CharSequence text) {
this.text = text;
}
public Toast(android.content.Context context) {
throw new RuntimeException("Stub!");
}
public void show() {
System.out.printf("made a Toast: \"%s\"\n", text.toString());
}
public void cancel() {
throw new RuntimeException("Stub!");
}
public void setView(android.view.View view) {
throw new RuntimeException("Stub!");
}
public android.view.View getView() {
throw new RuntimeException("Stub!");
}
public void setDuration(int duration) {
throw new RuntimeException("Stub!");
}
public int getDuration() {
throw new RuntimeException("Stub!");
}
public void setMargin(float horizontalMargin, float verticalMargin) {
throw new RuntimeException("Stub!");
}
public float getHorizontalMargin() {
throw new RuntimeException("Stub!");
}
public float getVerticalMargin() {
throw new RuntimeException("Stub!");
}
public void setGravity(int gravity, int xOffset, int yOffset) {
throw new RuntimeException("Stub!");
}
public int getGravity() {
throw new RuntimeException("Stub!");
}
public int getXOffset() {
throw new RuntimeException("Stub!");
}
public int getYOffset() {
throw new RuntimeException("Stub!");
}
public static Toast makeText(android.content.Context context, java.lang.CharSequence text, int duration) {
return new Toast(text);
}
public static android.widget.Toast makeText(android.content.Context context, int resId, int duration) throws android.content.res.Resources.NotFoundException {
throw new RuntimeException("Stub!");
}
public void setText(int resId) {
throw new RuntimeException("Stub!");
}
public void setText(java.lang.CharSequence s) {
throw new RuntimeException("Stub!");
}
}
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
package androidx.core.net
import android.net.Uri
import java.io.File
/**
* Creates a Uri from the given encoded URI string.
*
* @see Uri.parse
*/
public inline fun String.toUri(): Uri = Uri.parse(this)
/**
* Creates a Uri from the given file.
*
* @see Uri.fromFile
*/
public inline fun File.toUri(): Uri = Uri.fromFile(this)
/**
* Creates a [File] from the given [Uri]. Note that this will throw an
* [IllegalArgumentException] when invoked on a [Uri] that lacks `file` scheme.
*/
public fun Uri.toFile(): File {
require(scheme == "file") { "Uri lacks 'file' scheme: $this" }
return File(requireNotNull(path) { "Uri path is null: $this" })
}
@@ -0,0 +1,18 @@
package androidx.preference;
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import android.content.Context;
public class CheckBoxPreference extends TwoStatePreference {
// reference: https://android.googlesource.com/platform/frameworks/support/+/996971f962fcd554339a7cb2859cef9ca89dbcb7/preference/preference/src/main/java/androidx/preference/CheckBoxPreference.java
public CheckBoxPreference(Context context) {
super(context);
}
}
@@ -0,0 +1,33 @@
package androidx.preference;
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import android.content.Context;
public abstract class DialogPreference extends Preference {
private CharSequence dialogTitle;
private CharSequence dialogMessage;
public DialogPreference(Context context) { super(context); }
public CharSequence getDialogTitle() {
return dialogTitle;
}
public void setDialogTitle(CharSequence dialogTitle) {
this.dialogTitle = dialogTitle;
}
public CharSequence getDialogMessage() {
return dialogMessage;
}
public void setDialogMessage(CharSequence dialogMessage) {
this.dialogMessage = dialogMessage;
}
}
@@ -0,0 +1,53 @@
package androidx.preference;
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.widget.EditText;
import com.fasterxml.jackson.annotation.JsonIgnore;
public class EditTextPreference extends DialogPreference {
// reference: https://android.googlesource.com/platform/frameworks/support/+/996971f962fcd554339a7cb2859cef9ca89dbcb7/preference/preference/src/main/java/androidx/preference/EditTextPreference.java
private String text;
@JsonIgnore
private OnBindEditTextListener onBindEditTextListener;
public EditTextPreference(Context context) {
super(context);
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public OnBindEditTextListener getOnBindEditTextListener() {
return onBindEditTextListener;
}
public void setOnBindEditTextListener(@Nullable OnBindEditTextListener onBindEditTextListener) {
this.onBindEditTextListener = onBindEditTextListener;
}
public interface OnBindEditTextListener {
void onBindEditText(@NonNull EditText editText);
}
/** Tachidesk specific API */
@Override
public String getDefaultValueType() {
return "String";
}
}
@@ -0,0 +1,66 @@
package androidx.preference;
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import android.content.Context;
import android.text.TextUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;
public class ListPreference extends Preference {
// reference: https://android.googlesource.com/platform/frameworks/support/+/996971f962fcd554339a7cb2859cef9ca89dbcb7/preference/preference/src/main/java/androidx/preference/ListPreference.java
// Note: remove @JsonIgnore and implement methods if any extension ever uses these methods or the variables behind them
private CharSequence[] entries;
private CharSequence[] entryValues;
public ListPreference(Context context) {
super(context);
}
public CharSequence[] getEntries() {
return entries;
}
public void setEntries(CharSequence[] entries) {
this.entries = entries;
}
public int findIndexOfValue(String value) {
if (value != null && entryValues != null) {
for (int i = entryValues.length - 1; i >= 0; i--) {
if (TextUtils.equals(entryValues[i].toString(), value)) {
return i;
}
}
}
return -1;
}
public CharSequence[] getEntryValues() {
return entryValues;
}
public void setEntryValues(CharSequence[] entryValues) {
this.entryValues = entryValues;
}
@JsonIgnore
public void setValueIndex(int index) { throw new RuntimeException("Stub!"); }
@JsonIgnore
public String getValue() { throw new RuntimeException("Stub!"); }
@JsonIgnore
public void setValue(String value) { throw new RuntimeException("Stub!"); }
/** Tachidesk specific API */
@Override
public String getDefaultValueType() {
return "String";
}
}
@@ -0,0 +1,61 @@
package androidx.preference;
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import android.content.Context;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.Set;
public class MultiSelectListPreference extends DialogPreference {
// reference: https://android.googlesource.com/platform/frameworks/support/+/996971f962fcd554339a7cb2859cef9ca89dbcb7/preference/preference/src/main/java/androidx/preference/MultiSelectListPreference.java
// Note: remove @JsonIgnore and implement methods if any extension ever uses these methods or the variables behind them
private CharSequence[] entries;
private CharSequence[] entryValues;
public MultiSelectListPreference(Context context) {
super(context);
}
public void setEntries(CharSequence[] entries) {
this.entries = entries;
}
public CharSequence[] getEntries() {
return entries;
}
public void setEntryValues(CharSequence[] entryValues) {
this.entryValues = entryValues;
}
public CharSequence[] getEntryValues() {
return entryValues;
}
@JsonIgnore
public void setValues(Set<String> values) {
throw new RuntimeException("Stub!");
}
@JsonIgnore
public Set<String> getValues() {
throw new RuntimeException("Stub!");
}
public int findIndexOfValue(String value) {
throw new RuntimeException("Stub!");
}
/** Tachidesk specific API */
@Override
public String getDefaultValueType() {
return "Set<String>";
}
}
@@ -0,0 +1,162 @@
package androidx.preference;
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import android.content.Context;
import android.content.SharedPreferences;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.Set;
/**
* A minimal implementation of androidx.preference.Preference
*/
public class Preference {
// reference: https://android.googlesource.com/platform/frameworks/support/+/996971f962fcd554339a7cb2859cef9ca89dbcb7/preference/preference/src/main/java/androidx/preference/Preference.java
// Note: `Preference` doesn't actually hold or persist the value, `OnPreferenceChangeListener` is called and it's up to the extension to persist it.
@JsonIgnore
protected Context context;
private boolean isVisible;
private boolean isEnabled = true;
private String key;
private CharSequence title;
private CharSequence summary;
private Object defaultValue;
/** Tachidesk specific API */
@JsonIgnore
private SharedPreferences sharedPreferences;
@JsonIgnore
public OnPreferenceChangeListener onChangeListener;
public Preference(Context context) {
this.context = context;
}
public Context getContext() {
return context;
}
public void setOnPreferenceChangeListener(OnPreferenceChangeListener onPreferenceChangeListener) {
this.onChangeListener = onPreferenceChangeListener;
}
public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) {
throw new RuntimeException("Stub!");
}
public CharSequence getTitle() {
return title;
}
public void setTitle(CharSequence title) {
this.title = title;
}
public CharSequence getSummary() {
return summary;
}
public void setSummary(CharSequence summary) {
this.summary = summary;
}
public void setEnabled(boolean enabled) {
isEnabled = enabled;
}
public boolean isEnabled() {
return isEnabled;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public void setDefaultValue(Object defaultValue) {
this.defaultValue = defaultValue;
}
public boolean callChangeListener(Object newValue) {
return onChangeListener == null || onChangeListener.onPreferenceChange(this, newValue);
}
public Object getDefaultValue() {
return defaultValue;
}
/** Tachidesk specific API */
public String getDefaultValueType() {
return defaultValue.getClass().getSimpleName();
}
/** Tachidesk specific API */
public SharedPreferences getSharedPreferences() {
return sharedPreferences;
}
public void setVisible(boolean visible) {
isVisible = visible;
}
public boolean getVisible() {
return isVisible;
}
/** Tachidesk specific API */
public void setSharedPreferences(SharedPreferences sharedPreferences) {
this.sharedPreferences = sharedPreferences;
}
public interface OnPreferenceChangeListener {
boolean onPreferenceChange(Preference preference, Object newValue);
}
public interface OnPreferenceClickListener {
boolean onPreferenceClick(Preference preference);
}
/** Tachidesk specific API */
@SuppressWarnings("unchecked")
public Object getCurrentValue() {
switch (getDefaultValueType()) {
case "String":
return sharedPreferences.getString(key, (String)defaultValue);
case "Boolean":
return sharedPreferences.getBoolean(key, (Boolean)defaultValue);
case "Set<String>":
return sharedPreferences.getStringSet(key, (Set<String>)defaultValue);
default:
throw new RuntimeException("Unsupported type");
}
}
/** Tachidesk specific API */
@SuppressWarnings("unchecked")
public void saveNewValue(Object value) {
switch (getDefaultValueType()) {
case "String":
sharedPreferences.edit().putString(key, (String)value).apply();
break;
case "Boolean":
sharedPreferences.edit().putBoolean(key, (Boolean)value).apply();
break;
case "Set<String>":
sharedPreferences.edit().putStringSet(key, (Set<String>)value).apply();
break;
default:
throw new RuntimeException("Unsupported type");
}
}
}
@@ -0,0 +1,36 @@
package androidx.preference;
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import android.content.Context;
import java.util.LinkedList;
import java.util.List;
public class PreferenceScreen extends Preference {
/** Tachidesk specific API */
private List<Preference> preferences = new LinkedList<>();
public PreferenceScreen(Context context) {
super(context);
}
public boolean addPreference(Preference preference) {
// propagate own shared preferences
preference.setSharedPreferences(getSharedPreferences());
preferences.add(preference);
return true;
}
/** Tachidesk specific API */
public List<Preference> getPreferences(){
return preferences;
}
}
@@ -0,0 +1,18 @@
package androidx.preference;
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import android.content.Context;
public class SwitchPreferenceCompat extends TwoStatePreference {
// reference: https://android.googlesource.com/platform/frameworks/support/+/996971f962fcd554339a7cb2859cef9ca89dbcb7/preference/preference/src/main/java/androidx/preference/CheckBoxPreference.java
public SwitchPreferenceCompat(Context context) {
super(context);
}
}
@@ -0,0 +1,76 @@
package androidx.preference;
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import android.content.Context;
import com.fasterxml.jackson.annotation.JsonIgnore;
public class TwoStatePreference extends Preference {
private CharSequence mSummaryOn;
private CharSequence mSummaryOff;
public TwoStatePreference(Context context) {
super(context);
setDefaultValue(false);
}
@JsonIgnore
public boolean isChecked() { throw new RuntimeException("Stub!"); }
@JsonIgnore
public void setChecked(boolean checked) { throw new RuntimeException("Stub!"); }
@JsonIgnore
public CharSequence getSummaryOn() {
return mSummaryOn;
}
@JsonIgnore
public void setSummaryOn(CharSequence summary) {
this.mSummaryOn = summary;
}
@JsonIgnore
public CharSequence getSummaryOff() {
return mSummaryOff;
}
@JsonIgnore
public void setSummaryOff(CharSequence summary) {
this.mSummaryOff = summary;
}
@Override
public CharSequence getSummary() {
final CharSequence summary = super.getSummary();
if (summary != null) {
return summary;
}
final boolean checked = (Boolean) getCurrentValue();
if (checked && mSummaryOn != null) {
return mSummaryOn;
} else if (!checked && mSummaryOff != null) {
return mSummaryOff;
}
return null;
}
@JsonIgnore
public boolean getDisableDependentsState() { throw new RuntimeException("Stub!"); }
@JsonIgnore
public void setDisableDependentsState(boolean disableDependentsState) { throw new RuntimeException("Stub!"); }
/** Tachidesk specific API */
@Override
public String getDefaultValueType() {
return "Boolean";
}
}
@@ -0,0 +1,99 @@
package app.cash.quickjs;
import org.graalvm.polyglot.*;
import java.io.Closeable;
import java.math.BigInteger;
import java.util.Arrays;
public final class QuickJs implements Closeable {
private Context context;
public static QuickJs create() {
return new QuickJs();
}
public QuickJs() {
this.context = Context
.newBuilder("js")
.allowHostAccess(HostAccess.ALL)
.allowPolyglotAccess(PolyglotAccess.NONE)
.allowHostClassLoading(false)
.build();
context.enter();
}
public Object evaluate(String script, String ignoredFileName) {
return this.evaluate(script);
}
public Object evaluate(String script) {
try {
Value value = context.eval("js", script);
return translateType(value);
} catch (Exception exception) {
throw new QuickJsException(exception.getMessage(), exception);
}
}
private Object translateType(Value obj) {
if (obj.isBoolean()) {
return obj.asBoolean();
} else if (obj.hasArrayElements()) {
if (obj.getArraySize() == 0) {
return new int[0];
} else {
Value element = obj.getArrayElement(0);
if (element.isBoolean()) {
return obj.as(boolean[].class);
} else if (element.isNumber()) {
if (element.fitsInInt()) {
return obj.as(int[].class);
} else if (element.fitsInBigInteger()) {
return Arrays.stream(obj.as(BigInteger[].class)).map(BigInteger::longValue).toArray();
} else {
return obj.as(double[].class);
}
} else if (element.isHostObject()) {
return obj.as(Object[].class);
} else if (element.isString()) {
return obj.as(String[].class);
}
}
} else if (obj.isNumber()) {
if (obj.fitsInInt()) {
return obj.asInt();
} else if (obj.fitsInBigInteger()) {
return obj.asBigInteger().longValue();
} else {
return obj.asDouble();
}
} else if (obj.isHostObject()) {
return obj.asHostObject();
} else if (obj.isString()) {
return obj.asString();
}
return obj;
}
public byte[] compile(String sourceCode, String ignoredFileName) {
return sourceCode.getBytes();
}
public Object execute(byte[] bytecode) {
return this.evaluate(new String(bytecode));
}
public <T> void set(String name, Class<T> ignoredType, T object) {
context.getBindings("js").putMember(name, object);
}
@Override
public void close() {
if (this.context != null) {
this.context.leave();
this.context.close();
this.context = null;
}
}
}
@@ -0,0 +1,7 @@
package app.cash.quickjs;
public final class QuickJsException extends RuntimeException {
public QuickJsException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -14,13 +14,13 @@
* limitations under the License.
*/
package com.android.internal.util;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArraySet;
import libcore.util.EmptyArray;
import java.lang.reflect.Array;
import java.util.*;
import libcore.util.EmptyArray;
import android.annotation.NonNull;
/**
* ArrayUtils contains some methods that you can call to find out
* the most efficient increments by which to grow arrays.
@@ -50,6 +50,10 @@ public class ArrayUtils {
public static Object[] newUnpaddedObjectArray(int minLen) {
return new Object[minLen];
}
@SuppressWarnings("unchecked")
public static <T> T[] newUnpaddedArray(Class<T> clazz, int minLen) {
return (T[])Array.newInstance(clazz, minLen);
}
/**
* Checks if the beginnings of two byte arrays are equal.
*
@@ -468,4 +472,4 @@ public class ArrayUtils {
}
return size - leftIdx;
}
}
}
@@ -0,0 +1,155 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.util;
public final class GrowingArrayUtils {
public static <T> T[] append(T[] array, int currentSize, T element) {
assert currentSize <= array.length;
if (currentSize + 1 > array.length) {
@SuppressWarnings("unchecked")
T[] newArray = ArrayUtils.newUnpaddedArray(
(Class<T>) array.getClass().getComponentType(), growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, currentSize);
array = newArray;
}
array[currentSize] = element;
return array;
}
public static int[] append(int[] array, int currentSize, int element) {
assert currentSize <= array.length;
if (currentSize + 1 > array.length) {
int[] newArray = ArrayUtils.newUnpaddedIntArray(growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, currentSize);
array = newArray;
}
array[currentSize] = element;
return array;
}
public static long[] append(long[] array, int currentSize, long element) {
assert currentSize <= array.length;
if (currentSize + 1 > array.length) {
long[] newArray = ArrayUtils.newUnpaddedLongArray(growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, currentSize);
array = newArray;
}
array[currentSize] = element;
return array;
}
public static boolean[] append(boolean[] array, int currentSize, boolean element) {
assert currentSize <= array.length;
if (currentSize + 1 > array.length) {
boolean[] newArray = ArrayUtils.newUnpaddedBooleanArray(growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, currentSize);
array = newArray;
}
array[currentSize] = element;
return array;
}
public static float[] append(float[] array, int currentSize, float element) {
assert currentSize <= array.length;
if (currentSize + 1 > array.length) {
float[] newArray = ArrayUtils.newUnpaddedFloatArray(growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, currentSize);
array = newArray;
}
array[currentSize] = element;
return array;
}
public static <T> T[] insert(T[] array, int currentSize, int index, T element) {
assert currentSize <= array.length;
if (currentSize + 1 <= array.length) {
System.arraycopy(array, index, array, index + 1, currentSize - index);
array[index] = element;
return array;
}
@SuppressWarnings("unchecked")
T[] newArray = ArrayUtils.newUnpaddedArray((Class<T>)array.getClass().getComponentType(),
growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, index);
newArray[index] = element;
System.arraycopy(array, index, newArray, index + 1, array.length - index);
return newArray;
}
public static int[] insert(int[] array, int currentSize, int index, int element) {
assert currentSize <= array.length;
if (currentSize + 1 <= array.length) {
System.arraycopy(array, index, array, index + 1, currentSize - index);
array[index] = element;
return array;
}
int[] newArray = ArrayUtils.newUnpaddedIntArray(growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, index);
newArray[index] = element;
System.arraycopy(array, index, newArray, index + 1, array.length - index);
return newArray;
}
public static long[] insert(long[] array, int currentSize, int index, long element) {
assert currentSize <= array.length;
if (currentSize + 1 <= array.length) {
System.arraycopy(array, index, array, index + 1, currentSize - index);
array[index] = element;
return array;
}
long[] newArray = ArrayUtils.newUnpaddedLongArray(growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, index);
newArray[index] = element;
System.arraycopy(array, index, newArray, index + 1, array.length - index);
return newArray;
}
public static boolean[] insert(boolean[] array, int currentSize, int index, boolean element) {
assert currentSize <= array.length;
if (currentSize + 1 <= array.length) {
System.arraycopy(array, index, array, index + 1, currentSize - index);
array[index] = element;
return array;
}
boolean[] newArray = ArrayUtils.newUnpaddedBooleanArray(growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, index);
newArray[index] = element;
System.arraycopy(array, index, newArray, index + 1, array.length - index);
return newArray;
}
public static int growSize(int currentSize) {
return currentSize <= 4 ? 8 : currentSize * 2;
}
// Uninstantiable
private GrowingArrayUtils() {}
}
@@ -14,9 +14,10 @@
* limitations under the License.
*/
package com.android.internal.util;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -25,19 +26,14 @@ import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ProtocolException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/** {@hide} */
public class XmlUtils {
private static final String STRING_ARRAY_SEPARATOR = ":";
@@ -1396,9 +1392,9 @@ public class XmlUtils {
} else if (tagName.equals("long")) {
return Long.valueOf(parser.getAttributeValue(null, "value"));
} else if (tagName.equals("float")) {
return new Float(parser.getAttributeValue(null, "value"));
return Float.valueOf(parser.getAttributeValue(null, "value"));
} else if (tagName.equals("double")) {
return new Double(parser.getAttributeValue(null, "value"));
return Double.valueOf(parser.getAttributeValue(null, "value"));
} else if (tagName.equals("boolean")) {
return Boolean.valueOf(parser.getAttributeValue(null, "value"));
} else {
@@ -1,2 +0,0 @@
package com.f2prateek;
//TODO Consider if we can change this package into an Android dependency
@@ -1,34 +0,0 @@
/*
Copyright 2014 Prateek Srivastava
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This file may have been modified after being copied from it's original source.
*/
package com.f2prateek.rx.preferences;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
final class BooleanAdapter implements Preference.Adapter<Boolean> {
static final BooleanAdapter INSTANCE = new BooleanAdapter();
@Override public Boolean get(@NonNull String key, @NonNull SharedPreferences preferences) {
return preferences.getBoolean(key, false);
}
@Override public void set(@NonNull String key, @NonNull Boolean value,
@NonNull SharedPreferences.Editor editor) {
editor.putBoolean(key, value);
}
}
@@ -1,40 +0,0 @@
/*
Copyright 2014 Prateek Srivastava
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This file may have been modified after being copied from it's original source.
*/
package com.f2prateek.rx.preferences;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
final class EnumAdapter<T extends Enum<T>> implements Preference.Adapter<T> {
private final Class<T> enumClass;
EnumAdapter(Class<T> enumClass) {
this.enumClass = enumClass;
}
@Override public T get(@NonNull String key, @NonNull SharedPreferences preferences) {
String value = preferences.getString(key, null);
assert value != null; // Not called unless key is present.
return Enum.valueOf(enumClass, value);
}
@Override
public void set(@NonNull String key, @NonNull T value, @NonNull SharedPreferences.Editor editor) {
editor.putString(key, value.name());
}
}
@@ -1,34 +0,0 @@
/*
Copyright 2014 Prateek Srivastava
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This file may have been modified after being copied from it's original source.
*/
package com.f2prateek.rx.preferences;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
final class FloatAdapter implements Preference.Adapter<Float> {
static final FloatAdapter INSTANCE = new FloatAdapter();
@Override public Float get(@NonNull String key, @NonNull SharedPreferences preferences) {
return preferences.getFloat(key, 0f);
}
@Override public void set(@NonNull String key, @NonNull Float value,
@NonNull SharedPreferences.Editor editor) {
editor.putFloat(key, value);
}
}
@@ -1,34 +0,0 @@
/*
Copyright 2014 Prateek Srivastava
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This file may have been modified after being copied from it's original source.
*/
package com.f2prateek.rx.preferences;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
final class IntegerAdapter implements Preference.Adapter<Integer> {
static final IntegerAdapter INSTANCE = new IntegerAdapter();
@Override public Integer get(@NonNull String key, @NonNull SharedPreferences preferences) {
return preferences.getInt(key, 0);
}
@Override public void set(@NonNull String key, @NonNull Integer value,
@NonNull SharedPreferences.Editor editor) {
editor.putInt(key, value);
}
}
@@ -1,34 +0,0 @@
/*
Copyright 2014 Prateek Srivastava
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This file may have been modified after being copied from it's original source.
*/
package com.f2prateek.rx.preferences;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
final class LongAdapter implements Preference.Adapter<Long> {
static final LongAdapter INSTANCE = new LongAdapter();
@Override public Long get(@NonNull String key, @NonNull SharedPreferences preferences) {
return preferences.getLong(key, 0L);
}
@Override public void set(@NonNull String key, @NonNull Long value,
@NonNull SharedPreferences.Editor editor) {
editor.putLong(key, value);
}
}

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