Utilizing Glide

Fixed Reader FAB icon
Changed to use gallery id instead of galleryblock to open Reader
This commit is contained in:
tom5079
2019-06-30 22:04:35 +09:00
parent b5812f2a3a
commit bd4b61d7ac
16 changed files with 274 additions and 255 deletions

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" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/classes" /> <output url="file://$PROJECT_DIR$/classes" />
</component> </component>
</project> </project>

View File

@@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlinx-serialization' apply plugin: 'kotlinx-serialization'
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
@@ -52,8 +53,13 @@ dependencies {
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
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.github.bumptech.glide:glide:4.9.0'
implementation ("com.github.bumptech.glide:recyclerview-integration:4.9.0") {
transitive = false
}
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0' implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
implementation "ru.noties.markwon:core:${markwonVersion}" implementation "ru.noties.markwon:core:${markwonVersion}"
kapt 'com.github.bumptech.glide:compiler:4.9.0'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0'

View File

@@ -3,7 +3,6 @@ package xyz.quaver.pupil.adapters
import android.app.AlertDialog import android.app.AlertDialog
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.util.Log
import android.util.SparseBooleanArray import android.util.SparseBooleanArray
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@@ -15,6 +14,8 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
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.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.google.android.material.chip.Chip 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
@@ -23,9 +24,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.JsonConfiguration
import kotlinx.serialization.list
import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.ReaderItem import xyz.quaver.hitomi.Reader
import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.Pupil
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.types.Tag
@@ -47,8 +47,8 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
private lateinit var favorites: Histories private lateinit var favorites: Histories
inner class GalleryViewHolder(private val view: CardView) : RecyclerView.ViewHolder(view) { inner class GalleryViewHolder(val view: CardView) : RecyclerView.ViewHolder(view) {
fun bind(item: Pair<GalleryBlock, Deferred<String>>) { fun bind(holder: GalleryViewHolder, item: Pair<GalleryBlock, Deferred<String>>) {
with(view) { with(view) {
val resources = context.resources val resources = context.resources
val languages = resources.getStringArray(R.array.languages).map { val languages = resources.getStringArray(R.array.languages).map {
@@ -62,17 +62,15 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
val artists = galleryBlock.artists val artists = galleryBlock.artists
val series = galleryBlock.series val series = galleryBlock.series
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Main).launch {
val cache = thumbnail.await() val cache = thumbnail.await()
if (!File(cache).exists()) Glide.with(holder.view)
return@launch .load(cache)
.skipMemoryCache(true)
val bitmap = BitmapFactory.decodeFile(thumbnail.await()) .diskCacheStrategy(DiskCacheStrategy.NONE)
.error(R.drawable.image_broken_variant)
launch(Dispatchers.Main) { .into(galleryblock_thumbnail)
galleryblock_thumbnail.setImageBitmap(bitmap)
}
} }
//Check cache //Check cache
@@ -81,10 +79,10 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
if (readerCache.invoke().exists()) { if (readerCache.invoke().exists()) {
val reader = Json(JsonConfiguration.Stable) val reader = Json(JsonConfiguration.Stable)
.parse(ReaderItem.serializer().list, readerCache.invoke().readText()) .parse(Reader.serializer(), readerCache.invoke().readText())
with(galleryblock_progressbar) { with(galleryblock_progressbar) {
max = reader.size max = reader.readerItems.size
progress = imageCache.invoke().list()?.size ?: 0 progress = imageCache.invoke().list()?.size ?: 0
visibility = View.VISIBLE visibility = View.VISIBLE
@@ -108,8 +106,8 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
} else { } else {
if (visibility == View.GONE) { if (visibility == View.GONE) {
val reader = Json(JsonConfiguration.Stable) val reader = Json(JsonConfiguration.Stable)
.parse(ReaderItem.serializer().list, readerCache.invoke().readText()) .parse(Reader.serializer(), readerCache.invoke().readText())
max = reader.size max = reader.readerItems.size
visibility = View.VISIBLE visibility = View.VISIBLE
} }
@@ -334,7 +332,7 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is GalleryViewHolder) if (holder is GalleryViewHolder)
holder.bind(galleries[position-(if (showPrev) 1 else 0)]) holder.bind(holder, galleries[position-(if (showPrev) 1 else 0)])
} }
override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) { override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) {

View File

@@ -1,13 +1,13 @@
package xyz.quaver.pupil.adapters package xyz.quaver.pupil.adapters
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
class ReaderAdapter(private val images: List<String>) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() { class ReaderAdapter(private val images: List<String>) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
@@ -25,47 +25,19 @@ class ReaderAdapter(private val images: List<String>) : RecyclerView.Adapter<Rea
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val progressDrawable = CircularProgressDrawable(holder.view.context).apply {
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int { strokeWidth = 10f
// Raw height and width of image centerRadius = 100f
val (height: Int, width: Int) = options.run { outHeight to outWidth } start()
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight: Int = height / 2
val halfWidth: Int = width / 2
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
} }
return inSampleSize Glide.with(holder.view)
} .load(images[position])
.diskCacheStrategy(DiskCacheStrategy.NONE)
with(holder.view as ImageView) { .skipMemoryCache(true)
val options = BitmapFactory.Options() .placeholder(progressDrawable)
.error(R.drawable.image_broken_variant)
options.inJustDecodeBounds = true .into(holder.view as ImageView)
BitmapFactory.decodeFile(images[position], options)
val (reqWidth, reqHeight) = context.resources.displayMetrics.let {
Pair(it.widthPixels, it.heightPixels)
}
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
options.inPreferredConfig = Bitmap.Config.RGB_565
options.inJustDecodeBounds = false
val image = BitmapFactory.decodeFile(images[position], options)
setImageBitmap(image)
}
} }
override fun getItemCount() = images.size override fun getItemCount() = images.size

View File

@@ -0,0 +1,13 @@
package xyz.quaver.pupil.types
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
import kotlinx.android.parcel.Parcelize
import xyz.quaver.hitomi.Suggestion
@Parcelize
class SelectorSuggestion : SearchSuggestion {
override fun getBody(): String {
return ""
}
}

View File

@@ -5,7 +5,7 @@ import kotlinx.android.parcel.Parcelize
import xyz.quaver.hitomi.Suggestion import xyz.quaver.hitomi.Suggestion
@Parcelize @Parcelize
data class TagSuggestion constructor(val s: String, val t: Int, val u: String, val n: String) : SearchSuggestion { data class TagSuggestion(val s: String, val t: Int, val u: String, val n: String) : SearchSuggestion {
constructor(s: Suggestion) : this(s.s, s.t, s.u, s.n) constructor(s: Suggestion) : this(s.s, s.t, s.u, s.n)
override fun getBody(): String { override fun getBody(): String {

View File

@@ -55,6 +55,8 @@ import java.net.URL
import java.util.* import java.util.*
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.math.abs
import kotlin.math.ceil
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
@@ -72,8 +74,10 @@ class MainActivity : AppCompatActivity() {
private var query = "" private var query = ""
set(value) { set(value) {
field = value field = value
findViewById<SearchInputView>(R.id.search_bar_text) with(findViewById<SearchInputView>(R.id.search_bar_text)) {
.setText(query, TextView.BufferType.EDITABLE) if (text.toString() != value)
setText(query, TextView.BufferType.EDITABLE)
}
} }
private var mode = Mode.SEARCH private var mode = Mode.SEARCH
@@ -155,7 +159,7 @@ class MainActivity : AppCompatActivity() {
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
val preference = PreferenceManager.getDefaultSharedPreferences(this) val preference = PreferenceManager.getDefaultSharedPreferences(this)
val perPage = preference.getString("per_page", "25")!!.toInt() val perPage = preference.getString("per_page", "25")!!.toInt()
val maxPage = Math.ceil(totalItems / perPage.toDouble()).roundToInt() val maxPage = ceil(totalItems / perPage.toDouble()).roundToInt()
return when(keyCode) { return when(keyCode) {
KeyEvent.KEYCODE_VOLUME_DOWN -> { KeyEvent.KEYCODE_VOLUME_DOWN -> {
@@ -380,9 +384,9 @@ class MainActivity : AppCompatActivity() {
val intent = Intent(this@MainActivity, ReaderActivity::class.java) val intent = Intent(this@MainActivity, ReaderActivity::class.java)
val gallery = galleries[position].first val gallery = galleries[position].first
intent.putExtra("galleryblock", Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), gallery)) intent.putExtra("galleryID", gallery.id)
//TODO: Maybe sprinke some transitions will be nice :D //TODO: Maybe sprinkling some transitions will be nice :D
startActivity(intent) startActivity(intent)
histories.add(gallery.id) histories.add(gallery.id)
@@ -391,7 +395,7 @@ class MainActivity : AppCompatActivity() {
if (v !is CardView) if (v !is CardView)
return@setOnItemLongClickListener true return@setOnItemLongClickListener true
val galleryBlock = galleries[position].first val gallery = galleries[position].first
val view = LayoutInflater.from(this@MainActivity) val view = LayoutInflater.from(this@MainActivity)
.inflate(R.layout.dialog_galleryblock, recyclerView, false) .inflate(R.layout.dialog_galleryblock, recyclerView, false)
@@ -400,15 +404,15 @@ class MainActivity : AppCompatActivity() {
}.create() }.create()
with(view.main_dialog_download) { with(view.main_dialog_download) {
text = when(GalleryDownloader.get(galleryBlock.id)) { text = when(GalleryDownloader.get(gallery.id)) {
null -> getString(R.string.reader_fab_download) null -> getString(R.string.reader_fab_download)
else -> getString(R.string.reader_fab_download_cancel) else -> getString(R.string.reader_fab_download_cancel)
} }
isEnabled = !(adapter as GalleryBlockAdapter).completeFlag.get(galleryBlock.id, false) isEnabled = !(adapter as GalleryBlockAdapter).completeFlag.get(gallery.id, false)
setOnClickListener { setOnClickListener {
val downloader = GalleryDownloader.get(galleryBlock.id) val downloader = GalleryDownloader.get(gallery.id)
if (downloader == null) if (downloader == null)
GalleryDownloader(context, galleryBlock, true).start() GalleryDownloader(context, gallery.id, true).start()
else { else {
downloader.cancel() downloader.cancel()
downloader.clearNotification() downloader.clearNotification()
@@ -420,16 +424,16 @@ class MainActivity : AppCompatActivity() {
view.main_dialog_delete.setOnClickListener { view.main_dialog_delete.setOnClickListener {
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
with(GalleryDownloader[galleryBlock.id]) { with(GalleryDownloader[gallery.id]) {
this?.cancelAndJoin() this?.cancelAndJoin()
this?.clearNotification() this?.clearNotification()
} }
val cache = File(cacheDir, "imageCache/${galleryBlock.id}") val cache = File(cacheDir, "imageCache/${gallery.id}")
val data = getCachedGallery(context, galleryBlock.id) val data = getCachedGallery(context, gallery.id)
cache.deleteRecursively() cache.deleteRecursively()
data.deleteRecursively() data.deleteRecursively()
downloads.remove(galleryBlock.id) downloads.remove(gallery.id)
if (mode == Mode.DOWNLOAD) { if (mode == Mode.DOWNLOAD) {
runOnUiThread { runOnUiThread {
@@ -440,7 +444,7 @@ class MainActivity : AppCompatActivity() {
} }
} }
(adapter as GalleryBlockAdapter).completeFlag.put(galleryBlock.id, false) (adapter as GalleryBlockAdapter).completeFlag.put(gallery.id, false)
} }
dialog.dismiss() dialog.dismiss()
} }
@@ -583,7 +587,7 @@ class MainActivity : AppCompatActivity() {
//BOTTOM //BOTTOM
//Scrolling DOWN //Scrolling DOWN
if (dist < 0 && currentPage != Math.ceil(totalItems.toDouble()/perPage).roundToInt()-1) { if (dist < 0 && currentPage != ceil(totalItems.toDouble()/perPage).roundToInt()-1) {
with(main_recyclerview.adapter as GalleryBlockAdapter) { with(main_recyclerview.adapter as GalleryBlockAdapter) {
if(!showNext) { if(!showNext) {
showNext = true showNext = true
@@ -595,7 +599,7 @@ class MainActivity : AppCompatActivity() {
getChildAt(childCount-1) getChildAt(childCount-1)
} }
val absDist = Math.abs(dist) val absDist = abs(dist)
if (next is LinearLayout) { if (next is LinearLayout) {
val icon = next.findViewById<ImageView>(R.id.icon_next) val icon = next.findViewById<ImageView>(R.id.icon_next)
@@ -701,7 +705,7 @@ class MainActivity : AppCompatActivity() {
setMessage(getString( setMessage(getString(
R.string.main_jump_message, R.string.main_jump_message,
currentPage+1, currentPage+1,
Math.ceil(totalItems / perPage.toDouble()).roundToInt() ceil(totalItems / perPage.toDouble()).roundToInt()
)) ))
setPositiveButton(android.R.string.ok) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
@@ -729,10 +733,7 @@ class MainActivity : AppCompatActivity() {
val intent = Intent(this@MainActivity, ReaderActivity::class.java) val intent = Intent(this@MainActivity, ReaderActivity::class.java)
val gallery = val gallery =
getGalleryBlock(editText.text.toString().toInt()) ?: throw Exception() getGalleryBlock(editText.text.toString().toInt()) ?: throw Exception()
intent.putExtra( intent.putExtra("galleryID", gallery.id)
"galleryblock",
Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), gallery)
)
startActivity(intent) startActivity(intent)
} catch (e: Exception) { } catch (e: Exception) {
@@ -747,10 +748,17 @@ class MainActivity : AppCompatActivity() {
} }
setOnQueryChangeListener { _, query -> setOnQueryChangeListener { _, query ->
this@MainActivity.query = query
clearSuggestions() clearSuggestions()
if (query.isEmpty() or query.endsWith(' ')) if (query.isEmpty() or query.endsWith(' ')) {
swapSuggestions(json.parse(serializer, favoritesFile.readText()).map {
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
})
return@setOnQueryChangeListener return@setOnQueryChangeListener
}
val currentQuery = query.split(" ").last().replace('_', ' ') val currentQuery = query.split(" ").last().replace('_', ' ')
@@ -852,8 +860,6 @@ class MainActivity : AppCompatActivity() {
delete(if (lastIndexOf(' ') == -1) 0 else lastIndexOf(' ')+1, length) delete(if (lastIndexOf(' ') == -1) 0 else lastIndexOf(' ')+1, length)
append("${suggestion.n}:${suggestion.s.replace(Regex("\\s"), "_")} ") append("${suggestion.n}:${suggestion.s.replace(Regex("\\s"), "_")} ")
} }
clearSuggestions()
} }
override fun onSearchAction(currentQuery: String?) { override fun onSearchAction(currentQuery: String?) {
@@ -863,7 +869,7 @@ class MainActivity : AppCompatActivity() {
setOnFocusChangeListener(object: FloatingSearchView.OnFocusChangeListener { setOnFocusChangeListener(object: FloatingSearchView.OnFocusChangeListener {
override fun onFocus() { override fun onFocus() {
if (searchInputView.text.isEmpty()) if (query.isEmpty() or query.endsWith(' '))
swapSuggestions(json.parse(serializer, favoritesFile.readText()).map { swapSuggestions(json.parse(serializer, favoritesFile.readText()).map {
TagSuggestion(it.tag, -1, "", it.area ?: "tag") TagSuggestion(it.tag, -1, "", it.area ?: "tag")
}) })

View File

@@ -7,6 +7,7 @@ import android.os.Bundle
import android.view.* import android.view.*
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.PagerSnapHelper import androidx.recyclerview.widget.PagerSnapHelper
@@ -21,13 +22,8 @@ import kotlinx.android.synthetic.main.dialog_numberpicker.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.runBlocking
import kotlinx.io.IOException import kotlinx.io.IOException
import kotlinx.serialization.ImplicitReflectionSerializer import kotlinx.serialization.ImplicitReflectionSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.getGalleryBlock
import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.Pupil
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.adapters.ReaderAdapter import xyz.quaver.pupil.adapters.ReaderAdapter
@@ -37,8 +33,8 @@ import xyz.quaver.pupil.util.ItemClickSupport
class ReaderActivity : AppCompatActivity() { class ReaderActivity : AppCompatActivity() {
private var galleryID = 0
private val images = ArrayList<String>() private val images = ArrayList<String>()
private lateinit var galleryBlock: GalleryBlock
private var gallerySize = 0 private var gallerySize = 0
private var currentPage = 0 private var currentPage = 0
@@ -66,6 +62,9 @@ class ReaderActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
title = getString(R.string.reader_loading)
supportActionBar?.setDisplayHomeAsUpEnabled(false)
favorites = (application as Pupil).favorites favorites = (application as Pupil).favorites
window.setFlags( window.setFlags(
@@ -76,16 +75,13 @@ class ReaderActivity : AppCompatActivity() {
handleIntent(intent) handleIntent(intent)
Crashlytics.setInt("GalleryID", galleryBlock.id) Crashlytics.setInt("GalleryID", galleryID)
if (!::galleryBlock.isInitialized) { if (galleryID == 0) {
onBackPressed() onBackPressed()
return return
} }
supportActionBar?.title = galleryBlock.title
supportActionBar?.setDisplayHomeAsUpEnabled(false)
initDownloader() initDownloader()
initView() initView()
@@ -106,25 +102,16 @@ class ReaderActivity : AppCompatActivity() {
if (uri != null && lastPathSegment != null) { if (uri != null && lastPathSegment != null) {
val nonNumber = Regex("[^-?0-9]+") val nonNumber = Regex("[^-?0-9]+")
val galleryID = when (uri.host) { galleryID = when (uri.host) {
"hitomi.la" -> lastPathSegment.replace(nonNumber, "").toInt() "hitomi.la" -> lastPathSegment.replace(nonNumber, "").toInt()
"히요비.asia" -> lastPathSegment.toInt() "히요비.asia" -> lastPathSegment.toInt()
"xn--9w3b15m8vo.asia" -> lastPathSegment.toInt() "xn--9w3b15m8vo.asia" -> lastPathSegment.toInt()
"e-hentai.org" -> uri.pathSegments[1].toInt() "e-hentai.org" -> uri.pathSegments[1].toInt()
else -> return else -> return
} }
runBlocking {
CoroutineScope(Dispatchers.IO).launch {
galleryBlock = getGalleryBlock(galleryID) ?: return@launch
}.join()
}
} }
} else { } else {
galleryBlock = Json(JsonConfiguration.Stable).parse( galleryID = intent.getIntExtra("galleryID", 0)
GalleryBlock.serializer(),
intent.getStringExtra("galleryblock")!!
)
} }
} }
@@ -148,7 +135,7 @@ class ReaderActivity : AppCompatActivity() {
with(menu?.findItem(R.id.reader_menu_favorite)) { with(menu?.findItem(R.id.reader_menu_favorite)) {
this ?: return@with this ?: return@with
if (favorites.contains(galleryBlock.id)) if (favorites.contains(galleryID))
(icon as Animatable).start() (icon as Animatable).start()
} }
@@ -176,7 +163,7 @@ class ReaderActivity : AppCompatActivity() {
dialog.show() dialog.show()
} }
R.id.reader_menu_favorite -> { R.id.reader_menu_favorite -> {
val id = galleryBlock.id val id = galleryID
val favorite = menu?.findItem(R.id.reader_menu_favorite) ?: return true val favorite = menu?.findItem(R.id.reader_menu_favorite) ?: return true
if (favorites.contains(id)) { if (favorites.contains(id)) {
@@ -215,11 +202,11 @@ class ReaderActivity : AppCompatActivity() {
} }
private fun initDownloader() { private fun initDownloader() {
var d: GalleryDownloader? = GalleryDownloader.get(galleryBlock.id) var d: GalleryDownloader? = GalleryDownloader.get(galleryID)
if (d == null) { if (d == null) {
try { try {
d = GalleryDownloader(this, galleryBlock) d = GalleryDownloader(this, galleryID)
} catch (e: IOException) { } catch (e: IOException) {
Snackbar.make(reader_layout, R.string.unable_to_connect, Snackbar.LENGTH_LONG).show() Snackbar.make(reader_layout, R.string.unable_to_connect, Snackbar.LENGTH_LONG).show()
finish() finish()
@@ -230,17 +217,18 @@ class ReaderActivity : AppCompatActivity() {
downloader = d.apply { downloader = d.apply {
onReaderLoadedHandler = { onReaderLoadedHandler = {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
title = it.title
with(reader_download_progressbar) { with(reader_download_progressbar) {
max = it.size max = it.readerItems.size
progress = 0 progress = 0
} }
with(reader_progressbar) { with(reader_progressbar) {
max = it.size max = it.readerItems.size
progress = 0 progress = 0
} }
gallerySize = it.size gallerySize = it.readerItems.size
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${it.size}" menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${it.readerItems.size}"
} }
} }
onProgressHandler = { onProgressHandler = {
@@ -341,14 +329,9 @@ class ReaderActivity : AppCompatActivity() {
} }
} }
reader_fab_fullscreen.setOnClickListener { with(reader_fab_download) {
isFullscreen = true setImageResource(R.drawable.ic_download)
fullscreen(isFullscreen) setOnClickListener {
reader_fab.close(true)
}
reader_fab_download.setOnClickListener {
downloader.download = !downloader.download downloader.download = !downloader.download
if (!downloader.download) if (!downloader.download)
@@ -356,6 +339,17 @@ class ReaderActivity : AppCompatActivity() {
} }
} }
with(reader_fab_fullscreen) {
setImageResource(R.drawable.ic_fullscreen)
setOnClickListener {
isFullscreen = true
fullscreen(isFullscreen)
this@ReaderActivity.reader_fab.close(true)
}
}
}
private fun fullscreen(isFullscreen: Boolean) { private fun fullscreen(isFullscreen: Boolean) {
with(window.attributes) { with(window.attributes) {
if (isFullscreen) { if (isFullscreen) {

View File

@@ -13,12 +13,13 @@ import kotlinx.coroutines.*
import kotlinx.io.IOException import kotlinx.io.IOException
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.JsonConfiguration
import kotlinx.serialization.list import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.* import xyz.quaver.hitomi.getReader
import xyz.quaver.hitomi.getReferer
import xyz.quaver.hiyobi.cookie import xyz.quaver.hiyobi.cookie
import xyz.quaver.hiyobi.user_agent import xyz.quaver.hiyobi.user_agent
import xyz.quaver.pupil.R
import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.Pupil
import xyz.quaver.pupil.R
import xyz.quaver.pupil.ui.ReaderActivity import xyz.quaver.pupil.ui.ReaderActivity
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@@ -30,7 +31,7 @@ import kotlin.concurrent.schedule
class GalleryDownloader( class GalleryDownloader(
base: Context, base: Context,
private val galleryBlock: GalleryBlock, private val galleryID: Int,
_notify: Boolean = false _notify: Boolean = false
) : ContextWrapper(base) { ) : ContextWrapper(base) {
@@ -41,10 +42,10 @@ class GalleryDownloader(
set(value) { set(value) {
if (value) { if (value) {
field = true field = true
notificationManager.notify(galleryBlock.id, notificationBuilder.build()) notificationManager.notify(galleryID, notificationBuilder.build())
val data = getCachedGallery(this, galleryBlock.id) val data = getCachedGallery(this, galleryID)
val cache = File(cacheDir, "imageCache/${galleryBlock.id}") val cache = File(cacheDir, "imageCache/$galleryID")
if (File(cache, "images").exists() && !data.exists()) { if (File(cache, "images").exists() && !data.exists()) {
cache.copyRecursively(data, true) cache.copyRecursively(data, true)
@@ -54,7 +55,7 @@ class GalleryDownloader(
if (reader?.isActive == false && downloadJob?.isActive != true) if (reader?.isActive == false && downloadJob?.isActive != true)
field = false field = false
downloads.add(galleryBlock.id) downloads.add(galleryID)
} else { } else {
field = false field = false
} }
@@ -78,24 +79,24 @@ class GalleryDownloader(
companion object : SparseArray<GalleryDownloader>() companion object : SparseArray<GalleryDownloader>()
init { init {
put(galleryBlock.id, this) put(galleryID, this)
initNotification() initNotification()
reader = CoroutineScope(Dispatchers.IO).async { reader = CoroutineScope(Dispatchers.IO).async {
download = _notify download = _notify
val json = Json(JsonConfiguration.Stable) val json = Json(JsonConfiguration.Stable)
val serializer = ReaderItem.serializer().list val serializer = Reader.serializer()
//Check cache //Check cache
val cache = File(getCachedGallery(this@GalleryDownloader, galleryBlock.id), "reader.json") val cache = File(getCachedGallery(this@GalleryDownloader, galleryID), "reader.json")
if (cache.exists()) { if (cache.exists()) {
val cached = json.parse(serializer, cache.readText()) val cached = json.parse(serializer, cache.readText())
if (cached.isNotEmpty()) { if (cached.readerItems.isNotEmpty()) {
useHiyobi = when { useHiyobi = when {
cached.first().url.contains("hitomi.la") -> false cached.readerItems[0].url.contains("hitomi.la") -> false
else -> true else -> true
} }
@@ -108,22 +109,22 @@ class GalleryDownloader(
//Cache doesn't exist. Load from internet //Cache doesn't exist. Load from internet
val reader = when { val reader = when {
useHiyobi -> { useHiyobi -> {
xyz.quaver.hiyobi.getReader(galleryBlock.id).let { xyz.quaver.hiyobi.getReader(galleryID).let {
when { when {
it.isEmpty() -> { it.readerItems.isEmpty() -> {
useHiyobi = false useHiyobi = false
getReader(galleryBlock.id) getReader(galleryID)
} }
else -> it else -> it
} }
} }
} }
else -> { else -> {
getReader(galleryBlock.id) getReader(galleryID)
} }
} }
if (reader.isNotEmpty()) { if (reader.readerItems.isNotEmpty()) {
//Save cache //Save cache
if (cache.parentFile?.exists() == false) if (cache.parentFile?.exists() == false)
cache.parentFile!!.mkdirs() cache.parentFile!!.mkdirs()
@@ -141,7 +142,7 @@ class GalleryDownloader(
downloadJob = CoroutineScope(Dispatchers.Default).launch { downloadJob = CoroutineScope(Dispatchers.Default).launch {
val reader = reader!!.await() val reader = reader!!.await()
if (reader.isEmpty()) if (reader.readerItems.isEmpty())
onErrorHandler?.invoke(IOException("Couldn't retrieve Reader")) onErrorHandler?.invoke(IOException("Couldn't retrieve Reader"))
val list = ArrayList<String>() val list = ArrayList<String>()
@@ -149,29 +150,20 @@ class GalleryDownloader(
onReaderLoadedHandler?.invoke(reader) onReaderLoadedHandler?.invoke(reader)
notificationBuilder notificationBuilder
.setProgress(reader.size, 0, false) .setProgress(reader.readerItems.size, 0, false)
.setContentText("0/${reader.size}") .setContentText("0/${reader.readerItems.size}")
reader.chunked(4).forEachIndexed { chunkIndex, chunked -> reader.readerItems.chunked(4).forEachIndexed { chunkIndex, chunked ->
chunked.mapIndexed { i, it -> chunked.mapIndexed { i, it ->
val index = chunkIndex*4+i val index = chunkIndex*4+i
onProgressHandler?.invoke(index)
notificationBuilder
.setProgress(reader.size, index, false)
.setContentText("$index/${reader.size}")
if (download)
notificationManager.notify(galleryBlock.id, notificationBuilder.build())
async(Dispatchers.IO) { async(Dispatchers.IO) {
val url = if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url val url = if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url
val name = "$index".padStart(4, '0') val name = "$index".padStart(4, '0')
val ext = url.split('.').last() val ext = url.split('.').last()
val cache = File(getCachedGallery(this@GalleryDownloader, galleryBlock.id), "images/$name.$ext") val cache = File(getCachedGallery(this@GalleryDownloader, galleryID), "images/$name.$ext")
if (!cache.exists()) if (!cache.exists())
try { try {
@@ -180,7 +172,7 @@ class GalleryDownloader(
setRequestProperty("User-Agent", user_agent) setRequestProperty("User-Agent", user_agent)
setRequestProperty("Cookie", cookie) setRequestProperty("Cookie", cookie)
} else } else
setRequestProperty("Referer", getReferer(galleryBlock.id)) setRequestProperty("Referer", getReferer(galleryID))
if (cache.parentFile?.exists() == false) if (cache.parentFile?.exists() == false)
cache.parentFile!!.mkdirs() cache.parentFile!!.mkdirs()
@@ -193,31 +185,43 @@ class GalleryDownloader(
onErrorHandler?.invoke(e) onErrorHandler?.invoke(e)
notificationBuilder notificationBuilder
.setContentTitle(galleryBlock.title) .setContentTitle(reader.title)
.setContentText(getString(R.string.reader_notification_error)) .setContentText(getString(R.string.reader_notification_error))
.setProgress(0, 0, false) .setProgress(0, 0, false)
notificationManager.notify(galleryBlock.id, notificationBuilder.build()) notificationManager.notify(galleryID, notificationBuilder.build())
} }
cache.absolutePath cache.absolutePath
} }
}.forEach { }.forEach {
list.add(it.await()) list.add(it.await())
val index = list.size
onProgressHandler?.invoke(index)
notificationBuilder
.setProgress(reader.readerItems.size, index, false)
.setContentText("$index/${reader.readerItems.size}")
if (download)
notificationManager.notify(galleryID, notificationBuilder.build())
onDownloadedHandler?.invoke(list) onDownloadedHandler?.invoke(list)
} }
} }
Timer(false).schedule(1000) { Timer(false).schedule(1000) {
notificationBuilder notificationBuilder
.setContentTitle(galleryBlock.title) .setContentTitle(reader.title)
.setContentText(getString(R.string.reader_notification_complete)) .setContentText(getString(R.string.reader_notification_complete))
.setProgress(0, 0, false) .setProgress(0, 0, false)
if (download) { if (download) {
File(cacheDir, "imageCache/${galleryBlock.id}").let { File(cacheDir, "imageCache/${galleryID}").let {
if (it.exists()) { if (it.exists()) {
val target = File(getDownloadDirectory(this@GalleryDownloader), galleryBlock.id.toString()) val target = File(getDownloadDirectory(this@GalleryDownloader), galleryID.toString())
if (!target.exists()) if (!target.exists())
target.mkdirs() target.mkdirs()
@@ -227,7 +231,7 @@ class GalleryDownloader(
} }
} }
notificationManager.notify(galleryBlock.id, notificationBuilder.build()) notificationManager.notify(galleryID, notificationBuilder.build())
download = false download = false
} }
@@ -235,20 +239,20 @@ class GalleryDownloader(
onCompleteHandler?.invoke() onCompleteHandler?.invoke()
} }
remove(galleryBlock.id) remove(galleryID)
} }
} }
fun cancel() { fun cancel() {
downloadJob?.cancel() downloadJob?.cancel()
remove(galleryBlock.id) remove(galleryID)
} }
suspend fun cancelAndJoin() { suspend fun cancelAndJoin() {
downloadJob?.cancelAndJoin() downloadJob?.cancelAndJoin()
remove(galleryBlock.id) remove(galleryID)
} }
fun invokeOnReaderLoaded() { fun invokeOnReaderLoaded() {
@@ -258,7 +262,7 @@ class GalleryDownloader(
} }
fun clearNotification() { fun clearNotification() {
notificationManager.cancel(galleryBlock.id) notificationManager.cancel(galleryID)
} }
fun invokeOnNotifyChanged() { fun invokeOnNotifyChanged() {
@@ -267,22 +271,28 @@ class GalleryDownloader(
private fun initNotification() { private fun initNotification() {
val intent = Intent(this, ReaderActivity::class.java).apply { val intent = Intent(this, ReaderActivity::class.java).apply {
putExtra("galleryblock", Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), galleryBlock)) putExtra("galleryID", galleryID)
} }
val pendingIntent = TaskStackBuilder.create(this).run { val pendingIntent = TaskStackBuilder.create(this).run {
addNextIntentWithParentStack(intent) addNextIntentWithParentStack(intent)
getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
} }
notificationManager = NotificationManagerCompat.from(this)
notificationBuilder = NotificationCompat.Builder(this, "download").apply { notificationBuilder = NotificationCompat.Builder(this, "download").apply {
setContentTitle(galleryBlock.title) setContentTitle(getString(R.string.reader_loading))
setContentText(getString(R.string.reader_notification_text)) setContentText(getString(R.string.reader_notification_text))
setSmallIcon(R.drawable.ic_download) setSmallIcon(R.drawable.ic_download)
setContentIntent(pendingIntent) setContentIntent(pendingIntent)
setProgress(0, 0, true) setProgress(0, 0, true)
priority = NotificationCompat.PRIORITY_LOW priority = NotificationCompat.PRIORITY_LOW
} }
notificationManager = NotificationManagerCompat.from(this)
CoroutineScope(Dispatchers.Default).launch {
while (reader == null) ;
notificationBuilder.setContentTitle(reader.await().title)
}
} }
} }

View File

@@ -3,6 +3,7 @@
android:contentDescription="@string/reader_imageview_description" android:contentDescription="@string/reader_imageview_description"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="100dp"
android:paddingBottom="8dp" android:paddingBottom="8dp"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:adjustViewBounds="true"/> android:adjustViewBounds="true"/>

View File

@@ -79,4 +79,5 @@
<string name="settings_lock_wrong_confirm">ロックが一致しません。やり直してください。</string> <string name="settings_lock_wrong_confirm">ロックが一致しません。やり直してください。</string>
<string name="settings_lock_none">なし</string> <string name="settings_lock_none">なし</string>
<string name="settings_lock_remove_message">ロックを無効にしますか?</string> <string name="settings_lock_remove_message">ロックを無効にしますか?</string>
<string name="reader_loading">ロード中</string>
</resources> </resources>

View File

@@ -79,4 +79,5 @@
<string name="settings_lock_wrong_confirm">잠금이 일치하지 않습니다. 다시 시도하세요.</string> <string name="settings_lock_wrong_confirm">잠금이 일치하지 않습니다. 다시 시도하세요.</string>
<string name="settings_lock_none">없음</string> <string name="settings_lock_none">없음</string>
<string name="settings_lock_remove_message">잠금을 해제할까요?</string> <string name="settings_lock_remove_message">잠금을 해제할까요?</string>
<string name="reader_loading">로딩중</string>
</resources> </resources>

View File

@@ -71,6 +71,7 @@
<string name="galleryblock_type">Type: %1$s</string> <string name="galleryblock_type">Type: %1$s</string>
<string name="galleryblock_language">Language: %1$s</string> <string name="galleryblock_language">Language: %1$s</string>
<string name="reader_loading">Loading</string>
<string name="reader_go_to_page">Go to page</string> <string name="reader_go_to_page">Go to page</string>
<string name="reader_fab_fullscreen">Fullscreen</string> <string name="reader_fab_fullscreen">Fullscreen</string>
<string name="reader_fab_download">Background download</string> <string name="reader_fab_download">Background download</string>

View File

@@ -0,0 +1,72 @@
package xyz.quaver.hitomi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import kotlinx.serialization.list
import org.jsoup.Jsoup
import xyz.quaver.hiyobi.HiyobiReader
import java.net.URL
fun getReferer(galleryID: Int) = "https://hitomi.la/reader/$galleryID.html"
fun webpUrlFromUrl(url: String) = url.replace("/galleries/", "/webp/") + ".webp"
fun webpReaderFromReader(reader: Reader) : Reader {
if (reader is HiyobiReader)
return reader
return Reader(reader.title, reader.readerItems.map {
ReaderItem(
if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url,
it.galleryInfo
)
})
}
@Serializable
data class GalleryInfo(
val width: Int,
val haswebp: Int,
val name: String,
val height: Int
)
@Serializable
data class ReaderItem(
val url: String,
val galleryInfo: GalleryInfo?
)
@Serializable
open class Reader(val title: String, val readerItems: List<ReaderItem>)
//Set header `Referer` to reader url to avoid 403 error
fun getReader(galleryID: Int) : Reader {
val readerUrl = "https://hitomi.la/reader/$galleryID.html"
val galleryInfoUrl = "https://ltn.hitomi.la/galleries/$galleryID.js"
val doc = Jsoup.connect(readerUrl).get()
val title = doc.title()
val images = doc.select(".img-url").map {
protocol + urlFromURL(it.text())
}
val galleryInfo = ArrayList<GalleryInfo?>()
galleryInfo.addAll(
Json(JsonConfiguration.Stable).parse(
GalleryInfo.serializer().list,
Regex("""\[.+]""").find(
URL(galleryInfoUrl).readText()
)?.value ?: "[]"
)
)
if (images.size > galleryInfo.size)
galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size))
return Reader(title, (images zip galleryInfo).map {
ReaderItem(it.first, it.second)
})
}

View File

@@ -1,57 +0,0 @@
package xyz.quaver.hitomi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import kotlinx.serialization.list
import org.jsoup.Jsoup
import java.net.URL
fun getReferer(galleryID: Int) = "https://hitomi.la/reader/$galleryID.html"
@Serializable
data class GalleryInfo(
val width: Int,
val haswebp: Int,
val name: String,
val height: Int
)
@Serializable
data class ReaderItem(
val url: String,
val galleryInfo: GalleryInfo?
)
typealias Reader = List<ReaderItem>
//Set header `Referer` to reader url to avoid 403 error
fun getReader(galleryID: Int) : Reader {
val readerUrl = "https://hitomi.la/reader/$galleryID.html"
val galleryInfoUrl = "https://ltn.hitomi.la/galleries/$galleryID.js"
try {
val doc = Jsoup.connect(readerUrl).get()
val images = doc.select(".img-url").map {
protocol + urlFromURL(it.text())
}
val galleryInfo = ArrayList<GalleryInfo?>()
galleryInfo.addAll(
Json(JsonConfiguration.Stable).parse(
GalleryInfo.serializer().list,
Regex("""\[.+]""").find(
URL(galleryInfoUrl).readText()
)?.value ?: "[]"
)
)
if (images.size > galleryInfo.size)
galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size))
return (images zip galleryInfo).map {
ReaderItem(it.first, it.second)
}
} catch (e: Exception) {
return emptyList()
}
}

View File

@@ -1,9 +1,9 @@
package xyz.quaver.hiyobi package xyz.quaver.hiyobi
import kotlinx.io.IOException
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.JsonConfiguration
import kotlinx.serialization.json.content import kotlinx.serialization.json.content
import org.jsoup.Jsoup
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.ReaderItem import xyz.quaver.hitomi.ReaderItem
import java.net.URL import java.net.URL
@@ -12,13 +12,15 @@ import javax.net.ssl.HttpsURLConnection
const val hiyobi = "xn--9w3b15m8vo.asia" const val hiyobi = "xn--9w3b15m8vo.asia"
const val user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" const val user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36"
class HiyobiReader(title: String, readerItems: List<ReaderItem>) : Reader(title, readerItems)
var cookie: String = "" var cookie: String = ""
get() { get() {
if (field.isEmpty()) if (field.isEmpty())
field = renewCookie() field = renewCookie()
return field return field
} }
fun renewCookie() : String { fun renewCookie() : String {
val url = "https://$hiyobi/" val url = "https://$hiyobi/"
@@ -35,10 +37,12 @@ fun renewCookie() : String {
} }
} }
fun getReader(galleryId: Int) : Reader { fun getReader(galleryID: Int) : Reader {
val url = "https://$hiyobi/data/json/${galleryId}_list.json" val reader = "https://$hiyobi/reader/$galleryID"
val url = "https://$hiyobi/data/json/${galleryID}_list.json"
val title = Jsoup.connect(reader).get().title()
try {
val json = Json(JsonConfiguration.Stable).parseJson( val json = Json(JsonConfiguration.Stable).parseJson(
with(URL(url).openConnection() as HttpsURLConnection) { with(URL(url).openConnection() as HttpsURLConnection) {
setRequestProperty("User-Agent", user_agent) setRequestProperty("User-Agent", user_agent)
@@ -50,11 +54,8 @@ fun getReader(galleryId: Int) : Reader {
} }
) )
return json.jsonArray.map { return Reader(title, json.jsonArray.map {
val name = it.jsonObject["name"]!!.content val name = it.jsonObject["name"]!!.content
ReaderItem("https://$hiyobi/data/$galleryId/$name", null) ReaderItem("https://$hiyobi/data/$galleryID/$name", null)
} })
} catch (e: Exception) {
return emptyList()
}
} }