Compare commits

...

94 Commits

Author SHA1 Message Date
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
241 changed files with 3518 additions and 19776 deletions
+4 -11
View File
@@ -23,7 +23,7 @@ jobs:
steps: steps:
- name: Cancel previous runs - name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.5.0 uses: styfle/cancel-workflow-action@0.9.0
with: with:
access_token: ${{ github.token }} access_token: ${{ github.token }}
@@ -48,21 +48,14 @@ jobs:
- name: Download android.jar - name: Download android.jar
run: | run: |
cd master cd master
curl https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar curl https://raw.githubusercontent.com/Suwayomi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
- name: Cache node_modules - name: Build Jar
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 uses: eskatos/gradle-command-action@v1
with: with:
build-root-directory: master build-root-directory: master
wrapper-directory: master wrapper-directory: master
arguments: :webUI:copyBuild :server:shadowJar --stacktrace arguments: :server:shadowJar --stacktrace
wrapper-cache-enabled: true wrapper-cache-enabled: true
dependencies-cache-enabled: true dependencies-cache-enabled: true
configuration-cache-enabled: true configuration-cache-enabled: true
+11 -14
View File
@@ -25,7 +25,7 @@ jobs:
steps: steps:
- name: Cancel previous runs - name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.5.0 uses: styfle/cancel-workflow-action@0.9.0
with: with:
access_token: ${{ github.token }} access_token: ${{ github.token }}
@@ -50,23 +50,16 @@ jobs:
- name: Download android.jar - name: Download android.jar
run: | run: |
cd master cd master
curl https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar curl https://raw.githubusercontent.com/Suwayomi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
- name: Cache node_modules - name: Build Jar
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 uses: eskatos/gradle-command-action@v1
env: env:
TachideskBuildType: "Preview" ProductBuildType: "Preview"
with: with:
build-root-directory: master build-root-directory: master
wrapper-directory: master wrapper-directory: master
arguments: :webUI:copyBuild :server:shadowJar --stacktrace arguments: :server:shadowJar --stacktrace
wrapper-cache-enabled: true wrapper-cache-enabled: true
dependencies-cache-enabled: true dependencies-cache-enabled: true
configuration-cache-enabled: true configuration-cache-enabled: true
@@ -99,7 +92,7 @@ jobs:
- name: Checkout preview branch - name: Checkout preview branch
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
repository: 'Suwayomi/Tachidesk-preview' repository: 'Suwayomi/Tachidesk-Server-preview'
ref: main ref: main
path: preview path: preview
token: ${{ secrets.DEPLOY_PREVIEW_TOKEN }} token: ${{ secrets.DEPLOY_PREVIEW_TOKEN }}
@@ -125,5 +118,9 @@ jobs:
token: ${{ secrets.DEPLOY_PREVIEW_TOKEN }} token: ${{ secrets.DEPLOY_PREVIEW_TOKEN }}
artifacts: "master/server/build/*.jar,master/server/build/*.zip" artifacts: "master/server/build/*.jar,master/server/build/*.zip"
owner: "Suwayomi" owner: "Suwayomi"
repo: "Tachidesk-preview" repo: "Tachidesk-Server-preview"
tag: ${{ steps.GenTagName.outputs.value }} tag: ${{ steps.GenTagName.outputs.value }}
- name: Run Docker build workflow
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": "preview"}}' https://api.github.com/repos/suwayomi/docker-tachidesk/actions/workflows/build_container_images.yml/dispatches
+11 -6
View File
@@ -24,7 +24,7 @@ jobs:
steps: steps:
- name: Cancel previous runs - name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.5.0 uses: styfle/cancel-workflow-action@0.9.0
with: with:
access_token: ${{ github.token }} access_token: ${{ github.token }}
@@ -49,23 +49,23 @@ jobs:
- name: Download android.jar - name: Download android.jar
run: | run: |
cd master cd master
curl https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar curl https://raw.githubusercontent.com/Suwayomi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
- name: Cache node_modules - name: Cache node_modules
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: | path: |
**/react/node_modules **/webUI/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/react/yarn.lock') }} key: ${{ runner.os }}-${{ hashFiles('**/webUI/yarn.lock') }}
- name: Build and copy webUI, Build Jar - name: Build and copy webUI, Build Jar
uses: eskatos/gradle-command-action@v1 uses: eskatos/gradle-command-action@v1
env: env:
TachideskBuildType: "Stable" ProductBuildType: "Stable"
with: with:
build-root-directory: master build-root-directory: master
wrapper-directory: master wrapper-directory: master
arguments: :webUI:copyBuild :server:shadowJar --stacktrace arguments: :server:downloadWebUI :server:shadowJar --stacktrace
wrapper-cache-enabled: true wrapper-cache-enabled: true
dependencies-cache-enabled: true dependencies-cache-enabled: true
configuration-cache-enabled: true configuration-cache-enabled: true
@@ -85,3 +85,8 @@ jobs:
tags: true tags: true
draft: true draft: true
verbose: true verbose: true
- name: Run Docker build workflow
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
+1 -1
View File
@@ -6,7 +6,7 @@ gradle.properties
# Ignore Gradle build output directory # Ignore Gradle build output directory
build build
server/src/main/resources/react server/src/main/resources/WebUI.zip
server/tmp/ server/tmp/
server/tachiserver-data/ server/tachiserver-data/
@@ -12,7 +12,7 @@ import net.harawata.appdirs.AppDirsFactory
val ApplicationRootDir: String val ApplicationRootDir: String
get(): String { get(): String {
return System.getProperty( return System.getProperty(
"suwayomi.server.rootDir", "suwayomi.tachidesk.server.rootDir",
AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null) AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)
) )
} }
@@ -17,4 +17,4 @@ fun setLogLevel(level: Level) {
} }
fun debugLogsEnabled(config: Config) fun debugLogsEnabled(config: Config)
= System.getProperty("suwayomi.server.debugLogsEnabled", config.getString("server.debugLogsEnabled")).toBoolean() = System.getProperty("suwayomi.tachidesk.server.debugLogsEnabled", config.getString("server.debugLogsEnabled")).toBoolean()
+19 -4
View File
@@ -1,6 +1,7 @@
plugins { plugins {
application application
kotlin("plugin.serialization")
} }
@@ -24,13 +25,13 @@ dependencies {
// compileOnly( fileTree(dir: new File(rootProject.rootDir, "libs/other"), include: "*.jar") // compileOnly( fileTree(dir: new File(rootProject.rootDir, "libs/other"), include: "*.jar")
// JSON // JSON
compileOnly( "com.google.code.gson:gson:2.8.6") compileOnly("com.google.code.gson:gson:2.8.6")
// Javassist // Javassist
compileOnly( "org.javassist:javassist:3.27.0-GA") compileOnly("org.javassist:javassist:3.27.0-GA")
// XML // XML
compileOnly( group= "xmlpull", name= "xmlpull", version= "1.1.3.1") compileOnly(group= "xmlpull", name= "xmlpull", version= "1.1.3.1")
// Config API // Config API
implementation(project(":AndroidCompat:Config")) implementation(project(":AndroidCompat:Config"))
@@ -39,13 +40,27 @@ dependencies {
compileOnly("com.android.tools.build:apksig:4.2.0-alpha13") compileOnly("com.android.tools.build:apksig:4.2.0-alpha13")
// AndroidX annotations // AndroidX annotations
compileOnly( "androidx.annotation:annotation:1.2.0-alpha01") compileOnly("androidx.annotation:annotation:1.2.0-alpha01")
// substitute for duktape-android // substitute for duktape-android
// 'org.mozilla:rhino' includes some code that we don't need so use 'org.mozilla:rhino-runtime' instead // '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") implementation("org.mozilla:rhino-runtime:1.7.13")
// 'org.mozilla:rhino-engine' provides the same interface as 'javax.script' a.k.a Nashorn // 'org.mozilla:rhino-engine' provides the same interface as 'javax.script' a.k.a Nashorn
implementation("org.mozilla:rhino-engine:1.7.13") implementation("org.mozilla:rhino-engine:1.7.13")
// Kotlin wrapper around Java Preferences, makes certain things easier
val multiplatformSettingsVersion = "0.7.7"
implementation("com.russhwolf:multiplatform-settings-jvm:$multiplatformSettingsVersion")
implementation("com.russhwolf:multiplatform-settings-serialization-jvm:$multiplatformSettingsVersion")
// Android version of SimpleDateFormat
implementation("com.ibm.icu:icu4j:69.1")
}
tasks {
withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.freeCompilerArgs = listOf("-Xopt-in=kotlin.RequiresOptIn")
}
} }
//def fatJarTask = tasks.getByPath(':AndroidCompat:JVMPatch:fatJar') //def fatJarTask = tasks.getByPath(':AndroidCompat:JVMPatch:fatJar')
+15 -7
View File
@@ -15,7 +15,7 @@ Write-Output "Getting required Android.jar..."
Remove-Item -Recurse -Force "tmp" -ErrorAction SilentlyContinue | Out-Null Remove-Item -Recurse -Force "tmp" -ErrorAction SilentlyContinue | Out-Null
New-Item -ItemType Directory -Force -Path "tmp" | 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" $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 # We need to remove any stub classes that we have implementations for
Write-Output "Patching JAR..." Write-Output "Patching JAR..."
function Remove-Files-Zip($zipfile, $path) function Remove-Files-Zip($zipfile, $paths)
{ {
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression') | Out-Null [Reflection.Assembly]::LoadWithPartialName('System.IO.Compression') | Out-Null
@@ -32,7 +32,18 @@ function Remove-Files-Zip($zipfile, $path)
$mode = [IO.Compression.ZipArchiveMode]::Update $mode = [IO.Compression.ZipArchiveMode]::Update
$zip = New-Object IO.Compression.ZipArchive($stream, $mode) $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() $zip.Dispose()
$stream.Close() $stream.Close()
@@ -78,10 +89,7 @@ function Dedupe($path)
$classes = Get-ChildItem . *.* -Recurse | Where-Object { !$_.PSIsContainer } $classes = Get-ChildItem . *.* -Recurse | Where-Object { !$_.PSIsContainer }
$classes | ForEach-Object { $classes | ForEach-Object {
"Processing class: $($_.FullName)" "Processing class: $($_.FullName)"
Remove-Files-Zip $android_jar "$($_.Name).class" | Out-Null Remove-Files-Zip $android_jar ("$($_.Name).class","$($_.Name)$*.class","$($_.Name)Kt.class","$($_.Name)Kt$*.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
} }
Pop-Location 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; } which $dep >/dev/null 2>&1 || { echo >&2 "Error: This script needs $dep installed."; abort=yes; }
done done
if [ $abort = yes ]; then if [ "$abort" = yes ]; then
echo "Some of the dependencies didn't exist. Aborting." echo "Some of the dependencies didn't exist. Aborting."
exit 1 exit 1
fi fi
@@ -30,7 +30,7 @@ rm -rf "tmp"
mkdir -p "tmp" mkdir -p "tmp"
pushd "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 # We need to remove any stub classes that we have implementations for
echo "Patching JAR..." echo "Patching JAR..."
@@ -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 {
}
@@ -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,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,45 @@
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 {
// Note: remove @JsonIgnore and implement methods if any extension ever uses these methods or the variables behind them
public MultiSelectListPreference(Context context) { super(context); }
@JsonIgnore
public void setEntries(CharSequence[] entries) { throw new RuntimeException("Stub!"); }
@JsonIgnore
public CharSequence[] getEntries() { throw new RuntimeException("Stub!"); }
@JsonIgnore
public void setEntryValues(CharSequence[] entryValues) { throw new RuntimeException("Stub!"); }
@JsonIgnore
public CharSequence[] getEntryValues() { throw new RuntimeException("Stub!"); }
@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";
}
}
@@ -0,0 +1,140 @@
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;
/**
* 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 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) {
throw new RuntimeException("Stub!");
}
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;
}
/** 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 */
public Object getCurrentValue() {
switch (getDefaultValueType()) {
case "String":
return sharedPreferences.getString(key, (String)defaultValue);
case "Boolean":
return sharedPreferences.getBoolean(key, (Boolean)defaultValue);
default:
throw new RuntimeException("Unsupported type");
}
}
/** Tachidesk specific API */
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;
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,47 @@
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 {
// Note: remove @JsonIgnore and implement methods if any extension ever uses these methods or the variables behind them
public TwoStatePreference(Context context) { super(context); }
@JsonIgnore
public boolean isChecked() { throw new RuntimeException("Stub!"); }
@JsonIgnore
public void setChecked(boolean checked) { throw new RuntimeException("Stub!"); }
@JsonIgnore
public CharSequence getSummaryOn() { throw new RuntimeException("Stub!"); }
@JsonIgnore
public void setSummaryOn(CharSequence summary) { throw new RuntimeException("Stub!"); }
@JsonIgnore
public CharSequence getSummaryOff() { throw new RuntimeException("Stub!"); }
@JsonIgnore
public void setSummaryOff(CharSequence summary) { throw new RuntimeException("Stub!"); }
@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";
}
}
@@ -1,5 +1,6 @@
package rx.android.schedulers package rx.android.schedulers
import rx.Scheduler
import rx.internal.schedulers.ImmediateScheduler import rx.internal.schedulers.ImmediateScheduler
class AndroidSchedulers { class AndroidSchedulers {
@@ -11,6 +12,7 @@ class AndroidSchedulers {
/** /**
* Simulated main thread scheduler * Simulated main thread scheduler
*/ */
fun mainThread() = mainThreadScheduler @JvmStatic
fun mainThread(): Scheduler = mainThreadScheduler
} }
} }
@@ -26,5 +26,8 @@ class AndroidCompatInitializer {
ApplicationInfoConfigModule.register(GlobalConfigManager.config), ApplicationInfoConfigModule.register(GlobalConfigManager.config),
SystemConfigModule.register(GlobalConfigManager.config) SystemConfigModule.register(GlobalConfigManager.config)
) )
// Set some properties extensions use
System.setProperty("http.agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
} }
} }
@@ -38,7 +38,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl; import xyz.nulldev.androidcompat.info.ApplicationInfoImpl;
import xyz.nulldev.androidcompat.io.AndroidFiles; import xyz.nulldev.androidcompat.io.AndroidFiles;
import xyz.nulldev.androidcompat.io.sharedprefs.JsonSharedPreferences; import xyz.nulldev.androidcompat.io.sharedprefs.JavaSharedPreferences;
import xyz.nulldev.androidcompat.service.ServiceSupport; import xyz.nulldev.androidcompat.service.ServiceSupport;
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper; import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
@@ -50,10 +50,9 @@ import java.util.Map;
/** /**
* Custom context implementation. * Custom context implementation.
* *
* TODO Deal with packagemanager for extension sources
*/ */
public class CustomContext extends Context implements DIAware { public class CustomContext extends Context implements DIAware {
private DI kodein; private final DI kodein;
public CustomContext() { public CustomContext() {
this(KodeinGlobalHelper.kodein()); this(KodeinGlobalHelper.kodein());
} }
@@ -165,23 +164,22 @@ public class CustomContext extends Context implements DIAware {
/** Fake shared prefs! **/ /** Fake shared prefs! **/
private Map<String, SharedPreferences> prefs = new HashMap<>(); //Cache private Map<String, SharedPreferences> prefs = new HashMap<>(); //Cache
private File sharedPrefsFileFromString(String s) {
return new File(androidFiles.getPrefsDir(), s + ".json");
}
@Override @Override
public synchronized SharedPreferences getSharedPreferences(String s, int i) { public synchronized SharedPreferences getSharedPreferences(String s, int i) {
SharedPreferences preferences = prefs.get(s); SharedPreferences preferences = prefs.get(s);
//Create new shared preferences if one does not exist //Create new shared preferences if one does not exist
if(preferences == null) { if(preferences == null) {
preferences = getSharedPreferences(sharedPrefsFileFromString(s), i); preferences = new JavaSharedPreferences(s);
prefs.put(s, preferences); prefs.put(s, preferences);
} }
return preferences; return preferences;
} }
public SharedPreferences getSharedPreferences(File file, int mode) { @Override
return new JsonSharedPreferences(file); public SharedPreferences getSharedPreferences(@NotNull File file, int mode) {
String path = file.getAbsolutePath().replace('\\', '/');
int firstSlash = path.indexOf("/");
return new JavaSharedPreferences(path.substring(firstSlash));
} }
@Override @Override
@@ -191,8 +189,8 @@ public class CustomContext extends Context implements DIAware {
@Override @Override
public boolean deleteSharedPreferences(String name) { public boolean deleteSharedPreferences(String name) {
prefs.remove(name); JavaSharedPreferences item = (JavaSharedPreferences) prefs.remove(name);
return sharedPrefsFileFromString(name).delete(); return item.deleteAll();
} }
@Override @Override
@@ -735,4 +733,3 @@ public class CustomContext extends Context implements DIAware {
} }
} }
@@ -0,0 +1,177 @@
package xyz.nulldev.androidcompat.io.sharedprefs
/*
* 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.SharedPreferences
import com.russhwolf.settings.ExperimentalSettingsApi
import com.russhwolf.settings.ExperimentalSettingsImplementation
import com.russhwolf.settings.JvmPreferencesSettings
import com.russhwolf.settings.serialization.decodeValue
import com.russhwolf.settings.serialization.encodeValue
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.builtins.SetSerializer
import kotlinx.serialization.builtins.nullable
import kotlinx.serialization.builtins.serializer
import java.util.prefs.PreferenceChangeListener
import java.util.prefs.Preferences
@OptIn(ExperimentalSettingsImplementation::class, ExperimentalSerializationApi::class, ExperimentalSettingsApi::class)
class JavaSharedPreferences(key: String) : SharedPreferences {
private val javaPreferences = Preferences.userRoot().node("suwayomi/tachidesk/$key")
private val preferences = JvmPreferencesSettings(javaPreferences)
private val listeners = mutableMapOf<SharedPreferences.OnSharedPreferenceChangeListener, PreferenceChangeListener>()
// TODO: 2021-05-29 Need to find a way to get this working with all pref types
override fun getAll(): MutableMap<String, *> {
return preferences.keys.associateWith { preferences.getStringOrNull(it) }.toMutableMap()
}
override fun getString(key: String, defValue: String?): String? {
return if (defValue != null) {
preferences.getString(key, defValue)
} else {
preferences.getStringOrNull(key)
}
}
override fun getStringSet(key: String, defValues: MutableSet<String>?): MutableSet<String>? {
try {
return if (defValues != null) {
preferences.decodeValue(SetSerializer(String.serializer()).nullable, key, defValues)
} else {
preferences.decodeValue(SetSerializer(String.serializer()).nullable, key, null)
}?.toMutableSet()
} catch (e: SerializationException) {
throw ClassCastException("$key was not a StringSet")
}
}
override fun getInt(key: String, defValue: Int): Int {
return preferences.getInt(key, defValue)
}
override fun getLong(key: String, defValue: Long): Long {
return preferences.getLong(key, defValue)
}
override fun getFloat(key: String, defValue: Float): Float {
return preferences.getFloat(key, defValue)
}
override fun getBoolean(key: String, defValue: Boolean): Boolean {
return preferences.getBoolean(key, defValue)
}
override fun contains(key: String): Boolean {
return key in preferences.keys
}
override fun edit(): SharedPreferences.Editor {
return Editor(preferences)
}
class Editor(private val preferences: JvmPreferencesSettings) : SharedPreferences.Editor {
val itemsToAdd = mutableMapOf<String, Any>()
override fun putString(key: String, value: String?): SharedPreferences.Editor {
if (value != null) {
itemsToAdd[key] = value
} else {
remove(key)
}
return this
}
override fun putStringSet(
key: String,
values: MutableSet<String>?
): SharedPreferences.Editor {
if (values != null) {
itemsToAdd[key] = values
} else {
remove(key)
}
return this
}
override fun putInt(key: String, value: Int): SharedPreferences.Editor {
itemsToAdd[key] = value
return this
}
override fun putLong(key: String, value: Long): SharedPreferences.Editor {
itemsToAdd[key] = value
return this
}
override fun putFloat(key: String, value: Float): SharedPreferences.Editor {
itemsToAdd[key] = value
return this
}
override fun putBoolean(key: String, value: Boolean): SharedPreferences.Editor {
itemsToAdd[key] = value
return this
}
override fun remove(key: String): SharedPreferences.Editor {
itemsToAdd.remove(key)
return this
}
override fun clear(): SharedPreferences.Editor {
itemsToAdd.clear()
return this
}
override fun commit(): Boolean {
addToPreferences()
return true
}
override fun apply() {
addToPreferences()
}
private fun addToPreferences() {
itemsToAdd.forEach { (key, value) ->
@Suppress("UNCHECKED_CAST")
when (value) {
is Set<*> -> preferences.encodeValue(SetSerializer(String.serializer()), key, value as Set<String>)
is String -> preferences.putString(key, value)
is Int -> preferences.putInt(key, value)
is Long -> preferences.putLong(key, value)
is Float -> preferences.putFloat(key, value)
is Double -> preferences.putDouble(key, value)
is Boolean -> preferences.putBoolean(key, value)
}
}
}
}
override fun registerOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
val javaListener = PreferenceChangeListener {
listener.onSharedPreferenceChanged(this, it.key)
}
listeners[listener] = javaListener
javaPreferences.addPreferenceChangeListener(javaListener)
}
override fun unregisterOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
val registeredListener = listeners.remove(listener)
if (registeredListener != null) {
javaPreferences.removePreferenceChangeListener(registeredListener)
}
}
fun deleteAll(): Boolean {
javaPreferences.removeNode()
return true
}
}
@@ -233,7 +233,7 @@ public class JsonSharedPreferences implements SharedPreferences {
private JsonSharedPreferencesEditor() { private JsonSharedPreferencesEditor() {
} }
private void recordChange(String key) { private void recordChange(String key) {
if (!affectedKeys.contains(key)) { if (!affectedKeys.contains(key)) {
affectedKeys.add(key); affectedKeys.add(key);
} }
@@ -0,0 +1,249 @@
package xyz.nulldev.androidcompat.replace.java.text;
/*
* 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.ibm.icu.text.DisplayContext;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.ULocale;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.AttributedCharacterIterator;
import java.text.FieldPosition;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Locale;
public class NumberFormat extends java.text.NumberFormat {
private com.ibm.icu.text.NumberFormat delegate;
public NumberFormat(com.ibm.icu.text.NumberFormat delegate) {
this.delegate = delegate;
}
public StringBuffer format(Object number, StringBuffer toAppendTo, FieldPosition pos) {
return delegate.format(number, toAppendTo, pos);
}
public String format(BigInteger number) {
return delegate.format(number);
}
public String format(BigDecimal number) {
return delegate.format(number);
}
public String format(com.ibm.icu.math.BigDecimal number) {
return delegate.format(number);
}
public String format(CurrencyAmount currAmt) {
return delegate.format(currAmt);
}
public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
return delegate.format(number, toAppendTo, pos);
}
public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
return delegate.format(number, toAppendTo, pos);
}
public StringBuffer format(BigInteger number, StringBuffer toAppendTo, FieldPosition pos) {
return delegate.format(number, toAppendTo, pos);
}
public StringBuffer format(BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) {
return delegate.format(number, toAppendTo, pos);
}
public StringBuffer format(com.ibm.icu.math.BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) {
return delegate.format(number, toAppendTo, pos);
}
public StringBuffer format(CurrencyAmount currAmt, StringBuffer toAppendTo, FieldPosition pos) {
return delegate.format(currAmt, toAppendTo, pos);
}
public Number parse(String text, ParsePosition parsePosition) {
return delegate.parse(text, parsePosition);
}
public Number parse(String text) throws ParseException {
return delegate.parse(text);
}
public CurrencyAmount parseCurrency(CharSequence text, ParsePosition pos) {
return delegate.parseCurrency(text, pos);
}
public boolean isParseIntegerOnly() {
return delegate.isParseIntegerOnly();
}
public void setParseIntegerOnly(boolean value) {
delegate.setParseIntegerOnly(value);
}
public void setParseStrict(boolean value) {
delegate.setParseStrict(value);
}
public boolean isParseStrict() {
return delegate.isParseStrict();
}
public void setContext(DisplayContext context) {
delegate.setContext(context);
}
public DisplayContext getContext(DisplayContext.Type type) {
return delegate.getContext(type);
}
public static java.text.NumberFormat getInstance(Locale inLocale) {
return new NumberFormat(com.ibm.icu.text.NumberFormat.getInstance(inLocale));
}
public static com.ibm.icu.text.NumberFormat getInstance(ULocale inLocale) {
return com.ibm.icu.text.NumberFormat.getInstance(inLocale);
}
public static com.ibm.icu.text.NumberFormat getInstance(int style) {
return com.ibm.icu.text.NumberFormat.getInstance(style);
}
public static com.ibm.icu.text.NumberFormat getInstance(Locale inLocale, int style) {
return com.ibm.icu.text.NumberFormat.getInstance(inLocale, style);
}
public static com.ibm.icu.text.NumberFormat getNumberInstance(ULocale inLocale) {
return com.ibm.icu.text.NumberFormat.getNumberInstance(inLocale);
}
public static com.ibm.icu.text.NumberFormat getIntegerInstance(ULocale inLocale) {
return com.ibm.icu.text.NumberFormat.getIntegerInstance(inLocale);
}
public static com.ibm.icu.text.NumberFormat getCurrencyInstance(ULocale inLocale) {
return com.ibm.icu.text.NumberFormat.getCurrencyInstance(inLocale);
}
public static com.ibm.icu.text.NumberFormat getPercentInstance(ULocale inLocale) {
return com.ibm.icu.text.NumberFormat.getPercentInstance(inLocale);
}
public static com.ibm.icu.text.NumberFormat getScientificInstance(ULocale inLocale) {
return com.ibm.icu.text.NumberFormat.getScientificInstance(inLocale);
}
public static Locale[] getAvailableLocales() {
return com.ibm.icu.text.NumberFormat.getAvailableLocales();
}
public static ULocale[] getAvailableULocales() {
return com.ibm.icu.text.NumberFormat.getAvailableULocales();
}
public static Object registerFactory(com.ibm.icu.text.NumberFormat.NumberFormatFactory factory) {
return com.ibm.icu.text.NumberFormat.registerFactory(factory);
}
public static boolean unregister(Object registryKey) {
return com.ibm.icu.text.NumberFormat.unregister(registryKey);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
@Override
public Object clone() {
return delegate.clone();
}
public boolean isGroupingUsed() {
return delegate.isGroupingUsed();
}
public void setGroupingUsed(boolean newValue) {
delegate.setGroupingUsed(newValue);
}
public int getMaximumIntegerDigits() {
return delegate.getMaximumIntegerDigits();
}
public void setMaximumIntegerDigits(int newValue) {
delegate.setMaximumIntegerDigits(newValue);
}
public int getMinimumIntegerDigits() {
return delegate.getMinimumIntegerDigits();
}
public void setMinimumIntegerDigits(int newValue) {
delegate.setMinimumIntegerDigits(newValue);
}
public int getMaximumFractionDigits() {
return delegate.getMaximumFractionDigits();
}
public void setMaximumFractionDigits(int newValue) {
delegate.setMaximumFractionDigits(newValue);
}
public int getMinimumFractionDigits() {
return delegate.getMinimumFractionDigits();
}
public void setMinimumFractionDigits(int newValue) {
delegate.setMinimumFractionDigits(newValue);
}
public void setCurrency(Currency theCurrency) {
delegate.setCurrency(theCurrency);
}
public java.util.Currency getCurrency() {
return java.util.Currency.getInstance(delegate.getCurrency().getCurrencyCode());
}
public void setRoundingMode(int roundingMode) {
delegate.setRoundingMode(roundingMode);
}
public static com.ibm.icu.text.NumberFormat getInstance(ULocale desiredLocale, int choice) {
return com.ibm.icu.text.NumberFormat.getInstance(desiredLocale, choice);
}
@Deprecated
public static String getPatternForStyle(ULocale forLocale, int choice) {
return com.ibm.icu.text.NumberFormat.getPatternForStyle(forLocale, choice);
}
public ULocale getLocale(ULocale.Type type) {
return delegate.getLocale(type);
}
public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
return delegate.formatToCharacterIterator(obj);
}
public Object parseObject(String source) throws ParseException {
return delegate.parseObject(source);
}
}
@@ -0,0 +1,346 @@
package xyz.nulldev.androidcompat.replace.java.text;
/*
* 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.ibm.icu.text.DateFormatSymbols;
import com.ibm.icu.text.DisplayContext;
import com.ibm.icu.text.TimeZoneFormat;
import com.ibm.icu.util.ULocale;
import xyz.nulldev.androidcompat.replace.java.util.Calendar;
import xyz.nulldev.androidcompat.replace.java.util.TimeZone;
import java.text.*;
import java.util.Date;
import java.util.Locale;
/**
* Overridden to switch to Android implementation
*/
public class SimpleDateFormat extends java.text.DateFormat {
private com.ibm.icu.text.SimpleDateFormat delegate;
public SimpleDateFormat() {
delegate = new com.ibm.icu.text.SimpleDateFormat();
}
private SimpleDateFormat(com.ibm.icu.text.SimpleDateFormat delegate) {
this.delegate = delegate;
}
public SimpleDateFormat(String pattern) {
delegate = new com.ibm.icu.text.SimpleDateFormat(pattern);
}
public SimpleDateFormat(String pattern, Locale loc) {
delegate = new com.ibm.icu.text.SimpleDateFormat(pattern, loc);
}
public SimpleDateFormat(String pattern, ULocale loc) {
delegate = new com.ibm.icu.text.SimpleDateFormat(pattern, loc);
}
public SimpleDateFormat(String pattern, String override, ULocale loc) {
delegate = new com.ibm.icu.text.SimpleDateFormat(pattern, override, loc);
}
public SimpleDateFormat(String pattern, DateFormatSymbols formatData) {
delegate = new com.ibm.icu.text.SimpleDateFormat(pattern, formatData);
}
public SimpleDateFormat(String pattern, DateFormatSymbols formatData, ULocale loc) {
delegate = new com.ibm.icu.text.SimpleDateFormat(pattern, formatData, loc);
}
@Deprecated
public static SimpleDateFormat getInstance(com.ibm.icu.util.Calendar.FormatConfiguration formatConfig) {
return new SimpleDateFormat(com.ibm.icu.text.SimpleDateFormat.getInstance(formatConfig));
}
public void set2DigitYearStart(Date startDate) {
delegate.set2DigitYearStart(startDate);
}
public Date get2DigitYearStart() {
return delegate.get2DigitYearStart();
}
public void setContext(DisplayContext context) {
delegate.setContext(context);
}
public StringBuffer format(com.ibm.icu.util.Calendar cal, StringBuffer toAppendTo, FieldPosition pos) {
return delegate.format(cal, toAppendTo, pos);
}
public void setNumberFormat(com.ibm.icu.text.NumberFormat newNumberFormat) {
delegate.setNumberFormat(newNumberFormat);
}
public void parse(String text, com.ibm.icu.util.Calendar cal, ParsePosition parsePos) {
delegate.parse(text, cal, parsePos);
}
public String toPattern() {
return delegate.toPattern();
}
public String toLocalizedPattern() {
return delegate.toLocalizedPattern();
}
public void applyPattern(String pat) {
delegate.applyPattern(pat);
}
public void applyLocalizedPattern(String pat) {
delegate.applyLocalizedPattern(pat);
}
public DateFormatSymbols getDateFormatSymbols() {
return delegate.getDateFormatSymbols();
}
public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols) {
delegate.setDateFormatSymbols(newFormatSymbols);
}
public TimeZoneFormat getTimeZoneFormat() {
return delegate.getTimeZoneFormat();
}
public void setTimeZoneFormat(TimeZoneFormat tzfmt) {
delegate.setTimeZoneFormat(tzfmt);
}
@Override
public Object clone() {
return delegate.clone();
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
@Override
public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
return delegate.formatToCharacterIterator(obj);
}
@Deprecated
public StringBuffer intervalFormatByAlgorithm(com.ibm.icu.util.Calendar fromCalendar, com.ibm.icu.util.Calendar toCalendar, StringBuffer appendTo, FieldPosition pos) throws IllegalArgumentException {
return delegate.intervalFormatByAlgorithm(fromCalendar, toCalendar, appendTo, pos);
}
public void setNumberFormat(String fields, com.ibm.icu.text.NumberFormat overrideNF) {
delegate.setNumberFormat(fields, overrideNF);
}
public com.ibm.icu.text.NumberFormat getNumberFormat(char field) {
return delegate.getNumberFormat(field);
}
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
return delegate.format(date, toAppendTo, fieldPosition);
}
@Override
public Date parse(String text) throws ParseException {
return delegate.parse(text);
}
@Override
public Date parse(String text, ParsePosition pos) {
return delegate.parse(text, pos);
}
@Override
public Object parseObject(String source, ParsePosition pos) {
return delegate.parseObject(source, pos);
}
public static com.ibm.icu.text.DateFormat getTimeInstance(int style, ULocale locale) {
return com.ibm.icu.text.DateFormat.getTimeInstance(style, locale);
}
public static com.ibm.icu.text.DateFormat getDateInstance(int style, ULocale locale) {
return com.ibm.icu.text.DateFormat.getDateInstance(style, locale);
}
public static com.ibm.icu.text.DateFormat getDateTimeInstance(int dateStyle, int timeStyle, ULocale locale) {
return com.ibm.icu.text.DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
}
public static Locale[] getAvailableLocales() {
return com.ibm.icu.text.DateFormat.getAvailableLocales();
}
public static ULocale[] getAvailableULocales() {
return com.ibm.icu.text.DateFormat.getAvailableULocales();
}
@Override
public void setCalendar(java.util.Calendar newCalendar) {
com.ibm.icu.util.Calendar cal = com.ibm.icu.util.Calendar.getInstance(com.ibm.icu.util.TimeZone.getTimeZone(newCalendar.getTimeZone().getID()));
cal.setTimeInMillis(newCalendar.getTimeInMillis());
delegate.setCalendar(cal);
}
@Override
public java.util.Calendar getCalendar() {
return new Calendar(delegate.getCalendar());
}
@Override
public java.text.NumberFormat getNumberFormat() {
return new NumberFormat(delegate.getNumberFormat());
}
@Override
public void setTimeZone(java.util.TimeZone zone) {
delegate.setTimeZone(com.ibm.icu.util.TimeZone.getTimeZone(zone.getID()));
}
@Override
public java.util.TimeZone getTimeZone() {
return new TimeZone(delegate.getTimeZone());
}
@Override
public void setLenient(boolean lenient) {
delegate.setLenient(lenient);
}
@Override
public boolean isLenient() {
return delegate.isLenient();
}
public void setCalendarLenient(boolean lenient) {
delegate.setCalendarLenient(lenient);
}
public boolean isCalendarLenient() {
return delegate.isCalendarLenient();
}
public com.ibm.icu.text.DateFormat setBooleanAttribute(com.ibm.icu.text.DateFormat.BooleanAttribute key, boolean value) {
return delegate.setBooleanAttribute(key, value);
}
public boolean getBooleanAttribute(com.ibm.icu.text.DateFormat.BooleanAttribute key) {
return delegate.getBooleanAttribute(key);
}
public DisplayContext getContext(DisplayContext.Type type) {
return delegate.getContext(type);
}
public static com.ibm.icu.text.DateFormat getDateInstance(com.ibm.icu.util.Calendar cal, int dateStyle, Locale locale) {
return com.ibm.icu.text.DateFormat.getDateInstance(cal, dateStyle, locale);
}
public static com.ibm.icu.text.DateFormat getDateInstance(com.ibm.icu.util.Calendar cal, int dateStyle, ULocale locale) {
return com.ibm.icu.text.DateFormat.getDateInstance(cal, dateStyle, locale);
}
public static com.ibm.icu.text.DateFormat getTimeInstance(com.ibm.icu.util.Calendar cal, int timeStyle, Locale locale) {
return com.ibm.icu.text.DateFormat.getTimeInstance(cal, timeStyle, locale);
}
public static com.ibm.icu.text.DateFormat getTimeInstance(com.ibm.icu.util.Calendar cal, int timeStyle, ULocale locale) {
return com.ibm.icu.text.DateFormat.getTimeInstance(cal, timeStyle, locale);
}
public static com.ibm.icu.text.DateFormat getDateTimeInstance(com.ibm.icu.util.Calendar cal, int dateStyle, int timeStyle, Locale locale) {
return com.ibm.icu.text.DateFormat.getDateTimeInstance(cal, dateStyle, timeStyle, locale);
}
public static com.ibm.icu.text.DateFormat getDateTimeInstance(com.ibm.icu.util.Calendar cal, int dateStyle, int timeStyle, ULocale locale) {
return com.ibm.icu.text.DateFormat.getDateTimeInstance(cal, dateStyle, timeStyle, locale);
}
public static com.ibm.icu.text.DateFormat getInstance(com.ibm.icu.util.Calendar cal, Locale locale) {
return com.ibm.icu.text.DateFormat.getInstance(cal, locale);
}
public static com.ibm.icu.text.DateFormat getInstance(com.ibm.icu.util.Calendar cal, ULocale locale) {
return com.ibm.icu.text.DateFormat.getInstance(cal, locale);
}
public static com.ibm.icu.text.DateFormat getInstance(com.ibm.icu.util.Calendar cal) {
return com.ibm.icu.text.DateFormat.getInstance(cal);
}
public static com.ibm.icu.text.DateFormat getDateInstance(com.ibm.icu.util.Calendar cal, int dateStyle) {
return com.ibm.icu.text.DateFormat.getDateInstance(cal, dateStyle);
}
public static com.ibm.icu.text.DateFormat getTimeInstance(com.ibm.icu.util.Calendar cal, int timeStyle) {
return com.ibm.icu.text.DateFormat.getTimeInstance(cal, timeStyle);
}
public static com.ibm.icu.text.DateFormat getDateTimeInstance(com.ibm.icu.util.Calendar cal, int dateStyle, int timeStyle) {
return com.ibm.icu.text.DateFormat.getDateTimeInstance(cal, dateStyle, timeStyle);
}
public static com.ibm.icu.text.DateFormat getInstanceForSkeleton(String skeleton) {
return com.ibm.icu.text.DateFormat.getInstanceForSkeleton(skeleton);
}
public static com.ibm.icu.text.DateFormat getInstanceForSkeleton(String skeleton, Locale locale) {
return com.ibm.icu.text.DateFormat.getInstanceForSkeleton(skeleton, locale);
}
public static com.ibm.icu.text.DateFormat getInstanceForSkeleton(String skeleton, ULocale locale) {
return com.ibm.icu.text.DateFormat.getInstanceForSkeleton(skeleton, locale);
}
public static com.ibm.icu.text.DateFormat getInstanceForSkeleton(com.ibm.icu.util.Calendar cal, String skeleton, Locale locale) {
return com.ibm.icu.text.DateFormat.getInstanceForSkeleton(cal, skeleton, locale);
}
public static com.ibm.icu.text.DateFormat getInstanceForSkeleton(com.ibm.icu.util.Calendar cal, String skeleton, ULocale locale) {
return com.ibm.icu.text.DateFormat.getInstanceForSkeleton(cal, skeleton, locale);
}
public static com.ibm.icu.text.DateFormat getPatternInstance(String skeleton) {
return com.ibm.icu.text.DateFormat.getPatternInstance(skeleton);
}
public static com.ibm.icu.text.DateFormat getPatternInstance(String skeleton, Locale locale) {
return com.ibm.icu.text.DateFormat.getPatternInstance(skeleton, locale);
}
public static com.ibm.icu.text.DateFormat getPatternInstance(String skeleton, ULocale locale) {
return com.ibm.icu.text.DateFormat.getPatternInstance(skeleton, locale);
}
public static com.ibm.icu.text.DateFormat getPatternInstance(com.ibm.icu.util.Calendar cal, String skeleton, Locale locale) {
return com.ibm.icu.text.DateFormat.getPatternInstance(cal, skeleton, locale);
}
public static com.ibm.icu.text.DateFormat getPatternInstance(com.ibm.icu.util.Calendar cal, String skeleton, ULocale locale) {
return com.ibm.icu.text.DateFormat.getPatternInstance(cal, skeleton, locale);
}
public ULocale getLocale(ULocale.Type type) {
return delegate.getLocale(type);
}
@Override
public Object parseObject(String source) throws ParseException {
return delegate.parseObject(source);
}
}
@@ -0,0 +1,294 @@
package xyz.nulldev.androidcompat.replace.java.util;
/*
* 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.ibm.icu.text.DateFormat;
import com.ibm.icu.util.ULocale;
import java.util.Date;
import java.util.Locale;
public class Calendar extends java.util.Calendar {
private com.ibm.icu.util.Calendar delegate;
public Calendar(com.ibm.icu.util.Calendar delegate) {
this.delegate = delegate;
}
public static java.util.Calendar getInstance() {
return new Calendar(com.ibm.icu.util.Calendar.getInstance());
}
public static com.ibm.icu.util.Calendar getInstance(com.ibm.icu.util.TimeZone zone) {
return com.ibm.icu.util.Calendar.getInstance(zone);
}
public static java.util.Calendar getInstance(Locale aLocale) {
return new Calendar(com.ibm.icu.util.Calendar.getInstance(aLocale));
}
public static com.ibm.icu.util.Calendar getInstance(ULocale locale) {
return com.ibm.icu.util.Calendar.getInstance(locale);
}
public static com.ibm.icu.util.Calendar getInstance(com.ibm.icu.util.TimeZone zone, Locale aLocale) {
return com.ibm.icu.util.Calendar.getInstance(zone, aLocale);
}
public static com.ibm.icu.util.Calendar getInstance(com.ibm.icu.util.TimeZone zone, ULocale locale) {
return com.ibm.icu.util.Calendar.getInstance(zone, locale);
}
public static Locale[] getAvailableLocales() {
return com.ibm.icu.util.Calendar.getAvailableLocales();
}
@Override
protected void computeTime() {}
@Override
protected void computeFields() {}
public static ULocale[] getAvailableULocales() {
return com.ibm.icu.util.Calendar.getAvailableULocales();
}
public static String[] getKeywordValuesForLocale(String key, ULocale locale, boolean commonlyUsed) {
return com.ibm.icu.util.Calendar.getKeywordValuesForLocale(key, locale, commonlyUsed);
}
@Override
public long getTimeInMillis() {
return delegate.getTimeInMillis();
}
@Override
public void setTimeInMillis(long millis) {
delegate.setTimeInMillis(millis);
}
@Deprecated
public int getRelatedYear() {
return delegate.getRelatedYear();
}
@Deprecated
public void setRelatedYear(int year) {
delegate.setRelatedYear(year);
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
public boolean isEquivalentTo(com.ibm.icu.util.Calendar other) {
return delegate.isEquivalentTo(other);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean before(Object when) {
return delegate.before(when);
}
@Override
public boolean after(Object when) {
return delegate.after(when);
}
@Override
public int getActualMaximum(int field) {
return delegate.getActualMaximum(field);
}
@Override
public int getActualMinimum(int field) {
return delegate.getActualMinimum(field);
}
@Override
public void roll(int field, int amount) {
delegate.roll(field, amount);
}
@Override
public void add(int field, int amount) {
delegate.add(field, amount);
}
@Override
public void roll(int field, boolean up) {
roll(field, up ? 1 : -1);
}
public String getDisplayName(Locale loc) {
return delegate.getDisplayName(loc);
}
public String getDisplayName(ULocale loc) {
return delegate.getDisplayName(loc);
}
public int compareTo(com.ibm.icu.util.Calendar that) {
return delegate.compareTo(that);
}
public DateFormat getDateTimeFormat(int dateStyle, int timeStyle, Locale loc) {
return delegate.getDateTimeFormat(dateStyle, timeStyle, loc);
}
public DateFormat getDateTimeFormat(int dateStyle, int timeStyle, ULocale loc) {
return delegate.getDateTimeFormat(dateStyle, timeStyle, loc);
}
@Deprecated
public static String getDateTimePattern(com.ibm.icu.util.Calendar cal, ULocale uLocale, int dateStyle) {
return com.ibm.icu.util.Calendar.getDateTimePattern(cal, uLocale, dateStyle);
}
public int fieldDifference(Date when, int field) {
return delegate.fieldDifference(when, field);
}
public void setTimeZone(com.ibm.icu.util.TimeZone value) {
delegate.setTimeZone(value);
}
@Override
public java.util.TimeZone getTimeZone() {
return new TimeZone(delegate.getTimeZone());
}
@Override
public void setLenient(boolean lenient) {
delegate.setLenient(lenient);
}
@Override
public boolean isLenient() {
return delegate.isLenient();
}
public void setRepeatedWallTimeOption(int option) {
delegate.setRepeatedWallTimeOption(option);
}
public int getRepeatedWallTimeOption() {
return delegate.getRepeatedWallTimeOption();
}
public void setSkippedWallTimeOption(int option) {
delegate.setSkippedWallTimeOption(option);
}
public int getSkippedWallTimeOption() {
return delegate.getSkippedWallTimeOption();
}
@Override
public void setFirstDayOfWeek(int value) {
delegate.setFirstDayOfWeek(value);
}
@Override
public int getFirstDayOfWeek() {
return delegate.getFirstDayOfWeek();
}
@Override
public void setMinimalDaysInFirstWeek(int value) {
delegate.setMinimalDaysInFirstWeek(value);
}
@Override
public int getMinimalDaysInFirstWeek() {
return delegate.getMinimalDaysInFirstWeek();
}
@Override
public int getMinimum(int field) {
return delegate.getMinimum(field);
}
@Override
public int getMaximum(int field) {
return delegate.getMaximum(field);
}
@Override
public int getGreatestMinimum(int field) {
return delegate.getGreatestMinimum(field);
}
@Override
public int getLeastMaximum(int field) {
return delegate.getLeastMaximum(field);
}
@Deprecated
public int getDayOfWeekType(int dayOfWeek) {
return delegate.getDayOfWeekType(dayOfWeek);
}
@Deprecated
public int getWeekendTransition(int dayOfWeek) {
return delegate.getWeekendTransition(dayOfWeek);
}
public boolean isWeekend(Date date) {
return delegate.isWeekend(date);
}
public boolean isWeekend() {
return delegate.isWeekend();
}
@Override
public Object clone() {
return delegate.clone();
}
@Override
public String toString() {
return delegate.toString();
}
public static com.ibm.icu.util.Calendar.WeekData getWeekDataForRegion(String region) {
return com.ibm.icu.util.Calendar.getWeekDataForRegion(region);
}
public com.ibm.icu.util.Calendar.WeekData getWeekData() {
return delegate.getWeekData();
}
public com.ibm.icu.util.Calendar setWeekData(com.ibm.icu.util.Calendar.WeekData wdata) {
return delegate.setWeekData(wdata);
}
public int getFieldCount() {
return delegate.getFieldCount();
}
public String getType() {
return delegate.getType();
}
@Deprecated
public boolean haveDefaultCentury() {
return delegate.haveDefaultCentury();
}
public ULocale getLocale(ULocale.Type type) {
return delegate.getLocale(type);
}
}
@@ -0,0 +1,196 @@
package xyz.nulldev.androidcompat.replace.java.util;
/*
* 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.ibm.icu.util.ULocale;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
public class TimeZone extends java.util.TimeZone {
private com.ibm.icu.util.TimeZone delegate;
public TimeZone(com.ibm.icu.util.TimeZone delegate) {
this.delegate = delegate;
}
@Override
public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) {
return delegate.getOffset(era, year, month, day, dayOfWeek, milliseconds);
}
@Override
public int getOffset(long date) {
return delegate.getOffset(date);
}
public void getOffset(long date, boolean local, int[] offsets) {
delegate.getOffset(date, local, offsets);
}
@Override
public void setRawOffset(int offsetMillis) {
delegate.setRawOffset(offsetMillis);
}
@Override
public int getRawOffset() {
return delegate.getRawOffset();
}
@Override
public String getID() {
return delegate.getID();
}
@Override
public void setID(String ID) {
delegate.setID(ID);
}
public String getDisplayName(ULocale locale) {
return delegate.getDisplayName(locale);
}
@Override
public String getDisplayName(boolean daylight, int style, Locale locale) {
return delegate.getDisplayName(daylight, style, locale);
}
public String getDisplayName(boolean daylight, int style, ULocale locale) {
return delegate.getDisplayName(daylight, style, locale);
}
@Override
public int getDSTSavings() {
return delegate.getDSTSavings();
}
@Override
public boolean useDaylightTime() {
return delegate.useDaylightTime();
}
@Override
public boolean observesDaylightTime() {
return delegate.observesDaylightTime();
}
@Override
public boolean inDaylightTime(Date date) {
return delegate.inDaylightTime(date);
}
public static java.util.TimeZone getTimeZone(String ID) {
return new TimeZone(com.ibm.icu.util.TimeZone.getTimeZone(ID));
}
public static com.ibm.icu.util.TimeZone getFrozenTimeZone(String ID) {
return com.ibm.icu.util.TimeZone.getFrozenTimeZone(ID);
}
public static com.ibm.icu.util.TimeZone getTimeZone(String ID, int type) {
return com.ibm.icu.util.TimeZone.getTimeZone(ID, type);
}
public static void setDefaultTimeZoneType(int type) {
com.ibm.icu.util.TimeZone.setDefaultTimeZoneType(type);
}
public static int getDefaultTimeZoneType() {
return com.ibm.icu.util.TimeZone.getDefaultTimeZoneType();
}
public static Set<String> getAvailableIDs(com.ibm.icu.util.TimeZone.SystemTimeZoneType zoneType, String region, Integer rawOffset) {
return com.ibm.icu.util.TimeZone.getAvailableIDs(zoneType, region, rawOffset);
}
public static String[] getAvailableIDs(int rawOffset) {
return com.ibm.icu.util.TimeZone.getAvailableIDs(rawOffset);
}
public static String[] getAvailableIDs(String country) {
return com.ibm.icu.util.TimeZone.getAvailableIDs(country);
}
public static String[] getAvailableIDs() {
return com.ibm.icu.util.TimeZone.getAvailableIDs();
}
public static int countEquivalentIDs(String id) {
return com.ibm.icu.util.TimeZone.countEquivalentIDs(id);
}
public static String getEquivalentID(String id, int index) {
return com.ibm.icu.util.TimeZone.getEquivalentID(id, index);
}
public static java.util.TimeZone getDefault() {
return new TimeZone(com.ibm.icu.util.TimeZone.getDefault());
}
public static void setDefault(com.ibm.icu.util.TimeZone tz) {
com.ibm.icu.util.TimeZone.setDefault(tz);
}
public boolean hasSameRules(com.ibm.icu.util.TimeZone other) {
return delegate.hasSameRules(other);
}
@Override
public Object clone() {
return delegate.clone();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
public static String getTZDataVersion() {
return com.ibm.icu.util.TimeZone.getTZDataVersion();
}
public static String getCanonicalID(String id) {
return com.ibm.icu.util.TimeZone.getCanonicalID(id);
}
public static String getCanonicalID(String id, boolean[] isSystemID) {
return com.ibm.icu.util.TimeZone.getCanonicalID(id, isSystemID);
}
public static String getRegion(String id) {
return com.ibm.icu.util.TimeZone.getRegion(id);
}
public static String getWindowsID(String id) {
return com.ibm.icu.util.TimeZone.getWindowsID(id);
}
public static String getIDForWindowsID(String winid, String region) {
return com.ibm.icu.util.TimeZone.getIDForWindowsID(winid, region);
}
public boolean isFrozen() {
return delegate.isFrozen();
}
public com.ibm.icu.util.TimeZone freeze() {
return delegate.freeze();
}
public com.ibm.icu.util.TimeZone cloneAsThawed() {
return delegate.cloneAsThawed();
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ Here is a list of current features:
- A library to save your mangas and categories to put them into. - A library to save your mangas and categories to put them into.
- Searching and browsing installed sources. - Searching and browsing installed sources.
- A decent chapter reader. - A decent chapter reader.
- Ability to download Mangas for offline read(This partially works) - Ability to download Manga for offline read
- Backup and restore support powered by Tachiyomi Legacy Backups - Backup and restore support powered by Tachiyomi Legacy Backups
**Note:** Keep in mind that Tachidesk is alpha software and can break rarely and/or with each update. See [Troubleshooting](https://github.com/Suwayomi/Tachidesk/wiki/Troubleshooting) if it happens. **Note:** Keep in mind that Tachidesk is alpha software and can break rarely and/or with each update. See [Troubleshooting](https://github.com/Suwayomi/Tachidesk/wiki/Troubleshooting) if it happens.
+9 -1
View File
@@ -2,6 +2,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
kotlin("jvm") version "1.4.32" kotlin("jvm") version "1.4.32"
kotlin("plugin.serialization") version "1.4.32" apply false
} }
allprojects { allprojects {
@@ -42,7 +43,7 @@ configure(projects) {
// Kotlin // Kotlin
implementation(kotlin("stdlib-jdk8")) implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect")) implementation(kotlin("reflect"))
testImplementation(kotlin("test")) testImplementation(kotlin("test-junit5"))
// coroutines // coroutines
val coroutinesVersion = "1.4.3" val coroutinesVersion = "1.4.3"
@@ -50,6 +51,10 @@ configure(projects) {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$coroutinesVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion")
val kotlinSerializationVersion = "1.1.0"
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinSerializationVersion")
// Dependency Injection // Dependency Injection
implementation("org.kodein.di:kodein-di-conf-jvm:7.5.0") implementation("org.kodein.di:kodein-di-conf-jvm:7.5.0")
@@ -79,5 +84,8 @@ configure(projects) {
// APK parser // APK parser
implementation("net.dongliu:apk-parser:2.6.10") implementation("net.dongliu:apk-parser:2.6.10")
// Jackson
implementation("com.fasterxml.jackson.core:jackson-annotations:2.10.3")
} }
} }
@@ -1 +1 @@
jre\bin\java -Dsuwayomi.server.debugLogsEnabled=true -jar Tachidesk.jar jre\bin\java -Dsuwayomi.tachidesk.server.debugLogsEnabled=true -jar Tachidesk.jar
@@ -1 +1 @@
jre\bin\javaw "-Dsuwayomi.server.webInterface=electron" "-Dsuwayomi.server.electronPath=electron/electron.exe" -jar Tachidesk.jar jre\bin\javaw "-Dsuwayomi.tachidesk.server.webInterface=electron" "-Dsuwayomi.tachidesk.server.electronPath=electron/electron.exe" -jar Tachidesk.jar
-5
View File
@@ -1,5 +0,0 @@
#include <stdlib.h>
int main() {
system("start jre\\bin\\javaw -jar Tachidesk.jar");
}
-3
View File
@@ -1,3 +0,0 @@
# Building `Tachidesk Launcher.exe`
1. compile `Tachidesk Launcher.c` statically with MSVC compiler.
2. Add `server/src/main/resources/icon/faviconlogo.ico` into the exe with `rcedit` from the electron project: `rcedit "Tachidesk Launcher.exe" --set-icon faviconlogo.ico`
+2 -3
View File
@@ -22,9 +22,9 @@ jre_dir="jdk8u292-b10-jre"
echo "creating windows bundle" echo "creating windows bundle"
jar=$(ls ../server/build/Tachidesk-*.jar) jar=$(ls ../server/build/*.jar | tail -n1)
jar_name=$(echo $jar | cut -d'/' -f4) jar_name=$(echo $jar | cut -d'/' -f4)
release_name=$(echo $jar_name | cut -d'.' -f4 --complement)-$arch release_name=$(echo $jar_name | sed 's/.jar//')-$arch
# make release dir # make release dir
@@ -65,7 +65,6 @@ WINEARCH=win32 wine $rcedit $release_name/electron/electron.exe --set-icon ../se
# copy artifacts # copy artifacts
cp $jar $release_name/Tachidesk.jar cp $jar $release_name/Tachidesk.jar
#cp "resources/Tachidesk Launcher-$arch.exe" "$release_name/Tachidesk Launcher.exe"
cp "resources/Tachidesk Browser Launcher.bat" $release_name cp "resources/Tachidesk Browser Launcher.bat" $release_name
cp "resources/Tachidesk Debug Launcher.bat" $release_name cp "resources/Tachidesk Debug Launcher.bat" $release_name
cp "resources/Tachidesk Electron Launcher.bat" $release_name cp "resources/Tachidesk Electron Launcher.bat" $release_name
+28 -10
View File
@@ -44,11 +44,16 @@ dependencies {
// current database driver // current database driver
implementation("com.h2database:h2:1.4.200") implementation("com.h2database:h2:1.4.200")
// Exposed Migrations
val exposedMigrationsVersion = "3.1.0"
implementation("com.github.Suwayomi:exposed-migrations:$exposedMigrationsVersion")
// tray icon // tray icon
implementation("com.dorkbox:SystemTray:4.1") implementation("com.dorkbox:SystemTray:4.1")
implementation("com.dorkbox:Utilities:1.9") implementation("com.dorkbox:Utilities:1.9")
// dependencies of Tachiyomi extensions, some are duplicate, keeping it here for reference // dependencies of Tachiyomi extensions, some are duplicate, keeping it here for reference
implementation("com.github.inorichi.injekt:injekt-core:65b0440") implementation("com.github.inorichi.injekt:injekt-core:65b0440")
implementation("com.squareup.okhttp3:okhttp:4.9.1") implementation("com.squareup.okhttp3:okhttp:4.9.1")
@@ -58,6 +63,12 @@ dependencies {
implementation("com.github.salomonbrys.kotson:kotson:2.5.0") implementation("com.github.salomonbrys.kotson:kotson:2.5.0")
// asm for fixing SimpleDateFormat (must match Dex2Jar version)
implementation("org.ow2.asm:asm-debug-all:5.0.3")
// extracting zip files
implementation("net.lingala.zip4j:zip4j:2.9.0")
// Source models and interfaces from Tachiyomi 1.x // Source models and interfaces from Tachiyomi 1.x
// using source class from tachiyomi commit 9493577de27c40ce8b2b6122cc447d025e34c477 to not depend on tachiyomi.sourceapi // using source class from tachiyomi commit 9493577de27c40ce8b2b6122cc447d025e34c477 to not depend on tachiyomi.sourceapi
// implementation("tachiyomi.sourceapi:source-api:1.1") // implementation("tachiyomi.sourceapi:source-api:1.1")
@@ -68,12 +79,9 @@ dependencies {
// uncomment to test extensions directly // uncomment to test extensions directly
// implementation(fileTree("lib/")) // implementation(fileTree("lib/"))
// Testing
testImplementation(kotlin("test-junit5"))
} }
val MainClass = "suwayomi.MainKt" val MainClass = "suwayomi.tachidesk.MainKt"
application { application {
mainClass.set(MainClass) mainClass.set(MainClass)
@@ -93,11 +101,12 @@ sourceSets {
} }
// should be bumped with each stable release // should be bumped with each stable release
val tachideskVersion = "v0.4.2" val tachideskVersion = System.getenv("ProductVersion") ?: "v0.4.4"
val webUIRevisionTag = System.getenv("WebUIRevision") ?: "r20"
// counts commit count on master // counts commit count on master
val tachideskRevision = runCatching { val tachideskRevision = runCatching {
Runtime System.getenv("ProductRevision") ?: Runtime
.getRuntime() .getRuntime()
.exec("git rev-list HEAD --count") .exec("git rev-list HEAD --count")
.let { process -> .let { process ->
@@ -107,21 +116,25 @@ val tachideskRevision = runCatching {
} }
process.destroy() process.destroy()
"r" + output.trim() "r" + output.trim()
} }
}.getOrDefault("r0") }.getOrDefault("r0")
buildConfig { buildConfig {
clsName = "BuildConfig" clsName = "BuildConfig"
packageName = "suwayomi.server" packageName = "suwayomi.tachidesk.server"
buildConfigField("String", "NAME", rootProject.name) buildConfigField("String", "NAME", rootProject.name)
buildConfigField("String", "VERSION", tachideskVersion) buildConfigField("String", "VERSION", tachideskVersion)
buildConfigField("String", "REVISION", tachideskRevision) buildConfigField("String", "REVISION", tachideskRevision)
buildConfigField("String", "BUILD_TYPE", if (System.getenv("TachideskBuildType") == "Stable") "Stable" else "Preview") buildConfigField("String", "BUILD_TYPE", if (System.getenv("ProductBuildType") == "Stable") "Stable" else "Preview")
buildConfigField("long", "BUILD_TIME", Instant.now().epochSecond.toString()) buildConfigField("long", "BUILD_TIME", Instant.now().epochSecond.toString())
buildConfigField("String", "WEBUI_REPO", "https://github.com/Suwayomi/Tachidesk-WebUI-preview")
buildConfigField("String", "WEBUI_TAG", webUIRevisionTag)
buildConfigField("String", "GITHUB", "https://github.com/Suwayomi/Tachidesk") buildConfigField("String", "GITHUB", "https://github.com/Suwayomi/Tachidesk")
buildConfigField("String", "DISCORD", "https://discord.gg/DDZdqZWaHA") buildConfigField("String", "DISCORD", "https://discord.gg/DDZdqZWaHA")
} }
@@ -168,7 +181,12 @@ tasks {
named<Copy>("processResources") { named<Copy>("processResources") {
duplicatesStrategy = DuplicatesStrategy.INCLUDE duplicatesStrategy = DuplicatesStrategy.INCLUDE
mustRunAfter(":webUI:copyBuild") mustRunAfter("downloadWebUI")
}
register<de.undercouch.gradle.tasks.download.Download>("downloadWebUI") {
src("https://github.com/Suwayomi/Tachidesk-WebUI-preview/releases/download/$webUIRevisionTag/Tachidesk-WebUI-$webUIRevisionTag.zip")
dest("src/main/resources/WebUI.zip")
} }
withType<LintTask> { withType<LintTask> {
@@ -18,6 +18,7 @@ import com.google.gson.Gson
// import eu.kanade.tachiyomi.data.track.TrackManager // import eu.kanade.tachiyomi.data.track.TrackManager
// import eu.kanade.tachiyomi.extension.ExtensionManager // import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import kotlinx.serialization.json.Json
import rx.Observable import rx.Observable
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import uy.kohesive.injekt.api.InjektModule import uy.kohesive.injekt.api.InjektModule
@@ -54,6 +55,8 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory { Gson() } addSingletonFactory { Gson() }
addSingletonFactory { Json { ignoreUnknownKeys = true } }
// Asynchronously init expensive components for a faster cold start // Asynchronously init expensive components for a faster cold start
// rxAsync { get<PreferencesHelper>() } // rxAsync { get<PreferencesHelper>() }
@@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.animesource package eu.kanade.tachiyomi.animesource
import android.support.v7.preference.PreferenceScreen import androidx.preference.PreferenceScreen
interface ConfigurableAnimeSource : AnimeSource { interface ConfigurableAnimeSource : AnimeSource {
@@ -1,8 +1,8 @@
package eu.kanade.tachiyomi.source package eu.kanade.tachiyomi.source
// import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
interface ConfigurableSource : Source { interface ConfigurableSource : Source {
// fun setupPreferenceScreen(screen: PreferenceScreen) fun setupPreferenceScreen(screen: PreferenceScreen)
} }
@@ -44,4 +44,4 @@ interface Source {
// fun Source.icon(): Drawable? = Injekt.get<ExtensionManager>().getAppIconForSource(this) // fun Source.icon(): Drawable? = Injekt.get<ExtensionManager>().getAppIconForSource(this)
// fun Source.getPreferenceKey(): String = "source_$id" fun Source.getPreferenceKey(): String = "source_$id"
@@ -1,24 +0,0 @@
package suwayomi.server.database.migration
/*
* 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 org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.vendors.currentDialect
import suwayomi.server.database.migration.lib.Migration
@Suppress("ClassName", "unused")
class M0002_ChapterTableIndexRename : Migration() {
/** this migration renamed ChapterTable.NUMBER_IN_LIST to ChapterTable.INDEX */
override fun run() {
with(TransactionManager.current()) {
exec("ALTER TABLE CHAPTER ALTER COLUMN NUMBER_IN_LIST RENAME TO INDEX")
commit()
currentDialect.resetCaches()
}
}
}
@@ -1,24 +0,0 @@
package suwayomi.server.database.migration
/*
* 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 org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.vendors.currentDialect
import suwayomi.server.database.migration.lib.Migration
@Suppress("ClassName", "unused")
class M0003_DefaultCategory : Migration() {
/** this migration renamed CategoryTable.IS_LANDING to ChapterTable.IS_DEFAULT */
override fun run() {
with(TransactionManager.current()) {
exec("ALTER TABLE CATEGORY ALTER COLUMN IS_LANDING RENAME TO IS_DEFAULT")
commit()
currentDialect.resetCaches()
}
}
}
@@ -1,24 +0,0 @@
package suwayomi.server.database.migration
/*
* 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 org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.vendors.currentDialect
import suwayomi.server.database.migration.lib.Migration
@Suppress("ClassName", "unused")
class M0007_ChapterIsDownloaded : Migration() {
/** this migration added IS_DOWNLOADED to CHAPTER */
override fun run() {
with(TransactionManager.current()) {
exec("ALTER TABLE CHAPTER ADD COLUMN IS_DOWNLOADED BOOLEAN DEFAULT FALSE")
commit()
currentDialect.resetCaches()
}
}
}
@@ -1,24 +0,0 @@
package suwayomi.server.database.migration
/*
* 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 org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.vendors.currentDialect
import suwayomi.server.database.migration.lib.Migration
@Suppress("ClassName", "unused")
class M0008_ChapterPageCount : Migration() {
/** this migration added PAGE_COUNT to CHAPTER */
override fun run() {
with(TransactionManager.current()) {
exec("ALTER TABLE CHAPTER ADD COLUMN PAGE_COUNT INT DEFAULT -1")
commit()
currentDialect.resetCaches()
}
}
}
@@ -1,25 +0,0 @@
package suwayomi.server.database.migration
/*
* 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 org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.vendors.currentDialect
import suwayomi.server.database.migration.lib.Migration
@Suppress("ClassName", "unused")
class M0009_ChapterLastReadAt : Migration() {
/** this migration added PAGE_COUNT to CHAPTER */
override fun run() {
with(TransactionManager.current()) {
// BIGINT == Long
exec("ALTER TABLE CHAPTER ADD COLUMN LAST_READ_AT BIGINT DEFAULT 0")
commit()
currentDialect.resetCaches()
}
}
}
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 Andreas Mausch
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -1,25 +0,0 @@
package suwayomi.server.database.migration.lib
/*
* 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/. */
// originally licenced under MIT by Andreas Mausch, Changes are licenced under Mozilla Public License, v. 2.0.
// adopted from: https://gitlab.com/andreas-mausch/exposed-migrations/-/tree/4bf853c18a24d0170eda896ddbb899cb01233595
abstract class Migration {
val name: String
val version: Int
init {
val groups = Regex("^M(\\d+)_(.*)$").matchEntire(this::class.simpleName!!)?.groupValues
?: throw IllegalArgumentException("Migration class name doesn't match convention")
version = groups[1].toInt()
name = groups[2]
}
abstract fun run()
}
@@ -1,37 +0,0 @@
package suwayomi.server.database.migration.lib
/*
* 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/. */
// originally licenced under MIT by Andreas Mausch, Changes are licenced under Mozilla Public License, v. 2.0.
// adopted from: https://gitlab.com/andreas-mausch/exposed-migrations/-/tree/4bf853c18a24d0170eda896ddbb899cb01233595
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IdTable
import org.jetbrains.exposed.sql.`java-time`.timestamp
object MigrationsTable : IdTable<Int>() {
override val id = integer("version").entityId()
override val primaryKey = PrimaryKey(id)
val name = varchar("name", length = 400)
val executedAt = timestamp("executed_at")
init {
index(true, name)
}
}
class MigrationEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<MigrationEntity>(MigrationsTable)
var version by MigrationsTable.id
var name by MigrationsTable.name
var executedAt by MigrationsTable.executedAt
}
@@ -1,123 +0,0 @@
package suwayomi.server.database.migration.lib
/*
* 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/. */
// originally licenced under MIT by Andreas Mausch, Changes are licenced under Mozilla Public License, v. 2.0.
// adopted from: https://gitlab.com/andreas-mausch/exposed-migrations/-/tree/4bf853c18a24d0170eda896ddbb899cb01233595
import mu.KotlinLogging
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils.create
import org.jetbrains.exposed.sql.exists
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.server.ServerConfig
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Paths
import java.time.Clock
import java.time.Instant.now
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.isDirectory
import kotlin.io.path.name
import kotlin.streams.toList
private val logger = KotlinLogging.logger {}
fun runMigrations(migrations: List<Migration>, database: Database = TransactionManager.defaultDatabase!!, clock: Clock = Clock.systemUTC()) {
checkVersions(migrations)
logger.info { "Running migrations on database ${database.url}" }
val latestVersion = transaction(database) {
createTableIfNotExists(database)
MigrationEntity.all().maxByOrNull { it.version }?.version?.value
}
logger.info { "Database version before migrations: $latestVersion" }
migrations
.sortedBy { it.version }
.filter { shouldRun(latestVersion, it) }
.forEach {
logger.info { "Running migration version ${it.version}: ${it.name}" }
transaction(database) {
it.run()
MigrationEntity.new {
version = EntityID(it.version, MigrationsTable)
name = it.name
executedAt = now(clock)
}
}
}
logger.info { "Migrations finished successfully" }
}
@OptIn(ExperimentalPathApi::class)
private fun getTopLevelClasses(packageName: String): List<Class<*>> {
ServerConfig::class.java.getResource("/" + "suwayomi.server.database.migration".replace('.', '/'))
val path = "/" + packageName.replace('.', '/')
val uri = ServerConfig::class.java.getResource(path).toURI()
return when (uri.scheme) {
"jar" -> {
val fileSystem = FileSystems.newFileSystem(uri, emptyMap<String, Any>())
fileSystem.getPath(path)
}
else -> Paths.get(uri)
}.let { Files.walk(it, 1) }
.toList()
.filterNot { it.isDirectory() || it.name.contains('$') } // '$' means it's not a top level class
.filter { it.name.endsWith(".class") }
.map { Class.forName("$packageName.${it.name.substringBefore(".class")}") }
}
@Suppress("UnstableApiUsage")
fun loadMigrationsFrom(packageName: String): List<Migration> {
return getTopLevelClasses(packageName)
.map {
logger.debug("found Migration class ${it.name}")
val clazz = it.getDeclaredConstructor().newInstance()
if (clazz is Migration)
clazz
else
throw RuntimeException("found a class that's not a Migration")
}
}
private fun checkVersions(migrations: List<Migration>) {
val sorted = migrations.map { it.version }.sorted()
if ((1..migrations.size).toList() != sorted) {
throw IllegalStateException("List of migrations version is not consecutive: $sorted")
}
}
private fun createTableIfNotExists(database: Database) {
if (MigrationsTable.exists()) {
return
}
val tableNames = database.dialect.allTablesNames()
when (tableNames.isEmpty()) {
true -> {
logger.info { "Empty database found, creating table for migrations" }
create(MigrationsTable)
}
false -> throw IllegalStateException("Tried to run migrations against a non-empty database without a Migrations table. This is not supported.")
}
}
private fun shouldRun(latestVersion: Int?, migration: Migration): Boolean {
val run = latestVersion?.let { migration.version > it } ?: true
if (!run) {
logger.debug { "Skipping migration version ${migration.version}: ${migration.name}" }
}
return run
}
@@ -1,4 +1,4 @@
package suwayomi package suwayomi.tachidesk
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -7,8 +7,8 @@ package suwayomi
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import suwayomi.server.JavalinSetup.javalinSetup import suwayomi.tachidesk.server.JavalinSetup.javalinSetup
import suwayomi.server.applicationSetup import suwayomi.tachidesk.server.applicationSetup
fun main() { fun main() {
applicationSetup() applicationSetup()
@@ -1,4 +1,4 @@
package suwayomi.anime package suwayomi.tachidesk.anime
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -8,20 +8,20 @@ package suwayomi.anime
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import io.javalin.Javalin import io.javalin.Javalin
import suwayomi.anime.impl.Anime.getAnime import suwayomi.tachidesk.anime.impl.Anime.getAnime
import suwayomi.anime.impl.Anime.getAnimeThumbnail import suwayomi.tachidesk.anime.impl.Anime.getAnimeThumbnail
import suwayomi.anime.impl.AnimeList.getAnimeList import suwayomi.tachidesk.anime.impl.AnimeList.getAnimeList
import suwayomi.anime.impl.Episode.getEpisode import suwayomi.tachidesk.anime.impl.Episode.getEpisode
import suwayomi.anime.impl.Episode.getEpisodeList import suwayomi.tachidesk.anime.impl.Episode.getEpisodeList
import suwayomi.anime.impl.Episode.modifyEpisode import suwayomi.tachidesk.anime.impl.Episode.modifyEpisode
import suwayomi.anime.impl.Source.getAnimeSource import suwayomi.tachidesk.anime.impl.Source.getAnimeSource
import suwayomi.anime.impl.Source.getSourceList import suwayomi.tachidesk.anime.impl.Source.getSourceList
import suwayomi.anime.impl.extension.Extension.getExtensionIcon import suwayomi.tachidesk.anime.impl.extension.Extension.getExtensionIcon
import suwayomi.anime.impl.extension.Extension.installExtension import suwayomi.tachidesk.anime.impl.extension.Extension.installExtension
import suwayomi.anime.impl.extension.Extension.uninstallExtension import suwayomi.tachidesk.anime.impl.extension.Extension.uninstallExtension
import suwayomi.anime.impl.extension.Extension.updateExtension import suwayomi.tachidesk.anime.impl.extension.Extension.updateExtension
import suwayomi.anime.impl.extension.ExtensionsList.getExtensionList import suwayomi.tachidesk.anime.impl.extension.ExtensionsList.getExtensionList
import suwayomi.server.JavalinSetup.future import suwayomi.tachidesk.server.JavalinSetup.future
object AnimeAPI { object AnimeAPI {
fun defineEndpoints(app: Javalin) { fun defineEndpoints(app: Javalin) {
@@ -1,4 +1,4 @@
package suwayomi.anime.impl package suwayomi.tachidesk.anime.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -15,17 +15,17 @@ import org.jetbrains.exposed.sql.update
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import suwayomi.anime.impl.AnimeList.proxyThumbnailUrl import suwayomi.tachidesk.anime.impl.AnimeList.proxyThumbnailUrl
import suwayomi.anime.impl.Source.getAnimeSource import suwayomi.tachidesk.anime.impl.Source.getAnimeSource
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource import suwayomi.tachidesk.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.anime.model.dataclass.AnimeDataClass import suwayomi.tachidesk.anime.model.dataclass.AnimeDataClass
import suwayomi.anime.model.table.AnimeStatus import suwayomi.tachidesk.anime.model.table.AnimeStatus
import suwayomi.anime.model.table.AnimeTable import suwayomi.tachidesk.anime.model.table.AnimeTable
import suwayomi.server.ApplicationDirs import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.network.await
import suwayomi.tachidesk.impl.util.network.await import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.clearCachedImage
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.clearCachedImage import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse import suwayomi.tachidesk.server.ApplicationDirs
import java.io.InputStream import java.io.InputStream
object Anime { object Anime {
@@ -1,4 +1,4 @@
package suwayomi.anime.impl package suwayomi.tachidesk.anime.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -11,12 +11,12 @@ import eu.kanade.tachiyomi.animesource.model.AnimesPage
import org.jetbrains.exposed.sql.insertAndGetId import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource import suwayomi.tachidesk.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.anime.model.dataclass.AnimeDataClass import suwayomi.tachidesk.anime.model.dataclass.AnimeDataClass
import suwayomi.anime.model.dataclass.PagedAnimeListDataClass import suwayomi.tachidesk.anime.model.dataclass.PagedAnimeListDataClass
import suwayomi.anime.model.table.AnimeStatus import suwayomi.tachidesk.anime.model.table.AnimeStatus
import suwayomi.anime.model.table.AnimeTable import suwayomi.tachidesk.anime.model.table.AnimeTable
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
object AnimeList { object AnimeList {
fun proxyThumbnailUrl(animeId: Int): String { fun proxyThumbnailUrl(animeId: Int): String {
@@ -1,4 +1,4 @@
package suwayomi.anime.impl package suwayomi.tachidesk.anime.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -16,13 +16,13 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.anime.impl.Anime.getAnime import suwayomi.tachidesk.anime.impl.Anime.getAnime
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource import suwayomi.tachidesk.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.anime.model.dataclass.EpisodeDataClass import suwayomi.tachidesk.anime.model.dataclass.EpisodeDataClass
import suwayomi.anime.model.table.AnimeTable import suwayomi.tachidesk.anime.model.table.AnimeTable
import suwayomi.anime.model.table.EpisodeTable import suwayomi.tachidesk.anime.model.table.EpisodeTable
import suwayomi.anime.model.table.toDataClass import suwayomi.tachidesk.anime.model.table.toDataClass
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
object Episode { object Episode {
/** get episode list when showing an anime */ /** get episode list when showing an anime */
@@ -1,4 +1,4 @@
package suwayomi.anime.impl package suwayomi.tachidesk.anime.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -11,11 +11,11 @@ import mu.KotlinLogging
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.anime.impl.extension.Extension.getExtensionIconUrl import suwayomi.tachidesk.anime.impl.extension.Extension.getExtensionIconUrl
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource import suwayomi.tachidesk.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.anime.model.dataclass.AnimeSourceDataClass import suwayomi.tachidesk.anime.model.dataclass.AnimeSourceDataClass
import suwayomi.anime.model.table.AnimeExtensionTable import suwayomi.tachidesk.anime.model.table.AnimeExtensionTable
import suwayomi.anime.model.table.AnimeSourceTable import suwayomi.tachidesk.anime.model.table.AnimeSourceTable
object Source { object Source {
private val logger = KotlinLogging.logger {} private val logger = KotlinLogging.logger {}
@@ -1,4 +1,4 @@
package suwayomi.anime.impl.extension package suwayomi.tachidesk.anime.impl.extension
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -25,23 +25,23 @@ import org.jetbrains.exposed.sql.update
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import suwayomi.anime.impl.extension.ExtensionsList.extensionTableAsDataClass import suwayomi.tachidesk.anime.impl.extension.ExtensionsList.extensionTableAsDataClass
import suwayomi.anime.impl.extension.github.ExtensionGithubApi import suwayomi.tachidesk.anime.impl.extension.github.ExtensionGithubApi
import suwayomi.anime.impl.util.PackageTools.EXTENSION_FEATURE import suwayomi.tachidesk.anime.impl.util.PackageTools.EXTENSION_FEATURE
import suwayomi.anime.impl.util.PackageTools.LIB_VERSION_MAX import suwayomi.tachidesk.anime.impl.util.PackageTools.LIB_VERSION_MAX
import suwayomi.anime.impl.util.PackageTools.LIB_VERSION_MIN import suwayomi.tachidesk.anime.impl.util.PackageTools.LIB_VERSION_MIN
import suwayomi.anime.impl.util.PackageTools.METADATA_NSFW import suwayomi.tachidesk.anime.impl.util.PackageTools.METADATA_NSFW
import suwayomi.anime.impl.util.PackageTools.METADATA_SOURCE_CLASS import suwayomi.tachidesk.anime.impl.util.PackageTools.METADATA_SOURCE_CLASS
import suwayomi.anime.impl.util.PackageTools.dex2jar import suwayomi.tachidesk.anime.impl.util.PackageTools.dex2jar
import suwayomi.anime.impl.util.PackageTools.getPackageInfo import suwayomi.tachidesk.anime.impl.util.PackageTools.getPackageInfo
import suwayomi.anime.impl.util.PackageTools.getSignatureHash import suwayomi.tachidesk.anime.impl.util.PackageTools.getSignatureHash
import suwayomi.anime.impl.util.PackageTools.loadExtensionSources import suwayomi.tachidesk.anime.impl.util.PackageTools.loadExtensionSources
import suwayomi.anime.impl.util.PackageTools.trustedSignatures import suwayomi.tachidesk.anime.impl.util.PackageTools.trustedSignatures
import suwayomi.anime.model.table.AnimeExtensionTable import suwayomi.tachidesk.anime.model.table.AnimeExtensionTable
import suwayomi.anime.model.table.AnimeSourceTable import suwayomi.tachidesk.anime.model.table.AnimeSourceTable
import suwayomi.server.ApplicationDirs import suwayomi.tachidesk.manga.impl.util.network.await
import suwayomi.tachidesk.impl.util.network.await import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse import suwayomi.tachidesk.server.ApplicationDirs
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@@ -1,4 +1,4 @@
package suwayomi.anime.impl.extension package suwayomi.tachidesk.anime.impl.extension
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -14,11 +14,11 @@ import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.anime.impl.extension.Extension.getExtensionIconUrl import suwayomi.tachidesk.anime.impl.extension.Extension.getExtensionIconUrl
import suwayomi.anime.impl.extension.github.ExtensionGithubApi import suwayomi.tachidesk.anime.impl.extension.github.ExtensionGithubApi
import suwayomi.anime.impl.extension.github.OnlineExtension import suwayomi.tachidesk.anime.impl.extension.github.OnlineExtension
import suwayomi.anime.model.dataclass.AnimeExtensionDataClass import suwayomi.tachidesk.anime.model.dataclass.AnimeExtensionDataClass
import suwayomi.anime.model.table.AnimeExtensionTable import suwayomi.tachidesk.anime.model.table.AnimeExtensionTable
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
object ExtensionsList { object ExtensionsList {
@@ -1,4 +1,4 @@
package suwayomi.anime.impl.extension.github package suwayomi.tachidesk.anime.impl.extension.github
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -13,15 +13,15 @@ import com.google.gson.JsonArray
import com.google.gson.JsonParser import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import okhttp3.Request import okhttp3.Request
import suwayomi.anime.impl.util.PackageTools.LIB_VERSION_MAX import suwayomi.tachidesk.anime.impl.util.PackageTools.LIB_VERSION_MAX
import suwayomi.anime.impl.util.PackageTools.LIB_VERSION_MIN import suwayomi.tachidesk.anime.impl.util.PackageTools.LIB_VERSION_MIN
import suwayomi.anime.model.dataclass.AnimeExtensionDataClass import suwayomi.tachidesk.anime.model.dataclass.AnimeExtensionDataClass
import suwayomi.tachidesk.impl.util.network.UnzippingInterceptor import suwayomi.tachidesk.manga.impl.util.network.UnzippingInterceptor
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
object ExtensionGithubApi { object ExtensionGithubApi {
const val BASE_URL = "https://raw.githubusercontent.com" private const val BASE_URL = "https://raw.githubusercontent.com"
const val REPO_URL_PREFIX = "$BASE_URL/jmir1/tachiyomi-extensions/repo" private const val REPO_URL_PREFIX = "$BASE_URL/jmir1/tachiyomi-extensions/repo"
private fun parseResponse(json: JsonArray): List<OnlineExtension> { private fun parseResponse(json: JsonArray): List<OnlineExtension> {
return json return json
@@ -68,7 +68,7 @@ object ExtensionGithubApi {
.build() .build()
} }
private fun getRepo(): com.google.gson.JsonArray { private fun getRepo(): JsonArray {
val request = Request.Builder() val request = Request.Builder()
.url("$REPO_URL_PREFIX/index.json.gz") .url("$REPO_URL_PREFIX/index.json.gz")
.build() .build()
@@ -1,4 +1,4 @@
package suwayomi.anime.impl.extension.github package suwayomi.tachidesk.anime.impl.extension.github
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.anime.impl.util package suwayomi.tachidesk.anime.impl.util
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -15,10 +15,10 @@ import org.jetbrains.exposed.sql.transactions.transaction
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import suwayomi.anime.impl.util.PackageTools.loadExtensionSources import suwayomi.tachidesk.anime.impl.util.PackageTools.loadExtensionSources
import suwayomi.anime.model.table.AnimeExtensionTable import suwayomi.tachidesk.anime.model.table.AnimeExtensionTable
import suwayomi.anime.model.table.AnimeSourceTable import suwayomi.tachidesk.anime.model.table.AnimeSourceTable
import suwayomi.server.ApplicationDirs import suwayomi.tachidesk.server.ApplicationDirs
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
object GetAnimeHttpSource { object GetAnimeHttpSource {
@@ -1,4 +1,4 @@
package suwayomi.anime.impl.util package suwayomi.tachidesk.anime.impl.util
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -22,7 +22,8 @@ import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.Node import org.w3c.dom.Node
import suwayomi.server.ApplicationDirs import suwayomi.tachidesk.manga.impl.util.BytecodeEditor
import suwayomi.tachidesk.server.ApplicationDirs
import xyz.nulldev.androidcompat.pm.InstalledPackage.Companion.toList import xyz.nulldev.androidcompat.pm.InstalledPackage.Companion.toList
import xyz.nulldev.androidcompat.pm.toPackageInfo import xyz.nulldev.androidcompat.pm.toPackageInfo
import java.io.File import java.io.File
@@ -80,6 +81,8 @@ object PackageTools {
""".trimIndent() """.trimIndent()
) )
handler.dump(errorFile, emptyArray<String>()) handler.dump(errorFile, emptyArray<String>())
} else {
BytecodeEditor.fixAndroidClasses(jarFilePath.toFile())
} }
} }
@@ -1,4 +1,4 @@
package suwayomi.anime.model.dataclass package suwayomi.tachidesk.anime.model.dataclass
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -7,7 +7,7 @@ package suwayomi.anime.model.dataclass
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import suwayomi.anime.model.table.AnimeStatus import suwayomi.tachidesk.anime.model.table.AnimeStatus
data class AnimeDataClass( data class AnimeDataClass(
val id: Int, val id: Int,
@@ -1,4 +1,4 @@
package suwayomi.anime.model.dataclass package suwayomi.tachidesk.anime.model.dataclass
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.anime.model.dataclass package suwayomi.tachidesk.anime.model.dataclass
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.anime.model.dataclass package suwayomi.tachidesk.anime.model.dataclass
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.anime.model.table package suwayomi.tachidesk.anime.model.table
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.anime.model.table package suwayomi.tachidesk.anime.model.table
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.anime.model.table package suwayomi.tachidesk.anime.model.table
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -10,9 +10,9 @@ package suwayomi.anime.model.table
import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.animesource.model.SAnime
import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.ResultRow
import suwayomi.tachidesk.impl.MangaList.proxyThumbnailUrl import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl
import suwayomi.tachidesk.model.dataclass.MangaDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.table.MangaStatus.Companion import suwayomi.tachidesk.manga.model.table.MangaStatus.Companion
object AnimeTable : IntIdTable() { object AnimeTable : IntIdTable() {
val url = varchar("url", 2048) val url = varchar("url", 2048)
@@ -1,4 +1,4 @@
package suwayomi.anime.model.table package suwayomi.tachidesk.anime.model.table
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -11,7 +11,7 @@ import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.anime.model.dataclass.EpisodeDataClass import suwayomi.tachidesk.anime.model.dataclass.EpisodeDataClass
object EpisodeTable : IntIdTable() { object EpisodeTable : IntIdTable() {
val url = varchar("url", 2048) val url = varchar("url", 2048)
@@ -0,0 +1,24 @@
package suwayomi.tachidesk.global
/*
* 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 io.javalin.Javalin
import io.javalin.apibuilder.ApiBuilder.get
import io.javalin.apibuilder.ApiBuilder.path
import suwayomi.tachidesk.global.controller.SettingsController
object GlobalAPI {
fun defineEndpoints(app: Javalin) {
app.routes {
path("api/v1/settings") {
get("about", SettingsController::about)
get("check-update", SettingsController::checkUpdate)
}
}
}
}
@@ -0,0 +1,28 @@
package suwayomi.tachidesk.global.controller
/*
* 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 io.javalin.http.Context
import suwayomi.tachidesk.global.impl.About
import suwayomi.tachidesk.global.impl.AppUpdate
import suwayomi.tachidesk.server.JavalinSetup
/** Settings Page/Screen */
object SettingsController {
/** returns some static info about the current app build */
fun about(ctx: Context): Context {
return ctx.json(About.getAbout())
}
/** check for app updates */
fun checkUpdate(ctx: Context): Context {
return ctx.json(
JavalinSetup.future { AppUpdate.checkUpdate() }
)
}
}
@@ -1,4 +1,4 @@
package suwayomi.server.impl package suwayomi.tachidesk.global.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -7,7 +7,7 @@ package suwayomi.server.impl
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import suwayomi.server.BuildConfig import suwayomi.tachidesk.server.BuildConfig
data class AboutDataClass( data class AboutDataClass(
val name: String, val name: String,
@@ -0,0 +1,58 @@
package suwayomi.tachidesk.global.impl
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.NetworkHelper
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import suwayomi.tachidesk.manga.impl.util.network.await
import uy.kohesive.injekt.injectLazy
/*
* 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/. */
data class UpdateDataClass(
/** [channel] mirrors [suwayomi.tachidesk.server.BuildConfig.BUILD_TYPE] */
val channel: String,
val tag: String,
val url: String
)
object AppUpdate {
private const val LATEST_STABLE_CHANNEL_URL = "https://api.github.com/repos/Suwayomi/Tachidesk/releases/latest"
private const val LATEST_PREVIEW_CHANNEL_URL = "https://api.github.com/repos/Suwayomi/Tachidesk-preview/releases/latest"
private val json: Json by injectLazy()
private val network: NetworkHelper by injectLazy()
suspend fun checkUpdate(): List<UpdateDataClass> {
val stableJson = json.parseToJsonElement(
network.client.newCall(
GET(LATEST_STABLE_CHANNEL_URL)
).await().body!!.string()
).jsonObject
val previewJson = json.parseToJsonElement(
network.client.newCall(
GET(LATEST_PREVIEW_CHANNEL_URL)
).await().body!!.string()
).jsonObject
return listOf(
UpdateDataClass(
"Stable",
stableJson["tag_name"]!!.jsonPrimitive.content,
stableJson["html_url"]!!.jsonPrimitive.content,
),
UpdateDataClass(
"Preview",
previewJson["tag_name"]!!.jsonPrimitive.content,
previewJson["html_url"]!!.jsonPrimitive.content,
),
)
}
}
@@ -1,50 +0,0 @@
package suwayomi.tachidesk.impl
/*
* 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 mu.KotlinLogging
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.impl.extension.Extension.getExtensionIconUrl
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.model.dataclass.SourceDataClass
import suwayomi.tachidesk.model.table.ExtensionTable
import suwayomi.tachidesk.model.table.SourceTable
object Source {
private val logger = KotlinLogging.logger {}
fun getSourceList(): List<SourceDataClass> {
return transaction {
SourceTable.selectAll().map {
SourceDataClass(
it[SourceTable.id].value.toString(),
it[SourceTable.name],
it[SourceTable.lang],
getExtensionIconUrl(ExtensionTable.select { ExtensionTable.id eq it[SourceTable.extension] }.first()[ExtensionTable.apkName]),
getHttpSource(it[SourceTable.id].value).supportsLatest
)
}
}
}
fun getSource(sourceId: Long): SourceDataClass {
return transaction {
val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()
SourceDataClass(
sourceId.toString(),
source?.get(SourceTable.name),
source?.get(SourceTable.lang),
source?.let { ExtensionTable.select { ExtensionTable.id eq source[SourceTable.extension] }.first()[ExtensionTable.iconUrl] },
source?.let { getHttpSource(sourceId).supportsLatest }
)
}
}
}
@@ -1,3 +0,0 @@
package suwayomi.tachidesk.impl.backup.legacy.models
data class DHistory(val url: String, val lastRead: Long)
@@ -1,4 +1,4 @@
package suwayomi.tachidesk package suwayomi.tachidesk.manga
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -8,41 +8,45 @@ package suwayomi.tachidesk
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import io.javalin.Javalin import io.javalin.Javalin
import suwayomi.server.JavalinSetup.future import suwayomi.tachidesk.manga.impl.Category
import suwayomi.server.impl.About import suwayomi.tachidesk.manga.impl.CategoryManga.addMangaToCategory
import suwayomi.tachidesk.impl.Category import suwayomi.tachidesk.manga.impl.CategoryManga.getCategoryMangaList
import suwayomi.tachidesk.impl.CategoryManga.addMangaToCategory import suwayomi.tachidesk.manga.impl.CategoryManga.getMangaCategories
import suwayomi.tachidesk.impl.CategoryManga.getCategoryMangaList import suwayomi.tachidesk.manga.impl.CategoryManga.removeMangaFromCategory
import suwayomi.tachidesk.impl.CategoryManga.getMangaCategories import suwayomi.tachidesk.manga.impl.Chapter.getChapter
import suwayomi.tachidesk.impl.CategoryManga.removeMangaFromCategory import suwayomi.tachidesk.manga.impl.Chapter.getChapterList
import suwayomi.tachidesk.impl.Chapter.getChapter import suwayomi.tachidesk.manga.impl.Chapter.modifyChapter
import suwayomi.tachidesk.impl.Chapter.getChapterList import suwayomi.tachidesk.manga.impl.Chapter.modifyChapterMeta
import suwayomi.tachidesk.impl.Chapter.modifyChapter import suwayomi.tachidesk.manga.impl.Library.addMangaToLibrary
import suwayomi.tachidesk.impl.Library.addMangaToLibrary import suwayomi.tachidesk.manga.impl.Library.getLibraryMangas
import suwayomi.tachidesk.impl.Library.getLibraryMangas import suwayomi.tachidesk.manga.impl.Library.removeMangaFromLibrary
import suwayomi.tachidesk.impl.Library.removeMangaFromLibrary import suwayomi.tachidesk.manga.impl.Manga.getManga
import suwayomi.tachidesk.impl.Manga.getManga import suwayomi.tachidesk.manga.impl.Manga.getMangaThumbnail
import suwayomi.tachidesk.impl.Manga.getMangaThumbnail import suwayomi.tachidesk.manga.impl.Manga.modifyMangaMeta
import suwayomi.tachidesk.impl.MangaList.getMangaList import suwayomi.tachidesk.manga.impl.MangaList.getMangaList
import suwayomi.tachidesk.impl.Page.getPageImage import suwayomi.tachidesk.manga.impl.Page.getPageImage
import suwayomi.tachidesk.impl.Search.sourceFilters import suwayomi.tachidesk.manga.impl.Search.sourceFilters
import suwayomi.tachidesk.impl.Search.sourceGlobalSearch import suwayomi.tachidesk.manga.impl.Search.sourceGlobalSearch
import suwayomi.tachidesk.impl.Search.sourceSearch import suwayomi.tachidesk.manga.impl.Search.sourceSearch
import suwayomi.tachidesk.impl.Source.getSource import suwayomi.tachidesk.manga.impl.Source.SourcePreferenceChange
import suwayomi.tachidesk.impl.Source.getSourceList import suwayomi.tachidesk.manga.impl.Source.getSource
import suwayomi.tachidesk.impl.backup.BackupFlags import suwayomi.tachidesk.manga.impl.Source.getSourceList
import suwayomi.tachidesk.impl.backup.legacy.LegacyBackupExport.createLegacyBackup import suwayomi.tachidesk.manga.impl.Source.getSourcePreferences
import suwayomi.tachidesk.impl.backup.legacy.LegacyBackupImport.restoreLegacyBackup import suwayomi.tachidesk.manga.impl.Source.setSourcePreference
import suwayomi.tachidesk.impl.download.DownloadManager import suwayomi.tachidesk.manga.impl.backup.BackupFlags
import suwayomi.tachidesk.impl.extension.Extension.getExtensionIcon import suwayomi.tachidesk.manga.impl.backup.legacy.LegacyBackupExport.createLegacyBackup
import suwayomi.tachidesk.impl.extension.Extension.installExtension import suwayomi.tachidesk.manga.impl.backup.legacy.LegacyBackupImport.restoreLegacyBackup
import suwayomi.tachidesk.impl.extension.Extension.uninstallExtension import suwayomi.tachidesk.manga.impl.download.DownloadManager
import suwayomi.tachidesk.impl.extension.Extension.updateExtension import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIcon
import suwayomi.tachidesk.impl.extension.ExtensionsList.getExtensionList import suwayomi.tachidesk.manga.impl.extension.Extension.installExtension
import suwayomi.tachidesk.manga.impl.extension.Extension.uninstallExtension
import suwayomi.tachidesk.manga.impl.extension.Extension.updateExtension
import suwayomi.tachidesk.manga.impl.extension.ExtensionsList.getExtensionList
import suwayomi.tachidesk.server.JavalinSetup.future
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
object TachideskAPI { object MangaAPI {
fun defineEndpoints(app: Javalin) { fun defineEndpoints(app: Javalin) {
// list all extensions // list all extensions
app.get("/api/v1/extension/list") { ctx -> app.get("/api/v1/extension/list") { ctx ->
@@ -84,7 +88,7 @@ object TachideskAPI {
} }
// icon for extension named `apkName` // icon for extension named `apkName`
app.get("/api/v1/extension/icon/:apkName") { ctx -> // TODO: move to pkgName app.get("/api/v1/extension/icon/:apkName") { ctx ->
val apkName = ctx.pathParam("apkName") val apkName = ctx.pathParam("apkName")
ctx.result( ctx.result(
@@ -107,6 +111,19 @@ object TachideskAPI {
ctx.json(getSource(sourceId)) ctx.json(getSource(sourceId))
} }
// fetch preferences of source with id `sourceId`
app.get("/api/v1/source/:sourceId/preferences") { ctx ->
val sourceId = ctx.pathParam("sourceId").toLong()
ctx.json(getSourcePreferences(sourceId))
}
// fetch preferences of source with id `sourceId`
app.post("/api/v1/source/:sourceId/preferences") { ctx ->
val sourceId = ctx.pathParam("sourceId").toLong()
val preferenceChange = ctx.bodyAsClass(SourcePreferenceChange::class.java)
ctx.json(setSourcePreference(sourceId, preferenceChange))
}
// popular mangas from source with id `sourceId` // popular mangas from source with id `sourceId`
app.get("/api/v1/source/:sourceId/popular/:pageNum") { ctx -> app.get("/api/v1/source/:sourceId/popular/:pageNum") { ctx ->
val sourceId = ctx.pathParam("sourceId").toLong() val sourceId = ctx.pathParam("sourceId").toLong()
@@ -185,6 +202,18 @@ object TachideskAPI {
ctx.json(future { getChapterList(mangaId, onlineFetch) }) ctx.json(future { getChapterList(mangaId, onlineFetch) })
} }
// used to modify a manga's meta parameters
app.patch("/api/v1/manga/:mangaId/meta") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt()
val key = ctx.formParam("key")!!
val value = ctx.formParam("value")!!
modifyMangaMeta(mangaId, key, value)
ctx.status(200)
}
// used to display a chapter, get a chapter in order to show it's pages // used to display a chapter, get a chapter in order to show it's pages
app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex") { ctx -> app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex") { ctx ->
val chapterIndex = ctx.pathParam("chapterIndex").toInt() val chapterIndex = ctx.pathParam("chapterIndex").toInt()
@@ -207,6 +236,19 @@ object TachideskAPI {
ctx.status(200) ctx.status(200)
} }
// used to modify a chapter's meta parameters
app.patch("/api/v1/manga/:mangaId/chapter/:chapterIndex/meta") { ctx ->
val chapterIndex = ctx.pathParam("chapterIndex").toInt()
val mangaId = ctx.pathParam("mangaId").toInt()
val key = ctx.formParam("key")!!
val value = ctx.formParam("value")!!
modifyChapterMeta(mangaId, chapterIndex, key, value)
ctx.status(200)
}
// get page at index "index" // get page at index "index"
app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex/page/:index") { ctx -> app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex/page/:index") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt() val mangaId = ctx.pathParam("mangaId").toInt()
@@ -287,11 +329,6 @@ object TachideskAPI {
ctx.status(200) ctx.status(200)
} }
// returns some static info of the current app build
app.get("/api/v1/about/") { ctx ->
ctx.json(About.getAbout())
}
// category modification // category modification
app.patch("/api/v1/category/:categoryId") { ctx -> app.patch("/api/v1/category/:categoryId") { ctx ->
val categoryId = ctx.pathParam("categoryId").toInt() val categoryId = ctx.pathParam("categoryId").toInt()
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl package suwayomi.tachidesk.manga.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -14,11 +14,11 @@ import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.CategoryManga.removeMangaFromCategory import suwayomi.tachidesk.manga.impl.CategoryManga.removeMangaFromCategory
import suwayomi.tachidesk.model.dataclass.CategoryDataClass import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
import suwayomi.tachidesk.model.table.CategoryTable import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.model.table.toDataClass import suwayomi.tachidesk.manga.model.table.toDataClass
object Category { object Category {
/** /**
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl package suwayomi.tachidesk.manga.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -14,12 +14,12 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.model.dataclass.CategoryDataClass import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.model.dataclass.MangaDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
import suwayomi.tachidesk.model.table.CategoryTable import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.model.table.toDataClass import suwayomi.tachidesk.manga.model.table.toDataClass
object CategoryManga { object CategoryManga {
fun addMangaToCategory(mangaId: Int, categoryId: Int) { fun addMangaToCategory(mangaId: Int, categoryId: Int) {
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl package suwayomi.tachidesk.manga.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -9,6 +9,7 @@ package suwayomi.tachidesk.impl
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.SortOrder.DESC import org.jetbrains.exposed.sql.SortOrder.DESC
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.deleteWhere
@@ -16,14 +17,15 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.Manga.getManga import suwayomi.tachidesk.manga.impl.Manga.getManga
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.model.dataclass.ChapterDataClass import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass
import suwayomi.tachidesk.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.ChapterMetaTable
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.model.table.PageTable import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.model.table.toDataClass import suwayomi.tachidesk.manga.model.table.PageTable
import suwayomi.tachidesk.manga.model.table.toDataClass
import java.time.Instant import java.time.Instant
object Chapter { object Chapter {
@@ -131,6 +133,7 @@ object Chapter {
dbChapter[ChapterTable.pageCount], dbChapter[ChapterTable.pageCount],
chapterList.size, chapterList.size,
meta = getChapterMetaMap(dbChapter[ChapterTable.id])
) )
} }
} }
@@ -199,7 +202,8 @@ object Chapter {
chapterEntry[ChapterTable.chapterIndex], chapterEntry[ChapterTable.chapterIndex],
chapterEntry[ChapterTable.isDownloaded], chapterEntry[ChapterTable.isDownloaded],
pageCount, pageCount,
chapterCount.toInt() chapterCount.toInt(),
getChapterMetaMap(chapterEntry[ChapterTable.id])
) )
} else { } else {
ChapterTable.toDataClass(chapterEntry) ChapterTable.toDataClass(chapterEntry)
@@ -230,4 +234,30 @@ object Chapter {
} }
} }
} }
fun getChapterMetaMap(chapter: EntityID<Int>): Map<String, String> {
return transaction {
ChapterMetaTable.select { ChapterMetaTable.ref eq chapter }
.associate { it[ChapterMetaTable.key] to it[ChapterMetaTable.value] }
}
}
fun modifyChapterMeta(mangaId: Int, chapterIndex: Int, key: String, value: String) {
transaction {
val chapter = ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.chapterIndex eq chapterIndex) }
.first()[ChapterTable.id]
val meta = transaction { ChapterMetaTable.select { (ChapterMetaTable.ref eq chapter) and (ChapterMetaTable.key eq key) } }.firstOrNull()
if (meta == null) {
ChapterMetaTable.insert {
it[ChapterMetaTable.key] = key
it[ChapterMetaTable.value] = value
it[ChapterMetaTable.ref] = chapter
}
} else {
ChapterMetaTable.update {
it[ChapterMetaTable.value] = value
}
}
}
}
} }
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl package suwayomi.tachidesk.manga.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -13,12 +13,12 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.Manga.getManga import suwayomi.tachidesk.manga.impl.Manga.getManga
import suwayomi.tachidesk.model.dataclass.MangaDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
import suwayomi.tachidesk.model.table.CategoryTable import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.model.table.toDataClass import suwayomi.tachidesk.manga.model.table.toDataClass
object Library { object Library {
// TODO: `Category.isLanding` is to handle the default categories a new library manga gets, // TODO: `Category.isLanding` is to handle the default categories a new library manga gets,
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl package suwayomi.tachidesk.manga.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -9,23 +9,27 @@ package suwayomi.tachidesk.impl
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import suwayomi.server.ApplicationDirs import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl
import suwayomi.tachidesk.impl.MangaList.proxyThumbnailUrl import suwayomi.tachidesk.manga.impl.Source.getSource
import suwayomi.tachidesk.impl.Source.getSource import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.network.await
import suwayomi.tachidesk.impl.util.network.await import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.clearCachedImage
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.clearCachedImage import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.dataclass.MangaDataClass import suwayomi.tachidesk.manga.model.table.MangaMetaTable
import suwayomi.tachidesk.model.table.MangaStatus import suwayomi.tachidesk.manga.model.table.MangaStatus
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.server.ApplicationDirs
import java.io.InputStream import java.io.InputStream
object Manga { object Manga {
@@ -57,6 +61,7 @@ object Manga {
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name, MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
mangaEntry[MangaTable.inLibrary], mangaEntry[MangaTable.inLibrary],
getSource(mangaEntry[MangaTable.sourceReference]), getSource(mangaEntry[MangaTable.sourceReference]),
getMangaMetaMap(mangaEntry[MangaTable.id]),
false false
) )
} else { // initialize manga } else { // initialize manga
@@ -104,11 +109,38 @@ object Manga {
MangaStatus.valueOf(fetchedManga.status).name, MangaStatus.valueOf(fetchedManga.status).name,
mangaEntry[MangaTable.inLibrary], mangaEntry[MangaTable.inLibrary],
getSource(mangaEntry[MangaTable.sourceReference]), getSource(mangaEntry[MangaTable.sourceReference]),
getMangaMetaMap(mangaEntry[MangaTable.id]),
true true
) )
} }
} }
fun getMangaMetaMap(manga: EntityID<Int>): Map<String, String> {
return transaction {
MangaMetaTable.select { MangaMetaTable.ref eq manga }
.associate { it[MangaMetaTable.key] to it[MangaMetaTable.value] }
}
}
fun modifyMangaMeta(mangaId: Int, key: String, value: String) {
transaction {
val manga = MangaMetaTable.select { (MangaTable.id eq mangaId) }
.first()[MangaTable.id]
val meta = transaction { MangaMetaTable.select { (MangaMetaTable.ref eq manga) and (MangaMetaTable.key eq key) } }.firstOrNull()
if (meta == null) {
MangaMetaTable.insert {
it[MangaMetaTable.key] = key
it[MangaMetaTable.value] = value
it[MangaMetaTable.ref] = manga
}
} else {
MangaMetaTable.update {
it[MangaMetaTable.value] = value
}
}
}
}
private val applicationDirs by DI.global.instance<ApplicationDirs>() private val applicationDirs by DI.global.instance<ApplicationDirs>()
suspend fun getMangaThumbnail(mangaId: Int): Pair<InputStream, String> { suspend fun getMangaThumbnail(mangaId: Int): Pair<InputStream, String> {
val saveDir = applicationDirs.mangaThumbnailsRoot val saveDir = applicationDirs.mangaThumbnailsRoot
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl package suwayomi.tachidesk.manga.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -11,12 +11,13 @@ import eu.kanade.tachiyomi.source.model.MangasPage
import org.jetbrains.exposed.sql.insertAndGetId import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.Manga.getMangaMetaMap
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.model.dataclass.MangaDataClass import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.model.dataclass.PagedMangaListDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.table.MangaStatus import suwayomi.tachidesk.manga.model.dataclass.PagedMangaListDataClass
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaStatus
import suwayomi.tachidesk.manga.model.table.MangaTable
object MangaList { object MangaList {
fun proxyThumbnailUrl(mangaId: Int): String { fun proxyThumbnailUrl(mangaId: Int): String {
@@ -89,7 +90,8 @@ object MangaList {
mangaEntry[MangaTable.description], mangaEntry[MangaTable.description],
mangaEntry[MangaTable.genre], mangaEntry[MangaTable.genre],
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name, MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
mangaEntry[MangaTable.inLibrary] mangaEntry[MangaTable.inLibrary],
meta = getMangaMetaMap(mangaEntry[MangaTable.id])
) )
} }
} }
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl package suwayomi.tachidesk.manga.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -16,14 +16,14 @@ import org.jetbrains.exposed.sql.update
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import suwayomi.server.ApplicationDirs import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse import suwayomi.tachidesk.manga.impl.util.storage.SafePath
import suwayomi.tachidesk.impl.util.storage.SafePath import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.PageTable
import suwayomi.tachidesk.model.table.PageTable import suwayomi.tachidesk.server.ApplicationDirs
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl package suwayomi.tachidesk.manga.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -7,10 +7,10 @@ package suwayomi.tachidesk.impl
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import suwayomi.tachidesk.impl.MangaList.processEntries import suwayomi.tachidesk.manga.impl.MangaList.processEntries
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.model.dataclass.PagedMangaListDataClass import suwayomi.tachidesk.manga.model.dataclass.PagedMangaListDataClass
object Search { object Search {
// TODO // TODO
@@ -0,0 +1,125 @@
package suwayomi.tachidesk.manga.impl
/*
* 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.app.Application
import android.content.Context
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.getPreferenceKey
import mu.KotlinLogging
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl
import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.manga.impl.util.GetHttpSource.invalidateSourceCache
import suwayomi.tachidesk.manga.model.dataclass.SourceDataClass
import suwayomi.tachidesk.manga.model.table.ExtensionTable
import suwayomi.tachidesk.manga.model.table.SourceTable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import xyz.nulldev.androidcompat.androidimpl.CustomContext
object Source {
private val logger = KotlinLogging.logger {}
fun getSourceList(): List<SourceDataClass> {
return transaction {
SourceTable.selectAll().map {
SourceDataClass(
it[SourceTable.id].value.toString(),
it[SourceTable.name],
it[SourceTable.lang],
getExtensionIconUrl(ExtensionTable.select { ExtensionTable.id eq it[SourceTable.extension] }.first()[ExtensionTable.apkName]),
getHttpSource(it[SourceTable.id].value).supportsLatest,
getHttpSource(it[SourceTable.id].value) is ConfigurableSource
)
}
}
}
fun getSource(sourceId: Long): SourceDataClass {
return transaction {
val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()
SourceDataClass(
sourceId.toString(),
source?.get(SourceTable.name),
source?.get(SourceTable.lang),
source?.let { getExtensionIconUrl(ExtensionTable.select { ExtensionTable.id eq source[SourceTable.extension] }.first()[ExtensionTable.apkName]) },
source?.let { getHttpSource(sourceId).supportsLatest },
source?.let { getHttpSource(sourceId) is ConfigurableSource },
)
}
}
private val context by DI.global.instance<CustomContext>()
/**
* Clients should support these types for extensions to work properly (in order of importance)
* - EditTextPreference
* - SwitchPreferenceCompat
* - ListPreference
* - CheckBoxPreference
*/
data class PreferenceObject(
val type: String,
val props: Any
)
var preferenceScreenMap: MutableMap<Long, PreferenceScreen> = mutableMapOf()
/**
* Gets a source's PreferenceScreen, puts the result into [preferenceScreenMap]
*/
fun getSourcePreferences(sourceId: Long): List<PreferenceObject> {
val source = getHttpSource(sourceId)
if (source is ConfigurableSource) {
val sourceShardPreferences = Injekt.get<Application>().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE)
val screen = PreferenceScreen(context)
screen.sharedPreferences = sourceShardPreferences
source.setupPreferenceScreen(screen)
preferenceScreenMap[sourceId] = screen
return screen.preferences.map {
PreferenceObject(it::class.java.simpleName, it)
}
}
return emptyList()
}
data class SourcePreferenceChange(
val position: Int,
val value: String
)
fun setSourcePreference(sourceId: Long, change: SourcePreferenceChange) {
val screen = preferenceScreenMap[sourceId]!!
val pref = screen.preferences[change.position]
val newValue = when (pref.defaultValueType) {
"String" -> change.value
"Boolean" -> change.value.toBoolean()
else -> throw RuntimeException("Unsupported type conversion")
}
pref.saveNewValue(newValue)
pref.callChangeListener(newValue)
// must reload the source cache because a preference was changed
invalidateSourceCache(sourceId)
}
}
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup package suwayomi.tachidesk.manga.impl.backup
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.legacy package suwayomi.tachidesk.manga.impl.backup.legacy
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -11,16 +11,16 @@ import com.github.salomonbrys.kotson.registerTypeAdapter
import com.github.salomonbrys.kotson.registerTypeHierarchyAdapter import com.github.salomonbrys.kotson.registerTypeHierarchyAdapter
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import suwayomi.tachidesk.impl.backup.legacy.models.DHistory import suwayomi.tachidesk.manga.impl.backup.legacy.models.DHistory
import suwayomi.tachidesk.impl.backup.legacy.serializer.CategoryTypeAdapter import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.CategoryTypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.serializer.ChapterTypeAdapter import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.ChapterTypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.serializer.HistoryTypeAdapter import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.HistoryTypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.serializer.MangaTypeAdapter import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.MangaTypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.serializer.TrackTypeAdapter import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.TrackTypeAdapter
import suwayomi.tachidesk.impl.backup.models.CategoryImpl import suwayomi.tachidesk.manga.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.impl.backup.models.ChapterImpl import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.impl.backup.models.MangaImpl import suwayomi.tachidesk.manga.impl.backup.models.MangaImpl
import suwayomi.tachidesk.impl.backup.models.TrackImpl import suwayomi.tachidesk.manga.impl.backup.models.TrackImpl
import java.util.Date import java.util.Date
open class LegacyBackupBase { open class LegacyBackupBase {
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.legacy package suwayomi.tachidesk.manga.impl.backup.legacy
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -14,18 +14,18 @@ import com.google.gson.JsonObject
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.impl.Category.getCategoryList import suwayomi.tachidesk.manga.impl.Category.getCategoryList
import suwayomi.tachidesk.impl.CategoryManga.getMangaCategories import suwayomi.tachidesk.manga.impl.CategoryManga.getMangaCategories
import suwayomi.tachidesk.impl.backup.BackupFlags import suwayomi.tachidesk.manga.impl.backup.BackupFlags
import suwayomi.tachidesk.impl.backup.legacy.models.Backup import suwayomi.tachidesk.manga.impl.backup.legacy.models.Backup
import suwayomi.tachidesk.impl.backup.legacy.models.Backup.CURRENT_VERSION import suwayomi.tachidesk.manga.impl.backup.legacy.models.Backup.CURRENT_VERSION
import suwayomi.tachidesk.impl.backup.models.CategoryImpl import suwayomi.tachidesk.manga.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.impl.backup.models.ChapterImpl import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.impl.backup.models.Manga import suwayomi.tachidesk.manga.impl.backup.models.Manga
import suwayomi.tachidesk.impl.backup.models.MangaImpl import suwayomi.tachidesk.manga.impl.backup.models.MangaImpl
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
object LegacyBackupExport : LegacyBackupBase() { object LegacyBackupExport : LegacyBackupBase() {
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.legacy package suwayomi.tachidesk.manga.impl.backup.legacy
import com.github.salomonbrys.kotson.fromJson import com.github.salomonbrys.kotson.fromJson
import com.google.gson.JsonArray import com.google.gson.JsonArray
@@ -13,22 +13,22 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.Category.createCategory import suwayomi.tachidesk.manga.impl.Category.createCategory
import suwayomi.tachidesk.impl.Category.getCategoryList import suwayomi.tachidesk.manga.impl.Category.getCategoryList
import suwayomi.tachidesk.impl.backup.legacy.LegacyBackupValidator.ValidationResult import suwayomi.tachidesk.manga.impl.backup.legacy.LegacyBackupValidator.ValidationResult
import suwayomi.tachidesk.impl.backup.legacy.LegacyBackupValidator.validate import suwayomi.tachidesk.manga.impl.backup.legacy.LegacyBackupValidator.validate
import suwayomi.tachidesk.impl.backup.legacy.models.Backup import suwayomi.tachidesk.manga.impl.backup.legacy.models.Backup
import suwayomi.tachidesk.impl.backup.legacy.models.DHistory import suwayomi.tachidesk.manga.impl.backup.legacy.models.DHistory
import suwayomi.tachidesk.impl.backup.models.CategoryImpl import suwayomi.tachidesk.manga.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.impl.backup.models.Chapter import suwayomi.tachidesk.manga.impl.backup.models.Chapter
import suwayomi.tachidesk.impl.backup.models.ChapterImpl import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.impl.backup.models.Manga import suwayomi.tachidesk.manga.impl.backup.models.Manga
import suwayomi.tachidesk.impl.backup.models.MangaImpl import suwayomi.tachidesk.manga.impl.backup.models.MangaImpl
import suwayomi.tachidesk.impl.backup.models.Track import suwayomi.tachidesk.manga.impl.backup.models.Track
import suwayomi.tachidesk.impl.backup.models.TrackImpl import suwayomi.tachidesk.manga.impl.backup.models.TrackImpl
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
import java.io.InputStream import java.io.InputStream
import java.util.Date import java.util.Date
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.legacy package suwayomi.tachidesk.manga.impl.backup.legacy
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -10,8 +10,8 @@ package suwayomi.tachidesk.impl.backup.legacy
import com.google.gson.JsonObject import com.google.gson.JsonObject
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.impl.backup.legacy.models.Backup import suwayomi.tachidesk.manga.impl.backup.legacy.models.Backup
import suwayomi.tachidesk.model.table.SourceTable import suwayomi.tachidesk.manga.model.table.SourceTable
object LegacyBackupValidator { object LegacyBackupValidator {
data class ValidationResult(val missingSources: List<String>, val missingTrackers: List<String>) data class ValidationResult(val missingSources: List<String>, val missingTrackers: List<String>)
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.legacy.models package suwayomi.tachidesk.manga.impl.backup.legacy.models
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
@@ -0,0 +1,3 @@
package suwayomi.tachidesk.manga.impl.backup.legacy.models
data class DHistory(val url: String, val lastRead: Long)
@@ -1,8 +1,8 @@
package suwayomi.tachidesk.impl.backup.legacy.serializer package suwayomi.tachidesk.manga.impl.backup.legacy.serializer
import com.github.salomonbrys.kotson.typeAdapter import com.github.salomonbrys.kotson.typeAdapter
import com.google.gson.TypeAdapter import com.google.gson.TypeAdapter
import suwayomi.tachidesk.impl.backup.models.CategoryImpl import suwayomi.tachidesk.manga.impl.backup.models.CategoryImpl
/** /**
* JSON Serializer used to write / read [CategoryImpl] to / from json * JSON Serializer used to write / read [CategoryImpl] to / from json
@@ -1,9 +1,9 @@
package suwayomi.tachidesk.impl.backup.legacy.serializer package suwayomi.tachidesk.manga.impl.backup.legacy.serializer
import com.github.salomonbrys.kotson.typeAdapter import com.github.salomonbrys.kotson.typeAdapter
import com.google.gson.TypeAdapter import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonToken import com.google.gson.stream.JsonToken
import suwayomi.tachidesk.impl.backup.models.ChapterImpl import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl
/** /**
* JSON Serializer used to write / read [ChapterImpl] to / from json * JSON Serializer used to write / read [ChapterImpl] to / from json
@@ -1,8 +1,8 @@
package suwayomi.tachidesk.impl.backup.legacy.serializer package suwayomi.tachidesk.manga.impl.backup.legacy.serializer
import com.github.salomonbrys.kotson.typeAdapter import com.github.salomonbrys.kotson.typeAdapter
import com.google.gson.TypeAdapter import com.google.gson.TypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.models.DHistory import suwayomi.tachidesk.manga.impl.backup.legacy.models.DHistory
/** /**
* JSON Serializer used to write / read [DHistory] to / from json * JSON Serializer used to write / read [DHistory] to / from json

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