diff --git a/.idea/kotlinCodeInsightSettings.xml b/.idea/kotlinCodeInsightSettings.xml
new file mode 100644
index 00000000..71e404da
--- /dev/null
+++ b/.idea/kotlinCodeInsightSettings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 00000000..0dd4b354
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 2a35109e..a275c740 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -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'
diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/MirrorAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/MirrorAdapter.kt
new file mode 100644
index 00000000..273922e9
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/adapters/MirrorAdapter.kt
@@ -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 .
+ */
+
+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() {
+
+ 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) -> (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
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt
index 7107d437..90ae6755 100644
--- a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt
+++ b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt
@@ -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)
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
index b8f33aa6..a105a30d 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
@@ -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()
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt
index 6b657d69..d3d17ef0 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt
@@ -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) {
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialog.kt
index 09341979..125fa48b 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialog.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialog.kt
@@ -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)
- }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadLocationDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadLocationDialog.kt
index 3fcae22a..30854000 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadLocationDialog.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadLocationDialog.kt
@@ -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()
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)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/MirrorDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/MirrorDialog.kt
new file mode 100644
index 00000000..1a2f0351
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/MirrorDialog.kt
@@ -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 .
+ */
+
+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()
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt
index 58d8af96..2269b18c 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt
@@ -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
}
diff --git a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt
index c440000f..a6d202d6 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt
@@ -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()
diff --git a/app/src/main/java/xyz/quaver/pupil/util/download/Cache.kt b/app/src/main/java/xyz/quaver/pupil/util/download/Cache.kt
new file mode 100644
index 00000000..6950c037
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/util/download/Cache.kt
@@ -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 .
+ */
+
+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 {
+ 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))
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/util/download/DownloadWorker.kt b/app/src/main/java/xyz/quaver/pupil/util/download/DownloadWorker.kt
new file mode 100644
index 00000000..38d41a94
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/util/download/DownloadWorker.kt
@@ -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 .
+ */
+
+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()
+ 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)
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/util/download/Metadata.kt b/app/src/main/java/xyz/quaver/pupil/util/download/Metadata.kt
new file mode 100644
index 00000000..bf5249e2
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/util/download/Metadata.kt
@@ -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 .
+ */
+
+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? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/util/file.kt b/app/src/main/java/xyz/quaver/pupil/util/file.kt
index dd38730b..29d094f7 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/file.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/file.kt
@@ -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)
diff --git a/app/src/main/res/drawable/menu.xml b/app/src/main/res/drawable/menu.xml
new file mode 100644
index 00000000..bf79ca35
--- /dev/null
+++ b/app/src/main/res/drawable/menu.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_default_query.xml b/app/src/main/res/layout/dialog_default_query.xml
index 7e85a5d0..f907f3f4 100644
--- a/app/src/main/res/layout/dialog_default_query.xml
+++ b/app/src/main/res/layout/dialog_default_query.xml
@@ -18,21 +18,12 @@
-->
-
-
@@ -116,14 +107,4 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_mirrors.xml b/app/src/main/res/layout/item_mirrors.xml
new file mode 100644
index 00000000..e9566ed6
--- /dev/null
+++ b/app/src/main/res/layout/item_mirrors.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 089769bd..91b3f7ea 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -18,7 +18,7 @@
新しいアップデートがあります
注意
その他
- hiyobi.meからロード
+ ミラーサーバー
履歴を削除
履歴を削除しますか?
履歴数: %1$d
@@ -61,7 +61,7 @@
エクスポートエラーが発生しました
ダウンロード削除
ダウンロードしたギャラリーを全て削除します。\n実行しますか?
- ロード速度を向上させるため可能であればhiyobi.meからイメージロード
+ ミラーサーバからイメージをロード
お気に入り
ギャラリー番号で見る
エラーが発生しました
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 323652db..00a067e9 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -18,7 +18,6 @@
결과 없음
검색
기타
- hiyobi.me 사용
기록 삭제
기록을 삭제하시겠습니까?
기록 %1$d개 저장됨
@@ -61,7 +60,6 @@
내보내기 오류가 발생했습니다
다운로드 삭제
다운로드 된 만화를 모두 삭제합니다.\n계속하시겠습니까?
- 속도 향상을 위해 가능하면 hiyobi.me에서 이미지 로드
즐겨찾기
갤러리 번호로 열기
갤러리를 찾지 못했습니다
@@ -120,4 +118,6 @@
v%s
저해상도 이미지
로드 속도와 데이터 사용량을 줄이기 위해 저해상도 이미지를 로드
+ 미러 서버에서 이미지 로드
+ 미러 설정
\ No newline at end of file
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 99daa28e..0b62b1a3 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -57,4 +57,9 @@
- japanese|日本語
+
+ - HITOMI|hitomi.la
+ - HIYOBI|hiyobi.me
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 18e36b99..c2dc42a8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -151,8 +151,7 @@
Miscellaneous
- Use hiyobi.me
- Load images from hiyobi.me to improve loading speed (if available)
+ Load images from mirrors
Enable security mode
Enable security mode to make the screen invisible on recent app window
Dark mode
@@ -186,5 +185,6 @@
Filter BL
Filter Guro
Any
+ Mirrors
diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
index 6b7bc320..fe77a2f1 100644
--- a/app/src/main/res/xml/root_preferences.xml
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -66,10 +66,10 @@
-
+
.
*/
-@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))
diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/galleries.kt b/libpupil/src/main/java/xyz/quaver/hitomi/galleries.kt
index 1b88018e..63d15b93 100644
--- a/libpupil/src/main/java/xyz/quaver/hitomi/galleries.kt
+++ b/libpupil/src/main/java/xyz/quaver/hitomi/galleries.kt
@@ -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,
val langList: List>,
diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/results.kt b/libpupil/src/main/java/xyz/quaver/hitomi/results.kt
index af9a4787..9e200b70 100644
--- a/libpupil/src/main/java/xyz/quaver/hitomi/results.kt
+++ b/libpupil/src/main/java/xyz/quaver/hitomi/results.kt
@@ -26,7 +26,7 @@ fun doSearch(query: String, sortByPopularity: Boolean = false) : List {
val terms = query
.trim()
.replace(Regex("""^\?"""), "")
- .toLowerCase()
+ .toLowerCase(Locale.US)
.split(Regex("\\s+"))
.map {
it.replace('_', ' ')