(Almost) Full OkHTTP implementation
This commit is contained in:
5
.idea/jarRepositories.xml
generated
5
.idea/jarRepositories.xml
generated
@@ -51,5 +51,10 @@
|
||||
<option name="name" value="maven4" />
|
||||
<option name="url" value="https://maven.fabric.io/public" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenLocal" />
|
||||
<option name="name" value="MavenLocal" />
|
||||
<option name="url" value="file:/$USER_HOME$/.m2/repository/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
@@ -48,6 +48,7 @@ android {
|
||||
}
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
freeCompilerArgs += '-Xuse-experimental=kotlin.Experimental'
|
||||
}
|
||||
compileOptions {
|
||||
@@ -68,7 +69,6 @@ dependencies {
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation "androidx.biometric:biometric:1.0.1"
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.2.5'
|
||||
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
||||
implementation 'com.google.android.material:material:1.3.0-alpha02'
|
||||
@@ -78,8 +78,12 @@ dependencies {
|
||||
implementation 'com.google.firebase:firebase-perf:19.0.8'
|
||||
implementation 'com.github.arimorty:floatingsearchview:2.1.1'
|
||||
implementation 'com.github.clans:fab:1.6.4'
|
||||
//noinspection GradleDependency
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.12.12'
|
||||
implementation 'com.github.bumptech.glide:glide:4.11.0'
|
||||
implementation "com.github.bumptech.glide:okhttp3-integration:4.11.0"
|
||||
implementation ("com.github.bumptech.glide:okhttp3-integration:4.11.0") {
|
||||
transitive = false
|
||||
}
|
||||
implementation 'com.github.bumptech.glide:annotations:4.11.0'
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
|
||||
kapt 'com.github.bumptech.glide:compiler:4.11.0'
|
||||
@@ -92,7 +96,7 @@ dependencies {
|
||||
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
|
||||
//implementation 'com.andrognito.pinlockview:pinlockview:2.1.0'
|
||||
implementation "ru.noties.markwon:core:3.1.0"
|
||||
implementation ("xyz.quaver:libpupil:1.0") {
|
||||
implementation ("xyz.quaver:libpupil:1.1") {
|
||||
exclude group: 'org.jetbrains.kotlinx', module: 'kotlinx-serialization-core-jvm'
|
||||
}
|
||||
testImplementation 'junit:junit:4.13'
|
||||
|
||||
@@ -37,8 +37,11 @@
|
||||
|
||||
</provider>
|
||||
|
||||
<service android:name=".services.DownloadService"
|
||||
android:exported="false"/>
|
||||
|
||||
<receiver
|
||||
android:name=".BroadcastReciever"
|
||||
android:name=".reciever.UpdateBroadcastReciever"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
package xyz.quaver.pupil
|
||||
|
||||
import android.app.Application
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
@@ -25,7 +26,6 @@ import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.multidex.MultiDexApplication
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.gms.common.GooglePlayServicesNotAvailableException
|
||||
import com.google.android.gms.common.GooglePlayServicesRepairableException
|
||||
@@ -35,16 +35,37 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import xyz.quaver.proxy
|
||||
import okhttp3.Dispatcher
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Response
|
||||
import xyz.quaver.pupil.util.GalleryList
|
||||
import xyz.quaver.pupil.util.getProxy
|
||||
import xyz.quaver.pupil.util.getProxyInfo
|
||||
import xyz.quaver.setClient
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class Pupil : MultiDexApplication() {
|
||||
typealias PupilInterceptor = (Interceptor.Chain) -> Response
|
||||
|
||||
lateinit var histories: GalleryList
|
||||
lateinit var favorites: GalleryList
|
||||
lateinit var histories: GalleryList
|
||||
private set
|
||||
lateinit var favorites: GalleryList
|
||||
private set
|
||||
|
||||
val interceptors = mutableMapOf<KClass<out Any>, PupilInterceptor>()
|
||||
|
||||
lateinit var clientBuilder: OkHttpClient.Builder
|
||||
|
||||
var clientHolder: OkHttpClient? = null
|
||||
val client: OkHttpClient
|
||||
get() = clientHolder ?: clientBuilder.build().also {
|
||||
clientHolder = it
|
||||
setClient(it)
|
||||
}
|
||||
|
||||
class Pupil : Application() {
|
||||
|
||||
init {
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
@@ -63,7 +84,19 @@ class Pupil : MultiDexApplication() {
|
||||
|
||||
FirebaseCrashlytics.getInstance().setUserId(userID)
|
||||
|
||||
proxy = getProxy(this)
|
||||
val proxyInfo = getProxyInfo(this)
|
||||
|
||||
clientBuilder = OkHttpClient.Builder()
|
||||
.connectTimeout(0, TimeUnit.SECONDS)
|
||||
.readTimeout(0, TimeUnit.SECONDS)
|
||||
.proxy(proxyInfo.proxy())
|
||||
.proxyAuthenticator(proxyInfo.authenticator())
|
||||
.addInterceptor { chain ->
|
||||
val request = chain.request()
|
||||
val tag = request.tag() ?: return@addInterceptor chain.proceed(request)
|
||||
|
||||
interceptors[tag::class]?.invoke(chain) ?: chain.proceed(request)
|
||||
}
|
||||
|
||||
try {
|
||||
preference.getString("dl_location", null).also {
|
||||
@@ -112,6 +145,13 @@ class Pupil : MultiDexApplication() {
|
||||
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
||||
})
|
||||
|
||||
manager.createNotificationChannel(NotificationChannel("downloader", getString(R.string.channel_downloader), NotificationManager.IMPORTANCE_LOW).apply {
|
||||
description = getString(R.string.channel_downloader_description)
|
||||
enableLights(false)
|
||||
enableVibration(false)
|
||||
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
||||
})
|
||||
|
||||
manager.createNotificationChannel(NotificationChannel("update", getString(R.string.channel_update), NotificationManager.IMPORTANCE_HIGH).apply {
|
||||
description = getString(R.string.channel_update_description)
|
||||
enableLights(true)
|
||||
@@ -127,7 +167,6 @@ class Pupil : MultiDexApplication() {
|
||||
})
|
||||
}
|
||||
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
AppCompatDelegate.setDefaultNightMode(when (preference.getBoolean("dark_mode", false)) {
|
||||
true -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
false -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
|
||||
@@ -35,7 +35,7 @@ class PupilGlideModule : AppGlideModule() {
|
||||
registry.append(
|
||||
GlideUrl::class.java,
|
||||
InputStream::class.java,
|
||||
OkHttpUrlLoader.Factory(DownloadWorker.getInstance(context).client)
|
||||
OkHttpUrlLoader.Factory(client)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ import xyz.quaver.hitomi.getReader
|
||||
import xyz.quaver.pupil.BuildConfig
|
||||
import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.favorites
|
||||
import xyz.quaver.pupil.types.Tag
|
||||
import xyz.quaver.pupil.util.GalleryList
|
||||
import xyz.quaver.pupil.util.download.Cache
|
||||
@@ -68,8 +69,6 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
|
||||
PREV
|
||||
}
|
||||
|
||||
private lateinit var favorites: GalleryList
|
||||
|
||||
val timer = Timer()
|
||||
|
||||
var isThin = false
|
||||
@@ -257,9 +256,6 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
|
||||
}
|
||||
}
|
||||
|
||||
if (!::favorites.isInitialized)
|
||||
favorites = (context.applicationContext as Pupil).favorites
|
||||
|
||||
with(galleryblock_favorite) {
|
||||
setImageResource(if (favorites.contains(galleryBlock.id)) R.drawable.ic_star_filled else R.drawable.ic_star_empty)
|
||||
setOnClickListener {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.quaver.pupil
|
||||
package xyz.quaver.pupil.reciever
|
||||
|
||||
import android.app.DownloadManager
|
||||
import android.app.PendingIntent
|
||||
@@ -29,15 +29,10 @@ import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.preference.PreferenceManager
|
||||
import xyz.quaver.pupil.R
|
||||
import java.io.File
|
||||
|
||||
class BroadcastReciever : BroadcastReceiver() {
|
||||
|
||||
companion object {
|
||||
const val ACTION_CANCEL_IMPORT = "ACTION_CANCEL_IMPORT"
|
||||
|
||||
const val EXTRA_IMPORT_NOTIFICATION_ID = "EXTRA_IMPORT_NOTIFICATION_ID"
|
||||
}
|
||||
class UpdateBroadcastReciever : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
context ?: return
|
||||
155
app/src/main/java/xyz/quaver/pupil/services/DownloadService.kt
Normal file
155
app/src/main/java/xyz/quaver/pupil/services/DownloadService.kt
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.services
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.os.Binder
|
||||
import android.os.IBinder
|
||||
import android.util.SparseArray
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.ResponseBody
|
||||
import okio.*
|
||||
import xyz.quaver.pupil.R
|
||||
|
||||
private typealias ProgressListener = (Any?, Long, Long, Boolean) -> Unit
|
||||
|
||||
class Cache(context: Context) : ContextWrapper(context) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
class DownloadService : Service() {
|
||||
|
||||
//region Notification
|
||||
private val notificationManager by lazy {
|
||||
NotificationManagerCompat.from(this)
|
||||
}
|
||||
|
||||
private val serviceNotification by lazy {
|
||||
NotificationCompat.Builder(this, "downloader")
|
||||
.setContentTitle(getString(R.string.downloader_running))
|
||||
.setProgress(0, 0, false)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setOngoing(true)
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region ProgressListener
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val progressListener: ProgressListener = listener@{ tag, bytesRead, contentLength, done ->
|
||||
val (galleryID, index) = (tag as? Pair<Int, Int>) ?: return@listener
|
||||
|
||||
if (!done && progress[galleryID]?.get(index)?.isFinite() == true)
|
||||
progress[galleryID]?.set(index, bytesRead * 100F / contentLength)
|
||||
}
|
||||
|
||||
private class ProgressResponseBody(
|
||||
val tag: Any?,
|
||||
val responseBody: ResponseBody,
|
||||
val progressListener : ProgressListener
|
||||
) : ResponseBody() {
|
||||
private var bufferedSource : BufferedSource? = null
|
||||
|
||||
override fun contentLength() = responseBody.contentLength()
|
||||
override fun contentType() = responseBody.contentType()
|
||||
|
||||
override fun source(): BufferedSource {
|
||||
if (bufferedSource == null)
|
||||
bufferedSource = Okio.buffer(source(responseBody.source()))
|
||||
|
||||
return bufferedSource!!
|
||||
}
|
||||
|
||||
private fun source(source: Source) = object: ForwardingSource(source) {
|
||||
var totalBytesRead = 0L
|
||||
|
||||
override fun read(sink: Buffer, byteCount: Long): Long {
|
||||
val bytesRead = super.read(sink, byteCount)
|
||||
|
||||
totalBytesRead += if (bytesRead == -1L) 0L else bytesRead
|
||||
progressListener.invoke(tag, totalBytesRead, responseBody.contentLength(), bytesRead == -1L)
|
||||
|
||||
return bytesRead
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val interceptor = Interceptor { chain ->
|
||||
val request = chain.request()
|
||||
var response = chain.proceed(request)
|
||||
|
||||
var retry = 5
|
||||
while (!response.isSuccessful && retry > 0) {
|
||||
response = chain.proceed(request)
|
||||
retry--
|
||||
}
|
||||
|
||||
response.newBuilder()
|
||||
.body(response.body()?.let {
|
||||
ProgressResponseBody(request.tag(), it, progressListener)
|
||||
}).build()
|
||||
}
|
||||
//endregion
|
||||
|
||||
/**
|
||||
* KEY
|
||||
* primary galleryID
|
||||
* secondary index
|
||||
* PRIMARY VALUE
|
||||
* MutableList -> Download in progress
|
||||
* null -> Loading / Gallery doesn't exist
|
||||
* SECONDARY VALUE
|
||||
* 0 <= value < 100 -> Download in progress
|
||||
* Float.POSITIVE_INFINITY -> Download completed
|
||||
*/
|
||||
val progress = SparseArray<MutableList<Float>?>()
|
||||
|
||||
override fun onCreate() {
|
||||
startForeground(R.id.downloader_notification_id, serviceNotification.build())
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
inner class Binder : android.os.Binder() {
|
||||
val service = this@DownloadService
|
||||
}
|
||||
|
||||
private val binder = Binder()
|
||||
override fun onBind(p0: Intent?) = binder
|
||||
|
||||
fun load(galleryID: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun download(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,9 @@ import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.net.Proxy
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.*
|
||||
import android.text.style.AlignmentSpan
|
||||
@@ -59,9 +61,11 @@ import xyz.quaver.hitomi.GalleryBlock
|
||||
import xyz.quaver.hitomi.doSearch
|
||||
import xyz.quaver.hitomi.getGalleryIDsFromNozomi
|
||||
import xyz.quaver.hitomi.getSuggestionsForQuery
|
||||
import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
||||
import xyz.quaver.pupil.favorites
|
||||
import xyz.quaver.pupil.histories
|
||||
import xyz.quaver.pupil.services.DownloadService
|
||||
import xyz.quaver.pupil.types.TagSuggestion
|
||||
import xyz.quaver.pupil.types.Tags
|
||||
import xyz.quaver.pupil.ui.dialog.GalleryDialog
|
||||
@@ -110,9 +114,6 @@ class MainActivity : AppCompatActivity() {
|
||||
private var loadingJob: Job? = null
|
||||
private var currentPage = 0
|
||||
|
||||
private lateinit var histories: GalleryList
|
||||
private lateinit var favorites: GalleryList
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -147,11 +148,6 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
with(application as Pupil) {
|
||||
this@MainActivity.histories = histories
|
||||
this@MainActivity.favorites = favorites
|
||||
}
|
||||
|
||||
if (intent.action == Intent.ACTION_VIEW) {
|
||||
intent.dataString?.let { url ->
|
||||
restore(favorites, url,
|
||||
@@ -166,6 +162,15 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
Intent(this, DownloadService::class.java).let {
|
||||
when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ->
|
||||
startForegroundService(it)
|
||||
else ->
|
||||
startService(it)
|
||||
}
|
||||
}
|
||||
|
||||
checkUpdate(this)
|
||||
|
||||
initView()
|
||||
|
||||
@@ -46,6 +46,8 @@ import xyz.quaver.Code
|
||||
import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.adapters.ReaderAdapter
|
||||
import xyz.quaver.pupil.favorites
|
||||
import xyz.quaver.pupil.histories
|
||||
import xyz.quaver.pupil.util.GalleryList
|
||||
import xyz.quaver.pupil.util.download.Cache
|
||||
import xyz.quaver.pupil.util.download.DownloadWorker
|
||||
@@ -78,16 +80,12 @@ class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
private var menu: Menu? = null
|
||||
|
||||
private lateinit var favorites: GalleryList
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
title = getString(R.string.reader_loading)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(false)
|
||||
|
||||
favorites = (application as Pupil).favorites
|
||||
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE)
|
||||
@@ -96,7 +94,7 @@ class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
handleIntent(intent)
|
||||
|
||||
(application as Pupil).histories.add(galleryID)
|
||||
histories.add(galleryID)
|
||||
FirebaseCrashlytics.getInstance().setCustomKey("GalleryID", galleryID)
|
||||
|
||||
if (galleryID == 0) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import kotlinx.serialization.json.Json
|
||||
import net.rdrei.android.dirchooser.DirectoryChooserActivity
|
||||
import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.favorites
|
||||
import xyz.quaver.pupil.ui.fragment.LockSettingsFragment
|
||||
import xyz.quaver.pupil.ui.fragment.SettingsFragment
|
||||
import xyz.quaver.pupil.util.*
|
||||
@@ -100,7 +101,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
inputStream.readBytes().toString(Charset.defaultCharset())
|
||||
}
|
||||
|
||||
(application as Pupil).favorites.addAll(Json.decodeFromString<List<Int>>(str).also {
|
||||
favorites.addAll(Json.decodeFromString<List<Int>>(str).also {
|
||||
Snackbar.make(
|
||||
window.decorView,
|
||||
getString(R.string.settings_restore_success, it.size),
|
||||
|
||||
@@ -48,6 +48,7 @@ import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
||||
import xyz.quaver.pupil.adapters.ThumbnailPageAdapter
|
||||
import xyz.quaver.pupil.histories
|
||||
import xyz.quaver.pupil.types.Tag
|
||||
import xyz.quaver.pupil.ui.ReaderActivity
|
||||
import xyz.quaver.pupil.util.ItemClickSupport
|
||||
@@ -81,7 +82,7 @@ class GalleryDialog(context: Context, private val glide: RequestManager, private
|
||||
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
|
||||
putExtra("galleryID", galleryID)
|
||||
})
|
||||
(context.applicationContext as Pupil).histories.add(galleryID)
|
||||
histories.add(galleryID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +265,7 @@ class GalleryDialog(context: Context, private val glide: RequestManager, private
|
||||
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
|
||||
putExtra("galleryID", galleries[position].id)
|
||||
})
|
||||
(context.applicationContext as Pupil).histories.add(galleries[position].id)
|
||||
histories.add(galleries[position].id)
|
||||
}
|
||||
onItemLongClickListener = { _, position, _ ->
|
||||
GalleryDialog(
|
||||
|
||||
@@ -31,8 +31,10 @@ import androidx.preference.PreferenceManager
|
||||
import kotlinx.android.synthetic.main.dialog_proxy.view.*
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import xyz.quaver.proxy
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.client
|
||||
import xyz.quaver.pupil.clientBuilder
|
||||
import xyz.quaver.pupil.clientHolder
|
||||
import xyz.quaver.pupil.util.ProxyInfo
|
||||
import xyz.quaver.pupil.util.getProxyInfo
|
||||
import java.net.Proxy
|
||||
@@ -117,12 +119,15 @@ class ProxyDialog(context: Context) : Dialog(context) {
|
||||
}
|
||||
|
||||
ProxyInfo(type, addr, port, username, password).let {
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().putString("proxy",
|
||||
Json.encodeToString(it)
|
||||
).apply()
|
||||
|
||||
proxy = it.proxy()
|
||||
clientBuilder
|
||||
.proxy(it.proxy())
|
||||
.proxyAuthenticator(it.authenticator())
|
||||
clientHolder = null
|
||||
client
|
||||
}
|
||||
|
||||
dismiss()
|
||||
|
||||
@@ -30,6 +30,8 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import okhttp3.*
|
||||
import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.client
|
||||
import xyz.quaver.pupil.favorites
|
||||
import xyz.quaver.pupil.util.restore
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
@@ -52,7 +54,7 @@ class ManageFavoritesFragment : PreferenceFragmentCompat() {
|
||||
.build()
|
||||
).build()
|
||||
|
||||
OkHttpClient().newCall(request).enqueue(object: Callback {
|
||||
client.newCall(request).enqueue(object: Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
val view = view ?: return
|
||||
Snackbar.make(view, R.string.settings_backup_failed, Snackbar.LENGTH_LONG).show()
|
||||
@@ -79,7 +81,6 @@ class ManageFavoritesFragment : PreferenceFragmentCompat() {
|
||||
.setTitle(R.string.settings_restore_title)
|
||||
.setView(editText)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val favorites = (activity?.application as? Pupil)?.favorites ?: return@setPositiveButton
|
||||
restore(favorites, editText.text.toString(),
|
||||
onFailure = onFailure@{
|
||||
val view = view ?: return@onFailure
|
||||
|
||||
@@ -40,6 +40,7 @@ import net.rdrei.android.dirchooser.DirectoryChooserActivity
|
||||
import net.rdrei.android.dirchooser.DirectoryChooserConfig
|
||||
import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.histories
|
||||
import xyz.quaver.pupil.ui.LockActivity
|
||||
import xyz.quaver.pupil.ui.SettingsActivity
|
||||
import xyz.quaver.pupil.ui.dialog.DefaultQueryDialog
|
||||
@@ -143,8 +144,6 @@ class SettingsFragment :
|
||||
}.show()
|
||||
}
|
||||
"clear_history" -> {
|
||||
val histories = (requireContext().applicationContext as Pupil).histories
|
||||
|
||||
AlertDialog.Builder(requireContext()).apply {
|
||||
setTitle(R.string.warning)
|
||||
setMessage(R.string.settings_clear_history_alert_message)
|
||||
@@ -220,10 +219,10 @@ class SettingsFragment :
|
||||
|
||||
when (key) {
|
||||
"proxy" -> {
|
||||
summary = getProxyInfo(requireContext()).type.name
|
||||
summary = context?.let { getProxyInfo(it).type.name }
|
||||
}
|
||||
"dl_location" -> {
|
||||
summary = getDownloadDirectory(requireContext()).canonicalPath
|
||||
summary = context?.let { getDownloadDirectory(it).canonicalPath }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,7 +285,6 @@ class SettingsFragment :
|
||||
onPreferenceClickListener = this@SettingsFragment
|
||||
}
|
||||
"clear_history" -> {
|
||||
val histories = (requireActivity().application as Pupil).histories
|
||||
summary = getString(R.string.settings_clear_history_summary, histories.size)
|
||||
|
||||
onPreferenceClickListener = this@SettingsFragment
|
||||
|
||||
@@ -28,14 +28,13 @@ import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Dispatcher
|
||||
import xyz.quaver.Code
|
||||
import xyz.quaver.hitomi.GalleryBlock
|
||||
import xyz.quaver.hitomi.Reader
|
||||
import xyz.quaver.proxy
|
||||
import xyz.quaver.pupil.util.getCachedGallery
|
||||
import xyz.quaver.pupil.util.getDownloadDirectory
|
||||
import xyz.quaver.pupil.util.isParentOf
|
||||
import xyz.quaver.readBytes
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
@@ -91,11 +90,12 @@ class Cache(context: Context) : ContextWrapper(context) {
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
val thumbnail = if (metadata?.thumbnail == null)
|
||||
withContext(Dispatchers.IO) {
|
||||
val thumbnails = getGalleryBlock(galleryID)?.thumbnails
|
||||
val thumbnail = getGalleryBlock(galleryID)?.thumbnails?.firstOrNull() ?: return@withContext null
|
||||
try {
|
||||
Base64.encodeToString(URL(thumbnails?.firstOrNull()).openConnection(proxy).getInputStream().use {
|
||||
it.readBytes()
|
||||
}, Base64.DEFAULT)
|
||||
val data = URL(thumbnail).readBytes().apply {
|
||||
if (isEmpty()) return@withContext null
|
||||
}
|
||||
Base64.encodeToString(data, Base64.DEFAULT)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
@@ -40,13 +40,13 @@ import xyz.quaver.hitomi.imageUrlFromImage
|
||||
import xyz.quaver.hiyobi.cookie
|
||||
import xyz.quaver.hiyobi.createImgList
|
||||
import xyz.quaver.hiyobi.user_agent
|
||||
import xyz.quaver.proxy
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.client
|
||||
import xyz.quaver.pupil.interceptors
|
||||
import xyz.quaver.pupil.ui.ReaderActivity
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class DownloadWorker private constructor(context: Context) : ContextWrapper(context) {
|
||||
@@ -86,7 +86,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
}
|
||||
|
||||
private fun source(source: Source) = object: ForwardingSource(source) {
|
||||
|
||||
var totalBytesRead = 0L
|
||||
|
||||
override fun read(sink: Buffer, byteCount: Long): Long {
|
||||
@@ -100,6 +99,24 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
interceptors[Pair::class] = { chain ->
|
||||
val request = chain.request()
|
||||
var response = chain.proceed(request)
|
||||
|
||||
var retry = 5
|
||||
while (!response.isSuccessful && retry > 0) {
|
||||
response = chain.proceed(request)
|
||||
retry--
|
||||
}
|
||||
|
||||
response.newBuilder()
|
||||
.body(response.body()?.let {
|
||||
ProgressResponseBody(request.tag(), it, progressListener)
|
||||
}).build()
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Singleton
|
||||
@@ -135,34 +152,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
private val loop = loop()
|
||||
private val worker = SparseArray<Job?>()
|
||||
|
||||
val interceptor = Interceptor { chain ->
|
||||
val request = chain.request()
|
||||
var response = chain.proceed(request)
|
||||
|
||||
var retry = 5
|
||||
while (!response.isSuccessful && retry > 0) {
|
||||
response = chain.proceed(request)
|
||||
retry--
|
||||
}
|
||||
|
||||
response.newBuilder()
|
||||
.body(response.body()?.let {
|
||||
ProgressResponseBody(request.tag(), it, progressListener)
|
||||
}).build()
|
||||
}
|
||||
|
||||
val client : OkHttpClient =
|
||||
OkHttpClient.Builder()
|
||||
.connectTimeout(0, TimeUnit.SECONDS)
|
||||
.addInterceptor(interceptor)
|
||||
.readTimeout(0, TimeUnit.SECONDS)
|
||||
.dispatcher(Dispatcher().apply {
|
||||
maxRequests = 4
|
||||
maxRequestsPerHost = 4
|
||||
})
|
||||
.proxy(proxy)
|
||||
.build()
|
||||
|
||||
fun stop() {
|
||||
queue.clear()
|
||||
|
||||
@@ -300,8 +289,8 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
val ext = call.request().url().encodedPath().split('.').last()
|
||||
|
||||
try {
|
||||
response.body().use {
|
||||
Cache(this@DownloadWorker).putImage(galleryID, i, ext, it!!.byteStream())
|
||||
response.body()!!.use {
|
||||
Cache(this@DownloadWorker).putImage(galleryID, i, ext, it.byteStream())
|
||||
}
|
||||
progress[galleryID]?.set(i, Float.POSITIVE_INFINITY)
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ fun byteToString(byte: Long, precision : Int = 1) : String {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Convert android generated ID to requestCode
|
||||
* to prevent java.lang.IllegalArgumentException: Can only use lower 16 bits for requestCode
|
||||
*
|
||||
|
||||
@@ -23,8 +23,7 @@ import androidx.preference.PreferenceManager
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Authenticator
|
||||
import okhttp3.Credentials
|
||||
import okhttp3.*
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import okhttp3.*
|
||||
import ru.noties.markwon.Markwon
|
||||
import xyz.quaver.pupil.BuildConfig
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.client
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
@@ -187,7 +188,7 @@ fun restore(favorites: GalleryList, url: String, onFailure: ((Exception) -> Unit
|
||||
.get()
|
||||
.build()
|
||||
|
||||
OkHttpClient().newCall(request).enqueue(object: Callback {
|
||||
client.newCall(request).enqueue(object: Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
onFailure?.invoke(e)
|
||||
}
|
||||
|
||||
@@ -140,4 +140,7 @@
|
||||
<string name="settings_manage_favorites">ブックマーク管理</string>
|
||||
<string name="settings_backup_failed">エラーが発生しました</string>
|
||||
<string name="settings_backup_share">バックアップ共有</string>
|
||||
<string name="channel_downloader">ダウンローダ</string>
|
||||
<string name="channel_downloader_description">ダウンローダの状態を表示</string>
|
||||
<string name="downloader_running">ダウンローダー起動中</string>
|
||||
</resources>
|
||||
@@ -140,4 +140,7 @@
|
||||
<string name="settings_manage_favorites">즐겨찾기 관리</string>
|
||||
<string name="settings_backup_failed">업로드 실패</string>
|
||||
<string name="settings_backup_share">백업 공유</string>
|
||||
<string name="channel_downloader">다운로더</string>
|
||||
<string name="channel_downloader_description">다운로더 작동 여부 표시</string>
|
||||
<string name="downloader_running">다운로더 작동중…</string>
|
||||
</resources>
|
||||
@@ -10,4 +10,7 @@
|
||||
<item name="request_write_permission_and_saf" type="id" />
|
||||
|
||||
<item name="notification_id_update" type="id" />
|
||||
|
||||
<item name="downloader_notification_id" type="id" />
|
||||
<item name="downloader_notification_request" type="id" />
|
||||
</resources>
|
||||
@@ -29,6 +29,9 @@
|
||||
<string name="channel_download">Download</string>
|
||||
<string name="channel_download_description">Shows download status</string>
|
||||
|
||||
<string name="channel_downloader">Downloader</string>
|
||||
<string name="channel_downloader_description">Shows downloader status</string>
|
||||
|
||||
<string name="channel_update">Update</string>
|
||||
<string name="channel_update_description">Shows update progress</string>
|
||||
|
||||
@@ -103,6 +106,9 @@
|
||||
<string name="reader_notification_text">Downloading…</string>
|
||||
<string name="reader_notification_complete">Download complete</string>
|
||||
|
||||
<!-- DOWNLOADER -->
|
||||
<string name="downloader_running">Downloader running…</string>
|
||||
|
||||
<!-- SETTINGS -->
|
||||
|
||||
<string name="settings_title">Settings</string>
|
||||
|
||||
Reference in New Issue
Block a user