@@ -7,7 +7,12 @@ import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import xyz.quaver.hiyobi.cookie
|
||||
import xyz.quaver.hiyobi.getReader
|
||||
import xyz.quaver.hiyobi.user_agent
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
@@ -35,6 +40,17 @@ class ExampleInstrumentedTest {
|
||||
|
||||
@Test
|
||||
fun test_doSearch() {
|
||||
val reader = getReader(1426382)
|
||||
|
||||
val data: ByteArray
|
||||
|
||||
with(URL(reader[0].url).openConnection() as HttpsURLConnection) {
|
||||
setRequestProperty("User-Agent", user_agent)
|
||||
setRequestProperty("Cookie", cookie)
|
||||
|
||||
data = inputStream.readBytes()
|
||||
}
|
||||
|
||||
Log.d("Pupil", data.size.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package="xyz.quaver.pupil">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -14,16 +15,6 @@
|
||||
android:theme="@style/AppTheme"
|
||||
android:name=".Pupil">
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="xyz.quaver.pupil.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths"/>
|
||||
</provider>
|
||||
|
||||
<activity android:name=".ReaderActivity"
|
||||
android:parentActivityName=".MainActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"/>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package xyz.quaver.pupil
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.preference.PreferenceManager
|
||||
import android.text.*
|
||||
import android.text.style.AlignmentSpan
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
@@ -15,16 +17,20 @@ import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.GravityCompat
|
||||
import com.arlib.floatingsearchview.FloatingSearchView
|
||||
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
|
||||
import com.arlib.floatingsearchview.util.view.SearchInputView
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.activity_main_content.*
|
||||
import kotlinx.android.synthetic.main.dialog_galleryblock.view.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.io.IOException
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
@@ -35,9 +41,12 @@ import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
||||
import xyz.quaver.pupil.types.TagSuggestion
|
||||
import xyz.quaver.pupil.util.*
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.roundToInt
|
||||
@@ -68,6 +77,8 @@ class MainActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
checkPermissions()
|
||||
|
||||
val preference = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
if (Locale.getDefault().language == "ko") {
|
||||
@@ -238,6 +249,11 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPermissions() {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 13489)
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
var prevP1 = 0
|
||||
main_appbar_layout.addOnOffsetChangedListener(
|
||||
@@ -280,12 +296,7 @@ class MainActivity : AppCompatActivity() {
|
||||
loadBlocks()
|
||||
}
|
||||
R.id.main_drawer_help -> {
|
||||
AlertDialog.Builder(this@MainActivity).apply {
|
||||
title = getString(R.string.help_dialog_title)
|
||||
setMessage(R.string.help_dialog_message)
|
||||
|
||||
setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||
}.show()
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.help))))
|
||||
}
|
||||
R.id.main_drawer_github -> {
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.github))))
|
||||
@@ -359,10 +370,9 @@ class MainActivity : AppCompatActivity() {
|
||||
isEnabled = !(adapter as GalleryBlockAdapter).completeFlag.get(galleryBlock.id, false)
|
||||
setOnClickListener {
|
||||
val downloader = GalleryDownloader.get(galleryBlock.id)
|
||||
if (downloader == null) {
|
||||
if (downloader == null)
|
||||
GalleryDownloader(context, galleryBlock, true).start()
|
||||
downloads.add(galleryBlock.id)
|
||||
} else {
|
||||
else {
|
||||
downloader.cancel()
|
||||
downloader.clearNotification()
|
||||
}
|
||||
@@ -377,11 +387,91 @@ class MainActivity : AppCompatActivity() {
|
||||
this?.cancelAndJoin()
|
||||
this?.clearNotification()
|
||||
}
|
||||
val cache = File(cacheDir, "imageCache/${galleryBlock.id}/images/")
|
||||
val cache = File(cacheDir, "imageCache/${galleryBlock.id}")
|
||||
val data = File(ContextCompat.getDataDir(this@MainActivity), "images/${galleryBlock.id}")
|
||||
cache.deleteRecursively()
|
||||
data.deleteRecursively()
|
||||
|
||||
downloads.remove(galleryBlock.id)
|
||||
|
||||
if (mode == Mode.DOWNLOAD) {
|
||||
runOnUiThread {
|
||||
cancelFetch()
|
||||
clearGalleries()
|
||||
fetchGalleries(query)
|
||||
loadBlocks()
|
||||
}
|
||||
}
|
||||
|
||||
(adapter as GalleryBlockAdapter).completeFlag.put(galleryBlock.id, false)
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
with(view.main_dialog_export) {
|
||||
val images = File(ContextCompat.getDataDir(this@MainActivity), "images/${galleryBlock.id}/images").let {
|
||||
when {
|
||||
it.exists() -> it
|
||||
else -> File(cacheDir, "imageCache/${galleryBlock.id}/images")
|
||||
}
|
||||
}
|
||||
isEnabled = images.exists()
|
||||
|
||||
setOnClickListener {
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val preference = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val zip = preference.getBoolean("export_zip", false)
|
||||
|
||||
if (zip) {
|
||||
var target = File(Environment.getExternalStorageDirectory(), "Pupil/${galleryBlock.id} ${galleryBlock.title}.zip")
|
||||
|
||||
try {
|
||||
target.createNewFile()
|
||||
} catch (e: IOException) {
|
||||
target = File(Environment.getExternalStorageDirectory(), "Pupil/${galleryBlock.id}.zip")
|
||||
|
||||
try {
|
||||
target.createNewFile()
|
||||
} catch (e: IOException) {
|
||||
Snackbar.make(main_layout, getString(R.string.main_export_error), Snackbar.LENGTH_LONG).show()
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream(target).use { targetStream ->
|
||||
ZipOutputStream(targetStream).use {zipStream ->
|
||||
images.listFiles().forEach {
|
||||
zipStream.putNextEntry(ZipEntry(it.name))
|
||||
|
||||
FileInputStream(it).use { fileStream ->
|
||||
fileStream.copyTo(zipStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var target = File(Environment.getExternalStorageDirectory(), "Pupil/${galleryBlock.id} ${galleryBlock.title}")
|
||||
|
||||
try {
|
||||
target.canonicalPath
|
||||
} catch (e: IOException) {
|
||||
target = File(Environment.getExternalStorageDirectory(), "Pupil/${galleryBlock.id}")
|
||||
|
||||
try {
|
||||
target.canonicalPath
|
||||
} catch (e: IOException) {
|
||||
Snackbar.make(main_layout, getString(R.string.main_export_error), Snackbar.LENGTH_LONG).show()
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
images.copyRecursively(target, true)
|
||||
}
|
||||
|
||||
Snackbar.make(main_layout, getString(R.string.main_export_complete), Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
dialog.dismiss()
|
||||
(adapter as GalleryBlockAdapter).completeFlag.put(galleryBlock.id, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package xyz.quaver.pupil
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@@ -26,6 +27,11 @@ import xyz.quaver.hitomi.GalleryBlock
|
||||
import xyz.quaver.pupil.adapters.ReaderAdapter
|
||||
import xyz.quaver.pupil.util.GalleryDownloader
|
||||
import xyz.quaver.pupil.util.ItemClickSupport
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
@@ -163,6 +169,7 @@ class ReaderActivity : AppCompatActivity() {
|
||||
onProgressHandler = {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
reader_progressbar.progress = it
|
||||
menu?.findItem(R.id.reader_menu_use_hiyobi)?.isVisible = true
|
||||
}
|
||||
}
|
||||
onDownloadedHandler = {
|
||||
@@ -274,6 +281,14 @@ class ReaderActivity : AppCompatActivity() {
|
||||
if (!downloader.download)
|
||||
downloader.clearNotification()
|
||||
}
|
||||
|
||||
reader_fab_export.setOnClickListener {
|
||||
downloader.export( {
|
||||
Snackbar.make(reader_layout, getString(R.string.main_export_complete), Snackbar.LENGTH_LONG).show()
|
||||
}, {
|
||||
Snackbar.make(reader_layout, getString(R.string.main_export_error), Snackbar.LENGTH_LONG).show()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun fullscreen(isFullscreen: Boolean) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import kotlinx.android.synthetic.main.dialog_default_query.view.*
|
||||
@@ -95,6 +96,33 @@ class SettingsActivity : AppCompatActivity() {
|
||||
true
|
||||
}
|
||||
}
|
||||
with(findPreference<Preference>("delete_downloads")) {
|
||||
this ?: return@with
|
||||
|
||||
val dir = File(ContextCompat.getDataDir(context), "images")
|
||||
|
||||
summary = getCacheSize(dir)
|
||||
|
||||
setOnPreferenceClickListener {
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(R.string.warning)
|
||||
setMessage(R.string.settings_clear_downloads_alert_message)
|
||||
setPositiveButton(android.R.string.yes) { _, _ ->
|
||||
if (dir.exists())
|
||||
dir.deleteRecursively()
|
||||
|
||||
val downloads = (activity!!.application as Pupil).downloads
|
||||
|
||||
downloads.clear()
|
||||
|
||||
summary = getCacheSize(dir)
|
||||
}
|
||||
setNegativeButton(android.R.string.no) { _, _ -> }
|
||||
}.show()
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
with(findPreference<Preference>("clear_history")) {
|
||||
this ?: return@with
|
||||
|
||||
|
||||
@@ -68,16 +68,30 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
||||
}
|
||||
|
||||
//Check cache
|
||||
val readerCache = File(context.cacheDir, "imageCache/${gallery.id}/reader.json")
|
||||
val imageCache = File(context.cacheDir, "imageCache/${gallery.id}/images")
|
||||
val readerCache = {
|
||||
File(ContextCompat.getDataDir(context), "images/${gallery.id}/reader.json").let {
|
||||
when {
|
||||
it.exists() -> it
|
||||
else -> File(context.cacheDir, "imageCache/${gallery.id}/reader.json")
|
||||
}
|
||||
}
|
||||
}
|
||||
val imageCache = {
|
||||
File(ContextCompat.getDataDir(context), "images/${gallery.id}/images").let {
|
||||
when {
|
||||
it.exists() -> it
|
||||
else -> File(context.cacheDir, "imageCache/${gallery.id}/images")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (readerCache.exists()) {
|
||||
if (readerCache.invoke().exists()) {
|
||||
val reader = Json(JsonConfiguration.Stable)
|
||||
.parse(ReaderItem.serializer().list, readerCache.readText())
|
||||
.parse(ReaderItem.serializer().list, readerCache.invoke().readText())
|
||||
|
||||
with(galleryblock_progressbar) {
|
||||
max = reader.size
|
||||
progress = imageCache.list()?.size ?: 0
|
||||
progress = imageCache.invoke().list()?.size ?: 0
|
||||
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
@@ -89,9 +103,9 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
||||
val refresh = Timer(false).schedule(0, 1000) {
|
||||
post {
|
||||
with(view.galleryblock_progressbar) {
|
||||
progress = imageCache.list()?.size ?: 0
|
||||
progress = imageCache.invoke().list()?.size ?: 0
|
||||
|
||||
if (!readerCache.exists()) {
|
||||
if (!readerCache.invoke().exists()) {
|
||||
visibility = View.GONE
|
||||
max = 0
|
||||
progress = 0
|
||||
@@ -100,7 +114,7 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
||||
} else {
|
||||
if (visibility == View.GONE) {
|
||||
val reader = Json(JsonConfiguration.Stable)
|
||||
.parse(ReaderItem.serializer().list, readerCache.readText())
|
||||
.parse(ReaderItem.serializer().list, readerCache.invoke().readText())
|
||||
max = reader.size
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
@@ -4,10 +4,13 @@ import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.util.SparseArray
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.app.TaskStackBuilder
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.io.IOException
|
||||
@@ -15,12 +18,18 @@ import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import kotlinx.serialization.list
|
||||
import xyz.quaver.hitomi.*
|
||||
import xyz.quaver.hiyobi.cookie
|
||||
import xyz.quaver.hiyobi.user_agent
|
||||
import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.ReaderActivity
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.concurrent.schedule
|
||||
@@ -31,14 +40,27 @@ class GalleryDownloader(
|
||||
_notify: Boolean = false
|
||||
) : ContextWrapper(base) {
|
||||
|
||||
private val downloads = (applicationContext as Pupil).downloads
|
||||
var useHiyobi = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("use_hiyobi", false)
|
||||
|
||||
var download: Boolean = false
|
||||
set(value) {
|
||||
if (value) {
|
||||
field = true
|
||||
notificationManager.notify(galleryBlock.id, notificationBuilder.build())
|
||||
|
||||
val data = File(ContextCompat.getDataDir(this), "images/${galleryBlock.id}")
|
||||
val cache = File(cacheDir, "imageCache/${galleryBlock.id}")
|
||||
|
||||
if (cache.exists() && !data.exists()) {
|
||||
cache.copyRecursively(data, true)
|
||||
cache.deleteRecursively()
|
||||
}
|
||||
|
||||
if (!reader.isActive && downloadJob?.isActive != true)
|
||||
field = false
|
||||
|
||||
downloads.add(galleryBlock.id)
|
||||
} else {
|
||||
field = false
|
||||
}
|
||||
@@ -70,11 +92,14 @@ class GalleryDownloader(
|
||||
download = _notify
|
||||
val json = Json(JsonConfiguration.Stable)
|
||||
val serializer = ReaderItem.serializer().list
|
||||
val preference = PreferenceManager.getDefaultSharedPreferences(this@GalleryDownloader)
|
||||
val useHiyobi = preference.getBoolean("use_hiyobi", false)
|
||||
|
||||
//Check cache
|
||||
val cache = File(cacheDir, "imageCache/${galleryBlock.id}/reader.json")
|
||||
val cache = File(ContextCompat.getDataDir(this@GalleryDownloader), "images/${galleryBlock.id}/reader.json").let {
|
||||
when {
|
||||
it.exists() -> it
|
||||
else -> File(cacheDir, "imageCache/${galleryBlock.id}/reader.json")
|
||||
}
|
||||
}
|
||||
|
||||
if (cache.exists()) {
|
||||
val cached = json.parse(serializer, cache.readText())
|
||||
@@ -90,7 +115,10 @@ class GalleryDownloader(
|
||||
useHiyobi -> {
|
||||
xyz.quaver.hiyobi.getReader(galleryBlock.id).let {
|
||||
when {
|
||||
it.isEmpty() -> getReader(galleryBlock.id)
|
||||
it.isEmpty() -> {
|
||||
useHiyobi = false
|
||||
getReader(galleryBlock.id)
|
||||
}
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
@@ -148,12 +176,21 @@ class GalleryDownloader(
|
||||
val name = "$index".padStart(4, '0')
|
||||
val ext = url.split('.').last()
|
||||
|
||||
val cache = File(cacheDir, "/imageCache/${galleryBlock.id}/images/$name.$ext")
|
||||
val cache = File(ContextCompat.getDataDir(this@GalleryDownloader), "images/${galleryBlock.id}/images/$name.$ext").let {
|
||||
when {
|
||||
it.exists() -> it
|
||||
else -> File(cacheDir, "/imageCache/${galleryBlock.id}/images/$name.$ext")
|
||||
}
|
||||
}
|
||||
|
||||
if (!cache.exists())
|
||||
try {
|
||||
with(URL(url).openConnection() as HttpsURLConnection) {
|
||||
setRequestProperty("Referer", getReferer(galleryBlock.id))
|
||||
if (useHiyobi) {
|
||||
setRequestProperty("User-Agent", user_agent)
|
||||
setRequestProperty("Cookie", cookie)
|
||||
} else
|
||||
setRequestProperty("Referer", getReferer(galleryBlock.id))
|
||||
|
||||
if (!cache.parentFile.exists())
|
||||
cache.parentFile.mkdirs()
|
||||
@@ -189,8 +226,35 @@ class GalleryDownloader(
|
||||
.setContentText(getString(R.string.reader_notification_complete))
|
||||
.setProgress(0, 0, false)
|
||||
|
||||
if (download)
|
||||
notificationManager.notify(galleryBlock.id, notificationBuilder.build())
|
||||
if (download) {
|
||||
File(cacheDir, "imageCache/${galleryBlock.id}").let {
|
||||
if (it.exists()) {
|
||||
it.copyRecursively(
|
||||
File(ContextCompat.getDataDir(this@GalleryDownloader), "images/${galleryBlock.id}"),
|
||||
true
|
||||
)
|
||||
it.deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
||||
val preference = PreferenceManager.getDefaultSharedPreferences(this@GalleryDownloader)
|
||||
val autoExport = preference.getBoolean("auto_export", false)
|
||||
|
||||
if (autoExport) {
|
||||
export({
|
||||
notificationManager.notify(galleryBlock.id, notificationBuilder.build())
|
||||
}, {
|
||||
notificationBuilder
|
||||
.setContentTitle(galleryBlock.title)
|
||||
.setContentText(getString(R.string.main_export_error))
|
||||
.setProgress(0, 0, false)
|
||||
|
||||
notificationManager.notify(galleryBlock.id, notificationBuilder.build())
|
||||
})
|
||||
} else {
|
||||
notificationManager.notify(galleryBlock.id, notificationBuilder.build())
|
||||
}
|
||||
}
|
||||
|
||||
download = false
|
||||
}
|
||||
@@ -207,6 +271,8 @@ class GalleryDownloader(
|
||||
|
||||
suspend fun cancelAndJoin() {
|
||||
downloadJob?.cancelAndJoin()
|
||||
|
||||
remove(galleryBlock.id)
|
||||
}
|
||||
|
||||
fun invokeOnReaderLoaded() {
|
||||
@@ -243,4 +309,69 @@ class GalleryDownloader(
|
||||
notificationManager = NotificationManagerCompat.from(this)
|
||||
}
|
||||
|
||||
fun export(onSuccess: (() -> Unit)? = null, onError: (() -> Unit)? = null) {
|
||||
val images = File(ContextCompat.getDataDir(this), "images/${galleryBlock.id}/images").let {
|
||||
when {
|
||||
it.exists() -> it
|
||||
else -> File(cacheDir, "imageCache/${galleryBlock.id}/images")
|
||||
}
|
||||
}
|
||||
|
||||
if (!images.exists())
|
||||
return
|
||||
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val preference = PreferenceManager.getDefaultSharedPreferences(this@GalleryDownloader)
|
||||
val zip = preference.getBoolean("export_zip", false)
|
||||
|
||||
if (zip) {
|
||||
var target = File(Environment.getExternalStorageDirectory(), "Pupil/${galleryBlock.id} ${galleryBlock.title}.zip")
|
||||
|
||||
try {
|
||||
target.createNewFile()
|
||||
} catch (e: IOException) {
|
||||
target = File(Environment.getExternalStorageDirectory(), "Pupil/${galleryBlock.id}.zip")
|
||||
|
||||
try {
|
||||
target.createNewFile()
|
||||
} catch (e: IOException) {
|
||||
onError?.invoke()
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream(target).use { targetStream ->
|
||||
ZipOutputStream(targetStream).use { zipStream ->
|
||||
images.listFiles().forEach {
|
||||
zipStream.putNextEntry(ZipEntry(it.name))
|
||||
|
||||
FileInputStream(it).use { fileStream ->
|
||||
fileStream.copyTo(zipStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var target = File(Environment.getExternalStorageDirectory(), "Pupil/${galleryBlock.id} ${galleryBlock.title}")
|
||||
|
||||
try {
|
||||
target.canonicalPath
|
||||
} catch (e: IOException) {
|
||||
target = File(Environment.getExternalStorageDirectory(), "Pupil/${galleryBlock.id}")
|
||||
|
||||
try {
|
||||
target.canonicalPath
|
||||
} catch (e: IOException) {
|
||||
onError?.invoke()
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
images.copyRecursively(target, true)
|
||||
}
|
||||
|
||||
onSuccess?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
8
app/src/main/res/drawable/ic_export.xml
Normal file
8
app/src/main/res/drawable/ic_export.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<!-- drawable/export.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="M23,12L19,8V11H10V13H19V16M1,18V6C1,4.89 1.9,4 3,4H15A2,2 0 0,1 17,6V9H15V6H3V18H15V15H17V18A2,2 0 0,1 15,20H3A2,2 0 0,1 1,18Z" />
|
||||
</vector>
|
||||
@@ -51,6 +51,14 @@
|
||||
app:fab_label="@string/reader_fab_download"
|
||||
app:fab_size="mini"/>
|
||||
|
||||
<com.github.clans.fab.FloatingActionButton
|
||||
android:id="@+id/reader_fab_export"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_export"
|
||||
app:fab_label="@string/main_dialog_export"
|
||||
app:fab_size="mini"/>
|
||||
|
||||
<com.github.clans.fab.FloatingActionButton
|
||||
android:id="@+id/reader_fab_fullscreen"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -20,4 +20,12 @@
|
||||
android:text="@string/main_dialog_delete"
|
||||
app:layout_constraintTop_toBottomOf="@id/main_dialog_download"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/main_dialog_export"
|
||||
style="?borderlessButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/main_dialog_export"
|
||||
app:layout_constraintTop_toBottomOf="@id/main_dialog_delete"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -2,6 +2,12 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:id="@+id/reader_menu_use_hiyobi"
|
||||
android:title=""
|
||||
android:icon="@drawable/ic_hiyobi"
|
||||
app:showAsAction="ifRoom"
|
||||
android:visible="false"/>
|
||||
|
||||
<item android:id="@+id/reader_menu_page_indicator"
|
||||
android:title="@string/page_indicator_placeholder"
|
||||
app:showAsAction="always|withText"/>
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<string name="search_hint_with_page">ギャラリー検索</string>
|
||||
<string name="settings_cache_title">キャッシュ</string>
|
||||
<string name="settings_clear_image_cache">イメージキャッシュクリア</string>
|
||||
<string name="settings_clear_cache_alert_message">キャッシュをクリアするとイメージのロード速度に影響を与えます。実行しますか?</string>
|
||||
<string name="settings_clear_cache_summary">キャッシュサイズ: %1$d%2$s</string>
|
||||
<string name="settings_clear_cache_alert_message">キャッシュをクリアするとイメージのロード速度に影響を与えます。\n実行しますか?</string>
|
||||
<string name="settings_clear_cache_summary">サイズ: %1$d%2$s</string>
|
||||
<string name="settings_default_query">デフォルトキーワード</string>
|
||||
<string name="settings_galleries_per_page">一回にロードするギャラリー数</string>
|
||||
<string name="settings_search_title">検索設定</string>
|
||||
@@ -19,7 +19,6 @@
|
||||
<string name="update_title">新しいアップデートがあります</string>
|
||||
<string name="warning">注意</string>
|
||||
<string name="settings_miscellaneous_title">その他</string>
|
||||
<string name="settings_use_hiyobi_summary">ロード速度を向上させるためhiyobi.meからイメージロード</string>
|
||||
<string name="settings_use_hiyobi_title">hiyobi.meからロード</string>
|
||||
<string name="settings_clear_history">履歴を削除</string>
|
||||
<string name="settings_clear_history_alert_message">履歴を削除しますか?</string>
|
||||
@@ -41,8 +40,6 @@
|
||||
<string name="main_drawer_group_contact_help">ヘルプ</string>
|
||||
<string name="main_drawer_group_contact_github">Github</string>
|
||||
<string name="main_drawer_group_contact_email">メールを送る</string>
|
||||
<string name="help_dialog_title">準備中</string>
|
||||
<string name="help_dialog_message">準備中です。</string>
|
||||
<string name="reader_fab_fullscreen">フルスクリーン</string>
|
||||
<string name="channel_download">ダウンロード</string>
|
||||
<string name="channel_download_description">ダウンロードの進行を通知</string>
|
||||
@@ -59,4 +56,15 @@
|
||||
<string name="main_move">%1$dページへ移動</string>
|
||||
<string name="https_block_alert_title">(Korean only)</string>
|
||||
<string name="https_block_alert">(Korean only)</string>
|
||||
<string name="main_dialog_export">ギャラリーエクスポート</string>
|
||||
<string name="main_export_complete">エクスポート完了</string>
|
||||
<string name="main_export_open_folder">フォルダを開く</string>
|
||||
<string name="main_export_error">エクスポートエラーが発生しました</string>
|
||||
<string name="settings_export_zip_title">zipエクスポート</string>
|
||||
<string name="settings_export_zip_summary">イメージフォルダの代わりzipファイルでエクスポート</string>
|
||||
<string name="settings_auto_export_title">自動エクスポート</string>
|
||||
<string name="settings_auto_export_summary">ダウンロード完了後自動的にエクスポート</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>
|
||||
</resources>
|
||||
@@ -7,8 +7,8 @@
|
||||
<string name="search_hint_with_page">갤러리 검색</string>
|
||||
<string name="settings_default_query">기본 검색어</string>
|
||||
<string name="settings_clear_image_cache">이미지 캐시 정리하기</string>
|
||||
<string name="settings_clear_cache_alert_message">캐시를 정리하면 이미지 로딩속도가 느려질 수 있습니다. 계속하시겠습니까?</string>
|
||||
<string name="settings_clear_cache_summary">현재 캐시 사용량: %1$d%2$s</string>
|
||||
<string name="settings_clear_cache_alert_message">캐시를 정리하면 이미지 로딩속도가 느려질 수 있습니다.\n계속하시겠습니까?</string>
|
||||
<string name="settings_clear_cache_summary">사용량: %1$d%2$s</string>
|
||||
<string name="settings_galleries_per_page">한 번에 로드할 갤러리 수</string>
|
||||
<string name="settings_search_title">검색 설정</string>
|
||||
<string name="settings_title">설정</string>
|
||||
@@ -19,7 +19,6 @@
|
||||
<string name="main_search">검색</string>
|
||||
<string name="settings_cache_title">캐시</string>
|
||||
<string name="settings_miscellaneous_title">기타</string>
|
||||
<string name="settings_use_hiyobi_summary">속도 향상을 위해 가능하면 hiyobi.me에서 이미지 로드</string>
|
||||
<string name="settings_use_hiyobi_title">hiyobi.me 사용</string>
|
||||
<string name="settings_clear_history">기록 삭제</string>
|
||||
<string name="settings_clear_history_alert_message">기록을 삭제하시겠습니까?</string>
|
||||
@@ -41,8 +40,6 @@
|
||||
<string name="main_drawer_group_contact_help">도움말</string>
|
||||
<string name="main_drawer_group_contact_homepage">홈페이지</string>
|
||||
<string name="main_drawer_group_contact_title">문의</string>
|
||||
<string name="help_dialog_title">준비 중</string>
|
||||
<string name="help_dialog_message">준비중입니다.</string>
|
||||
<string name="reader_fab_fullscreen">전체 화면</string>
|
||||
<string name="channel_download">다운로드</string>
|
||||
<string name="channel_download_description">다운로드 상태 알림</string>
|
||||
@@ -59,4 +56,15 @@
|
||||
<string name="main_move">%1$d 페이지로 이동</string>
|
||||
<string name="https_block_alert_title">접속 불가 현상 안내</string>
|
||||
<string name="https_block_alert">최근 https 차단으로 접속이 안 되는 경우가 발생하고 있습니다\n이 경우 플레이스토어에서 SNIper앱을 이용하시면 정상이용이 가능합니다.</string>
|
||||
<string name="main_dialog_export">갤러리 내보내기</string>
|
||||
<string name="main_export_complete">내보내기 완료</string>
|
||||
<string name="main_export_open_folder">폴더 열기</string>
|
||||
<string name="main_export_error">내보내기 오류가 발생했습니다</string>
|
||||
<string name="settings_export_zip_title">zip 파일로 내보내기</string>
|
||||
<string name="settings_export_zip_summary">이미지 폴더 대신 zip 파일로 내보내기</string>
|
||||
<string name="settings_auto_export_title">자동 내보내기</string>
|
||||
<string name="settings_auto_export_summary">다운로드가 끝난 후 자동 내보내기</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>
|
||||
</resources>
|
||||
@@ -5,6 +5,7 @@
|
||||
<string name="release_name" translatable="false">Pupil-v(\\d+\\.)+\\d+\\.apk</string>
|
||||
|
||||
<string name="home_page" translatable="false">https://tom5079.github.io/Pupil</string>
|
||||
<string name="help" translatable="false">https://tom5079.github.io/Pupil/2019/06/02/manual-kr.html</string>
|
||||
<string name="github" translatable="false">https://github.com/tom5079/Pupil-issue/issues/new/choose</string>
|
||||
<string name="email" translatable="false">mailto:pupil.hentai@gmail.com</string>
|
||||
|
||||
@@ -46,9 +47,11 @@
|
||||
<string name="main_move">Move to page %1$d</string>
|
||||
|
||||
<string name="main_dialog_delete">Delete this gallery</string>
|
||||
<string name="main_dialog_export">Export this gallery</string>
|
||||
|
||||
<string name="help_dialog_title">WIP</string>
|
||||
<string name="help_dialog_message">While in progress!</string>
|
||||
<string name="main_export_complete">Export completed</string>
|
||||
<string name="main_export_open_folder">Open Folder</string>
|
||||
<string name="main_export_error">Error occurred during export</string>
|
||||
|
||||
<string name="update_title">Update available</string>
|
||||
<string name="update_download_started">Download started</string>
|
||||
@@ -76,14 +79,20 @@
|
||||
<string name="settings_default_query">Default query</string>
|
||||
<string name="settings_cache_title">Cache</string>
|
||||
<string name="settings_clear_image_cache">Clear image cache</string>
|
||||
<string name="settings_clear_cache_summary">Currently using %1$d%2$s of cache</string>
|
||||
<string name="settings_clear_cache_alert_message">Deleting cache can affect image loading speed. Do you want to continue?</string>
|
||||
<string name="settings_clear_cache_summary">Currently using %1$d%2$s</string>
|
||||
<string name="settings_clear_cache_alert_message">Deleting cache can affect image loading speed.\nDo you want to continue?</string>
|
||||
<string name="settings_clear_downloads">Clear downloads</string>
|
||||
<string name="settings_clear_downloads_alert_message">Delete all downloaded galleries.\nDo you want to continue?</string>
|
||||
<string name="settings_clear_history">Clear history</string>
|
||||
<string name="settings_clear_history_alert_message">Do you want to clear histories?</string>
|
||||
<string name="settings_clear_history_summary">%1$d histories saved</string>
|
||||
<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_export_zip_title">Export zip</string>
|
||||
<string name="settings_export_zip_summary">Export to zip instead of image folder</string>
|
||||
<string name="settings_auto_export_title">Auto Export</string>
|
||||
<string name="settings_auto_export_summary">Automatically exports galleries when download is finished</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>
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<external-path name="Download" path="Download"/>
|
||||
</paths>
|
||||
@@ -28,6 +28,10 @@
|
||||
app:title="@string/settings_clear_image_cache"
|
||||
app:key="delete_image_cache"/>
|
||||
|
||||
<Preference
|
||||
app:title="@string/settings_clear_downloads"
|
||||
app:key="delete_downloads"/>
|
||||
|
||||
<Preference
|
||||
app:title="@string/settings_clear_history"
|
||||
app:key="clear_history"/>
|
||||
@@ -42,6 +46,16 @@
|
||||
app:title="@string/settings_use_hiyobi_title"
|
||||
app:summary="@string/settings_use_hiyobi_summary"/>
|
||||
|
||||
<SwitchPreference
|
||||
app:key="export_zip"
|
||||
app:title="@string/settings_export_zip_title"
|
||||
app:summary="@string/settings_export_zip_summary"/>
|
||||
|
||||
<SwitchPreference
|
||||
app:key="auto_export"
|
||||
app:title="@string/settings_auto_export_title"
|
||||
app:summary="@string/settings_auto_export_summary"/>
|
||||
|
||||
<SwitchPreference
|
||||
app:key="security_mode"
|
||||
app:title="@string/settings_security_mode_title"
|
||||
|
||||
@@ -13,6 +13,12 @@ const val hiyobi = "xn--9w3b15m8vo.asia"
|
||||
const val user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36"
|
||||
|
||||
var cookie: String = ""
|
||||
get() {
|
||||
if (field.isEmpty())
|
||||
field = renewCookie()
|
||||
|
||||
return field
|
||||
}
|
||||
|
||||
fun renewCookie() : String {
|
||||
val url = "https://$hiyobi/"
|
||||
@@ -32,9 +38,6 @@ fun renewCookie() : String {
|
||||
fun getReader(galleryId: Int) : Reader {
|
||||
val url = "https://$hiyobi/data/json/${galleryId}_list.json"
|
||||
|
||||
if (cookie.isEmpty())
|
||||
cookie = renewCookie()
|
||||
|
||||
try {
|
||||
val json = Json(JsonConfiguration.Stable).parseJson(
|
||||
with(URL(url).openConnection() as HttpsURLConnection) {
|
||||
|
||||
@@ -14,8 +14,7 @@ class UnitTest {
|
||||
fun test_nozomi() {
|
||||
val nozomi = fetchNozomi(start = 0, count = 5)
|
||||
|
||||
for (n in nozomi)
|
||||
println(n)
|
||||
nozomi.first
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -62,6 +61,8 @@ class UnitTest {
|
||||
|
||||
@Test
|
||||
fun test_hiyobi() {
|
||||
print(xyz.quaver.hiyobi.getReader(1415416).size)
|
||||
xyz.quaver.hiyobi.getReader(1415416).forEach {
|
||||
println(it.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user