diff --git a/app/build.gradle b/app/build.gradle index b73ad775..b5c1eb32 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,7 +23,6 @@ android { versionCode 65 versionName "6.0.0-alpha2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - vectorDrawables.useSupportLibrary = true } buildTypes { debug { @@ -66,21 +65,21 @@ android { dependencies { implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"]) - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.0" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.10" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0-RC" implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.0" - implementation "androidx.appcompat:appcompat:1.2.0" - implementation "androidx.activity:activity-ktx:1.3.0-alpha07" - implementation "androidx.fragment:fragment-ktx:1.3.3" + implementation "androidx.appcompat:appcompat:1.3.0" + implementation "androidx.activity:activity-ktx:1.3.0-beta01" + implementation "androidx.fragment:fragment-ktx:1.3.4" implementation "androidx.preference:preference-ktx:1.1.1" - implementation "androidx.recyclerview:recyclerview:1.2.0" + implementation "androidx.recyclerview:recyclerview:1.2.1" implementation "androidx.constraintlayout:constraintlayout:2.0.4" implementation "androidx.gridlayout:gridlayout:1.0.0" implementation "androidx.biometric:biometric:1.1.0" - implementation "androidx.work:work-runtime-ktx:2.6.0-alpha02" + implementation "androidx.work:work-runtime-ktx:2.6.0-beta01" - implementation 'org.kodein.di:kodein-di-framework-android-x:7.5.0' + implementation 'org.kodein.di:kodein-di-framework-android-x:7.6.0' implementation "com.daimajia.swipelayout:library:1.2.0@aar" @@ -111,7 +110,7 @@ dependencies { implementation "ru.noties.markwon:core:3.1.0" - implementation "xyz.quaver:libpupil:2.0.0" + implementation "xyz.quaver:libpupil:2.1.0" implementation "xyz.quaver:documentfilex:0.6.1" implementation "xyz.quaver:floatingsearchview:1.1.7" diff --git a/app/src/main/java/xyz/quaver/pupil/sources/Common.kt b/app/src/main/java/xyz/quaver/pupil/sources/Common.kt index e3442ad7..35e9028d 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/Common.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/Common.kt @@ -151,4 +151,5 @@ val sourceModule = DI.Module(name = "source") { } bind { factory { source: String -> History(di, source) } } + bind { singleton { Downloads(di) } } } \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/sources/Downloads.kt b/app/src/main/java/xyz/quaver/pupil/sources/Downloads.kt new file mode 100644 index 00000000..e5f57669 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/sources/Downloads.kt @@ -0,0 +1,103 @@ +/* + * Pupil, Hitomi.la viewer for Android + * Copyright (C) 2021 tom5079 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package xyz.quaver.pupil.sources + +import android.app.Application +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import org.kodein.di.DI +import org.kodein.di.DIAware +import org.kodein.di.instance +import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion +import xyz.quaver.io.FileX +import xyz.quaver.io.util.getChild +import xyz.quaver.pupil.R +import xyz.quaver.pupil.util.DownloadManager +import kotlin.math.max +import kotlin.math.min + +class Downloads(override val di: DI) : Source(), DIAware { + + override val name: String + get() = "Downloads" + override val iconResID: Int + get() = R.drawable.ic_download + override val preferenceID: Int + get() = -1 + override val availableSortMode: Array = DefaultSortMode.values() + + private val downloadManager: DownloadManager by instance() + + private val applicationContext: Application by instance() + + override suspend fun search(query: String, range: IntRange, sortMode: Enum<*>): Pair, Int> { + val downloads = downloadManager.downloads.toList() + + val channel = Channel() + val sanitizedRange = max(0, range.first) .. min(range.last, downloads.size - 1) + + CoroutineScope(Dispatchers.IO).launch { + downloads.slice(sanitizedRange).map { (_, folderName) -> + transform(downloadManager.downloadFolder.getChild(folderName)) + }.forEach { + channel.send(it) + } + + channel.close() + } + + return Pair(channel, downloads.size) + } + + override suspend fun suggestion(query: String): List { + return emptyList() + } + + override suspend fun images(itemID: String): List { + TODO("Not yet implemented") + } + + override suspend fun info(itemID: String): ItemInfo { + TODO("Not yet implemented") + } + + companion object { + private fun firstImage(folder: FileX): String? = + folder.list { _, name -> + name.takeLastWhile { it != '.' } !in listOf("jpg", "png", "gif", "webp") + }?.firstOrNull() + + fun transform(folder: FileX): ItemInfo = + kotlin.runCatching { + Json.decodeFromString(folder.getChild(".metadata").readText()) + }.getOrNull() ?: + ItemInfo( + "Downloads", + "", + folder.name, + firstImage(folder) ?: "", + "" + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index 3d255dff..8d918db9 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -239,14 +239,12 @@ class MainActivity : binding.navView.setNavigationItemSelectedListener(this) with (binding.contents.cancelFab) { - setImageResource(R.drawable.cancel) setOnClickListener { } } with (binding.contents.jumpFab) { - setImageResource(R.drawable.ic_jump) setOnClickListener { val perPage = Preferences["per_page", "25"].toInt() val editText = EditText(context) @@ -269,12 +267,10 @@ class MainActivity : } with (binding.contents.randomFab) { - setImageResource(R.drawable.shuffle_variant) setOnClickListener { setImageDrawable(CircularProgressDrawable(context)) model.random { runOnUiThread { - setImageResource(R.drawable.shuffle_variant) GalleryDialogFragment(model.source.value!!.name, it.id).apply { onChipClickedHandler.add { model.setQueryAndSearch(it.toQuery()) @@ -286,7 +282,6 @@ class MainActivity : } with (binding.contents.idFab) { - setImageResource(R.drawable.numeric) setOnClickListener { val editText = EditText(context).apply { inputType = InputType.TYPE_CLASS_NUMBER @@ -469,6 +464,7 @@ class MainActivity : when(item.itemId) { R.id.main_drawer_home -> model.setModeAndReset(MainViewModel.MainMode.SEARCH) R.id.main_drawer_history -> model.setModeAndReset(MainViewModel.MainMode.HISTORY) + R.id.main_drawer_downloads -> model.setModeAndReset(MainViewModel.MainMode.DOWNLOADS) R.id.main_drawer_help -> 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)))) R.id.main_drawer_homepage -> startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.home_page)))) diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadLocationDialogFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadLocationDialogFragment.kt index 5a8b2aea..c666e7d9 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadLocationDialogFragment.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadLocationDialogFragment.kt @@ -79,7 +79,7 @@ class DownloadLocationDialogFragment : DialogFragment(), DIAware { } } } else { - val downloadFolder = DownloadManager.getInstance(context ?: return@registerForActivityResult).downloadFolder.canonicalPath + val downloadFolder = downloadManager.downloadFolder.canonicalPath val key = entries.keys.firstOrNull { it?.canonicalPath == downloadFolder } if (key == null) entries[key]!!.locationAvailable.text = downloadFolder diff --git a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt index 0374f9cf..ec4796dc 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt @@ -28,6 +28,7 @@ import org.kodein.di.direct import org.kodein.di.instance import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion import xyz.quaver.pupil.sources.AnySource +import xyz.quaver.pupil.sources.Downloads import xyz.quaver.pupil.sources.History import xyz.quaver.pupil.sources.ItemInfo import xyz.quaver.pupil.util.Preferences @@ -106,6 +107,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { sourceFactory = when (mode) { MainMode.SEARCH -> defaultSourceFactory MainMode.HISTORY -> { { direct.instance(arg = it) } } + MainMode.DOWNLOADS -> { { direct.instance() } } else -> return } diff --git a/app/src/main/java/xyz/quaver/pupil/util/DownloadManager.kt b/app/src/main/java/xyz/quaver/pupil/util/DownloadManager.kt index 70916eb7..80278d03 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/DownloadManager.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/DownloadManager.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.launch import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json +import okhttp3.internal.toImmutableMap import org.kodein.di.DIAware import org.kodein.di.android.closestDI import xyz.quaver.io.FileX @@ -72,6 +73,9 @@ class DownloadManager constructor(context: Context) : ContextWrapper(context), D return downloadFolderMapInstance ?: mutableMapOf() } + val downloads: Map + get() = downloadFolderMap.toImmutableMap() + @Synchronized fun getDownloadFolder(source: String, itemID: String): FileX? = downloadFolderMap["$source-$itemID"]?.let { downloadFolder.getChild(it) } diff --git a/app/src/main/res/layout/main_activity_content.xml b/app/src/main/res/layout/main_activity_content.xml index 29150271..c10da9bb 100644 --- a/app/src/main/res/layout/main_activity_content.xml +++ b/app/src/main/res/layout/main_activity_content.xml @@ -83,6 +83,7 @@ android:id="@+id/cancel_fab" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:src="@drawable/cancel" app:fab_label="@string/main_fab_cancel" app:fab_size="mini"/> @@ -90,6 +91,7 @@ android:id="@+id/jump_fab" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:src="@drawable/ic_jump" app:fab_label="@string/main_jump_title" app:fab_size="mini"/> @@ -97,6 +99,7 @@ android:id="@+id/random_fab" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:src="@drawable/shuffle_variant" app:fab_label="@string/main_fab_random" app:fab_size="mini"/> @@ -104,6 +107,7 @@ android:id="@+id/id_fab" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:src="@drawable/numeric" app:fab_label="@string/main_open_gallery_by_id" app:fab_size="mini"/> diff --git a/build.gradle b/build.gradle index b86ae168..add77926 100644 --- a/build.gradle +++ b/build.gradle @@ -10,10 +10,10 @@ buildscript { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - classpath "com.google.gms:google-services:4.3.5" + classpath "com.google.gms:google-services:4.3.8" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files - classpath "com.google.firebase:firebase-crashlytics-gradle:2.6.0" + classpath "com.google.firebase:firebase-crashlytics-gradle:2.7.0" classpath "com.google.firebase:perf-plugin:1.4.0" classpath "com.google.android.gms:oss-licenses-plugin:0.10.4" } diff --git a/gradle.properties b/gradle.properties index ddb58e41..5c529e8f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,4 +21,4 @@ android.enableJetifier=true android.useAndroidX=true android.enableBuildCache=true -kotlin_version=1.5.0 \ No newline at end of file +kotlin_version=1.5.10 \ No newline at end of file