Dependency update / Dropped features that supports <Android 21 / Dropped support for hiyobi.me

This commit is contained in:
tom5079
2021-05-15 06:43:14 +09:00
parent 00c8078642
commit 51851addc1
21 changed files with 64 additions and 268 deletions

2
.idea/compiler.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8" /> <bytecodeTargetLevel target="11" />
</component> </component>
</project> </project>

1
.idea/gradle.xml generated
View File

@@ -14,7 +14,6 @@
</set> </set>
</option> </option>
<option name="resolveModulePerSourceSet" value="false" /> <option name="resolveModulePerSourceSet" value="false" />
<option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings> </GradleProjectSettings>
</option> </option>
</component> </component>

2
.idea/misc.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@@ -3,6 +3,7 @@
<component name="RunConfigurationProducerService"> <component name="RunConfigurationProducerService">
<option name="ignoredProducers"> <option name="ignoredProducers">
<set> <set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" /> <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" /> <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" /> <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />

View File

@@ -14,23 +14,6 @@ if (file("google-services.json").exists()) {
logger.lifecycle("Firebase Disabled") logger.lifecycle("Firebase Disabled")
} }
ext {
okhttp_version = "3.12.12"
}
configurations {
all {
resolutionStrategy {
eachDependency { DependencyResolveDetails details ->
if (details.requested.group == "com.squareup.okhttp3" && details.requested.name == "okhttp") {
// OkHttp drops support before 5.0 since 3.13.0
details.useVersion okhttp_version
}
}
}
}
}
android { android {
compileSdkVersion 30 compileSdkVersion 30
defaultConfig { defaultConfig {
@@ -75,7 +58,6 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
buildToolsVersion = "29.0.3"
lintOptions { lintOptions {
abortOnError false abortOnError false
@@ -84,27 +66,27 @@ android {
dependencies { dependencies {
implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"]) implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0-RC"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1" implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.0"
implementation "androidx.appcompat:appcompat:1.2.0" implementation "androidx.appcompat:appcompat:1.2.0"
implementation "androidx.activity:activity-ktx:1.3.0-alpha05" implementation "androidx.activity:activity-ktx:1.3.0-alpha07"
implementation "androidx.fragment:fragment-ktx:1.3.2" implementation "androidx.fragment:fragment-ktx:1.3.3"
implementation "androidx.preference:preference-ktx:1.1.1" implementation "androidx.preference:preference-ktx:1.1.1"
implementation "androidx.recyclerview:recyclerview:1.1.0" implementation "androidx.recyclerview:recyclerview:1.2.0"
implementation "androidx.constraintlayout:constraintlayout:2.0.4" implementation "androidx.constraintlayout:constraintlayout:2.0.4"
implementation "androidx.gridlayout:gridlayout:1.0.0" implementation "androidx.gridlayout:gridlayout:1.0.0"
implementation "androidx.biometric:biometric:1.1.0" implementation "androidx.biometric:biometric:1.1.0"
implementation "androidx.work:work-runtime-ktx:2.5.0" implementation "androidx.work:work-runtime-ktx:2.6.0-alpha02"
implementation 'org.kodein.di:kodein-di-framework-android-x:7.4.0' implementation 'org.kodein.di:kodein-di-framework-android-x:7.5.0'
implementation "com.daimajia.swipelayout:library:1.2.0@aar" implementation "com.daimajia.swipelayout:library:1.2.0@aar"
implementation "com.google.android.material:material:1.3.0" implementation "com.google.android.material:material:1.3.0"
implementation platform("com.google.firebase:firebase-bom:26.1.0") implementation platform("com.google.firebase:firebase-bom:28.0.0")
implementation "com.google.firebase:firebase-analytics-ktx" implementation "com.google.firebase:firebase-analytics-ktx"
implementation "com.google.firebase:firebase-crashlytics" implementation "com.google.firebase:firebase-crashlytics"
implementation "com.google.firebase:firebase-perf" implementation "com.google.firebase:firebase-perf"
@@ -115,26 +97,23 @@ dependencies {
//implementation "com.quiph.ui:recyclerviewfastscroller:0.2.1" //implementation "com.quiph.ui:recyclerviewfastscroller:0.2.1"
implementation 'com.github.piasy:BigImageViewer:1.7.0' implementation 'com.github.piasy:BigImageViewer:1.8.0'
implementation 'com.github.piasy:FrescoImageLoader:1.7.0' implementation 'com.github.piasy:FrescoImageLoader:1.8.0'
implementation 'com.github.piasy:FrescoImageViewFactory:1.7.0' implementation 'com.github.piasy:FrescoImageViewFactory:1.8.0'
//noinspection GradleDependency //noinspection GradleDependency
implementation "com.squareup.okhttp3:okhttp:$okhttp_version" implementation "com.squareup.okhttp3:okhttp:4.9.0"
implementation "com.tbuonomo.andrui:viewpagerdotsindicator:4.1.2" implementation "com.tbuonomo:dotsindicator:4.2"
implementation "net.rdrei.android.dirchooser:library:3.2@aar" //implementation "com.andrognito.patternlockview:patternlockview:1.0.0"
implementation "com.gu:option:1.3"
implementation "com.andrognito.patternlockview:patternlockview:1.0.0"
//implementation "com.andrognito.pinlockview:pinlockview:2.1.0" //implementation "com.andrognito.pinlockview:pinlockview:2.1.0"
implementation "ru.noties.markwon:core:3.1.0" implementation "ru.noties.markwon:core:3.1.0"
implementation "xyz.quaver:libpupil:1.9.7" implementation "xyz.quaver:libpupil:2.0.0"
implementation "xyz.quaver:documentfilex:0.5-SNAPSHOT" implementation "xyz.quaver:documentfilex:0.6.1"
implementation "xyz.quaver:floatingsearchview:1.1.3-SNAPSHOT" implementation "xyz.quaver:floatingsearchview:1.1.7"
implementation "com.orhanobut:logger:2.2.0" implementation "com.orhanobut:logger:2.2.0"

Binary file not shown.

View File

@@ -143,8 +143,7 @@ val sourceModule = DI.Module(name = "source") {
bind() from setBinding<SourceEntry>() bind() from setBinding<SourceEntry>()
listOf( listOf(
Hitomi(), Hitomi()
Hiyobi()
).forEach { source -> ).forEach { source ->
bind<SourceEntry>().inSet() with multiton { _: Unit -> source.name to (source as AnySource) } bind<SourceEntry>().inSet() with multiton { _: Unit -> source.name to (source as AnySource) }
} }

View File

@@ -227,7 +227,7 @@ class Hitomi : Source<Hitomi.SortMode, Hitomi.TagSuggestion>() {
}.getOrDefault("") }, }.getOrDefault("") },
ExtraType.SERIES to CoroutineScope(Dispatchers.Unconfined).async { galleryBlock.series.joinToString { it.wordCapitalize() } }, ExtraType.SERIES to CoroutineScope(Dispatchers.Unconfined).async { galleryBlock.series.joinToString { it.wordCapitalize() } },
ExtraType.TYPE to CoroutineScope(Dispatchers.Unconfined).async { galleryBlock.type.wordCapitalize() }, ExtraType.TYPE to CoroutineScope(Dispatchers.Unconfined).async { galleryBlock.type.wordCapitalize() },
ExtraType.LANGUAGE to CoroutineScope(Dispatchers.Unconfined).async { galleryBlock.language }, ExtraType.LANGUAGE to CoroutineScope(Dispatchers.Unconfined).async { languageMap[galleryBlock.language] },
ExtraType.PAGECOUNT to CoroutineScope(Dispatchers.IO).async { kotlin.runCatching { ExtraType.PAGECOUNT to CoroutineScope(Dispatchers.IO).async { kotlin.runCatching {
getGalleryInfo(galleryBlock.id).files.size.toString() getGalleryInfo(galleryBlock.id).files.size.toString()
}.getOrNull() }, }.getOrNull() },

View File

@@ -1,140 +0,0 @@
/*
* Pupil, Hitomi.la viewer for Android
* Copyright (C) 2020 tom5079
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package xyz.quaver.pupil.sources
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Request
import xyz.quaver.floatingsearchview.databinding.SearchSuggestionItemBinding
import xyz.quaver.hiyobi.*
import xyz.quaver.pupil.R
import xyz.quaver.pupil.client
import xyz.quaver.pupil.util.wordCapitalize
import java.io.IOException
import java.util.*
class Hiyobi : Source<DefaultSortMode, DefaultSearchSuggestion>() {
override val name: String = "hiyobi.me"
override val iconResID: Int = R.drawable.ic_hiyobi
override val availableSortMode: Array<DefaultSortMode> = DefaultSortMode.values()
override suspend fun search(query: String, range: IntRange, sortMode: Enum<*>): Pair<Channel<ItemInfo>, Int> {
val channel = Channel<ItemInfo>()
val (results, total) = if (query.isEmpty())
list(range)
else
search(query.trim(), range)
CoroutineScope(Dispatchers.Unconfined).launch {
results.forEach {
channel.send(transform(name, it))
}
channel.close()
}
return Pair(channel, total)
}
override suspend fun suggestion(query: String): List<DefaultSearchSuggestion> {
val result = mutableSetOf<String>()
for (tag in allTags.await()) {
if (result.size >= 10)
break
val lowQuery = query.toLowerCase(Locale.ROOT)
if (tag.contains(lowQuery, true))
result.add(tag)
}
return result.map { DefaultSearchSuggestion(it) }
}
override suspend fun images(itemID: String): List<String> {
return createImgList(itemID, getGalleryInfo(itemID), true).map {
it.path
}
}
override suspend fun info(itemID: String): ItemInfo {
return transform(name, getGalleryBlock(itemID))
}
override fun onSuggestionBind(binding: SearchSuggestionItemBinding, item: DefaultSearchSuggestion) {
val split = item.body.split(':', limit = 2)
if (split.size != 2)
return
binding.leftIcon.setImageResource(
when(split.first()) {
"female" -> R.drawable.gender_female
"male" -> R.drawable.gender_male
"language" -> R.drawable.translate
"group" -> R.drawable.account_group
"character" -> R.drawable.account_star
"series" -> R.drawable.book_open
"artist" -> R.drawable.brush
else -> R.drawable.tag
}
)
binding.body.text = split.last()
}
companion object {
private fun downloadAllTagsAsync(): Deferred<List<String>> = CoroutineScope(Dispatchers.IO).async {
Json.decodeFromString(kotlin.runCatching {
client.newCall(Request.Builder().url("https://api.hiyobi.me/auto.json").build()).execute().also { if (it.code() != 200) throw IOException() }.body()?.use { it.string() }
}.getOrNull() ?: "[]")
}
private var _allTags: Deferred<List<String>>? = null
val allTags: Deferred<List<String>>
get() = if (_allTags == null || (_allTags!!.isCompleted && runBlocking { _allTags!!.await() }.isEmpty())) downloadAllTagsAsync().also {
_allTags = it
} else _allTags!!
suspend fun transform(name: String, galleryBlock: GalleryBlock): ItemInfo = withContext(Dispatchers.IO) {
ItemInfo(
name,
galleryBlock.id,
galleryBlock.title,
"https://cdn.$hiyobi/tn/${galleryBlock.id}.jpg",
galleryBlock.artists.joinToString { it.value.wordCapitalize() },
mapOf(
ItemInfo.ExtraType.CHARACTER to async { galleryBlock.characters.joinToString { it.value.wordCapitalize() } },
ItemInfo.ExtraType.SERIES to async { galleryBlock.parodys.joinToString { it.value.wordCapitalize() } },
ItemInfo.ExtraType.TYPE to async { galleryBlock.type.name.replace('_', ' ').wordCapitalize() },
ItemInfo.ExtraType.PAGECOUNT to async { getGalleryInfo(galleryBlock.id).files.size.toString() },
ItemInfo.ExtraType.GROUP to async { galleryBlock.groups.joinToString { it.value.wordCapitalize() } },
ItemInfo.ExtraType.TAGS to async { galleryBlock.tags.joinToString() { it.value } }
)
)
}
}
}

View File

@@ -29,8 +29,6 @@ import androidx.core.content.ContextCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import net.rdrei.android.dirchooser.DirectoryChooserActivity
import net.rdrei.android.dirchooser.DirectoryChooserConfig
import org.kodein.di.DIAware import org.kodein.di.DIAware
import org.kodein.di.android.x.di import org.kodein.di.android.x.di
import org.kodein.di.instance import org.kodein.di.instance
@@ -63,8 +61,7 @@ class DownloadLocationDialogFragment : DialogFragment(), DIAware {
it.data?.data?.let { uri -> it.data?.data?.let { uri ->
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) context.contentResolver.takePersistableUriPermission(uri, takeFlags)
context.contentResolver.takePersistableUriPermission(uri, takeFlags)
if (kotlin.runCatching { FileX(context, uri).canWrite() }.getOrDefault(false)) { if (kotlin.runCatching { FileX(context, uri).canWrite() }.getOrDefault(false)) {
entries[null]?.locationAvailable?.text = uri.toFile(context)?.canonicalPath entries[null]?.locationAvailable?.text = uri.toFile(context)?.canonicalPath
@@ -85,32 +82,6 @@ class DownloadLocationDialogFragment : DialogFragment(), DIAware {
} }
} }
private val requestDownloadFolderOldLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val context = context ?: return@registerForActivityResult
val dialog = dialog ?: return@registerForActivityResult
if (it.resultCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
val directory = it.data?.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR)!!
if (!File(directory).canWrite()) {
Snackbar.make(
dialog.window!!.decorView.rootView,
R.string.settings_download_folder_not_writable,
Snackbar.LENGTH_LONG
).show()
val downloadFolder = downloadManager.downloadFolder.canonicalPath
val key = entries.keys.firstOrNull { it?.canonicalPath == downloadFolder }
entries[key]!!.button.isChecked = true
if (key == null) entries[key]!!.locationAvailable.text = downloadFolder
}
else {
entries[null]?.locationAvailable?.text = directory
Preferences["download_folder"] = File(directory).toURI().toString()
}
}
}
private fun initView() { private fun initView() {
val externalFilesDirs = ContextCompat.getExternalFilesDirs(requireContext(), null) val externalFilesDirs = ContextCompat.getExternalFilesDirs(requireContext(), null)
@@ -145,29 +116,16 @@ class DownloadLocationDialogFragment : DialogFragment(), DIAware {
} }
button.performClick() button.performClick()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply { addFlags(
addFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION )
) putExtra("android.content.extra.SHOW_ADVANCED", true)
putExtra("android.content.extra.SHOW_ADVANCED", true)
}
requestDownloadFolderLauncher.launch(intent)
} else { // Can't use SAF on old Androids!
val config = DirectoryChooserConfig.builder()
.newDirectoryName("Pupil")
.allowNewDirectoryNameModification(true)
.build()
val intent = Intent(context, DirectoryChooserActivity::class.java).apply {
putExtra(DirectoryChooserActivity.EXTRA_CONFIG, config)
}
requestDownloadFolderOldLauncher.launch(intent)
} }
requestDownloadFolderLauncher.launch(intent)
} }
entries[null] = this entries[null] = this
} }

View File

@@ -26,6 +26,7 @@ import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.kodein.di.* import org.kodein.di.*
import org.kodein.di.android.x.closestDI
import org.kodein.di.android.x.di import org.kodein.di.android.x.di
import org.kodein.type.jvmType import org.kodein.type.jvmType
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
@@ -36,7 +37,7 @@ import xyz.quaver.pupil.sources.SourceEntries
class SourceSelectDialog : DialogFragment(), DIAware { class SourceSelectDialog : DialogFragment(), DIAware {
override val di by di() override val di by closestDI()
var onSourceSelectedListener: ((String) -> Unit)? = null var onSourceSelectedListener: ((String) -> Unit)? = null

View File

@@ -29,6 +29,7 @@ import androidx.preference.PreferenceFragmentCompat
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import okhttp3.* import okhttp3.*
import org.kodein.di.DIAware import org.kodein.di.DIAware
import org.kodein.di.android.x.closestDI
import org.kodein.di.android.x.di import org.kodein.di.android.x.di
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.client import xyz.quaver.pupil.client
@@ -38,7 +39,7 @@ import java.io.IOException
class ManageFavoritesFragment : PreferenceFragmentCompat(), DIAware { class ManageFavoritesFragment : PreferenceFragmentCompat(), DIAware {
override val di by di() override val di by closestDI()
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.manage_favorites_preferences, rootKey) setPreferencesFromResource(R.xml.manage_favorites_preferences, rootKey)
@@ -65,14 +66,14 @@ class ManageFavoritesFragment : PreferenceFragmentCompat(), DIAware {
} }
override fun onResponse(call: Call, response: Response) { override fun onResponse(call: Call, response: Response) {
if (response.code() != 200) { if (response.code != 200) {
response.close() response.close()
return return
} }
Intent(Intent.ACTION_SEND).apply { Intent(Intent.ACTION_SEND).apply {
type = "text/plain" type = "text/plain"
putExtra(Intent.EXTRA_TEXT, response.body()?.use { it.string() }?.replace("\n", "")) putExtra(Intent.EXTRA_TEXT, response.body?.use { it.string() }?.replace("\n", ""))
}.let { }.let {
getContext()?.startActivity(Intent.createChooser(it, getString(R.string.settings_backup_share))) getContext()?.startActivity(Intent.createChooser(it, getString(R.string.settings_backup_share)))
} }

View File

@@ -41,7 +41,7 @@ import xyz.quaver.pupil.types.NoResultSuggestion
import java.util.* import java.util.*
class FloatingSearchView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class FloatingSearchView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
FloatingSearchView(context, attrs), xyz.quaver.floatingsearchview.FloatingSearchView(context, attrs),
FloatingSearchView.OnSearchListener, FloatingSearchView.OnSearchListener,
TextWatcher TextWatcher
{ {

View File

@@ -26,6 +26,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.* import kotlinx.coroutines.*
import okhttp3.Headers import okhttp3.Headers
import okhttp3.Headers.Companion.toHeaders
import okhttp3.Request import okhttp3.Request
import org.kodein.di.DIAware import org.kodein.di.DIAware
import org.kodein.di.android.x.di import org.kodein.di.android.x.di
@@ -73,7 +74,7 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
val file = cache.load( val file = cache.load(
Request.Builder() Request.Builder()
.url(image) .url(image)
.headers(Headers.of(source.getHeadersForImage(itemID, image))) .headers(source.getHeadersForImage(itemID, image).toHeaders())
.build() .build()
) )

View File

@@ -62,8 +62,8 @@ class ImageCache(context: Context) : DIAware {
} }
fun free(images: List<String>) { fun free(images: List<String>) {
client.dispatcher().let { it.queuedCalls() + it.runningCalls() } client.dispatcher.let { it.queuedCalls() + it.runningCalls() }
.filter { it.request().url().toString() in images } .filter { it.request().url.toString() in images }
.forEach { it.cancel() } .forEach { it.cancel() }
images.forEach { _channels.remove(it) } images.forEach { _channels.remove(it) }
@@ -71,7 +71,7 @@ class ImageCache(context: Context) : DIAware {
@Synchronized @Synchronized
suspend fun clear() = coroutineScope { suspend fun clear() = coroutineScope {
client.dispatcher().queuedCalls().forEach { it.cancel() } client.dispatcher.queuedCalls().forEach { it.cancel() }
cacheFolder.listFiles()?.forEach { it.delete() } cacheFolder.listFiles()?.forEach { it.delete() }
cache.clear() cache.clear()
@@ -79,7 +79,7 @@ class ImageCache(context: Context) : DIAware {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
fun load(request: Request): File { fun load(request: Request): File {
val key = request.url().toString() val key = request.url.toString()
val channel = if (_channels[key]?.isClosedForSend == false) val channel = if (_channels[key]?.isClosedForSend == false)
_channels[key]!! _channels[key]!!
@@ -93,16 +93,16 @@ class ImageCache(context: Context) : DIAware {
client.newCall(request).enqueue(object: Callback { client.newCall(request).enqueue(object: Callback {
override fun onFailure(call: Call, e: IOException) { override fun onFailure(call: Call, e: IOException) {
file.delete() file.delete()
cache.remove(call.request().url().toString()) cache.remove(call.request().url.toString())
FirebaseCrashlytics.getInstance().recordException(e) FirebaseCrashlytics.getInstance().recordException(e)
channel.close(e) channel.close(e)
} }
override fun onResponse(call: Call, response: Response) { override fun onResponse(call: Call, response: Response) {
if (response.code() != 200) { if (response.code != 200) {
file.delete() file.delete()
cache.remove(call.request().url().toString()) cache.remove(call.request().url.toString())
channel.close(IOException("HTTP Response code is not 200")) channel.close(IOException("HTTP Response code is not 200"))
@@ -110,7 +110,7 @@ class ImageCache(context: Context) : DIAware {
return return
} }
response.body()?.use { body -> response.body?.use { body ->
if (!file.exists()) if (!file.exists())
file.createNewFile() file.createNewFile()

View File

@@ -46,7 +46,7 @@ data class ProxyInfo(
Authenticator { _, response -> Authenticator { _, response ->
val credential = Credentials.basic(username, password) val credential = Credentials.basic(username, password)
response.request().newBuilder() response.request.newBuilder()
.header("Proxy-Authorization", credential) .header("Proxy-Authorization", credential)
.build() .build()
} }

View File

@@ -44,10 +44,10 @@ fun updateTranslations() = CoroutineScope(Dispatchers.IO).launch {
translations = emptyMap() translations = emptyMap()
kotlin.runCatching { kotlin.runCatching {
translations = Json.decodeFromString<Map<String, String>>(client.newCall( translations = Json.decodeFromString<Map<String, String>>(client.newCall(
Request.Builder() Request.Builder()
.url(contentURL + "${Preferences["tag_translation", ""].let { if (it.isEmpty()) Locale.getDefault().language else it }}.json") .url(contentURL + "${Preferences["tag_translation", ""].let { if (it.isEmpty()) Locale.getDefault().language else it }}.json")
.build() .build()
).execute().also { if (it.code() != 200) return@launch }.body()?.use { it.string() } ?: return@launch).filterValues { it.isNotEmpty() } ).execute().also { if (it.code != 200) return@launch }.body?.use { it.string() } ?: return@launch).filterValues { it.isNotEmpty() }
} }
} }
@@ -58,7 +58,7 @@ fun getAvailableLanguages(): List<String> {
Request.Builder() Request.Builder()
.url(filesURL) .url(filesURL)
.build() .build()
).execute().also { if (it.code() != 200) throw IOException() }.body()?.use { it.string() } ?: return emptyList()) ).execute().also { if (it.code != 200) throw IOException() }.body?.use { it.string() } ?: return emptyList())
return listOf("en") + (json["tree"]?.jsonArray?.mapNotNull { return listOf("en") + (json["tree"]?.jsonArray?.mapNotNull {
val name = it["path"]?.jsonPrimitive?.content?.takeWhile { c -> c != '.' } val name = it["path"]?.jsonPrimitive?.content?.takeWhile { c -> c != '.' }

View File

@@ -199,7 +199,7 @@ fun restore(context: Context, url: String, onFailure: ((Throwable) -> Unit)? = n
override fun onResponse(call: Call, response: Response) { override fun onResponse(call: Call, response: Response) {
val favorites = object: DIAware { override val di by di(context); val favorites: SavedSourceSet by instance(tag = "favorites") } val favorites = object: DIAware { override val di by di(context); val favorites: SavedSourceSet by instance(tag = "favorites") }
kotlin.runCatching { kotlin.runCatching {
Json.decodeFromString<Set<String>>(response.also { if (it.code() != 200) throw IOException() }.body().use { it?.string() } ?: "[]").let { Json.decodeFromString<Set<String>>(response.also { if (it.code != 200) throw IOException() }.body.use { it?.string() } ?: "[]").let {
favorites.favorites.addAll(mapOf("hitomi.la" to it)) favorites.favorites.addAll(mapOf("hitomi.la" to it))
onSuccess?.invoke(it) onSuccess?.invoke(it)
} }

View File

@@ -3,32 +3,29 @@
buildscript { buildscript {
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.1.3' classpath 'com.android.tools.build:gradle:4.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "com.google.gms:google-services:4.3.5" classpath "com.google.gms:google-services:4.3.5"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
classpath "com.google.firebase:firebase-crashlytics-gradle:2.5.2" classpath "com.google.firebase:firebase-crashlytics-gradle:2.6.0"
classpath "com.google.firebase:perf-plugin:1.3.5" classpath "com.google.firebase:perf-plugin:1.4.0"
classpath "com.google.android.gms:oss-licenses-plugin:0.10.2" classpath "com.google.android.gms:oss-licenses-plugin:0.10.4"
} }
} }
allprojects { allprojects {
repositories { repositories {
maven { url "https://dl.bintray.com/piasy/maven" }
google() google()
mavenCentral() mavenCentral()
jcenter()
mavenLocal() mavenLocal()
maven { url "https://oss.sonatype.org/content/repositories/snapshots" } maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
maven { url "https://guardian.github.com/maven/repo-releases" }
} }
} }

View File

@@ -21,4 +21,4 @@ android.enableJetifier=true
android.useAndroidX=true android.useAndroidX=true
android.enableBuildCache=true android.enableBuildCache=true
kotlin_version=1.4.32 kotlin_version=1.5.0

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip