Source settings
@@ -237,7 +237,6 @@
|
||||
android:pathPattern="/..*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="net.rdrei.android.dirchooser.DirectoryChooserActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -65,9 +65,9 @@ class Pupil : Application(), DIAware {
|
||||
import(androidXModule(this@Pupil))
|
||||
import(sourceModule)
|
||||
|
||||
bind<OkHttpClient>() with provider { client }
|
||||
bind<ImageCache>() with singleton { ImageCache(this@Pupil) }
|
||||
bind<DownloadManager>() with singleton { DownloadManager(this@Pupil) }
|
||||
bind { provider { client } }
|
||||
bind { singleton { ImageCache(this@Pupil) } }
|
||||
bind { singleton { DownloadManager(this@Pupil) } }
|
||||
|
||||
bind<SavedSourceSet>(tag = "histories") with singleton { SavedSourceSet(File(ContextCompat.getDataDir(this@Pupil), "histories.json")) }
|
||||
bind<SavedSourceSet>(tag = "favorites") with singleton { SavedSourceSet(File(ContextCompat.getDataDir(this@Pupil), "favorites.json")) }
|
||||
@@ -99,7 +99,7 @@ class Pupil : Application(), DIAware {
|
||||
|
||||
try {
|
||||
Preferences.get<String>("download_folder").also {
|
||||
if (it.startsWith("content") && Build.VERSION.SDK_INT > 19)
|
||||
if (it.startsWith("content"))
|
||||
contentResolver.takePersistableUriPermission(
|
||||
Uri.parse(it),
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
|
||||
@@ -93,16 +93,6 @@ class SearchResultsAdapter(var results: LiveData<List<ItemInfo>>) : RecyclerSwip
|
||||
})
|
||||
|
||||
binding.tagGroup.onClickListener = onChipClickedHandler
|
||||
|
||||
|
||||
/*
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
while (true) {
|
||||
updateProgress()
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private val controllerListener = object: BaseControllerListener<ImageInfo>() {
|
||||
@@ -119,21 +109,6 @@ class SearchResultsAdapter(var results: LiveData<List<ItemInfo>>) : RecyclerSwip
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateProgress() {
|
||||
/* TODO
|
||||
binding.root.max = cache.metadata.imageList?.size ?: 0
|
||||
binding.root.progress = cache.metadata.imageList?.count { it != null } ?: 0
|
||||
|
||||
binding.root.type = if (cache.metadata.imageList?.all { it != null } == true) { // Download completed
|
||||
if (DownloadManager.getInstance(itemView.context).getDownloadFolder(source, itemID) != null)
|
||||
ProgressCardView.Type.DOWNLOAD
|
||||
else
|
||||
ProgressCardView.Type.CACHE
|
||||
} else
|
||||
ProgressCardView.Type.LOADING
|
||||
*/
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bind(result: ItemInfo) {
|
||||
source = result.source
|
||||
@@ -210,7 +185,6 @@ class SearchResultsAdapter(var results: LiveData<List<ItemInfo>>) : RecyclerSwip
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
|
||||
ViewHolder(SearchResultItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||
|
||||
@ExperimentalTime
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
mItemManger.bindView(holder.itemView, position)
|
||||
holder.bind(results.value!![position])
|
||||
|
||||
@@ -30,6 +30,7 @@ import xyz.quaver.pupil.sources.SourceEntries
|
||||
class SourceAdapter(sources: SourceEntries) : RecyclerView.Adapter<SourceAdapter.ViewHolder>() {
|
||||
|
||||
var onSourceSelectedListener: ((String) -> Unit)? = null
|
||||
var onSourceSettingsSelectedListener: ((String) -> Unit)? = null
|
||||
|
||||
private val sources = sources.toList()
|
||||
|
||||
@@ -40,6 +41,9 @@ class SourceAdapter(sources: SourceEntries) : RecyclerView.Adapter<SourceAdapter
|
||||
binding.go.setOnClickListener {
|
||||
onSourceSelectedListener?.invoke(source.name)
|
||||
}
|
||||
binding.settings.setOnClickListener {
|
||||
onSourceSettingsSelectedListener?.invoke(source.name)
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(source: AnySource) {
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
|
||||
package xyz.quaver.pupil.sources
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.core.content.ContextCompat
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
@@ -120,6 +117,7 @@ typealias AnySource = Source<*, SearchSuggestion>
|
||||
abstract class Source<Query_SortMode: Enum<Query_SortMode>, Suggestion: SearchSuggestion> {
|
||||
abstract val name: String
|
||||
abstract val iconResID: Int
|
||||
abstract val preferenceID: Int
|
||||
abstract val availableSortMode: Array<Query_SortMode>
|
||||
|
||||
abstract suspend fun search(query: String, range: IntRange, sortMode: Enum<*>) : Pair<Channel<ItemInfo>, Int>
|
||||
@@ -138,15 +136,19 @@ abstract class Source<Query_SortMode: Enum<Query_SortMode>, Suggestion: SearchSu
|
||||
|
||||
typealias SourceEntry = Pair<String, AnySource>
|
||||
typealias SourceEntries = Set<SourceEntry>
|
||||
typealias PreferenceID = Pair<String, Int>
|
||||
typealias PreferenceIDs = Set<PreferenceID>
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val sourceModule = DI.Module(name = "source") {
|
||||
bind() from setBinding<SourceEntry>()
|
||||
bindSet<SourceEntry>()
|
||||
bindSet<PreferenceID>()
|
||||
|
||||
listOf(
|
||||
Hitomi()
|
||||
).forEach { source ->
|
||||
bind<SourceEntry>().inSet() with multiton { _: Unit -> source.name to (source as AnySource) }
|
||||
inSet { multiton { _: Unit -> source.name to (source as AnySource) } }
|
||||
inSet { singleton { source.name to source.preferenceID } }
|
||||
}
|
||||
|
||||
bind<History>() with factory { source: String -> History(di, source) }
|
||||
bind { factory { source: String -> History(di, source) } }
|
||||
}
|
||||
@@ -26,6 +26,7 @@ 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
|
||||
import xyz.quaver.pupil.util.SavedSourceSet
|
||||
import xyz.quaver.pupil.util.source
|
||||
|
||||
@@ -38,6 +39,8 @@ class History(override val di: DI, source: String) : Source<DefaultSortMode, Sea
|
||||
get() = source.name
|
||||
override val iconResID: Int
|
||||
get() = source.iconResID
|
||||
override val preferenceID: Int
|
||||
get() = source.preferenceID
|
||||
override val availableSortMode: Array<DefaultSortMode> = DefaultSortMode.values()
|
||||
|
||||
override suspend fun search(query: String, range: IntRange, sortMode: Enum<*>): Pair<Channel<ItemInfo>, Int> {
|
||||
|
||||
@@ -29,6 +29,7 @@ import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
|
||||
import xyz.quaver.hitomi.*
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.sources.ItemInfo.ExtraType
|
||||
import xyz.quaver.pupil.util.Preferences
|
||||
import xyz.quaver.pupil.util.translations
|
||||
import xyz.quaver.pupil.util.wordCapitalize
|
||||
import kotlin.math.max
|
||||
@@ -56,6 +57,7 @@ class Hitomi : Source<Hitomi.SortMode, Hitomi.TagSuggestion>() {
|
||||
|
||||
override val name: String = "hitomi.la"
|
||||
override val iconResID: Int = R.drawable.hitomi
|
||||
override val preferenceID: Int = R.xml.hitomi_preferences
|
||||
override val availableSortMode: Array<SortMode> = SortMode.values()
|
||||
|
||||
var cachedQuery: String? = null
|
||||
@@ -67,7 +69,7 @@ class Hitomi : Source<Hitomi.SortMode, Hitomi.TagSuggestion>() {
|
||||
cachedQuery = null
|
||||
cache.clear()
|
||||
yield()
|
||||
doSearch(query, sortMode == SortMode.POPULAR).let {
|
||||
doSearch("$query ${Preferences["hitomi.default_query", ""]}", sortMode == SortMode.POPULAR).let {
|
||||
yield()
|
||||
cache.addAll(it)
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import com.orhanobut.logger.Logger
|
||||
import kotlinx.coroutines.*
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.di
|
||||
import org.kodein.di.android.closestDI
|
||||
import xyz.quaver.floatingsearchview.FloatingSearchView
|
||||
import xyz.quaver.pupil.*
|
||||
import xyz.quaver.pupil.adapters.SearchResultsAdapter
|
||||
@@ -66,11 +66,13 @@ class MainActivity :
|
||||
NavigationView.OnNavigationItemSelectedListener,
|
||||
DIAware
|
||||
{
|
||||
override val di by di()
|
||||
override val di by closestDI()
|
||||
|
||||
private lateinit var binding: MainActivityBinding
|
||||
private val model: MainViewModel by viewModels()
|
||||
|
||||
private var refreshOnResume = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = MainActivityBinding.inflate(layoutInflater)
|
||||
@@ -165,6 +167,15 @@ class MainActivity :
|
||||
} }
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (refreshOnResume) {
|
||||
model.query()
|
||||
|
||||
refreshOnResume = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
binding.contents.recyclerview.adapter = null
|
||||
@@ -440,6 +451,13 @@ class MainActivity :
|
||||
|
||||
dismiss()
|
||||
}
|
||||
|
||||
onSourceSettingsSelectedListener = {
|
||||
startActivity(Intent(this@MainActivity, SettingsActivity::class.java).putExtra(SettingsActivity.SETTINGS_EXTRA, it))
|
||||
|
||||
refreshOnResume = true
|
||||
dismiss()
|
||||
}
|
||||
}.show(supportFragmentManager, null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,16 +22,27 @@ import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.ui.fragment.SettingsFragment
|
||||
import xyz.quaver.pupil.ui.fragment.SourceSettingsFragment
|
||||
|
||||
class SettingsActivity : BaseActivity() {
|
||||
|
||||
companion object {
|
||||
const val SETTINGS_EXTRA = "xyz.quaver.pupil.ui.SettingsActivity.SETTINGS_EXTRA"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.settings_activity)
|
||||
|
||||
val fragment = intent.getStringExtra(SETTINGS_EXTRA)?.run {
|
||||
SourceSettingsFragment(this)
|
||||
} ?: SettingsFragment()
|
||||
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings, SettingsFragment())
|
||||
.replace(R.id.settings, fragment)
|
||||
.commit()
|
||||
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import xyz.quaver.pupil.sources.Hitomi
|
||||
import xyz.quaver.pupil.types.Tags
|
||||
import xyz.quaver.pupil.util.Preferences
|
||||
|
||||
class DefaultQueryDialogFragment() : DialogFragment() {
|
||||
class DefaultQueryDialogFragment : DialogFragment() {
|
||||
// TODO languageMap
|
||||
private val languages = Hitomi.languageMap
|
||||
private val reverseLanguages = languages.entries.associate { (k, v) -> v to k }
|
||||
@@ -85,7 +85,7 @@ class DefaultQueryDialogFragment() : DialogFragment() {
|
||||
|
||||
private fun initView() {
|
||||
val tags = Tags.parse(
|
||||
Preferences["default_query"]
|
||||
Preferences["hitomi.default_query"]
|
||||
)
|
||||
|
||||
with (binding.languageSelector) {
|
||||
@@ -153,7 +153,7 @@ class DefaultQueryDialogFragment() : DialogFragment() {
|
||||
s.replace(
|
||||
0,
|
||||
s.length,
|
||||
s.toString().toLowerCase(java.util.Locale.getDefault())
|
||||
s.toString().lowercase()
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -21,7 +21,6 @@ package xyz.quaver.pupil.ui.dialog
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
@@ -30,21 +29,21 @@ import androidx.core.net.toUri
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.x.di
|
||||
import org.kodein.di.android.x.closestDI
|
||||
import org.kodein.di.instance
|
||||
import xyz.quaver.io.FileX
|
||||
import xyz.quaver.io.util.toFile
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.databinding.DownloadLocationDialogBinding
|
||||
import xyz.quaver.pupil.databinding.DownloadLocationItemBinding
|
||||
import xyz.quaver.pupil.util.DownloadManager
|
||||
import xyz.quaver.pupil.util.Preferences
|
||||
import xyz.quaver.pupil.util.byteToString
|
||||
import xyz.quaver.pupil.util.DownloadManager
|
||||
import java.io.File
|
||||
|
||||
class DownloadLocationDialogFragment : DialogFragment(), DIAware {
|
||||
|
||||
override val di by di()
|
||||
override val di by closestDI()
|
||||
|
||||
private val downloadManager: DownloadManager by instance()
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ 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.android.x.closestDI
|
||||
import org.kodein.di.instance
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.adapters.SearchResultsAdapter
|
||||
@@ -60,7 +60,7 @@ import kotlin.collections.ArrayList
|
||||
|
||||
class GalleryDialogFragment(private val source: String, private val itemID: String) : DialogFragment(), DIAware {
|
||||
|
||||
override val di by di()
|
||||
override val di by closestDI()
|
||||
|
||||
private val favoriteTags: SavedSourceSet by instance(tag = "favoriteTags")
|
||||
|
||||
|
||||
@@ -34,12 +34,14 @@ import xyz.quaver.pupil.adapters.SourceAdapter
|
||||
import xyz.quaver.pupil.sources.AnySource
|
||||
import xyz.quaver.pupil.sources.Source
|
||||
import xyz.quaver.pupil.sources.SourceEntries
|
||||
import xyz.quaver.pupil.util.ItemClickSupport
|
||||
|
||||
class SourceSelectDialog : DialogFragment(), DIAware {
|
||||
|
||||
override val di by closestDI()
|
||||
|
||||
var onSourceSelectedListener: ((String) -> Unit)? = null
|
||||
var onSourceSettingsSelectedListener: ((String) -> Unit)? = null
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return Dialog(requireContext()).apply {
|
||||
@@ -50,6 +52,7 @@ class SourceSelectDialog : DialogFragment(), DIAware {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = SourceAdapter(direct.instance()).apply {
|
||||
onSourceSelectedListener = this@SourceSelectDialog.onSourceSelectedListener
|
||||
onSourceSettingsSelectedListener = this@SourceSelectDialog.onSourceSettingsSelectedListener
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -26,11 +26,8 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.preference.*
|
||||
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.x.di
|
||||
import org.kodein.di.android.x.closestDI
|
||||
import org.kodein.di.instance
|
||||
import xyz.quaver.io.FileX
|
||||
import xyz.quaver.io.util.getChild
|
||||
@@ -39,7 +36,6 @@ import xyz.quaver.pupil.ui.LockActivity
|
||||
import xyz.quaver.pupil.ui.SettingsActivity
|
||||
import xyz.quaver.pupil.ui.dialog.*
|
||||
import xyz.quaver.pupil.util.*
|
||||
import xyz.quaver.pupil.util.DownloadManager
|
||||
import java.util.*
|
||||
|
||||
class SettingsFragment :
|
||||
@@ -49,7 +45,7 @@ class SettingsFragment :
|
||||
SharedPreferences.OnSharedPreferenceChangeListener,
|
||||
DIAware {
|
||||
|
||||
override val di by di()
|
||||
override val di by closestDI()
|
||||
|
||||
private val downloadManager: DownloadManager by instance()
|
||||
|
||||
@@ -92,14 +88,6 @@ class SettingsFragment :
|
||||
"download_folder" -> {
|
||||
DownloadLocationDialogFragment().show(parentFragmentManager, "Download Location Dialog")
|
||||
}
|
||||
"default_query" -> {
|
||||
DefaultQueryDialogFragment().apply {
|
||||
onPositiveButtonClickListener = { newTags ->
|
||||
Preferences["default_query"] = newTags.toString()
|
||||
summary = newTags.toString()
|
||||
}
|
||||
}.show(parentFragmentManager, "Default Query Dialog")
|
||||
}
|
||||
"app_lock" -> {
|
||||
val intent = Intent(requireContext(), LockActivity::class.java).apply {
|
||||
putExtra("force", true)
|
||||
@@ -127,9 +115,6 @@ class SettingsFragment :
|
||||
this ?: return false
|
||||
|
||||
when (key) {
|
||||
"tag_translation" -> {
|
||||
updateTranslations()
|
||||
}
|
||||
"nomedia" -> {
|
||||
val create = (newValue as? Boolean) ?: return false
|
||||
|
||||
@@ -228,11 +213,6 @@ class SettingsFragment :
|
||||
|
||||
onPreferenceChangeListener = this@SettingsFragment
|
||||
}
|
||||
"default_query" -> {
|
||||
summary = Preferences.get<String>("default_query")
|
||||
|
||||
onPreferenceClickListener = this@SettingsFragment
|
||||
}
|
||||
"app_lock" -> {
|
||||
val lockManager = LockManager(requireContext())
|
||||
summary =
|
||||
@@ -250,27 +230,6 @@ class SettingsFragment :
|
||||
|
||||
onPreferenceClickListener = this@SettingsFragment
|
||||
}
|
||||
"tag_translation" -> {
|
||||
this as ListPreference
|
||||
|
||||
isEnabled = false
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
kotlin.runCatching {
|
||||
val languages = getAvailableLanguages().distinct().toTypedArray()
|
||||
|
||||
entries = languages.map { Locale(it).let { loc -> loc.getDisplayLanguage(loc) } }.toTypedArray()
|
||||
entryValues = languages
|
||||
|
||||
launch(Dispatchers.Main) {
|
||||
isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPreferenceChangeListener = this@SettingsFragment
|
||||
|
||||
}
|
||||
"proxy" -> {
|
||||
summary = getProxyInfo().type.name
|
||||
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
package xyz.quaver.pupil.ui.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.x.closestDI
|
||||
import org.kodein.di.direct
|
||||
import org.kodein.di.instance
|
||||
import xyz.quaver.pupil.sources.PreferenceIDs
|
||||
import xyz.quaver.pupil.ui.dialog.DefaultQueryDialogFragment
|
||||
import xyz.quaver.pupil.util.Preferences
|
||||
import xyz.quaver.pupil.util.getAvailableLanguages
|
||||
import xyz.quaver.pupil.util.updateTranslations
|
||||
import java.util.*
|
||||
|
||||
class SourceSettingsFragment(private val source: String) :
|
||||
PreferenceFragmentCompat(),
|
||||
Preference.OnPreferenceClickListener,
|
||||
Preference.OnPreferenceChangeListener,
|
||||
DIAware {
|
||||
override val di by closestDI()
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(direct.instance<PreferenceIDs>().toMap()[source]!!, rootKey)
|
||||
|
||||
initPreferences()
|
||||
}
|
||||
|
||||
override fun onPreferenceClick(preference: Preference?): Boolean {
|
||||
with (preference) {
|
||||
this ?: return false
|
||||
|
||||
when (key) {
|
||||
"hitomi.default_query" -> {
|
||||
DefaultQueryDialogFragment().apply {
|
||||
onPositiveButtonClickListener = { newTags ->
|
||||
Preferences["hitomi.default_query"] = newTags.toString()
|
||||
summary = newTags.toString()
|
||||
}
|
||||
}.show(parentFragmentManager, "Default Query Dialog")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPreferenceChange(preference: Preference?, newValue: Any?): Boolean {
|
||||
with (preference) {
|
||||
this ?: return false
|
||||
|
||||
when (key) {
|
||||
"hitomi.tag_translation" -> {
|
||||
updateTranslations()
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun initPreferences() {
|
||||
for (i in 0 until preferenceScreen.preferenceCount) {
|
||||
|
||||
preferenceScreen.getPreference(i).run {
|
||||
if (this is PreferenceCategory)
|
||||
(0 until preferenceCount).map { getPreference(it) }
|
||||
else
|
||||
listOf(this)
|
||||
}.forEach { preference ->
|
||||
with (preference) {
|
||||
when (key) {
|
||||
"hitomi.default_query" -> {
|
||||
summary = Preferences.get<String>(key)
|
||||
|
||||
onPreferenceClickListener = this@SourceSettingsFragment
|
||||
}
|
||||
|
||||
"hitomi.tag_translation" -> {
|
||||
this as ListPreference
|
||||
|
||||
isEnabled = false
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
kotlin.runCatching {
|
||||
val languages = getAvailableLanguages().distinct().toTypedArray()
|
||||
|
||||
entries = languages.map { Locale(it).let { loc -> loc.getDisplayLanguage(loc) } }.toTypedArray()
|
||||
entryValues = languages
|
||||
|
||||
launch(Dispatchers.Main) {
|
||||
isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPreferenceChangeListener = this@SourceSettingsFragment
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ class FloatingSearchView @JvmOverloads constructor(context: Context, attrs: Attr
|
||||
s ?: return
|
||||
|
||||
if (s.any { it.isUpperCase() })
|
||||
s.replace(0, s.length, s.toString().toLowerCase(Locale.getDefault()))
|
||||
s.replace(0, s.length, s.toString().lowercase())
|
||||
}
|
||||
|
||||
override fun onSuggestionClicked(searchSuggestion: SearchSuggestion?) {
|
||||
|
||||
@@ -25,15 +25,14 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.*
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.x.di
|
||||
import org.kodein.di.instance
|
||||
import org.kodein.di.android.x.closestDI
|
||||
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 {
|
||||
|
||||
override val di by di()
|
||||
override val di by closestDI()
|
||||
|
||||
private val _info = MutableLiveData<ItemInfo>()
|
||||
val info: LiveData<ItemInfo> = _info
|
||||
|
||||
@@ -128,7 +128,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
||||
queryJob = viewModelScope.launch {
|
||||
val channel = withContext(Dispatchers.IO) {
|
||||
val (channel, count) = source.search(
|
||||
(query.value ?: "") + Preferences["default_query", ""],
|
||||
query.value ?: "",
|
||||
(currentPage - 1) * perPage until currentPage * perPage,
|
||||
sortMode
|
||||
)
|
||||
|
||||
@@ -28,27 +28,24 @@ import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.di
|
||||
import org.kodein.di.instance
|
||||
import org.kodein.di.android.closestDI
|
||||
import xyz.quaver.io.FileX
|
||||
import xyz.quaver.io.util.*
|
||||
import xyz.quaver.pupil.sources.AnySource
|
||||
|
||||
class DownloadManager constructor(context: Context) : ContextWrapper(context), DIAware {
|
||||
|
||||
override val di by di(context)
|
||||
override val di by closestDI(context)
|
||||
|
||||
private val defaultDownloadFolder = FileX(this, getExternalFilesDir(null)!!)
|
||||
|
||||
val downloadFolder: FileX
|
||||
get() = {
|
||||
kotlin.runCatching {
|
||||
FileX(this, Preferences.get<String>("download_folder"))
|
||||
}.getOrElse {
|
||||
Preferences["download_folder"] = defaultDownloadFolder.uri.toString()
|
||||
defaultDownloadFolder
|
||||
}
|
||||
}.invoke()
|
||||
get() = kotlin.runCatching {
|
||||
FileX(this, Preferences.get<String>("download_folder"))
|
||||
}.getOrElse {
|
||||
Preferences["download_folder"] = defaultDownloadFolder.uri.toString()
|
||||
defaultDownloadFolder
|
||||
}
|
||||
|
||||
private var prevDownloadFolder: FileX? = null
|
||||
private var downloadFolderMapInstance: MutableMap<String, String>? = null
|
||||
@@ -57,21 +54,19 @@ class DownloadManager constructor(context: Context) : ContextWrapper(context), D
|
||||
get() {
|
||||
if (prevDownloadFolder != downloadFolder) {
|
||||
prevDownloadFolder = downloadFolder
|
||||
downloadFolderMapInstance = {
|
||||
downloadFolderMapInstance = run {
|
||||
val file = downloadFolder.getChild(".download")
|
||||
|
||||
val data = if (file.exists())
|
||||
kotlin.runCatching {
|
||||
file.readText()?.let { Json.decodeFromString<MutableMap<String, String>>(it) }
|
||||
file.readText()?.let<String, MutableMap<String, String>> { Json.decodeFromString(it) }
|
||||
}.onFailure { file.delete() }.getOrNull()
|
||||
else
|
||||
null
|
||||
|
||||
data ?: {
|
||||
data ?: run {
|
||||
file.createNewFile()
|
||||
mutableMapOf<String, String>()
|
||||
}.invoke()
|
||||
}.invoke()
|
||||
mutableMapOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return downloadFolderMapInstance ?: mutableMapOf()
|
||||
|
||||
@@ -23,11 +23,11 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.sendBlocking
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import okhttp3.*
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.di
|
||||
import org.kodein.di.android.closestDI
|
||||
import org.kodein.di.instance
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
@@ -35,7 +35,7 @@ import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class ImageCache(context: Context) : DIAware {
|
||||
override val di by di(context)
|
||||
override val di by closestDI(context)
|
||||
|
||||
private val client: OkHttpClient by instance()
|
||||
|
||||
@@ -115,7 +115,7 @@ class ImageCache(context: Context) : DIAware {
|
||||
file.createNewFile()
|
||||
|
||||
body.byteStream().copyTo(file.outputStream()) { bytes, _ ->
|
||||
channel.sendBlocking(bytes / body.contentLength().toFloat() * 100)
|
||||
channel.trySendBlocking(bytes / body.contentLength().toFloat() * 100)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ fun updateTranslations() = CoroutineScope(Dispatchers.IO).launch {
|
||||
kotlin.runCatching {
|
||||
translations = Json.decodeFromString<Map<String, String>>(client.newCall(
|
||||
Request.Builder()
|
||||
.url(contentURL + "${Preferences["tag_translation", ""].let { if (it.isEmpty()) Locale.getDefault().language else it }}.json")
|
||||
.url(contentURL + "${Preferences["hitomi.tag_translation", ""].let { if (it.isEmpty()) Locale.getDefault().language else it }}.json")
|
||||
.build()
|
||||
).execute().also { if (it.code != 200) return@launch }.body?.use { it.string() } ?: return@launch).filterValues { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 183 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 432 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 605 B After Width: | Height: | Size: 0 B |
31
app/src/main/res/xml/hitomi_preferences.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<Preference
|
||||
app:key="hitomi.default_query"
|
||||
app:title="@string/settings_default_query"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
<ListPreference
|
||||
app:key="hitomi.tag_translation"
|
||||
app:title="@string/settings_tag_translation"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
|
||||
</PreferenceScreen>
|
||||