Compare commits

...

7 Commits
4.9 ... 4.12

Author SHA1 Message Date
Pupil
b4f2a33016 Merge pull request #72 from tom5079/dev
Modified proguard rules to fix error occurs on Android 4
2020-02-25 22:02:48 +09:00
Pupil
ee7ede2885 Modified proguard rules to fix error occurs on Android 4 2020-02-25 21:50:36 +09:00
Pupil
6abc404eb7 Merge pull request #71 from tom5079/dev
Version 4.11
2020-02-25 20:35:41 +09:00
Pupil
61afe01e36 Fixed image loading from hiyobi.me 2020-02-25 20:34:31 +09:00
Pupil
c3e60f9988 Typo fixed 2020-02-25 19:19:27 +09:00
Pupil
593197cd7e Bug fix
Thin mode added
Cancel all downloads added
2020-02-25 19:17:23 +09:00
Pupil
ee1592b478 Bug fix 2020-02-25 10:38:11 +09:00
26 changed files with 139 additions and 142 deletions

View File

@@ -1,9 +1,6 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="120" /> <option name="RIGHT_MARGIN" value="120" />
<AndroidXmlCodeStyleSettings>
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
</AndroidXmlCodeStyleSettings>
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>

3
.idea/gradle.xml generated
View File

@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>
<option name="delegatedBuild" value="false" />
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" /> <option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules"> <option name="modules">

View File

@@ -19,8 +19,8 @@ android {
applicationId "xyz.quaver.pupil" applicationId "xyz.quaver.pupil"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 29
versionCode 45 versionCode 48
versionName "4.9" versionName "4.12"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@@ -66,7 +66,7 @@ dependencies {
implementation 'androidx.preference:preference:1.1.0' implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.gridlayout:gridlayout:1.0.0' implementation 'androidx.gridlayout:gridlayout:1.0.0'
implementation "androidx.biometric:biometric:1.0.1" implementation "androidx.biometric:biometric:1.0.1"
implementation 'com.android.support:multidex:1.0.3' implementation 'androidx.multidex:multidex:2.0.1'
implementation "com.daimajia.swipelayout:library:1.2.0@aar" implementation "com.daimajia.swipelayout:library:1.2.0@aar"
implementation 'com.google.android.material:material:1.2.0-alpha05' implementation 'com.google.android.material:material:1.2.0-alpha05'
implementation 'com.google.firebase:firebase-core:17.2.2' implementation 'com.google.firebase:firebase-core:17.2.2'
@@ -74,14 +74,12 @@ dependencies {
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
implementation 'com.github.arimorty:floatingsearchview:2.1.1' implementation 'com.github.arimorty:floatingsearchview:2.1.1'
implementation 'com.github.clans:fab:1.6.4' implementation 'com.github.clans:fab:1.6.4'
implementation 'com.github.bumptech.glide:glide:4.11.0' implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
kapt 'com.github.bumptech.glide:compiler:4.11.0' kapt 'com.github.bumptech.glide:compiler:4.11.0'
implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") { implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
transitive = false transitive = false
} }
implementation 'net.rdrei.android.dirchooser:library:3.2@aar' implementation 'net.rdrei.android.dirchooser:library:3.2@aar'
implementation 'com.github.chrisbanes:PhotoView:2.3.0' implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0' implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'

View File

@@ -23,8 +23,13 @@
-dontobfuscate -dontobfuscate
-keep public class * implements com.bumptech.glide.module.GlideModule -keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule -keep class * extends com.bumptech.glide.module.AppGlideModule {
<init>(...);
}
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** { -keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES; **[] $VALUES;
public *; public *;
} }
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
*** rewind();
}

View File

@@ -1 +1 @@
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":45,"versionName":"4.9","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] [{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":48,"versionName":"4.12","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release","dirName":""},"path":"app-release.apk","properties":{}}]

View File

@@ -105,9 +105,9 @@ class ExampleInstrumentedTest {
val galleryID = 1561552 val galleryID = 1561552
runBlocking { runBlocking {
Log.i("PUPILD", Cache(context).getReader(galleryID)?.title ?: "null") Log.i("PUPILD", Cache(context).getReader(galleryID)?.galleryInfo?.title ?: "null")
} }
Log.i("PUPILD", Cache(context).getReaderOrNull(galleryID)?.title ?: "null") Log.i("PUPILD", Cache(context).getReaderOrNull(galleryID)?.galleryInfo?.title ?: "null")
} }
} }

View File

@@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application <application
android:name=".Pupil" android:name=".Pupil"
android:allowBackup="true" android:allowBackup="true"

View File

@@ -49,13 +49,12 @@ import xyz.quaver.pupil.R
import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.types.Tag
import xyz.quaver.pupil.util.Histories import xyz.quaver.pupil.util.Histories
import xyz.quaver.pupil.util.download.Cache import xyz.quaver.pupil.util.download.Cache
import xyz.quaver.pupil.util.download.DownloadWorker
import xyz.quaver.pupil.util.wordCapitalize import xyz.quaver.pupil.util.wordCapitalize
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.concurrent.schedule import kotlin.concurrent.schedule
class GalleryBlockAdapter(context: Context, private val galleries: List<GalleryBlock>) : RecyclerSwipeAdapter<RecyclerView.ViewHolder>(), SwipeAdapterInterface { class GalleryBlockAdapter(private val context: Context, private val galleries: List<GalleryBlock>) : RecyclerSwipeAdapter<RecyclerView.ViewHolder>(), SwipeAdapterInterface {
enum class ViewType { enum class ViewType {
NEXT, NEXT,
@@ -68,11 +67,12 @@ class GalleryBlockAdapter(context: Context, private val galleries: List<GalleryB
val timer = Timer() val timer = Timer()
var isThin = false
inner class GalleryViewHolder(val view: View) : RecyclerView.ViewHolder(view) { inner class GalleryViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
var timerTask: TimerTask? = null var timerTask: TimerTask? = null
private fun updateProgress(context: Context, galleryID: Int) { private fun updateProgress(context: Context, galleryID: Int) {
val cache = Cache(context).getCachedGallery(galleryID)
val reader = Cache(context).getReaderOrNull(galleryID) val reader = Cache(context).getReaderOrNull(galleryID)
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
@@ -84,9 +84,7 @@ class GalleryBlockAdapter(context: Context, private val galleries: List<GalleryB
with(view.galleryblock_progressbar) { with(view.galleryblock_progressbar) {
progress = cache.listFiles()?.count { file -> progress = Cache(context).getImages(galleryID)?.size ?: 0
Regex("^[0-9]+.+\$").matches(file.name)
} ?: 0
if (visibility == View.GONE) { if (visibility == View.GONE) {
visibility = View.VISIBLE visibility = View.VISIBLE
@@ -126,6 +124,10 @@ class GalleryBlockAdapter(context: Context, private val galleries: List<GalleryB
val artists = galleryBlock.artists val artists = galleryBlock.artists
val series = galleryBlock.series val series = galleryBlock.series
if (isThin)
galleryblock_thumbnail.layoutParams.width = context.resources.getDimensionPixelSize(
R.dimen.galleryblock_thumbnail_thin
)
galleryblock_thumbnail.setImageDrawable(CircularProgressDrawable(context).also { galleryblock_thumbnail.setImageDrawable(CircularProgressDrawable(context).also {
it.start() it.start()
}) })
@@ -138,16 +140,18 @@ class GalleryBlockAdapter(context: Context, private val galleries: List<GalleryB
null null
} }
glide galleryblock_thumbnail.post {
.load(thumbnail) glide
.skipMemoryCache(true) .load(thumbnail)
.diskCacheStrategy(DiskCacheStrategy.NONE) .skipMemoryCache(true)
.error(R.drawable.image_broken_variant) .diskCacheStrategy(DiskCacheStrategy.NONE)
.apply { .error(R.drawable.image_broken_variant)
if (BuildConfig.CENSOR) .apply {
override(5, 8) if (BuildConfig.CENSOR)
} override(5, 8)
.into(galleryblock_thumbnail) }
.into(galleryblock_thumbnail)
}
} }
//Check cache //Check cache
@@ -264,6 +268,14 @@ class GalleryBlockAdapter(context: Context, private val galleries: List<GalleryB
} }
} }
} }
// Make some views invisible to make it thinner
if (isThin) {
galleryblock_language.visibility = View.GONE
galleryblock_type.visibility = View.GONE
galleryblock_tag_group.visibility = View.GONE
}
} }
} }
} }
@@ -341,10 +353,10 @@ class GalleryBlockAdapter(context: Context, private val galleries: List<GalleryB
mItemManger.closeAllExcept(layout) mItemManger.closeAllExcept(layout)
holder.view.galleryblock_download.text = holder.view.galleryblock_download.text =
if (DownloadWorker.getInstance(holder.view.context).progress.indexOfKey(gallery.id) < 0) if (Cache(context).isDownloading(gallery.id))
holder.view.context.getString(R.string.main_download)
else
holder.view.context.getString(android.R.string.cancel) holder.view.context.getString(android.R.string.cancel)
else
holder.view.context.getString(R.string.main_download)
} }
override fun onClose(layout: SwipeLayout?) {} override fun onClose(layout: SwipeLayout?) {}

View File

@@ -100,6 +100,9 @@ class ReaderAdapter(private val context: Context,
} else { } else {
holder.view.layoutParams.height = RecyclerView.LayoutParams.WRAP_CONTENT holder.view.layoutParams.height = RecyclerView.LayoutParams.WRAP_CONTENT
holder.view.container.layoutParams.height = 0 holder.view.container.layoutParams.height = 0
(holder.view.container.layoutParams as ConstraintLayout.LayoutParams)
.dimensionRatio = "W,${reader!!.galleryInfo.files[position].width}:${reader!!.galleryInfo.files[position].height}"
} }
holder.view.image.setOnPhotoTapListener { _, _, _ -> holder.view.image.setOnPhotoTapListener { _, _, _ ->
@@ -110,10 +113,6 @@ class ReaderAdapter(private val context: Context,
onItemClickListener?.invoke(position) onItemClickListener?.invoke(position)
} }
if (!isFullScreen)
(holder.view.container.layoutParams as ConstraintLayout.LayoutParams)
.dimensionRatio = "W,${reader!!.galleryInfo.files[position].width}:${reader!!.galleryInfo.files[position].height}"
holder.view.reader_index.text = (position+1).toString() holder.view.reader_index.text = (position+1).toString()
val images = Cache(context).getImage(galleryID, position) val images = Cache(context).getImage(galleryID, position)

View File

@@ -330,6 +330,13 @@ class MainActivity : AppCompatActivity() {
true true
} }
with(main_fab_cancel) {
setImageResource(R.drawable.cancel)
setOnClickListener {
DownloadWorker.getInstance(context).stop()
}
}
with(main_fab_jump) { with(main_fab_jump) {
setImageResource(R.drawable.ic_jump) setImageResource(R.drawable.ic_jump)
setOnClickListener { setOnClickListener {
@@ -408,7 +415,7 @@ class MainActivity : AppCompatActivity() {
if (!completeFlag.get(galleryID, false)) { if (!completeFlag.get(galleryID, false)) {
val worker = DownloadWorker.getInstance(context) val worker = DownloadWorker.getInstance(context)
if (worker.progress.indexOfKey(galleryID) >= 0) //download in progress if (Cache(context).isDownloading(galleryID)) //download in progress
worker.cancel(galleryID) worker.cancel(galleryID)
else { else {
Cache(context).setDownloading(galleryID, true) Cache(context).setDownloading(galleryID, true)
@@ -724,6 +731,15 @@ class MainActivity : AppCompatActivity() {
setOnMenuItemClickListener { setOnMenuItemClickListener {
when(it.itemId) { when(it.itemId) {
R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), REQUEST_SETTINGS) R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), REQUEST_SETTINGS)
R.id.main_menu_thin -> {
main_recyclerview.apply {
(adapter as GalleryBlockAdapter).apply {
isThin = !isThin
}
adapter = adapter // Force to redraw
}
}
R.id.main_menu_sort_newest -> { R.id.main_menu_sort_newest -> {
sortMode = SortMode.NEWEST sortMode = SortMode.NEWEST
it.isChecked = true it.isChecked = true

View File

@@ -361,6 +361,8 @@ class ReaderActivity : AppCompatActivity() {
window.attributes = this window.attributes = this
} }
reader_recyclerview.adapter = reader_recyclerview.adapter // Force to redraw
} }
private fun scrollMode(isScroll: Boolean) { private fun scrollMode(isScroll: Boolean) {
@@ -386,7 +388,7 @@ class ReaderActivity : AppCompatActivity() {
if (worker.progress[galleryID]?.all { !it.isFinite() } == true) // If download is finished, stop animating if (worker.progress[galleryID]?.all { !it.isFinite() } == true) // If download is finished, stop animating
post { post {
setImageResource(R.drawable.ic_download) setImageResource(R.drawable.ic_download)
labelText = getString(R.string.reader_fab_download) labelText = getString(R.string.reader_fab_download_cancel)
} }
else // Or continue animate else // Or continue animate
post { post {

View File

@@ -21,21 +21,19 @@ package xyz.quaver.pupil.util.download
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.util.Base64 import android.util.Base64
import android.util.Log
import android.util.SparseArray import android.util.SparseArray
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext
import kotlinx.io.InputStream import kotlinx.io.InputStream
import xyz.quaver.Code 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.proxy import xyz.quaver.proxy
import xyz.quaver.pupil.util.copyRecursively
import xyz.quaver.pupil.util.getCachedGallery import xyz.quaver.pupil.util.getCachedGallery
import xyz.quaver.pupil.util.getDownloadDirectory import xyz.quaver.pupil.util.getDownloadDirectory
import xyz.quaver.pupil.util.isParentOf
import xyz.quaver.pupil.util.json import xyz.quaver.pupil.util.json
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@@ -247,15 +245,27 @@ class Cache(context: Context) : ContextWrapper(context) {
} }
} }
fun moveToDownload(galleryID: Int) { fun moveToDownload(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch {
val cache = getCachedGallery(galleryID).also { val cache = getCachedGallery(galleryID).also {
if (!it.exists()) if (!it.exists())
return return@launch
} }
val download = File(getDownloadDirectory(this), galleryID.toString()) val download = File(getDownloadDirectory(this@Cache), galleryID.toString())
cache.copyRecursively(download, true) { _, _ -> OnErrorAction.SKIP } if (download.isParentOf(cache))
return@launch
Log.i("PUPILD", "MOVING ${cache.canonicalPath} --> ${download.canonicalPath}")
cache.copyRecursively(download, true) { file, err ->
Log.i("PUPILD", "MOVING ERROR ${file.canonicalPath} ${err.message}")
OnErrorAction.SKIP
}
Log.i("PUPILD", "MOVED ${cache.canonicalPath}")
Log.i("PUPILD", "DELETING ${cache.canonicalPath}")
cache.deleteRecursively() cache.deleteRecursively()
Log.i("PUPILD", "DELETED ${cache.canonicalPath}")
} }
fun isDownloading(galleryID: Int) = getCachedMetadata(galleryID)?.isDownloading == true fun isDownloading(galleryID: Int) = getCachedMetadata(galleryID)?.isDownloading == true

View File

@@ -149,7 +149,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
private val loop = loop() private val loop = loop()
private val worker = SparseArray<Job?>() private val worker = SparseArray<Job?>()
val clients = SparseArray<OkHttpClient>()
val interceptor = Interceptor { chain -> val interceptor = Interceptor { chain ->
val request = chain.request() val request = chain.request()
@@ -159,7 +158,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()
} }
fun buildClient() = val client =
OkHttpClient.Builder() OkHttpClient.Builder()
.addInterceptor(interceptor) .addInterceptor(interceptor)
.connectTimeout(0, TimeUnit.SECONDS) .connectTimeout(0, TimeUnit.SECONDS)
@@ -172,17 +171,14 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
queue.clear() queue.clear()
loop.cancel() loop.cancel()
for (i in 0..worker.size()) { for (i in 0 until worker.size()) {
val galleryID = worker.keyAt(i) val galleryID = worker.keyAt(i)
Cache(this@DownloadWorker).setDownloading(galleryID, false) Cache(this@DownloadWorker).setDownloading(galleryID, false)
worker[galleryID]?.cancel() worker[galleryID]?.cancel()
} }
for (i in 0 until clients.size()) { client.dispatcher().cancelAll()
clients.valueAt(i).dispatcher().cancelAll()
}
clients.clear()
progress.clear() progress.clear()
exception.clear() exception.clear()
@@ -194,17 +190,19 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
queue.remove(galleryID) queue.remove(galleryID)
worker[galleryID]?.cancel() worker[galleryID]?.cancel()
clients[galleryID]?.dispatcher()?.cancelAll() client.dispatcher().queuedCalls().filter {
clients.remove(galleryID) ((it.request().tag() as Pair<*, *>).first as Int) == galleryID
}.forEach {
it.cancel()
}
progress.remove(galleryID) progress.remove(galleryID)
exception.remove(galleryID) exception.remove(galleryID)
notification.remove(galleryID) notification.remove(galleryID)
notificationManager.cancel(galleryID) notificationManager.cancel(galleryID)
if (progress.indexOfKey(galleryID) >= 0) { if (progress.indexOfKey(galleryID) >= 0)
Cache(this@DownloadWorker).setDownloading(galleryID, false) Cache(this@DownloadWorker).setDownloading(galleryID, false)
}
} }
fun isCompleted(galleryID: Int) = progress[galleryID]?.all { !it.isFinite() } == true fun isCompleted(galleryID: Int) = progress[galleryID]?.all { !it.isFinite() } == true
@@ -236,10 +234,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
tag(galleryID to index) tag(galleryID to index)
}.build() }.build()
if (clients.get(galleryID) == null) client.newCall(request).enqueue(callback)
clients.put(galleryID, buildClient())
clients[galleryID]?.newCall(request)?.enqueue(callback)
} }
private fun download(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch { private fun download(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch {
@@ -294,8 +289,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
notify(galleryID) notify(galleryID)
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
if (isCompleted(galleryID) && clients.indexOfKey(galleryID) >= 0) { if (isCompleted(galleryID)) {
clients.remove(galleryID)
with(Cache(this@DownloadWorker)) { with(Cache(this@DownloadWorker)) {
if (isDownloading(galleryID)) { if (isDownloading(galleryID)) {
moveToDownload(galleryID) moveToDownload(galleryID)
@@ -320,8 +314,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
notify(galleryID) notify(galleryID)
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
if (isCompleted(galleryID) && clients.indexOfKey(galleryID) >= 0) { if (isCompleted(galleryID)) {
clients.remove(galleryID)
with(Cache(this@DownloadWorker)) { with(Cache(this@DownloadWorker)) {
if (isDownloading(galleryID)) { if (isDownloading(galleryID)) {
moveToDownload(galleryID) moveToDownload(galleryID)
@@ -340,8 +333,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
notify(galleryID) notify(galleryID)
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
if (isCompleted(galleryID) && clients.indexOfKey(galleryID) >= 0) { if (isCompleted(galleryID)) {
clients.remove(galleryID)
with(Cache(this@DownloadWorker)) { with(Cache(this@DownloadWorker)) {
if (isDownloading(galleryID)) { if (isDownloading(galleryID)) {
moveToDownload(galleryID) moveToDownload(galleryID)
@@ -418,7 +410,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
val galleryID = queue.peek() ?: continue val galleryID = queue.peek() ?: continue
if (clients.indexOfKey(galleryID) >= 0) // Gallery already downloading! if (progress.indexOfKey(galleryID) >= 0) // Gallery already downloading!
continue continue
if (notification[galleryID] == null) if (notification[galleryID] == null)
@@ -427,10 +419,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
if (Cache(this@DownloadWorker).isDownloading(galleryID)) if (Cache(this@DownloadWorker).isDownloading(galleryID))
notificationManager.notify(galleryID, notification[galleryID].build()) notificationManager.notify(galleryID, notification[galleryID].build())
if (clients.size() >= preferences.getInt("max_download", 4)) Log.i("PUPILD", "QUEUED $galleryID")
continue
Log.i("PUPILD", "QUEUED $galleryID #${clients.size()+1}")
worker.put(galleryID, download(galleryID)) worker.put(galleryID, download(galleryID))
queue.poll() queue.poll()

View File

@@ -26,7 +26,6 @@ import android.os.storage.StorageManager
import android.provider.DocumentsContract import android.provider.DocumentsContract
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import kotlinx.io.IOException
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.lang.reflect.Array import java.lang.reflect.Array
@@ -214,64 +213,5 @@ fun Uri.toFile(context: Context): File? {
return File(context.getExternalFilesDir(null)?.canonicalPath?.substringBeforeLast("/Android/data") ?: return null, folderName) return File(context.getExternalFilesDir(null)?.canonicalPath?.substringBeforeLast("/Android/data") ?: return null, folderName)
} }
fun File.copyRecursively( fun File.isParentOf(another: File) =
target: File, another.absolutePath.startsWith(this.absolutePath)
overwrite: Boolean = false,
onError: (File, IOException) -> OnErrorAction = { _, exception -> throw exception }
): Boolean {
if (!exists()) {
return onError(this, NoSuchFileException(file = this, reason = "The source file doesn't exist.")) !=
OnErrorAction.TERMINATE
}
try {
// We cannot break for loop from inside a lambda, so we have to use an exception here
for (src in walkTopDown().onFail { f, e -> if (onError(f, e) == OnErrorAction.TERMINATE) throw IOException("Walk failed") }) {
if (!src.exists()) {
if (onError(src, NoSuchFileException(file = src, reason = "The source file doesn't exist.")) ==
OnErrorAction.TERMINATE)
return false
} else {
val relPath = src.toRelativeString(this)
val dstFile = File(target, relPath)
if (dstFile.exists() && !(src.isDirectory && dstFile.isDirectory)) {
val stillExists = if (!overwrite) true else {
if (dstFile.isDirectory)
!dstFile.deleteRecursively()
else
!dstFile.delete()
}
if (stillExists) {
if (onError(dstFile, FileAlreadyExistsException(file = src,
other = dstFile,
reason = "The destination file already exists.")) == OnErrorAction.TERMINATE)
return false
continue
}
}
if (src.isDirectory) {
dstFile.mkdirs()
} else {
val length = try {
src.copyTo(dstFile, overwrite).length()
} catch (e: IOException) {
if (onError(src, e) == OnErrorAction.TERMINATE)
return false
else
-1
}
if (length != src.length()) {
if (onError(src, IOException("Source file wasn't copied completely, length of destination file differs.")) == OnErrorAction.TERMINATE)
return false
}
}
}
}
return true
} catch (e: IOException) {
return false
}
}

View File

@@ -0,0 +1,8 @@
<!-- drawable/cancel.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="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12C4,13.85 4.63,15.55 5.68,16.91L16.91,5.68C15.55,4.63 13.85,4 12,4M12,20A8,8 0 0,0 20,12C20,10.15 19.37,8.45 18.32,7.09L7.09,18.32C8.45,19.37 10.15,20 12,20Z" />
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -82,6 +82,13 @@
android:layout_margin="16dp" android:layout_margin="16dp"
app:menu_colorNormal="@color/colorAccent"> app:menu_colorNormal="@color/colorAccent">
<com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_label="@string/main_fab_cancel"
app:fab_size="mini"/>
<com.github.clans.fab.FloatingActionButton <com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_jump" android:id="@+id/main_fab_jump"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -20,6 +20,9 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/main_menu_thin"
android:title="@string/main_menu_thin"/>
<item <item
android:id="@+id/main_menu_sort" android:id="@+id/main_menu_sort"
android:title="@string/main_menu_sort"> android:title="@string/main_menu_sort">

View File

@@ -130,4 +130,6 @@
<string name="proxy_dialog_error">エラー</string> <string name="proxy_dialog_error">エラー</string>
<string name="proxy_dialog_addr_hint">サーバーアドレス</string> <string name="proxy_dialog_addr_hint">サーバーアドレス</string>
<string name="proxy_dialog_server">サーバー</string> <string name="proxy_dialog_server">サーバー</string>
<string name="main_menu_thin">簡単モード</string>
<string name="main_fab_cancel">すべてのダウンロードキャンセル</string>
</resources> </resources>

View File

@@ -130,4 +130,6 @@
<string name="proxy_dialog_error">잘못된 값</string> <string name="proxy_dialog_error">잘못된 값</string>
<string name="proxy_dialog_addr_hint">서버 주소</string> <string name="proxy_dialog_addr_hint">서버 주소</string>
<string name="proxy_dialog_server">서버</string> <string name="proxy_dialog_server">서버</string>
<string name="main_menu_thin">간단히 보기 모드</string>
<string name="main_fab_cancel">다운로드 모두 취소</string>
</resources> </resources>

View File

@@ -11,6 +11,6 @@
<dimen name="thumbnail_margin">8dp</dimen> <dimen name="thumbnail_margin">8dp</dimen>
<dimen name="galleryblock_thumbnail_thin">50dp</dimen> <dimen name="galleryblock_thumbnail_thin">100dp</dimen>
<dimen name="galleryblock_thumbnail_normal">150dp</dimen> <dimen name="galleryblock_thumbnail_normal">150dp</dimen>
</resources> </resources>

View File

@@ -53,6 +53,8 @@
<string name="main_drawer_group_contact_email">Email me!</string> <string name="main_drawer_group_contact_email">Email me!</string>
<string name="main_drawer_grouop_contact_discord">Discord</string> <string name="main_drawer_grouop_contact_discord">Discord</string>
<string name="main_menu_thin">Toggle Thin Mode</string>
<string name="main_menu_sort">Sort</string> <string name="main_menu_sort">Sort</string>
<string name="main_menu_sort_newest">Newest</string> <string name="main_menu_sort_newest">Newest</string>
<string name="main_menu_sort_popular">Popular</string> <string name="main_menu_sort_popular">Popular</string>
@@ -61,6 +63,7 @@
<string name="main_jump_message">Current page: %1$d\nMaximum page: %2$d</string> <string name="main_jump_message">Current page: %1$d\nMaximum page: %2$d</string>
<string name="main_open_gallery_by_id">Open Gallery by ID</string> <string name="main_open_gallery_by_id">Open Gallery by ID</string>
<string name="reader_failed_to_find_gallery">Failed to open gallery</string> <string name="reader_failed_to_find_gallery">Failed to open gallery</string>
<string name="main_fab_cancel">Cancel all downloads</string>
<string name="main_move">Move to page %1$d</string> <string name="main_move">Move to page %1$d</string>

View File

@@ -8,7 +8,7 @@ buildscript {
maven { url 'https://maven.fabric.io/public' } maven { url 'https://maven.fabric.io/public' }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.3' classpath 'com.android.tools.build:gradle:3.6.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"

View File

@@ -15,4 +15,3 @@ kotlin.code.style=official
android.enableJetifier=true android.enableJetifier=true
org.gradle.jvmargs=-Xmx1024M -Dkotlin.daemon.jvm.options\="-Xmx1024M" org.gradle.jvmargs=-Xmx1024M -Dkotlin.daemon.jvm.options\="-Xmx1024M"
android.useAndroidX=true android.useAndroidX=true
android.enableR8.fullMode=true

View File

@@ -1,6 +1,6 @@
#Fri Aug 23 08:21:15 KST 2019 #Tue Feb 25 09:55:23 KST 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

View File

@@ -30,6 +30,7 @@ import java.net.URL
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
const val hiyobi = "hiyobi.me" const val hiyobi = "hiyobi.me"
const val primary_img_domain = "cdn.hiyobi.me"
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" 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 = "" var cookie: String = ""
@@ -87,7 +88,7 @@ fun createImgList(galleryID: Int, reader: Reader, lowQuality: Boolean = false) =
if (lowQuality) if (lowQuality)
reader.galleryInfo.files.map { reader.galleryInfo.files.map {
val name = it.name.replace(Regex("""\.[^/.]+$"""), "") val name = it.name.replace(Regex("""\.[^/.]+$"""), "")
Images("$protocol//$hiyobi/data_r/$galleryID/$name.jpg", galleryID, it.name) Images("$protocol//$primary_img_domain/data_r/$galleryID/$name.jpg", galleryID, it.name)
} }
else else
reader.galleryInfo.files.map { Images("$protocol//$hiyobi/data/$galleryID/${it.name}", galleryID, it.name) } reader.galleryInfo.files.map { Images("$protocol//$primary_img_domain/data/$galleryID/${it.name}", galleryID, it.name) }