From 9ea55664b643ad6d9915bbaaa7a0c2cfafe74d0e Mon Sep 17 00:00:00 2001 From: Pupil Date: Fri, 17 Jan 2020 10:30:39 +0900 Subject: [PATCH 01/15] Might be a fix for #35 but it's quite dirty :v --- .../quaver/pupil/adapters/ReaderAdapter.kt | 27 ++++++++++++++++--- .../quaver/pupil/util/GalleryDownloader.kt | 2 +- app/src/main/res/layout/item_reader.xml | 19 ++++++++++--- 3 files changed, 39 insertions(+), 9 deletions(-) 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 36d29dc3..7107d437 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt @@ -21,12 +21,16 @@ package xyz.quaver.pupil.adapters import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.ImageView +import androidx.constraintlayout.widget.ConstraintLayout import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.RequestManager import com.bumptech.glide.load.engine.DiskCacheStrategy +import kotlinx.android.synthetic.main.item_reader.view.* +import kotlinx.coroutines.runBlocking +import xyz.quaver.hitomi.Reader import xyz.quaver.pupil.BuildConfig import xyz.quaver.pupil.R +import xyz.quaver.pupil.util.GalleryDownloader import xyz.quaver.pupil.util.getCachedGallery import java.io.File @@ -47,24 +51,39 @@ class ReaderAdapter(private val glide: RequestManager, } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.view as ImageView + holder.view as ConstraintLayout if (isFullScreen) holder.view.layoutParams.height = RecyclerView.LayoutParams.MATCH_PARENT else holder.view.layoutParams.height = RecyclerView.LayoutParams.WRAP_CONTENT + var reader: Reader? = null + with (GalleryDownloader[galleryID]?.reader) { + if (this?.isCompleted == true) + runBlocking { + reader = await() + } + } + glide .load(File(getCachedGallery(holder.view.context, galleryID), images[position])) .diskCacheStrategy(DiskCacheStrategy.NONE) .skipMemoryCache(true) .error(R.drawable.image_broken_variant) - .dontTransform() .apply { if (BuildConfig.CENSOR) override(5, 8) + else { + val galleryInfo = reader?.galleryInfo?.get(position) + + if (galleryInfo != null) { + (holder.view.image.layoutParams as ConstraintLayout.LayoutParams) + .dimensionRatio = "${galleryInfo.width}:${galleryInfo.height}" + } + } } - .into(holder.view) + .into(holder.view.image) } override fun getItemCount() = images.size 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 2eb58b08..ef87e4cc 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt @@ -84,7 +84,7 @@ class GalleryDownloader( onNotifyChangedHandler?.invoke(value) } - private val reader: Deferred? + val reader: Deferred? private var downloadJob: Job? = null private lateinit var notificationBuilder: NotificationCompat.Builder diff --git a/app/src/main/res/layout/item_reader.xml b/app/src/main/res/layout/item_reader.xml index de2c4621..b9b42d73 100644 --- a/app/src/main/res/layout/item_reader.xml +++ b/app/src/main/res/layout/item_reader.xml @@ -17,9 +17,20 @@ ~ along with this program. If not, see . --> - \ No newline at end of file + xmlns:app="http://schemas.android.com/apk/res-auto"> + + + + \ No newline at end of file From a39484b6ea16c78369e288213f96c239333bcb9f Mon Sep 17 00:00:00 2001 From: Pupil Date: Fri, 17 Jan 2020 10:30:39 +0900 Subject: [PATCH 02/15] Might be a fix for #35 but it's quite dirty :v --- .../quaver/pupil/adapters/ReaderAdapter.kt | 27 ++++++++++++++++--- .../quaver/pupil/util/GalleryDownloader.kt | 2 +- app/src/main/res/layout/item_reader.xml | 20 +++++++++++--- 3 files changed, 40 insertions(+), 9 deletions(-) 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 36d29dc3..7107d437 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt @@ -21,12 +21,16 @@ package xyz.quaver.pupil.adapters import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.ImageView +import androidx.constraintlayout.widget.ConstraintLayout import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.RequestManager import com.bumptech.glide.load.engine.DiskCacheStrategy +import kotlinx.android.synthetic.main.item_reader.view.* +import kotlinx.coroutines.runBlocking +import xyz.quaver.hitomi.Reader import xyz.quaver.pupil.BuildConfig import xyz.quaver.pupil.R +import xyz.quaver.pupil.util.GalleryDownloader import xyz.quaver.pupil.util.getCachedGallery import java.io.File @@ -47,24 +51,39 @@ class ReaderAdapter(private val glide: RequestManager, } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.view as ImageView + holder.view as ConstraintLayout if (isFullScreen) holder.view.layoutParams.height = RecyclerView.LayoutParams.MATCH_PARENT else holder.view.layoutParams.height = RecyclerView.LayoutParams.WRAP_CONTENT + var reader: Reader? = null + with (GalleryDownloader[galleryID]?.reader) { + if (this?.isCompleted == true) + runBlocking { + reader = await() + } + } + glide .load(File(getCachedGallery(holder.view.context, galleryID), images[position])) .diskCacheStrategy(DiskCacheStrategy.NONE) .skipMemoryCache(true) .error(R.drawable.image_broken_variant) - .dontTransform() .apply { if (BuildConfig.CENSOR) override(5, 8) + else { + val galleryInfo = reader?.galleryInfo?.get(position) + + if (galleryInfo != null) { + (holder.view.image.layoutParams as ConstraintLayout.LayoutParams) + .dimensionRatio = "${galleryInfo.width}:${galleryInfo.height}" + } + } } - .into(holder.view) + .into(holder.view.image) } override fun getItemCount() = images.size 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 660bb913..c440000f 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt @@ -84,7 +84,7 @@ class GalleryDownloader( onNotifyChangedHandler?.invoke(value) } - private val reader: Deferred? + val reader: Deferred? private var downloadJob: Job? = null private lateinit var notificationBuilder: NotificationCompat.Builder diff --git a/app/src/main/res/layout/item_reader.xml b/app/src/main/res/layout/item_reader.xml index 4a9ceffd..4b971651 100644 --- a/app/src/main/res/layout/item_reader.xml +++ b/app/src/main/res/layout/item_reader.xml @@ -17,9 +17,21 @@ ~ along with this program. If not, see . --> - \ No newline at end of file + xmlns:app="http://schemas.android.com/apk/res-auto"> + + + + \ No newline at end of file From c2043532200ec23f671afaf3a1810a86868332e7 Mon Sep 17 00:00:00 2001 From: Pupil Date: Fri, 24 Jan 2020 15:11:35 +0900 Subject: [PATCH 03/15] Added mirror selector Developing new Downloader under xyz.quaver.pupil.util.download --- .idea/kotlinCodeInsightSettings.xml | 7 + .idea/kotlinc.xml | 6 + app/build.gradle | 12 +- .../quaver/pupil/adapters/MirrorAdapter.kt | 85 +++++++++++ .../quaver/pupil/adapters/ReaderAdapter.kt | 8 +- .../java/xyz/quaver/pupil/ui/MainActivity.kt | 6 +- .../xyz/quaver/pupil/ui/ReaderActivity.kt | 28 ++-- .../pupil/ui/dialog/DefaultQueryDialog.kt | 69 +++++---- .../pupil/ui/dialog/DownloadLocationDialog.kt | 5 +- .../quaver/pupil/ui/dialog/MirrorDialog.kt | 97 ++++++++++++ .../pupil/ui/fragment/SettingsFragment.kt | 10 +- .../quaver/pupil/util/GalleryDownloader.kt | 24 ++- .../xyz/quaver/pupil/util/download/Cache.kt | 142 ++++++++++++++++++ .../pupil/util/download/DownloadWorker.kt | 100 ++++++++++++ .../quaver/pupil/util/download/Metadata.kt | 30 ++++ .../main/java/xyz/quaver/pupil/util/file.kt | 1 + app/src/main/res/drawable/menu.xml | 8 + .../main/res/layout/dialog_default_query.xml | 25 +-- app/src/main/res/layout/item_mirrors.xml | 47 ++++++ app/src/main/res/values-ja/strings.xml | 4 +- app/src/main/res/values-ko/strings.xml | 4 +- app/src/main/res/values/arrays.xml | 5 + app/src/main/res/values/strings.xml | 4 +- app/src/main/res/xml/root_preferences.xml | 8 +- .../java/xyz/quaver/pupil/ExampleUnitTest.kt | 4 +- .../main/java/xyz/quaver/hitomi/galleries.kt | 2 + .../main/java/xyz/quaver/hitomi/results.kt | 2 +- 27 files changed, 651 insertions(+), 92 deletions(-) create mode 100644 .idea/kotlinCodeInsightSettings.xml create mode 100644 .idea/kotlinc.xml create mode 100644 app/src/main/java/xyz/quaver/pupil/adapters/MirrorAdapter.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/ui/dialog/MirrorDialog.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/util/download/Cache.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/util/download/DownloadWorker.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/util/download/Metadata.kt create mode 100644 app/src/main/res/drawable/menu.xml create mode 100644 app/src/main/res/layout/item_mirrors.xml 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 @@ -