From f3f269c7fade8a8d37044d0c3992ff0d4803f56e Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 30 Dec 2024 10:01:08 +0200 Subject: [PATCH 1/6] Fix NPE in SyncSettings --- .../kotatsu/settings/SyncSettingsFragment.kt | 2 +- .../kotatsu/sync/data/SyncAuthenticator.kt | 2 +- .../org/koitharu/kotatsu/sync/data/SyncSettings.kt | 14 ++++++++------ .../org/koitharu/kotatsu/sync/domain/SyncHelper.kt | 2 +- .../kotatsu/sync/ui/SyncHostDialogFragment.kt | 4 ++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SyncSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SyncSettingsFragment.kt index 4902b8130..928546b6b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SyncSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SyncSettingsFragment.kt @@ -44,6 +44,6 @@ class SyncSettingsFragment : BasePreferenceFragment(R.string.sync_settings), Fra private fun bindHostSummary() { val preference = findPreference(SyncSettings.KEY_SYNC_URL) ?: return - preference.summary = syncSettings.syncURL + preference.summary = syncSettings.syncUrl } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncAuthenticator.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncAuthenticator.kt index b5840dfbd..213e60127 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncAuthenticator.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncAuthenticator.kt @@ -32,7 +32,7 @@ class SyncAuthenticator( private fun tryRefreshToken() = runCatching { runBlocking { authApi.authenticate( - syncSettings.syncURL, + syncSettings.syncUrl, account.name, accountManager.getPassword(account), ) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncSettings.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncSettings.kt index 9a0651b44..b08aca06e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncSettings.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncSettings.kt @@ -27,13 +27,9 @@ class SyncSettings( @get:WorkerThread @set:WorkerThread - var syncURL: String + var syncUrl: String get() = account?.let { - val result = accountManager.getUserData(it, KEY_SYNC_URL) - if (!result.startsWith("http://") && !result.startsWith("https://")) { - return "http://$result" - } - return result + accountManager.getUserData(it, KEY_SYNC_URL)?.withHttpSchema() }.ifNullOrEmpty { defaultSyncUrl } set(value) { account?.let { @@ -43,6 +39,12 @@ class SyncSettings( companion object { + private fun String.withHttpSchema(): String = if (!startsWith("http://") && !startsWith("https://")) { + "http://$this" + } else { + this + } + const val KEY_SYNC_URL = "host" } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/domain/SyncHelper.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/domain/SyncHelper.kt index 3aa5959c4..6b7ebdd50 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/domain/SyncHelper.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/sync/domain/SyncHelper.kt @@ -59,7 +59,7 @@ class SyncHelper @AssistedInject constructor( .addInterceptor(SyncInterceptor(context, account)) .build() private val baseUrl: String by lazy { - settings.syncURL + settings.syncUrl } private val defaultGcPeriod: Long // gc period if sync enabled get() = TimeUnit.DAYS.toMillis(4) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncHostDialogFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncHostDialogFragment.kt index 3fa3b4506..f2821b5e1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncHostDialogFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncHostDialogFragment.kt @@ -52,7 +52,7 @@ class SyncHostDialogFragment : AlertDialogFragment Date: Wed, 1 Jan 2025 13:53:17 +0200 Subject: [PATCH 2/6] Update parsers --- app/build.gradle | 4 ++-- gradle/libs.versions.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b00f82afc..92880cb0e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { applicationId 'org.koitharu.kotatsu' minSdk = 21 targetSdk = 35 - versionCode = 696 - versionName = '7.7.4' + versionCode = 697 + versionName = '7.7.5' generatedDensities = [] testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner' ksp { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6ba7fe0e2..d3e9bb735 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,7 +31,7 @@ material = "1.12.0" moshi = "1.15.2" okhttp = "4.12.0" okio = "3.9.1" -parsers = "2550b9cac1" +parsers = "8ce6694232" preference = "1.2.1" recyclerview = "1.3.2" room = "2.6.1" From 3b5a9cd2b4d2ae656db0f29c6f6d3896114e02c5 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 1 Jan 2025 12:16:28 +0200 Subject: [PATCH 3/6] Skip non-existing local chapters --- .../local/data/input/LocalMangaParser.kt | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaParser.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaParser.kt index 910ff964e..9c28e7b16 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaParser.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaParser.kt @@ -67,13 +67,18 @@ class LocalMangaParser(private val uri: Uri) { coverUrl = coverEntry?.let { uri.child(it, resolve = true).toString() }.orEmpty(), largeCoverUrl = null, chapters = if (withDetails) { - mangaInfo.chapters?.map { c -> - c.copyInternal( - url = index.getChapterFileName(c.id)?.toPath()?.let { - uri.child(it, resolve = false).toString() - } ?: uri.toString(), - source = LocalMangaSource, - ) + mangaInfo.chapters?.mapNotNull { c -> + val path = index.getChapterFileName(c.id)?.toPath() + if (path != null && !fileSystem.exists(rootPath / path)) { + null + } else { + c.copyInternal( + url = path?.let { + uri.child(it, resolve = false).toString() + } ?: uri.toString(), + source = LocalMangaSource, + ) + } } } else { null From 8d519dd80f8bd986086ed3fd77185c37def42a46 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 1 Jan 2025 13:36:02 +0200 Subject: [PATCH 4/6] Fix settings search --- .../kotatsu/settings/SettingsActivity.kt | 6 +++-- .../settings/search/SettingsSearchFragment.kt | 16 ++++++++++- .../settings/search/SettingsSearchHelper.kt | 27 +++++++++++++++++++ .../search/SettingsSearchMenuProvider.kt | 10 ++++--- .../search/SettingsSearchViewModel.kt | 27 ++++++++++++++----- app/src/main/res/xml/pref_user_data.xml | 2 +- 6 files changed, 73 insertions(+), 15 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt index c554de7b1..aa6b9f8b6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -25,6 +25,7 @@ import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSourceInfo import org.koitharu.kotatsu.core.parser.external.ExternalMangaSource import org.koitharu.kotatsu.core.ui.BaseActivity +import org.koitharu.kotatsu.core.util.ext.buildBundle import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.textAndVisible @@ -174,8 +175,9 @@ class SettingsActivity : } private fun navigateToPreference(item: SettingsItem) { - val args = Bundle(1) - args.putString(ARG_PREF_KEY, item.key) + val args = buildBundle(1) { + putString(ARG_PREF_KEY, item.key) + } openFragment(item.fragmentClass, args, true) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchFragment.kt index 4b11f6d5d..bda7eef32 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchFragment.kt @@ -7,6 +7,8 @@ import android.view.ViewGroup import androidx.core.graphics.Insets import androidx.core.view.updatePadding import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.AsyncListDiffer.ListListener +import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ui.BaseFragment @@ -17,7 +19,9 @@ import org.koitharu.kotatsu.databinding.FragmentSearchSuggestionBinding import org.koitharu.kotatsu.list.ui.adapter.ListItemType @AndroidEntryPoint -class SettingsSearchFragment : BaseFragment(), OnListItemClickListener { +class SettingsSearchFragment : BaseFragment(), + OnListItemClickListener, + ListListener { private val viewModel: SettingsSearchViewModel by activityViewModels() @@ -29,6 +33,7 @@ class SettingsSearchFragment : BaseFragment(), super.onViewBindingCreated(binding, savedInstanceState) val adapter = BaseListAdapter() .addDelegate(ListItemType.NAV_ITEM, settingsItemAD(this)) + adapter.addListListener(this) binding.root.adapter = adapter binding.root.setHasFixedSize(true) viewModel.content.observe(viewLifecycleOwner, adapter) @@ -45,4 +50,13 @@ class SettingsSearchFragment : BaseFragment(), } override fun onItemClick(item: SettingsItem, view: View) = viewModel.navigateToPreference(item) + + override fun onCurrentListChanged( + previousList: List, + currentList: List + ) { + if (currentList.size != previousList.size) { + (viewBinding?.root?.layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(0, 0) + } + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchHelper.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchHelper.kt index 2bdc6871b..d0411d663 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchHelper.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchHelper.kt @@ -13,9 +13,12 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.settings.AppearanceSettingsFragment import org.koitharu.kotatsu.settings.DownloadsSettingsFragment import org.koitharu.kotatsu.settings.NetworkSettingsFragment +import org.koitharu.kotatsu.settings.ProxySettingsFragment import org.koitharu.kotatsu.settings.ReaderSettingsFragment import org.koitharu.kotatsu.settings.ServicesSettingsFragment +import org.koitharu.kotatsu.settings.SuggestionsSettingsFragment import org.koitharu.kotatsu.settings.about.AboutSettingsFragment +import org.koitharu.kotatsu.settings.backup.PeriodicalBackupSettingsFragment import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment import org.koitharu.kotatsu.settings.userdata.UserDataSettingsFragment @@ -39,6 +42,30 @@ class SettingsSearchHelper @Inject constructor( preferenceManager.inflateTo(result, R.xml.pref_tracker, emptyList(), TrackerSettingsFragment::class.java) preferenceManager.inflateTo(result, R.xml.pref_services, emptyList(), ServicesSettingsFragment::class.java) preferenceManager.inflateTo(result, R.xml.pref_about, emptyList(), AboutSettingsFragment::class.java) + preferenceManager.inflateTo( + result, + R.xml.pref_backup_periodic, + listOf(context.getString(R.string.data_and_privacy)), + PeriodicalBackupSettingsFragment::class.java, + ) + preferenceManager.inflateTo( + result, + R.xml.pref_proxy, + listOf(context.getString(R.string.proxy)), + ProxySettingsFragment::class.java, + ) + preferenceManager.inflateTo( + result, + R.xml.pref_suggestions, + listOf(context.getString(R.string.suggestions)), + SuggestionsSettingsFragment::class.java, + ) + preferenceManager.inflateTo( + result, + R.xml.pref_sources, + listOf(context.getString(R.string.remote_sources)), + SourcesSettingsFragment::class.java, + ) return result } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchMenuProvider.kt index 7f2f4527e..59fd74334 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchMenuProvider.kt @@ -22,18 +22,20 @@ class SettingsSearchMenuProvider( override fun onPrepareMenu(menu: Menu) { super.onPrepareMenu(menu) - val currentQuery = viewModel.currentQuery - if (currentQuery.isNotEmpty()) { + if (viewModel.isSearchActive.value) { val menuItem = menu.findItem(R.id.action_search) menuItem.expandActionView() val searchView = menuItem.actionView as SearchView - searchView.setQuery(currentQuery, false) + searchView.setQuery(viewModel.currentQuery, false) } } override fun onMenuItemSelected(menuItem: MenuItem): Boolean = false - override fun onMenuItemActionExpand(item: MenuItem): Boolean = true + override fun onMenuItemActionExpand(item: MenuItem): Boolean { + viewModel.startSearch() + return true + } override fun onMenuItemActionCollapse(item: MenuItem): Boolean { viewModel.discardSearch() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchViewModel.kt index 6be04636a..b698dd51a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchViewModel.kt @@ -18,30 +18,43 @@ class SettingsSearchViewModel @Inject constructor( private val searchHelper: SettingsSearchHelper, ) : BaseViewModel() { - private val query = MutableStateFlow("") + private val query = MutableStateFlow(null) private val allSettings by lazy { searchHelper.inflatePreferences() } val content = query.map { q -> - allSettings.filter { it.title.contains(q, ignoreCase = true) } + if (q == null) { + emptyList() + } else { + allSettings.filter { it.title.contains(q, ignoreCase = true) } + } }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, emptyList()) val isSearchActive = query.map { - it.isNotEmpty() - }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, false) + it != null + }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, query.value != null) val onNavigateToPreference = MutableEventFlow() val currentQuery: String - get() = query.value + get() = query.value.orEmpty() fun onQueryChanged(value: String) { - query.value = value + if (query.value != null) { + query.value = value + } } - fun discardSearch() = onQueryChanged("") + fun discardSearch() { + query.value = null + } + + fun startSearch() { + query.value = query.value.orEmpty() + } fun navigateToPreference(item: SettingsItem) { + discardSearch() onNavigateToPreference.call(item) } } diff --git a/app/src/main/res/xml/pref_user_data.xml b/app/src/main/res/xml/pref_user_data.xml index 38eacc270..5a606dd2b 100644 --- a/app/src/main/res/xml/pref_user_data.xml +++ b/app/src/main/res/xml/pref_user_data.xml @@ -46,7 +46,7 @@ android:summary="@string/restore_summary" android:title="@string/restore_backup" /> - Date: Wed, 1 Jan 2025 13:49:14 +0200 Subject: [PATCH 5/6] Fix details cover corners --- app/src/main/res/layout-w600dp-land/activity_details.xml | 1 + app/src/main/res/layout/activity_details.xml | 1 + app/src/main/res/layout/fragment_preview.xml | 1 + app/src/main/res/layout/sheet_scrobbling.xml | 1 + 4 files changed, 4 insertions(+) diff --git a/app/src/main/res/layout-w600dp-land/activity_details.xml b/app/src/main/res/layout-w600dp-land/activity_details.xml index d90ca8c41..afeea511a 100644 --- a/app/src/main/res/layout-w600dp-land/activity_details.xml +++ b/app/src/main/res/layout-w600dp-land/activity_details.xml @@ -59,6 +59,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:background="?colorSecondaryContainer" + android:clipToOutline="true" android:foreground="?selectableItemBackground" android:scaleType="centerCrop" app:layout_constraintDimensionRatio="H,13:18" diff --git a/app/src/main/res/layout/activity_details.xml b/app/src/main/res/layout/activity_details.xml index be679a6f4..f3093cc07 100644 --- a/app/src/main/res/layout/activity_details.xml +++ b/app/src/main/res/layout/activity_details.xml @@ -52,6 +52,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:background="?colorSecondaryContainer" + android:clipToOutline="true" android:foreground="?selectableItemBackground" android:scaleType="centerCrop" app:layout_constraintDimensionRatio="H,13:18" diff --git a/app/src/main/res/layout/fragment_preview.xml b/app/src/main/res/layout/fragment_preview.xml index 1539c93b2..136505674 100644 --- a/app/src/main/res/layout/fragment_preview.xml +++ b/app/src/main/res/layout/fragment_preview.xml @@ -20,6 +20,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:background="?colorSecondaryContainer" + android:clipToOutline="true" android:foreground="?selectableItemBackground" android:scaleType="centerCrop" app:layout_constraintDimensionRatio="H,13:18" diff --git a/app/src/main/res/layout/sheet_scrobbling.xml b/app/src/main/res/layout/sheet_scrobbling.xml index 41330ffc8..7fe6dea42 100644 --- a/app/src/main/res/layout/sheet_scrobbling.xml +++ b/app/src/main/res/layout/sheet_scrobbling.xml @@ -25,6 +25,7 @@ android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="16dp" + android:clipToOutline="true" android:foreground="?selectableItemBackground" android:scaleType="centerCrop" app:layout_constraintDimensionRatio="H,13:18" From fc1d704f6f24669d73919e3537ff8b3987ed3fd3 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 1 Jan 2025 14:24:43 +0200 Subject: [PATCH 6/6] Fix build --- .../kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt index aa6b9f8b6..3d687637e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -25,7 +25,6 @@ import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSourceInfo import org.koitharu.kotatsu.core.parser.external.ExternalMangaSource import org.koitharu.kotatsu.core.ui.BaseActivity -import org.koitharu.kotatsu.core.util.ext.buildBundle import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.textAndVisible @@ -175,7 +174,7 @@ class SettingsActivity : } private fun navigateToPreference(item: SettingsItem) { - val args = buildBundle(1) { + val args = Bundle(1).apply { putString(ARG_PREF_KEY, item.key) } openFragment(item.fragmentClass, args, true)