diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 45908c99..4574b689 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -76,5 +76,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index d0f26fa7..8ef12af2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -104,7 +104,7 @@ dependencies {
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
- implementation "com.google.android.material:material:1.3.0-rc01"
+ implementation "com.google.android.material:material:1.3.0"
implementation platform("com.google.firebase:firebase-bom:26.1.0")
implementation "com.google.firebase:firebase-analytics-ktx"
@@ -134,7 +134,7 @@ dependencies {
implementation "ru.noties.markwon:core:3.1.0"
- implementation "xyz.quaver:libpupil:1.9.7"
+ implementation "xyz.quaver:libpupil:1.9.7-SNAPSHOT"
implementation "xyz.quaver:documentfilex:0.4-alpha02"
implementation "xyz.quaver:floatingsearchview:1.1.1"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1ddb7c2b..fe8f21dd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -42,9 +42,6 @@
-
-
diff --git a/app/src/main/java/xyz/quaver/pupil/Pupil.kt b/app/src/main/java/xyz/quaver/pupil/Pupil.kt
index 36b46417..440b9e6e 100644
--- a/app/src/main/java/xyz/quaver/pupil/Pupil.kt
+++ b/app/src/main/java/xyz/quaver/pupil/Pupil.kt
@@ -44,23 +44,12 @@ import okhttp3.*
import org.kodein.di.*
import org.kodein.di.android.x.androidXModule
import xyz.quaver.io.FileX
-import xyz.quaver.pupil.sources.initSources
import xyz.quaver.pupil.sources.sourceModule
-import xyz.quaver.pupil.types.Tag
import xyz.quaver.pupil.util.*
import xyz.quaver.setClient
import java.io.File
import java.util.*
-lateinit var histories: SavedSet
- private set
-lateinit var favorites: SavedSet
- private set
-lateinit var favoriteTags: SavedSet
- private set
-lateinit var searchHistory: SavedSet
- private set
-
lateinit var clientBuilder: OkHttpClient.Builder
var clientHolder: OkHttpClient? = null
@@ -79,6 +68,11 @@ class Pupil : Application(), DIAware {
bind() with provider { client }
bind() with singleton { ImageCache(this@Pupil) }
bind() with singleton { DownloadManager(this@Pupil) }
+
+ bind(tag = "histories") with singleton { SavedSourceSet(File(ContextCompat.getDataDir(this@Pupil), "histories.json")) }
+ bind(tag = "favorites") with singleton { SavedSourceSet(File(ContextCompat.getDataDir(this@Pupil), "favorites.json")) }
+ bind(tag = "favoriteTags") with singleton { SavedSourceSet(File(ContextCompat.getDataDir(this@Pupil), "favoriteTags.json")) }
+ bind(tag = "searchHistory") with singleton { SavedSourceSet(File(ContextCompat.getDataDir(this@Pupil), "searchHistory.json")) }
}
private lateinit var firebaseAnalytics: FirebaseAnalytics
@@ -93,8 +87,6 @@ class Pupil : Application(), DIAware {
else userID
}
- initSources(this)
-
firebaseAnalytics = Firebase.analytics
FirebaseCrashlytics.getInstance().setUserId(userID)
@@ -125,11 +117,6 @@ class Pupil : Application(), DIAware {
Preferences["reset_secure"] = true
}
- histories = SavedSet(File(ContextCompat.getDataDir(this), "histories.json"), "")
- favorites = SavedSet(File(ContextCompat.getDataDir(this), "favorites.json"), "")
- favoriteTags = SavedSet(File(ContextCompat.getDataDir(this), "favorites_tags.json"), Tag.parse(""))
- searchHistory = SavedSet(File(ContextCompat.getDataDir(this), "search_histories.json"), "")
-
if (BuildConfig.DEBUG)
FirebaseAnalytics.getInstance(this).setAnalyticsCollectionEnabled(false)
diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt
index fd1e52fe..88c6cceb 100644
--- a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt
+++ b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt
@@ -60,8 +60,12 @@ class ReaderAdapter : ListAdapter(ReaderIt
with (binding.image) {
setImageViewFactory(FrescoImageViewFactory().apply {
updateView = { imageInfo ->
- layoutParams.height = imageInfo.height
- (mainView as? SimpleDraweeView)?.aspectRatio = imageInfo.width / imageInfo.height.toFloat()
+ if (!fullscreen) {
+ binding.root.layoutParams.height = imageInfo.height
+ layoutParams.height = imageInfo.height
+
+ (mainView as? SimpleDraweeView)?.aspectRatio = imageInfo.width / imageInfo.height.toFloat()
+ }
}
})
setImageShownCallback(object: ImageShownCallback {
diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/SearchResultsAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/SearchResultsAdapter.kt
index 8183737b..5f81ac50 100644
--- a/app/src/main/java/xyz/quaver/pupil/adapters/SearchResultsAdapter.kt
+++ b/app/src/main/java/xyz/quaver/pupil/adapters/SearchResultsAdapter.kt
@@ -27,6 +27,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
+import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.RecyclerView
import com.daimajia.swipe.SwipeLayout
import com.daimajia.swipe.adapters.RecyclerSwipeAdapter
@@ -35,13 +36,15 @@ import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.drawee.controller.BaseControllerListener
import com.facebook.imagepipeline.image.ImageInfo
import kotlinx.coroutines.*
+import org.kodein.di.DIAware
+import org.kodein.di.android.di
import xyz.quaver.pupil.R
import xyz.quaver.pupil.databinding.SearchResultItemBinding
import xyz.quaver.pupil.sources.ItemInfo
import xyz.quaver.pupil.types.Tag
import kotlin.time.ExperimentalTime
-class SearchResultsAdapter(private val results: List) : RecyclerSwipeAdapter(), SwipeAdapterInterface {
+class SearchResultsAdapter(var results: LiveData>) : RecyclerSwipeAdapter(), SwipeAdapterInterface {
var onChipClickedHandler: ((Tag) -> Unit)? = null
var onDownloadClickedHandler: ((source: String, itemI: String) -> Unit)? = null
@@ -64,6 +67,7 @@ class SearchResultsAdapter(private val results: List) : RecyclerSwipeA
}
binding.idView.setOnClickListener {
+ // TODO: MEMLEAK
(itemView.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(
ClipData.newPlainText("item_id", itemID)
)
@@ -146,6 +150,7 @@ class SearchResultsAdapter(private val results: List) : RecyclerSwipeA
CoroutineScope(Dispatchers.Main).launch {
with (binding.tagGroup) {
tags.clear()
+ source = result.source
result.extra[ItemInfo.ExtraType.TAGS]?.await()?.split(", ")?.let { if (it.size == 1 && it.first().isEmpty()) emptyList() else it }?.map {
Tag.parse(it)
}?.let { tags.addAll(it) }
@@ -201,10 +206,10 @@ class SearchResultsAdapter(private val results: List) : RecyclerSwipeA
@ExperimentalTime
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
mItemManger.bindView(holder.itemView, position)
- holder.bind(results[position])
+ holder.bind(results.value!![position])
}
- override fun getItemCount(): Int = results.size
+ override fun getItemCount(): Int = results.value?.size ?: 0
override fun getSwipeLayoutResourceId(position: Int): Int = R.id.swipe_layout
diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/SourceAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/SourceAdapter.kt
index 56c8cfa2..65a8ecc6 100644
--- a/app/src/main/java/xyz/quaver/pupil/adapters/SourceAdapter.kt
+++ b/app/src/main/java/xyz/quaver/pupil/adapters/SourceAdapter.kt
@@ -23,26 +23,30 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
import xyz.quaver.pupil.databinding.SourceSelectDialogItemBinding
+import xyz.quaver.pupil.sources.AnySource
import xyz.quaver.pupil.sources.Source
-import xyz.quaver.pupil.sources.sourceIcons
+import xyz.quaver.pupil.sources.SourceEntries
-class SourceAdapter(private val sources: List>) : RecyclerView.Adapter() {
+class SourceAdapter(sources: SourceEntries) : RecyclerView.Adapter() {
- var onSourceSelectedListener: ((Source<*, SearchSuggestion>) -> Unit)? = null
+ var onSourceSelectedListener: ((String) -> Unit)? = null
+
+ private val sources = sources.toList()
inner class ViewHolder(private val binding: SourceSelectDialogItemBinding) : RecyclerView.ViewHolder(binding.root) {
- lateinit var source: Source<*, SearchSuggestion>
+ lateinit var source: AnySource
init {
binding.go.setOnClickListener {
- onSourceSelectedListener?.invoke(source)
+ onSourceSelectedListener?.invoke(source.name)
}
}
- fun bind(source: Source<*, SearchSuggestion>) {
+ fun bind(source: AnySource) {
this.source = source
- binding.icon.setImageDrawable(sourceIcons[source.name])
+ // TODO: save image somewhere else
+ binding.icon.setImageResource(source.iconResID)
binding.name.text = source.name
}
}
@@ -52,7 +56,7 @@ class SourceAdapter(private val sources: List>) : Re
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- holder.bind(sources[position])
+ holder.bind(sources[position].second)
}
override fun getItemCount(): Int = sources.size
diff --git a/app/src/main/java/xyz/quaver/pupil/migrate/Migrate.kt b/app/src/main/java/xyz/quaver/pupil/migrate/Migrate.kt
new file mode 100644
index 00000000..5ef1cc0b
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/migrate/Migrate.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.migrate
+
+class Migrate {
+
+ fun migrate() {
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/migrate/Migrate001.kt b/app/src/main/java/xyz/quaver/pupil/migrate/Migrate001.kt
new file mode 100644
index 00000000..aabdc265
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/migrate/Migrate001.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.migrate
+
+class Migrate001 {
+
+
+
+}
\ No newline at end of file
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 5a038d38..599ed282 100644
--- a/app/src/main/java/xyz/quaver/pupil/sources/Common.kt
+++ b/app/src/main/java/xyz/quaver/pupil/sources/Common.kt
@@ -29,10 +29,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
-import org.kodein.di.DI
-import org.kodein.di.bind
-import org.kodein.di.contexted
-import org.kodein.di.instance
+import org.kodein.di.*
import xyz.quaver.floatingsearchview.databinding.SearchSuggestionItemBinding
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
import xyz.quaver.pupil.R
@@ -139,29 +136,18 @@ abstract class Source, Suggestion: SearchSu
}
}
-@Deprecated("")
-val sources = mutableMapOf()
-val sourceIcons = mutableMapOf()
-
+typealias SourceEntry = Pair
+typealias SourceEntries = Set
@Suppress("UNCHECKED_CAST")
val sourceModule = DI.Module(name = "source") {
- listOf(
- Hitomi(),
- Hiyobi()
- ).forEach {
- bind(tag = it.name) with instance (it as AnySource)
- }
-}
+ bind() from setBinding()
-@Deprecated("")
-@Suppress("UNCHECKED_CAST")
-fun initSources(context: Context) {
- // Add Default Sources
listOf(
Hitomi(),
Hiyobi()
- ).forEach {
- sources[it.name] = it as AnySource
- sourceIcons[it.name] = ContextCompat.getDrawable(context, it.iconResID)
+ ).forEach { source ->
+ bind().inSet() with multiton { _: Unit -> source.name to (source as AnySource) }
}
+
+ bind() with factory { source: String -> History(di, source) }
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/sources/History.kt b/app/src/main/java/xyz/quaver/pupil/sources/History.kt
new file mode 100644
index 00000000..29b68b7d
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/sources/History.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.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
+import xyz.quaver.pupil.util.source
+
+class History(override val di: DI, source: String) : Source(), DIAware {
+
+ private val source: AnySource by source(source)
+ private val histories: SavedSourceSet by instance(tag = "histories")
+
+ override val name: String
+ get() = source.name
+ override val iconResID: Int
+ get() = source.iconResID
+ override val availableSortMode: Array = DefaultSortMode.values()
+
+ override suspend fun search(query: String, range: IntRange, sortMode: Enum<*>): Pair, Int> {
+ val channel = Channel()
+
+ CoroutineScope(Dispatchers.IO).launch {
+ Logger.d(histories.map)
+ histories.map[source.name]?.forEach {
+ channel.send(source.info(it))
+ }
+ }
+
+ return Pair(channel, histories.map.size)
+ }
+
+ override suspend fun suggestion(query: String): List {
+ return source.suggestion(query)
+ }
+
+ override suspend fun images(itemID: String): List {
+ return source.images(itemID)
+ }
+
+ override suspend fun info(itemID: String): ItemInfo {
+ return source.info(itemID)
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/sources/Hiyobi.kt b/app/src/main/java/xyz/quaver/pupil/sources/Hiyobi.kt
index f3d2d6a8..1f423bad 100644
--- a/app/src/main/java/xyz/quaver/pupil/sources/Hiyobi.kt
+++ b/app/src/main/java/xyz/quaver/pupil/sources/Hiyobi.kt
@@ -73,7 +73,7 @@ class Hiyobi : Source() {
}
override suspend fun images(itemID: String): List {
- return createImgList(itemID, getGalleryInfo(itemID), false).map {
+ return createImgList(itemID, getGalleryInfo(itemID), true).map {
it.path
}
}
diff --git a/app/src/main/java/xyz/quaver/pupil/types/Tags.kt b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt
index 2325fca8..29340644 100644
--- a/app/src/main/java/xyz/quaver/pupil/types/Tags.kt
+++ b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt
@@ -70,12 +70,12 @@ class Tags(val tags: MutableSet = mutableSetOf()) : MutableSet by tags
companion object {
fun parse(tags: String) : Tags {
return Tags(
- tags.split(' ').map {
+ tags.split(' ').mapNotNull {
if (it.isNotEmpty())
Tag.parse(it)
else
null
- }.filterNotNull().toMutableSet()
+ }.toMutableSet()
)
}
}
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 3be2bee5..e4801087 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
@@ -27,11 +27,11 @@ import android.text.util.Linkify
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
-import android.view.View
import android.view.animation.DecelerateInterpolator
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
+import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.core.view.GravityCompat
import androidx.core.view.ViewCompat
@@ -42,53 +42,38 @@ 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 kotlinx.coroutines.channels.Channel
+import org.kodein.di.DIAware
+import org.kodein.di.android.di
import xyz.quaver.floatingsearchview.FloatingSearchView
-import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
-import xyz.quaver.floatingsearchview.util.view.SearchInputView
import xyz.quaver.pupil.*
import xyz.quaver.pupil.adapters.SearchResultsAdapter
import xyz.quaver.pupil.databinding.MainActivityBinding
import xyz.quaver.pupil.sources.ItemInfo
-import xyz.quaver.pupil.sources.Source
-import xyz.quaver.pupil.sources.sourceIcons
-import xyz.quaver.pupil.sources.sources
import xyz.quaver.pupil.types.*
import xyz.quaver.pupil.ui.dialog.DownloadLocationDialogFragment
import xyz.quaver.pupil.ui.dialog.GalleryDialogFragment
import xyz.quaver.pupil.ui.dialog.SourceSelectDialog
import xyz.quaver.pupil.ui.view.ProgressCardView
import xyz.quaver.pupil.ui.view.SwipePageTurnView
+import xyz.quaver.pupil.ui.viewmodel.MainViewModel
import xyz.quaver.pupil.util.*
import java.util.regex.Pattern
import kotlin.math.*
-import kotlin.random.Random
class MainActivity :
BaseActivity(),
- NavigationView.OnNavigationItemSelectedListener
+ NavigationView.OnNavigationItemSelectedListener,
+ DIAware
{
- private val searchResults = mutableListOf()
+ override val di by di()
- private var query = ""
- set(value) {
- field = value
- with (findViewById(R.id.search_bar_text)) {
- if (text.toString() != value)
- setText(query, TextView.BufferType.EDITABLE)
- }
- }
+ private val searchResults = mutableListOf()
private var queryStack = mutableListOf()
- private lateinit var source: Source<*, SearchSuggestion>
- private lateinit var sortMode: Enum<*>
-
- private var searchJob: Deferred, Int>>? = null
- private var totalItems = 0
- private var currentPage = 1
-
private lateinit var binding: MainActivityBinding
+ private val model: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -97,7 +82,7 @@ class MainActivity :
if (intent.action == Intent.ACTION_VIEW) {
intent.dataString?.let { url ->
- restore(url,
+ restore(this, url,
onFailure = {
Snackbar.make(binding.contents.recyclerview, R.string.settings_backup_failed, Snackbar.LENGTH_LONG).show()
}, onSuccess = {
@@ -113,6 +98,74 @@ class MainActivity :
checkUpdate(this)
initView()
+
+ model.query.observe(this) {
+ binding.contents.searchview.binding.querySection.searchBarText.run {
+ if (text?.toString() != it) setText(it, TextView.BufferType.EDITABLE)
+ }
+ }
+
+ model.availableSortMode.observe(this) {
+ binding.contents.searchview.post {
+ 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)
+ }
+
+ setGroupCheckable(R.id.sort_mode_group_id, true, true)
+
+ children.first().isChecked = true
+ }
+ }
+ }
+
+ model.sourceIcon.observe(this) {
+ binding.contents.searchview.post {
+ (binding.contents.searchview.binding.querySection.menuView.getChildAt(1) as ImageView).apply {
+ ImageViewCompat.setImageTintList(this, null)
+
+ setImageResource(it)
+ }
+ }
+ }
+
+ model.searchResults.observe(this) {
+ binding.contents.recyclerview.post {
+ if (model.loading) {
+ if (it.isEmpty()) {
+ binding.contents.noresult.hide()
+ binding.contents.progressbar.show()
+
+ (binding.contents.recyclerview.adapter as RecyclerSwipeAdapter).run {
+ mItemManger.closeAllItems()
+
+ notifyDataSetChanged()
+ }
+
+ ViewCompat.animate(binding.contents.searchview)
+ .setDuration(100)
+ .setInterpolator(DecelerateInterpolator())
+ .translationY(0F)
+ }
+ } else {
+ binding.contents.progressbar.hide()
+ if (it.isEmpty()) {
+ binding.contents.noresult.show()
+ } else {
+ binding.contents.recyclerview.adapter?.notifyItemInserted(it.size-1)
+ }
+ }
+ }
+ }
+
+ model.suggestions.observe(this) { runOnUiThread {
+ Logger.d(it)
+ binding.contents.searchview.swapSuggestions(
+ if (it.isEmpty()) listOf(NoResultSuggestion(getString(R.string.main_no_result))) else it
+ )
+ } }
}
override fun onDestroy() {
@@ -126,36 +179,31 @@ class MainActivity :
when {
binding.drawer.isDrawerOpen(GravityCompat.START) -> binding.drawer.closeDrawer(GravityCompat.START)
queryStack.removeLastOrNull() != null && queryStack.isNotEmpty() -> runOnUiThread {
- query = queryStack.last()
+ model.query.value = queryStack.last()
- query()
+ model.query()
}
else -> super.onBackPressed()
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
- val perPage = Preferences["per_page", "25"].toInt()
- val maxPage = ceil(totalItems / perPage.toDouble()).roundToInt()
-
return when(keyCode) {
KeyEvent.KEYCODE_VOLUME_UP -> {
- if (currentPage > 1) {
+ if (model.currentPage.value!! > 1) {
runOnUiThread {
- currentPage--
-
- query()
+ model.prevPage()
+ model.query()
}
}
true
}
KeyEvent.KEYCODE_VOLUME_DOWN -> {
- if (currentPage < maxPage) {
+ if (model.currentPage.value!! < model.totalPages.value!!) {
runOnUiThread {
- currentPage++
-
- query()
+ model.nextPage()
+ model.query()
}
}
@@ -165,34 +213,6 @@ class MainActivity :
}
}
- private fun setSource(source: Source<*, SearchSuggestion>) {
- this.source = source
- sortMode = source.availableSortMode.first()
-
- query = ""
- currentPage = 1
-
- with (binding.contents.searchview.binding.querySection.menuView) {
- post {
- menuItems.findMenu(R.id.sort).subMenu.apply {
- clear()
-
- source.availableSortMode.forEach {
- add(R.id.sort_mode_group_id, it.ordinal, Menu.NONE, it.name)
- }
-
- setGroupCheckable(R.id.sort_mode_group_id, true, true)
-
- children.first().isChecked = true
- }
- with (getChildAt(1) as ImageView) {
- ImageViewCompat.setImageTintList(this, null)
- setImageDrawable(sourceIcons[source.name])
- }
- }
- }
- }
-
private fun initView() {
binding.contents.recyclerview.addOnScrollListener(object: RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
@@ -234,14 +254,13 @@ class MainActivity :
setTitle(R.string.main_jump_title)
setMessage(getString(
R.string.main_jump_message,
- currentPage,
- ceil(totalItems / perPage.toDouble()).roundToInt()
+ model.currentPage.value!!,
+ ceil(model.totalPages.value!! / perPage.toDouble()).roundToInt()
))
setPositiveButton(android.R.string.ok) { _, _ ->
- currentPage = (editText.text.toString().toIntOrNull() ?: return@setPositiveButton)
-
- query()
+ model.setPage(editText.text.toString().toIntOrNull() ?: return@setPositiveButton)
+ model.query()
}
}.show()
}
@@ -251,30 +270,16 @@ class MainActivity :
setImageResource(R.drawable.shuffle_variant)
setOnClickListener {
setImageDrawable(CircularProgressDrawable(context))
- if (totalItems > 0)
- CoroutineScope(Dispatchers.IO).launch {
- val random = Random.Default.nextInt(totalItems)
- val randomResult =
- source.search(
- query + Preferences["default_query", ""],
- random .. random,
- sortMode
- ).first.receive()
-
- launch(Dispatchers.Main) {
- setImageResource(R.drawable.shuffle_variant)
- GalleryDialogFragment(source.name, randomResult.id).apply {
- onChipClickedHandler.add {
- query = it.toQuery()
- currentPage = 1
-
- query()
- dismiss()
- }
- }.show(supportFragmentManager, "GalleryDialogFragment")
+ model.random { runOnUiThread {
+ setImageResource(R.drawable.shuffle_variant)
+ GalleryDialogFragment(model.sourceName.value!!, it.id).apply {
+ onChipClickedHandler.add {
+ model.setQueryAndSearch(it.toQuery())
+ dismiss()
}
- }
+ }.show(supportFragmentManager, "GalleryDialogFragment")
+ } }
}
}
@@ -292,12 +297,9 @@ class MainActivity :
setPositiveButton(android.R.string.ok) { _, _ ->
val galleryID = editText.text.toString()
- GalleryDialogFragment(source.name, galleryID).apply {
+ GalleryDialogFragment(model.sourceName.value!!, galleryID).apply {
onChipClickedHandler.add {
- query = it.toQuery()
- currentPage = 1
-
- query()
+ model.setQueryAndSearch(it.toQuery())
dismiss()
}
}.show(supportFragmentManager, "GalleryDialogFragment")
@@ -309,16 +311,16 @@ class MainActivity :
with (binding.contents.swipePageTurnView) {
setOnPageTurnListener(object: SwipePageTurnView.OnPageTurnListener {
override fun onPrev(page: Int) {
- currentPage--
+ model.prevPage()
// disable pageturn until the contents are loaded
setCurrentPage(1, false)
- query()
+ model.query()
}
override fun onNext(page: Int) {
- currentPage++
+ model.nextPage()
// disable pageturn until the contents are loaded
setCurrentPage(1, false)
@@ -328,30 +330,25 @@ class MainActivity :
.setInterpolator(DecelerateInterpolator())
.translationY(0F)
- query()
+ model.query()
}
})
}
setupSearchBar()
setupRecyclerView()
- setSource(sources.values.first())
- query()
+ // TODO: Save recent source
}
@SuppressLint("ClickableViewAccessibility")
private fun setupRecyclerView() {
with (binding.contents.recyclerview) {
- adapter = SearchResultsAdapter(searchResults).apply {
+ adapter = SearchResultsAdapter(model.searchResults).apply {
onChipClickedHandler = {
- query = it.toQuery()
- currentPage = 1
-
- query()
+ model.setQueryAndSearch(it.toQuery())
}
onDownloadClickedHandler = { source, itemID ->
-
closeAllItems()
}
@@ -366,7 +363,7 @@ class MainActivity :
return@listener
val intent = Intent(this@MainActivity, ReaderActivity::class.java).apply {
- putExtra("source", source.name)
+ putExtra("source", model.sourceName.value!!)
putExtra("id", searchResults[position].id)
}
@@ -380,12 +377,9 @@ class MainActivity :
val result = searchResults.getOrNull(position) ?: return@listener true
- GalleryDialogFragment(source.name, result.id).apply {
+ GalleryDialogFragment(model.sourceName.value!!, result.id).apply {
onChipClickedHandler.add {
- query = it.toQuery()
- currentPage = 1
-
- query()
+ model.setQueryAndSearch(it.toQuery())
dismiss()
}
}.show(supportFragmentManager, "GalleryDialogFragment")
@@ -396,7 +390,6 @@ class MainActivity :
}
}
- private var suggestionJob : Job? = null
private fun setupSearchBar() {
with (binding.contents.searchview) {
onMenuStatusChangeListener = object: FloatingSearchView.OnMenuStatusChangeListener {
@@ -414,29 +407,15 @@ class MainActivity :
}
onQueryChangeListener = lambda@{ _, query ->
- this@MainActivity.query = query
+ model.query.value = query
- suggestionJob?.cancel()
+ model.suggestion()
swapSuggestions(listOf(LoadingSuggestion(getText(R.string.reader_loading).toString())))
-
- val currentQuery = query.split(" ").last()
- .replace(Regex("^-"), "")
- .replace('_', ' ')
-
- suggestionJob = CoroutineScope(Dispatchers.IO).launch {
- val suggestions = kotlin.runCatching {
- source.suggestion(currentQuery)
- }.getOrElse { emptyList() }
-
- withContext(Dispatchers.Main) {
- swapSuggestions(if (suggestions.isNotEmpty()) suggestions else listOf(NoResultSuggestion(getText(R.string.main_no_result).toString())))
- }
- }
}
onSuggestionBinding = { binding, item ->
- source.onSuggestionBind(binding, item)
+ model.source.value!!.onSuggestionBind(binding, item)
}
onFocusChangeListener = object: FloatingSearchView.OnFocusChangeListener {
@@ -445,10 +424,8 @@ class MainActivity :
}
override fun onFocusCleared() {
- suggestionJob?.cancel()
-
- currentPage = 1
- query()
+ model.setPage(1)
+ model.query()
}
}
@@ -458,21 +435,19 @@ class MainActivity :
private fun onActionMenuItemSelected(item: MenuItem?) {
if (item?.groupId == R.id.sort_mode_group_id) {
- currentPage = 1
- sortMode = source.availableSortMode.let { availableSortMode ->
+ model.setPage(1)
+ model.sortMode.value = model.availableSortMode.value?.let { availableSortMode ->
availableSortMode.getOrElse(item.itemId) { availableSortMode.first() }
}
- query()
+ model.query()
}
else
when(item?.itemId) {
R.id.main_menu_settings -> startActivity(Intent(this@MainActivity, SettingsActivity::class.java))
R.id.source -> SourceSelectDialog().apply {
onSourceSelectedListener = {
- setSource(it)
-
- query()
+ model.setSourceAndReset(it)
dismiss()
}
@@ -485,6 +460,9 @@ class MainActivity :
binding.drawer.closeDrawers()
when(item.itemId) {
+ R.id.main_drawer_history -> {
+ //model.setSourceAndReset(direct.instance(arg = source.name))
+ }
R.id.main_drawer_help -> {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.help))))
}
@@ -505,55 +483,4 @@ class MainActivity :
return true
}
-
- private fun cancelFetch() {
- searchJob?.cancel()
- }
-
- private fun clearGalleries() = CoroutineScope(Dispatchers.Main).launch {
- searchResults.clear()
-
- (binding.contents.recyclerview.adapter as RecyclerSwipeAdapter).mItemManger.closeAllItems()
- binding.contents.recyclerview.adapter?.notifyDataSetChanged()
-
- binding.contents.noresult.visibility = View.INVISIBLE
- binding.contents.progressbar.show()
-
- ViewCompat.animate(binding.contents.searchview)
- .setDuration(100)
- .setInterpolator(DecelerateInterpolator())
- .translationY(0F)
- }
- private fun query() {
- val perPage = Preferences["per_page", "25"].toInt()
-
- cancelFetch()
- clearGalleries()
-
- CoroutineScope(Dispatchers.Main).launch {
- searchJob = async(Dispatchers.IO) {
- source.search(
- query + Preferences["default_query", ""],
- (currentPage - 1) * perPage until currentPage * perPage,
- sortMode
- )
- }.also {
- it.await().let { r ->
- totalItems = r.second
- r.first
- }.let { channel ->
- binding.contents.progressbar.hide()
- binding.contents.swipePageTurnView.setCurrentPage(currentPage, totalItems > currentPage*perPage)
-
- for (result in channel) {
- searchResults.add(result)
- binding.contents.recyclerview.adapter?.notifyItemInserted(searchResults.size)
- }
- }
-
- if (searchResults.isEmpty())
- binding.contents.noresult.visibility = View.VISIBLE
- }
- }
- }
}
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 5ee5785d..879d4c08 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt
@@ -30,6 +30,7 @@ import androidx.recyclerview.widget.PagerSnapHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.google.firebase.crashlytics.FirebaseCrashlytics
+import com.orhanobut.logger.Logger
import com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
import org.kodein.di.DIAware
import org.kodein.di.android.di
@@ -38,10 +39,11 @@ import org.kodein.di.instance
import xyz.quaver.pupil.R
import xyz.quaver.pupil.adapters.ReaderAdapter
import xyz.quaver.pupil.databinding.ReaderActivityBinding
-import xyz.quaver.pupil.favorites
import xyz.quaver.pupil.sources.AnySource
import xyz.quaver.pupil.ui.viewmodel.ReaderViewModel
import xyz.quaver.pupil.util.Preferences
+import xyz.quaver.pupil.util.SavedSourceSet
+import xyz.quaver.pupil.util.source
class ReaderActivity : BaseActivity(), DIAware {
@@ -64,6 +66,9 @@ class ReaderActivity : BaseActivity(), DIAware {
private lateinit var binding: ReaderActivityBinding
private val model: ReaderViewModel by viewModels()
+ private val favorites: SavedSourceSet by instance(tag = "favorites")
+ private val histories: SavedSourceSet by instance(tag = "histories")
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ReaderActivityBinding.inflate(layoutInflater)
@@ -78,8 +83,11 @@ class ReaderActivity : BaseActivity(), DIAware {
return
}
+ histories.add(source, itemID)
FirebaseCrashlytics.getInstance().setCustomKey("GalleryID", itemID)
+ Logger.d(histories)
+
model.readerItems.observe(this) {
(binding.recyclerview.adapter as ReaderAdapter).submitList(it.toMutableList())
@@ -135,11 +143,11 @@ class ReaderActivity : BaseActivity(), DIAware {
menu?.forEach {
when (it.itemId) {
R.id.reader_menu_favorite -> {
- if (favorites.contains(itemID))
+ if (favorites.map[source]?.contains(itemID) == true)
(it.icon as Animatable).start()
}
R.id.source -> {
- it.setIcon(direct.instance(tag = source).iconResID)
+ it.setIcon(source(source).value.iconResID)
}
}
}
@@ -154,11 +162,11 @@ class ReaderActivity : BaseActivity(), DIAware {
val id = itemID
val favorite = menu?.findItem(R.id.reader_menu_favorite) ?: return true
- if (favorites.contains(id)) {
- favorites.remove(id)
+ if (favorites.map[source]?.contains(id) == true) {
+ favorites.remove(source, id)
favorite.icon = AnimatedVectorDrawableCompat.create(this, R.drawable.avd_star)
} else {
- favorites.add(id)
+ favorites.add(source, id)
(favorite.icon as Animatable).start()
}
}
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialogFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialogFragment.kt
index 1e49682c..dd30c133 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialogFragment.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialogFragment.kt
@@ -50,10 +50,10 @@ class DefaultQueryDialogFragment() : DialogFragment() {
initView()
- return AlertDialog.Builder(requireContext()).apply {
- setTitle(R.string.default_query_dialog_title)
- setView(binding.root)
- setPositiveButton(android.R.string.ok) { _, _ ->
+ return AlertDialog.Builder(requireContext())
+ .setTitle(R.string.default_query_dialog_title)
+ .setView(binding.root)
+ .setPositiveButton(android.R.string.ok) { _, _ ->
val newTags = Tags.parse(binding.edittext.text.toString())
with (binding.languageSelector) {
@@ -75,8 +75,7 @@ class DefaultQueryDialogFragment() : DialogFragment() {
}
onPositiveButtonClickListener?.invoke(newTags)
- }
- }.create()
+ }.create()
}
override fun onDestroy() {
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialogFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialogFragment.kt
index 5ba43263..8e9313f9 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialogFragment.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialogFragment.kt
@@ -30,32 +30,39 @@ import androidx.core.content.ContextCompat
import androidx.core.view.forEach
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.viewModels
+import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.drawee.controller.BaseControllerListener
import com.facebook.imagepipeline.image.ImageInfo
-import com.orhanobut.logger.Logger
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
+import org.kodein.di.DIAware
+import org.kodein.di.android.x.di
+import org.kodein.di.instance
import xyz.quaver.pupil.R
import xyz.quaver.pupil.adapters.SearchResultsAdapter
import xyz.quaver.pupil.adapters.ThumbnailPageAdapter
import xyz.quaver.pupil.databinding.*
-import xyz.quaver.pupil.favoriteTags
import xyz.quaver.pupil.sources.ItemInfo
import xyz.quaver.pupil.types.Tag
import xyz.quaver.pupil.ui.ReaderActivity
import xyz.quaver.pupil.ui.view.TagChip
import xyz.quaver.pupil.ui.viewmodel.GalleryDialogViewModel
import xyz.quaver.pupil.util.ItemClickSupport
+import xyz.quaver.pupil.util.SavedSourceSet
import xyz.quaver.pupil.util.wordCapitalize
import java.util.*
import kotlin.collections.ArrayList
-class GalleryDialogFragment(private val source: String, private val itemID: String) : DialogFragment() {
+class GalleryDialogFragment(private val source: String, private val itemID: String) : DialogFragment(), DIAware {
+
+ override val di by di()
+
+ private val favoriteTags: SavedSourceSet by instance(tag = "favoriteTags")
val onChipClickedHandler = ArrayList<((Tag) -> (Unit))>()
@@ -150,7 +157,7 @@ class GalleryDialogFragment(private val source: String, private val itemID: Stri
info.extra[ItemInfo.ExtraType.TAGS]?.await()?.split(", ")?.filterNot { it.isEmpty() }?.sortedBy {
val tag = Tag.parse(it)
- if (favoriteTags.contains(tag))
+ if (favoriteTags.map[source]?.contains(tag.toString()) == true)
-1
else
when(Tag.parse(it).area) {
@@ -175,7 +182,7 @@ class GalleryDialogFragment(private val source: String, private val itemID: Stri
content!!.forEach { tag ->
tags.addView(
- TagChip(requireContext(), tag).apply {
+ TagChip(requireContext(), source, tag).apply {
setOnClickListener {
onChipClickedHandler.forEach { handler ->
handler.invoke(tag)
@@ -210,7 +217,7 @@ class GalleryDialogFragment(private val source: String, private val itemID: Stri
}
private fun addRelated(relatedItems: List) {
- val adapter = SearchResultsAdapter(relatedItems).apply {
+ val adapter = SearchResultsAdapter(MutableLiveData(relatedItems)).apply {
onChipClickedHandler = { tag ->
this@GalleryDialogFragment.onChipClickedHandler.forEach { handler ->
handler.invoke(tag)
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/SourceSelectDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/SourceSelectDialog.kt
index 631eab36..16ff3857 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/SourceSelectDialog.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/SourceSelectDialog.kt
@@ -25,14 +25,20 @@ import android.view.Window
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import org.kodein.di.*
+import org.kodein.di.android.x.di
+import org.kodein.type.jvmType
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
import xyz.quaver.pupil.adapters.SourceAdapter
+import xyz.quaver.pupil.sources.AnySource
import xyz.quaver.pupil.sources.Source
-import xyz.quaver.pupil.sources.sources
+import xyz.quaver.pupil.sources.SourceEntries
-class SourceSelectDialog : DialogFragment() {
+class SourceSelectDialog : DialogFragment(), DIAware {
- var onSourceSelectedListener: ((Source<*, SearchSuggestion>) -> Unit)? = null
+ override val di by di()
+
+ var onSourceSelectedListener: ((String) -> Unit)? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return Dialog(requireContext()).apply {
@@ -41,7 +47,7 @@ class SourceSelectDialog : DialogFragment() {
setContentView(RecyclerView(context).apply {
layoutManager = LinearLayoutManager(context)
- adapter = SourceAdapter(sources.values.toList()).apply {
+ adapter = SourceAdapter(direct.instance()).apply {
onSourceSelectedListener = this@SourceSelectDialog.onSourceSelectedListener
}
})
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageFavoritesFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageFavoritesFragment.kt
index cba64dfa..a99c0264 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageFavoritesFragment.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageFavoritesFragment.kt
@@ -28,14 +28,17 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.google.android.material.snackbar.Snackbar
import okhttp3.*
+import org.kodein.di.DIAware
+import org.kodein.di.android.x.di
import xyz.quaver.pupil.R
import xyz.quaver.pupil.client
-import xyz.quaver.pupil.favorites
import xyz.quaver.pupil.util.restore
import java.io.File
import java.io.IOException
-class ManageFavoritesFragment : PreferenceFragmentCompat() {
+class ManageFavoritesFragment : PreferenceFragmentCompat(), DIAware {
+
+ override val di by di()
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.manage_favorites_preferences, rootKey)
@@ -87,7 +90,7 @@ class ManageFavoritesFragment : PreferenceFragmentCompat() {
.setTitle(R.string.settings_restore_title)
.setView(editText)
.setPositiveButton(android.R.string.ok) { _, _ ->
- restore(editText.text.toString(),
+ restore(context, editText.text.toString(),
onFailure = onFailure@{
val view = view ?: return@onFailure
Snackbar.make(view, R.string.settings_restore_failed, Snackbar.LENGTH_LONG).show()
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageStorageFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageStorageFragment.kt
index 2831beec..6982b997 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageStorageFragment.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageStorageFragment.kt
@@ -28,11 +28,7 @@ import org.kodein.di.android.x.di
import org.kodein.di.instance
import xyz.quaver.io.FileX
import xyz.quaver.pupil.R
-import xyz.quaver.pupil.histories
-import xyz.quaver.pupil.util.DownloadManager
-import xyz.quaver.pupil.util.ImageCache
-import xyz.quaver.pupil.util.byteToString
-import xyz.quaver.pupil.util.size
+import xyz.quaver.pupil.util.*
class ManageStorageFragment : PreferenceFragmentCompat(), DIAware, Preference.OnPreferenceClickListener {
@@ -43,6 +39,8 @@ class ManageStorageFragment : PreferenceFragmentCompat(), DIAware, Preference.On
private val downloadManager: DownloadManager by instance()
private val cache: ImageCache by instance()
+ private val histories: SavedSourceSet by instance(tag = "histories")
+
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.manage_storage_preferences, rootKey)
@@ -122,7 +120,7 @@ class ManageStorageFragment : PreferenceFragmentCompat(), DIAware, Preference.On
setMessage(R.string.settings_clear_history_alert_message)
setPositiveButton(android.R.string.ok) { _, _ ->
histories.clear()
- summary = context.getString(R.string.settings_clear_history_summary, histories.size)
+ summary = context.getString(R.string.settings_clear_history_summary, histories.map.values.sumOf { it.size })
}
setNegativeButton(android.R.string.cancel) { _, _ -> }
}.show()
@@ -169,7 +167,7 @@ class ManageStorageFragment : PreferenceFragmentCompat(), DIAware, Preference.On
with (findPreference("clear_history")) {
this ?: return@with
- summary = context.getString(R.string.settings_clear_history_summary, histories.size)
+ summary = context.getString(R.string.settings_clear_history_summary, histories.map.values.sumOf { it.size })
onPreferenceClickListener = this@ManageStorageFragment
}
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/view/FloatingSearchView.kt b/app/src/main/java/xyz/quaver/pupil/ui/view/FloatingSearchView.kt
index d3d5f026..cb9fed2c 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/view/FloatingSearchView.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/view/FloatingSearchView.kt
@@ -21,28 +21,23 @@ package xyz.quaver.pupil.ui.view
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
-import android.graphics.drawable.Animatable
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.view.View
import android.view.inputmethod.EditorInfo
-import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
-import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import xyz.quaver.floatingsearchview.FloatingSearchView
import xyz.quaver.floatingsearchview.databinding.SearchSuggestionItemBinding
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
import xyz.quaver.floatingsearchview.util.view.SearchInputView
import xyz.quaver.pupil.R
-import xyz.quaver.pupil.databinding.SuggestionCountBinding
-import xyz.quaver.pupil.favoriteTags
-import xyz.quaver.pupil.sources.DefaultSearchSuggestion
import xyz.quaver.pupil.sources.Hitomi
-import xyz.quaver.pupil.types.*
+import xyz.quaver.pupil.types.FavoriteHistorySwitch
+import xyz.quaver.pupil.types.HistorySuggestion
+import xyz.quaver.pupil.types.LoadingSuggestion
+import xyz.quaver.pupil.types.NoResultSuggestion
import java.util.*
class FloatingSearchView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/view/TagChip.kt b/app/src/main/java/xyz/quaver/pupil/ui/view/TagChip.kt
index 93421314..42fcb609 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/view/TagChip.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/view/TagChip.kt
@@ -22,15 +22,22 @@ import android.annotation.SuppressLint
import android.content.Context
import androidx.core.content.ContextCompat
import com.google.android.material.chip.Chip
+import org.kodein.di.DIAware
+import org.kodein.di.android.di
+import org.kodein.di.instance
import xyz.quaver.pupil.R
-import xyz.quaver.pupil.favoriteTags
import xyz.quaver.pupil.sources.Hitomi
import xyz.quaver.pupil.types.Tag
+import xyz.quaver.pupil.util.SavedSourceSet
import xyz.quaver.pupil.util.translations
import xyz.quaver.pupil.util.wordCapitalize
@SuppressLint("ViewConstructor")
-class TagChip(context: Context, _tag: Tag) : Chip(context) {
+class TagChip(context: Context, private val source: String, _tag: Tag) : Chip(context), DIAware {
+
+ override val di by di(context)
+
+ private val favoriteTags: SavedSourceSet by instance(tag = "favoriteTags")
val tag: Tag =
_tag.let {
@@ -56,20 +63,20 @@ class TagChip(context: Context, _tag: Tag) : Chip(context) {
}
}
- if (favoriteTags.contains(tag))
+ if (favoriteTags.map[source]?.contains(tag.toString()) == true)
setChipBackgroundColorResource(R.color.material_orange_500)
isCloseIconVisible = true
closeIcon = ContextCompat.getDrawable(context,
- if (favoriteTags.contains(tag))
+ if (favoriteTags.map[source]?.contains(tag.toString()) == true)
R.drawable.ic_star_filled
else
R.drawable.ic_star_empty
)
setOnCloseIconClickListener {
- if (favoriteTags.contains(tag)) {
- favoriteTags.remove(tag)
+ if (favoriteTags.map[source]?.contains(tag.toString()) == true) {
+ favoriteTags.remove(source, tag.toString())
closeIcon = ContextCompat.getDrawable(context, R.drawable.ic_star_empty)
when(tag.area) {
@@ -78,7 +85,7 @@ class TagChip(context: Context, _tag: Tag) : Chip(context) {
else -> chipBackgroundColor = null
}
} else {
- favoriteTags.add(tag)
+ favoriteTags.add(source, tag.toString())
closeIcon = ContextCompat.getDrawable(context, R.drawable.ic_star_filled)
setChipBackgroundColorResource(R.color.material_orange_500)
}
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/view/TagChipGroup.kt b/app/src/main/java/xyz/quaver/pupil/ui/view/TagChipGroup.kt
index 3c3dd136..a3f22385 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/view/TagChipGroup.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/view/TagChipGroup.kt
@@ -29,7 +29,7 @@ import xyz.quaver.pupil.R
import xyz.quaver.pupil.types.Tag
import xyz.quaver.pupil.types.Tags
-class TagChipGroup @JvmOverloads constructor(context: Context, attr: AttributeSet? = null, attrStyle: Int = R.attr.chipGroupStyle, val tags: Tags = Tags()) : ChipGroup(context, attr, attrStyle), MutableSet by tags {
+class TagChipGroup @JvmOverloads constructor(context: Context, attr: AttributeSet? = null, attrStyle: Int = R.attr.chipGroupStyle, var source: String = "hitomi.la", val tags: Tags = Tags()) : ChipGroup(context, attr, attrStyle), MutableSet by tags {
object Defaults {
const val maxChipSize = 10
@@ -53,7 +53,7 @@ class TagChipGroup @JvmOverloads constructor(context: Context, attr: AttributeSe
for (i in maxChipSize until tags.size) {
val tag = tags.elementAt(i)
- addView(TagChip(context, tag).apply {
+ addView(TagChip(context, source, tag).apply {
setOnClickListener {
onClickListener?.invoke(tag)
}
@@ -76,7 +76,7 @@ class TagChipGroup @JvmOverloads constructor(context: Context, attr: AttributeSe
refreshJob = CoroutineScope(Dispatchers.Main).launch {
tags.take(maxChipSize).map {
CoroutineScope(Dispatchers.Default).async {
- TagChip(context, it).apply {
+ TagChip(context, source, it).apply {
setOnClickListener {
onClickListener?.invoke(this.tag)
}
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/GalleryDialogViewModel.kt b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/GalleryDialogViewModel.kt
index fd54f932..5c6d306e 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/GalleryDialogViewModel.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/GalleryDialogViewModel.kt
@@ -29,6 +29,7 @@ import org.kodein.di.android.x.di
import org.kodein.di.instance
import xyz.quaver.pupil.sources.AnySource
import xyz.quaver.pupil.sources.ItemInfo
+import xyz.quaver.pupil.util.source
class GalleryDialogViewModel(app: Application) : AndroidViewModel(app), DIAware {
@@ -41,7 +42,7 @@ class GalleryDialogViewModel(app: Application) : AndroidViewModel(app), DIAware
val related: LiveData> = _related
fun load(source: String, itemID: String) {
- val source: AnySource by instance(tag = source)
+ val source: AnySource by source(source)
viewModelScope.launch {
_info.value = withContext(Dispatchers.IO) {
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
new file mode 100644
index 00000000..0d839b35
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt
@@ -0,0 +1,174 @@
+/*
+ * 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.ui.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.*
+import kotlinx.coroutines.*
+import org.kodein.di.DIAware
+import org.kodein.di.android.x.di
+import org.kodein.di.direct
+import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
+import xyz.quaver.pupil.sources.AnySource
+import xyz.quaver.pupil.sources.ItemInfo
+import xyz.quaver.pupil.util.Preferences
+import xyz.quaver.pupil.util.notify
+import xyz.quaver.pupil.util.source
+import kotlin.math.ceil
+import kotlin.math.roundToInt
+import kotlin.random.Random
+
+@Suppress("UNCHECKED_CAST")
+class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
+
+ override val di by di()
+
+ private val _searchResults = MutableLiveData>()
+ val searchResults = _searchResults as LiveData>
+ var loading = false
+ private set
+
+ private var queryJob: Job? = null
+ private var suggestionJob: Job? = null
+
+ val query = MutableLiveData()
+ private val queryStack = mutableListOf()
+
+ private val _source = MutableLiveData()
+ val source: LiveData = _source
+
+ val availableSortMode = Transformations.map(_source) {
+ it.availableSortMode
+ }
+ val sortMode = MutableLiveData>()
+
+ val sourceIcon = Transformations.map(_source) {
+ it.iconResID
+ }
+
+ val sourceName = Transformations.map(_source) {
+ it.name
+ }
+
+ private val _currentPage = MutableLiveData()
+ val currentPage: LiveData = _currentPage
+
+ private val totalItems = MutableLiveData()
+
+ val totalPages = Transformations.map(totalItems) {
+ val perPage = Preferences["per_page", "25"].toInt()
+
+ ceil(it / perPage.toDouble()).roundToInt()
+ }
+
+ private val _suggestions = MutableLiveData>()
+ val suggestions: LiveData> = _suggestions
+
+ init {
+ setSourceAndReset("hitomi.la")
+ }
+
+ fun setSourceAndReset(sourceName: String) {
+ _source.value = direct.source(sourceName).also {
+ sortMode.value = it.availableSortMode.first()
+ }
+
+ setQueryAndSearch()
+ }
+
+ fun setQueryAndSearch(query: String = "") {
+ this.query.value = query
+ setPage(1)
+
+ query()
+ }
+
+ 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 currentPage = currentPage.value ?: 1
+
+ suggestionJob?.cancel()
+ queryJob?.cancel()
+
+ loading = true
+ val results = mutableListOf()
+ _searchResults.value = results
+
+ queryJob = viewModelScope.launch {
+ val channel = withContext(Dispatchers.IO) {
+ val (channel, count) = source.search(
+ (query.value ?: "") + Preferences["default_query", ""],
+ (currentPage - 1) * perPage until currentPage * perPage,
+ sortMode
+ )
+
+ totalItems.postValue(count)
+
+ channel
+ }
+
+ for (result in channel) {
+ yield()
+ results.add(result)
+ _searchResults.notify()
+ }
+ _searchResults.notify()
+
+ loading = false
+ }
+ }
+
+ fun prevPage() { _currentPage.value = _currentPage.value!! - 1 }
+ fun nextPage() { _currentPage.value = _currentPage.value!! + 1 }
+ fun setPage(page: Int) { _currentPage.value = page }
+
+ fun random(callback: (ItemInfo) -> Unit) {
+ if (totalItems.value!! == 0)
+ return
+
+ val random = Random.Default.nextInt(totalItems.value!!)
+
+ viewModelScope.launch {
+ withContext(Dispatchers.IO) {
+ _source.value?.search(
+ query.value + Preferences["default_query", ""],
+ random .. random,
+ sortMode.value!!
+ )?.first?.receive()
+ }?.let(callback)
+ }
+ }
+
+ fun suggestion() {
+ suggestionJob?.cancel()
+
+ _suggestions.value = mutableListOf()
+
+ suggestionJob = viewModelScope.launch {
+ _suggestions.value = withContext(Dispatchers.IO) {
+ kotlin.runCatching {
+ _source.value!!.suggestion(query.value!!)
+ }.getOrElse { emptyList() }
+ }!!
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/ReaderViewModel.kt b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/ReaderViewModel.kt
index 6bcf7d64..70439ef6 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/ReaderViewModel.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/ReaderViewModel.kt
@@ -34,6 +34,7 @@ import xyz.quaver.pupil.adapters.ReaderItem
import xyz.quaver.pupil.sources.AnySource
import xyz.quaver.pupil.util.ImageCache
import xyz.quaver.pupil.util.notify
+import xyz.quaver.pupil.util.source
@Suppress("UNCHECKED_CAST")
class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
@@ -53,7 +54,7 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
@OptIn(ExperimentalCoroutinesApi::class)
fun load(sourceName: String, itemID: String) {
- val source: AnySource by instance(tag = sourceName)
+ val source: AnySource by source(sourceName)
viewModelScope.launch {
_title.value = withContext(Dispatchers.IO) {
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 83b6ffc5..f5838802 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/DownloadManager.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/DownloadManager.kt
@@ -83,7 +83,7 @@ class DownloadManager constructor(context: Context) : ContextWrapper(context), D
@Synchronized
fun download(source: String, itemID: String) = CoroutineScope(Dispatchers.IO).launch {
- val source: AnySource by instance(tag = source)
+ val source: AnySource by source(source)
val info = async { source.info(itemID) }
val images = async { source.images(itemID) }
diff --git a/app/src/main/java/xyz/quaver/pupil/util/SavedCollections.kt b/app/src/main/java/xyz/quaver/pupil/util/SavedCollections.kt
index a3960074..37131e4a 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/SavedCollections.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/SavedCollections.kt
@@ -23,7 +23,9 @@ import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
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
@@ -44,7 +46,7 @@ class SavedSet (private val file: File, any: T, private val set: Mutabl
fun load() {
set.clear()
kotlin.runCatching {
- Json.decodeFromString(serializer, file.readText())
+ decodeFromString(serializer, file.readText())
}.onSuccess {
set.addAll(it)
}
@@ -111,7 +113,7 @@ class SavedMap (private val file: File, anyKey: K, anyValue: V,
fun load() {
map.clear()
kotlin.runCatching {
- Json.decodeFromString(serializer, file.readText())
+ decodeFromString(serializer, file.readText())
}.onSuccess {
map.putAll(it)
}
@@ -167,4 +169,79 @@ class SavedMap (private val file: File, anyKey: K, anyValue: V,
save()
}
+}
+
+class SavedSourceSet(private val file: File) {
+
+ private val _map = mutableMapOf>()
+ val map: Map> = _map
+
+ private val serializer = MapSerializer(String.serializer(), SetSerializer(String.serializer()))
+
+ @Synchronized
+ fun load() {
+ _map.clear()
+ kotlin.runCatching {
+ decodeFromString(serializer, file.readText())
+ }.onSuccess {
+ it.forEach { (k, v) ->
+ _map[k] = v.toMutableSet()
+ }
+ }
+ }
+
+ @Synchronized
+ fun save() {
+ file.parentFile?.mkdirs()
+ if (!file.exists())
+ file.createNewFile()
+
+ file.writeText(Json.encodeToString(serializer, _map))
+ }
+
+ @Synchronized
+ fun add(source: String, value: String) {
+ load()
+
+ _map[source]?.remove(value)
+
+ if (!_map.containsKey(source))
+ _map[source] = mutableSetOf()
+ else
+ _map[source]!!.add(value)
+
+ save()
+ }
+
+ @Synchronized
+ fun addAll(from: Map>) {
+ load()
+
+ for (source in from.keys) {
+ if (_map.containsKey(source)) {
+ _map[source]!!.removeAll(from[source]!!)
+ _map[source]!!.addAll(from[source]!!)
+ } else {
+ _map[source] = from[source]!!.toMutableSet()
+ }
+ }
+
+ save()
+ }
+
+ @Synchronized
+ fun remove(source: String, value: String): Boolean {
+ load()
+
+ return (_map[source]?.remove(value) ?: false).also {
+ save()
+ }
+ }
+
+ @Synchronized
+ fun clear() {
+ _map.clear()
+ save()
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/util/misc.kt b/app/src/main/java/xyz/quaver/pupil/util/misc.kt
index 50aafc39..3e744f79 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/misc.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/misc.kt
@@ -20,14 +20,17 @@ package xyz.quaver.pupil.util
import android.annotation.SuppressLint
import android.view.MenuItem
+import android.view.View
import androidx.lifecycle.MutableLiveData
import kotlinx.serialization.json.*
import okhttp3.OkHttpClient
import okhttp3.Request
+import org.kodein.di.*
import xyz.quaver.hitomi.GalleryInfo
import xyz.quaver.hitomi.getReferer
import xyz.quaver.hitomi.imageUrlFromImage
import xyz.quaver.pupil.sources.ItemInfo
+import xyz.quaver.pupil.sources.SourceEntries
import java.io.InputStream
import java.io.OutputStream
import java.util.*
@@ -128,4 +131,15 @@ fun InputStream.copyTo(out: OutputStream, onCopy: (totalBytesCopied: Long, bytes
bytes = read(buffer)
}
return bytesCopied
+}
+
+fun DIAware.source(source: String) = lazy { direct.source(source) }
+fun DirectDIAware.source(source: String) = instance().toMap()[source]!!
+
+fun View.hide() {
+ visibility = View.INVISIBLE
+}
+
+fun View.show() {
+ visibility = View.VISIBLE
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/util/proxy.kt b/app/src/main/java/xyz/quaver/pupil/util/proxy.kt
index 778ff2ee..dbe0b6fc 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/proxy.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/proxy.kt
@@ -18,7 +18,6 @@
package xyz.quaver.pupil.util
-import android.content.Context
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
diff --git a/app/src/main/java/xyz/quaver/pupil/util/update.kt b/app/src/main/java/xyz/quaver/pupil/util/update.kt
index 2973531a..4528d4b4 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/update.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/update.kt
@@ -18,37 +18,33 @@
package xyz.quaver.pupil.util
-import android.annotation.SuppressLint
import android.app.DownloadManager
-import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import android.content.IntentFilter
import android.net.Uri
-import android.util.Base64
import android.webkit.URLUtil
import androidx.appcompat.app.AlertDialog
-import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.preference.PreferenceManager
-import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString
-import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.*
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Request
import okhttp3.Response
+import org.kodein.di.DI
+import org.kodein.di.DIAware
+import org.kodein.di.android.di
+import org.kodein.di.instance
import ru.noties.markwon.Markwon
import xyz.quaver.pupil.BuildConfig
import xyz.quaver.pupil.R
import xyz.quaver.pupil.client
-import xyz.quaver.pupil.favorites
import java.io.File
import java.io.IOException
import java.net.URL
@@ -184,7 +180,7 @@ fun checkUpdate(context: Context, force: Boolean = false) {
}
}
-fun restore(url: String, onFailure: ((Throwable) -> Unit)? = null, onSuccess: ((List) -> Unit)? = null) {
+fun restore(context: Context, url: String, onFailure: ((Throwable) -> Unit)? = null, onSuccess: ((Set) -> Unit)? = null) {
if (!URLUtil.isValidUrl(url)) {
onFailure?.invoke(IllegalArgumentException())
return
@@ -201,9 +197,10 @@ fun restore(url: String, onFailure: ((Throwable) -> Unit)? = null, onSuccess: ((
}
override fun onResponse(call: Call, response: Response) {
+ val favorites = object: DIAware { override val di by di(context); val favorites: SavedSourceSet by instance(tag = "favorites") }
kotlin.runCatching {
- Json.decodeFromString>(response.also { if (it.code() != 200) throw IOException() }.body().use { it?.string() } ?: "[]").let {
- favorites.addAll(it)
+ Json.decodeFromString>(response.also { if (it.code() != 200) throw IOException() }.body().use { it?.string() } ?: "[]").let {
+ favorites.favorites.addAll(mapOf("hitomi.la" to it))
onSuccess?.invoke(it)
}
}.onFailure { onFailure?.invoke(it) }
diff --git a/app/src/main/res/layout/reader_item.xml b/app/src/main/res/layout/reader_item.xml
index 5720be44..885e44ad 100644
--- a/app/src/main/res/layout/reader_item.xml
+++ b/app/src/main/res/layout/reader_item.xml
@@ -63,8 +63,8 @@
diff --git a/build.gradle b/build.gradle
index 29cb9a8f..a8a3c669 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,7 +10,7 @@ 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.4"
+ classpath "com.google.gms:google-services:4.3.5"
// 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.4.1"
@@ -23,7 +23,9 @@ allprojects {
repositories {
maven { url "https://dl.bintray.com/piasy/maven" }
google()
+ mavenCentral()
jcenter()
+ maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
maven { url "https://jitpack.io" }
maven { url "https://guardian.github.com/maven/repo-releases" }
}
diff --git a/gradle.properties b/gradle.properties
index 74243548..417cbe63 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -21,4 +21,4 @@ android.enableJetifier=true
android.useAndroidX=true
android.enableBuildCache=true
-kotlin_version=1.4.21
\ No newline at end of file
+kotlin_version=1.4.30
\ No newline at end of file