From 35ee438376d6bdb2688a9d9ae578a0bdf0684ed6 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Sat, 24 Jul 2021 09:48:54 +0900 Subject: [PATCH] Reimplemented sort [WIP] ImHentai --- app/build.gradle | 10 +- .../java/xyz/quaver/pupil/sources/Common.kt | 22 ++--- .../xyz/quaver/pupil/sources/Downloads.kt | 4 +- .../java/xyz/quaver/pupil/sources/History.kt | 4 +- .../java/xyz/quaver/pupil/sources/Hitomi.kt | 16 ++-- .../java/xyz/quaver/pupil/sources/ImHentai.kt | 88 ++++++++++++++++++ .../java/xyz/quaver/pupil/ui/MainActivity.kt | 54 +++++------ .../pupil/ui/viewmodel/MainViewModel.kt | 11 ++- app/src/main/res/drawable/ic_hiyobi.png | Bin 159 -> 0 bytes app/src/main/res/drawable/ic_imhentai.png | Bin 0 -> 3719 bytes app/src/main/res/values/arrays.xml | 12 +++ app/src/main/res/xml/imhentai_preferences.xml | 21 +++++ 12 files changed, 179 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/xyz/quaver/pupil/sources/ImHentai.kt delete mode 100644 app/src/main/res/drawable/ic_hiyobi.png create mode 100644 app/src/main/res/drawable/ic_imhentai.png create mode 100644 app/src/main/res/xml/imhentai_preferences.xml diff --git a/app/build.gradle b/app/build.gradle index fa929846..e3839680 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -74,14 +74,14 @@ dependencies { implementation "io.ktor:ktor-client-serialization:1.6.1" implementation "androidx.appcompat:appcompat:1.3.0" - implementation "androidx.activity:activity-ktx:1.3.0-rc01" - implementation "androidx.fragment:fragment-ktx:1.3.5" + implementation "androidx.activity:activity-ktx:1.3.0-rc02" + implementation "androidx.fragment:fragment-ktx:1.3.6" implementation "androidx.preference:preference-ktx:1.1.1" 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-beta01" + implementation "androidx.work:work-runtime-ktx:2.6.0-beta02" implementation 'org.kodein.di:kodein-di-framework-android-x:7.6.0' @@ -104,7 +104,7 @@ dependencies { implementation 'com.github.piasy:FrescoImageLoader:1.8.0' implementation 'com.github.piasy:FrescoImageViewFactory:1.8.0' - implementation "org.jsoup:jsoup:1.13.1" + implementation "org.jsoup:jsoup:1.14.1" implementation "com.tbuonomo:dotsindicator:4.2" @@ -120,7 +120,7 @@ dependencies { debugImplementation "com.orhanobut:logger:2.2.0" debugImplementation "com.squareup.leakcanary:leakcanary-android:2.6" - testImplementation "junit:junit:4.13.1" + testImplementation "junit:junit:4.13.2" testImplementation "org.mockito:mockito-inline:3.11.2" androidTestImplementation "androidx.test.ext:junit:1.1.3" 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 85ac3113..290e6906 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/Common.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/Common.kt @@ -107,25 +107,21 @@ data class ItemInfo( } } -enum class DefaultSortMode : SortModeInterface { - DEFAULT -} - @Parcelize class DefaultSearchSuggestion(override val body: String) : SearchSuggestion interface SortModeInterface { val ordinal: Int - val name: String + val name: Int } abstract class Source { abstract val name: String abstract val iconResID: Int abstract val preferenceID: Int - abstract val availableSortMode: List + abstract val availableSortMode: List - abstract suspend fun search(query: String, range: IntRange, sortMode: SortModeInterface) : Pair, Int> + abstract suspend fun search(query: String, range: IntRange, sortMode: Int) : Pair, Int> abstract suspend fun suggestion(query: String) : List abstract suspend fun images(itemID: String) : List abstract suspend fun info(itemID: String) : ItemInfo @@ -146,11 +142,13 @@ val sourceModule = DI.Module(name = "source") { bindSet() bindSet() - listOf( - Hitomi() - ).forEach { source -> - inSet { multiton { _: Unit -> source.name to source } } - inSet { singleton { source.name to source.preferenceID } } + onReady { + listOf( + Hitomi(instance()) + ).forEach { source -> + inSet { multiton { _: Unit -> source.name to source } } + inSet { singleton { source.name to source.preferenceID } } + } } bind { factory { source: String -> History(di, source) } } diff --git a/app/src/main/java/xyz/quaver/pupil/sources/Downloads.kt b/app/src/main/java/xyz/quaver/pupil/sources/Downloads.kt index aff9e9d1..a889c10a 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/Downloads.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/Downloads.kt @@ -41,11 +41,11 @@ class Downloads(override val di: DI) : Source(), DIAware { get() = R.drawable.ic_download override val preferenceID: Int get() = R.xml.download_preferences - override val availableSortMode: List = DefaultSortMode.values().toList() + override val availableSortMode: List = emptyList() private val downloadManager: DownloadManager by instance() - override suspend fun search(query: String, range: IntRange, sortMode: SortModeInterface): Pair, Int> { + override suspend fun search(query: String, range: IntRange, sortMode: Int): Pair, Int> { val downloads = downloadManager.downloads.toList() val channel = Channel() diff --git a/app/src/main/java/xyz/quaver/pupil/sources/History.kt b/app/src/main/java/xyz/quaver/pupil/sources/History.kt index 2500ab1f..8231321e 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/History.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/History.kt @@ -40,9 +40,9 @@ class History(override val di: DI, source: String) : Source(), DIAware { get() = source.iconResID override val preferenceID: Int get() = source.preferenceID - override val availableSortMode: List = DefaultSortMode.values().toList() + override val availableSortMode: List = emptyList() - override suspend fun search(query: String, range: IntRange, sortMode: SortModeInterface): Pair, Int> { + override suspend fun search(query: String, range: IntRange, sortMode: Int): Pair, Int> { val channel = Channel() CoroutineScope(Dispatchers.IO).launch { diff --git a/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt b/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt index bc265fc3..4a7bcc2f 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt @@ -18,6 +18,7 @@ package xyz.quaver.pupil.sources +import android.app.Application import android.view.LayoutInflater import android.widget.TextView import io.ktor.http.* @@ -35,12 +36,7 @@ import xyz.quaver.pupil.util.wordCapitalize import kotlin.math.max import kotlin.math.min -class Hitomi : Source() { - - enum class SortMode : SortModeInterface { - NEWEST, - POPULAR - } +class Hitomi(app: Application) : Source() { @Parcelize data class TagSuggestion(val s: String, val t: Int, val u: String, val n: String) : SearchSuggestion { @@ -60,18 +56,18 @@ class Hitomi : Source() { override val name: String = "hitomi.la" override val iconResID: Int = R.drawable.hitomi override val preferenceID: Int = R.xml.hitomi_preferences - override val availableSortMode: List = SortMode.values().toList() + override val availableSortMode: List = app.resources.getStringArray(R.array.hitomi_sort_mode).toList() var cachedQuery: String? = null - var cachedSortMode: SortMode? = null + var cachedSortMode: Int = -1 val cache = mutableListOf() - override suspend fun search(query: String, range: IntRange, sortMode: SortModeInterface): Pair, Int> { + override suspend fun search(query: String, range: IntRange, sortMode: Int): Pair, Int> { if (cachedQuery != query || cachedSortMode != sortMode || cache.isEmpty()) { cachedQuery = null cache.clear() yield() - doSearch("$query ${Preferences["hitomi.default_query", ""]}", sortMode == SortMode.POPULAR).let { + doSearch("$query ${Preferences["hitomi.default_query", ""]}", sortMode == 1).let { yield() cache.addAll(it) } diff --git a/app/src/main/java/xyz/quaver/pupil/sources/ImHentai.kt b/app/src/main/java/xyz/quaver/pupil/sources/ImHentai.kt new file mode 100644 index 00000000..188a6fc8 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/sources/ImHentai.kt @@ -0,0 +1,88 @@ +/* + * 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 io.ktor.client.* +import io.ktor.client.features.* +import io.ktor.client.features.get +import io.ktor.client.request.* +import io.ktor.client.statement.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.jsoup.Jsoup +import org.jsoup.nodes.Element +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.pupil.R + +class ImHentai(override val di: DI) : Source(), DIAware { + + private val app: Application by instance() + private val client: HttpClient by instance() + + override val name: String + get() = ImHentai.name + override val iconResID: Int + get() = R.drawable.ic_imhentai + override val preferenceID: Int + get() = R.xml.imhentai_preferences + override val availableSortMode = app.resources.getStringArray(R.array.imhentai_sort_mode).toList() + + override suspend fun search(query: String, range: IntRange, sortMode: Int): Pair, Int> = withContext(Dispatchers.IO) { + val channel = Channel() + + val doc = Jsoup.connect("https://imhentai.xxx/search/?key=$query").get() + + val count = countRegex.find(doc.getElementsByClass("heading2").text())?.groupValues?.get(1)?.toIntOrNull() ?: 0 + + launch { + doc.getElementsByClass("thumb") + } + + return@withContext Pair(channel, count) + } + + override suspend fun suggestion(query: String): List { + TODO("Not yet implemented") + } + + 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 const val name = "imhentai" + private val countRegex = Regex("""\(\d+\) results found.""") + private val idRegex = Regex("""/gallery/(\d+)/""") + + private fun transform(item: Element) { + val caption = item.select(".caption a") + } + } + +} \ 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 6cf44c3b..6b07c11a 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -41,7 +41,6 @@ import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.CircularProgressDrawable import com.daimajia.swipe.adapters.RecyclerSwipeAdapter import com.google.android.material.navigation.NavigationView -import com.google.android.material.snackbar.Snackbar import com.orhanobut.logger.Logger import kotlinx.coroutines.* import org.kodein.di.DIAware @@ -96,8 +95,18 @@ class MainActivity : binding.contents.searchview.binding.querySection.menuView.menuItems.findMenu(R.id.sort).subMenu.apply { clear() - it.forEach { - add(R.id.sort_mode_group_id, it.ordinal, Menu.NONE, it.name) + it.forEachIndexed { index, sortMode -> + add(R.id.sort_mode_group_id, index, Menu.NONE, sortMode).setOnMenuItemClickListener { + model.setPage(1) + model.sortModeIndex.value = it.itemId + + children.forEachIndexed { menuIndex, menuItem -> + menuItem.isChecked = menuIndex == index + } + + model.query() + true + } } setGroupCheckable(R.id.sort_mode_group_id, true, true) @@ -417,32 +426,23 @@ class MainActivity : } private fun onActionMenuItemSelected(item: MenuItem?) { - if (item?.groupId == R.id.sort_mode_group_id) { - model.setPage(1) - model.sortMode.value = model.availableSortMode.value?.let { availableSortMode -> - availableSortMode.getOrElse(item.itemId) { availableSortMode.first() } - } + when(item?.itemId) { + R.id.main_menu_settings -> startActivity(Intent(this@MainActivity, SettingsActivity::class.java)) + R.id.source -> SourceSelectDialog().apply { + onSourceSelectedListener = { + model.setSourceAndReset(it) - model.query() + dismiss() + } + + onSourceSettingsSelectedListener = { + startActivity(Intent(this@MainActivity, SettingsActivity::class.java).putExtra(SettingsActivity.SETTINGS_EXTRA, it)) + + refreshOnResume = true + dismiss() + } + }.show(supportFragmentManager, null) } - else - when(item?.itemId) { - R.id.main_menu_settings -> startActivity(Intent(this@MainActivity, SettingsActivity::class.java)) - R.id.source -> SourceSelectDialog().apply { - onSourceSelectedListener = { - model.setSourceAndReset(it) - - dismiss() - } - - onSourceSettingsSelectedListener = { - startActivity(Intent(this@MainActivity, SettingsActivity::class.java).putExtra(SettingsActivity.SETTINGS_EXTRA, it)) - - refreshOnResume = true - dismiss() - } - }.show(supportFragmentManager, null) - } } override fun onNavigationItemSelected(item: MenuItem): Boolean { 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 a68a7eb2..f3c7647d 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 @@ -60,7 +60,8 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { val availableSortMode = Transformations.map(_source) { it.availableSortMode } - val sortMode = MutableLiveData() + + val sortModeIndex = MutableLiveData() val sourceIcon = Transformations.map(_source) { it.iconResID @@ -86,7 +87,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { fun setSourceAndReset(sourceName: String) { _source.value = sourceFactory(sourceName).also { - sortMode.value = it.availableSortMode.first() + sortModeIndex.value = 0 } setQueryAndSearch() @@ -119,7 +120,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { fun query() { val perPage = Preferences["per_page", "25"].toInt() val source = _source.value ?: error("Source is null") - val sortMode = sortMode.value ?: source.availableSortMode.first() + val sortModeIndex = sortModeIndex.value ?: 0 val currentPage = currentPage.value ?: 1 suggestionJob?.cancel() @@ -134,7 +135,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { val (channel, count) = source.search( query.value ?: "", (currentPage - 1) * perPage until currentPage * perPage, - sortMode + sortModeIndex ) totalItems.postValue(count) @@ -168,7 +169,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { _source.value?.search( query.value + Preferences["default_query", ""], random .. random, - sortMode.value!! + sortModeIndex.value!! )?.first?.receive() }?.let(callback) } diff --git a/app/src/main/res/drawable/ic_hiyobi.png b/app/src/main/res/drawable/ic_hiyobi.png deleted file mode 100644 index 2af60c05bb97b9d184397e154222ffdf7798f9c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9JOMr-uK$4y28RC(|AD}=|Lh$g z$IsKnF@)o~?U{vw2NZZ5F0L=Iwec?PUy*l!E2rUM-)XOzN~RWiu2*e-D)25aIT1A1 z`>^Nd0FyYES59lCi+va7hrXNO$d}Dtc_Mml{Z9sw8w$s#{n*J1vd7ca&t;ucLK6TV COE=^I diff --git a/app/src/main/res/drawable/ic_imhentai.png b/app/src/main/res/drawable/ic_imhentai.png new file mode 100644 index 0000000000000000000000000000000000000000..191eb1d5ecd544f603c4e81bc89ae2bcef5581e1 GIT binary patch literal 3719 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 1e2fd1df..e446095d 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -19,4 +19,16 @@ SOCKS + + NEWEST + POPULAR + + + + LATEST + DOWNLOADED + POPULAR + TOP RATED + + \ No newline at end of file diff --git a/app/src/main/res/xml/imhentai_preferences.xml b/app/src/main/res/xml/imhentai_preferences.xml new file mode 100644 index 00000000..6f4b1e38 --- /dev/null +++ b/app/src/main/res/xml/imhentai_preferences.xml @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file