Added mirror selector

Developing new Downloader under xyz.quaver.pupil.util.download
This commit is contained in:
Pupil
2020-01-24 15:11:35 +09:00
parent 37123a2cd5
commit c204353220
27 changed files with 651 additions and 92 deletions

7
.idea/kotlinCodeInsightSettings.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinCodeInsightWorkspaceSettings">
<option name="addUnambiguousImportsOnTheFly" value="true" />
<option name="optimizeImportsOnTheFly" value="true" />
</component>
</project>

6
.idea/kotlinc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="1.8" />
</component>
</project>

View File

@@ -41,6 +41,9 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
buildToolsVersion = '29.0.2'
}
@@ -50,8 +53,8 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
@@ -73,11 +76,12 @@ dependencies {
implementation ("com.github.bumptech.glide:recyclerview-integration:4.9.0") {
transitive = false
}
implementation 'com.github.chrisbanes:PhotoView:2.0.0'
implementation "com.squareup.okhttp3:okhttp:4.3.1"
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
implementation "ru.noties.markwon:core:${markwonVersion}"
kapt 'com.github.bumptech.glide:compiler:4.9.0'
testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test:runner:1.2.0'

View File

@@ -0,0 +1,85 @@
/*
* 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.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_mirrors.view.*
import xyz.quaver.pupil.R
import java.util.*
class MirrorAdapter(context: Context) : RecyclerView.Adapter<MirrorAdapter.ViewHolder>() {
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)
val mirrors = context.resources.getStringArray(R.array.mirrors).map {
it.split('|').let { split ->
Pair(split.first(), split.last())
}
}.toMap()
val list = mirrors.keys.toMutableList().apply {
PreferenceManager.getDefaultSharedPreferences(context)
.getString("mirrors", "")!!
.split(">")
.reversed()
.forEach {
if (this.contains(it)) {
this.remove(it)
this.add(0, it)
}
}
}
val onItemMove : ((Int, Int) -> Unit) = { from, to ->
Collections.swap(list, from, to)
notifyItemMoved(from, to)
onItemMoved?.invoke(list)
}
var onStartDrag : ((ViewHolder) -> Unit)? = null
var onItemMoved : ((List<String>) -> (Unit))? = null
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
with(holder.view) {
mirror_name.text = mirrors[list.elementAt(position)]
mirror_button.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN)
onStartDrag?.invoke(holder)
true
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return LayoutInflater.from(parent.context).inflate(
R.layout.item_mirrors, parent, false
).let {
ViewHolder(it)
}
}
override fun getItemCount() = mirrors.size
}

View File

@@ -40,6 +40,8 @@ class ReaderAdapter(private val glide: RequestManager,
var isFullScreen = false
var onItemClickListener : ((Int) -> (Unit))? = null
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@@ -60,12 +62,16 @@ class ReaderAdapter(private val glide: RequestManager,
var reader: Reader? = null
with (GalleryDownloader[galleryID]?.reader) {
if (this?.isCompleted == true)
if (reader == null && this?.isCompleted == true)
runBlocking {
reader = await()
}
}
holder.view.image.setOnPhotoTapListener { _, _, _ ->
onItemClickListener?.invoke(position)
}
glide
.load(File(getCachedGallery(holder.view.context, galleryID), images[position]))
.diskCacheStrategy(DiskCacheStrategy.NONE)

View File

@@ -413,7 +413,11 @@ class MainActivity : AppCompatActivity() {
val downloader = GalleryDownloader.get(galleryID)
if (downloader == null)
GalleryDownloader(context, galleryID, true).start()
GalleryDownloader(
context,
galleryID,
true
).start()
else {
downloader.cancel()
downloader.clearNotification()

View File

@@ -48,7 +48,6 @@ import xyz.quaver.pupil.R
import xyz.quaver.pupil.adapters.ReaderAdapter
import xyz.quaver.pupil.util.GalleryDownloader
import xyz.quaver.pupil.util.Histories
import xyz.quaver.pupil.util.ItemClickSupport
class ReaderActivity : AppCompatActivity() {
@@ -333,7 +332,19 @@ class ReaderActivity : AppCompatActivity() {
private fun initView() {
with(reader_recyclerview) {
adapter = ReaderAdapter(Glide.with(this@ReaderActivity), galleryID, images)
adapter = ReaderAdapter(Glide.with(this@ReaderActivity), galleryID, images).apply {
onItemClickListener = {
if (isScroll) {
isScroll = false
isFullscreen = true
scrollMode(false)
fullscreen(true)
} else {
(reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPosition(currentPage) //Moves to next page because currentPage is 1-based indexing
}
}
}
addOnScrollListener(object: RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
@@ -353,19 +364,6 @@ class ReaderActivity : AppCompatActivity() {
this@ReaderActivity.reader_progressbar.progress = currentPage
}
})
ItemClickSupport.addTo(this)
.setOnItemClickListener { _, _, _ ->
if (isScroll) {
isScroll = false
isFullscreen = true
scrollMode(false)
fullscreen(true)
} else {
(reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPosition(currentPage) //Moves to next page because currentPage is 1-based indexing
}
}
}
with(reader_fab_download) {

View File

@@ -19,6 +19,7 @@
package xyz.quaver.pupil.ui.dialog
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.text.Editable
@@ -28,6 +29,7 @@ import android.view.View
import android.widget.ArrayAdapter
import androidx.appcompat.app.AlertDialog
import androidx.preference.PreferenceManager
import kotlinx.android.synthetic.main.dialog_default_query.*
import kotlinx.android.synthetic.main.dialog_default_query.view.*
import xyz.quaver.pupil.R
import xyz.quaver.pupil.types.Tags
@@ -50,21 +52,41 @@ class DefaultQueryDialog(context : Context) : AlertDialog(context) {
@SuppressLint("InflateParams")
override fun onCreate(savedInstanceState: Bundle?) {
initDialog()
setTitle(R.string.default_query_dialog_title)
setView(dialogView)
setButton(Dialog.BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ ->
val newTags = Tags.parse(default_query_dialog_edittext.text.toString())
with(default_query_dialog_language_selector) {
if (selectedItemPosition != 0)
newTags.add("language:${reverseLanguages[selectedItem]}")
}
if (default_query_dialog_BL_checkbox.isChecked)
newTags.add(excludeBL)
if (default_query_dialog_guro_checkbox.isChecked)
excludeGuro.forEach { tag ->
newTags.add(tag)
}
onPositiveButtonClickListener?.invoke(newTags)
}
super.onCreate(savedInstanceState)
dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_default_query, null)
initView()
setContentView(dialogView)
}
private fun initView() {
@SuppressLint("InflateParams")
private fun initDialog() {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
val tags = Tags.parse(
preferences.getString("default_query", "") ?: ""
)
dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_default_query, null)
with(dialogView.default_query_dialog_language_selector) {
adapter =
ArrayAdapter(
@@ -105,7 +127,13 @@ class DefaultQueryDialog(context : Context) : AlertDialog(context) {
with(dialogView.default_query_dialog_edittext) {
setText(tags.toString(), android.widget.TextView.BufferType.EDITABLE)
addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
@@ -113,29 +141,14 @@ class DefaultQueryDialog(context : Context) : AlertDialog(context) {
s ?: return
if (s.any { it.isUpperCase() })
s.replace(0, s.length, s.toString().toLowerCase(java.util.Locale.getDefault()))
s.replace(
0,
s.length,
s.toString().toLowerCase(java.util.Locale.getDefault())
)
}
})
}
dialogView.default_query_dialog_ok.setOnClickListener {
val newTags = Tags.parse(dialogView.default_query_dialog_edittext.text.toString())
with(dialogView.default_query_dialog_language_selector) {
if (selectedItemPosition != 0)
newTags.add("language:${reverseLanguages[selectedItem]}")
}
if (dialogView.default_query_dialog_BL_checkbox.isChecked)
newTags.add(excludeBL)
if (dialogView.default_query_dialog_guro_checkbox.isChecked)
excludeGuro.forEach { tag ->
newTags.add(tag)
}
onPositiveButtonClickListener?.invoke(newTags)
}
}
}

View File

@@ -21,6 +21,7 @@ package xyz.quaver.pupil.ui.dialog
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.widget.LinearLayout
import android.widget.RadioButton
import androidx.appcompat.app.AlertDialog
@@ -37,7 +38,7 @@ class DownloadLocationDialog(context: Context) : AlertDialog(context) {
private val buttons = mutableListOf<RadioButton>()
var onDownloadLocationChangedListener : ((Int) -> (Unit))? = null
init {
override fun onCreate(savedInstanceState: Bundle?) {
val view = layoutInflater.inflate(R.layout.dialog_dl_location, null) as LinearLayout
ContextCompat.getExternalFilesDirs(context, null).forEachIndexed { index, dir ->
@@ -73,6 +74,8 @@ class DownloadLocationDialog(context: Context) : AlertDialog(context) {
setButton(Dialog.BUTTON_POSITIVE, context.getText(android.R.string.ok)) { _, _ ->
dismiss()
}
super.onCreate(savedInstanceState)
}
}

View File

@@ -0,0 +1,97 @@
/*
* 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.ui.dialog
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import xyz.quaver.pupil.R
import xyz.quaver.pupil.adapters.MirrorAdapter
class MirrorDialog(context: Context) : AlertDialog(context) {
class ItemTouchHelperCallback : ItemTouchHelper.Callback() {
var onMoveItem : ((Int, Int) -> (Unit))? = null
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
) = makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0)
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
onMoveItem?.invoke(viewHolder.adapterPosition, target.adapterPosition)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
}
private lateinit var recyclerView: RecyclerView
@SuppressLint("InflateParams")
override fun onCreate(savedInstanceState: Bundle?) {
initDialog()
setTitle(R.string.settings_mirror_title)
setView(recyclerView)
setButton(Dialog.BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ -> }
super.onCreate(savedInstanceState)
}
private fun initDialog() {
recyclerView = RecyclerView(context).apply recyclerview@{
addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
layoutManager = LinearLayoutManager(context)
adapter = MirrorAdapter(context).apply adapter@{
val itemTouchHelper = ItemTouchHelper(ItemTouchHelperCallback().apply {
onMoveItem = this@adapter.onItemMove
}).apply {
attachToRecyclerView(this@recyclerview)
}
onStartDrag = {
itemTouchHelper.startDrag(it)
}
onItemMoved = {
PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.putString("mirrors", it.joinToString(">"))
.apply()
}
}
}
}
}

View File

@@ -34,6 +34,7 @@ import xyz.quaver.pupil.ui.LockActivity
import xyz.quaver.pupil.ui.SettingsActivity
import xyz.quaver.pupil.ui.dialog.DefaultQueryDialog
import xyz.quaver.pupil.ui.dialog.DownloadLocationDialog
import xyz.quaver.pupil.ui.dialog.MirrorDialog
import xyz.quaver.pupil.util.*
import java.io.File
@@ -137,8 +138,6 @@ class SettingsFragment :
onPositiveButtonClickListener = { newTags ->
sharedPreferences.edit().putString("default_query", newTags.toString()).apply()
summary = newTags.toString()
dismiss() //This sucks
// TODO: make dialog dissmiss itself :P
}
}.show()
}
@@ -146,6 +145,10 @@ class SettingsFragment :
val intent = Intent(context, LockActivity::class.java)
activity?.startActivityForResult(intent, (activity as SettingsActivity).REQUEST_LOCK)
}
"mirrors" -> {
MirrorDialog(context)
.show()
}
"backup" -> {
File(ContextCompat.getDataDir(context), "favorites.json").copyTo(
File(getDownloadDirectory(context), "favorites.json"),
@@ -259,6 +262,9 @@ class SettingsFragment :
onPreferenceClickListener = this@SettingsFragment
}
"mirrors" -> {
onPreferenceClickListener = this@SettingsFragment
}
"dark_mode" -> {
onPreferenceChangeListener = this@SettingsFragment
}

View File

@@ -1,6 +1,6 @@
/*
* Pupil, Hitomi.la viewer for Android
* Copyright (C) 2019 tom5079
* 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
@@ -65,7 +65,10 @@ class GalleryDownloader(
notificationManager.notify(galleryID, notificationBuilder.build())
if (reader?.isActive == false && downloadJob?.isActive != true) {
val data = File(getDownloadDirectory(this), galleryID.toString())
val data = File(
getDownloadDirectory(
this
), galleryID.toString())
val cache = File(cacheDir, "imageCache/$galleryID")
if (File(cache, "images").exists() && !data.exists()) {
@@ -111,7 +114,11 @@ class GalleryDownloader(
val serializer = Reader.serializer()
//Check cache
val cache = File(getCachedGallery(this@GalleryDownloader, galleryID), "reader.json")
val cache = File(
getCachedGallery(
this@GalleryDownloader,
galleryID
), "reader.json")
try {
json.parse(serializer, cache.readText())
@@ -197,7 +204,11 @@ class GalleryDownloader(
val name = "$index".padStart(4, '0')
val ext = url.split('.').last()
val cache = File(getCachedGallery(this@GalleryDownloader, galleryID), "images/$name.$ext")
val cache = File(
getCachedGallery(
this@GalleryDownloader,
galleryID
), "images/$name.$ext")
if (!cache.exists())
try {
@@ -255,7 +266,10 @@ class GalleryDownloader(
if (download) {
File(cacheDir, "imageCache/${galleryID}").let {
if (it.exists()) {
val target = File(getDownloadDirectory(this@GalleryDownloader), galleryID.toString())
val target = File(
getDownloadDirectory(
this@GalleryDownloader
), galleryID.toString())
if (!target.exists())
target.mkdirs()

View File

@@ -0,0 +1,142 @@
/*
* 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.util.download
import android.content.Context
import android.content.ContextWrapper
import androidx.core.content.ContextCompat
import kotlinx.serialization.ImplicitReflectionSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.parse
import kotlinx.serialization.stringify
import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader
import java.io.File
class Cache(context: Context) : ContextWrapper(context) {
// Search in this order
// Download -> Cache
fun getCachedGallery(galleryID: Int) : File? {
var file : File
ContextCompat.getExternalFilesDirs(this, null).forEach {
file = File(it, galleryID.toString())
if (file.exists())
return file
}
file = File(cacheDir, "imageCache/$galleryID")
return if (file.exists())
file
else
null
}
@UseExperimental(ImplicitReflectionSerializer::class)
fun getCachedMetadata(galleryID: Int) : Metadata? {
val file = File(getCachedGallery(galleryID) ?: return null, ".metadata")
if (!file.exists())
return null
return try {
Json.parse(file.readText())
} catch (e: Exception) {
//File corrupted
file.delete()
null
}
}
@UseExperimental(ImplicitReflectionSerializer::class)
fun setCachedMetadata(galleryID: Int, metadata: Metadata) {
val file = File(getCachedGallery(galleryID), ".metadata")
if (!file.exists())
return
try {
file.writeText(Json.stringify(metadata))
} catch (e: Exception) {
}
}
fun getGalleryBlock(galleryID: Int): GalleryBlock {
var meta = Cache(this).getCachedMetadata(galleryID)
if (meta == null) {
meta = Metadata(galleryBlock = xyz.quaver.hitomi.getGalleryBlock(galleryID))
Cache(this).setCachedMetadata(
galleryID,
meta
)
} else if (meta.galleryBlock == null)
Cache(this).setCachedMetadata(
galleryID,
meta.apply {
galleryBlock = xyz.quaver.hitomi.getGalleryBlock(galleryID)
}
)
return meta.galleryBlock!!
}
fun getReaders(galleryID: Int): List<Reader> {
var meta = getCachedMetadata(galleryID)
if (meta == null) {
meta = Metadata(reader = mutableListOf(xyz.quaver.hitomi.getReader(galleryID)))
setCachedMetadata(
galleryID,
meta
)
} else if (meta.reader == null)
setCachedMetadata(
galleryID,
meta.apply {
reader = mutableListOf(xyz.quaver.hitomi.getReader(galleryID))
}
)
else if (!meta.reader!!.any { it.code == Reader.Code.HITOMI })
setCachedMetadata(
galleryID,
meta.apply {
reader!!.add(xyz.quaver.hitomi.getReader(galleryID))
}
)
return meta.reader!!
}
fun getImage(galleryID: Int, index: Int): File {
val cache = getCachedGallery(galleryID)
if (cache == null)
;//TODO: initiate image download
return File(cache, "%04d".format(index))
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.util.download
import android.content.Context
import android.content.ContextWrapper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import okhttp3.ResponseBody
import okio.*
import java.util.concurrent.Executors
class DownloadWorker(context: Context) : ContextWrapper(context) {
interface ProgressListener {
fun update(bytesRead : Long, contentLength: Long, done: Boolean)
}
//region ProgressResponseBody
class ProgressResponseBody(
val responseBody: ResponseBody,
val progressListener : ProgressListener
) : ResponseBody() {
var bufferedSource : BufferedSource? = null
override fun contentLength() = responseBody.contentLength()
override fun contentType() = responseBody.contentType()
override fun source(): BufferedSource {
if (bufferedSource == null)
bufferedSource = source(responseBody.source()).buffer()
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.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1L)
return bytesRead
}
}
}
//endregion
val queue = Channel<Int>()
val worker = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
val progressListener = object: ProgressListener {
override fun update(bytesRead: Long, contentLength: Long, done: Boolean) {
}
}
val client = OkHttpClient.Builder()
.addNetworkInterceptor { chain ->
chain.proceed(chain.request()).let { originalResponse ->
originalResponse.newBuilder()
.body(ProgressResponseBody(originalResponse.body!!, progressListener))
.build()
}
}.build()
init {
CoroutineScope(Dispatchers.IO).launch {
while (true) {
val galleryID = queue.receive()
val reader = Cache(context).getReaders(galleryID)
}
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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.util.download
import kotlinx.serialization.Serializable
import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader
@Serializable
data class Metadata(
var thumbnail: String? = null,
var galleryBlock: GalleryBlock? = null,
var reader: MutableList<Reader>? = null
)

View File

@@ -49,6 +49,7 @@ fun URL.download(to: File, onDownloadProgress: ((Long, Long) -> Unit)? = null) {
var bytesCopied: Long = 0
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var bytes = it.read(buffer)
while (bytes >= 0) {
out.write(buffer, 0, bytes)

View File

@@ -0,0 +1,8 @@
<!-- drawable/menu.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M3,6H21V8H3V6M3,11H21V13H3V11M3,16H21V18H3V16Z" />
</vector>

View File

@@ -18,21 +18,12 @@
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:padding="16dp">
<TextView
android:id="@+id/default_query_dialog_title"
style="@style/TextAppearance.AppCompat.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/default_query_dialog_title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<EditText
tools:ignore="Autofill"
android:inputType="text"
@@ -40,7 +31,7 @@
android:id="@+id/default_query_dialog_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/default_query_dialog_title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
@@ -116,14 +107,4 @@
</LinearLayout>
<Button
android:id="@+id/default_query_dialog_ok"
style="?borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/default_query_dialog_guro_layout"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="@android:string/ok"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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/>.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingStart="32dp"
android:paddingEnd="32dp"
android:paddingTop="16dp">
<TextView
android:id="@+id/mirror_name"
style="@style/TextAppearance.MaterialComponents.Headline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/mirror_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/menu"
android:tint="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -18,7 +18,7 @@
<string name="update_title">新しいアップデートがあります</string>
<string name="warning">注意</string>
<string name="settings_miscellaneous_title">その他</string>
<string name="settings_use_hiyobi_title">hiyobi.meからロード</string>
<string name="settings_mirror_title">ミラーサーバー</string>
<string name="settings_clear_history">履歴を削除</string>
<string name="settings_clear_history_alert_message">履歴を削除しますか?</string>
<string name="settings_clear_history_summary">履歴数: %1$d</string>
@@ -61,7 +61,7 @@
<string name="main_export_error">エクスポートエラーが発生しました</string>
<string name="settings_clear_downloads">ダウンロード削除</string>
<string name="settings_clear_downloads_alert_message">ダウンロードしたギャラリーを全て削除します。\n実行しますか</string>
<string name="settings_use_hiyobi_summary">ロード速度を向上させるため可能であればhiyobi.meからイメージロード</string>
<string name="settings_mirror_summary">ミラーサーバからイメージロード</string>
<string name="main_drawer_favorite">お気に入り</string>
<string name="main_open_gallery_by_id">ギャラリー番号で見る</string>
<string name="main_open_gallery_by_id_error">エラーが発生しました</string>

View File

@@ -18,7 +18,6 @@
<string name="main_no_result">결과 없음</string>
<string name="main_search">검색</string>
<string name="settings_miscellaneous_title">기타</string>
<string name="settings_use_hiyobi_title">hiyobi.me 사용</string>
<string name="settings_clear_history">기록 삭제</string>
<string name="settings_clear_history_alert_message">기록을 삭제하시겠습니까?</string>
<string name="settings_clear_history_summary">기록 %1$d개 저장됨</string>
@@ -61,7 +60,6 @@
<string name="main_export_error">내보내기 오류가 발생했습니다</string>
<string name="settings_clear_downloads">다운로드 삭제</string>
<string name="settings_clear_downloads_alert_message">다운로드 된 만화를 모두 삭제합니다.\n계속하시겠습니까?</string>
<string name="settings_use_hiyobi_summary">속도 향상을 위해 가능하면 hiyobi.me에서 이미지 로드</string>
<string name="main_drawer_favorite">즐겨찾기</string>
<string name="main_open_gallery_by_id">갤러리 번호로 열기</string>
<string name="main_open_gallery_by_id_error">갤러리를 찾지 못했습니다</string>
@@ -120,4 +118,6 @@
<string name="settings_app_version_description">v%s</string>
<string name="settings_low_quality">저해상도 이미지</string>
<string name="settings_low_quality_summary">로드 속도와 데이터 사용량을 줄이기 위해 저해상도 이미지를 로드</string>
<string name="settings_mirror_summary">미러 서버에서 이미지 로드</string>
<string name="settings_mirror_title">미러 설정</string>
</resources>

View File

@@ -57,4 +57,9 @@
<item>japanese|日本語</item>
</string-array>
<string-array name="mirrors">
<item>HITOMI|hitomi.la</item>
<item>HIYOBI|hiyobi.me</item>
</string-array>
</resources>

View File

@@ -151,8 +151,7 @@
<!-- SETTINGS/MISCELLANEOUS -->
<string name="settings_miscellaneous_title">Miscellaneous</string>
<string name="settings_use_hiyobi_title">Use hiyobi.me</string>
<string name="settings_use_hiyobi_summary">Load images from hiyobi.me to improve loading speed (if available)</string>
<string name="settings_mirror_summary">Load images from mirrors</string>
<string name="settings_security_mode_title">Enable security mode</string>
<string name="settings_security_mode_summary">Enable security mode to make the screen invisible on recent app window</string>
<string name="settings_dark_mode_title">Dark mode</string>
@@ -186,5 +185,6 @@
<string name="default_query_dialog_filter_BL">Filter BL</string>
<string name="default_query_dialog_filter_guro">Filter Guro</string>
<string name="default_query_dialog_language_selector_none">Any</string>
<string name="settings_mirror_title">Mirrors</string>
</resources>

View File

@@ -66,10 +66,10 @@
<PreferenceCategory
app:title="@string/settings_miscellaneous_title">
<SwitchPreferenceCompat
app:key="use_hiyobi"
app:title="@string/settings_use_hiyobi_title"
app:summary="@string/settings_use_hiyobi_summary"/>
<Preference
app:key="mirrors"
app:title="@string/settings_mirror_title"
app:summary="@string/settings_mirror_summary"/>
<SwitchPreferenceCompat
app:key="security_mode"

View File

@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@file:Suppress("UNUSED_VARIABLE")
@file:Suppress("UNUSED_VARIABLE", "IncorrectScope")
package xyz.quaver.pupil
@@ -35,7 +35,7 @@ class ExampleUnitTest {
@Test
fun test() {
URL("https://github.om/tom5079/Pupil/releases/download/4.2-beta2-hotfix2/Pupil-v4.2-beta2-hotfix2.apk").download(
URL("https://github.com/tom5079/Pupil/releases/download/4.2-beta2-hotfix2/Pupil-v4.2-beta2-hotfix2.apk").download(
File(System.getenv("USERPROFILE"), "Pupil.apk")
) { downloaded, fileSize ->
println("%.1f%%".format(downloaded*100.0/fileSize))

View File

@@ -16,9 +16,11 @@
package xyz.quaver.hitomi
import kotlinx.serialization.Serializable
import org.jsoup.Jsoup
import java.net.URLDecoder
@Serializable
data class Gallery(
val related: List<Int>,
val langList: List<Pair<String, String>>,

View File

@@ -26,7 +26,7 @@ fun doSearch(query: String, sortByPopularity: Boolean = false) : List<Int> {
val terms = query
.trim()
.replace(Regex("""^\?"""), "")
.toLowerCase()
.toLowerCase(Locale.US)
.split(Regex("\\s+"))
.map {
it.replace('_', ' ')