Compare commits

...

4 Commits

Author SHA1 Message Date
Pupil
6172a73719 Bug fix 2020-02-09 17:36:28 +09:00
Pupil
7455e68a45 Bug fix 2020-02-09 17:35:34 +09:00
Pupil
748495ca64 Downloader thread number to 4 2020-02-09 17:25:55 +09:00
Pupil
f6d9c7f550 Bug fix
Networking optimized
2020-02-09 17:11:35 +09:00
15 changed files with 106 additions and 75 deletions

View File

@@ -19,8 +19,8 @@ android {
applicationId "xyz.quaver.pupil"
minSdkVersion 16
targetSdkVersion 29
versionCode 33
versionName "5.0"
versionCode 36
versionName "5.3-beta2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
vectorDrawables.useSupportLibrary = true

View File

@@ -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":36,"versionName":"5.3-beta2","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]

View File

@@ -22,7 +22,7 @@
tools:replace="android:theme">
<provider
android:authorities="${applicationId}.fileprovider"
android:authorities="${applicationId}.provider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">

View File

@@ -49,7 +49,11 @@ class Pupil : MultiDexApplication() {
histories = Histories(File(ContextCompat.getDataDir(this), "histories.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) {
val default = ContextCompat.getExternalFilesDirs(this, null)[0]

View File

@@ -129,7 +129,12 @@ class GalleryBlockAdapter(context: Context, private val galleries: List<GalleryB
})
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
.load(thumbnail)

View File

@@ -26,7 +26,8 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
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.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -108,17 +109,13 @@ class ReaderAdapter(private val context: Context,
val progress = DownloadWorker.getInstance(context).progress[galleryID]?.get(position)
if (progress?.isNaN() == true) {
if (Fabric.isInitialized())
Crashlytics.logException(DownloadWorker.getInstance(context).exception[galleryID]?.get(position))
glide
.load(R.drawable.image_broken_variant)
.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
}

View File

@@ -162,7 +162,7 @@ class ReaderActivity : AppCompatActivity() {
val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, findViewById(android.R.id.content), false)
with(view.dialog_number_picker) {
minValue=1
maxValue=reader_recyclerview.adapter?.itemCount ?: 0
maxValue=reader_recyclerview?.adapter?.itemCount ?: 0
value=currentPage
}
val dialog = AlertDialog.Builder(this).apply {
@@ -196,7 +196,7 @@ class ReaderActivity : AppCompatActivity() {
super.onDestroy()
timer.cancel()
(reader_recyclerview.adapter as ReaderAdapter).timer.cancel()
(reader_recyclerview?.adapter as? ReaderAdapter)?.timer?.cancel()
if (!Cache(this).isDownloading(galleryID))
DownloadWorker.getInstance(this@ReaderActivity).cancel(galleryID)

View File

@@ -101,7 +101,7 @@ class SettingsFragment :
}.show()
}
"delete_downloads" -> {
val dir = getDownloadDirectory(context)!!
val dir = getDownloadDirectory(context)
AlertDialog.Builder(context).apply {
setTitle(R.string.warning)
@@ -150,7 +150,7 @@ class SettingsFragment :
"backup" -> {
File(ContextCompat.getDataDir(context), "favorites.json").copyTo(
context,
getDownloadDirectory(context)?.createFile("null", "favorites.json")!!
getDownloadDirectory(context).createFile("null", "favorites.json")!!
)
Snackbar.make(this@SettingsFragment.listView, R.string.settings_backup_snackbar, Snackbar.LENGTH_LONG)
@@ -192,8 +192,7 @@ class SettingsFragment :
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
when (key) {
"dl_location" -> {
findPreference<Preference>(key)?.summary =
FileUtils.getPath(context, getDownloadDirectory(context!!)?.uri)
findPreference<Preference>(key)?.summary = getDownloadDirectory(context!!).uri.path
}
}
}
@@ -230,7 +229,7 @@ class SettingsFragment :
onPreferenceClickListener = this@SettingsFragment
}
"delete_downloads" -> {
val dir = getDownloadDirectory(context)!!
val dir = getDownloadDirectory(context)
summary = getDirSize(dir)
onPreferenceClickListener = this@SettingsFragment
@@ -242,7 +241,7 @@ class SettingsFragment :
onPreferenceClickListener = this@SettingsFragment
}
"dl_location" -> {
summary = FileUtils.getPath(context, getDownloadDirectory(context)?.uri)
summary = getDownloadDirectory(context).uri.path
onPreferenceClickListener = this@SettingsFragment
}

View File

@@ -23,11 +23,15 @@ import android.content.ContextWrapper
import android.util.Base64
import androidx.documentfile.provider.DocumentFile
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.json.Json
import kotlinx.serialization.parse
import kotlinx.serialization.stringify
import xyz.quaver.Code
import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader
import xyz.quaver.pupil.util.*
@@ -107,17 +111,21 @@ class Cache(context: Context) : ContextWrapper(context) {
suspend fun getGalleryBlock(galleryID: Int): GalleryBlock? {
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)
listOf(
{ xyz.quaver.hitomi.getGalleryBlock(galleryID) },
{ xyz.quaver.hiyobi.getGalleryBlock(galleryID) }
).map {
source.entries.map {
CoroutineScope(Dispatchers.IO).async {
kotlin.runCatching {
it.invoke()
it.value.invoke()
}.getOrNull()
}
}.awaitAll().filterNotNull()
}.firstOrNull {
it.await() != null
}?.await()
else
metadata.galleryBlock
@@ -126,52 +134,56 @@ class Cache(context: Context) : ContextWrapper(context) {
Metadata(Cache(this).getCachedMetadata(galleryID), galleryBlock = galleryBlock)
)
val mirrors = preference.getString("mirrors", "")!!.split('>')
return galleryBlock.firstOrNull {
mirrors.contains(it.code.name)
} ?: galleryBlock.firstOrNull()
return galleryBlock
}
fun getReaderOrNull(galleryID: Int): Reader? {
val metadata = getCachedMetadata(galleryID)
val mirrors = preference.getString("mirrors", "")!!.split('>')
return metadata?.readers?.firstOrNull {
mirrors.contains(it.code.name)
} ?: metadata?.readers?.firstOrNull()
return getCachedMetadata(galleryID)?.reader
}
suspend fun getReader(galleryID: Int): Reader? {
val metadata = getCachedMetadata(galleryID)
val mirrors = preference.getString("mirrors", null)?.split('>') ?: listOf()
val readers = if (metadata?.readers == null) {
listOf(
{ xyz.quaver.hitomi.getReader(galleryID) },
{ xyz.quaver.hiyobi.getReader(galleryID) }
).map {
CoroutineScope(Dispatchers.IO).async {
kotlin.runCatching {
it.invoke()
}.getOrNull()
}
}.awaitAll().filterNotNull()
} else {
metadata.readers
val sources = mapOf(
Code.HITOMI to { xyz.quaver.hitomi.getReader(galleryID) },
Code.HIYOBI to { xyz.quaver.hiyobi.getReader(galleryID) }
).let {
if (mirrors.isNotEmpty())
it.toSortedMap(
Comparator { o1, o2 ->
mirrors.indexOf(o1.name) - mirrors.indexOf(o2.name)
}
)
else
it
}
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(
galleryID,
Metadata(Cache(this).getCachedMetadata(galleryID), readers = readers)
Metadata(Cache(this).getCachedMetadata(galleryID), readers = reader)
)
val mirrors = preference.getString("mirrors", "")!!.split('>')
return readers.firstOrNull {
mirrors.contains(it.code.name)
} ?: readers.firstOrNull()
return reader
}
fun getImages(galleryID: Int): List<DocumentFile?>? {

View File

@@ -162,7 +162,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
.body(ProgressResponseBody(request.tag(), response.body(), progressListener))
.build()
}
.dispatcher(Dispatcher(Executors.newSingleThreadExecutor()))
.dispatcher(Dispatcher(Executors.newFixedThreadPool(4)))
.build()
fun stop() {
@@ -279,6 +279,9 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
progress.put(galleryID, reader.galleryInfo.map { 0F }.toMutableList())
exception.put(galleryID, reader.galleryInfo.map { null }.toMutableList())
if (notification[galleryID] == null)
initNotification(galleryID)
notification[galleryID].setContentTitle(reader.title)
notify(galleryID)

View File

@@ -25,20 +25,20 @@ import xyz.quaver.hitomi.Reader
@Serializable
data class Metadata(
val thumbnail: String? = null,
val galleryBlock: List<GalleryBlock>? = null,
val readers: List<Reader>? = null,
val galleryBlock: GalleryBlock? = null,
val reader: Reader? = null,
val isDownloading: Boolean? = null
) {
constructor(
metadata: Metadata?,
thumbnail: String? = null,
galleryBlock: List<GalleryBlock>? = null,
readers: List<Reader>? = null,
galleryBlock: GalleryBlock? = null,
readers: Reader? = null,
isDownloading: Boolean? = null
) : this(
thumbnail ?: metadata?.thumbnail,
galleryBlock ?: metadata?.galleryBlock,
readers ?: metadata?.readers,
readers ?: metadata?.reader,
isDownloading ?: metadata?.isDownloading
)
}

View File

@@ -20,6 +20,7 @@ package xyz.quaver.pupil.util
import android.content.Context
import android.net.Uri
import androidx.core.content.FileProvider
import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceManager
import java.io.File
@@ -28,20 +29,29 @@ import java.nio.charset.Charset
import java.util.*
fun getCachedGallery(context: Context, galleryID: Int) =
getDownloadDirectory(context)?.findFile(galleryID.toString()) ?:
getDownloadDirectory(context).findFile(galleryID.toString()) ?:
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 {
Uri.parse(it)
if (it != null)
Uri.parse(it)
else
Uri.fromFile(context.getExternalFilesDir(null))
}
return if (uri.toString().startsWith("file"))
DocumentFile.fromFile(File(uri.path!!))
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) {
context.contentResolver.openOutputStream(to.uri).use { out ->
out!!

View File

@@ -168,7 +168,7 @@ fun checkUpdate(context: AppCompatActivity, force: Boolean = false) {
val install = Intent(Intent.ACTION_VIEW).apply {
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 {

View File

@@ -17,6 +17,7 @@
~ 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-files-path name="files" path="/"/>
</paths>

View File

@@ -75,7 +75,7 @@ class UnitTest {
@Test
fun test_getReader() {
val reader = getReader(1442740)
val reader = getReader(1567569)
print(reader)
}