diff --git a/app/build.gradle b/app/build.gradle index 0f0316d4..7c9bf7f8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -74,14 +74,12 @@ dependencies { implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.github.arimorty:floatingsearchview:2.1.1' implementation 'com.github.clans:fab:1.6.4' - implementation 'com.github.bumptech.glide:glide:4.11.0' annotationProcessor '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") { transitive = false } - implementation 'net.rdrei.android.dirchooser:library:3.2@aar' implementation 'com.github.chrisbanes:PhotoView:2.3.0' implementation 'com.andrognito.patternlockview:patternlockview:1.0.0' diff --git a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt index 92c22553..2d73549c 100644 --- a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt @@ -105,9 +105,9 @@ class ExampleInstrumentedTest { val galleryID = 1561552 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") } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f9bf5296..addcb73f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ + ) : RecyclerSwipeAdapter(), SwipeAdapterInterface { +class GalleryBlockAdapter(private val context: Context, private val galleries: List) : RecyclerSwipeAdapter(), SwipeAdapterInterface { enum class ViewType { NEXT, @@ -68,11 +67,12 @@ class GalleryBlockAdapter(context: Context, private val galleries: List - Regex("^[0-9]+.+\$").matches(file.name) - } ?: 0 + progress = Cache(context).getImages(galleryID)?.size ?: 0 if (visibility == View.GONE) { visibility = View.VISIBLE @@ -126,6 +124,10 @@ class GalleryBlockAdapter(context: Context, private val galleries: List= 0) //download in progress + if (Cache(context).isDownloading(galleryID)) //download in progress worker.cancel(galleryID) else { Cache(context).setDownloading(galleryID, true) @@ -724,6 +731,15 @@ class MainActivity : AppCompatActivity() { setOnMenuItemClickListener { when(it.itemId) { 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 -> { sortMode = SortMode.NEWEST it.isChecked = true diff --git a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt index 5caf8770..f4a6c22e 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt @@ -362,7 +362,7 @@ class ReaderActivity : AppCompatActivity() { window.attributes = this } - reader_recyclerview.adapter?.notifyDataSetChanged() // Force to redraw + reader_recyclerview.adapter = reader_recyclerview.adapter // Force to redraw } private fun scrollMode(isScroll: Boolean) { @@ -388,7 +388,7 @@ class ReaderActivity : AppCompatActivity() { if (worker.progress[galleryID]?.all { !it.isFinite() } == true) // If download is finished, stop animating post { setImageResource(R.drawable.ic_download) - labelText = getString(R.string.reader_fab_download) + labelText = getString(R.string.reader_fab_download_cancel) } else // Or continue animate post { diff --git a/app/src/main/java/xyz/quaver/pupil/util/download/Cache.kt b/app/src/main/java/xyz/quaver/pupil/util/download/Cache.kt index 757dbf9a..14a0e7a1 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/download/Cache.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/download/Cache.kt @@ -21,21 +21,19 @@ package xyz.quaver.pupil.util.download import android.content.Context import android.content.ContextWrapper import android.util.Base64 +import android.util.Log import android.util.SparseArray import androidx.preference.PreferenceManager import com.crashlytics.android.Crashlytics -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import kotlinx.io.InputStream import xyz.quaver.Code import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.Reader import xyz.quaver.proxy -import xyz.quaver.pupil.util.copyRecursively import xyz.quaver.pupil.util.getCachedGallery import xyz.quaver.pupil.util.getDownloadDirectory +import xyz.quaver.pupil.util.isParentOf import xyz.quaver.pupil.util.json import java.io.File 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 { 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() + Log.i("PUPILD", "DELETED ${cache.canonicalPath}") } fun isDownloading(galleryID: Int) = getCachedMetadata(galleryID)?.isDownloading == true diff --git a/app/src/main/java/xyz/quaver/pupil/util/download/DownloadWorker.kt b/app/src/main/java/xyz/quaver/pupil/util/download/DownloadWorker.kt index 2d54ef0c..cd957af8 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/download/DownloadWorker.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/download/DownloadWorker.kt @@ -149,7 +149,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont private val loop = loop() private val worker = SparseArray() - val clients = SparseArray() val interceptor = Interceptor { chain -> val request = chain.request() @@ -159,7 +158,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont .body(ProgressResponseBody(request.tag(), response.body(), progressListener)) .build() } - fun buildClient() = + val client = OkHttpClient.Builder() .addInterceptor(interceptor) .connectTimeout(0, TimeUnit.SECONDS) @@ -172,17 +171,14 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont queue.clear() loop.cancel() - for (i in 0..worker.size()) { + for (i in 0 until worker.size()) { val galleryID = worker.keyAt(i) Cache(this@DownloadWorker).setDownloading(galleryID, false) worker[galleryID]?.cancel() } - for (i in 0 until clients.size()) { - clients.valueAt(i).dispatcher().cancelAll() - } - clients.clear() + client.dispatcher().cancelAll() progress.clear() exception.clear() @@ -194,17 +190,19 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont queue.remove(galleryID) worker[galleryID]?.cancel() - clients[galleryID]?.dispatcher()?.cancelAll() - clients.remove(galleryID) + client.dispatcher().queuedCalls().filter { + ((it.request().tag() as Pair<*, *>).first as Int) == galleryID + }.forEach { + it.cancel() + } progress.remove(galleryID) exception.remove(galleryID) notification.remove(galleryID) notificationManager.cancel(galleryID) - if (progress.indexOfKey(galleryID) >= 0) { + if (progress.indexOfKey(galleryID) >= 0) Cache(this@DownloadWorker).setDownloading(galleryID, false) - } } 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) }.build() - if (clients.get(galleryID) == null) - clients.put(galleryID, buildClient()) - - clients[galleryID]?.newCall(request)?.enqueue(callback) + client.newCall(request).enqueue(callback) } private fun download(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch { @@ -294,8 +289,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont notify(galleryID) CoroutineScope(Dispatchers.IO).launch { - if (isCompleted(galleryID) && clients.indexOfKey(galleryID) >= 0) { - clients.remove(galleryID) + if (isCompleted(galleryID)) { with(Cache(this@DownloadWorker)) { if (isDownloading(galleryID)) { moveToDownload(galleryID) @@ -320,8 +314,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont notify(galleryID) CoroutineScope(Dispatchers.IO).launch { - if (isCompleted(galleryID) && clients.indexOfKey(galleryID) >= 0) { - clients.remove(galleryID) + if (isCompleted(galleryID)) { with(Cache(this@DownloadWorker)) { if (isDownloading(galleryID)) { moveToDownload(galleryID) @@ -340,8 +333,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont notify(galleryID) CoroutineScope(Dispatchers.IO).launch { - if (isCompleted(galleryID) && clients.indexOfKey(galleryID) >= 0) { - clients.remove(galleryID) + if (isCompleted(galleryID)) { with(Cache(this@DownloadWorker)) { if (isDownloading(galleryID)) { moveToDownload(galleryID) @@ -418,7 +410,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont val galleryID = queue.peek() ?: continue - if (clients.indexOfKey(galleryID) >= 0) // Gallery already downloading! + if (progress.indexOfKey(galleryID) >= 0) // Gallery already downloading! continue if (notification[galleryID] == null) @@ -427,10 +419,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont if (Cache(this@DownloadWorker).isDownloading(galleryID)) notificationManager.notify(galleryID, notification[galleryID].build()) - if (clients.size() >= preferences.getInt("max_download", 4)) - continue - - Log.i("PUPILD", "QUEUED $galleryID #${clients.size()+1}") + Log.i("PUPILD", "QUEUED $galleryID") worker.put(galleryID, download(galleryID)) queue.poll() diff --git a/app/src/main/java/xyz/quaver/pupil/util/file.kt b/app/src/main/java/xyz/quaver/pupil/util/file.kt index 497424c1..4ed014e2 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/file.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/file.kt @@ -26,7 +26,6 @@ import android.os.storage.StorageManager import android.provider.DocumentsContract import androidx.core.content.ContextCompat import androidx.preference.PreferenceManager -import kotlinx.io.IOException import java.io.File import java.io.FileOutputStream 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) } -fun File.copyRecursively( - target: File, - 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 - } -} \ No newline at end of file +fun File.isParentOf(another: File) = + another.absolutePath.startsWith(this.absolutePath) \ No newline at end of file diff --git a/app/src/main/res/drawable/cancel.xml b/app/src/main/res/drawable/cancel.xml new file mode 100644 index 00000000..cad57ac3 --- /dev/null +++ b/app/src/main/res/drawable/cancel.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/img_thumbnail.jpg b/app/src/main/res/drawable/img_thumbnail.jpg deleted file mode 100644 index c6959911..00000000 Binary files a/app/src/main/res/drawable/img_thumbnail.jpg and /dev/null differ diff --git a/app/src/main/res/layout/activity_main_content.xml b/app/src/main/res/layout/activity_main_content.xml index 3041a890..aa8350ee 100644 --- a/app/src/main/res/layout/activity_main_content.xml +++ b/app/src/main/res/layout/activity_main_content.xml @@ -82,6 +82,13 @@ android:layout_margin="16dp" app:menu_colorNormal="@color/colorAccent"> + + + + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 5cb0d9a4..5c54d534 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -130,4 +130,6 @@ エラー サーバーアドレス サーバー + 簡単モード + すべてのダウンロード取り消し \ No newline at end of file diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 05c173f1..99ebfe1f 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -130,4 +130,6 @@ 잘못된 값 서버 주소 서버 + 간단히 보기 모드 + 다운로드 모두 취소 \ No newline at end of file diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 68307f90..64a437a1 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -11,6 +11,6 @@ 8dp - 50dp + 100dp 150dp \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c79b2143..fd51625d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -53,6 +53,8 @@ Email me! Discord + Toggle Thin Mode + Sort Newest Popular @@ -61,6 +63,7 @@ Current page: %1$d\nMaximum page: %2$d Open Gallery by ID Failed to open gallery + Cancel all downloads Move to page %1$d