Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
547b6e8e3b | ||
|
|
d88ac27e72 | ||
|
|
e551a40d08 | ||
|
|
e810abe33a | ||
|
|
6172a73719 | ||
|
|
7455e68a45 | ||
|
|
748495ca64 | ||
|
|
f6d9c7f550 |
@@ -19,8 +19,8 @@ android {
|
|||||||
applicationId "xyz.quaver.pupil"
|
applicationId "xyz.quaver.pupil"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 33
|
versionCode 37
|
||||||
versionName "5.0"
|
versionName "5.4"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":33,"versionName":"5.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
|
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":37,"versionName":"5.4","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
tools:replace="android:theme">
|
tools:replace="android:theme">
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:authorities="${applicationId}.fileprovider"
|
android:authorities="${applicationId}.provider"
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
|
|||||||
@@ -49,7 +49,11 @@ class Pupil : MultiDexApplication() {
|
|||||||
histories = Histories(File(ContextCompat.getDataDir(this), "histories.json"))
|
histories = Histories(File(ContextCompat.getDataDir(this), "histories.json"))
|
||||||
favorites = Histories(File(ContextCompat.getDataDir(this), "favorites.json"))
|
favorites = Histories(File(ContextCompat.getDataDir(this), "favorites.json"))
|
||||||
|
|
||||||
val download = preference.getString("dl_location", null)
|
val download = try {
|
||||||
|
preference.getString("dl_location", null)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
preference.edit().remove("dl_location").apply()
|
||||||
|
}
|
||||||
|
|
||||||
if (download == null) {
|
if (download == null) {
|
||||||
val default = ContextCompat.getExternalFilesDirs(this, null)[0]
|
val default = ContextCompat.getExternalFilesDirs(this, null)[0]
|
||||||
|
|||||||
@@ -129,7 +129,12 @@ class GalleryBlockAdapter(context: Context, private val galleries: List<GalleryB
|
|||||||
})
|
})
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val thumbnail = Base64.decode(Cache(context).getThumbnail(galleryBlock.id), Base64.DEFAULT)
|
val thumbnail = Cache(context).getThumbnail(galleryBlock.id).let {
|
||||||
|
if (it != null)
|
||||||
|
Base64.decode(it, Base64.DEFAULT)
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
glide
|
glide
|
||||||
.load(thumbnail)
|
.load(thumbnail)
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.crashlytics.android.Crashlytics
|
||||||
|
import io.fabric.sdk.android.Fabric
|
||||||
import kotlinx.android.synthetic.main.item_reader.view.*
|
import kotlinx.android.synthetic.main.item_reader.view.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -108,17 +109,13 @@ class ReaderAdapter(private val context: Context,
|
|||||||
val progress = DownloadWorker.getInstance(context).progress[galleryID]?.get(position)
|
val progress = DownloadWorker.getInstance(context).progress[galleryID]?.get(position)
|
||||||
|
|
||||||
if (progress?.isNaN() == true) {
|
if (progress?.isNaN() == true) {
|
||||||
|
|
||||||
|
if (Fabric.isInitialized())
|
||||||
|
Crashlytics.logException(DownloadWorker.getInstance(context).exception[galleryID]?.get(position))
|
||||||
|
|
||||||
glide
|
glide
|
||||||
.load(R.drawable.image_broken_variant)
|
.load(R.drawable.image_broken_variant)
|
||||||
.into(holder.view.image)
|
.into(holder.view.image)
|
||||||
Snackbar
|
|
||||||
.make(
|
|
||||||
holder.view,
|
|
||||||
DownloadWorker.getInstance(context).exception[galleryID]!![position]?.message
|
|
||||||
?: context.getText(R.string.default_error_msg),
|
|
||||||
Snackbar.LENGTH_INDEFINITE
|
|
||||||
)
|
|
||||||
.show()
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -969,11 +969,11 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mode.DOWNLOAD -> {
|
Mode.DOWNLOAD -> {
|
||||||
val downloads = getDownloadDirectory(this@MainActivity)?.listFiles()?.filter { file ->
|
val downloads = getDownloadDirectory(this@MainActivity).listFiles().filter { file ->
|
||||||
file.isDirectory && (file.name!!.toIntOrNull() != null) && file.findFile(".metadata") != null
|
file.isDirectory && (file.name!!.toIntOrNull() != null) && file.findFile(".metadata") != null
|
||||||
}?.map {
|
}.map {
|
||||||
it.name!!.toInt()
|
it.name!!.toInt()
|
||||||
}?: listOf()
|
}
|
||||||
|
|
||||||
when {
|
when {
|
||||||
query.isEmpty() -> downloads.apply {
|
query.isEmpty() -> downloads.apply {
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, findViewById(android.R.id.content), false)
|
val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, findViewById(android.R.id.content), false)
|
||||||
with(view.dialog_number_picker) {
|
with(view.dialog_number_picker) {
|
||||||
minValue=1
|
minValue=1
|
||||||
maxValue=reader_recyclerview.adapter?.itemCount ?: 0
|
maxValue=reader_recyclerview?.adapter?.itemCount ?: 0
|
||||||
value=currentPage
|
value=currentPage
|
||||||
}
|
}
|
||||||
val dialog = AlertDialog.Builder(this).apply {
|
val dialog = AlertDialog.Builder(this).apply {
|
||||||
@@ -196,7 +196,7 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
(reader_recyclerview.adapter as ReaderAdapter).timer.cancel()
|
(reader_recyclerview?.adapter as? ReaderAdapter)?.timer?.cancel()
|
||||||
|
|
||||||
if (!Cache(this).isDownloading(galleryID))
|
if (!Cache(this).isDownloading(galleryID))
|
||||||
DownloadWorker.getInstance(this@ReaderActivity).cancel(galleryID)
|
DownloadWorker.getInstance(this@ReaderActivity).cancel(galleryID)
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class SettingsFragment :
|
|||||||
}.show()
|
}.show()
|
||||||
}
|
}
|
||||||
"delete_downloads" -> {
|
"delete_downloads" -> {
|
||||||
val dir = getDownloadDirectory(context)!!
|
val dir = getDownloadDirectory(context)
|
||||||
|
|
||||||
AlertDialog.Builder(context).apply {
|
AlertDialog.Builder(context).apply {
|
||||||
setTitle(R.string.warning)
|
setTitle(R.string.warning)
|
||||||
@@ -150,7 +150,12 @@ class SettingsFragment :
|
|||||||
"backup" -> {
|
"backup" -> {
|
||||||
File(ContextCompat.getDataDir(context), "favorites.json").copyTo(
|
File(ContextCompat.getDataDir(context), "favorites.json").copyTo(
|
||||||
context,
|
context,
|
||||||
getDownloadDirectory(context)?.createFile("null", "favorites.json")!!
|
getDownloadDirectory(context).let {
|
||||||
|
if (it.findFile("favorites.json") != null)
|
||||||
|
it
|
||||||
|
else
|
||||||
|
it.createFile("null", "favorites.json")!!
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
Snackbar.make(this@SettingsFragment.listView, R.string.settings_backup_snackbar, Snackbar.LENGTH_LONG)
|
Snackbar.make(this@SettingsFragment.listView, R.string.settings_backup_snackbar, Snackbar.LENGTH_LONG)
|
||||||
@@ -192,8 +197,7 @@ class SettingsFragment :
|
|||||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||||
when (key) {
|
when (key) {
|
||||||
"dl_location" -> {
|
"dl_location" -> {
|
||||||
findPreference<Preference>(key)?.summary =
|
findPreference<Preference>(key)?.summary = getDownloadDirectory(context!!).uri.path
|
||||||
FileUtils.getPath(context, getDownloadDirectory(context!!)?.uri)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,7 +234,7 @@ class SettingsFragment :
|
|||||||
onPreferenceClickListener = this@SettingsFragment
|
onPreferenceClickListener = this@SettingsFragment
|
||||||
}
|
}
|
||||||
"delete_downloads" -> {
|
"delete_downloads" -> {
|
||||||
val dir = getDownloadDirectory(context)!!
|
val dir = getDownloadDirectory(context)
|
||||||
summary = getDirSize(dir)
|
summary = getDirSize(dir)
|
||||||
|
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
onPreferenceClickListener = this@SettingsFragment
|
||||||
@@ -242,7 +246,7 @@ class SettingsFragment :
|
|||||||
onPreferenceClickListener = this@SettingsFragment
|
onPreferenceClickListener = this@SettingsFragment
|
||||||
}
|
}
|
||||||
"dl_location" -> {
|
"dl_location" -> {
|
||||||
summary = FileUtils.getPath(context, getDownloadDirectory(context)?.uri)
|
summary = getDownloadDirectory(context).uri.path
|
||||||
|
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
onPreferenceClickListener = this@SettingsFragment
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,11 +23,15 @@ import android.content.ContextWrapper
|
|||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.ImplicitReflectionSerializer
|
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.parse
|
import kotlinx.serialization.parse
|
||||||
import kotlinx.serialization.stringify
|
import kotlinx.serialization.stringify
|
||||||
|
import xyz.quaver.Code
|
||||||
import xyz.quaver.hitomi.GalleryBlock
|
import xyz.quaver.hitomi.GalleryBlock
|
||||||
import xyz.quaver.hitomi.Reader
|
import xyz.quaver.hitomi.Reader
|
||||||
import xyz.quaver.pupil.util.*
|
import xyz.quaver.pupil.util.*
|
||||||
@@ -41,7 +45,7 @@ class Cache(context: Context) : ContextWrapper(context) {
|
|||||||
// Search in this order
|
// Search in this order
|
||||||
// Download -> Cache
|
// Download -> Cache
|
||||||
fun getCachedGallery(galleryID: Int) : DocumentFile? {
|
fun getCachedGallery(galleryID: Int) : DocumentFile? {
|
||||||
var file = getDownloadDirectory(this)?.findFile(galleryID.toString())
|
var file = getDownloadDirectory(this).findFile(galleryID.toString())
|
||||||
|
|
||||||
if (file?.exists() == true)
|
if (file?.exists() == true)
|
||||||
return file
|
return file
|
||||||
@@ -107,17 +111,21 @@ class Cache(context: Context) : ContextWrapper(context) {
|
|||||||
suspend fun getGalleryBlock(galleryID: Int): GalleryBlock? {
|
suspend fun getGalleryBlock(galleryID: Int): GalleryBlock? {
|
||||||
val metadata = Cache(this).getCachedMetadata(galleryID)
|
val metadata = Cache(this).getCachedMetadata(galleryID)
|
||||||
|
|
||||||
|
val source = mapOf(
|
||||||
|
Code.HITOMI to { xyz.quaver.hitomi.getGalleryBlock(galleryID) },
|
||||||
|
Code.HIYOBI to { xyz.quaver.hiyobi.getGalleryBlock(galleryID) }
|
||||||
|
)
|
||||||
|
|
||||||
val galleryBlock = if (metadata?.galleryBlock == null)
|
val galleryBlock = if (metadata?.galleryBlock == null)
|
||||||
listOf(
|
source.entries.map {
|
||||||
{ xyz.quaver.hitomi.getGalleryBlock(galleryID) },
|
|
||||||
{ xyz.quaver.hiyobi.getGalleryBlock(galleryID) }
|
|
||||||
).map {
|
|
||||||
CoroutineScope(Dispatchers.IO).async {
|
CoroutineScope(Dispatchers.IO).async {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
it.invoke()
|
it.value.invoke()
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
}
|
}
|
||||||
}.awaitAll().filterNotNull()
|
}.firstOrNull {
|
||||||
|
it.await() != null
|
||||||
|
}?.await()
|
||||||
else
|
else
|
||||||
metadata.galleryBlock
|
metadata.galleryBlock
|
||||||
|
|
||||||
@@ -126,52 +134,56 @@ class Cache(context: Context) : ContextWrapper(context) {
|
|||||||
Metadata(Cache(this).getCachedMetadata(galleryID), galleryBlock = galleryBlock)
|
Metadata(Cache(this).getCachedMetadata(galleryID), galleryBlock = galleryBlock)
|
||||||
)
|
)
|
||||||
|
|
||||||
val mirrors = preference.getString("mirrors", "")!!.split('>')
|
return galleryBlock
|
||||||
|
|
||||||
return galleryBlock.firstOrNull {
|
|
||||||
mirrors.contains(it.code.name)
|
|
||||||
} ?: galleryBlock.firstOrNull()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getReaderOrNull(galleryID: Int): Reader? {
|
fun getReaderOrNull(galleryID: Int): Reader? {
|
||||||
val metadata = getCachedMetadata(galleryID)
|
return getCachedMetadata(galleryID)?.reader
|
||||||
|
|
||||||
val mirrors = preference.getString("mirrors", "")!!.split('>')
|
|
||||||
|
|
||||||
return metadata?.readers?.firstOrNull {
|
|
||||||
mirrors.contains(it.code.name)
|
|
||||||
} ?: metadata?.readers?.firstOrNull()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getReader(galleryID: Int): Reader? {
|
suspend fun getReader(galleryID: Int): Reader? {
|
||||||
val metadata = getCachedMetadata(galleryID)
|
val metadata = getCachedMetadata(galleryID)
|
||||||
|
val mirrors = preference.getString("mirrors", null)?.split('>') ?: listOf()
|
||||||
|
|
||||||
val readers = if (metadata?.readers == null) {
|
val sources = mapOf(
|
||||||
listOf(
|
Code.HITOMI to { xyz.quaver.hitomi.getReader(galleryID) },
|
||||||
{ xyz.quaver.hitomi.getReader(galleryID) },
|
Code.HIYOBI to { xyz.quaver.hiyobi.getReader(galleryID) }
|
||||||
{ xyz.quaver.hiyobi.getReader(galleryID) }
|
).let {
|
||||||
).map {
|
if (mirrors.isNotEmpty())
|
||||||
CoroutineScope(Dispatchers.IO).async {
|
it.toSortedMap(
|
||||||
kotlin.runCatching {
|
Comparator { o1, o2 ->
|
||||||
it.invoke()
|
mirrors.indexOf(o1.name) - mirrors.indexOf(o2.name)
|
||||||
}.getOrNull()
|
}
|
||||||
}
|
)
|
||||||
}.awaitAll().filterNotNull()
|
else
|
||||||
} else {
|
it
|
||||||
metadata.readers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (readers.isNotEmpty())
|
val reader = if (metadata?.reader == null) {
|
||||||
|
CoroutineScope(Dispatchers.IO).async {
|
||||||
|
var retval: Reader? = null
|
||||||
|
|
||||||
|
for (source in sources) {
|
||||||
|
retval = kotlin.runCatching {
|
||||||
|
source.value.invoke()
|
||||||
|
}.getOrNull()
|
||||||
|
|
||||||
|
if (retval != null)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
retval
|
||||||
|
}.await()
|
||||||
|
} else
|
||||||
|
metadata.reader
|
||||||
|
|
||||||
|
if (reader != null)
|
||||||
setCachedMetadata(
|
setCachedMetadata(
|
||||||
galleryID,
|
galleryID,
|
||||||
Metadata(Cache(this).getCachedMetadata(galleryID), readers = readers)
|
Metadata(Cache(this).getCachedMetadata(galleryID), readers = reader)
|
||||||
)
|
)
|
||||||
|
|
||||||
val mirrors = preference.getString("mirrors", "")!!.split('>')
|
return reader
|
||||||
|
|
||||||
return readers.firstOrNull {
|
|
||||||
mirrors.contains(it.code.name)
|
|
||||||
} ?: readers.firstOrNull()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getImages(galleryID: Int): List<DocumentFile?>? {
|
fun getImages(galleryID: Int): List<DocumentFile?>? {
|
||||||
@@ -180,7 +192,7 @@ class Cache(context: Context) : ContextWrapper(context) {
|
|||||||
val images = gallery.listFiles()
|
val images = gallery.listFiles()
|
||||||
|
|
||||||
return reader.galleryInfo.indices.map { index ->
|
return reader.galleryInfo.indices.map { index ->
|
||||||
images.firstOrNull { file -> file.name?.startsWith(index.toString()) == true }
|
images.firstOrNull { file -> file.name?.startsWith("%05d".format(index)) == true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,21 +206,26 @@ class Cache(context: Context) : ContextWrapper(context) {
|
|||||||
if (!Regex("""^[0-9]+.+$""").matches(name))
|
if (!Regex("""^[0-9]+.+$""").matches(name))
|
||||||
throw IllegalArgumentException("File name is not a number")
|
throw IllegalArgumentException("File name is not a number")
|
||||||
|
|
||||||
cache.createFile("null", name)?.writeBytes(this, data)
|
cache.let {
|
||||||
|
if (it.findFile(name) != null)
|
||||||
|
it
|
||||||
|
else
|
||||||
|
it.createFile("null", name)
|
||||||
|
}?.writeBytes(this, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun moveToDownload(galleryID: Int) {
|
fun moveToDownload(galleryID: Int) {
|
||||||
val cache = getCachedGallery(galleryID)
|
val cache = getCachedGallery(galleryID)
|
||||||
|
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
val download = getDownloadDirectory(this)!!
|
val download = getDownloadDirectory(this)
|
||||||
|
|
||||||
if (!download.isParentOf(cache)) {
|
if (!download.isParentOf(cache)) {
|
||||||
cache.copyRecursively(this, download)
|
cache.copyRecursively(this, download)
|
||||||
cache.deleteRecursively()
|
cache.deleteRecursively()
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
getDownloadDirectory(this)?.createDirectory(galleryID.toString())
|
getDownloadDirectory(this).createDirectory(galleryID.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isDownloading(galleryID: Int) = getCachedMetadata(galleryID)?.isDownloading == true
|
fun isDownloading(galleryID: Int) = getCachedMetadata(galleryID)?.isDownloading == true
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
.body(ProgressResponseBody(request.tag(), response.body(), progressListener))
|
.body(ProgressResponseBody(request.tag(), response.body(), progressListener))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
.dispatcher(Dispatcher(Executors.newSingleThreadExecutor()))
|
.dispatcher(Dispatcher(Executors.newFixedThreadPool(4)))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
@@ -279,6 +279,9 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
progress.put(galleryID, reader.galleryInfo.map { 0F }.toMutableList())
|
progress.put(galleryID, reader.galleryInfo.map { 0F }.toMutableList())
|
||||||
exception.put(galleryID, reader.galleryInfo.map { null }.toMutableList())
|
exception.put(galleryID, reader.galleryInfo.map { null }.toMutableList())
|
||||||
|
|
||||||
|
if (notification[galleryID] == null)
|
||||||
|
initNotification(galleryID)
|
||||||
|
|
||||||
notification[galleryID].setContentTitle(reader.title)
|
notification[galleryID].setContentTitle(reader.title)
|
||||||
notify(galleryID)
|
notify(galleryID)
|
||||||
|
|
||||||
@@ -309,7 +312,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
val ext =
|
val ext =
|
||||||
call.request().url().encodedPath().split('.').last()
|
call.request().url().encodedPath().split('.').last()
|
||||||
|
|
||||||
Cache(this@DownloadWorker).putImage(galleryID, "$i.$ext", res)
|
Cache(this@DownloadWorker).putImage(galleryID, "%05d.%s".format(i, ext), res)
|
||||||
progress[galleryID]?.set(i, Float.POSITIVE_INFINITY)
|
progress[galleryID]?.set(i, Float.POSITIVE_INFINITY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,20 +25,20 @@ import xyz.quaver.hitomi.Reader
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class Metadata(
|
data class Metadata(
|
||||||
val thumbnail: String? = null,
|
val thumbnail: String? = null,
|
||||||
val galleryBlock: List<GalleryBlock>? = null,
|
val galleryBlock: GalleryBlock? = null,
|
||||||
val readers: List<Reader>? = null,
|
val reader: Reader? = null,
|
||||||
val isDownloading: Boolean? = null
|
val isDownloading: Boolean? = null
|
||||||
) {
|
) {
|
||||||
constructor(
|
constructor(
|
||||||
metadata: Metadata?,
|
metadata: Metadata?,
|
||||||
thumbnail: String? = null,
|
thumbnail: String? = null,
|
||||||
galleryBlock: List<GalleryBlock>? = null,
|
galleryBlock: GalleryBlock? = null,
|
||||||
readers: List<Reader>? = null,
|
readers: Reader? = null,
|
||||||
isDownloading: Boolean? = null
|
isDownloading: Boolean? = null
|
||||||
) : this(
|
) : this(
|
||||||
thumbnail ?: metadata?.thumbnail,
|
thumbnail ?: metadata?.thumbnail,
|
||||||
galleryBlock ?: metadata?.galleryBlock,
|
galleryBlock ?: metadata?.galleryBlock,
|
||||||
readers ?: metadata?.readers,
|
readers ?: metadata?.reader,
|
||||||
isDownloading ?: metadata?.isDownloading
|
isDownloading ?: metadata?.isDownloading
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,7 @@ package xyz.quaver.pupil.util
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -28,20 +29,29 @@ import java.nio.charset.Charset
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun getCachedGallery(context: Context, galleryID: Int) =
|
fun getCachedGallery(context: Context, galleryID: Int) =
|
||||||
getDownloadDirectory(context)?.findFile(galleryID.toString()) ?:
|
getDownloadDirectory(context).findFile(galleryID.toString()) ?:
|
||||||
DocumentFile.fromFile(File(context.cacheDir, "imageCache/$galleryID"))
|
DocumentFile.fromFile(File(context.cacheDir, "imageCache/$galleryID"))
|
||||||
|
|
||||||
fun getDownloadDirectory(context: Context) : DocumentFile? {
|
fun getDownloadDirectory(context: Context) : DocumentFile {
|
||||||
val uri = PreferenceManager.getDefaultSharedPreferences(context).getString("dl_location", null).let {
|
val uri = PreferenceManager.getDefaultSharedPreferences(context).getString("dl_location", null).let {
|
||||||
Uri.parse(it)
|
if (it != null)
|
||||||
|
Uri.parse(it)
|
||||||
|
else
|
||||||
|
Uri.fromFile(context.getExternalFilesDir(null))
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (uri.toString().startsWith("file"))
|
return if (uri.toString().startsWith("file"))
|
||||||
DocumentFile.fromFile(File(uri.path!!))
|
DocumentFile.fromFile(File(uri.path!!))
|
||||||
else
|
else
|
||||||
DocumentFile.fromTreeUri(context, uri)
|
DocumentFile.fromTreeUri(context, uri) ?: DocumentFile.fromFile(context.getExternalFilesDir(null)!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun convertUpdateUri(context: Context, uri: Uri) : Uri =
|
||||||
|
if (uri.toString().startsWith("file"))
|
||||||
|
FileProvider.getUriForFile(context, context.applicationContext.packageName + ".provider", File(uri.path!!.substringAfter("file:///")))
|
||||||
|
else
|
||||||
|
uri
|
||||||
|
|
||||||
fun URL.download(context: Context, to: DocumentFile, onDownloadProgress: ((Long, Long) -> Unit)? = null) {
|
fun URL.download(context: Context, to: DocumentFile, onDownloadProgress: ((Long, Long) -> Unit)? = null) {
|
||||||
context.contentResolver.openOutputStream(to.uri).use { out ->
|
context.contentResolver.openOutputStream(to.uri).use { out ->
|
||||||
out!!
|
out!!
|
||||||
@@ -94,12 +104,17 @@ fun DocumentFile.copyRecursively(
|
|||||||
if (!exists())
|
if (!exists())
|
||||||
throw Exception("The source file doesn't exist.")
|
throw Exception("The source file doesn't exist.")
|
||||||
|
|
||||||
if (this.isFile)
|
if (this.isFile) {
|
||||||
target.createFile("null", name!!)!!.writeBytes(
|
target.let {
|
||||||
|
if (it.findFile(name!!) != null)
|
||||||
|
it
|
||||||
|
else
|
||||||
|
createFile("null", name!!)!!
|
||||||
|
}.writeBytes(
|
||||||
context,
|
context,
|
||||||
readBytes(context)
|
readBytes(context)
|
||||||
)
|
)
|
||||||
else if (this.isDirectory) {
|
} else if (this.isDirectory) {
|
||||||
target.createDirectory(name!!).also { newTarget ->
|
target.createDirectory(name!!).also { newTarget ->
|
||||||
listFiles().forEach { child ->
|
listFiles().forEach { child ->
|
||||||
child.copyRecursively(context, newTarget!!)
|
child.copyRecursively(context, newTarget!!)
|
||||||
|
|||||||
@@ -146,7 +146,12 @@ fun checkUpdate(context: AppCompatActivity, force: Boolean = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch io@{
|
CoroutineScope(Dispatchers.IO).launch io@{
|
||||||
val target = getDownloadDirectory(context)?.createFile("null", "Pupil.apk")!!
|
val target = getDownloadDirectory(context).let {
|
||||||
|
if (it.findFile("Pupil.apk") != null)
|
||||||
|
it
|
||||||
|
else
|
||||||
|
it.createFile("null", "Pupil.apk")!!
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
URL(url).download(context, target) { progress, fileSize ->
|
URL(url).download(context, target) { progress, fileSize ->
|
||||||
@@ -168,7 +173,7 @@ fun checkUpdate(context: AppCompatActivity, force: Boolean = false) {
|
|||||||
|
|
||||||
val install = Intent(Intent.ACTION_VIEW).apply {
|
val install = Intent(Intent.ACTION_VIEW).apply {
|
||||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
setDataAndType(target.uri, MimeTypeMap.getSingleton().getMimeTypeFromExtension("apk"))
|
setDataAndType(convertUpdateUri(context, target.uri), MimeTypeMap.getSingleton().getMimeTypeFromExtension("apk"))
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.apply {
|
builder.apply {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
<paths>
|
||||||
<external-path name="external" path="/"/>
|
<external-path name="external" path="/"/>
|
||||||
|
<external-files-path name="files" path="/"/>
|
||||||
</paths>
|
</paths>
|
||||||
@@ -75,7 +75,7 @@ class UnitTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_getReader() {
|
fun test_getReader() {
|
||||||
val reader = getReader(1442740)
|
val reader = getReader(1567569)
|
||||||
|
|
||||||
print(reader)
|
print(reader)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user