Added History
This commit is contained in:
@@ -134,9 +134,9 @@ dependencies {
|
||||
|
||||
implementation "ru.noties.markwon:core:3.1.0"
|
||||
|
||||
implementation "xyz.quaver:libpupil:1.9.7-SNAPSHOT"
|
||||
implementation "xyz.quaver:libpupil:1.9.7"
|
||||
implementation "xyz.quaver:documentfilex:0.4-alpha02"
|
||||
implementation "xyz.quaver:floatingsearchview:1.1.1"
|
||||
implementation "xyz.quaver:floatingsearchview:1.1.3-SNAPSHOT"
|
||||
|
||||
implementation "com.orhanobut:logger:2.2.0"
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ class MirrorAdapter(context: Context) : RecyclerView.Adapter<MirrorAdapter.ViewH
|
||||
val list = mirrors.keys.toMutableList().apply {
|
||||
Preferences.get<String>("mirrors")
|
||||
.split(">")
|
||||
.reversed()
|
||||
.asReversed()
|
||||
.forEach {
|
||||
if (this.contains(it)) {
|
||||
this.remove(it)
|
||||
|
||||
@@ -18,15 +18,12 @@
|
||||
|
||||
package xyz.quaver.pupil.sources
|
||||
|
||||
import android.util.Log
|
||||
import com.orhanobut.logger.Logger
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.di
|
||||
import org.kodein.di.instance
|
||||
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
|
||||
import xyz.quaver.pupil.util.SavedSourceSet
|
||||
@@ -47,9 +44,11 @@ class History(override val di: DI, source: String) : Source<DefaultSortMode, Sea
|
||||
val channel = Channel<ItemInfo>()
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
histories.map[source.name]?.forEach {
|
||||
histories[source.name]?.asReversed()?.forEach {
|
||||
channel.send(source.info(it))
|
||||
}
|
||||
|
||||
channel.close()
|
||||
}
|
||||
|
||||
return Pair(channel, histories.map.size)
|
||||
|
||||
@@ -148,6 +148,7 @@ class MainActivity :
|
||||
} else {
|
||||
binding.contents.progressbar.hide()
|
||||
if (it.isEmpty()) {
|
||||
binding.contents.recyclerview.adapter?.notifyDataSetChanged()
|
||||
binding.contents.noresult.show()
|
||||
} else {
|
||||
binding.contents.recyclerview.adapter?.notifyItemInserted(it.size-1)
|
||||
@@ -396,7 +397,7 @@ class MainActivity :
|
||||
onActionMenuItemSelected(it)
|
||||
}
|
||||
|
||||
onQueryChangeListener = lambda@{ _, query ->
|
||||
onQueryChangeListener = { _, query ->
|
||||
model.query.value = query
|
||||
|
||||
model.suggestion()
|
||||
@@ -404,9 +405,7 @@ class MainActivity :
|
||||
swapSuggestions(listOf(LoadingSuggestion(getText(R.string.reader_loading).toString())))
|
||||
}
|
||||
|
||||
onSuggestionBinding = { binding, item ->
|
||||
model.source.value!!.onSuggestionBind(binding, item)
|
||||
}
|
||||
onSuggestionBinding = model.source.value!!::onSuggestionBind
|
||||
|
||||
onFocusChangeListener = object: FloatingSearchView.OnFocusChangeListener {
|
||||
override fun onFocus() {
|
||||
@@ -450,24 +449,13 @@ class MainActivity :
|
||||
binding.drawer.closeDrawers()
|
||||
|
||||
when(item.itemId) {
|
||||
R.id.main_drawer_history -> {
|
||||
//model.setSourceAndReset(direct.instance<String, History>(arg = source.name))
|
||||
}
|
||||
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))))
|
||||
}
|
||||
R.id.main_drawer_email -> {
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.email))))
|
||||
}
|
||||
R.id.main_drawer_kakaotalk -> {
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.discord))))
|
||||
}
|
||||
R.id.main_drawer_home -> model.setModeAndReset(MainViewModel.MainMode.SEARCH)
|
||||
R.id.main_drawer_history -> model.setModeAndReset(MainViewModel.MainMode.HISTORY)
|
||||
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))))
|
||||
R.id.main_drawer_email -> startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.email))))
|
||||
R.id.main_drawer_kakaotalk -> startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.discord))))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ class ReaderActivity : BaseActivity(), DIAware {
|
||||
menu?.forEach {
|
||||
when (it.itemId) {
|
||||
R.id.reader_menu_favorite -> {
|
||||
if (favorites.map[source]?.contains(itemID) == true)
|
||||
if (favorites[source]?.contains(itemID) == true)
|
||||
(it.icon as Animatable).start()
|
||||
}
|
||||
R.id.source -> {
|
||||
@@ -160,7 +160,7 @@ class ReaderActivity : BaseActivity(), DIAware {
|
||||
val id = itemID
|
||||
val favorite = menu?.findItem(R.id.reader_menu_favorite) ?: return true
|
||||
|
||||
if (favorites.map[source]?.contains(id) == true) {
|
||||
if (favorites[source]?.contains(id) == true) {
|
||||
favorites.remove(source, id)
|
||||
favorite.icon = AnimatedVectorDrawableCompat.create(this, R.drawable.avd_star)
|
||||
} else {
|
||||
|
||||
@@ -75,7 +75,7 @@ class TagChip(context: Context, private val source: String, _tag: Tag) : Chip(co
|
||||
)
|
||||
|
||||
setOnCloseIconClickListener {
|
||||
if (favoriteTags.map[source]?.contains(tag.toString()) == true) {
|
||||
if (favoriteTags[source]?.contains(tag.toString()) == true) {
|
||||
favoriteTags.remove(source, tag.toString())
|
||||
closeIcon = ContextCompat.getDrawable(context, R.drawable.ic_star_empty)
|
||||
|
||||
|
||||
@@ -25,8 +25,10 @@ import kotlinx.coroutines.*
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.x.di
|
||||
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.History
|
||||
import xyz.quaver.pupil.sources.ItemInfo
|
||||
import xyz.quaver.pupil.util.Preferences
|
||||
import xyz.quaver.pupil.util.notify
|
||||
@@ -37,7 +39,6 @@ import kotlin.random.Random
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
||||
|
||||
override val di by di()
|
||||
|
||||
private val _searchResults = MutableLiveData<MutableList<ItemInfo>>()
|
||||
@@ -51,6 +52,10 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
||||
val query = MutableLiveData<String>()
|
||||
private val queryStack = mutableListOf<String>()
|
||||
|
||||
private val defaultSourceFactory: (String) -> AnySource = {
|
||||
direct.source(it)
|
||||
}
|
||||
private var sourceFactory: (String) -> AnySource = defaultSourceFactory
|
||||
private val _source = MutableLiveData<AnySource>()
|
||||
val source: LiveData<AnySource> = _source
|
||||
|
||||
@@ -82,7 +87,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
||||
}
|
||||
|
||||
fun setSourceAndReset(sourceName: String) {
|
||||
_source.value = direct.source(sourceName).also {
|
||||
_source.value = sourceFactory(sourceName).also {
|
||||
sortMode.value = it.availableSortMode.first()
|
||||
}
|
||||
|
||||
@@ -97,6 +102,16 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
||||
query()
|
||||
}
|
||||
|
||||
fun setModeAndReset(mode: MainMode) {
|
||||
sourceFactory = when (mode) {
|
||||
MainMode.SEARCH -> defaultSourceFactory
|
||||
MainMode.HISTORY -> { { direct.instance<String, History>(arg = it) } }
|
||||
else -> return
|
||||
}
|
||||
|
||||
setSourceAndReset(source.value!!.name)
|
||||
}
|
||||
|
||||
fun query() {
|
||||
val perPage = Preferences["per_page", "25"].toInt()
|
||||
val source = _source.value ?: error("Source is null")
|
||||
@@ -181,4 +196,11 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
||||
return true
|
||||
}
|
||||
|
||||
enum class MainMode {
|
||||
SEARCH,
|
||||
HISTORY,
|
||||
DOWNLOADS,
|
||||
FAVORITES
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,81 +21,14 @@ package xyz.quaver.pupil.util
|
||||
import androidx.annotation.RequiresApi
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.ListSerializer
|
||||
import kotlinx.serialization.builtins.MapSerializer
|
||||
import kotlinx.serialization.builtins.SetSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.Json.Default.decodeFromString
|
||||
import kotlinx.serialization.serializer
|
||||
import java.io.File
|
||||
|
||||
class SavedSet <T: Any> (private val file: File, any: T, private val set: MutableSet<T> = mutableSetOf()) : MutableSet<T> by set {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
val serializer: KSerializer<Set<T>> = SetSerializer(serializer(any::class.java) as KSerializer<T>)
|
||||
|
||||
init {
|
||||
if (!file.exists()) {
|
||||
save()
|
||||
}
|
||||
load()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun load() {
|
||||
set.clear()
|
||||
kotlin.runCatching {
|
||||
decodeFromString(serializer, file.readText())
|
||||
}.onSuccess {
|
||||
set.addAll(it)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun save() {
|
||||
file.parentFile?.mkdirs()
|
||||
if (!file.exists())
|
||||
file.createNewFile()
|
||||
|
||||
file.writeText(Json.encodeToString(serializer, set))
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun add(element: T): Boolean {
|
||||
set.remove(element)
|
||||
|
||||
return set.add(element).also {
|
||||
save()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun addAll(elements: Collection<T>): Boolean {
|
||||
set.removeAll(elements)
|
||||
|
||||
return set.addAll(elements).also {
|
||||
save()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun remove(element: T): Boolean {
|
||||
load()
|
||||
|
||||
return set.remove(element).also {
|
||||
save()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun clear() {
|
||||
set.clear()
|
||||
save()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SavedMap <K: Any, V: Any> (private val file: File, anyKey: K, anyValue: V, private val map: MutableMap<K, V> = mutableMapOf()) : MutableMap<K, V> by map {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -172,11 +105,10 @@ class SavedMap <K: Any, V: Any> (private val file: File, anyKey: K, anyValue: V,
|
||||
}
|
||||
|
||||
class SavedSourceSet(private val file: File) {
|
||||
private val _map = mutableMapOf<String, MutableList<String>>()
|
||||
val map: Map<String, List<String>> = _map
|
||||
|
||||
private val _map = mutableMapOf<String, MutableSet<String>>()
|
||||
val map: Map<String, Set<String>> = _map
|
||||
|
||||
private val serializer = MapSerializer(String.serializer(), SetSerializer(String.serializer()))
|
||||
private val serializer = MapSerializer(String.serializer(), ListSerializer(String.serializer()))
|
||||
|
||||
@Synchronized
|
||||
fun load() {
|
||||
@@ -185,7 +117,7 @@ class SavedSourceSet(private val file: File) {
|
||||
decodeFromString(serializer, file.readText())
|
||||
}.onSuccess {
|
||||
it.forEach { (k, v) ->
|
||||
_map[k] = v.toMutableSet()
|
||||
_map[k] = v.toMutableList()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,6 +131,8 @@ class SavedSourceSet(private val file: File) {
|
||||
file.writeText(Json.encodeToString(serializer, _map))
|
||||
}
|
||||
|
||||
operator fun get(key: String) = _map[key]
|
||||
|
||||
@Synchronized
|
||||
fun add(source: String, value: String) {
|
||||
load()
|
||||
@@ -206,7 +140,7 @@ class SavedSourceSet(private val file: File) {
|
||||
_map[source]?.remove(value)
|
||||
|
||||
if (!_map.containsKey(source))
|
||||
_map[source] = mutableSetOf()
|
||||
_map[source] = mutableListOf()
|
||||
else
|
||||
_map[source]!!.add(value)
|
||||
|
||||
@@ -222,7 +156,7 @@ class SavedSourceSet(private val file: File) {
|
||||
_map[source]!!.removeAll(from[source]!!)
|
||||
_map[source]!!.addAll(from[source]!!)
|
||||
} else {
|
||||
_map[source] = from[source]!!.toMutableSet()
|
||||
_map[source] = from[source]!!.toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
<!--
|
||||
~ 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 <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<!--drawable/clock_end.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 1C8.14 1 5 4.14 5 8a7 7 0 0 0 7 7c3.86 0 7-3.13 7-7 0-3.86-3.14-7-7-7m0 2.15c2.67 0 4.85 2.17 4.85 4.85 0 2.68-2.18 4.85-4.85 4.85A4.85 4.85 0 0 1 7.15 8 4.85 4.85 0 0 1 12 3.15M11 5v3.69l3.19 1.84 0.75-1.3-2.44-1.41V5M15 16v3H3v2h12v3l4-4m0 0v4h2v-8h-2"/>
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
<!--
|
||||
~ 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 <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<!--drawable/clock_start.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 1C8.14 1 5 4.14 5 8a7 7 0 0 0 7 7c3.86 0 7-3.13 7-7 0-3.86-3.14-7-7-7m0 2.15c2.67 0 4.85 2.17 4.85 4.85 0 2.68-2.18 4.85-4.85 4.85A4.85 4.85 0 0 1 7.15 8 4.85 4.85 0 0 1 12 3.15M11 5v3.69l3.19 1.84 0.75-1.3-2.44-1.41V5M4 16v8h2v-3h12v3l4-4-4-4v3H6v-3"/>
|
||||
|
||||
@@ -19,24 +19,4 @@
|
||||
<item>SOCKS</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="cache_size">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>4</item>
|
||||
<item>8</item>
|
||||
<item>16</item>
|
||||
<item>32</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="cache_size_text">
|
||||
<item>@string/settings_cache_unlimited</item>
|
||||
<item>1G</item>
|
||||
<item>2G</item>
|
||||
<item>4G</item>
|
||||
<item>8G</item>
|
||||
<item>16G</item>
|
||||
<item>32G</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
Reference in New Issue
Block a user