Compare commits

..

12 Commits

Author SHA1 Message Date
tom5079
439a8e93ec App built 2020-09-09 11:25:53 +09:00
tom5079
83801feee9 Bug fix 2020-09-09 11:23:22 +09:00
tom5079
8a6860c96e Import cleanup 2020-09-09 09:29:33 +09:00
tom5079
5c959f2987 Bug fix 2020-09-09 09:29:33 +09:00
tom5079
4e4397287a Implemented fast scroll 2020-09-09 09:22:17 +09:00
tom5079
fe02abc9e8 Bug fix 2020-09-08 20:04:12 +09:00
tom5079
59347ab317 Bug fix 2020-09-08 19:16:15 +09:00
tom5079
f408a91176 Bug fix 2020-09-07 10:00:10 +09:00
tom5079
6f6956ce27 Fixed DownloadLocationDialogFragment keep showing up when any button is clicked 2020-09-06 17:19:18 +09:00
tom5079
4ecad8eccc Fixed migration 2020-09-05 18:31:53 +09:00
tom5079
486fbe46a0 Fixed migration 2020-09-05 18:11:20 +09:00
tom5079
1ddb636dd0 Fixed migration 2020-09-05 18:00:15 +09:00
30 changed files with 186 additions and 134 deletions

View File

@@ -56,5 +56,10 @@
<option name="name" value="MavenLocal" /> <option name="name" value="MavenLocal" />
<option name="url" value="file:/$USER_HOME$/.m2/repository/" /> <option name="url" value="file:/$USER_HOME$/.m2/repository/" />
</remote-repository> </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> </component>
</project> </project>

View File

@@ -14,13 +14,13 @@ if (file("google-services.json").exists() && file("src/debug/google-services.jso
} }
android { android {
compileSdkVersion 29 compileSdkVersion 30
defaultConfig { defaultConfig {
applicationId "xyz.quaver.pupil" applicationId "xyz.quaver.pupil"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 30
versionCode 57 versionCode 57
versionName "5.0-beta2" versionName "5.0-beta7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
} }
@@ -63,7 +63,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0-RC-HOTFIX1" //implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0-RC"
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.preference:preference:1.1.1' implementation 'androidx.preference:preference:1.1.1'
@@ -78,6 +78,7 @@ dependencies {
implementation 'com.google.firebase:firebase-perf:19.0.8' implementation 'com.google.firebase:firebase-perf:19.0.8'
implementation 'com.github.arimorty:floatingsearchview:2.1.1' implementation 'com.github.arimorty:floatingsearchview:2.1.1'
implementation 'com.github.clans:fab:1.6.4' implementation 'com.github.clans:fab:1.6.4'
//implementation 'com.quiph.ui:recyclerviewfastscroller:0.2.1'
//noinspection GradleDependency //noinspection GradleDependency
implementation 'com.squareup.okhttp3:okhttp:3.12.12' implementation 'com.squareup.okhttp3:okhttp:3.12.12'
implementation 'com.github.bumptech.glide:glide:4.11.0' implementation 'com.github.bumptech.glide:glide:4.11.0'
@@ -96,10 +97,10 @@ dependencies {
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0' 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.3") { implementation ("xyz.quaver:libpupil:1.5") {
exclude group: 'org.jetbrains.kotlinx', module: 'kotlinx-serialization-core-jvm' exclude group: 'org.jetbrains.kotlinx', module: 'kotlinx-serialization-core-jvm'
} }
implementation "xyz.quaver:documentfilex:0.2.14-alpha2" implementation "xyz.quaver:documentfilex:0.2.15"
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test:rules:1.3.0' androidTestImplementation 'androidx.test:rules:1.3.0'

Binary file not shown.

Binary file not shown.

View File

@@ -12,7 +12,7 @@
"filters": [], "filters": [],
"properties": [], "properties": [],
"versionCode": 57, "versionCode": 57,
"versionName": "5.0-beta2", "versionName": "5.0-beta7",
"enabled": true, "enabled": true,
"outputFile": "app-release.apk" "outputFile": "app-release.apk"
} }

View File

@@ -97,7 +97,7 @@ class Pupil : Application() {
try { try {
Preferences.get<String>("download_folder").also { Preferences.get<String>("download_folder").also {
if (Build.VERSION.SDK_INT > 19) if (it.startsWith("content") && Build.VERSION.SDK_INT > 19)
contentResolver.takePersistableUriPermission( contentResolver.takePersistableUriPermission(
Uri.parse(it), Uri.parse(it),
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION

View File

@@ -19,9 +19,6 @@
package xyz.quaver.pupil.adapters package xyz.quaver.pupil.adapters
import android.content.Context import android.content.Context
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.util.SparseBooleanArray import android.util.SparseBooleanArray
import android.view.LayoutInflater import android.view.LayoutInflater
@@ -29,23 +26,26 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.CircularProgressDrawable import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.Animatable2Compat
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.bumptech.glide.RequestManager import com.bumptech.glide.RequestManager
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.daimajia.swipe.SwipeLayout import com.daimajia.swipe.SwipeLayout
import com.daimajia.swipe.adapters.RecyclerSwipeAdapter import com.daimajia.swipe.adapters.RecyclerSwipeAdapter
import com.daimajia.swipe.interfaces.SwipeAdapterInterface import com.daimajia.swipe.interfaces.SwipeAdapterInterface
import com.google.android.material.chip.Chip
import kotlinx.android.synthetic.main.item_galleryblock.view.* import kotlinx.android.synthetic.main.item_galleryblock.view.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import xyz.quaver.hitomi.getReader import xyz.quaver.hitomi.getReader
import xyz.quaver.io.util.getChild
import xyz.quaver.pupil.BuildConfig import xyz.quaver.pupil.BuildConfig
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.favorites import xyz.quaver.pupil.favorites
@@ -88,11 +88,10 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
val imageList = cache.metadata.imageList!! val imageList = cache.metadata.imageList!!
progress = imageList.filterNotNull().size progress = imageList.filterNotNull().size
max = imageList.size
if (visibility == View.GONE) { if (visibility == View.GONE)
visibility = View.VISIBLE visibility = View.VISIBLE
max = imageList.size
}
if (progress == max) { if (progress == max) {
val downloadManager = DownloadManager.getInstance(context) val downloadManager = DownloadManager.getInstance(context)
@@ -158,6 +157,28 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
.skipMemoryCache(true) .skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
.error(R.drawable.image_broken_variant) .error(R.drawable.image_broken_variant)
.listener(object: RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
Cache.getInstance(context, galleryID).let {
it.cacheFolder.getChild(".thumbnail").let { if (it.exists()) it.delete() }
it.downloadFolder?.getChild(".thumbnail")?.let { if (it.exists()) it.delete() }
}
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean = false
})
.apply { .apply {
if (BuildConfig.CENSOR) if (BuildConfig.CENSOR)
override(5, 8) override(5, 8)

View File

@@ -40,9 +40,7 @@ import xyz.quaver.Code
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.getReferer import xyz.quaver.hitomi.getReferer
import xyz.quaver.hitomi.imageUrlFromImage import xyz.quaver.hitomi.imageUrlFromImage
import xyz.quaver.hiyobi.cookie
import xyz.quaver.hiyobi.createImgList import xyz.quaver.hiyobi.createImgList
import xyz.quaver.hiyobi.user_agent
import xyz.quaver.io.util.readBytes import xyz.quaver.io.util.readBytes
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.services.DownloadService import xyz.quaver.pupil.services.DownloadService
@@ -116,10 +114,7 @@ class ReaderAdapter(private val activity: ReaderActivity,
) )
, LazyHeaders.Builder().addHeader("Referer", getReferer(galleryID)).build()) , LazyHeaders.Builder().addHeader("Referer", getReferer(galleryID)).build())
Code.HIYOBI -> Code.HIYOBI ->
GlideUrl(createImgList(galleryID, reader!!, lowQuality)[position].path, LazyHeaders.Builder() GlideUrl(createImgList(galleryID, reader!!, lowQuality)[position].path)
.addHeader("User-Agent", user_agent)
.addHeader("Cookie", cookie)
.build())
else -> null else -> null
} }
holder.view.image.post { holder.view.image.post {

View File

@@ -307,13 +307,18 @@ class DownloadService : Service() {
return@launch return@launch
} }
if (progress.indexOfKey(galleryID) < 0) progress.put(galleryID, MutableList(reader.galleryInfo.files.size) { 0F })
progress.put(galleryID, MutableList(reader.galleryInfo.files.size) { 0F })
cache.metadata.imageList?.forEachIndexed { index, image -> cache.metadata.imageList?.forEachIndexed { index, image ->
progress[galleryID]?.set(index, if (image != null) Float.POSITIVE_INFINITY else 0F) progress[galleryID]?.set(index, if (image != null) Float.POSITIVE_INFINITY else 0F)
} }
if (isCompleted(galleryID)) {
notificationManager.cancel(galleryID)
startId?.let { stopSelf(it) }
return@launch
}
notification[galleryID]?.setContentTitle(reader.galleryInfo.title?.ellipsize(30)) notification[galleryID]?.setContentTitle(reader.galleryInfo.title?.ellipsize(30))
notify(galleryID) notify(galleryID)
@@ -328,9 +333,11 @@ class DownloadService : Service() {
} }
} }
reader.requestBuilders.filterIndexed { index, _ -> progress[galleryID]?.get(index)?.isInfinite() != true }.forEachIndexed { index, it -> reader.requestBuilders.forEachIndexed { index, it ->
val request = it.tag(Tag(galleryID, index, startId)).build() if (progress[galleryID]?.get(index)?.isInfinite() != true) {
client.newCall(request).enqueue(callback) val request = it.tag(Tag(galleryID, index, startId)).build()
client.newCall(request).enqueue(callback)
}
} }
queued.forEach { download(it) } queued.forEach { download(it) }

View File

@@ -19,7 +19,6 @@
package xyz.quaver.pupil.types package xyz.quaver.pupil.types
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
@Serializable @Serializable
data class Tag(val area: String?, val tag: String, val isNegative: Boolean = false) { data class Tag(val area: String?, val tag: String, val isNegative: Boolean = false) {

View File

@@ -981,7 +981,7 @@ class MainActivity : AppCompatActivity() {
if (query.isNotEmpty() && mode != Mode.SEARCH) { if (query.isNotEmpty() && mode != Mode.SEARCH) {
Snackbar.make(this@MainActivity.main_recyclerview, R.string.search_all, Snackbar.LENGTH_SHORT).apply { Snackbar.make(this@MainActivity.main_recyclerview, R.string.search_all, Snackbar.LENGTH_SHORT).apply {
setAction(android.R.string.yes) { setAction(android.R.string.ok) {
cancelFetch() cancelFetch()
clearGalleries() clearGalleries()
currentPage = 0 currentPage = 0
@@ -1034,7 +1034,7 @@ class MainActivity : AppCompatActivity() {
val downloads = DownloadManager.getInstance(this@MainActivity).downloadFolderMap.keys.toList() val downloads = DownloadManager.getInstance(this@MainActivity).downloadFolderMap.keys.toList()
when { when {
query.isEmpty() -> downloads.also { query.isEmpty() -> downloads.reversed().also {
totalItems = it.size totalItems = it.size
} }
else -> { else -> {

View File

@@ -196,8 +196,8 @@ class ReaderActivity : AppCompatActivity() {
return true return true
} }
override fun onOptionsItemSelected(item: MenuItem?): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item?.itemId) { when(item.itemId) {
R.id.reader_menu_page_indicator -> { R.id.reader_menu_page_indicator -> {
val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, reader_layout, false) val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, reader_layout, false)
with(view.dialog_number_picker) { with(view.dialog_number_picker) {

View File

@@ -22,23 +22,19 @@ import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.WindowManager import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.settings_activity.*
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import net.rdrei.android.dirchooser.DirectoryChooserActivity
import xyz.quaver.io.FileX
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.favorites import xyz.quaver.pupil.favorites
import xyz.quaver.pupil.ui.fragment.LockSettingsFragment import xyz.quaver.pupil.ui.fragment.LockSettingsFragment
import xyz.quaver.pupil.ui.fragment.SettingsFragment import xyz.quaver.pupil.ui.fragment.SettingsFragment
import xyz.quaver.pupil.util.* import xyz.quaver.pupil.util.Preferences
import java.io.File import xyz.quaver.pupil.util.normalizeID
import java.nio.charset.Charset import java.nio.charset.Charset
class SettingsActivity : AppCompatActivity() { class SettingsActivity : AppCompatActivity() {
@@ -68,8 +64,8 @@ class SettingsActivity : AppCompatActivity() {
super.onResume() super.onResume()
} }
override fun onOptionsItemSelected(item: MenuItem?): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item?.itemId) { when (item.itemId) {
android.R.id.home -> onBackPressed() android.R.id.home -> onBackPressed()
} }

View File

@@ -122,6 +122,9 @@ class DownloadLocationDialogFragment : DialogFragment() {
.setTitle(R.string.settings_download_folder) .setTitle(R.string.settings_download_folder)
.setView(build()) .setView(build())
.setPositiveButton(requireContext().getText(android.R.string.ok)) { _, _ -> .setPositiveButton(requireContext().getText(android.R.string.ok)) { _, _ ->
if (Preferences["download_folder", ""].isEmpty())
Preferences["download_folder"] = context?.getExternalFilesDir(null)?.canonicalPath ?: ""
DownloadManager.getInstance(requireContext()).migrate() DownloadManager.getInstance(requireContext()).migrate()
} }

View File

@@ -82,7 +82,7 @@ class MirrorDialog(context: Context) : AlertDialog(context) {
} }
onItemMoved = { onItemMoved = {
Preferences["mirrors", it.joinToString(">")] Preferences["mirrors"] = it.joinToString(">")
} }
} }
} }

View File

@@ -74,11 +74,11 @@ class LockSettingsFragment : PreferenceFragmentCompat() {
setTitle(R.string.warning) setTitle(R.string.warning)
setMessage(R.string.settings_lock_remove_message) setMessage(R.string.settings_lock_remove_message)
setPositiveButton(android.R.string.yes) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
lockManager.remove(Lock.Type.PATTERN) lockManager.remove(Lock.Type.PATTERN)
onResume() onResume()
} }
setNegativeButton(android.R.string.no) { _, _ -> } setNegativeButton(android.R.string.cancel) { _, _ -> }
}.show() }.show()
} else { } else {
val intent = Intent(requireContext(), LockActivity::class.java).apply { val intent = Intent(requireContext(), LockActivity::class.java).apply {
@@ -107,11 +107,11 @@ class LockSettingsFragment : PreferenceFragmentCompat() {
setTitle(R.string.warning) setTitle(R.string.warning)
setMessage(R.string.settings_lock_remove_message) setMessage(R.string.settings_lock_remove_message)
setPositiveButton(android.R.string.yes) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
lockManager.remove(Lock.Type.PIN) lockManager.remove(Lock.Type.PIN)
onResume() onResume()
} }
setNegativeButton(android.R.string.no) { _, _ -> } setNegativeButton(android.R.string.cancel) { _, _ -> }
}.show() }.show()
} else { } else {
val intent = Intent(requireContext(), LockActivity::class.java).apply { val intent = Intent(requireContext(), LockActivity::class.java).apply {

View File

@@ -57,7 +57,7 @@ class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenc
AlertDialog.Builder(context).apply { AlertDialog.Builder(context).apply {
setTitle(R.string.warning) setTitle(R.string.warning)
setMessage(R.string.settings_clear_cache_alert_message) setMessage(R.string.settings_clear_cache_alert_message)
setPositiveButton(android.R.string.yes) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
if (dir.exists()) if (dir.exists())
dir.deleteRecursively() dir.deleteRecursively()
@@ -74,7 +74,7 @@ class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenc
} }
} }
} }
setNegativeButton(android.R.string.no) { _, _ -> } setNegativeButton(android.R.string.cancel) { _, _ -> }
}.show() }.show()
} }
"delete_downloads" -> { "delete_downloads" -> {
@@ -83,7 +83,7 @@ class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenc
AlertDialog.Builder(context).apply { AlertDialog.Builder(context).apply {
setTitle(R.string.warning) setTitle(R.string.warning)
setMessage(R.string.settings_clear_downloads_alert_message) setMessage(R.string.settings_clear_downloads_alert_message)
setPositiveButton(android.R.string.yes) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
job?.cancel() job?.cancel()
launch(Dispatchers.Main) { launch(Dispatchers.Main) {
@@ -109,18 +109,18 @@ class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenc
} }
} }
} }
setNegativeButton(android.R.string.no) { _, _ -> } setNegativeButton(android.R.string.cancel) { _, _ -> }
}.show() }.show()
} }
"clear_history" -> { "clear_history" -> {
AlertDialog.Builder(context).apply { AlertDialog.Builder(context).apply {
setTitle(R.string.warning) setTitle(R.string.warning)
setMessage(R.string.settings_clear_history_alert_message) setMessage(R.string.settings_clear_history_alert_message)
setPositiveButton(android.R.string.yes) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
histories.clear() histories.clear()
summary = context.getString(R.string.settings_clear_history_summary, histories.size) summary = context.getString(R.string.settings_clear_history_summary, histories.size)
} }
setNegativeButton(android.R.string.no) { _, _ -> } setNegativeButton(android.R.string.cancel) { _, _ -> }
}.show() }.show()
} }
else -> return false else -> return false

View File

@@ -36,15 +36,17 @@ class GalleryList(private val file: File, private val list: MutableSet<Int> = mu
fun load() { fun load() {
synchronized(this) { synchronized(this) {
list.clear() list.clear()
list.addAll( kotlin.runCatching {
Json.decodeFromString<List<Int>>(file.bufferedReader().use { it.readText() }) Json.decodeFromString<List<Int>>(file.bufferedReader().use { it.readText() })
) }.onSuccess {
list.addAll(it)
}
} }
} }
fun save() { fun save() {
synchronized(this) { synchronized(this) {
file.writeText(Json.encodeToString(list)) file.writeText(Json.encodeToString(list.toList()))
} }
} }

View File

@@ -41,6 +41,7 @@ import java.io.FileOutputStream
import java.io.InputStream import java.io.InputStream
import java.net.URL import java.net.URL
@Suppress("DEPRECATION")
@Deprecated("Use downloader.Cache instead") @Deprecated("Use downloader.Cache instead")
class Cache(context: Context) : ContextWrapper(context) { class Cache(context: Context) : ContextWrapper(context) {

View File

@@ -37,9 +37,7 @@ import xyz.quaver.Code
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.getReferer import xyz.quaver.hitomi.getReferer
import xyz.quaver.hitomi.imageUrlFromImage import xyz.quaver.hitomi.imageUrlFromImage
import xyz.quaver.hiyobi.cookie
import xyz.quaver.hiyobi.createImgList import xyz.quaver.hiyobi.createImgList
import xyz.quaver.hiyobi.user_agent
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.client import xyz.quaver.pupil.client
import xyz.quaver.pupil.interceptors import xyz.quaver.pupil.interceptors
@@ -48,6 +46,7 @@ import java.io.File
import java.io.IOException import java.io.IOException
import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.LinkedBlockingQueue
@Suppress("DEPRECATION")
@Deprecated("Use DownloadService instead") @Deprecated("Use DownloadService instead")
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
class DownloadWorker private constructor(context: Context) : ContextWrapper(context) { class DownloadWorker private constructor(context: Context) : ContextWrapper(context) {
@@ -219,8 +218,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
} }
Code.HIYOBI -> { Code.HIYOBI -> {
url(createImgList(galleryID, reader, lowQuality)[index].path) url(createImgList(galleryID, reader, lowQuality)[index].path)
addHeader("User-Agent", user_agent)
addHeader("Cookie", cookie)
} }
else -> { else -> {
//shouldn't be called anyway //shouldn't be called anyway

View File

@@ -22,6 +22,7 @@ import kotlinx.serialization.Serializable
import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
@Suppress("DEPRECATION")
@Deprecated("Use downloader.Cache.Metadata instead") @Deprecated("Use downloader.Cache.Metadata instead")
@Serializable @Serializable
data class Metadata( data class Metadata(

View File

@@ -34,10 +34,7 @@ import xyz.quaver.Code
import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.io.FileX import xyz.quaver.io.FileX
import xyz.quaver.io.util.getChild import xyz.quaver.io.util.*
import xyz.quaver.io.util.readBytes
import xyz.quaver.io.util.readText
import xyz.quaver.io.util.writeBytes
import xyz.quaver.pupil.client import xyz.quaver.pupil.client
import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.Preferences
@@ -60,6 +57,7 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
instances[galleryID] ?: Cache(context, galleryID).also { instances.put(galleryID, it) } instances[galleryID] ?: Cache(context, galleryID).also { instances.put(galleryID, it) }
} }
@Synchronized
fun delete(galleryID: Int) { fun delete(galleryID: Int) {
instances[galleryID]?.cacheFolder?.deleteRecursively() instances[galleryID]?.cacheFolder?.deleteRecursively()
instances.delete(galleryID) instances.delete(galleryID)
@@ -86,11 +84,11 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
} }
fun findFile(fileName: String): FileX? = fun findFile(fileName: String): FileX? =
cacheFolder.getChild(fileName).let { downloadFolder?.let { downloadFolder -> downloadFolder.getChild(fileName).let {
if (it.exists()) it else null if (it.exists()) it else null
} ?: downloadFolder?.let { downloadFolder -> downloadFolder.getChild(fileName).let { } } ?: cacheFolder.getChild(fileName).let {
if (it.exists()) it else null if (it.exists()) it else null
} } }
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
fun setMetadata(change: (Metadata) -> Unit) { fun setMetadata(change: (Metadata) -> Unit) {

View File

@@ -124,7 +124,7 @@ class DownloadManager private constructor(context: Context) : ContextWrapper(con
downloadFolderMap[galleryID]?.let { downloadFolderMap[galleryID]?.let {
kotlin.runCatching { kotlin.runCatching {
downloadFolder.getChild(it).delete() downloadFolder.getChild(it).deleteRecursively()
downloadFolderMap.remove(galleryID) downloadFolderMap.remove(galleryID)
downloadFolder.getChild(".download").let { if (!it.exists()) it.createNewFile() } downloadFolder.getChild(".download").let { if (!it.exists()) it.createNewFile() }

View File

@@ -27,6 +27,7 @@ import java.io.FileOutputStream
import java.lang.reflect.Array import java.lang.reflect.Array
import java.net.URL import java.net.URL
@Suppress("DEPRECATION")
@Deprecated("Use downloader.Cache instead") @Deprecated("Use downloader.Cache instead")
fun getCachedGallery(context: Context, galleryID: Int) = fun getCachedGallery(context: Context, galleryID: Int) =
File(getDownloadDirectory(context), galleryID.toString()).let { File(getDownloadDirectory(context), galleryID.toString()).let {
@@ -36,6 +37,7 @@ fun getCachedGallery(context: Context, galleryID: Int) =
File(context.cacheDir, "imageCache/$galleryID") File(context.cacheDir, "imageCache/$galleryID")
} }
@Suppress("DEPRECATION")
@Deprecated("Use downloader.Cache instead") @Deprecated("Use downloader.Cache instead")
fun getDownloadDirectory(context: Context) = fun getDownloadDirectory(context: Context) =
Preferences.get<String>("dl_location").let { Preferences.get<String>("dl_location").let {
@@ -45,6 +47,7 @@ fun getDownloadDirectory(context: Context) =
context.getExternalFilesDir(null)!! context.getExternalFilesDir(null)!!
} }
@Suppress("DEPRECATION")
@Deprecated("Use FileX instead") @Deprecated("Use FileX instead")
fun File.isParentOf(another: File) = fun File.isParentOf(another: File) =
another.absolutePath.startsWith(this.absolutePath) another.absolutePath.startsWith(this.absolutePath)

View File

@@ -22,11 +22,6 @@ import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import xyz.quaver.Code import xyz.quaver.Code
@@ -34,11 +29,7 @@ import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.getReferer import xyz.quaver.hitomi.getReferer
import xyz.quaver.hitomi.imageUrlFromImage import xyz.quaver.hitomi.imageUrlFromImage
import xyz.quaver.hiyobi.cookie
import xyz.quaver.hiyobi.createImgList import xyz.quaver.hiyobi.createImgList
import xyz.quaver.hiyobi.user_agent
import xyz.quaver.pupil.util.downloader.Cache
import xyz.quaver.pupil.util.downloader.Metadata
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@@ -134,8 +125,6 @@ val Reader.requestBuilders: List<Request.Builder>
createImgList(galleryID, this, lowQuality).map { createImgList(galleryID, this, lowQuality).map {
Request.Builder() Request.Builder()
.url(it.path) .url(it.path)
.header("User-Agent", user_agent)
.header("Cookie", cookie)
} }
} }
} }

View File

@@ -24,6 +24,7 @@ import android.app.PendingIntent
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.net.Uri import android.net.Uri
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
@@ -32,11 +33,13 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
import okhttp3.Call import okhttp3.Call
import okhttp3.Callback import okhttp3.Callback
@@ -45,6 +48,8 @@ import okhttp3.Response
import ru.noties.markwon.Markwon import ru.noties.markwon.Markwon
import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.getGalleryBlock
import xyz.quaver.hitomi.getReader
import xyz.quaver.io.FileX import xyz.quaver.io.FileX
import xyz.quaver.io.util.getChild import xyz.quaver.io.util.getChild
import xyz.quaver.io.util.* import xyz.quaver.io.util.*
@@ -53,6 +58,7 @@ import xyz.quaver.pupil.R
import xyz.quaver.pupil.client import xyz.quaver.pupil.client
import xyz.quaver.pupil.services.DownloadService import xyz.quaver.pupil.services.DownloadService
import xyz.quaver.pupil.util.downloader.Cache import xyz.quaver.pupil.util.downloader.Cache
import xyz.quaver.pupil.util.downloader.Metadata
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
@@ -152,7 +158,7 @@ fun checkUpdate(context: Context, force: Boolean = false) {
setTitle(R.string.update_title) setTitle(R.string.update_title)
val msg = extractReleaseNote(update, Locale.getDefault()) val msg = extractReleaseNote(update, Locale.getDefault())
setMessage(Markwon.create(context).toMarkdown(msg)) setMessage(Markwon.create(context).toMarkdown(msg))
setPositiveButton(android.R.string.yes) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
@@ -175,7 +181,7 @@ fun checkUpdate(context: Context, force: Boolean = false) {
Preferences["update_download_id"] = it Preferences["update_download_id"] = it
} }
} }
setNegativeButton(if (force) android.R.string.no else R.string.ignore_update) { _, _ -> setNegativeButton(if (force) android.R.string.cancel else R.string.ignore_update) { _, _ ->
if (!force) if (!force)
preferences.edit() preferences.edit()
.putLong("ignore_update_until", System.currentTimeMillis() + 604800000) .putLong("ignore_update_until", System.currentTimeMillis() + 604800000)
@@ -226,12 +232,15 @@ private val receiver = object: BroadcastReceiver() {
ACTION_CANCEL -> { ACTION_CANCEL -> {
job?.cancel() job?.cancel()
NotificationManagerCompat.from(context).cancel(R.id.notification_id_import) NotificationManagerCompat.from(context).cancel(R.id.notification_id_import)
context.unregisterReceiver(this)
} }
} }
} }
} }
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() { fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
registerReceiver(receiver, IntentFilter().apply { addAction(receiver.ACTION_CANCEL) })
val notificationManager = NotificationManagerCompat.from(this) val notificationManager = NotificationManagerCompat.from(this)
val action = NotificationCompat.Action.Builder(0, getText(android.R.string.cancel), val action = NotificationCompat.Action.Builder(0, getText(android.R.string.cancel),
PendingIntent.getBroadcast(this, R.id.notification_import_cancel_action.normalizeID(), Intent(receiver.ACTION_CANCEL), PendingIntent.FLAG_UPDATE_CURRENT) PendingIntent.getBroadcast(this, R.id.notification_import_cancel_action.normalizeID(), Intent(receiver.ACTION_CANCEL), PendingIntent.FLAG_UPDATE_CURRENT)
@@ -247,55 +256,65 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
job?.cancel() job?.cancel()
job = CoroutineScope(Dispatchers.IO).launch { job = CoroutineScope(Dispatchers.IO).launch {
val folders = downloadFolder.listFiles { folder -> val downloadFolders = downloadFolder.listFiles { folder ->
(folder as? FileX)?.isDirectory == true && !downloadFolderMap.values.contains(folder.name) (folder as? FileX)?.isDirectory == true && !downloadFolderMap.values.contains(folder.name)
} }
if (folders.isNullOrEmpty()) return@launch
folders.forEachIndexed { index, folder -> if (downloadFolders.isNullOrEmpty()) return@launch
downloadFolders.forEachIndexed { index, folder ->
notification notification
.setContentText(getString(R.string.import_old_galleries_notification_text, index, folders.size)) .setContentText(getString(R.string.import_old_galleries_notification_text, index, downloadFolders.size))
.setProgress(index, folders.size, false) .setProgress(index, downloadFolders.size, false)
notificationManager.notify(R.id.notification_id_import, notification.build()) notificationManager.notify(R.id.notification_id_import, notification.build())
kotlin.runCatching { kotlin.runCatching {
val folder = (folder as? FileX) ?: return@runCatching if (folder !is FileX) return@runCatching
val metadata = folder.getChild(".metadata").readText()?.let { Json.parseToJsonElement(it).jsonObject } ?: return@runCatching val metadata = kotlin.runCatching {
folder.getChild(".metadata").readText()?.let { Json.parseToJsonElement(it).jsonObject }
}.getOrNull()
val galleryBlock: GalleryBlock? = val galleryID = folder.name.toIntOrNull() ?: return@runCatching
metadata["galleryBlock"]?.let { Json.decodeFromJsonElement<GalleryBlock>(it) }
val reader: Reader? =
metadata["reader"]?.let { Json.decodeFromJsonElement<Reader>(it) }
val galleryID = galleryBlock?.id ?: reader?.galleryInfo?.id ?: folder.name.toIntOrNull() ?: return@runCatching val galleryBlock: GalleryBlock? = kotlin.runCatching {
metadata?.get("galleryBlock")?.let { Json.decodeFromJsonElement<GalleryBlock>(it) }
}.getOrNull() ?: getGalleryBlock(galleryID)
val reader: Reader? = kotlin.runCatching {
metadata?.get("reader")?.let { Json.decodeFromJsonElement<Reader>(it) }
}.getOrNull() ?: getReader(galleryID)
metadata["thumbnail"]?.jsonPrimitive?.contentOrNull.let { thumbnail -> metadata?.get("thumbnail")?.jsonPrimitive?.contentOrNull?.also { thumbnail ->
val file = folder.getChild(".thumbnail").also { val file = folder.getChild(".thumbnail").also {
if (!it.exists()) if (it.exists())
it.createNewFile() it.delete()
it.createNewFile()
} }
file.writeBytes(Base64.decode(thumbnail, Base64.DEFAULT)) file.writeBytes(Base64.decode(thumbnail, Base64.DEFAULT))
} }
downloadFolderMap[galleryID] = folder.name
val cache = Cache.getInstance(this@migrate, galleryID)
val list: MutableList<String?> = val list: MutableList<String?> =
MutableList(cache.getReader()!!.galleryInfo.files.size) { null } MutableList(reader!!.galleryInfo.files.size) { null }
folder.listFiles { dir -> folder.listFiles { file ->
dir?.nameWithoutExtension?.toIntOrNull() != null file?.nameWithoutExtension?.let {
Regex("""\d{5}""").matches(it) && it.toIntOrNull() != null
} == true
}?.forEach { }?.forEach {
list[it.nameWithoutExtension.toInt()] = it.name list[it.nameWithoutExtension.toInt()] = it.name
} }
cache.setMetadata { folder.getChild(".metadata").also { if (it.exists()) it.delete(); it.createNewFile() }.writeText(
it.galleryBlock = galleryBlock Json.encodeToString(Metadata(galleryBlock, reader, list))
it.reader = reader )
it.imageList = list
synchronized(Cache) {
Cache.delete(galleryID)
} }
downloadFolderMap[galleryID] = folder.name
downloadFolder.getChild(".download").let { if (!it.exists()) it.createNewFile(); it.writeText(Json.encodeToString(downloadFolderMap)) }
} }
} }
@@ -305,5 +324,9 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
.setOngoing(false) .setOngoing(false)
.mActions.clear() .mActions.clear()
notificationManager.notify(R.id.notification_id_import, notification.build()) notificationManager.notify(R.id.notification_id_import, notification.build())
kotlin.runCatching {
unregisterReceiver(receiver)
}
} }
} }

View File

@@ -63,18 +63,22 @@
android:text="@string/main_no_result" android:text="@string/main_no_result"
android:visibility="invisible"/> android:visibility="invisible"/>
<androidx.recyclerview.widget.RecyclerView <com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
android:id="@+id/main_recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingTop="64dp" app:handleHeight="100dp"
android:clipToPadding="false" app:addLastItemPadding="true"
app:fastScrollEnabled="true" app:popupDrawable="@color/transparent">
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable" <androidx.recyclerview.widget.RecyclerView
app:fastScrollHorizontalTrackDrawable="@drawable/track_drawable" android:id="@+id/main_recyclerview"
app:fastScrollVerticalTrackDrawable="@drawable/track_drawable" android:layout_width="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/> android:layout_height="match_parent"
android:paddingTop="64dp"
android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
<com.github.clans.fab.FloatingActionMenu <com.github.clans.fab.FloatingActionMenu
android:id="@+id/main_fab" android:id="@+id/main_fab"

View File

@@ -63,18 +63,22 @@
android:text="@string/main_no_result" android:text="@string/main_no_result"
android:visibility="invisible"/> android:visibility="invisible"/>
<androidx.recyclerview.widget.RecyclerView <com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
android:id="@+id/main_recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingTop="64dp" app:handleHeight="100dp"
android:clipToPadding="false" app:addLastItemPadding="true"
app:fastScrollEnabled="true" app:popupDrawable="@color/transparent">
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable" <androidx.recyclerview.widget.RecyclerView
app:fastScrollHorizontalTrackDrawable="@drawable/track_drawable" android:id="@+id/main_recyclerview"
app:fastScrollVerticalTrackDrawable="@drawable/track_drawable" android:layout_width="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/> android:layout_height="match_parent"
android:paddingTop="64dp"
android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
<com.github.clans.fab.FloatingActionMenu <com.github.clans.fab.FloatingActionMenu
android:id="@+id/main_fab" android:id="@+id/main_fab"

View File

@@ -26,16 +26,20 @@
android:background="@color/dark_gray" android:background="@color/dark_gray"
tools:context=".ui.ReaderActivity"> tools:context=".ui.ReaderActivity">
<androidx.recyclerview.widget.RecyclerView <com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
android:id="@+id/reader_recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:fastScrollEnabled="true" app:handleHeight="100dp"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable" app:addLastItemPadding="true"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable" app:popupDrawable="@color/transparent">
app:fastScrollHorizontalTrackDrawable="@drawable/track_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/track_drawable" <androidx.recyclerview.widget.RecyclerView
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/> android:id="@+id/reader_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -22,7 +22,6 @@ allprojects {
repositories { repositories {
google() google()
jcenter() jcenter()
mavenLocal()
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
maven { url 'https://guardian.github.com/maven/repo-releases' } maven { url 'https://guardian.github.com/maven/repo-releases' }
} }